Custom Menu Wizard Widget - Version 2.0.0

Version Description

  • ! Possible Breaker ! The calculation of Start Level has been made consistent across the Show all and Children of filters : if you previously had a setup where you were filtering for the children of an item at level 2, with start level set to 4, there would have been no output because the immediate children (at level 3) were outside the start level. Now, there will be output, starting with the grand-children (at level 4).

  • ! Possible Breaker ! There is now deemed to be an artificial "root" item above the level 1 items, which mean that a Children of filter set to "Current Parent Item" or "Current Root Item" will no longer fail for a top-level "current menu item". If you have the "no ancestor" fallback set then this change will have no impact (but you may now want to consider turning the fallback off?); if you don't currently use the "no ancestor" fallback, then where there was previously no output there will now be some!

  • added new option : Items, a comma- or space-delimited list of menu item ids, as an alternative Filter

  • added new option : Depth Relative to Current Item to the Filter section (depth_rel_current=1 in the shortcode)

  • added new option : Must Contain Current Item to the Output section (contains_current=1 in the shortcode)

  • changed the widget's "demo" facility to "assist" and brought it into WordPress admin, with full interactivity with the widget

  • refactored code

Download this release

Release Info

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

Code changes from version 1.2.2 to 2.0.0

custom-menu-wizard.css ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * custom-menu-wizard.css
3
+ * Version: 2.0.0
4
+ */
5
+ /*widget admin style...*/
6
+ /*NB .widget-content class is not present in accessibility mode*/
7
+ .js .widget-content .widget-custom-menu-wizard-onchange .cmw-start-fieldset-collapsed {display:none;}
8
+ .widget-custom-menu-wizard-onchange .cmw-colour-grey {color:#999999;}
9
+ .widget-custom-menu-wizard-onchange .cmw-pad-left-1 {padding-left:1em;}
10
+ .widget-custom-menu-wizard-onchange .cmw-off-the-page {display:none; position:absolute; left:-5000px; top:-5000px;}
11
+ .widget-custom-menu-wizard-onchange .cmw-toggle-assist {float:right; line-height:1; margin-left:1em;}
12
+ .widget-custom-menu-wizard-collapsible-fieldset {margin:0 0 0.5em; cursor:pointer;}
13
+ .widget-custom-menu-wizard-collapsible-fieldset h3 {font-size:1em; margin:0; padding:2px 0.5em;}
14
+ .widget-custom-menu-wizard-collapsible-fieldset div {background-color:#transparent; background-repeat:no-repeat; background-position:0 -36px; height:16px; width:16px; float:right; outline:0 none;}
15
+ .js .widget-content .widget-custom-menu-wizard-collapsible-fieldset div.cmw-collapsed-fieldset {background-position:0 0;}
16
+ .widget-custom-menu-wizard-disableifnot-rp, .widget-custom-menu-wizard-disableifnot-ci {line-height:1.3333;}
17
+ .widget-custom-menu-wizard-childrenof optgroup option {padding-left:0.75em;}
18
+ select.widget-custom-menu-wizard-listen {max-width:100%;}
19
+ /*dialog style...*/
20
+ /*...menu...*/
21
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu {margin:0; font-size:0.8333em;}
22
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu ul {font-size:10px; list-style-type:none; margin:0.5em 0; padding:0;}
23
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu ul ul {margin:0 0 0 5em;}
24
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu li {display:block; margin:0; padding:0;}
25
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu a {display:inline-block; text-align:center; padding:3px 4px; margin:2px; color:#333333; text-decoration:none;}
26
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu a:focus {outline:0 none;}
27
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu input {vertical-align:top; visibility:hidden}
28
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu ul span {display:block; width:10em; padding:0 2px; height:1.75em; line-height:1.75; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; border:1px solid #999999; box-shadow:2px 2px 2px 0px #666666;}
29
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu .current-menu-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)";}
30
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu .current-menu-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)";}
31
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu .current-menu-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)";}
32
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu .picked > a {background-color:#333366; color:#ffffff; font-weight:bold;}
33
+ .widget-custom-menu-wizard-dialog .cmw-demo-filteritems input {visibility:visible}
34
+ /*...shortcode...*/
35
+ .widget-custom-menu-wizard-dialog .cmw-demo-theshortcode {clear:both; width:100%; margin:1em 0 0.5em;}
36
+ .widget-custom-menu-wizard-dialog .cmw-demo-theshortcode code {border:1px solid #CCCCCC; display:inline-block; line-height:1.5; padding:0.25em 0.5em;}
37
+ /*...output...*/
38
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput {float:right; margin:0 0 1em 2em; font-size:0.8333em;}
39
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput h3 {border-bottom:1px solid #CCCCCC; margin:0 0 0.25em; padding: 0.5em 0 0.25em;}
40
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap {margin:0.5em 0; padding:0 1em; border:1px solid #cccccc; min-height:2em; min-width:9em;}
41
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ul, .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol {margin:0.5em 0 0.5em 1.5em; padding:0;}
42
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap li {margin:0;}
43
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap li ul {margin:0 0 0 1em;}
44
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap li ol {margin:0 0 0 1.5em;}
45
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ul {list-style-type:disc;}
46
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ul ul {list-style-type:circle;}
47
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ul ul ul {list-style-type:square;}
48
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ul ul ul ul {list-style-type:disc;}
49
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ul ul ul ul ul {list-style-type:circle;}
50
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol {list-style-type:decimal;}
51
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol {list-style-type:upper-alpha;}
52
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol ol {list-style-type:lower-alpha;}
53
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol ol ol {list-style-type:lower-roman;}
54
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol ol ol ol {list-style-type:decimal;}
55
+ .widget-custom-menu-wizard-dialog .cmw-demo-fallback {display:none; text-align:right; color:#006600;}
56
+ .widget-custom-menu-wizard-dialog .cmw-demo-fallback.cmw-demo-fellback {display:block;}
custom-menu-wizard.js CHANGED
@@ -1,34 +1,528 @@
1
  /* Plugin Name: Custom Menu Wizard
2
- * Version: 1.2.2
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(){
12
- var chkbox = $('input', this).eq(0),
 
13
  collapse = !chkbox.prop('checked');
14
  if(chkbox.length){
15
  chkbox.prop('checked', collapse);
16
- $('div', this).css({backgroundPosition:collapse?'0 0':'0 -36px'});
17
- $(this).next('div')[collapse?'slideUp':'slideDown']();
18
  }
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;
@@ -49,28 +543,65 @@ jQuery(function($){
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');
1
  /* Plugin Name: Custom Menu Wizard
2
+ * Version: 2.0.0
3
  * Author: Roger Barrett
4
  *
5
  * Script for controlling this widget's options (in Admin -> Widgets)
6
  */
7
+ /*global jQuery, window, document */
8
+ /*jslint forin: true, nomen: true, plusplus: true, regexp: true, unparam: true, sloppy: true, white: true */
9
  jQuery(function($){
10
+ var dotPrefix = '.widget-custom-menu-wizard',
11
+ assist = {
12
+ /**
13
+ * gets a widget form's dialog element as a jQuery object
14
+ * @param {Object} fm jQuery of the widget form
15
+ * @return (Object) jQuery of the dialog element
16
+ */
17
+ getDialog : function(fm){
18
+ return $( '#' + fm.find(dotPrefix + '-onchange').data().cmwDialogId );
19
+ },
20
+ /**
21
+ * gets the widget form's values
22
+ * @param {Object} fm jQuery of the widget form
23
+ * @return {Object} key=>value pairs of the form element values
24
+ */
25
+ getSettings : function(fm){
26
+ var settings = {};
27
+ $.each(fm.serializeArray(), function(i, v){
28
+ var name = v.name.replace(/.*\[([^\]]+)\]$/, '$1'),
29
+ val = name !== 'items' && /^-?\d+$/.test(v.value) ? parseInt(v.value, 10) : v.value;
30
+ settings[name] = val;
31
+ if(name === 'items'){
32
+ settings._items_sep = !val || /(^\d+$|,)/.test($.trim(val)) ? ',' : ' ';
33
+ val = $.map(val.split(/[,\s]+/), function(x){
34
+ x = x ? parseInt(x, 10) : 0;
35
+ return isNaN(x) || x < 1 ? null : x;
36
+ });
37
+ settings._items = val.join(settings._items_sep);
38
+ }
39
+ });
40
+ return settings;
41
+ },
42
+ /**
43
+ * recursively build LI items for the menu structure (kicked off by createMenu())
44
+ * @param {Object} items Set of menu items with same parent
45
+ * @param {integer} level Level of parent within the structure (1-based)
46
+ * @return {string} HTML
47
+ */
48
+ buildRecurse : function(items, level, trace){
49
+ var rtn = '', n, i;
50
+ level = (level || 0) + 1;
51
+ trace = trace || '';
52
+ for(n in items){
53
+ i = n.split('|')[0];
54
+ rtn += '<li class="level-' + level + '" data-itemid="' + i + '" data-level="' + level + '" data-trace="' + trace + '">';
55
+ rtn += '<a class="ui-corner-all" href="#"><span class="ui-corner-all" title="#' + i + '">' + n.replace(/^\d+\|/, '');
56
+ rtn += '</span></a><input type="checkbox" value="' + i + '" />';
57
+ if(items[n]){
58
+ rtn += '<ul>' + assist.buildRecurse(items[n], level, i + (trace ? ',' : '') + trace) + '</ul>';
59
+ }
60
+ rtn += '</li>';
61
+ }
62
+ return rtn;
63
+ },
64
+ /**
65
+ * change handler for an item's checkbox in the menu structure
66
+ * @this {Element} Input checkbox element
67
+ * @param {Object} e Event object
68
+ */
69
+ changeMenu : function(e){
70
+ var self = $(this),
71
+ itemsField = $(self.closest('.ui-dialog-content').data().cmwTriggerChange).closest('form').find(dotPrefix + '-setitems'),
72
+ currVal = $.trim(itemsField.val()),
73
+ sep = !currVal || /(^\d+$|,)/.test(currVal) ? ',' : ' ';
74
+ itemsField.val(
75
+ self.closest('.cmw-demo-themenu').find('input').map(function(){
76
+ return this.checked ? this.value : null;
77
+ }).get().join(sep)
78
+ ).trigger('change');
79
+ },
80
+ /**
81
+ * click handler for an item in the menu structure : sets or clears current menu item and its ancestors
82
+ * @this {Element} Anchor element clicked on
83
+ * @param {Object} e Event object
84
+ * @return {boolean} false
85
+ */
86
+ clickMenu : function(e){
87
+ var self = $(this),
88
+ cls = ['current-menu-item', 'current-menu-parent', 'current-menu-ancestor'],
89
+ dialog = self.closest('.ui-dialog-content'),
90
+ themenu = dialog.find('.cmw-demo-themenu'),
91
+ inPath = self.find('span').not('.' + cls[0]).parentsUntil(themenu, 'li'),
92
+ i, n,
93
+ appendCls = function(){
94
+ this.title = this.title + ' ' + n.replace(' ', ' & ').replace(/-/g, ' ');
95
+ };
96
+ themenu.find('.' + cls.join(',.')).removeClass(cls.join(' ')).each(function(){
97
+ this.title = this.title.replace(/\s.*$/, '');
98
+ });
99
+ for(i = 0; i < inPath.length; i++){
100
+ n = i === 1 ? cls.join(' ') : cls[0];
101
+ inPath.eq(i).children('a').find('span').addClass(n).each(appendCls);
102
+ if(cls.length > 1){
103
+ cls.shift();
104
+ }
105
+ }
106
+ //trigger a change in the widget form, causing update() to be run...
107
+ $( dialog.data().cmwTriggerChange ).trigger('change');
108
+ return false;
109
+ },
110
+ /**
111
+ * click handler for an item in the Basic Output list : triggers a click on the respective menu structure item
112
+ * @this {Element} Anchor element clicked on
113
+ * @param {Object} e Event object
114
+ * @return {boolean} false
115
+ */
116
+ clickOutput : function(e){
117
+ var indx = this.href.split('#')[1];
118
+ $(this).closest('.ui-dialog-content').find('.cmw-demo-themenu a').eq(indx).not(':has(.current-menu-item)').trigger('click');
119
+ this.blur();
120
+ return false;
121
+ },
122
+ /**
123
+ * creates a new list of menu items and inserts it into the dialog content in place of any previous one
124
+ * @param {Object} dialog jQuery object of the dialog
125
+ * @param {Object} fm jQuery object of the widget form
126
+ */
127
+ createMenu : function(dialog, fm){
128
+ var data = dialog.data(),
129
+ themenu = dialog.find('.cmw-demo-themenu'),
130
+ selectmenu = fm.find(dotPrefix + '-selectmenu'),
131
+ menuid = parseInt(selectmenu.val(), 10),
132
+ currentmenu = themenu.find('ul').eq(0),
133
+ mdata = themenu.data(),
134
+ menu;
135
+ if(!currentmenu.length || currentmenu.data('menuid') !== menuid){
136
+ menu = $('<ul>' + assist.buildRecurse( fm.find(dotPrefix + '-childrenof optgroup').data().cmwItems || {} ) + '</ul>');
137
+ currentmenu.remove();
138
+ dialog.dialog('option', 'title', data.cmwTitlePrefix + selectmenu.find('option:selected').text() );
139
+ mdata.maxLevel = 0;
140
+ themenu.append( menu.data('menuid', menuid) )
141
+ .find('a').each(function(i){
142
+ var level = $(this).parent('li').data().level;
143
+ $(this).data('indx', i);
144
+ if(level && level > mdata.maxLevel){
145
+ mdata.maxLevel = level;
146
+ }
147
+ });
148
+ }
149
+ },
150
+ /**
151
+ * toggles the assist dialog open/closed, creating it if necessary
152
+ * @this {Element} A -toggle-assist anchor
153
+ * @param {Object} e Event object
154
+ * @return {boolean} false
155
+ */
156
+ init : function(e){
157
+ var self = $(this),
158
+ data = self.closest(dotPrefix + '-onchange').data(),
159
+ dialog = $( '#' + data.cmwDialogId ),
160
+ fm = self.closest('form');
161
+ if(!dialog.length){
162
+ //create it...
163
+ dialog = $('<div/>', {id:data.cmwDialogId}).addClass(dotPrefix.substr(1) + '-dialog')
164
+ .data({cmwTriggerChange:data.cmwDialogTrigger, cmwTitlePrefix:data.cmwDialogTitle})
165
+ .append( $('<div/>').addClass('cmw-demo-theoutput').html('<strong>' + data.cmwDialogOutput + '</strong> &hellip;<div class="cmw-demo-theoutput-wrap ui-corner-all"></div><div class="cmw-demo-fallback"><small>' + data.cmwDialogFallback + '</small></div>') )
166
+ .append( $('<div/>').addClass('cmw-demo-themenu').html('<small><em>' + data.cmwDialogPrompt + '</em></small>') )
167
+ .append( $('<div/>').addClass('cmw-demo-theshortcode').html('<code class="ui-corner-all">[custom_menu_wizard]</code>') );
168
+ dialog.find('.cmw-demo-themenu').on('click', 'a', assist.clickMenu);
169
+ dialog.find('.cmw-demo-themenu').on('change', 'input', assist.changeMenu);
170
+ dialog.find('.cmw-demo-theoutput').on('click', 'a', assist.clickOutput);
171
+ dialog.dialog({autoOpen:false, width:Math.min($(window).width() * 0.8, 600), modal:false});
172
+ }
173
+ if(dialog.dialog('isOpen')){
174
+ dialog.dialog('close');
175
+ }else{
176
+ assist.createMenu(dialog, fm);
177
+ dialog.dialog('open');
178
+ $(data.cmwDialogTrigger).trigger('change');
179
+ }
180
+ this.blur();
181
+ return false;
182
+ },
183
+ /**
184
+ * create and show the shortcode equivalent
185
+ * @param {Object} fm jQuery object of the widget form
186
+ * @param {Object} settings Form element values
187
+ */
188
+ shortcode : function(fm, settings){
189
+ var args = {
190
+ 'menu' : [settings.menu]
191
+ },
192
+ v, m, n;
193
+ if(settings.title){
194
+ args.title = settings.title;
195
+ }
196
+ if(settings.filter > 0){
197
+ switch(settings.filter_item){
198
+ case 0: args.children_of = 'current'; break;
199
+ case -1: args.children_of = 'parent'; break;
200
+ case -2: args.children_of = 'root'; break;
201
+ default:
202
+ args.children_of = [settings.filter_item];
203
+ }
204
+ }
205
+ if(settings.filter < 0){
206
+ args.items = settings._items;
207
+ }
208
+ if(settings.filter > 0 && settings.filter_item < 0 && settings.fallback_no_ancestor){
209
+ if(settings.fallback_include_parent_siblings){
210
+ args.fallback_parent = 'siblings';
211
+ }else if(settings.fallback_include_parent){
212
+ args.fallback_parent = 'parent';
213
+ }else{
214
+ args.fallback_parent = [1];
215
+ }
216
+ }
217
+ if(settings.filter > 0 && !settings.filter_item && settings.fallback_no_children){
218
+ if(settings.fallback_nc_include_parent_siblings){
219
+ args.fallback_current = 'siblings';
220
+ }else if(settings.fallback_nc_include_parent){
221
+ args.fallback_current = 'parent';
222
+ }else{
223
+ args.fallback_current = [1];
224
+ }
225
+ }
226
+ if(settings.start_level > 1){
227
+ args.start_level = [settings.start_level];
228
+ }
229
+ if(settings.depth > 0){
230
+ args.depth = [settings.depth];
231
+ }
232
+ if(settings.depth_rel_current && settings.depth > 0){
233
+ args.depth_rel_current = [1];
234
+ }
235
+ n = [];
236
+ if(settings.filter > 0){
237
+ if(settings.include_parent_siblings){
238
+ n.push('siblings');
239
+ }else if(settings.include_parent){
240
+ n.push('parent');
241
+ }
242
+ if(settings.include_ancestors){
243
+ n.push('ancestors');
244
+ }
245
+ if(n.length){
246
+ args.include = n.join(' ');
247
+ }
248
+ }
249
+ n = [];
250
+ if(settings.filter > 0 && settings.title_from_parent){
251
+ n.push('parent');
252
+ }
253
+ if(settings.title_from_current){
254
+ n.push('current');
255
+ }
256
+ if(n.length){
257
+ args.title_from = n.join(' ');
258
+ }
259
+ for(n in {flat_output:1, contains_current:1, ol_root:1, ol_sub:1}){
260
+ if(settings[n]){
261
+ args[n] = [1];
262
+ }
263
+ }
264
+ v = {container:'div', container_id:'', container_class:'', menu_class:'menu-widget', widget_class:''};
265
+ for(n in v){
266
+ if(settings[n] !== v[n]){
267
+ args[n] = settings[n];
268
+ }
269
+ }
270
+ v = {wrap_link:'before', wrap_link_text:'link_before'};
271
+ for(n in v){
272
+ m = settings[v[n]].toString().match(/^<(\w+)/);
273
+ if(m && m[1]){
274
+ args[n] = m[1];
275
+ }
276
+ }
277
+ v = [];
278
+ for(n in args){
279
+ //array indicates 'as is', otherwise surround it in double quotes...
280
+ v.push( $.isArray(args[n]) ? n + '=' + args[n][0] : n + '="' + args[n] + '"' );
281
+ }
282
+ assist.getDialog(fm).find('code').text('[custom_menu_wizard ' + v.join(' ') + ']');
283
+ },
284
+ /**
285
+ *
286
+ * @this {Element}
287
+ * @param {Object} e Event object
288
+ * @param {Object} fm jQuery of widget form
289
+ * @param {Object} settings Form element values
290
+ */
291
+ show : function(e, fm, settings){ //scope is a widget form element
292
+ //hide_empty is assumed to be On (WP < v3.6) or will automatically be On (WP v3.6+)
293
+ fm = fm || $(this).closest('form');
294
+ settings = settings || assist.getSettings(fm);
295
+ var dialog = assist.getDialog(fm),
296
+ themenu = dialog.find('.cmw-demo-themenu'),
297
+ items = themenu.find('.picked'),
298
+ html = '',
299
+ title = '',
300
+ currLevel = 0,
301
+ output = dialog.find('.cmw-demo-theoutput-wrap').empty(),
302
+ listClass = ['menu-widget'],
303
+ itemList = {};
304
+ if(items.length && output.length){
305
+ if(settings.filter > 0 && settings.title_from_parent){
306
+ title = themenu.find('.the-parent').children('a').text() || '';
307
+ }
308
+ if(!title && settings.title_from_current){
309
+ title = themenu.find('.current-menu-item').text() || '';
310
+ }
311
+ if(!title && !settings.hide_title){
312
+ title = settings.title || '';
313
+ }
314
+ items.each(function(i){
315
+ var self = $(this),
316
+ data = self.data(),
317
+ trace = data.trace ? data.trace.toString().split(',') : [],
318
+ iid = data.itemid.toString(),
319
+ level = 1,
320
+ anchor = self.children('a');
321
+ if(!settings.flat_output){
322
+ itemList[iid] = 1;
323
+ for(i = 0; i < trace.length; i++){
324
+ if(itemList[trace[i]]){
325
+ level++;
326
+ }
327
+ }
328
+ }
329
+ if(currLevel){
330
+ if(level > currLevel){
331
+ html += settings.ol_sub ? '<ol>' : '<ul>';
332
+ }else{
333
+ while(currLevel > level){
334
+ --currLevel;
335
+ html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
336
+ }
337
+ html += '</li>';
338
+ }
339
+ }
340
+ html += '<li class="cmw-level-' + level + (data.included || '') + '"><a href="#' + anchor.data('indx') + '">' + anchor.text() + '</a>';
341
+ currLevel = level;
342
+ });
343
+ while(currLevel > 1){
344
+ --currLevel;
345
+ html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
346
+ }
347
+ html += '</li>';
348
+ listClass.push( dialog.find('.cmw-demo-fallback').data('fellback') );
349
+ html = (settings.ol_root ? '<ol' : '<ul') + ' class="' + $.trim(listClass.join(' ')) + '">' + html + (settings.ol_root ? '</ol>' : '</ul>');
350
+ output.html(html);
351
+ if(title){
352
+ output.prepend('<h3>' + title + '</h3>');
353
+ }
354
+ output.find('li').filter(function(){
355
+ return !!$(this).children('ul, ol').length;
356
+ }).addClass('cmw-has-submenu');
357
+ }
358
+ assist.shortcode(fm, settings);
359
+ },
360
+ /**
361
+ * updates the graphic menu structure from the widget form data
362
+ * @this {Element} An input (radio or checkbox) or select element from the widget form
363
+ * @param {Object} e Event object
364
+ */
365
+ update : function(e){
366
+ var fm = $(this).closest('form'),
367
+ dialog = assist.getDialog(fm),
368
+ maxLevel, settings, includeParent, includeParentSiblings, themenu, items,
369
+ currentItemLI, currentItemLevel, fallback, parent, i, j;
370
+ if(!dialog.length || !dialog.dialog('isOpen')){
371
+ return;
372
+ }
373
+
374
+ if($(e.target).hasClass(dotPrefix.substr(1) + '-selectmenu')){
375
+ assist.createMenu(dialog, fm);
376
+ }
377
+ settings = assist.getSettings(fm);
378
+ includeParent = settings.include_parent;
379
+ includeParentSiblings = settings.include_parent_siblings;
380
+ themenu = dialog.find('.cmw-demo-themenu');
381
+ maxLevel = themenu.data().maxLevel;
382
+ currentItemLI = themenu.find('.current-menu-item').closest('li');
383
+ currentItemLevel = currentItemLI.length ? currentItemLI.data().level : -1;
384
+ items = themenu.find('li').removeData('included').removeClass('the-parent');
385
+
386
+ if(settings.filter < 0){
387
+ items = items.filter(function(){
388
+ var checkbox = $(this).children('input'),
389
+ checked = (settings._items_sep + settings._items + settings._items_sep).indexOf(settings._items_sep + checkbox[0].value + settings._items_sep) > -1;
390
+ checkbox.prop('checked', checked);
391
+ return checked;
392
+ });
393
+ if(!settings._items){
394
+ items = $([]);
395
+ }
396
+ }
397
+
398
+ if(items.length && !currentItemLI.length && (settings.contains_current || (settings.filter > 0 && settings.filter_item < 1))){
399
+ items = $([]);
400
+ }
401
+
402
+ if(items.length && settings.filter > 0){
403
+ //kids of...
404
+ if(settings.filter_item > 0){
405
+ //specific item...
406
+ parent = items.filter('[data-itemid=' + settings.filter_item + ']');
407
+ }else if(!settings.filter_item){
408
+ //current...
409
+ if(currentItemLI.find('li').length){
410
+ parent = currentItemLI;
411
+ }else if(settings.fallback_no_children){
412
+ //fall back to current parent...
413
+ parent = themenu.find('.current-menu-parent').closest('li');
414
+ if(!parent.length){
415
+ parent = themenu; //beware!
416
+ }
417
+ includeParent = includeParent || settings.fallback_nc_include_parent;
418
+ includeParentSiblings = includeParentSiblings || settings.fallback_nc_include_parent_siblings;
419
+ fallback = 'cmw-fellback-to-parent';
420
+ }
421
+ }else{
422
+ //parent or root...
423
+ if(currentItemLevel === 1 && settings.fallback_no_ancestor){
424
+ parent = currentItemLI;
425
+ includeParent = includeParent || settings.fallback_include_parent;
426
+ includeParentSiblings = includeParentSiblings || settings.fallback_include_parent_siblings;
427
+ fallback = 'cmw-fellback-to-current';
428
+ }else if(currentItemLevel === 1){
429
+ parent = themenu; //beware!
430
+ }else if(settings.filter_item < -1){
431
+ parent = themenu.find('.current-menu-ancestor').eq(0).closest('li');
432
+ }else{
433
+ parent = themenu.find('.current-menu-parent').closest('li');
434
+ }
435
+ }
436
+ }
437
+
438
+ if(items.length){
439
+ if(!settings.filter){
440
+ //showall : use the levels...
441
+ if(settings.depth_rel_current && settings.depth && currentItemLI.length && currentItemLevel >= settings.start_level){
442
+ j = currentItemLevel + settings.depth - 1;
443
+ }else{
444
+ j = settings.depth ? settings.start_level + settings.depth - 1 : 9999;
445
+ }
446
+ for(i = 1; i <= maxLevel; i++){
447
+ if(i < settings.start_level || i > j){
448
+ items = items.not('.level-' + i);
449
+ }
450
+ }
451
+ }else if(parent && parent.length){
452
+ //kids of...
453
+ if(settings.depth_rel_current && settings.depth && currentItemLI.length && parent.has(currentItemLI[0])){
454
+ j = currentItemLevel - 1 + settings.depth;
455
+ }else{
456
+ j = settings.depth ? Math.max( (parent.data().level || 0) + settings.depth, settings.start_level + settings.depth - 1 ) : 9999;
457
+ }
458
+ items = parent.find('li').filter(function(){
459
+ var level = $(this).data().level;
460
+ return level >= settings.start_level && level <= j;
461
+ });
462
+ }else if(settings.filter > 0){
463
+ //kids-of, but no parent found...
464
+ items = $([]);
465
+ }
466
+ }
467
+
468
+ if(items.length){
469
+ if(settings.filter > 0 && parent && parent.is('li')){
470
+ //kids of an item...
471
+ if(includeParentSiblings){
472
+ items = items.add( parent.siblings('li').data('included', ' cmw-an-included-parent-sibling') );
473
+ includeParent = true;
474
+ }
475
+ if(settings.include_ancestors){
476
+ items = items.add( parent.parentsUntil(themenu, 'li').data('included', ' cmw-an-included-ancestor') );
477
+ includeParent = true;
478
+ }
479
+ if(includeParent){
480
+ items = items.add( parent.data('included', ' cmw-the-included-parent') );
481
+ }
482
+ }
483
+ }
484
+
485
+ //must contain current item?...
486
+ if(items.length && settings.contains_current && (!currentItemLI.length || !items.filter(currentItemLI).length)){
487
+ items = $([]);
488
+ }
489
+
490
+ if(items.length && parent && parent.is('li')){
491
+ parent.addClass('the-parent');
492
+ }
493
+ fallback = items.length ? fallback : '';
494
+ dialog.find('.cmw-demo-fallback').data('fellback', fallback).toggleClass('cmw-demo-fellback', !!fallback);
495
+ themenu.toggleClass('cmw-demo-filteritems', settings.filter < 0)
496
+ .find('.picked').not( items.addClass('picked') ).removeClass('picked');
497
+ assist.show.call(this, e, fm, settings);
498
+ }
499
+ };
500
+
501
  $(document)
502
  //fieldsets...
503
  .on('click', dotPrefix + '-collapsible-fieldset', function(){
504
+ var self = $(this),
505
+ chkbox = self.find('input').eq(0),
506
  collapse = !chkbox.prop('checked');
507
  if(chkbox.length){
508
  chkbox.prop('checked', collapse);
509
+ self.find('div').toggleClass('cmw-collapsed-fieldset', collapse);
510
+ self.next('div')[collapse?'slideUp':'slideDown']();
511
  }
512
  this.blur();
513
  return false;
514
  })
515
  //change of menu, and enableif / disableif...
516
  .on('change', dotPrefix + '-listen', function(){
517
+ var fm = $(this.form),
518
+ selectMenu = fm.find(dotPrefix + '-selectmenu'),
519
+ showAll = fm.find(dotPrefix + '-showall').prop('checked'),
520
+ showSpecific = fm.find(dotPrefix + '-showspecific').prop('checked'),
521
+ filterItem = fm.find(dotPrefix + '-childrenof'),
522
  fiVal = parseInt(filterItem.val(), 10),
523
  groupClone;
524
  if(selectMenu.is(this)){
525
+ //change of menu : swap out the childrenof's optgroup for the new one...
526
  selectMenu = this.selectedIndex;
527
  if(!filterItem.find('optgroup').filter(function(){
528
  var keep = $(this).data('cmwOptgroupIndex') === selectMenu;
543
  }
544
  }
545
  $.each(
546
+ { '' : showAll || showSpecific,
547
+ '-ss' : showSpecific,
548
+ 'not-rp' : showAll || showSpecific || fiVal >= 0,
549
+ 'not-ci' : showAll || showSpecific || !!fiVal
550
  },
551
  function(k, v){
552
+ $(dotPrefix + '-disableif' + k, this.form).toggleClass('cmw-colour-grey', v).find('input,select').prop('disabled', v);
553
  });
554
+ })
555
+ //any change event on inputs or selects...
556
+ .on('change', dotPrefix + '-onchange', assist.update)
557
+ //assist dialog...
558
+ .on('click', dotPrefix + '-toggle-assist', assist.init)
559
+ //when a widget is closed, close its open dialog...
560
+ .on('click', '.widget-action, .widget-control-close', function(){
561
+ $(this).closest('div.widget').find('.widget-custom-menu-wizard-onchange').each(function(){
562
+ var dialog = $('#' + $(this).data().cmwDialogId);
563
+ if(dialog.length && dialog.dialog('isOpen')){
564
+ dialog.dialog('close');
565
+ }
566
+ });
567
  });
568
 
569
+ //1. when a widget is opened or saved, trigger change on the filter_item select
570
+ //2. when a widget is deleted, destroy its dialog
571
+ //To achieve this I've elected to modify WP's window.wpWidgets object and intercept some of its methods
572
+ // - for (1), the fixLabels() method, which handily gets called whenever a widget is opened or saved
573
+ // - for (2), the save() method
574
+ if(window.wpWidgets){
575
+ if(window.wpWidgets.fixLabels && !window.wpWidgets._cmw_fixLabels){
576
+ //save the original...
577
+ window.wpWidgets._cmw_fixLabels = window.wpWidgets.fixLabels;
578
+ //replace the original...
579
+ window.wpWidgets.fixLabels = function(widget){
580
+ //trigger change on selectmenu...
581
+ widget.find('.widget-custom-menu-wizard-selectmenu').trigger('change');
582
+ //run the original...
583
+ window.wpWidgets._cmw_fixLabels(widget);
584
+ };
585
+ }
586
+ if(window.wpWidgets.save && !window.wpWidgets._cmw_save){
587
+ //save the original...
588
+ window.wpWidgets._cmw_save = window.wpWidgets.save;
589
+ //replace the original...
590
+ window.wpWidgets.save = function(widget, del, animate, order){
591
+ //destroy dialog if deleting the widget...
592
+ if(del){
593
+ widget.find('.widget-custom-menu-wizard-onchange').each(function(){
594
+ var dialog = $('#' + $(this).data().cmwDialogId);
595
+ if(dialog.length){
596
+ dialog.dialog('destroy');
597
+ dialog.remove();
598
+ }
599
+ });
600
+ }
601
+ //run the original...
602
+ window.wpWidgets._cmw_save(widget, del, animate, order);
603
+ };
604
+ }
605
  }else{
606
  //one-off fallback...
607
  $(dotPrefix + '-selectmenu').trigger('change');
custom-menu-wizard.min.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Plugin Name: Custom Menu Wizard
2
+ * Version: 2.0.0
3
+ * Author: Roger Barrett
4
+ *
5
+ * Script for controlling this widget's options (in Admin -> Widgets)
6
+ */
7
+ jQuery(function(f){var n={getDialog:function(c){return f("#"+c.find(".widget-custom-menu-wizard-onchange").data().cmwDialogId)},getSettings:function(c){var a={};f.each(c.serializeArray(),function(d,c){var b=c.name.replace(/.*\[([^\]]+)\]$/,"$1"),e="items"!==b&&/^-?\d+$/.test(c.value)?parseInt(c.value,10):c.value;a[b]=e;"items"===b&&(a._items_sep=!e||/(^\d+$|,)/.test(f.trim(e))?",":" ",e=f.map(e.split(/[,\s]+/),function(a){a=a?parseInt(a,10):0;return isNaN(a)||1>a?null:a}),a._items=e.join(a._items_sep))});
8
+ return a},buildRecurse:function(c,a,d){var f="",b,e;a=(a||0)+1;d=d||"";for(b in c)e=b.split("|")[0],f+='<li class="level-'+a+'" data-itemid="'+e+'" data-level="'+a+'" data-trace="'+d+'">',f+='<a class="ui-corner-all" href="#"><span class="ui-corner-all" title="#'+e+'">'+b.replace(/^\d+\|/,""),f+='</span></a><input type="checkbox" value="'+e+'" />',c[b]&&(f+="<ul>"+n.buildRecurse(c[b],a,e+(d?",":"")+d)+"</ul>"),f+="</li>";return f},changeMenu:function(c){c=f(this);var a=f(c.closest(".ui-dialog-content").data().cmwTriggerChange).closest("form").find(".widget-custom-menu-wizard-setitems"),
9
+ d=f.trim(a.val()),d=!d||/(^\d+$|,)/.test(d)?",":" ";a.val(c.closest(".cmw-demo-themenu").find("input").map(function(){return this.checked?this.value:null}).get().join(d)).trigger("change")},clickMenu:function(c){var a=f(this);c=["current-menu-item","current-menu-parent","current-menu-ancestor"];var d=a.closest(".ui-dialog-content"),h=d.find(".cmw-demo-themenu"),a=a.find("span").not("."+c[0]).parentsUntil(h,"li"),b,e=function(){this.title=this.title+" "+b.replace(" "," & ").replace(/-/g," ")};h.find("."+
10
+ c.join(",.")).removeClass(c.join(" ")).each(function(){this.title=this.title.replace(/\s.*$/,"")});for(h=0;h<a.length;h++)b=1===h?c.join(" "):c[0],a.eq(h).children("a").find("span").addClass(b).each(e),1<c.length&&c.shift();f(d.data().cmwTriggerChange).trigger("change");return!1},clickOutput:function(c){c=this.href.split("#")[1];f(this).closest(".ui-dialog-content").find(".cmw-demo-themenu a").eq(c).not(":has(.current-menu-item)").trigger("click");this.blur();return!1},createMenu:function(c,a){var d=
11
+ c.data(),h=c.find(".cmw-demo-themenu"),b=a.find(".widget-custom-menu-wizard-selectmenu"),e=parseInt(b.val(),10),m=h.find("ul").eq(0),k=h.data(),g;m.length&&m.data("menuid")===e||(g=f("<ul>"+n.buildRecurse(a.find(".widget-custom-menu-wizard-childrenof optgroup").data().cmwItems||{})+"</ul>"),m.remove(),c.dialog("option","title",d.cmwTitlePrefix+b.find("option:selected").text()),k.maxLevel=0,h.append(g.data("menuid",e)).find("a").each(function(a){var b=f(this).parent("li").data().level;f(this).data("indx",
12
+ a);b&&b>k.maxLevel&&(k.maxLevel=b)}))},init:function(c){var a=f(this);c=a.closest(".widget-custom-menu-wizard-onchange").data();var d=f("#"+c.cmwDialogId),a=a.closest("form");d.length||(d=f("<div/>",{id:c.cmwDialogId}).addClass("widget-custom-menu-wizard-dialog").data({cmwTriggerChange:c.cmwDialogTrigger,cmwTitlePrefix:c.cmwDialogTitle}).append(f("<div/>").addClass("cmw-demo-theoutput").html("<strong>"+c.cmwDialogOutput+'</strong> &hellip;<div class="cmw-demo-theoutput-wrap ui-corner-all"></div><div class="cmw-demo-fallback"><small>'+
13
+ c.cmwDialogFallback+"</small></div>")).append(f("<div/>").addClass("cmw-demo-themenu").html("<small><em>"+c.cmwDialogPrompt+"</em></small>")).append(f("<div/>").addClass("cmw-demo-theshortcode").html('<code class="ui-corner-all">[custom_menu_wizard]</code>')),d.find(".cmw-demo-themenu").on("click","a",n.clickMenu),d.find(".cmw-demo-themenu").on("change","input",n.changeMenu),d.find(".cmw-demo-theoutput").on("click","a",n.clickOutput),d.dialog({autoOpen:!1,width:Math.min(0.8*f(window).width(),600),
14
+ modal:!1}));d.dialog("isOpen")?d.dialog("close"):(n.createMenu(d,a),d.dialog("open"),f(c.cmwDialogTrigger).trigger("change"));this.blur();return!1},shortcode:function(c,a){var d={menu:[a.menu]},h,b,e;a.title&&(d.title=a.title);if(0<a.filter)switch(a.filter_item){case 0:d.children_of="current";break;case -1:d.children_of="parent";break;case -2:d.children_of="root";break;default:d.children_of=[a.filter_item]}0>a.filter&&(d.items=a._items);0<a.filter&&0>a.filter_item&&a.fallback_no_ancestor&&(d.fallback_parent=
15
+ a.fallback_include_parent_siblings?"siblings":a.fallback_include_parent?"parent":[1]);0<a.filter&&!a.filter_item&&a.fallback_no_children&&(d.fallback_current=a.fallback_nc_include_parent_siblings?"siblings":a.fallback_nc_include_parent?"parent":[1]);1<a.start_level&&(d.start_level=[a.start_level]);0<a.depth&&(d.depth=[a.depth]);a.depth_rel_current&&0<a.depth&&(d.depth_rel_current=[1]);e=[];0<a.filter&&(a.include_parent_siblings?e.push("siblings"):a.include_parent&&e.push("parent"),a.include_ancestors&&
16
+ e.push("ancestors"),e.length&&(d.include=e.join(" ")));e=[];0<a.filter&&a.title_from_parent&&e.push("parent");a.title_from_current&&e.push("current");e.length&&(d.title_from=e.join(" "));for(e in{flat_output:1,contains_current:1,ol_root:1,ol_sub:1})a[e]&&(d[e]=[1]);h={container:"div",container_id:"",container_class:"",menu_class:"menu-widget",widget_class:""};for(e in h)a[e]!==h[e]&&(d[e]=a[e]);h={wrap_link:"before",wrap_link_text:"link_before"};for(e in h)(b=a[h[e]].toString().match(/^<(\w+)/))&&
17
+ b[1]&&(d[e]=b[1]);h=[];for(e in d)h.push(f.isArray(d[e])?e+"="+d[e][0]:e+'="'+d[e]+'"');n.getDialog(c).find("code").text("[custom_menu_wizard "+h.join(" ")+"]")},show:function(c,a,d){a=a||f(this).closest("form");d=d||n.getSettings(a);c=n.getDialog(a);var h=c.find(".cmw-demo-themenu"),b=h.find(".picked"),e="",m="",k=0,g=c.find(".cmw-demo-theoutput-wrap").empty(),p=["menu-widget"],q={};if(b.length&&g.length){0<d.filter&&d.title_from_parent&&(m=h.find(".the-parent").children("a").text()||"");!m&&d.title_from_current&&
18
+ (m=h.find(".current-menu-item").text()||"");m||d.hide_title||(m=d.title||"");for(b.each(function(a){var b=f(this),c=b.data(),h=c.trace?c.trace.toString().split(","):[];a=c.itemid.toString();var g=1,b=b.children("a");if(!d.flat_output)for(q[a]=1,a=0;a<h.length;a++)q[h[a]]&&g++;if(k)if(g>k)e+=d.ol_sub?"<ol>":"<ul>";else{for(;k>g;)--k,e+="</li>"+(d.ol_sub?"</ol>":"</ul>");e+="</li>"}e+='<li class="cmw-level-'+g+(c.included||"")+'"><a href="#'+b.data("indx")+'">'+b.text()+"</a>";k=g});1<k;)--k,e+="</li>"+
19
+ (d.ol_sub?"</ol>":"</ul>");e+="</li>";p.push(c.find(".cmw-demo-fallback").data("fellback"));e=(d.ol_root?"<ol":"<ul")+' class="'+f.trim(p.join(" "))+'">'+e+(d.ol_root?"</ol>":"</ul>");g.html(e);m&&g.prepend("<h3>"+m+"</h3>");g.find("li").filter(function(){return!!f(this).children("ul, ol").length}).addClass("cmw-has-submenu")}n.shortcode(a,d)},update:function(c){var a=f(this).closest("form"),d=n.getDialog(a),h,b,e,m,k,g,p,q,r,l,s;if(d.length&&d.dialog("isOpen")){f(c.target).hasClass("widget-custom-menu-wizard-selectmenu")&&
20
+ n.createMenu(d,a);b=n.getSettings(a);e=b.include_parent;m=b.include_parent_siblings;k=d.find(".cmw-demo-themenu");h=k.data().maxLevel;p=k.find(".current-menu-item").closest("li");q=p.length?p.data().level:-1;g=k.find("li").removeData("included").removeClass("the-parent");0>b.filter&&(g=g.filter(function(){var a=f(this).children("input"),c=-1<(b._items_sep+b._items+b._items_sep).indexOf(b._items_sep+a[0].value+b._items_sep);a.prop("checked",c);return c}),b._items||(g=f([])));g.length&&!p.length&&(b.contains_current||
21
+ 0<b.filter&&1>b.filter_item)&&(g=f([]));g.length&&0<b.filter&&(0<b.filter_item?l=g.filter("[data-itemid="+b.filter_item+"]"):b.filter_item?1===q&&b.fallback_no_ancestor?(l=p,e=e||b.fallback_include_parent,m=m||b.fallback_include_parent_siblings,r="cmw-fellback-to-current"):l=1===q?k:-1>b.filter_item?k.find(".current-menu-ancestor").eq(0).closest("li"):k.find(".current-menu-parent").closest("li"):p.find("li").length?l=p:b.fallback_no_children&&(l=k.find(".current-menu-parent").closest("li"),l.length||
22
+ (l=k),e=e||b.fallback_nc_include_parent,m=m||b.fallback_nc_include_parent_siblings,r="cmw-fellback-to-parent"));if(g.length)if(b.filter)l&&l.length?(s=b.depth_rel_current&&b.depth&&p.length&&l.has(p[0])?q-1+b.depth:b.depth?Math.max((l.data().level||0)+b.depth,b.start_level+b.depth-1):9999,g=l.find("li").filter(function(){var a=f(this).data().level;return a>=b.start_level&&a<=s})):0<b.filter&&(g=f([]));else for(s=b.depth_rel_current&&b.depth&&p.length&&q>=b.start_level?q+b.depth-1:b.depth?b.start_level+
23
+ b.depth-1:9999,q=1;q<=h;q++)if(q<b.start_level||q>s)g=g.not(".level-"+q);g.length&&0<b.filter&&l&&l.is("li")&&(m&&(g=g.add(l.siblings("li").data("included"," cmw-an-included-parent-sibling")),e=!0),b.include_ancestors&&(g=g.add(l.parentsUntil(k,"li").data("included"," cmw-an-included-ancestor")),e=!0),e&&(g=g.add(l.data("included"," cmw-the-included-parent"))));!g.length||!b.contains_current||p.length&&g.filter(p).length||(g=f([]));g.length&&l&&l.is("li")&&l.addClass("the-parent");r=g.length?r:"";
24
+ d.find(".cmw-demo-fallback").data("fellback",r).toggleClass("cmw-demo-fellback",!!r);k.toggleClass("cmw-demo-filteritems",0>b.filter).find(".picked").not(g.addClass("picked")).removeClass("picked");n.show.call(this,c,a,b)}}};f(document).on("click",".widget-custom-menu-wizard-collapsible-fieldset",function(){var c=f(this),a=c.find("input").eq(0),d=!a.prop("checked");a.length&&(a.prop("checked",d),c.find("div").toggleClass("cmw-collapsed-fieldset",d),c.next("div")[d?"slideUp":"slideDown"]());this.blur();
25
+ return!1}).on("change",".widget-custom-menu-wizard-listen",function(){var c=f(this.form),a=c.find(".widget-custom-menu-wizard-selectmenu"),d=c.find(".widget-custom-menu-wizard-showall").prop("checked"),h=c.find(".widget-custom-menu-wizard-showspecific").prop("checked"),c=c.find(".widget-custom-menu-wizard-childrenof"),b=parseInt(c.val(),10),e;a.is(this)&&(a=this.selectedIndex,c.find("optgroup").filter(function(){var b=f(this).data("cmwOptgroupIndex")===a;b||f(this).remove();return b}).length||(e=
26
+ f("#"+c.attr("id")+"_ignore").find("optgroup").eq(a).clone(),e.length&&(0<b&&(b=0,c.val(b)),e.find("option[selected]").removeAttr("selected").prop("selected",!1),c.append(e))));f.each({"":d||h,"-ss":h,"not-rp":d||h||0<=b,"not-ci":d||h||!!b},function(a,b){f(".widget-custom-menu-wizard-disableif"+a,this.form).toggleClass("cmw-colour-grey",b).find("input,select").prop("disabled",b)})}).on("change",".widget-custom-menu-wizard-onchange",n.update).on("click",".widget-custom-menu-wizard-toggle-assist",n.init).on("click",
27
+ ".widget-action, .widget-control-close",function(){f(this).closest("div.widget").find(".widget-custom-menu-wizard-onchange").each(function(){var c=f("#"+f(this).data().cmwDialogId);c.length&&c.dialog("isOpen")&&c.dialog("close")})});window.wpWidgets?(window.wpWidgets.fixLabels&&!window.wpWidgets._cmw_fixLabels&&(window.wpWidgets._cmw_fixLabels=window.wpWidgets.fixLabels,window.wpWidgets.fixLabels=function(c){c.find(".widget-custom-menu-wizard-selectmenu").trigger("change");window.wpWidgets._cmw_fixLabels(c)}),
28
+ window.wpWidgets.save&&!window.wpWidgets._cmw_save&&(window.wpWidgets._cmw_save=window.wpWidgets.save,window.wpWidgets.save=function(c,a,d,h){a&&c.find(".widget-custom-menu-wizard-onchange").each(function(){var a=f("#"+f(this).data().cmwDialogId);a.length&&(a.dialog("destroy"),a.remove())});window.wpWidgets._cmw_save(c,a,d,h)})):f(".widget-custom-menu-wizard-selectmenu").trigger("change")});
custom-menu-wizard.php CHANGED
@@ -2,16 +2,28 @@
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; filter for specific level(s), for children of a selected menu item, the current menu item, or its parent or root item
6
- * Version: 1.2.2
7
  * Author: Roger Barrett
8
  * Author URI: http://www.wizzud.com/
9
  * License: GPL2+
10
  */
11
 
12
  /*
 
 
 
 
 
 
 
 
 
 
 
13
  * v1.2.2 change log:
14
  * - bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
 
15
  * v1.2.1 change log:
16
  * - added some extra custom classes, when applicable : cmw-fellback-to-current & cmw-fellback-to-parent (on outer UL/OL) and cmw-the-included-parent, cmw-an-included-parent-sibling & cmw-an-included-ancestor (on relevant LIs)
17
  * - corrected 'show all from start level 1' processing so that custom classes get applied and 'Title from "Current" Item' works (regardless of filter settings)
@@ -36,7 +48,7 @@
36
  * - moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript
37
  */
38
 
39
- $Custom_Menu_Wizard_Widget_Version = '1.2.2';
40
 
41
  /**
42
  * registers the widget and adds the shortcode
@@ -51,10 +63,93 @@ add_action('widgets_init', 'custom_menu_wizard_register_widget');
51
  * enqueues script file for the widget admin
52
  */
53
  function custom_menu_wizard_widget_admin_script(){
54
- wp_enqueue_script('custom-menu-wizard-plugin-script', plugins_url('/custom-menu-wizard.js', __FILE__), array('jquery'), $Custom_Menu_Wizard_Widget_Version);
 
 
 
 
 
 
 
55
  }
56
  add_action('admin_print_scripts-widgets.php', 'custom_menu_wizard_widget_admin_script');
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  /*
59
  * Custom Menu Wizard Walker class
60
  * NB: Walker_Nav_Menu class is in wp-includes/nav-menu-template.php, and is itself an
@@ -104,7 +199,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
104
 
105
  $cmw =& $args->_custom_menu_wizard;
106
  //in $cmw (array) :
107
- // filter : true = kids of (current [root|parent] item or specific item)
108
  // filter_item : 0 = current item, -1 = parent of current (v1.1.0), -2 = root ancestor of current (v1.1.0); else a menu item id
109
  // flat_output : true = equivalent of $max_depth == -1
110
  // include_parent : true = include the filter_item menu item
@@ -114,12 +209,15 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
114
  // title_from_current : true = widget wants current item's title (v1.2.0)
115
  // start_level : integer, 1+
116
  // depth : integer, replacement for max_depth and also applied to 'flat' output
 
117
  // 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)
118
  // fallback_include_parent : true = if fallback_no_ancestor comes into play then force include_parent to true (v1.1.0)
119
  // fallback_include_parent_siblings : true = if fallback_no_ancestor comes into play then force include_parent_siblings to true (v1.1.0)
120
  // 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)
121
  // fallback_nc_include_parent : true = if fallback_no_children comes into play then force include_parent to true (v1.2.0)
122
  // fallback_nc_include_parent_siblings : true = if fallback_no_children comes into play then force include_parent_siblings to true (v1.2.0)
 
 
123
  //
124
  // _walker (array) : for anything that only the walker can determine and that needs to be communicated back to the widget instance
125
  //
@@ -128,11 +226,12 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
128
  //second item is [2] whether it's at root or subordinate to first item)
129
  $cmw['_walker']['fellback'] = false;
130
 
131
- $find_kids_of = $cmw['filter'];
 
132
  $find_current_item = $find_kids_of && empty( $cmw['filter_item'] );
133
  $find_current_parent = $find_kids_of && $cmw['filter_item'] == -1; //v1.1.0
134
  $find_current_root = $find_kids_of && $cmw['filter_item'] == -2; //v1.1.0
135
- $fallback_to_current_item = $cmw['fallback_no_ancestor'] && ( $find_current_parent || $find_current_root ); //v1.1.0
136
  //these could change depending on whether a fallback comes into play (v1.1.0)
137
  $include_parent = $cmw['include_parent'];
138
  $include_parent_siblings = $cmw['include_parent_siblings'];
@@ -140,229 +239,278 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
140
  $id_field = $this->db_fields['id']; //eg. = 'db_id'
141
  $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
142
 
143
- //start level applies to the *kids* of a find_kids_of search, not to the parent, so while we
144
- //are still looking for the parent, the start_level for a find_kids_of search is actually one
145
- //up from cmw['start_level']...
146
- $start_level = $find_kids_of ? $cmw['start_level'] - 1 : $cmw['start_level'];
 
 
 
 
 
 
 
 
147
 
148
- $keep_ids = array();
149
- $keep_items = array();
150
- $temp = array(0 => array('kids' => array()));
151
  foreach( $elements as $i=>$item ){
152
- if( empty( $item->$parent_field ) ){
153
- //set root level of menu, and no ancestors...
154
- $temp[ $item->$id_field ] = array(
155
- 'level' => 1,
156
- //this is an array of indexes into $elements...
157
- 'breadcrumb' => array( $i ),
158
- 'parent' => 0,
159
- 'kids' => array()
160
- );
161
- $temp[0]['kids'][] = $i;
162
- }elseif( isset( $temp[ $item->$parent_field ] ) ){
163
- //set one greater than parent's level, and ancestors are parent's ancestors plus the parent...
164
- $temp[ $item->$id_field ] = array(
165
- 'level' => $temp[ $item->$parent_field ]['level'] + 1,
166
- 'breadcrumb' => $temp[ $item->$parent_field ]['breadcrumb'],
167
- 'parent' => $item->$parent_field,
168
- 'kids' => array()
169
- );
170
- $temp[ $item->$id_field ]['breadcrumb'][] = $i;
171
- $temp[ $item->$parent_field ]['kids'][] = $i;
172
- }
173
- //if $temp[] hasn't been set then it's an orphan; in order to keep orphans, max_depth must be 0 (ie. unlimited)
174
  //note that if a child is an orphan then all descendants of that child are also considered to be orphans!
175
  //also note that orphans (in the original menu) are ignored by this widget!
176
-
177
- if( isset( $temp[ $item->$id_field ] ) ){
178
- //let's keep track of current menu item (as index into $elements)...
179
  if( $item->current ){
180
- $ci_index = $i;
181
  }
182
- //are we at or below the start level?...
183
- if( $temp[ $item->$id_field ]['level'] >= $start_level ){
184
- //are we still looking for a starting point?...
185
- if( empty( $keep_ids ) ){
186
- if( //...we're looking for unspecific items starting at this level...
187
- !$find_kids_of ||
188
- //...we're looking for current item, and this is it...
189
- ( $find_current_item && $item->current ) ||
190
- //...we're looking for current parent, and this is it...
191
- ( $find_current_parent && $item->current_item_parent ) ||
192
- //...we're looking for a current root ancestor, and this is one...
193
- ( $find_current_root && $item->current_item_ancestor ) ||
194
- //...we've got a fallback for a top-level current item with no ancestor...
195
- ( $fallback_to_current_item && $item->current && $temp[ $item->$id_field ]['level'] == 1 ) ||
196
- //...we're looking for a particular menu item, and this is it...
197
- ( $cmw['filter_item'] == $item->$id_field )
198
- ){
199
- //NOTE : at this point I'm *keeping* the id of the parent of a find_kids_of search, but not the actual item!
200
- $keep_ids[] = $item->$id_field;
201
- if( !$find_kids_of ){
202
- $keep_items[] = $item;
203
- }
204
- //v1.1.0 if this was the fallback_no_ancestor option, we may need to update $include_parent[_siblings]...
205
- if( $fallback_to_current_item && $item->current && $temp[ $item->$id_field ]['level'] == 1 ){
206
- $include_parent = $include_parent || $cmw['fallback_include_parent'];
207
- $include_parent_siblings = $include_parent_siblings || $cmw['fallback_include_parent_siblings'];
208
- $cmw['_walker']['fellback'] = 'to-current'; //v1.2.1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  }
210
- //depth, if set, kicks in at this point :
211
- // if doing a find_kids_of search then this level counts as 0, and the next level (the kids) counts as 1
212
- // otherwise, the current level counts as 1
213
- if( $cmw['depth'] > 0 ){
214
- $max_level = $temp[ $item->$id_field ]['level'] + $cmw['depth'] - ($find_kids_of ? 0 : 1);
215
- }else{
216
- //unlimited...
217
- $max_level = 9999;
218
  }
219
- //...and reset start level...
220
- $start_level = $cmw['start_level'];
221
  }
222
- //having found at least one, any more have to be:
223
- // - within max_depth of the first one found, and
224
- // - either it's an unspecific search, or we have the parent already
225
- }elseif( $temp[ $item->$id_field ]['level'] <= $max_level && ( !$find_kids_of || in_array( $item->$parent_field, $keep_ids ) ) ){
226
- $keep_ids[] = $item->$id_field;
227
- $keep_items[] = $item;
228
  }
229
  }
230
  }
231
- } //end foreach
232
-
233
- //v1.2.0 do we need to invoke the fallback for a childless 'children of current item'?...
234
- //note that this is slightly different to the no_ancestor fallback, in that we are treating it like a switch from current item
235
- //to current parent, *except* that here the fallback for not having an ancestor is to pretend there's a level-0 ancestor (above
236
- //root); obviously it (the level-0 ancestor) will no item of its own, which means the kids now have no (real) parent
237
- if( $find_current_item && $cmw['fallback_no_children'] && empty( $keep_items ) && !empty( $keep_ids ) ){
238
- //what are the options?
239
- // - the current item & its siblings
240
- // - ... optionally plus its parent (if there is one)
241
- // - ... optionally plus its parent (if there is one) and the parent's siblings
242
- //keep_ids[0] is the id of the current item; put the parent's kids into keep_items...
243
- foreach( $temp[ $temp[ $keep_ids[0] ]['parent'] ]['kids'] as $item ){
244
- //v1.2.2 note : each 'kid' is an index into $elements!
245
- $keep_items[] = $elements[ $item ];
246
- }
247
- $include_parent = $include_parent || $cmw['fallback_nc_include_parent'];
248
- $include_parent_siblings = $include_parent_siblings || $cmw['fallback_nc_include_parent_siblings'];
249
- $cmw['_walker']['fellback'] = 'to-parent'; //v1.2.1
250
  }
251
 
252
- unset( $keep_ids );
253
- if( !empty( $keep_items) ){
254
-
255
- //do we need to prepend parent or ancestors?...
256
- $breadcrumb = $temp[ $keep_items[0]->$id_field ]['breadcrumb'];
257
- //remove the last breadcrumb element, which is the item's own index...
258
- array_pop( $breadcrumb );
259
- //last element is now the parent (if there is one)
260
- $i = $j = count( $breadcrumb );
261
- //might we want the parent's title as the widget title?...
262
- if( $find_kids_of && $cmw['title_from_parent'] && $i > 0 ){
263
  $cmw['_walker']['parent_title'] = apply_filters(
264
  'the_title',
265
- $elements[ $breadcrumb[ $i - 1 ] ]->title,
266
- $elements[ $breadcrumb[ $i - 1 ] ]->ID
267
  );
268
  }
269
- //v1.2.0 might we want the current item's title as the widget title?...
270
- //NB: using $ci_index because it's quite possible that the current menu item is not in $keep_items
271
- if( !empty( $ci_index ) && $cmw['title_from_current'] ){
272
  $cmw['_walker']['current_title'] = apply_filters(
273
  'the_title',
274
- $elements[ $ci_index ]->title,
275
- $elements[ $ci_index ]->ID
276
  );
277
  }
278
 
279
- //if we have a parent and we also want all the parent siblings, then we need
280
- //to pop the parent off the bottom of temp and either append or prepend the
281
- //kids of the parent's parent onto keep_items...
282
- $paClass = 'cmw-the-included-parent';
283
- if( $find_kids_of && $i > 0 && $include_parent_siblings ){
284
- $siblings = $temp[ $keep_items[0]->$id_field ]['parent'];
285
- if( !empty( $siblings ) ){
286
- $siblings = $temp[ $temp[ $siblings ]['parent'] ]['kids'];
287
- }
288
- if( !empty( $siblings ) ){
289
- //remove (and store) parent...
290
- $j = array_pop( $breadcrumb );
291
- $parentAt = -1;
292
- //going backwards thru the array, prepend the parent and anything higher in the array than it...
293
- for($i = count($siblings) - 1; $i > -1; $i--){
294
- if( $parentAt < 0 && $siblings[ $i ] == $j ){
295
- $parentAt = $i;
296
- }
297
- if( $parentAt > -1 ){
298
- array_unshift( $keep_items, $elements[ $siblings[ $i ] ]);
299
- $keep_items[0]->classes[] = $parentAt == $i ? 'cmw-the-included-parent' : 'cmw-an-included-parent-sibling';
300
  }
301
  }
302
- //going forwards thru the array, append anything lower in the array than the parent...
303
- for($i = $parentAt + 1; $i < count($siblings); $i++){
304
- //anything after parent gets appended; parent and before get prepended...
305
- $j = count( $keep_items );
306
- array_push( $keep_items, $elements[ $siblings[ $i ] ]);
307
- $keep_items[$j]->classes[] = 'cmw-an-included-parent-sibling';
 
 
 
 
 
 
 
 
308
  }
309
- $i = $j = count( $breadcrumb );
310
- //don't include_parent now because we just have...
311
- $include_parent = false;
312
- $paClass = 'cmw-an-included-ancestor';
313
  }
314
- unset( $siblings );
315
- }
316
-
317
- if( $find_kids_of && $i > 0 ){
318
- if( $cmw['include_ancestors'] ){
319
- $j = 0; //everything in breadcrumb, from bottom to top
320
- }elseif( $include_parent ){
321
- --$j; //just the last item from breadcrumb
322
- }
323
- while( $i > $j ){
324
- array_unshift( $keep_items, $elements[ $breadcrumb[ --$i ] ]);
325
- $keep_items[0]->classes[] = $paClass;
326
- $paClass = 'cmw-an-included-ancestor';
327
- }
328
- }
329
- unset( $breadcrumb );
330
- }
331
-
332
- //for each item we're keeping, use the temp array to hold:
333
- // [0] => the level within the new structure (starting at 1), and
334
- // [1] => the number of kids each item has
335
- $temp = array();
336
- foreach( $keep_items as $item ){
337
- if( isset( $temp[ $item->$parent_field ] ) ){
338
- $temp[ $item->$id_field ] = array( $temp[ $item->$parent_field ][0] + 1, 0 );
339
- $temp[ $item->$parent_field ][1] += 1;
340
- }else{
341
- $temp[ $item->$id_field ] = array( 1, 0 );
342
  }
343
  }
344
 
345
- //transfer $keep back into $elements, resetting the index to increment from 1; also add
346
- //new classes to indicate level (starting at 1) and whether any item has kids
347
- //
348
- //note that we have already filtered out real orphans, but we may have introduced top-level
349
- //items that would appear to be orphans to the parent::walk() method, so we need to set all
350
- //the top-level items to appear as if they are root-level items...
351
  $elements = array();
352
  $i = 1;
353
- foreach( $keep_items as $item ){
354
- $item->classes[] = 'cmw-level-' . $temp[ $item->$id_field ][0];
355
- if( $temp[ $item->$id_field ][1] > 0 ){
356
- $item->classes[] = 'cmw-has-submenu';
357
- }
358
- if( $temp[ $item->$id_field ][0] == 1 ){
359
- //fake as root level item...
360
- $item->$parent_field = 0;
361
  }
362
- $elements[ $i++ ] = $item;
 
 
363
  }
364
- unset( $keep_items, $temp );
365
-
366
  //since we've done all the depth filtering, set max_depth to unlimited (unless 'flat' was requested!)...
367
  if( !$cmw['flat_output'] ){
368
  $max_depth = 0;
@@ -372,7 +520,26 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
372
  return empty( $elements ) ? '' : parent::walk( apply_filters( 'custom_menu_wizard_walker_items', $elements, $args ), $max_depth, $args );
373
  }
374
 
375
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
 
377
  /**
378
  * Custom Menu Wizard Widget class
@@ -381,7 +548,8 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
381
 
382
  var $_cmw_switches = array(
383
  'hide_title' => 0,
384
- 'filter' => 0, //v1.1.0 changed from integer
 
385
  'fallback_no_ancestor' => 0, //v1.1.0 added
386
  'fallback_include_parent' => 0, //v1.1.0 added
387
  'fallback_include_parent_siblings' => 0, //v1.1.0 added
@@ -407,6 +575,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
407
  );
408
  var $_cmw_strings = array(
409
  'title' => '',
 
410
  'container' => 'div',
411
  'container_id' => '',
412
  'container_class' => '',
@@ -421,6 +590,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
421
  );
422
  var $_cmw_integers = array(
423
  'depth' => 0,
 
424
  'filter_item' => -2, //v1.1.0 changed from 0
425
  'menu' => 0,
426
  'start_level' => 1
@@ -591,6 +761,9 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
591
  'flat_output' => $instance['flat_output'],
592
  'start_level' => $instance['start_level'],
593
  'depth' => $instance['depth'],
 
 
 
594
  //v1.2.1 this is for the walker's use...
595
  '_walker' => array()
596
  )
@@ -670,6 +843,18 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
670
  foreach( $this->_cmw_html as $k=>$v ){
671
  $instance[ $k ] = isset( $new_instance[ $k ] ) ? trim( $new_instance[ $k ] ) : $v;
672
  }
 
 
 
 
 
 
 
 
 
 
 
 
673
 
674
  return $instance;
675
  }
@@ -714,6 +899,16 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
714
  return;
715
  }
716
 
 
 
 
 
 
 
 
 
 
 
717
  /**
718
  * permanently visible section : Title (with Hide) and Menu
719
  */
@@ -730,6 +925,9 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
730
  </p>
731
 
732
  <p>
 
 
 
733
  <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
734
  <select id="<?php echo $this->get_field_id('menu'); ?>"
735
  class="widget-<?php echo $this->id_base; ?>-selectmenu widget-<?php echo $this->id_base; ?>-listen"
@@ -752,18 +950,22 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
752
  */
753
  $this->_open_a_field_section($instance, 'Filter', 'fs_filter');
754
  ?>
755
- <small class="alignright" style="line-height:1;"><a href="<?php echo plugins_url('/demo.html', __FILE__); ?>" target="_blank"><?php _e(' demo' ); ?></a></small>
756
  <p>
 
 
 
757
  <label>
758
- <input id="<?php echo $this->get_field_id('filter'); ?>_0" class="widget-<?php echo $this->id_base; ?>-listen"
759
- name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="0" <?php checked(!$instance['filter']); ?> />
 
760
  <?php _e('Show all'); ?></label>
761
  <br /><label>
762
  <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="widget-<?php echo $this->id_base; ?>-listen"
763
- name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="1" <?php checked($instance['filter']); ?> />
764
  <?php _e('Children of:'); ?></label>
765
- <select id="<?php echo $this->get_field_id('filter_item'); ?>" class="widget-<?php echo $this->id_base; ?>-listen"
766
- style="max-width:100%;" name="<?php echo $this->get_field_name('filter_item'); ?>">
 
767
  <option value="0" <?php selected( $instance['filter_item'], 0 ); ?>><?php _e('Current Item'); ?></option>
768
  <option value="-2" <?php selected( $instance['filter_item'], -2 ); ?>><?php _e('Current Root Item'); ?></option>
769
  <option value="-1" <?php selected( $instance['filter_item'], -1 ); ?>><?php _e('Current Parent Item'); ?></option>
@@ -777,34 +979,68 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
777
  foreach( $menus as $i=>$menu ){
778
  //as of v1.2.0 : no items, no optgroup!
779
  if( !empty( $menu->_items ) ){
780
- //v1.1.0 changed the indents from padding to hyphen-space (for IE!!!! grrrr...)
781
- $itemindents = array('0' => 0);
782
- $menuOptions[] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $i . '">';
783
  foreach( $menu->_items as $item ){
784
  //exclude orpans!
785
  if( isset($itemindents[ $item->menu_item_parent ])){
786
- $itemindents[ $item->ID ] = $itemindents[ $item->menu_item_parent ] + 1;
787
- $maxlevel = max( $maxlevel, $itemindents[ $item->ID ] );
788
- $menuOptions[] = '<option style="padding-left:0.75em;" value="' . $item->ID . '" ' .
 
 
 
 
 
 
 
 
 
789
  selected( $instance['filter_item'], $item->ID, false ) . '>' .
790
- str_repeat('- ', $itemindents[ $item->menu_item_parent ]) . $item->title . '</option>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
  }
 
 
 
 
792
  }
793
- $menuOptions[] = '</optgroup>';
794
  }
795
  }
796
  $menuOptions = implode("\n", $menuOptions);
797
  echo $menuOptions;
798
  ?>
799
  </select>
 
 
 
 
 
 
 
 
800
  <select id="<?php echo $this->get_field_id('filter_item_ignore'); ?>" disabled="disabled"
801
- style="display:none;position:absolute;left:-5000px;top:-5000px;"
802
- name="<?php echo $this->get_field_name('filter_item_ignore'); ?>">
803
  <?php echo $menuOptions; ?>
804
  </select>
805
  </p>
806
 
807
- <p>
808
  <label for="<?php echo $this->get_field_id('start_level'); ?>"><?php _e('Starting Level:'); ?></label>
809
  <select id="<?php echo $this->get_field_id('start_level'); ?>" name="<?php echo $this->get_field_name('start_level'); ?>">
810
  <?php
@@ -819,7 +1055,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
819
  <br /><small><em><?php _e('Level to start testing items for inclusion'); ?></em></small>
820
  </p>
821
 
822
- <p>
823
  <label for="<?php echo $this->get_field_id('depth'); ?>"><?php _e('For Depth:'); ?></label>
824
  <select id="<?php echo $this->get_field_id('depth'); ?>" name="<?php echo $this->get_field_name('depth'); ?>">
825
  <option value="0" <?php selected( $instance['depth'], 0 ); ?>><?php _e('unlimited'); ?></option>
@@ -832,7 +1068,12 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
832
  }
833
  ?>
834
  </select>
835
- <br /><small><em><?php _e('Relative to the first Filtered item found'); ?></em></small>
 
 
 
 
 
836
  </p>
837
  <?php $this->_close_a_field_section(); ?>
838
 
@@ -842,15 +1083,17 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
842
  */
843
  $this->_open_a_field_section($instance, 'Fallbacks', 'fs_fallbacks');
844
  ?>
845
- <small class="alignright" style="line-height:1;"><a href="<?php echo plugins_url('/demo.html', __FILE__); ?>" target="_blank"><?php _e(' demo' ); ?></a></small>
846
- <p class="clear widget-<?php echo $this->id_base; ?>-disableifnot-rp" style="line-height:1.3333;">
 
 
847
  <small><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Root / Parent Item</em>, and no ancestor exists' ); ?> :</strong></small>
848
  <br /><label>
849
  <input id="<?php echo $this->get_field_id('fallback_no_ancestor'); ?>"
850
  name="<?php echo $this->get_field_name('fallback_no_ancestor'); ?>" type="checkbox" value="1"
851
  <?php checked($instance['fallback_no_ancestor']); ?> />
852
  <?php _e('Switch to Current Item, and'); ?></label>
853
- <br /><label style="padding-left:1em;">
854
  <input id="<?php echo $this->get_field_id('fallback_include_parent'); ?>"
855
  name="<?php echo $this->get_field_name('fallback_include_parent'); ?>" type="checkbox" value="1"
856
  <?php checked($instance['fallback_include_parent']); ?> />
@@ -859,17 +1102,17 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
859
  <input id="<?php echo $this->get_field_id('fallback_include_parent_siblings'); ?>"
860
  name="<?php echo $this->get_field_name('fallback_include_parent_siblings'); ?>" type="checkbox" value="1"
861
  <?php checked($instance['fallback_include_parent_siblings']); ?> />
862
- <?php _e('&amp; its Siblings'); ?></label>
863
  </p>
864
 
865
- <p class="widget-<?php echo $this->id_base; ?>-disableifnot-ci" style="line-height:1.3333;">
866
  <small><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Item</em>, and current item has no children' ); ?> :</strong></small>
867
  <br /><label>
868
  <input id="<?php echo $this->get_field_id('fallback_no_children'); ?>"
869
  name="<?php echo $this->get_field_name('fallback_no_children'); ?>" type="checkbox" value="1"
870
  <?php checked($instance['fallback_no_children']); ?> />
871
  <?php _e('Switch to Current Parent Item, and'); ?></label>
872
- <br /><label style="padding-left:1em;">
873
  <input id="<?php echo $this->get_field_id('fallback_nc_include_parent'); ?>"
874
  name="<?php echo $this->get_field_name('fallback_nc_include_parent'); ?>" type="checkbox" value="1"
875
  <?php checked($instance['fallback_nc_include_parent']); ?> />
@@ -878,7 +1121,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
878
  <input id="<?php echo $this->get_field_id('fallback_nc_include_parent_siblings'); ?>"
879
  name="<?php echo $this->get_field_name('fallback_nc_include_parent_siblings'); ?>" type="checkbox" value="1"
880
  <?php checked($instance['fallback_nc_include_parent_siblings']); ?> />
881
- <?php _e('&amp; its Siblings'); ?></label>
882
  </p>
883
 
884
 
@@ -890,8 +1133,10 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
890
  */
891
  $this->_open_a_field_section($instance, 'Output', 'fs_output');
892
  ?>
893
- <small class="alignright" style="line-height:1;"><a href="<?php echo plugins_url('/demo.html', __FILE__); ?>" target="_blank"><?php _e(' demo' ); ?></a></small>
894
  <p>
 
 
 
895
  <label>
896
  <input id="<?php echo $this->get_field_id('flat_output'); ?>_0" name="<?php echo $this->get_field_name('flat_output'); ?>"
897
  type="radio" value="0" <?php checked(!$instance['flat_output']); ?> />
@@ -902,6 +1147,15 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
902
  <?php _e('Flat'); ?></label>
903
  </p>
904
 
 
 
 
 
 
 
 
 
 
905
  <p class="widget-<?php echo $this->id_base; ?>-disableif">
906
  <label>
907
  <input id="<?php echo $this->get_field_id('include_parent'); ?>"
@@ -912,7 +1166,7 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
912
  <input id="<?php echo $this->get_field_id('include_parent_siblings'); ?>"
913
  name="<?php echo $this->get_field_name('include_parent_siblings'); ?>" type="checkbox"
914
  value="1" <?php checked($instance['include_parent_siblings']); ?> />
915
- <?php _e('&amp; its Siblings'); ?></label>
916
  <br /><label>
917
  <input id="<?php echo $this->get_field_id('include_ancestors'); ?>"
918
  name="<?php echo $this->get_field_name('include_ancestors'); ?>" type="checkbox"
@@ -1045,6 +1299,8 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
1045
  </p>
1046
  <?php $this->_close_a_field_section(); ?>
1047
 
 
 
1048
  <?php
1049
  } //end form()
1050
 
@@ -1057,13 +1313,13 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
1057
  */
1058
  function _open_a_field_section( &$instance, $text, $fname ){
1059
  ?>
1060
- <div class="stuffbox widget-<?php echo $this->id_base; ?>-collapsible-fieldset" title="<?php _e( 'Click to show/hide' ); ?>" style="margin:0 0 0.5em;cursor:pointer;">
1061
  <input id="<?php echo $this->get_field_id($fname); ?>" class="hidden-field" name="<?php echo $this->get_field_name($fname); ?>"
1062
  type="checkbox" value="1" <?php checked( $instance[$fname] ); ?> />
1063
- <div style="background:transparent url(images/arrows.png) no-repeat 0 <?php echo $instance[$fname] ? '0' : '-36px'; ?>;height:16px; width:16px;float:right;outline:0 none;"></div>
1064
- <h3 style="font-size:1em;margin:0;padding:2px 0.5em;"><?php _e( $text ); ?></h3>
1065
  </div>
1066
- <div class="hide-if-js"<?php echo $instance[$fname] ? '' : ' style="display:block;"'; ?>>
1067
  <?php
1068
  } //end _open_a_field_section()
1069
 
@@ -1114,22 +1370,25 @@ function custom_menu_wizard_widget_shortcode($atts, $content, $tag){
1114
  $instance = shortcode_atts( array(
1115
  'title' => '',
1116
  'menu' => 0, // menu id, slug or name
1117
- //determines filter & filter_item...
1118
- 'children_of' => '', // empty = show all; menu item id or title (caseless), or current|current-item|parent|current-parent|root|current-ancestor
 
1119
  'start_level' => 1,
1120
  'depth' => 0, // 0 = unlimited
1121
  //only if children_of is (parent|current-parent|root|current-ancestor); determines fallback_no_ancestor, fallback_include_parent & fallback_include_parent_siblings...
1122
  'fallback_parent' => 0, // 1 = use current-item; 'parent' = *and* include parent, 'siblings' = *and* include both parent and its siblings
1123
  //only if children_of is (current|current-item); determines fallback_no_children, fallback_nc_include_parent & fallback_nc_include_parent_siblings...
1124
  'fallback_current' => 0, // 1 = use current-parent; 'parent' = *and* include parent (if available), 'siblings' = *and* include both parent (if available) and its siblings
1125
- //switches...
1126
  'flat_output' => 0,
 
1127
  //determines include_parent, include_parent_siblings & include_ancestors...
1128
  'include' =>'', //comma|space|hyphen separated list of 'parent', 'siblings', 'ancestors'
1129
  'ol_root' => 0,
1130
  'ol_sub' => 0,
1131
  //determines title_from_parent & title_from_current...
1132
  'title_from' => '', //comma|space|hyphen separated list of 'parent', 'current'
 
1133
  //strings...
1134
  'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
1135
  'container_id' => '',
@@ -1167,36 +1426,40 @@ function custom_menu_wizard_widget_shortcode($atts, $content, $tag){
1167
  unset( $menus );
1168
 
1169
  if( $ok ){
1170
- //children_of => filter & filter_item...
1171
- if( empty( $instance['children_of'] ) ){
1172
- $instance['children_of'] = '';
1173
- }
1174
  $instance['filter'] = $instance['filter_item'] = 0;
1175
- switch( $instance['children_of'] ){
1176
- case '':
1177
- break;
1178
- case 'root': case 'current-ancestor':
1179
- --$instance['filter_item']; //ends up as -2
1180
- case 'parent': case 'current-parent':
1181
- --$instance['filter_item']; //ends up as -1
1182
- case 'current': case 'current-item':
1183
- $instance['filter'] = 1;
1184
- break;
1185
- default:
1186
- $instance['filter'] = 1;
1187
- $instance['filter_item'] = strtolower( $instance['children_of'] );
1188
- }
1189
- unset( $instance['children_of'] );
1190
- //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...
1191
- if( !is_numeric( $instance['filter_item'] ) ){
1192
- foreach( $items as $item ){
1193
- $ok = strtolower( $item->title ) == $instance['filter_item'];
1194
- if( $ok ){
1195
- $instance['filter_item'] = $item->ID;
1196
  break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1197
  }
1198
  }
 
 
1199
  }
 
1200
  }
1201
 
1202
  if( $ok ){
@@ -1269,6 +1532,7 @@ function custom_menu_wizard_widget_shortcode($atts, $content, $tag){
1269
  $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
1270
  $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
1271
  }
 
1272
  //handle widget_class here because we have full control over $before_widget...
1273
  $before_widget_class = array(
1274
  'widget_custom_menu_wizard',
2
  /*
3
  * Plugin Name: Custom Menu Wizard
4
  * Plugin URI: http://wordpress.org/plugins/custom-menu-wizard/
5
+ * Description: Show any part of a custom menu in a Widget, or in content using a Shortcode. Customise the output with extra classes or html; filter by current menu item or a specific item; set a depth, show the parent(s), change the list style, etc. Use the included emulator to assist with the filter settings.
6
+ * Version: 2.0.0
7
  * Author: Roger Barrett
8
  * Author URI: http://www.wizzud.com/
9
  * License: GPL2+
10
  */
11
 
12
  /*
13
+ * v2.0.0 change log:
14
+ * - Possible Breaker! : start level has been made consistent for showall and kids-off filters. Previously, a kids-of filter on an item at level 2,
15
+ * with start level set to 4, would return no output because the immediate kids (at level 3) were outside the start level; now, there will
16
+ * be output, starting with the grand-kids (at level 4)
17
+ * - Possible Breaker! : there is now an artificial "root" above the top level menu items, which means that a parent or root children-of filter will no
18
+ * longer fail for a top-level current menu item; this may well obviate the need for the current item fallback, but it has been left in for
19
+ * backward compatibility.
20
+ * - added option for calculating depth relative to current menu item
21
+ * - added option allowing list output to be dependent on current menu item being present somewhere in the list
22
+ * - refactored the code
23
+ *
24
  * v1.2.2 change log:
25
  * - bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
26
+ *
27
  * v1.2.1 change log:
28
  * - added some extra custom classes, when applicable : cmw-fellback-to-current & cmw-fellback-to-parent (on outer UL/OL) and cmw-the-included-parent, cmw-an-included-parent-sibling & cmw-an-included-ancestor (on relevant LIs)
29
  * - corrected 'show all from start level 1' processing so that custom classes get applied and 'Title from "Current" Item' works (regardless of filter settings)
48
  * - moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript
49
  */
50
 
51
+ $Custom_Menu_Wizard_Widget_Version = '2.0.0';
52
 
53
  /**
54
  * registers the widget and adds the shortcode
63
  * enqueues script file for the widget admin
64
  */
65
  function custom_menu_wizard_widget_admin_script(){
66
+ global $wp_scripts;
67
+ wp_enqueue_style('custom-menu-wizard-plugin-styles', plugins_url('/custom-menu-wizard.css', __FILE__), array(), $Custom_Menu_Wizard_Widget_Version);
68
+ wp_enqueue_script('custom-menu-wizard-plugin-script', plugins_url('/custom-menu-wizard.min.js', __FILE__), array('jquery-ui-dialog'), $Custom_Menu_Wizard_Widget_Version);
69
+ if( !wp_style_is( 'jquery-ui', 'registered' ) ) {
70
+ $jquery_ui_version = isset( $wp_scripts->registered['jquery-ui-core']->ver ) ? $wp_scripts->registered['jquery-ui-core']->ver : '1.9.2';
71
+ wp_register_style( 'jquery-ui', 'http://ajax.googleapis.com/ajax/libs/jqueryui/' . $jquery_ui_version . '/themes/smoothness/jquery-ui.css' );
72
+ }
73
+ wp_enqueue_style( 'jquery-ui' );
74
  }
75
  add_action('admin_print_scripts-widgets.php', 'custom_menu_wizard_widget_admin_script');
76
 
77
+ /**
78
+ * puts the contents of the Upgrade Notice (from readme.txt) for a new version under the widget's entry in Appearances - Widgets
79
+ */
80
+ function custom_menu_wizard_update_message($plugin_data, $r){
81
+ global $Custom_Menu_Wizard_Widget_Version;
82
+ $readme = wp_remote_fopen( 'http://plugins.svn.wordpress.org/custom-menu-wizard/trunk/readme.txt' );
83
+ // $readme = file_get_contents( plugins_url( '/readme.txt', __FILE__ ) );
84
+ if(!empty($readme)){
85
+ //grab the Upgrade Notice section from the readme...
86
+ if(preg_match('/== upgrade notice ==(.+)(==|$)/ims', $readme, $match) > 0){
87
+ $readme = $match[1];
88
+ }else{
89
+ $readme = '';
90
+ }
91
+ }
92
+ if(!empty($readme)){
93
+ //if there's a heading for the currently installed version, take anything above it...
94
+ if(($match = strpos($readme, "= $Custom_Menu_Wizard_Widget_Version =")) !== false){
95
+ $readme = substr($readme, 0, $match);
96
+ }
97
+ //trim it...
98
+ $readme = trim(str_replace("\r", '', $readme), " \n");
99
+ }
100
+ if(!empty($readme)){
101
+ $readme = preg_replace(
102
+ array(
103
+ '/^= (\d+\.\d+\.\d+.*) =/m', // => /P H4 Upgrade Notice ... /H4 P
104
+ '/(__|\*\*)!\s?([^*]+!)\1/', // => STRONG red ... /STRONG
105
+ '/(__|\*\*)([^*]+)\1/', // => STRONG ... /STRONG
106
+ '/\*([^*]+)\*/', // => EM ... /EM
107
+ '/`([^`]+)`/', // => CODE ... /CODE
108
+ '/\[([^\]]+)\]\(([^\)]+)\)/', // => A ... /A
109
+ '/\n\+\s+/', // => SPAN indented bullet
110
+ '/\n[ \n]*/', // => BR
111
+ //remove breaks that immediately follow/precede a paragraph start/end tag...
112
+ '/(<p[^>]*>)<br\s\/>/', // => P
113
+ '/<br\s\/>(<\/p>)/' // => /P
114
+ ),
115
+ array(
116
+ '</p><h4 style="margin:0;"><em>' . __("Upgrade Notice") . ' $1</em></h4><p style="margin:0.25em 1em;">',
117
+ '<strong style="color:#cc0000;">$2</strong>',
118
+ '<strong>$2</strong>',
119
+ '<em>$1</em>',
120
+ '<code>$1</code>',
121
+ '<a href="$2">$1</a>',
122
+ "\n" . '&nbsp;<span style="margin:0 0.5em;">&bull;</span>',
123
+ '<br />',
124
+ '$1',
125
+ '$1'
126
+ ),
127
+ //convert html chars...
128
+ esc_html($readme)
129
+ );
130
+ //remove the *first* P end tag...
131
+ $readme = preg_replace('/<\/p>/', '', $readme . '</p>', 1);
132
+ }
133
+ //show if not empty...
134
+ if(!empty($readme)){
135
+ ?>
136
+ <div style="font-weight:normal;background-color:#fff0c0;border:1px solid #ff9933;border-radius:0.5em;margin:0.5em;">
137
+ <div style="margin:0.5em 0.5em 0.5em 1em;max-height:12em;overflow:auto;">
138
+ <?php echo $readme; ?>
139
+ </div>
140
+ </div>
141
+ <?php
142
+ }
143
+ }
144
+ /**
145
+ * if the plugin has an update...
146
+ */
147
+ function custom_menu_wizard_admin_menu(){
148
+ add_action('in_plugin_update_message-' . plugin_basename(__FILE__), 'custom_menu_wizard_update_message', 10, 2);
149
+ }
150
+ add_action('admin_menu', 'custom_menu_wizard_admin_menu');
151
+
152
+
153
  /*
154
  * Custom Menu Wizard Walker class
155
  * NB: Walker_Nav_Menu class is in wp-includes/nav-menu-template.php, and is itself an
199
 
200
  $cmw =& $args->_custom_menu_wizard;
201
  //in $cmw (array) :
202
+ // filter : 0 = show all; 1 = kids of (current [root|parent] item or specific item); -1 = specific items (v2.0.0)
203
  // filter_item : 0 = current item, -1 = parent of current (v1.1.0), -2 = root ancestor of current (v1.1.0); else a menu item id
204
  // flat_output : true = equivalent of $max_depth == -1
205
  // include_parent : true = include the filter_item menu item
209
  // title_from_current : true = widget wants current item's title (v1.2.0)
210
  // start_level : integer, 1+
211
  // depth : integer, replacement for max_depth and also applied to 'flat' output
212
+ // depth_rel_current : true = changes depth calc from "relative to first filtered item found" to "relative to current item's level" (if current item is found below level/branch) (v2.0.0)
213
  // 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)
214
  // fallback_include_parent : true = if fallback_no_ancestor comes into play then force include_parent to true (v1.1.0)
215
  // fallback_include_parent_siblings : true = if fallback_no_ancestor comes into play then force include_parent_siblings to true (v1.1.0)
216
  // 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)
217
  // fallback_nc_include_parent : true = if fallback_no_children comes into play then force include_parent to true (v1.2.0)
218
  // fallback_nc_include_parent_siblings : true = if fallback_no_children comes into play then force include_parent_siblings to true (v1.2.0)
219
+ // contains_current : true = the output - both Filtered and any Included items - must contain the current menu item (v2.0.0)
220
+ // items : comma-or-space delimited list of item ids
221
  //
222
  // _walker (array) : for anything that only the walker can determine and that needs to be communicated back to the widget instance
223
  //
226
  //second item is [2] whether it's at root or subordinate to first item)
227
  $cmw['_walker']['fellback'] = false;
228
 
229
+ $find_kids_of = $cmw['filter'] > 0;
230
+ $find_specific_items = $cmw['filter'] < 1; //v2.0.0
231
  $find_current_item = $find_kids_of && empty( $cmw['filter_item'] );
232
  $find_current_parent = $find_kids_of && $cmw['filter_item'] == -1; //v1.1.0
233
  $find_current_root = $find_kids_of && $cmw['filter_item'] == -2; //v1.1.0
234
+ $depth_rel_current = $cmw['depth_rel_current'] && $cmw['depth'] > 0; //v2.0.0
235
  //these could change depending on whether a fallback comes into play (v1.1.0)
236
  $include_parent = $cmw['include_parent'];
237
  $include_parent_siblings = $cmw['include_parent_siblings'];
239
  $id_field = $this->db_fields['id']; //eg. = 'db_id'
240
  $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
241
 
242
+ $structure = array(0 => array(
243
+ 'level' => 0,
244
+ 'ancestors' => array(),
245
+ 'kids' => array(),
246
+ 'element' => -1,
247
+ 'keepCount' => 0
248
+ ));
249
+ $levels = array(
250
+ array() //for the artificial level-0
251
+ );
252
+ $allLevels = 9999;
253
+ $startWithKidsOf = -1;
254
 
 
 
 
255
  foreach( $elements as $i=>$item ){
256
+ $itemID = $item->$id_field;
257
+ $parentID = empty( $item->$parent_field ) ? 0 : $item->$parent_field;
258
+
259
+
260
+ //if $structure[] hasn't been set then it's an orphan; in order to keep orphans, max_depth must be 0 (ie. unlimited)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  //note that if a child is an orphan then all descendants of that child are also considered to be orphans!
262
  //also note that orphans (in the original menu) are ignored by this widget!
263
+ if( isset( $structure[ $parentID ] ) ){
264
+ //keep track of current item (as a structure key)...
 
265
  if( $item->current ){
266
+ $currentItem = $itemID;
267
  }
268
+ //this level...
269
+ $thisLevel = $structure[ $parentID ]['level'] + 1;
270
+ if( empty( $levels[ $thisLevel ] ) ){
271
+ $levels[ $thisLevel ] = array();
272
+ }
273
+ $levels[ $thisLevel ][] = $itemID;
274
+
275
+ $structure[ $itemID ] = array(
276
+ //level within structure...
277
+ 'level' => $thisLevel,
278
+ //ancestors (from the artificial level-0, right down to parent, inclusive) within structure...
279
+ 'ancestors' => $structure[ $parentID ]['ancestors'],
280
+ //kids within structure, ie array of itemID's...
281
+ 'kids' => array(),
282
+ //item within elements...
283
+ 'element' => $i,
284
+ //assume no matches...
285
+ 'keep' => false
286
+ );
287
+ $structure[ $itemID ]['ancestors'][] = $parentID;
288
+ $structure[ $parentID ]['kids'][] = $itemID;
289
+ }
290
+ } //end foreach
291
+
292
+ //no point doing much more if we need the current item and we haven't found it, or if we're looking for specific items with none given...
293
+ $continue = true;
294
+ if( empty( $currentItem ) && ( $find_current_item || $find_current_parent || $find_current_root || $cmw['contains_current'] ) ){
295
+ $continue = false;
296
+ }elseif( $find_specific_items && empty( $cmw['items'] ) ){
297
+ $continue = false;
298
+ }
299
+
300
+ // IMPORTANT : as of v2.0.0, start level has been rationalised so that it acts the same across all filters (except for specific items!).
301
+ // Previously ...
302
+ // start level for a show-all filter literally started at the specified level and reported all levels until depth was reached.
303
+ // however, start level for a kids-of filter specified the level that the *immediate* kids of the selected filter had to be at
304
+ // or below. That was consistent for a specific item, current-item and current-parent filter, but for a current-root filter what
305
+ // it actually did was test the current item against the start level, not the current item's root ancestor! Inconsistent!
306
+ // But regardless of the current-root filter's use of start level, there was still the inconsistency between show-all and
307
+ // kids-of usage.
308
+ // Now (as of v2.0.0) ...
309
+ // start level and depth have been changed to definitively be secondary filters to the show-all & kids-of primary filter.
310
+ // The primary filter - show-all, or a kids-of - will provide the initial set of items, and the secondary - start level & depth -
311
+ // will further refine that set, with start level being an absolute, and depth still being relative to the first item found.
312
+ // The sole exception to this is when Depth Relative to Current Menu Item is set, which modifies the calculation of depth (only)
313
+ // such that it becomes relative to the level at which the current menu item can be found (but only if it can be found at or
314
+ // below start level).
315
+ // The effects of this change are that previously, filtering for kids of an item that was at level 2, with a start level of 4,
316
+ // would fail to return any items because the immediate kids (at level 3) were outside the start level. Now, the returned items
317
+ // will begin with the grand-kids (ie. those at level 4).
318
+ // Note that neither start level nor depth are applicable to a specific items filter (also new at v2.0.0)!
319
+
320
+ //the kids-of filters...
321
+ if( $continue && $find_kids_of ){
322
+ //specific item...
323
+ if( $cmw['filter_item'] > 0 && isset( $structure[ $cmw['filter_item'] ] ) && !empty( $structure[ $cmw['filter_item'] ]['kids'] ) ){
324
+ $startWithKidsOf = $cmw['filter_item'];
325
+ }
326
+ if( $find_current_item ){
327
+ if( !empty( $structure[ $currentItem ]['kids'] ) ){
328
+ $startWithKidsOf = $currentItem;
329
+ }elseif( $cmw['fallback_no_children'] ){
330
+ //no kids, and fallback to current parent is set...
331
+ //note that there is no "double fallback", so current parent "can" be the artifical zero element (level-0) *if*
332
+ // the current item is a singleton( ie. no kids & no ancestors)!
333
+ $ancestor = array_slice( $structure[ $currentItem ]['ancestors'], -1, 1 );
334
+ $startWithKidsOf = $ancestor[0]; //can be zero!
335
+ $include_parent = $include_parent || $cmw['fallback_nc_include_parent'];
336
+ $include_parent_siblings = $include_parent_siblings || $cmw['fallback_nc_include_parent_siblings'];
337
+ $cmw['_walker']['fellback'] = 'to-parent';
338
+ }
339
+ }elseif( $find_current_parent || $find_current_root ){
340
+ //as of v2.0.0 the fallback to current item - for current menu items at the top level - is deprecated, but
341
+ //retained for a while to maintain backward compatibility
342
+ //if no parent : fall back to current item (if set)...
343
+ if( $structure[ $currentItem ]['level'] == 1 && $cmw['fallback_no_ancestor'] ){
344
+ $startWithKidsOf = $currentItem;
345
+ $include_parent = $include_parent || $cmw['fallback_include_parent'];
346
+ $include_parent_siblings = $include_parent_siblings || $cmw['fallback_include_parent_siblings'];
347
+ $cmw['_walker']['fellback'] = 'to-current';
348
+ }else{
349
+ //as of v2.0.0, the artificial level-0 counts as parent of a top-level current menu item...
350
+ if( $find_current_parent ){
351
+ $ancestor = -1;
352
+ }elseif( $structure[ $currentItem ]['level'] > 1 ){
353
+ $ancestor = 1;
354
+ }else{
355
+ $ancestor = 0;
356
+ }
357
+ $ancestor = array_slice( $structure[ $currentItem ]['ancestors'], $ancestor, 1 );
358
+ if( !empty( $ancestor ) ){
359
+ $startWithKidsOf = $ancestor[0]; //as of v2.0.0, this can now be zero!
360
+ }
361
+ }
362
+ }
363
+ }
364
+
365
+ if( $continue ){
366
+ //right, let's set the keep flags
367
+ //for specific items, go straight in on the item id (start level and depth do not apply here)...
368
+ if( $find_specific_items ){
369
+ foreach( preg_split('/[,\s]+/', $cmw['items'] ) as $itemID ){
370
+ if( isset( $structure[ $itemID ] ) ){
371
+ $structure[ $itemID ]['keep'] = true;
372
+ $structure[0]['keepCount']++;
373
+ }
374
+ }
375
+ //for show-all filter, just use the levels...
376
+ }elseif( !$find_kids_of ){
377
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
378
+ if( $depth_rel_current && !empty( $currentItem ) && $structure[ $currentItem ]['level'] >= $cmw['start_level'] ){
379
+ $bottomLevel = $structure[ $currentItem ]['level'] + $cmw['depth'] - 1;
380
+ }else{
381
+ $bottomLevel = $cmw['depth'] > 0 ? $cmw['start_level'] + $cmw['depth'] - 1 : $allLevels;
382
+ }
383
+ for( $i = $cmw['start_level']; isset( $levels[ $i ] ) && $i <= $bottomLevel; $i++ ){
384
+ foreach( $levels[ $i ] as $itemID ){
385
+ $structure[ $itemID ]['keep'] = true;
386
+ $structure[0]['keepCount']++;
387
+ }
388
+ }
389
+ //for kids-of filters, run a recursive through the structure's kids...
390
+ }elseif( $startWithKidsOf > -1 ){
391
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
392
+ //NB the in_array() of ancestors prevents depth_rel_current when startWithKidsOf == currentItem
393
+ if( $depth_rel_current && !empty( $currentItem ) && $structure[ $currentItem ]['level'] >= $cmw['start_level']
394
+ && in_array( $startWithKidsOf, $structure[ $currentItem ]['ancestors'] ) ){
395
+ $bottomLevel = $structure[ $currentItem ]['level'] - 1 + $cmw['depth'];
396
+ }else{
397
+ $bottomLevel = $cmw['depth'] > 0
398
+ ? max( $structure[ $startWithKidsOf ]['level'] + $cmw['depth'], $cmw['start_level'] + $cmw['depth'] - 1 )
399
+ : $allLevels;
400
+ }
401
+ //$structure[0]['keepCount'] gets incremented in this recursive method...
402
+ $this->_cmw_set_keep_kids( $structure, $startWithKidsOf, $cmw['start_level'], $bottomLevel );
403
+ }
404
+
405
+ if( $structure[0]['keepCount'] > 0 ){
406
+ //we have some items! we now may need to set some more keep flags, depending on the include settings...
407
+
408
+ //do we need to include parent, parent siblings, and/or ancestors?...
409
+ //NB these are not restricted by start_level!
410
+ if( $find_kids_of && $startWithKidsOf > 0 ){
411
+ if( $include_parent ){
412
+ $structure[ $startWithKidsOf ]['keep'] = true;
413
+ //add the class directly to the elements item...
414
+ $elements[ $structure[ $startWithKidsOf ]['element'] ]->classes[] = 'cmw-the-included-parent';
415
+ }
416
+ if( $include_parent_siblings ){
417
+ $ancestor = array_slice( $structure[ $startWithKidsOf ]['ancestors'], -1, 1);
418
+ foreach($structure[ $ancestor[0] ]['kids'] as $itemID ){
419
+ //may have already been kept by include_parent...
420
+ if( !$structure[ $itemID ]['keep'] ){
421
+ $structure[ $itemID ]['keep'] = true;
422
+ //add the class directly to the elements item...
423
+ $elements[ $structure[ $itemID ]['element'] ]->classes[] = 'cmw-an-included-parent-sibling';
424
  }
425
+ }
426
+ }
427
+ if( $cmw['include_ancestors'] ){
428
+ foreach( $structure[ $startWithKidsOf ]['ancestors'] as $itemID ){
429
+ if( $itemID > 0 && !$structure[ $itemID ]['keep'] ){
430
+ $structure[ $itemID ]['keep'] = true;
431
+ //add the class directly to the elements item...
432
+ $elements[ $structure[ $itemID ]['element'] ]->classes[] = 'cmw-an-included-parent-ancestor';
433
  }
 
 
434
  }
 
 
 
 
 
 
435
  }
436
  }
437
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  }
439
 
440
+ $substructure = array();
441
+ //check that (a) we have items, and (b) if we must have current menu item, we've got it...
442
+ if( $structure[0]['keepCount'] > 0 && ( !$cmw['contains_current'] || $structure[ $currentItem ]['keep'] ) ){
443
+
444
+ //might we want the parent's title as the widget title?...
445
+ if( $find_kids_of && $cmw['title_from_parent'] && $startWithKidsOf > 0 ){
 
 
 
 
 
446
  $cmw['_walker']['parent_title'] = apply_filters(
447
  'the_title',
448
+ $elements[ $structure[ $startWithKidsOf ]['element'] ]->title,
449
+ $elements[ $structure[ $startWithKidsOf ]['element'] ]->ID
450
  );
451
  }
452
+ //might we want the current item's title as the widget title?...
453
+ if( !empty( $currentItem ) && $cmw['title_from_current'] ){
 
454
  $cmw['_walker']['current_title'] = apply_filters(
455
  'the_title',
456
+ $elements[ $structure[ $currentItem ]['element'] ]->title,
457
+ $elements[ $structure[ $currentItem ]['element'] ]->ID
458
  );
459
  }
460
 
461
+ //now we need to gather together all the 'keep' items from structure;
462
+ //while doing so, we need to set up levels and kids, ready for adding classes...
463
+ foreach( $structure as $k=>$v ){
464
+ if( $v['keep'] ){
465
+ $substructure[ $k ] = $v;
466
+ //take a copy of the elements item...
467
+ $substructure[ $k ]['element'] = $elements[ $v['element'] ];
468
+ //use kids as a has-submenu flag...
469
+ $substructure[ $k ]['kids'] = 0;
470
+ //any surviving parent (except the artificial level-0) should have submenu class set on it...
471
+ array_shift( $v['ancestors'] ); //remove the level-0
472
+ for( $i = count( $v['ancestors'] ) - 1; $i >= 0; $i-- ){
473
+ if( $substructure[ $v['ancestors'][ $i ] ]['keep'] ){
474
+ $substructure[ $v['ancestors'][ $i ] ]['kids']++;
475
+ }else{
476
+ //not a 'kept' ancestor so remove it...
477
+ array_splice( $v['ancestors'], $i, 1 );
 
 
 
 
478
  }
479
  }
480
+ //ancestors now only has 'kept' ancestors...
481
+ $substructure[ $k ]['level'] = count( $v['ancestors'] ) + 1;
482
+ //need to ensure that the parent_field of all the new top-level (ie. root) items is set to
483
+ //zero, otherwise the parent::walk() will assume they're orphans and ignore them.
484
+ //however, we also need to check - especially for a specific-items filter (v2.0.0) - that parent_field of a
485
+ //child actually points to the closest 'kept' ancestor; otherwise, given A (kept) > B (not kept) > C (kept)
486
+ //the parent_field of C would point to a non-existent B and would subsequently be considered an orphan!
487
+ if( $substructure[ $k ]['level'] == 1){
488
+ $substructure[ $k ]['element']->$parent_field = 0;
489
+ }else{
490
+ //NB even though this really only needs to be done for $find_specific_items, I'm doing it regardless.
491
+ //set to the closest ancestor, ie. the new(?) parent...
492
+ $ancestor = array_slice( $v['ancestors'], -1, 1 );
493
+ $substructure[ $k ]['element']->$parent_field = $ancestor[0];
494
  }
 
 
 
 
495
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
  }
497
  }
498
 
499
+ //put substructure's elements back into $elements (remember that it's a 1-based array!)...
 
 
 
 
 
500
  $elements = array();
501
  $i = 1;
502
+ foreach( $substructure as $k=>$v ){
503
+ $elements[ $i ] = $v['element'];
504
+ //add the submenu class?...
505
+ if( $v['kids'] > 0 ){
506
+ $elements[ $i ]->classes[] = 'cmw-has-submenu';
 
 
 
507
  }
508
+ //add the level class...
509
+ $elements[ $i ]->classes[] = 'cmw-level-' . $v['level'];
510
+ $i++;
511
  }
512
+ unset( $structure, $substructure );
513
+
514
  //since we've done all the depth filtering, set max_depth to unlimited (unless 'flat' was requested!)...
515
  if( !$cmw['flat_output'] ){
516
  $max_depth = 0;
520
  return empty( $elements ) ? '' : parent::walk( apply_filters( 'custom_menu_wizard_walker_items', $elements, $args ), $max_depth, $args );
521
  }
522
 
523
+ /**
524
+ * recursively set the keep flag if within specified level/depth
525
+ */
526
+ function _cmw_set_keep_kids( &$structure, $itemId, $topLevel, $bottomLevel ){
527
+ $ct = count( $structure[ $itemId ]['kids'] );
528
+ for( $i = 0; $i < $ct; $i++ ){
529
+ $j = $structure[ $itemId ]['kids'][ $i ];
530
+ if( $structure[ $j ]['level'] <= $bottomLevel ){
531
+ $structure[ $j ]['keep'] = $structure[ $j ]['level'] >= $topLevel;
532
+ if( $structure[ $j ]['keep'] ){
533
+ $structure[0]['keepCount']++;
534
+ }
535
+ }
536
+ if( $structure[ $j ]['level'] < $bottomLevel ){
537
+ $this->_cmw_set_keep_kids( $structure, $j, $topLevel, $bottomLevel );
538
+ }
539
+ }
540
+ }
541
+
542
+ } //end Custom_Menu_Wizard_Walker class
543
 
544
  /**
545
  * Custom Menu Wizard Widget class
548
 
549
  var $_cmw_switches = array(
550
  'hide_title' => 0,
551
+ 'contains_current' => 0, //v2.0.0 added
552
+ 'depth_rel_current' => 0, //v2.0.0 added
553
  'fallback_no_ancestor' => 0, //v1.1.0 added
554
  'fallback_include_parent' => 0, //v1.1.0 added
555
  'fallback_include_parent_siblings' => 0, //v1.1.0 added
575
  );
576
  var $_cmw_strings = array(
577
  'title' => '',
578
+ 'items' => '', //v2.0.0 added
579
  'container' => 'div',
580
  'container_id' => '',
581
  'container_class' => '',
590
  );
591
  var $_cmw_integers = array(
592
  'depth' => 0,
593
+ 'filter' => -1, //v2.0.0 changed from switch
594
  'filter_item' => -2, //v1.1.0 changed from 0
595
  'menu' => 0,
596
  'start_level' => 1
761
  'flat_output' => $instance['flat_output'],
762
  'start_level' => $instance['start_level'],
763
  'depth' => $instance['depth'],
764
+ 'depth_rel_current' => $instance['depth_rel_current'], //v2.0.0
765
+ 'contains_current' => $instance['contains_current'], //v2.0.0
766
+ 'items' => $instance['items'], //v2.0.0
767
  //v1.2.1 this is for the walker's use...
768
  '_walker' => array()
769
  )
843
  foreach( $this->_cmw_html as $k=>$v ){
844
  $instance[ $k ] = isset( $new_instance[ $k ] ) ? trim( $new_instance[ $k ] ) : $v;
845
  }
846
+ //items special case...
847
+ if( !empty( $instance['items'] ) ){
848
+ $sep = preg_match( '/(^\d+$|,)/', $instance['items'] ) > 0 ? ',' : ' ';
849
+ $a = array();
850
+ foreach( preg_split('/[,\s]+/', $instance['items'], -1, PREG_SPLIT_NO_EMPTY ) as $v ){
851
+ $i = intval( $v );
852
+ if( $i > 0 ){
853
+ $a[] = $i;
854
+ }
855
+ }
856
+ $instance['items'] = implode( $sep, $a );
857
+ }
858
 
859
  return $instance;
860
  }
899
  return;
900
  }
901
 
902
+ ?>
903
+ <div class="widget-<?php echo $this->id_base; ?>-onchange"
904
+ data-cmw-dialog-title='<?php _e('Selected Menu : '); ?>'
905
+ data-cmw-dialog-prompt='<?php _e('Click an item to toggle &quot;Current Menu Item&quot;'); ?>'
906
+ data-cmw-dialog-output='<?php _e('Basic Output'); ?>'
907
+ data-cmw-dialog-fallback='<?php _e('Fallback invoked'); ?>'
908
+ data-cmw-dialog-trigger='#<?php echo $this->get_field_id('filter_item'); ?>'
909
+ data-cmw-dialog-id='<?php echo $this->get_field_id('dialog'); ?>'>
910
+ <?php
911
+
912
  /**
913
  * permanently visible section : Title (with Hide) and Menu
914
  */
925
  </p>
926
 
927
  <p>
928
+ <small class="cmw-toggle-assist">
929
+ <a class="widget-<?php echo $this->id_base; ?>-toggle-assist" href="#"><?php _e('assist'); ?></a>
930
+ </small>
931
  <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
932
  <select id="<?php echo $this->get_field_id('menu'); ?>"
933
  class="widget-<?php echo $this->id_base; ?>-selectmenu widget-<?php echo $this->id_base; ?>-listen"
950
  */
951
  $this->_open_a_field_section($instance, 'Filter', 'fs_filter');
952
  ?>
 
953
  <p>
954
+ <small class="cmw-toggle-assist">
955
+ <a class="widget-<?php echo $this->id_base; ?>-toggle-assist" href="#"><?php _e('assist'); ?></a>
956
+ </small>
957
  <label>
958
+ <input id="<?php echo $this->get_field_id('filter'); ?>_0"
959
+ class="widget-<?php echo $this->id_base; ?>-showall widget-<?php echo $this->id_base; ?>-listen"
960
+ name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="0" <?php checked($instance['filter'], 0); ?> />
961
  <?php _e('Show all'); ?></label>
962
  <br /><label>
963
  <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="widget-<?php echo $this->id_base; ?>-listen"
964
+ name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="1" <?php checked($instance['filter'], 1); ?> />
965
  <?php _e('Children of:'); ?></label>
966
+ <select id="<?php echo $this->get_field_id('filter_item'); ?>"
967
+ class="widget-<?php echo $this->id_base; ?>-childrenof widget-<?php echo $this->id_base; ?>-listen"
968
+ name="<?php echo $this->get_field_name('filter_item'); ?>">
969
  <option value="0" <?php selected( $instance['filter_item'], 0 ); ?>><?php _e('Current Item'); ?></option>
970
  <option value="-2" <?php selected( $instance['filter_item'], -2 ); ?>><?php _e('Current Root Item'); ?></option>
971
  <option value="-1" <?php selected( $instance['filter_item'], -1 ); ?>><?php _e('Current Parent Item'); ?></option>
979
  foreach( $menus as $i=>$menu ){
980
  //as of v1.2.0 : no items, no optgroup!
981
  if( !empty( $menu->_items ) ){
982
+ $grpdata = array();
983
+ $itemindents = array( '0' => array( 'level'=>0, 'grpkey'=>'' ) );
984
+ $menuGrpOpts = array();
985
  foreach( $menu->_items as $item ){
986
  //exclude orpans!
987
  if( isset($itemindents[ $item->menu_item_parent ])){
988
+ $title = apply_filters( 'the_title', $item->title, $item->ID );
989
+ $level = $itemindents[ $item->menu_item_parent ]['level'] + 1;
990
+ $grpkey = $item->ID . '|' . $title;
991
+ $grpdata[ $grpkey ] = array();
992
+ if( !empty( $itemindents[ $item->menu_item_parent ]['grpkey'] )){
993
+ $grpdata[ $itemindents[ $item->menu_item_parent ]['grpkey'] ][ $grpkey ] = array();
994
+ }
995
+
996
+ $itemindents[ $item->ID ] = array( 'level'=>$level, 'grpkey'=>$grpkey );
997
+ $maxlevel = max( $maxlevel, $level );
998
+ //v2.0.0 indents changed to non-breaking spaces...
999
+ $menuGrpOpts[] = '<option value="' . $item->ID . '" ' .
1000
  selected( $instance['filter_item'], $item->ID, false ) . '>' .
1001
+ str_repeat( '&nbsp;', ($level - 1) * 3 ) . $title . '</option>';
1002
+ }
1003
+ }
1004
+
1005
+ //the menu had items, but they might all have been orphans?...
1006
+ if( !empty( $menuGrpOpts ) ){
1007
+ foreach( array_reverse( $grpdata ) as $k=>$v ){
1008
+ if( empty( $v ) ){
1009
+ $grpdata[ $k ] = false;
1010
+ }else{
1011
+ foreach( $v as $n=>$j ){
1012
+ $grpdata[ $k ][ $n ] = $grpdata[ $n ];
1013
+ unset( $grpdata[ $n ] );
1014
+ }
1015
+ }
1016
  }
1017
+ $grpdata = json_encode( $grpdata );
1018
+ $menuOptions[] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $i . '" data-cmw-items="' . esc_attr($grpdata) . '">';
1019
+ $menuOptions[] = implode("\n", $menuGrpOpts);
1020
+ $menuOptions[] = '</optgroup>';
1021
  }
1022
+ unset( $menuGrpOpts, $grpdata, $itemindents );
1023
  }
1024
  }
1025
  $menuOptions = implode("\n", $menuOptions);
1026
  echo $menuOptions;
1027
  ?>
1028
  </select>
1029
+ <br /><label>
1030
+ <input id="<?php echo $this->get_field_id('filter'); ?>_2"
1031
+ class="widget-<?php echo $this->id_base; ?>-showspecific widget-<?php echo $this->id_base; ?>-listen"
1032
+ name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="-1" <?php checked($instance['filter'], -1); ?> />
1033
+ <?php _e('Items:'); ?></label>
1034
+ <input id="<?php echo $this->get_field_id('items'); ?>" class="widget-<?php echo $this->id_base; ?>-setitems"
1035
+ name="<?php echo $this->get_field_name('items'); ?>" type="text" value="<?php echo $instance['items']; ?>" />
1036
+
1037
  <select id="<?php echo $this->get_field_id('filter_item_ignore'); ?>" disabled="disabled"
1038
+ class='cmw-off-the-page' name="<?php echo $this->get_field_name('filter_item_ignore'); ?>">
 
1039
  <?php echo $menuOptions; ?>
1040
  </select>
1041
  </p>
1042
 
1043
+ <p class="widget-<?php echo $this->id_base; ?>-disableif-ss">
1044
  <label for="<?php echo $this->get_field_id('start_level'); ?>"><?php _e('Starting Level:'); ?></label>
1045
  <select id="<?php echo $this->get_field_id('start_level'); ?>" name="<?php echo $this->get_field_name('start_level'); ?>">
1046
  <?php
1055
  <br /><small><em><?php _e('Level to start testing items for inclusion'); ?></em></small>
1056
  </p>
1057
 
1058
+ <p class="widget-<?php echo $this->id_base; ?>-disableif-ss">
1059
  <label for="<?php echo $this->get_field_id('depth'); ?>"><?php _e('For Depth:'); ?></label>
1060
  <select id="<?php echo $this->get_field_id('depth'); ?>" name="<?php echo $this->get_field_name('depth'); ?>">
1061
  <option value="0" <?php selected( $instance['depth'], 0 ); ?>><?php _e('unlimited'); ?></option>
1068
  }
1069
  ?>
1070
  </select>
1071
+ <br /><small><em><?php _e('Relative to first Filter item found, <strong>unless</strong>&hellip;'); ?></em></small>
1072
+ <br /><label>
1073
+ <input id="<?php echo $this->get_field_id('depth_rel_current'); ?>"
1074
+ name="<?php echo $this->get_field_name('depth_rel_current'); ?>" type="checkbox" value="1"
1075
+ <?php checked($instance['depth_rel_current']); ?> />
1076
+ <?php _e('Relative to &quot;Current&quot; Item <small><em>(if found)</em></small>'); ?></label>
1077
  </p>
1078
  <?php $this->_close_a_field_section(); ?>
1079
 
1083
  */
1084
  $this->_open_a_field_section($instance, 'Fallbacks', 'fs_fallbacks');
1085
  ?>
1086
+ <p class="clear widget-<?php echo $this->id_base; ?>-disableifnot-rp">
1087
+ <small class="cmw-toggle-assist">
1088
+ <a class="widget-<?php echo $this->id_base; ?>-toggle-assist" href="#"><?php _e('assist'); ?></a>
1089
+ </small>
1090
  <small><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Root / Parent Item</em>, and no ancestor exists' ); ?> :</strong></small>
1091
  <br /><label>
1092
  <input id="<?php echo $this->get_field_id('fallback_no_ancestor'); ?>"
1093
  name="<?php echo $this->get_field_name('fallback_no_ancestor'); ?>" type="checkbox" value="1"
1094
  <?php checked($instance['fallback_no_ancestor']); ?> />
1095
  <?php _e('Switch to Current Item, and'); ?></label>
1096
+ <br /><label class="cmw-pad-left-1">
1097
  <input id="<?php echo $this->get_field_id('fallback_include_parent'); ?>"
1098
  name="<?php echo $this->get_field_name('fallback_include_parent'); ?>" type="checkbox" value="1"
1099
  <?php checked($instance['fallback_include_parent']); ?> />
1102
  <input id="<?php echo $this->get_field_id('fallback_include_parent_siblings'); ?>"
1103
  name="<?php echo $this->get_field_name('fallback_include_parent_siblings'); ?>" type="checkbox" value="1"
1104
  <?php checked($instance['fallback_include_parent_siblings']); ?> />
1105
+ <?php _e('with Siblings'); ?></label>
1106
  </p>
1107
 
1108
+ <p class="widget-<?php echo $this->id_base; ?>-disableifnot-ci">
1109
  <small><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Item</em>, and current item has no children' ); ?> :</strong></small>
1110
  <br /><label>
1111
  <input id="<?php echo $this->get_field_id('fallback_no_children'); ?>"
1112
  name="<?php echo $this->get_field_name('fallback_no_children'); ?>" type="checkbox" value="1"
1113
  <?php checked($instance['fallback_no_children']); ?> />
1114
  <?php _e('Switch to Current Parent Item, and'); ?></label>
1115
+ <br /><label class="cmw-pad-left-1">
1116
  <input id="<?php echo $this->get_field_id('fallback_nc_include_parent'); ?>"
1117
  name="<?php echo $this->get_field_name('fallback_nc_include_parent'); ?>" type="checkbox" value="1"
1118
  <?php checked($instance['fallback_nc_include_parent']); ?> />
1121
  <input id="<?php echo $this->get_field_id('fallback_nc_include_parent_siblings'); ?>"
1122
  name="<?php echo $this->get_field_name('fallback_nc_include_parent_siblings'); ?>" type="checkbox" value="1"
1123
  <?php checked($instance['fallback_nc_include_parent_siblings']); ?> />
1124
+ <?php _e('with Siblings'); ?></label>
1125
  </p>
1126
 
1127
 
1133
  */
1134
  $this->_open_a_field_section($instance, 'Output', 'fs_output');
1135
  ?>
 
1136
  <p>
1137
+ <small class="cmw-toggle-assist">
1138
+ <a class="widget-<?php echo $this->id_base; ?>-toggle-assist" href="#"><?php _e('assist'); ?></a>
1139
+ </small>
1140
  <label>
1141
  <input id="<?php echo $this->get_field_id('flat_output'); ?>_0" name="<?php echo $this->get_field_name('flat_output'); ?>"
1142
  type="radio" value="0" <?php checked(!$instance['flat_output']); ?> />
1147
  <?php _e('Flat'); ?></label>
1148
  </p>
1149
 
1150
+ <p>
1151
+ <label>
1152
+ <input id="<?php echo $this->get_field_id('contains_current'); ?>"
1153
+ name="<?php echo $this->get_field_name('contains_current'); ?>" type="checkbox"
1154
+ value="1" <?php checked($instance['contains_current']); ?> />
1155
+ <?php _e('Must Contain &quot;Current&quot; Item'); ?></label>
1156
+ <br /><small><em><?php _e('Checks both Filtered and Included items'); ?></em></small>
1157
+ </p>
1158
+
1159
  <p class="widget-<?php echo $this->id_base; ?>-disableif">
1160
  <label>
1161
  <input id="<?php echo $this->get_field_id('include_parent'); ?>"
1166
  <input id="<?php echo $this->get_field_id('include_parent_siblings'); ?>"
1167
  name="<?php echo $this->get_field_name('include_parent_siblings'); ?>" type="checkbox"
1168
  value="1" <?php checked($instance['include_parent_siblings']); ?> />
1169
+ <?php _e('with Siblings'); ?></label>
1170
  <br /><label>
1171
  <input id="<?php echo $this->get_field_id('include_ancestors'); ?>"
1172
  name="<?php echo $this->get_field_name('include_ancestors'); ?>" type="checkbox"
1299
  </p>
1300
  <?php $this->_close_a_field_section(); ?>
1301
 
1302
+ </div>
1303
+
1304
  <?php
1305
  } //end form()
1306
 
1313
  */
1314
  function _open_a_field_section( &$instance, $text, $fname ){
1315
  ?>
1316
+ <div class="stuffbox widget-<?php echo $this->id_base; ?>-collapsible-fieldset" title="<?php _e( 'Click to show/hide' ); ?>">
1317
  <input id="<?php echo $this->get_field_id($fname); ?>" class="hidden-field" name="<?php echo $this->get_field_name($fname); ?>"
1318
  type="checkbox" value="1" <?php checked( $instance[$fname] ); ?> />
1319
+ <div style="background-image:url(images/arrows.png);" class="<?php echo $instance[$fname] ? 'cmw-collapsed-fieldset' : ''; ?>"></div>
1320
+ <h3><?php _e( $text ); ?></h3>
1321
  </div>
1322
+ <div class="<?php echo $instance[$fname] ? 'cmw-start-fieldset-collapsed' : ''; ?>">
1323
  <?php
1324
  } //end _open_a_field_section()
1325
 
1370
  $instance = shortcode_atts( array(
1371
  'title' => '',
1372
  'menu' => 0, // menu id, slug or name
1373
+ //determines filter & filter_item ('items' takes precedence over 'children_of' because it's more specific)...
1374
+ 'children_of' => '', // empty = show all (dep. on 'items'); menu item id or title (caseless), or current|current-item|parent|current-parent|root|current-ancestor
1375
+ 'items' => '', // v2.0.0 empty = show all (dep. on 'children_of'); comma- or space-separated list of menu item ids (start level and depth don't apply)
1376
  'start_level' => 1,
1377
  'depth' => 0, // 0 = unlimited
1378
  //only if children_of is (parent|current-parent|root|current-ancestor); determines fallback_no_ancestor, fallback_include_parent & fallback_include_parent_siblings...
1379
  'fallback_parent' => 0, // 1 = use current-item; 'parent' = *and* include parent, 'siblings' = *and* include both parent and its siblings
1380
  //only if children_of is (current|current-item); determines fallback_no_children, fallback_nc_include_parent & fallback_nc_include_parent_siblings...
1381
  'fallback_current' => 0, // 1 = use current-parent; 'parent' = *and* include parent (if available), 'siblings' = *and* include both parent (if available) and its siblings
1382
+ //switches...
1383
  'flat_output' => 0,
1384
+ 'contains_current' => 0, // v2.0.0
1385
  //determines include_parent, include_parent_siblings & include_ancestors...
1386
  'include' =>'', //comma|space|hyphen separated list of 'parent', 'siblings', 'ancestors'
1387
  'ol_root' => 0,
1388
  'ol_sub' => 0,
1389
  //determines title_from_parent & title_from_current...
1390
  'title_from' => '', //comma|space|hyphen separated list of 'parent', 'current'
1391
+ 'depth_rel_current' => 0, // v2.0.0
1392
  //strings...
1393
  'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
1394
  'container_id' => '',
1426
  unset( $menus );
1427
 
1428
  if( $ok ){
 
 
 
 
1429
  $instance['filter'] = $instance['filter_item'] = 0;
1430
+ if( empty( $instance['items'] ) ){
1431
+ //children_of => filter & filter_item...
1432
+ if( empty( $instance['children_of'] ) ){
1433
+ $instance['children_of'] = '';
1434
+ }
1435
+ switch( $instance['children_of'] ){
1436
+ case '':
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1437
  break;
1438
+ case 'root': case 'current-ancestor':
1439
+ --$instance['filter_item']; //ends up as -2
1440
+ case 'parent': case 'current-parent':
1441
+ --$instance['filter_item']; //ends up as -1
1442
+ case 'current': case 'current-item':
1443
+ $instance['filter'] = 1;
1444
+ break;
1445
+ default:
1446
+ $instance['filter'] = 1;
1447
+ $instance['filter_item'] = strtolower( $instance['children_of'] );
1448
+ }
1449
+ //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...
1450
+ if( !is_numeric( $instance['filter_item'] ) ){
1451
+ foreach( $items as $item ){
1452
+ $ok = strtolower( $item->title ) == $instance['filter_item'];
1453
+ if( $ok ){
1454
+ $instance['filter_item'] = $item->ID;
1455
+ break;
1456
+ }
1457
  }
1458
  }
1459
+ }else{
1460
+ $instance['filter'] = -1;
1461
  }
1462
+ unset( $instance['children_of'] );
1463
  }
1464
 
1465
  if( $ok ){
1532
  $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
1533
  $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
1534
  }
1535
+
1536
  //handle widget_class here because we have full control over $before_widget...
1537
  $before_widget_class = array(
1538
  'widget_custom_menu_wizard',
demo.html DELETED
@@ -1,592 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en-GB">
3
- <head>
4
- <title>Custom Menu Wizard : Interactive Demo</title>
5
- <script type='text/javascript' src='//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js'></script>
6
- <script type='text/javascript' src='custom-menu-wizard.js'></script>
7
- <style type='text/css'>
8
- html, body {width:100%; border:0 none; margin:0; padding:0;}
9
- body {font-family:arial,sans-serif; font-size:14px; font-weight:normal; line-height:1.25; color:#333333; background-color:#ffffff;}
10
- div, p, h3, ul, ol, li, form, input, select, label, a, code {position:relative;}
11
- a {color:#333333; text-decoration:none;}
12
- code {color:#000099;}
13
- p {margin: 1em 0;}
14
- input, select, button {font-family:inherit; font-size:inherit; font-weight:inherit; color:inherit; -webkit-box-sizing:border-box; -moz-box-sizing:border-box; box-sizing:border-box;}
15
- select {max-width:100%;}
16
- label {vertical-align:middle;}
17
- form {display:block; border:1px solid #999999; border-radius:0.5em; margin:0; padding:0;}
18
- h3 {border-bottom:1px solid #cccccc;margin:0 0 0.25em; padding: 0.5em 0 0.25em;}
19
- button {background-color:#cccccc; border-color:#999999; border-radius:0.4em 0 0.4em 0; border-style:solid; border-width:1px 0 0 1px; bottom:0; color:#000000; cursor:pointer; margin:0; padding:0 0.2em; position:absolute; right:0;}
20
- button:hover {background-color:#666666; color:#ffffff;}
21
- optgroup option {padding-left:0.75em;}
22
- #theform {margin:1em; width:300px;}
23
- #formheader {background-color:#666666; border-radius:0.4em 0.4em 0 0; color:#FFFFFF; margin:0 0 -0.5em; padding:0.25em 0; text-align:center;}
24
- #formfields {padding:0 1em;}
25
- #moreinfo {padding:0.5em 0; text-align:center;}
26
- #moreinfo a {color:#0000ee;}
27
- #theoutput {margin:1em;}
28
- #widget-output {margin:0.5em 0; padding:0 1em; border:1px solid #cccccc; border-radius:0.5em; min-height:2em; min-width:9em;}
29
- #widget-output ul, #widget-output ol {margin:0.5em 0 0.5em 1.5em; padding:0;}
30
- #widget-output li ul {margin:0 0 0 1em;}
31
- #widget-output li ol {margin:0 0 0 1.5em;}
32
- #widget-output ul {list-style-type:disc;}
33
- #widget-output ul ul {list-style-type:circle;}
34
- #widget-output ul ul ul {list-style-type:square;}
35
- #widget-output ul ul ul ul {list-style-type:disc;}
36
- #widget-output ul ul ul ul ul {list-style-type:circle;}
37
- #widget-output ol {list-style-type:decimal;}
38
- #widget-output ol ol {list-style-type:upper-alpha;}
39
- #widget-output ol ol ol {list-style-type:lower-alpha;}
40
- #widget-output ol ol ol ol {list-style-type:lower-roman;}
41
- #widget-output ol ol ol ol ol {list-style-type:decimal;}
42
- #fallback {display:none; text-align:right; color:#006600;}
43
- #fallback.cmw-fellback-to-current, #fallback.cmw-fellback-to-parent {display:block;}
44
- #themenu {margin:1em;}
45
- #themenu ul {font-size:12px; list-style-type:none; margin:1em 0; padding:0;}
46
- #themenu ul ul {margin:0 0 0 5em;}
47
- #themenu li {display:block; margin:0; padding:0;}
48
- #themenu a {display:inline-block; text-align:center; padding:0.3333em; margin:0.1667em; border-radius:0.3333em;}
49
- #themenu span {display:block; width:6em; height:1.75em; line-height:1.75; border:1px solid #999999; border-radius:0.25em; box-shadow:2px 2px 2px 0px #666666;}
50
- #thefooter {float:left; clear:left; margin:0 1em; color:#808080;}
51
- #thefooter div {font-size:12px;}
52
- #theshortcode {color:#333333;}
53
- .alignleft {float:left;}
54
- .alignright {float:right;}
55
- .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;}
56
- .collapsible span {display:none; position:relative; float:right; color:#999999;}
57
- .collapsible:hover span {color:#333333;}
58
- .collapsible span:first-child, .collapsible.closed span:last-child {display:inline;}
59
- .collapsible.closed span:first-child {display:none;}
60
- .current-menu-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)";}
61
- .current-menu-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)";}
62
- .current-menu-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)";}
63
- .grey {color:#999999;}
64
- .picked > a {background-color:#000066; color:#ffffff; font-weight:bold;}
65
- .tryit {margin-left:1em; text-shadow:1px 1px 1px #ff0000;}
66
- .widefat {width:100%;}
67
- </style>
68
- </head>
69
- <body>
70
- <div>
71
- <div id='theform' class='alignright'>
72
- <form>
73
- <div id='formheader'>
74
- <strong>Custom Menu Wizard</strong> Interactive Demo
75
- <br /><small>v1.2.2 <em>(subset of admin options)</em></small><strong class="tryit"><em>Try it out&hellip;</em></strong>
76
- </div>
77
- <div id='formfields'>
78
-
79
- <p>
80
- <label for="widget-custom-menu-wizard-2-title">Title:</label>
81
- <label class="alignright">
82
- <input type="checkbox" value="1" name="hide_title" id="widget-custom-menu-wizard-2-hide_title">
83
- Hide</label>
84
- <input type="text" value="" name="title" class="widefat" id="widget-custom-menu-wizard-2-title">
85
- <small><em>Title can be set, but need not be displayed</em></small>
86
- </p>
87
-
88
- <p class='collapsible'>Filter<span>&#9650;</span><span>&#9660;</span></p>
89
-
90
- <p>
91
- <label>
92
- <input type="radio" checked="checked" value="0" name="filter" class="widget-custom-menu-wizard-listen" id="widget-custom-menu-wizard-2-filter_0">
93
- Show all</label>
94
- <br /><label>
95
- <input type="radio" value="1" name="filter" class="widget-custom-menu-wizard-listen" id="widget-custom-menu-wizard-2-filter_1">
96
- Children of:</label>
97
- <select name="filter_item" style="max-width:100%;" class="widget-custom-menu-wizard-listen" id="widget-custom-menu-wizard-2-filter_item">
98
- <option selected="selected" value="0">Current Item</option>
99
- <option value="-2">Current Root Item</option>
100
- <option value="-1">Current Parent Item</option>
101
- <optgroup label="the-menu"></optgroup>
102
- </select>
103
- </p>
104
-
105
- <p>
106
- <label for="widget-custom-menu-wizard-2-start_level">Starting Level:</label>
107
- <select name="start_level" id="widget-custom-menu-wizard-2-start_level">
108
- </select>
109
- <br /><small><em>Level to start testing items for inclusion</em></small>
110
- </p>
111
-
112
- <p>
113
- <label for="widget-custom-menu-wizard-2-depth">For Depth:</label>
114
- <select name="depth" id="widget-custom-menu-wizard-2-depth">
115
- <option selected="selected" value="0">unlimited</option>
116
- </select>
117
- <br /><small><em>Relative to the first Filtered item found</em></small>
118
- </p>
119
-
120
- <p class='collapsible end-collapsible'>Fallbacks<span>&#9650;</span><span>&#9660;</span></p>
121
-
122
- <p class="widget-custom-menu-wizard-disableifnot-rp">
123
- <small><strong>If &quot;Children of&quot; is <em>Current Root / Parent Item</em>, and no ancestor exists :</strong></small>
124
- <br /><label>
125
- <input type="checkbox" value="1" name="fallback_no_ancestor" id="widget-custom-menu-wizard-2-fallback_no_ancestor">
126
- Switch to Current Item, and</label>
127
- <br /><label style="padding-left:1em;">
128
- <input type="checkbox" value="1" name="fallback_include_parent" id="widget-custom-menu-wizard-2-fallback_include_parent">
129
- Include Parent... </label>
130
- <label>
131
- <input type="checkbox" value="1" name="fallback_include_parent_siblings" id="widget-custom-menu-wizard-2-fallback_include_parent_siblings">
132
- &amp; its Siblings</label>
133
- <br /><small><em>If Current Root/Parent and no ancestor exists</em></small>
134
- </p>
135
-
136
- <p class="widget-custom-menu-wizard-disableifnot-ci">
137
- <small><strong>If &quot;Children of&quot; is <em>Current Item</em>, and current item has no children :</strong></small>
138
- <br /><label>
139
- <input type="checkbox" value="1" name="fallback_no_children" id="widget-custom-menu-wizard-2-fallback_no_children">
140
- Switch to Current Parent Item, and</label>
141
- <br /><label style="padding-left:1em;">
142
- <input type="checkbox" value="1" name="fallback_nc_include_parent" id="widget-custom-menu-wizard-2-fallback_nc_include_parent">
143
- Include Parent... </label>
144
- <label>
145
- <input type="checkbox" value="1" name="fallback_nc_include_parent_siblings" id="widget-custom-menu-wizard-2-fallback_nc_include_parent_siblings">
146
- &amp; its Siblings</label>
147
- </p>
148
-
149
- <p class='collapsible end-collapsible'>Output<span>&#9650;</span><span>&#9660;</span></p>
150
-
151
- <p>
152
- <label>
153
- <input type="radio" checked="checked" value="0" name="flat_output" id="widget-custom-menu-wizard-2-flat_output_0">
154
- Hierarchical</label>
155
- &nbsp;<label>
156
- <input type="radio" value="1" name="flat_output" id="widget-custom-menu-wizard-2-flat_output_1">
157
- Flat</label>
158
- </p>
159
-
160
- <p class="widget-custom-menu-wizard-disableif">
161
- <label>
162
- <input type="checkbox" value="1" name="include_parent" id="widget-custom-menu-wizard-2-include_parent">
163
- Include Parent... </label>
164
- <label>
165
- <input type="checkbox" value="1" name="include_parent_siblings" id="widget-custom-menu-wizard-2-include_parent_siblings">
166
- &amp; its Siblings</label>
167
- <br /><label>
168
- <input type="checkbox" value="1" name="include_ancestors" id="widget-custom-menu-wizard-2-include_ancestors">
169
- Include Ancestors</label>
170
- <br /><label>
171
- <input type="checkbox" value="1" name="title_from_parent" id="widget-custom-menu-wizard-2-title_from_parent">
172
- Title from Parent</label>
173
- <br /><small><em>Only if the "Children of" Filter returns items</em></small>
174
- </p>
175
-
176
- <p>
177
- <label>
178
- <input type="checkbox" value="1" name="title_from_current" id="widget-custom-menu-wizard-2-title_from_current">
179
- Title from &quot;Current&quot; Item</label>
180
- <br /><small><em>Lower priority than "Title from Parent"</em></small>
181
- </p>
182
-
183
- <p>
184
- Change UL to OL:
185
- <br /><label>
186
- <input type="checkbox" value="1" name="ol_root" id="widget-custom-menu-wizard-2-ol_root">
187
- Top Level</label>
188
- &nbsp;<label>
189
- <input type="checkbox" value="1" name="ol_sub" id="widget-custom-menu-wizard-2-ol_sub">
190
- Sub-Levels</label>
191
- </p>
192
-
193
- <p class="grey end-collapsible">
194
- <small><em>Note that since WP v3.6 does it automatically,
195
- <br />&quot;Hide Widget if Empty&quot; is ON for this demo.</em></small>
196
- </p>
197
-
198
- </div>
199
- <button title="Reset the form to its defaults...">reset</button>
200
- </form>
201
-
202
- <div id="moreinfo">
203
- <small>Doc.:
204
- <a href="http://wordpress.org/plugins/custom-menu-wizard/" target='_blank'>wordpress.org/plugins/custom-menu-wizard/</a>
205
- </small>
206
- </div>
207
-
208
- </div>
209
-
210
- <div id='theoutput' class='alignright'>
211
- <strong>Basic Output</strong> &hellip;
212
- <div id='widget-output'></div>
213
- <div id='fallback'><small>Fallback invoked</small></div>
214
- </div>
215
-
216
- <div id='themenu' class='alignleft'>
217
- <div>
218
- <strong>&quot;Selected&quot; Menu</strong> <small class='grey'>(example)</small>
219
- <small><em>Click an item to toggle &quot;Current Item&quot;</em></small>
220
- </div>
221
- <ul>
222
- <li>
223
- <a href='#'><span>alpha</span></a>
224
- <ul>
225
- <li>
226
- <a href='#'><span>bravo</span></a>
227
- </li>
228
- <li>
229
- <a href='#'><span>charlie</span></a>
230
- <ul>
231
- <li>
232
- <a href='#'><span>delta</span></a>
233
- <ul>
234
- <li>
235
- <a href='#'><span>echo</span></a>
236
- <ul>
237
- <li>
238
- <a href='#'><span>foxtrot</span></a>
239
- </li>
240
- <li>
241
- <a href='#'><span>golf</span></a>
242
- </li>
243
- </ul>
244
- </li>
245
- </ul>
246
- </li>
247
- <li>
248
- <a href='#'><span>hotel</span></a>
249
- <ul>
250
- <li>
251
- <a href='#'><span>india</span></a>
252
- <ul>
253
- <li>
254
- <a href='#'><span>juliet</span></a>
255
- </li>
256
- </ul>
257
- </li>
258
- </ul>
259
- </li>
260
- </ul>
261
- </li>
262
- <li>
263
- <a href='#'><span>kilo</span></a>
264
- </li>
265
- </ul>
266
- </li>
267
- <li>
268
- <a href='#'><span>lima</span></a>
269
- <ul>
270
- <li>
271
- <a href='#'><span>mike</span></a>
272
- <ul>
273
- <li>
274
- <a href='#'><span>november</span></a>
275
- </li>
276
- </ul>
277
- </li>
278
- </ul>
279
- </li>
280
- <li>
281
- <a href='#'><span>oscar</span></a>
282
- </li>
283
- </ul>
284
- </div>
285
-
286
- <div id='thefooter'>
287
- <div id='theshortcode'>Shortcode : <code id='shortcode'></code></div>
288
- <div>
289
- Please note that this page is a simulator : it uses javascript to reproduce what the widget
290
- would do, without interacting with WordPress in any way.
291
- </div>
292
- </div>
293
-
294
- </div>
295
- <script type='text/javascript'>
296
- jQuery(function($){
297
- var maxlevel = 0,
298
- 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, title_from_current:1, ol_root:1, ol_sub:1},
299
- integers = {filter_item:1, start_level:1, depth:1},
300
- getSettings = function(){
301
- var settings = {};
302
- $.each($('form').serializeArray(), function(i, v){
303
- settings[v.name] = switches[v.name] ? v.value === '1' : (integers[v.name] ? parseInt(v.value, 10) : v.value);
304
- });
305
- return settings;
306
- },
307
- hyphenIndent = function(s, n){
308
- while(--n){
309
- s = '- ' + s;
310
- }
311
- return s;
312
- },
313
- shortcode = function(settings){
314
- var args = {
315
- 'menu' : 'example',
316
- },
317
- v, n;
318
- if(settings.title){
319
- args.title = settings.title;
320
- }
321
- if(settings.filter){
322
- switch(settings.filter_item){
323
- case 0: args.children_of = 'current'; break;
324
- case -1: args.children_of = 'parent'; break;
325
- case -2: args.children_of = 'root'; break;
326
- default:
327
- args.children_of = $('#themenu a').eq(settings.filter_item - 1).text();
328
- }
329
- }
330
- if(settings.filter && settings.filter_item < 0 && settings.fallback_no_ancestor){
331
- if(settings.fallback_include_parent_siblings){
332
- args.fallback_parent = 'siblings';
333
- }else if(settings.fallback_include_parent){
334
- args.fallback_parent = 'parent';
335
- }else{
336
- args.fallback_parent = [1];
337
- }
338
- }
339
- if(settings.filter && !settings.filter_item && settings.fallback_no_children){
340
- if(settings.fallback_nc_include_parent_siblings){
341
- args.fallback_current = 'siblings';
342
- }else if(settings.fallback_nc_include_parent){
343
- args.fallback_current = 'parent';
344
- }else{
345
- args.fallback_current = [1];
346
- }
347
- }
348
- if(settings.start_level > 1){
349
- args.start_level = [settings.start_level];
350
- }
351
- if(settings.depth > 0){
352
- args.depth = [settings.depth];
353
- }
354
- n = [];
355
- if(settings.filter){
356
- if(settings.include_parent_siblings){
357
- n.push('siblings');
358
- }else if(settings.include_parent){
359
- n.push('parent')
360
- }
361
- if(settings.include_ancestors){
362
- n.push('ancestors');
363
- }
364
- if(n.length){
365
- args.include = n.join(' ');
366
- }
367
- }
368
- n = [];
369
- if(settings.filter && settings.title_from_parent){
370
- n.push('parent');
371
- }
372
- if(settings.title_from_current){
373
- n.push('current');
374
- }
375
- if(n.length){
376
- args.title_from = n.join(' ');
377
- }
378
- for(n in {flat_output:1, ol_root:1, ol_sub:1}){
379
- if(settings[n]){
380
- args[n] = [1];
381
- }
382
- }
383
- v = [];
384
- for(n in args){
385
- v.push( $.isArray(args[n]) ? n + '=' + args[n][0] : n + '="' + args[n] + '"' );
386
- }
387
- $('code').text('[custom_menu_wizard ' + v.join(' ') + ']');
388
- },
389
- show = function(e, settings){
390
- //hide_empty is assumed to be on (WP < v3.6) or will automatically on (WP v3.6+)
391
- var output = $('#widget-output').empty(),
392
- items = $('.picked'),
393
- html = '',
394
- title = '',
395
- currLevel = 0,
396
- startLevel;
397
- settings = settings || getSettings();
398
- if(items.length){
399
- if(settings.filter && settings.title_from_parent){
400
- title = $('.the-parent').children('a').text() || '';
401
- }
402
- if(!title && settings.title_from_current){
403
- title = $('.current-menu-item').text() || '';
404
- }
405
- if(!title && !settings.hide_title){
406
- title = settings.title || '';
407
- }
408
- items.each(function(){
409
- var level = settings.flat_output ? 1 : parseInt(this.className.match(/level-(\d+)/)[1], 10),
410
- anchor = $(this).children('a');
411
- if(currLevel){
412
- if(level > currLevel){
413
- html += settings.ol_sub ? '<ol>' : '<ul>';
414
- }else{
415
- while(currLevel > level){
416
- --currLevel;
417
- html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
418
- }
419
- html += '</li>';
420
- }
421
- }else{
422
- startLevel = level;
423
- }
424
- html += '<li class="cmw-level-' + (level - startLevel + 1) + ($(this).data().included || '') + '"><a href="#' + anchor.data('indx') + '">' + anchor.text() + '</a>';
425
- currLevel = level;
426
- });
427
- while(currLevel > startLevel){
428
- --currLevel;
429
- html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
430
- }
431
- html += '</li>';
432
- output.html( (settings.ol_root ? '<ol' : '<ul') + ' class="' + $.trim( 'menu-widget ' + ($('#fallback').get(0).className || '') ) + '">' + html + (settings.ol_root ? '</ol>' : '</ul>') );
433
- if(title){
434
- output.prepend('<h3>' + title + '</h3>');
435
- }
436
- output.find('li').filter(function(){
437
- return !!$(this).children('ul, ol').length;
438
- }).addClass('cmw-has-submenu');
439
- }
440
- shortcode(settings);
441
- },
442
- walk = function(e){
443
- var settings = getSettings(),
444
- includeParent = settings.include_parent,
445
- includeParentSiblings = settings.include_parent_siblings,
446
- items = $('#themenu li').removeData('included').removeClass('the-parent'),
447
- picked = items.filter('.picked'),
448
- cls = [],
449
- firstParent = true,
450
- fallback, parent, i;
451
- if(!settings.filter){
452
- //show all...
453
- for(i = 1; i < settings.start_level; i++){
454
- items = items.not('.level-' + i);
455
- }
456
- if(settings.depth){
457
- for(i = i + settings.depth; i < maxlevel + 1; i++){
458
- items = items.not('.level-' + i);
459
- }
460
- }
461
- }else{
462
- //parent item...
463
- if(settings.filter_item > 0){
464
- parent = items.eq(settings.filter_item - 1);
465
- }else if(!settings.filter_item){
466
- parent = $('.current-menu-item').closest('li');
467
- }else{
468
- if(settings.filter_item < -1){
469
- //this could be more than one!...
470
- parent = $('.current-menu-ancestor').closest('li');
471
- }else{
472
- parent = $('.current-menu-parent').closest('li');
473
- }
474
- if(!parent.length && settings.fallback_no_ancestor){
475
- parent = $('.current-menu-item').closest('li');
476
- includeParent = includeParent || settings.fallback_include_parent;
477
- includeParentSiblings = includeParentSiblings || settings.fallback_include_parent_siblings;
478
- if(parent.length){
479
- fallback = 'cmw-fellback-to-current';
480
- }
481
- }
482
- }
483
- //could be multiple - only want first one that matches start_level...
484
- parent = parent.filter(function(){
485
- var level = parseInt(this.className.match(/level-(\d+)/)[1], 10),
486
- rtn = firstParent && level > settings.start_level - 2,
487
- i, j;
488
- for(i = level + 1, j = 0; rtn && i <= maxlevel && (!settings.depth || j < settings.depth); i++, j++){
489
- cls.push('.level-' + i);
490
- }
491
- if(rtn){
492
- firstParent = false;
493
- }
494
- return rtn;
495
- });
496
- //kids...
497
- items = parent.find('li').filter( cls.join(',') );
498
- //...if current item is looked for and found, but has no kids, and fallback is set...
499
- if(parent.length && !settings.filter_item && !items.length && settings.fallback_no_children){
500
- items = parent.siblings('li').add( parent );
501
- includeParent = includeParent || settings.fallback_nc_include_parent;
502
- includeParentSiblings = includeParentSiblings || settings.fallback_nc_include_parent_siblings;
503
- parent = parent.parents('li').eq(0);
504
- if(parent.length){
505
- fallback = 'cmw-fellback-to-parent';
506
- }
507
- }
508
- if(items.length){
509
- if(includeParentSiblings){
510
- items = items.add( parent.siblings('li').data('included', ' cmw-an-included-parent-sibling') ).add( parent );
511
- includeParent = true;
512
- }
513
- if(settings.include_ancestors){
514
- items = items.add( parent.parentsUntil('#themenu', 'li').data('included', ' cmw-an-included-ancestor') ).add( parent );
515
- includeParent = true;
516
- }
517
- if(includeParent){
518
- items = items.add( parent.data('included', ' cmw-the-included-parent') );
519
- }
520
- parent.addClass('the-parent');
521
- }
522
- }
523
- $('#fallback').removeClass();
524
- if(items.length && fallback){
525
- $('#fallback').addClass(fallback);
526
- }
527
- picked.not(items).removeClass('picked');
528
- items.addClass('picked');
529
- show.call(this, e, settings);
530
- },
531
- flds = $('input,select');
532
- $('#themenu a').on('click', function(){
533
- //click on a #themenu item to [un]make it the current item...
534
- var cls = ['current-menu-item', 'current-menu-parent', 'current-menu-ancestor'],
535
- inPath = $(this).find('span').not('.' + cls[0]).parentsUntil('#themenu', 'li'),
536
- i, n;
537
- $('.' + cls.join(',.')).removeClass().removeAttr('title');
538
- for(i = 0; i < inPath.length; i++){
539
- n = i === 1 ? cls.join(' ') : cls[0];
540
- inPath.eq(i).children('a').find('span').addClass(n).attr({title:n.replace(' ', ' & ').replace(/-/g, ' ')});
541
- if(cls.length > 1){
542
- cls.shift();
543
- }
544
- }
545
- flds.eq(0).trigger('change');
546
- return false;
547
- }).map(function(i){
548
- //create filter_item's optgroup options...
549
- var el = $(this).data('indx', i),
550
- level = el.parentsUntil('#themenu', 'li').length;
551
- maxlevel = Math.max(maxlevel, level);
552
- el.parent().addClass('level-' + level);
553
- return $('<option/>', {value:i + 1}).text( hyphenIndent(el.text(), level) ).get(0);
554
- }).appendTo('optgroup');
555
- //allow setting current from widget output (only setting, not un-setting!)...
556
- $('#widget-output').on('click', 'a', function(){
557
- var indx = this.href.split('#')[1];
558
- $('#themenu a').eq(indx).not(':has(.current-menu-item)').trigger('click');
559
- return false;
560
- });
561
- //populate starting-level and depth options...
562
- for(i = 1; i < maxlevel + 1; i++){
563
- var levels = i < 2 ? ' level' : ' levels',
564
- att = {value:i};
565
- $('<option/>', att).text(i + levels).appendTo('#widget-custom-menu-wizard-2-depth');
566
- if(i < 2){
567
- att.selected = 'selected';
568
- }
569
- $('<option/>', att).text(i).appendTo('#widget-custom-menu-wizard-2-start_level');
570
- }
571
- //interactivity (need to ensure that any delegated handlers from the included script get run first)...
572
- flds.not( flds.filter('[type="text"]').on('blur', show) ).addClass('demo-change');
573
- $(document).on('change', '.demo-change', walk);
574
- //reset...
575
- $('button').on('click', function(){
576
- $('form').get(0).reset();
577
- $('#widget-custom-menu-wizard-2-filter_item').trigger('change');
578
- return false;
579
- }).trigger('click');
580
- //collapsibles...
581
- $('.collapsible').on('click', function(){
582
- var Self = $(this)
583
- data = Self.data();
584
- Self.toggleClass('closed', !data.closed).nextUntil('.end-collapsible')[data.closed ? 'slideDown' : 'slideUp']();
585
- data.closed = !data.closed;
586
- this.blur();
587
- return false;
588
- }).slice(1).trigger('click');
589
- });
590
- </script>
591
- </body>
592
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -3,10 +3,10 @@ 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.2.2
7
  License: GPLv2 or Later
8
 
9
- Custom Menu Wizard Widget : Show branches or levels of your menu in a widget, with full customisation.
10
 
11
  == Description ==
12
 
@@ -28,11 +28,14 @@ Features include:
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
- Demo Emulator : [www.wizzud.com/custom-menu-wizard](http://www.wizzud.com/custom-menu-wizard/) -
33
- even shows the shortcode for the options that have been set. (This emulator is also provided as part of the plugin)
34
 
35
- **WIDGET OPTIONS**
36
 
37
  There are quite a few options, which makes the widget settings box very long. I have therefore grouped most of the options into
38
  logical sections and made each section collapsible (with remembered state once saved). As of v1.2.1, only the Filter section is
@@ -78,21 +81,37 @@ open by default; all sections below that start off collapsed.
78
 
79
  If you change `Select Menu`, the options presented in this dropdown will change accordingly and the selected option will revert to the default.
80
 
 
 
 
 
 
 
 
 
81
  * **Starting Level** *(select, default: "1")*
82
 
83
  This is the level within the chosen menu (from `Select Menu`) that the widget will start looking for items to keep. Obviously, level 1
84
  is the root level (ie. those items that have no parent item); level 2 is all the immediate children of the root level items, and so on.
85
  Note that for a `Children of` filter there is no difference between level 1 and level 2 (because there are no children at level 1).
 
86
 
87
  * **For Depth** *(select, default: "unlimited")*
88
 
89
  This is the maximum depth of the eventual output structure after filtering, and in the case of `Flat` output being requested it is
90
  still applied - as if the output were `Hierarchical` and then flattened at the very last moment.
91
 
92
- You need to be aware that the
93
  `For Depth` setting is applied relative to the level at which the first item to be kept is found. For example, say you were to set
94
  `Children of` to "Current Item", `Starting Level` to "2", and `For Depth` to "2 levels" : if the current item was found at level 3,
95
  then you would get the current item's immediate children (from level 4), plus *their* immediate children (from level 5).
 
 
 
 
 
 
 
96
 
97
  * **Fallbacks**
98
 
@@ -156,6 +175,10 @@ open by default; all sections below that start off collapsed.
156
  Output in a single list format, ignoring any parent-child relationship other than to maintain the same physical order as would be
157
  presented in a `Hierarchical` output.
158
 
 
 
 
 
159
  * **Include Parent...** *(checkbox)*
160
 
161
  If checked, include the parent item in the output. Only applies to a successful `Children of` filter.
@@ -250,12 +273,13 @@ open by default; all sections below that start off collapsed.
250
 
251
  Text or HTML that will be placed immediately after each menu item's link text.
252
 
253
- **SHORTCODE**
 
 
 
254
 
255
- The shortcode is **`[custom_menu_wizard]`**. Most of the attributes reflect the options available to the widget, but some have been simplified for
256
- easier use in the shortcode format.
257
- 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
258
- found then there will be no output from the shortcode.
259
 
260
  * **title** *(string)*
261
 
@@ -268,7 +292,8 @@ found then there will be no output from the shortcode.
268
 
269
  * **children_of** *(string | integer)*
270
 
271
- 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 :
 
272
 
273
  * 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`).
274
  (Hint : In Menus Admin, hover over the item's **Remove** link and note the number after *menu-item=* in the URL)
@@ -283,7 +308,10 @@ found then there will be no output from the shortcode.
283
 
284
  * 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
285
  (within `menu`). Please note that the code looks for a *caseless* title match, so specifying `children_of="my menu item"` will
286
- match against a menu item with the title "My Menu Item".
 
 
 
287
 
288
  * **fallback_parent** *(string | integer)*
289
 
@@ -310,9 +338,13 @@ found then there will be no output from the shortcode.
310
  * **start_level** *(integer, default 1)* See widget's `Starting Level` option, under **Filter** above.
311
 
312
  * **depth** *(integer, default 0)* See widget's `For Depth` option, under **Filter** above.
 
 
313
 
314
  * **flat_output** *(switch, off by default, 1 to enable)* See widget's `Flat` option, under **Output** above.
315
 
 
 
316
  * **include** *(string)*
317
 
318
  * *'parent'* : Enables widget's `Include Parent...` **Output** option
@@ -347,13 +379,13 @@ found then there will be no output from the shortcode.
347
 
348
  * **wrap_link** *(string)*
349
 
350
- This is an optional tag name (eg. *'div'*, *'p'*, *'span*') that, if provided, will be made into HTML start/end tags
351
  and sent through to the widget as its `Before the Link` and `After the Link` options. Please note that the shortcode usage - a simple
352
  tag name - is much more restrictive than the widget's options, which allow HTML.
353
 
354
  * **wrap_link_text** *(string)*
355
 
356
- This is an optional tag name (eg. *'span*', *'em'*, '*strong*') that, if provided, will be made into HTML start/end tags
357
  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
358
  simple tag name - is much more restrictive than the widget's options, which allow HTML.
359
 
@@ -361,15 +393,21 @@ found then there will be no output from the shortcode.
361
 
362
  * Show the entire "main" menu :
363
 
364
- `[custom_menu_wizard menu=main]`
 
 
365
 
366
  * Show the children of the Current Item within the "main" menu, for unlimited depth, and include the Current Item's parent :
367
 
368
- `[custom_menu_wizard menu=main children_of=current include=parent]`
 
 
369
 
370
  * From the "animals" menu, show all the items *immediately* below (depth=1) "Small Dogs", plus "Small Dogs" and its sibling items, as ordered lists :
371
 
372
- `[custom_menu_wizard menu="animals" children_of="small dogs" depth=1 include="siblings" ol_root=1 ol_sub=1]`
 
 
373
 
374
  == Installation ==
375
 
@@ -380,11 +418,56 @@ found then there will be no output from the shortcode.
380
  1. Activate the plugin through the 'Plugins' menu in your WP Admin
381
 
382
  The widget will now be available in the 'Widgets' admin page.
383
- As long as you already have at least one Menu defined, you can add the new widget to a sidebar and configure it however you want.
 
384
 
385
  == Frequently Asked Questions ==
386
 
387
- If you have a question or problem, please use the integrated Support forum.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
 
389
  == Screenshots ==
390
 
@@ -394,10 +477,26 @@ If you have a question or problem, please use the integrated Support forum.
394
 
395
  3. Even more widget options
396
 
397
- 4. Demo / Helper
398
 
399
  == Changelog ==
400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  = 1.2.2 =
402
 
403
  * bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
@@ -414,7 +513,7 @@ If you have a question or problem, please use the integrated Support forum.
414
 
415
  * in demo.html, added a link to the documentation page
416
 
417
- * corrected 2 of the shortcode examples in the readme.txt, and made emulator (dem) available from the readme
418
 
419
  = 1.2.0 =
420
 
@@ -434,7 +533,7 @@ If you have a question or problem, please use the integrated Support forum.
434
 
435
  * added 'Current Root Item' and 'Current Parent Item' to the `Children of` filter
436
 
437
- * added `Fallback to Current Item` option, with subsibiary options for overriding a couple of Output options, as a means to enable Current Root & Current Parent to match a Current Item at root level
438
 
439
  * added an Output option to include both the parent item **and** the parent's siblings (for a successful `Children of` filter)
440
 
@@ -456,6 +555,23 @@ Initial release
456
 
457
  == Upgrade Notice ==
458
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
459
  = 1.2.1 =
460
 
461
  Added a few extra custom classes, and changed the defaults for new widgets such that only the Filter section is open by default.
@@ -464,6 +580,6 @@ Fixed a couple of the shortcode examples in the readme.txt, and added display of
464
 
465
  = 1.2.0 =
466
 
467
- Added custom_menu_wizard shortcode, to run the widget from within content. Also added a new fallback for Current Item having no children, and
468
- moved all fallbacks into a collapsible Fallbacks section. Fixed a bug with optgroups/options made available for the 'Children of' selector
469
- after the widget has been saved (also affected disabled fields and styling).
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: 2.0.0
7
  License: GPLv2 or Later
8
 
9
+ Show branches or levels of your menu in a widget, or in content using a shortcode, with full customisation.
10
 
11
  == Description ==
12
 
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
+ * *As of v2.0.0* : Make the output conditional upon the "current" item appearing in the selected/included items
32
+ * *As of v2.0.0* : Specify specific menu items
33
+ * *As of v2.0.0* : Use the widget's interactive "assist" to help with the widget settings or shortcode definition
34
 
35
+ Documentation for the Widget Options, and the associated Shortcode Parameters, can be found under
36
+ [Other Notes](http://wordpress.org/plugins/custom-menu-wizard/other_notes/).
37
 
38
+ == Widget Options ==
39
 
40
  There are quite a few options, which makes the widget settings box very long. I have therefore grouped most of the options into
41
  logical sections and made each section collapsible (with remembered state once saved). As of v1.2.1, only the Filter section is
81
 
82
  If you change `Select Menu`, the options presented in this dropdown will change accordingly and the selected option will revert to the default.
83
 
84
+ * **Items** (radio & text input)* as of v2.0.0
85
+
86
+ Takes a comma- or space-delimited list of menu item ids, specifiying the specific menu items that are required. This is intended
87
+ for situations that it is not possible to handle using other option settings. The simplest way to determine the menu item ids is
88
+ to use the "assist" facility.
89
+
90
+ Note that the `Starting Level` and `Depth` options (including `Relative to "Current" Item`) have no effect if `Items` is set.
91
+
92
  * **Starting Level** *(select, default: "1")*
93
 
94
  This is the level within the chosen menu (from `Select Menu`) that the widget will start looking for items to keep. Obviously, level 1
95
  is the root level (ie. those items that have no parent item); level 2 is all the immediate children of the root level items, and so on.
96
  Note that for a `Children of` filter there is no difference between level 1 and level 2 (because there are no children at level 1).
97
+ Also note that this option does not apply if `Items` is set.
98
 
99
  * **For Depth** *(select, default: "unlimited")*
100
 
101
  This is the maximum depth of the eventual output structure after filtering, and in the case of `Flat` output being requested it is
102
  still applied - as if the output were `Hierarchical` and then flattened at the very last moment.
103
 
104
+ You need to be aware that, by default, the
105
  `For Depth` setting is applied relative to the level at which the first item to be kept is found. For example, say you were to set
106
  `Children of` to "Current Item", `Starting Level` to "2", and `For Depth` to "2 levels" : if the current item was found at level 3,
107
  then you would get the current item's immediate children (from level 4), plus *their* immediate children (from level 5).
108
+ Note that this option does not apply if `Items` is set.
109
+
110
+ * **Relative to "Current" Item** *(checkbox)* as of v2.0.0
111
+
112
+ This changes the `For Depth` option such that depth is applied relative to the current menu item, instead of relative to the
113
+ first item found that is to be kept. It only has any effect when `For Depth` is set to something other than "unlimited", and when
114
+ the current menu item is within the filtered items (before taking `For Depth` into account).
115
 
116
  * **Fallbacks**
117
 
175
  Output in a single list format, ignoring any parent-child relationship other than to maintain the same physical order as would be
176
  presented in a `Hierarchical` output.
177
 
178
+ * **Must Contain "Current" Item** *(checkbox)* as of v2.0.0
179
+
180
+ If checked, the widget will not list any menu items unless the current menu item appears somewhere in the list.
181
+
182
  * **Include Parent...** *(checkbox)*
183
 
184
  If checked, include the parent item in the output. Only applies to a successful `Children of` filter.
273
 
274
  Text or HTML that will be placed immediately after each menu item's link text.
275
 
276
+ == Shortcode Parameters ==
277
+
278
+ The shortcode is **`[custom_menu_wizard]`**. Most of the attributes reflect the options available to the widget, but some have been simplified for easier use in the shortcode format.
279
+ 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 found then there will be no output from the shortcode.
280
 
281
+ The simplest way to build a shortcode is to use the widget's "assist" facility (new in v2.0.0). The facilty is available even when the widget is in
282
+ the Inactive Widgets area, so you don't have to add an unwanted instance of the widget to a sidebar.
 
 
283
 
284
  * **title** *(string)*
285
 
292
 
293
  * **children_of** *(string | integer)*
294
 
295
+ If not empty then it specifies a `Children of` filter. If neither `children_of` nor `items` are supplied (or are empty) then the
296
+ filter defaults back to `Show all` (see above). Note that `items`, if supplied, will take precedence over `children_of`.
297
 
298
  * 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`).
299
  (Hint : In Menus Admin, hover over the item's **Remove** link and note the number after *menu-item=* in the URL)
308
 
309
  * 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
310
  (within `menu`). Please note that the code looks for a *caseless* title match, so specifying `children_of="my menu item"` will
311
+ match against a menu item with the title "My Menu Item". Also note that the first match found (hierarchically) is the one that
312
+ gets used (it is quite possible to have same-named items within a menu structure).
313
+
314
+ * **items** *(string)* See widget's `Items` option, under **Filter** above.
315
 
316
  * **fallback_parent** *(string | integer)*
317
 
338
  * **start_level** *(integer, default 1)* See widget's `Starting Level` option, under **Filter** above.
339
 
340
  * **depth** *(integer, default 0)* See widget's `For Depth` option, under **Filter** above.
341
+
342
+ * **depth_rel_current** *(switch, off by default, 1 to enable)* See widget's `Relative to "Currrent" Item` option, under **Filter** above.
343
 
344
  * **flat_output** *(switch, off by default, 1 to enable)* See widget's `Flat` option, under **Output** above.
345
 
346
+ * **contains_current** *(switch, off by default, 1 to enable)* See widget's `Must Contain "Current" Item` option, under **Output** above.
347
+
348
  * **include** *(string)*
349
 
350
  * *'parent'* : Enables widget's `Include Parent...` **Output** option
379
 
380
  * **wrap_link** *(string)*
381
 
382
+ This is an optional tag name (eg. *'div'*, *'p'*, *'span'*) that, if provided, will be made into HTML start/end tags
383
  and sent through to the widget as its `Before the Link` and `After the Link` options. Please note that the shortcode usage - a simple
384
  tag name - is much more restrictive than the widget's options, which allow HTML.
385
 
386
  * **wrap_link_text** *(string)*
387
 
388
+ This is an optional tag name (eg. *'span'*, *'em'*, *'strong'*) that, if provided, will be made into HTML start/end tags
389
  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
390
  simple tag name - is much more restrictive than the widget's options, which allow HTML.
391
 
393
 
394
  * Show the entire "main" menu :
395
 
396
+ `
397
+ [custom_menu_wizard menu=main]
398
+ `
399
 
400
  * Show the children of the Current Item within the "main" menu, for unlimited depth, and include the Current Item's parent :
401
 
402
+ `
403
+ [custom_menu_wizard menu=main children_of=current include=parent]
404
+ `
405
 
406
  * From the "animals" menu, show all the items *immediately* below (depth=1) "Small Dogs", plus "Small Dogs" and its sibling items, as ordered lists :
407
 
408
+ `
409
+ [custom_menu_wizard menu="animals" children_of="small dogs" depth=1 include="siblings" ol_root=1 ol_sub=1]
410
+ `
411
 
412
  == Installation ==
413
 
418
  1. Activate the plugin through the 'Plugins' menu in your WP Admin
419
 
420
  The widget will now be available in the 'Widgets' admin page.
421
+ As long as you already have at least one Menu defined, you can add the new widget to a sidebar and configure it however you want.
422
+ Alternatively, you can use the shortcode in your content.
423
 
424
  == Frequently Asked Questions ==
425
 
426
+ If you have a question or problem that is not covered here, please use the integrated Support forum.
427
+
428
+ = Why isn't it working? Why is there no output? =
429
+
430
+ I don't know. With all due respect (and a certain amount of confidence in the widget) I would venture to suggest that it is probably due to
431
+ the option settings on the widget/shortcode. The quickest way to resolve any such issues is to use the widget's interactive "assist", and
432
+ ensure that you set the current menu item correctly for the page(s) that you are having problems with. However, I am well aware that I not
433
+ infallible, and if you still have problems then please let me have as much information as possible and I will endeavour to help. (Please
434
+ note that simply reporting "It doesn't work" is not the most useful of feedbacks, and is unlikely to get a response other than, possibly,
435
+ a request for more details).
436
+
437
+ = How do I use the "assist"? =
438
+
439
+ The widget's interactive "assist" is specific to each widget instance. It is a javascript-driven *emulator* that uses the widget instance's
440
+ option settings - including the menu selected - to build a pictorial representation of the menu and show you, in blue, which menu items will
441
+ be output according to the current option settings. It also shows a very basic output list of those menu items, although it will not apply
442
+ some of the more advanced HTML-modifying options such as can be found under the Container, Classes or Links sections.
443
+ Any of the displayed menu items can be designated as the "current menu item" simply by clicking on it (click again to deselect, or another
444
+ item to change). The "current menu item" is shaded red, with its parent shaded orange and ancestors shaded yellow. All changes in the
445
+ "current menu item" and the widget options are immediately reflected by the "assist" (text fields in the widget options simply need to lose
446
+ focus).
447
+
448
+ Once you are happy with the results, having tested all possible settings of "current menu item" (if it applies), then simply Save the widget.
449
+ Alternatively, simply copy-paste the shortcode code produced by the "assist" straight into your post (you do not need to Save the widget!).
450
+ The widget does not have to Saved to *test* any of the options.
451
+
452
+ = Is there an easy way to construct the shortcode to get the results that I want? =
453
+
454
+ Yes. Use the widget's interactive "assist" capability (see above). Note that you do not need to have the widget in a sidebar : the
455
+ "assist" also works off a widget that is in the Inactive Widgets area of the widget admin page.
456
+
457
+ = How do I get the menu item ids for the `Items` option? =
458
+
459
+ Use the widget's interactive "assist" (see above). Within the representation menu structure, each menu item's id is set in its title
460
+ attribute, so should be seen when the cursor is moved over the item. A simpler way is to check the `Items` option : the "assist" will
461
+ then show a checkbox beside each menu item and you simply [un]check the items as required. Each selection will be reflected back into the
462
+ widget's `Items` settings, and also in the shortcode code.
463
+
464
+ Alternatively, go to Appearance, Menus and select the relevant menu; hover over the edit, Remove, or Cancel link for an item and look in
465
+ the URL (the link's href) for `menu-item=NNN` ... the NNN is the menu item id.
466
+
467
+ = Why is the `Must Contain Current Item` option in the Output section and not in the Filter section? =
468
+
469
+ It was a close call, but since the Output options can extend the final list - and the check for "current menu item" is made against the
470
+ *entire* resultant list - I decided that `Must contain Current Item` was more of a "final output" check than an initial filter.
471
 
472
  == Screenshots ==
473
 
477
 
478
  3. Even more widget options
479
 
480
+ 4. Widget's "assist"
481
 
482
  == Changelog ==
483
 
484
+ = 2.0.0 =
485
+
486
+ * **! Possible Breaker !** The calculation of `Start Level` has been made consistent across the `Show all` and `Children of` filters : if you previously had a setup where you were filtering for the children of an item at level 2, with start level set to 4, there would have been no output because the immediate children (at level 3) were outside the start level. Now, there *will* be output, starting with the grand-children (at level 4).
487
+
488
+ * **! Possible Breaker !** There is now deemed to be an artificial "root" item above the level 1 items, which mean that a `Children of` filter set to "Current Parent Item" or "Current Root Item" will no longer fail for a top-level "current menu item". If you have the "no ancestor" fallback set then this change will have no impact (but you may now want to consider turning the fallback off?); if you *don't* currently use the "no ancestor" fallback, then where there was previously no output there will now be some!
489
+
490
+ * added new option : Items, a comma- or space-delimited list of menu item ids, as an alternative Filter
491
+
492
+ * added new option : Depth Relative to Current Item to the Filter section (depth_rel_current=1 in the shortcode)
493
+
494
+ * added new option : Must Contain Current Item to the Output section (contains_current=1 in the shortcode)
495
+
496
+ * changed the widget's "demo" facility to "assist" and brought it into WordPress admin, with full interactivity with the widget
497
+
498
+ * refactored code
499
+
500
  = 1.2.2 =
501
 
502
  * bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
513
 
514
  * in demo.html, added a link to the documentation page
515
 
516
+ * corrected 2 of the shortcode examples in the readme.txt, and made emulator (demo) available from the readme
517
 
518
  = 1.2.0 =
519
 
533
 
534
  * added 'Current Root Item' and 'Current Parent Item' to the `Children of` filter
535
 
536
+ * added `Fallback to Current Item` option, with subsidiary options for overriding a couple of Output options, as a means to enable Current Root & Current Parent to match a Current Item at root level
537
 
538
  * added an Output option to include both the parent item **and** the parent's siblings (for a successful `Children of` filter)
539
 
555
 
556
  == Upgrade Notice ==
557
 
558
+ = 2.0.0 =
559
+
560
+ **! Possible Breaker !** My apologies if this affects you, but there are 2 possible scenarios where settings that previously resulted in no output *might* now produce output :
561
+ + if you have set a `Children of` filter, **and** you have changed the `Start Level` to a level greater than 2, or
562
+ + if you have set the `Children of` filter to Current Parent/Root Item, and you have **not** set the "no ancestor" fallback.
563
+ *__If you think you may be impacted, please check the [Changelog](http://wordpress.org/plugins/custom-menu-wizard/changelog/) for a fuller explanation of what has changed.__*
564
+
565
+ New options :
566
+ + `Items` allows specific menu item ids to be listed, as an alternative to the other filters
567
+ + `Relative to "Current" Item` allows a limited Depth to be calculated relative to the current menu item
568
+ + `Must Contain "Current" Item` requires that there be no output unless the resultant list contains the current menu item.
569
+ Rebuilt the "demo" facility as an "assist" wizard for the widget It is now fully interactive with the widget instance, and generates the entire shortcode according to the widget instance settings.
570
+
571
+ = 1.2.2 =
572
+
573
+ Bugfix : The fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
574
+
575
  = 1.2.1 =
576
 
577
  Added a few extra custom classes, and changed the defaults for new widgets such that only the Filter section is open by default.
580
 
581
  = 1.2.0 =
582
 
583
+ Added custom_menu_wizard shortcode, to run the widget from within content.
584
+ Added a new fallback for Current Item having no children, and moved all fallbacks into a collapsible Fallbacks section.
585
+ 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).