Custom Menu Wizard Widget - Version 3.1.5

Version Description

  • addition : expanded Title From to allow absolute ancestor levels (besides root) and relative ancestor levels
  • addition : added a fallback option to switch determination of Current Item from first-found to last-found
  • addition : added a shortcode attribute that loads an existing widget instance : [cmwizard widget=N/]
  • documentation : updated, and provided an html version in the plugin download
Download this release

Release Info

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

Code changes from version 3.1.4 to 3.1.5

custom-menu-wizard.css CHANGED
@@ -14,7 +14,6 @@
14
  .widget-custom-menu-wizard-onchange input[type="text"] {max-width:100%;}
15
  .widget-custom-menu-wizard-onchange textarea {height:5em;}
16
  .widget-custom-menu-wizard-onchange optgroup option {padding-left:0.75em;}
17
- .widget-custom-menu-wizard-onchange .cmw-border-top {border-top:1px solid #e5e5e5;}
18
  .widget-custom-menu-wizard-onchange .cmw-colour-grey,
19
  .widget-custom-menu-wizard-onchange .cmw-colour-grey input,
20
  .widget-custom-menu-wizard-onchange .cmw-colour-grey select,
@@ -24,6 +23,7 @@
24
  .widget-custom-menu-wizard-onchange .cmw-followed-by:after {content:""; display:inline-block; width:1em;}
25
  .widget-custom-menu-wizard-onchange .cmw-indented {margin-left:1em; text-indent:-1em;}
26
  .widget-custom-menu-wizard-onchange .cmw-maxwidth-twothirds {max-width:66% !important;}
 
27
  .widget-custom-menu-wizard-onchange .cmw-off-the-page {display:none; position:absolute; left:-5000px; top:-5000px;}
28
  .widget-custom-menu-wizard-onchange .cmw-pseudo-para {margin:1em 0;}
29
  .widget-custom-menu-wizard-onchange .cmw-small-block {display:block; font-size:0.769em;}
@@ -61,7 +61,8 @@ a.widget-custom-menu-wizard-fieldset.cmw-collapsed-fieldset:active {background-p
61
  .widget-custom-menu-wizard-no-menus {background-color:#ffffff; font-size:1em; padding:0.5em 1em;}
62
  /*shortcodes...*/
63
  .widget-custom-menu-wizard-dialog .cmw-demo-theshortcode {clear:both; margin:1em 0 0.5em; width:100%;}
64
- .widget-custom-menu-wizard-onchange .cmw-shortcode-wrap {clear:both; margin:0.75em 0 1em; width:100%;}
 
65
  .widget-custom-menu-wizard-dialog code,
66
  .widget-custom-menu-wizard-onchange code {border:1px solid #CCCCCC; display:inline-block; line-height:1.5; padding:0.25em 0.5em;}
67
  .widget-custom-menu-wizard-onchange code {padding:0.1em 0.2em; font-size:0.846em;}
14
  .widget-custom-menu-wizard-onchange input[type="text"] {max-width:100%;}
15
  .widget-custom-menu-wizard-onchange textarea {height:5em;}
16
  .widget-custom-menu-wizard-onchange optgroup option {padding-left:0.75em;}
 
17
  .widget-custom-menu-wizard-onchange .cmw-colour-grey,
18
  .widget-custom-menu-wizard-onchange .cmw-colour-grey input,
19
  .widget-custom-menu-wizard-onchange .cmw-colour-grey select,
23
  .widget-custom-menu-wizard-onchange .cmw-followed-by:after {content:""; display:inline-block; width:1em;}
24
  .widget-custom-menu-wizard-onchange .cmw-indented {margin-left:1em; text-indent:-1em;}
25
  .widget-custom-menu-wizard-onchange .cmw-maxwidth-twothirds {max-width:66% !important;}
26
+ .widget-custom-menu-wizard-onchange .cmw-mock-fieldset {border-top:1px solid #E5E5E5; color:#0074a2; font-weight:bold; line-height:1.7; margin:0.5em 0 0; padding:0.4em 0 0; text-transform:uppercase;}
27
  .widget-custom-menu-wizard-onchange .cmw-off-the-page {display:none; position:absolute; left:-5000px; top:-5000px;}
28
  .widget-custom-menu-wizard-onchange .cmw-pseudo-para {margin:1em 0;}
29
  .widget-custom-menu-wizard-onchange .cmw-small-block {display:block; font-size:0.769em;}
61
  .widget-custom-menu-wizard-no-menus {background-color:#ffffff; font-size:1em; padding:0.5em 1em;}
62
  /*shortcodes...*/
63
  .widget-custom-menu-wizard-dialog .cmw-demo-theshortcode {clear:both; margin:1em 0 0.5em; width:100%;}
64
+ .widget-custom-menu-wizard-onchange .cmw-the-shortcodes {border-bottom:1px solid #e5e5e5; margin:0 0 1em;}
65
+ .widget-custom-menu-wizard-onchange .cmw-shortcode-wrap {clear:both; margin:0.2em 0 0.5em 0; width:100%;}
66
  .widget-custom-menu-wizard-dialog code,
67
  .widget-custom-menu-wizard-onchange code {border:1px solid #CCCCCC; display:inline-block; line-height:1.5; padding:0.25em 0.5em;}
68
  .widget-custom-menu-wizard-onchange code {padding:0.1em 0.2em; font-size:0.846em;}
custom-menu-wizard.js CHANGED
@@ -1,1877 +1,1928 @@
1
  /* Custom Menu Wizard plugin
2
  * Script for controlling this widget's options (in Admin -> Widgets)
 
 
3
  */
4
  /*global jQuery, window, document, ajaxurl */
5
  /*jslint forin: true, nomen: true, plusplus: true, regexp: true, unparam: true, white: true */
6
  /*jshint curly: true, eqeqeq: true, es3: true, freeze: true, immed: true, latedef: true, newcap: true, noarg: true, noempty: true, nonbsp: true, nonew: true, quotmark: single, undef: true, strict: true, trailing: true, laxbreak: true */
7
  jQuery(function($){
8
- 'use strict';
9
- var cmwAssist,
10
- //only test for : key="value", key='value' and key=value...
11
- parseSwitchTo = /(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)/g,
12
- isNumeric = function(x){
13
- return (/^[+\-]?\d+$/).test(x.toString());
14
- },
15
- widgetCustomMenuWizardClass = function(suffix, dot){
16
- return (!dot ? '' : '.') + 'widget-custom-menu-wizard-' + suffix;
17
- },
18
- /**
19
- * updates the graphic menu structure from the widget settings
20
- * @this {element} div.widget-custom-menu-wizard-onchange
21
- * @param {object} e Event object
22
- */
23
- assistance = function(e){
24
- var v = $(this).data().cmwDialogVersion.replace(/\./g, '');
25
- v = /^\d+$/.test(v) ? 'v' + v : v;
26
- //run the update() method of the relevant assist object, based on a version number...
27
- if(cmwAssist[v]){
28
- cmwAssist[v].update(e ? e.target : this);
29
- }else{
30
- cmwAssist.update(e ? e.target : this);
31
- }
32
- }, //end assistance()
33
- /**
34
- * gets the widget's field values, or their equivalents from an alternative set
35
- * @param {object|boolean} oc jQuery of the widget's onchange wrapper (false if alts are supplied)
36
- * @param {object} alts Optional parsed set of alternative field settings
37
- * @return {object} key=>value pairs of the field element values
38
- */
39
- getSettings = function(oc, altFields){
40
- var useAlternative = oc === false,
41
- ocd = useAlternative ? {} : oc.data(),
42
- settings = {},
43
- legacyVersion = !useAlternative && ocd.cmwDialogVersion === '2.1.0',
44
- csv = {items:1, exclude:1},
45
- keepAsString = $.extend({branch_start:1, exclude_level:1, include_level:1}, csv);
46
- $.each(useAlternative ? altFields : oc.find(':input').serializeArray(), function(i, v){
47
- var name = v.name.replace(/.*\[([^\]]+)\]$/, '$1'),
48
- val = !keepAsString[name] && /^-?\d+$/.test(v.value) ? parseInt(v.value, 10) : v.value;
49
- settings[name] = val;
50
- if(name === 'hide_empty'){
51
- settings[name] = useAlternative || !!ocd.cmwV36plus || val;
52
- }else if(csv[name]){
53
- settings['_' + name + '_sep'] = !val || /(^\d+\+?$|,)/.test($.trim(val)) ? ',' : ' ';
54
- val = $.map(val.split(/[,\s]+/), function(x){
55
- var inherit = !legacyVersion && /\+$/.test(x);
56
- x = x ? parseInt(x, 10) : 0;
57
- return isNaN(x) || x < 1 ? null : (inherit ? x + '+' : x);
58
- });
59
- settings['_' + name] = val.join(settings['_' + name + '_sep']);
60
- }
61
- });
62
- return settings;
63
- }, //end getSettings()
64
- /**
65
- * checks for switching to the alternative settings (v3.1.0)
66
- * @param {string} at Processing stage
67
- * @param {boolean} hasCurrent Current Item is in items
68
- * @param {integer} itemsLength Items length
69
- * @param {array} settings Current settings
70
- * @return {boolean} True if should switch to alternative settings
71
- */
72
- alternativeCheckFor = function(at, hasCurrent, itemsLength, settings){
73
- var switchIf = settings.switch_if;
74
- return (settings.switch_at === at && (
75
- ( switchIf === 'current' && hasCurrent ) ||
76
- ( switchIf === 'no-current' && !hasCurrent ) ||
77
- ( switchIf === 'no-output' && !itemsLength )
78
- ) );
79
- }, //end alternativeCheckFor()
80
- /**
81
- * takes a switch_to setting, parses it, and returns settings (equiv. Custom_Menu_Wizard_Plugin->shortcode_instance())
82
- * @param {string} switchTo switch_to value
83
- * @param {object} themenu jQuery of demo menu structure (.cmw-demo-themenu-ul)
84
- * @return {object} Settings, or False if can't be determined
85
- */
86
- alternativeParse = function(switchTo, themenu){
87
- switchTo = $.trim(switchTo || '');
88
- var alts = {
89
- 'title' : '',
90
- 'level' : 1, //default setting
91
- 'branch' : 0,
92
- 'items' : '',
93
- 'depth' : 0,
94
- 'depth_rel_current' : 0,
95
- 'start_at' : '',
96
- 'start_mode' : '',
97
- 'allow_all_root' : 0,
98
- 'ancestors' : 0,
99
- 'ancestor_siblings' : 0,
100
- 'include_root' : 0,
101
- 'include_level' : '',
102
- 'siblings' : 0,
103
- 'exclude' : '',
104
- 'exclude_level' : '',
105
- 'contains_current' : '',
106
- 'fallback' : '',
107
- 'flat_output' : 0,
108
- 'title_from' : '',
109
- 'title_linked' : 0,
110
- 'ol_root' : 0,
111
- 'ol_sub' : 0
112
- },
113
- attribute = parseSwitchTo.exec(switchTo),
114
- attr = {},
115
- i = 0,
116
- byItems, byBranch, byLevel, n;
117
- while(attribute){
118
- i++;
119
- // key = "value" [1] [2] ...
120
- if(attribute[1]){
121
- attr[ attribute[1] ] = attribute[2];
122
- // key = 'value' [3] [4] ...
123
- }else if(attribute[3]){
124
- attr[ attribute[3] ] = attribute[4];
125
- // key = value [5] [6] ...
126
- }else if(attribute[5]){
127
- attr[ attribute[5] ] = attribute[6];
128
- }else{
129
- i--;
130
- }
131
- attribute = parseSwitchTo.exec(switchTo);
132
- }
133
- if(i){
134
- for(n in attr){
135
- if(alts.hasOwnProperty(n)){
136
- alts[n] = attr[n];
137
- }
138
- }
139
- }
140
-
141
- //in order of priority...
142
- byItems = !!alts.items;
143
- byBranch = !byItems && !!alts.branch;
144
- byLevel = !byItems && !byBranch;
145
-
146
- if(byItems){
147
- alts.filter = 'items';
148
- }
149
- if(byBranch){
150
- alts.filter = 'branch';
151
- n = alts.start_at.toString();
152
- //default...
153
- alts.branch_start = n;
154
- //override...
155
- if(n === '0' || n === 'branch'){
156
- alts.branch_start = '';
157
- }
158
- if(n === 'root'){
159
- alts.branch_start = '1';
160
- }
161
- if(n === 'children'){
162
- alts.branch_start = '+1';
163
- }
164
- if(n === 'parent'){
165
- alts.branch_start = '-1';
166
- }
167
- if(alts.branch === 'current' || alts.branch === 'current-item'){
168
- alts.branch = 0;
169
- }else if( !isNumeric( alts.branch ) ){
170
- //if branch is non-numeric then it could be the title of a menu item, but we need it to be the menu item's id...
171
- //NB : if branch *is* numeric, but the item id is not within this menu, then ... tough! Basically, this assist mimics what
172
- // the widget does, and the widget doesn't check numeric branch values until it gets into the walker - at which point
173
- // it either produces the relevant output or doesn't. only if branch is non-numeric and possibly an item title does
174
- // the widget pre-check for it being in the menu (because the walker requires an item id) : so this does the same.
175
- alts.branch = alts.branch.toLowerCase();
176
- n = themenu.find('a.cmw-item').filter(function(){
177
- return $(this).text().toLowerCase() === alts.branch;
178
- });
179
- if(n.length){
180
- alts.branch = n.parent().data('itemid');
181
- }else{
182
- //COP OUT!
183
- return false;
184
- }
185
- }
186
- }
187
- if(byLevel){
188
- alts.filter = '';
189
- alts.level = Math.max(1, parseInt(alts.level, 10));
190
- }
191
- alts.start_at = null;
192
-
193
- //include_level, and the deprecated include_root switch...
194
- //if level is empty but root is set, set include_level to '1'...
195
- if( !alts.include_level && alts.include_root === '1' ){
196
- alts.include_level = '1';
197
- }
198
- alts.include_root = null;
199
- //fallback => fallback and fallback_siblings and fallback_depth...
200
- //allows "X", "X,Y" or "X,Y,Z" where comma could be space, and X|Y|Z could be "quit"|"current"|"parent", or "+siblings", or digit(s)
201
- //but "quit", "current" or "parent" must be present (others are optional)
202
- if(byBranch && !alts.branch && alts.fallback ){
203
- attr = alts.fallback.toLowerCase().split(/[\s,]+/);
204
- n = ' ' + attr.join(' ') + ' ';
205
- alts.fallback = '';
206
- if(n.indexOf(' quit ') >= 0){
207
- alts.fallback = 'quit';
208
- }else if(n.indexOf(' parent ') >= 0){
209
- alts.fallback = 'parent';
210
- }else if(n.indexOf(' current ') >= 0){
211
- alts.fallback = 'current';
212
- }
213
- if(alts.fallback !== '' && alts.fallback !== 'quit'){
214
- if( n.indexOf(' +siblings ') >= 0){
215
- alts.fallback_siblings = 1;
216
- }
217
- for(i = 0; i < attr.length; i++){
218
- if(/^\d+$/.test(attr[i])){
219
- n = parseInt(attr[i], 10);
220
- if(n > 0){
221
- alts.fallback_depth = n;
222
- break;
223
- }
224
- }
225
- }
226
- }
227
- }
228
- //title_from => title_from_...
229
- if(alts.title_from){
230
- attr = alts.title_from.toLowerCase().split(/[\s,]+/);
231
- for(i = 0; i < attr.length; i++){
232
- if(attr[i] === 'branch' || attr[i] === 'current'){
233
- alts['title_from_' + attr[i]] = 1;
234
- }else if(attr[i] === 'branch-root' || attr[i] === 'current-root'){
235
- alts['title_from_' + attr[i].replace('-', '_')] = 1;
236
- }
237
- }
238
- }
239
- alts.title_from = null;
240
- alts.hide_empty = 1;
241
-
242
- return getSettings( false, $.map(alts, function(v, k){ return v === null ? v : {name:k, value:v}; }) );
243
- }, //end alternativeParse()
244
- /**
245
- * strips an alternative settings shortcode down to its bare essentials (v3.1.0)
246
- * @param {string} x The alternative
247
- * @return {string}
248
- */
249
- alternativeStripDown = function(x){
250
- var rtn = '';
251
- if(!!x){
252
- //remove tabs, CRLFs, containing square brackets, self-terminator and spaces, then split on square bracket, taking first element...
253
- rtn = x.replace(/[\r\n\t]+/g, ' ').replace(/^[\[\s]+/, '').replace(/[\s\/\]]+$/, '').split(/[\[\]]/)[0];
254
- //trim trailing slash, surrounding spaces, and append a space...
255
- rtn = $.trim(rtn.replace(/\/+$/, '')) + ' ';
256
- //remove leading cmwizard, any occurrence of menu=something, and any occurrence of alternative="something" (optional quotes)...
257
- rtn = $.trim( (' ' + rtn.replace(/^cmwizard\s/, '') + ' ').replace(/\smenu=[^\s]*\s/, ' ').replace(/\salternative=("[^"]*"|[^\s]*)\s/, ' ') );
258
- //remove multiple spaces...
259
- rtn = rtn.replace(/\s\s+/g, ' ');
260
- }
261
- return rtn;
262
- },
263
- /**
264
- * retrieves alternative settings
265
- * @param {array} settings Current settings
266
- * @param {object} dialog jQuery of dialog
267
- * @return {boolean|array} Alternative settings
268
- */
269
- alternativeUse = function(settings, dialog){
270
- //alt settings are cached in the data of the menu display (which gets reconstructed when menu id changes)
271
- var themenu = dialog.find('.cmw-demo-themenu-ul'),
272
- dataStore = themenu.data(),
273
- altCode = alternativeStripDown( settings.switch_to );
274
- altCode = 'cmwizard menu=' + settings.menu + (altCode === '' ? altCode : ' ' + altCode);
275
-
276
- //if don't have cached code, or the code has changed, get new set...
277
- if(!dataStore.altCode || dataStore.altCode !== altCode){
278
- dataStore.altCode = altCode;
279
- dataStore.altSettings = alternativeParse(altCode, themenu);
280
- }
281
- //show that we are - or should be - using the alternative settings...
282
- dialog.find('.cmw-demo-alternative').addClass('updated')
283
- //...but if they're bad settings (from a bad switch_to code?) then show as an error...
284
- .toggleClass('error', dataStore.altSettings === false);
285
-
286
- return dataStore.altSettings === false ? false : $.extend({}, dataStore.altSettings);
287
- }, //end alternativeUse()
288
- /**
289
- * sets the tick or cross classes and returns a filtered set of the items that *are* ticked/crossed
290
- * @param {object} items jQuery of elements to filter
291
- * @param {array} settings Widget settings
292
- * @param {string} tickOrCross Either 'tick' or 'cross'
293
- * @return {object} jQuery of filtered items
294
- */
295
- filterTickCross = function( items, settings, tickOrCross ){
296
- var sep = tickOrCross === 'tick' ? settings._items_sep : settings._exclude_sep,
297
- inheritance = [],
298
- haystack = tickOrCross === 'tick' ? '_items' : '_exclude';
299
- haystack = settings[haystack]
300
- ? //extract those with inheritance...
301
- $.grep(settings[haystack].split(sep), function(v){
302
- if(/\+$/.test(v)){
303
- inheritance.push(parseInt(v, 10));
304
- return !v;
305
- }
306
- return !!v;
307
- })
308
- : [];
309
- haystack = sep + haystack.join(sep) + sep;
310
- inheritance = sep + inheritance.join(sep) + sep;
311
- //need to remember that we're turning off as well as on, because there's no generic clear-down...
312
- items = items.each(function(){
313
- var item = $(this),
314
- data = item.data(),
315
- inherit = inheritance.indexOf(sep + data.itemid + sep) > -1,
316
- matched = inherit || haystack.indexOf(sep + data.itemid + sep) > -1;
317
- item.toggleClass('cmw-has-' + tickOrCross, matched)
318
- .toggleClass('cmw-inherit-' + tickOrCross, inherit);
319
- });
320
- //returning items that *are* ticked/crossed, so get the inheritance items...
321
- return items.filter('.cmw-inherit-' + tickOrCross)
322
- //...get their descendants and clear any tick/cross classes that may have been set...
323
- .find('li').removeClass('cmw-has-' + tickOrCross + ' cmw-inherit-' + tickOrCross)
324
- //...add back in (to the inheritance descendants) any (other) items that still have tick/cross
325
- //set, which will include the uppermost of any inheritance items...
326
- .add( items.filter('.cmw-has-' + tickOrCross) );
327
- }, //end filterTickCross
328
- /**
329
- * gets the -onchange wrapper
330
- * @param {object} below jQuery of element to search below (exclusive, ie. find())
331
- * @param {object} above jQuery of element to search above (inclusive, ie. closest())
332
- * @return {object} jQuery of the -onchange wrapper
333
- */
334
- findOnchange = function(below, above){
335
- return (!below ? above : below)[!below ? 'closest' : 'find'](widgetCustomMenuWizardClass('onchange', 1));
336
- },
337
- /**
338
- * given an option in the form of one-or-more digits, optionally followed by a plus or minus, return the relevant classes
339
- * @param {string} option A setting, eg. settings.exclude_level
340
- * @param {integer} maxLevel Maximum number of levels available
341
- * @return {string} CSV of classes, eg. '.level-1,'level-2' for option='2-' (or option='1+' if maxLevel=2)
342
- */
343
- getLevelClasses = function(option, maxLevel){
344
- var rtn = [],
345
- k = option.match(/^(\d+)(\+|-)?$/),
346
- i;
347
- k = k ? [parseInt(k[1], 10), k[2] || ''] : [];
348
- if(k[0] > 0){
349
- for(i = 1; i <= maxLevel; i++){
350
- if( i === k[0] || (k[1] === '-' && i < k[0]) || (k[1] === '+' && i > k[0]) ){
351
- rtn.push('.level-' + i);
352
- }
353
- }
354
- }
355
- return rtn.join(',');
356
- },
357
- /**
358
- * produces the final output
359
- * @param {object} dialog jQuery of target dialog
360
- * @param {object} settings Field element values
361
- */
362
- showOutput = function(dialog, settings){
363
- var topOfMenu = dialog.find('.cmw-demo-themenu-ul'),
364
- items = topOfMenu.find('.picked'),
365
- html = '',
366
- title = settings.hide_title ? '' : settings.title,
367
- titleLinked = 0,
368
- currLevel = 0,
369
- output = dialog.find('.cmw-demo-theoutput-wrap').empty(),
370
- listClass = ['menu-widget'],
371
- itemList = {};
372
- if(items.length && output.length){
373
- //determine title: update() might have set a class for it...
374
- title = topOfMenu.find('.title-from-item').children('.cmw-item').text() || '';
375
- titleLinked = !!title && !!settings.title_linked;
376
- //...otherwise, check the actual widget title...
377
- if(!title && !settings.hide_title){
378
- title = settings.title || '';
379
- }
380
-
381
- items.each(function(i){
382
- var self = $(this),
383
- data = self.data(),
384
- trace = data.trace ? data.trace.toString().split(',') : [],
385
- iid = data.itemid.toString(),
386
- level = 1,
387
- anchor = self.children('.cmw-item');
388
- if(!settings.flat_output){
389
- itemList[iid] = 1;
390
- for(i = 0; i < trace.length; i++){
391
- if(itemList[trace[i]]){
392
- level++;
393
- }
394
- }
395
- }
396
- if(currLevel){
397
- if(level > currLevel){
398
- html += settings.ol_sub ? '<ol>' : '<ul>';
399
- }else{
400
- while(currLevel > level){
401
- --currLevel;
402
- html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
403
- }
404
- html += '</li>';
405
- }
406
- }
407
- html += '<li class="cmw-level-' + level + (data.included || '') + '"><a href="#' + anchor.data('indx') + '">' + anchor.text() + '</a>';
408
- currLevel = level;
409
- });
410
- while(currLevel > 1){
411
- --currLevel;
412
- html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
413
- }
414
- html += '</li>';
415
- listClass.push( dialog.find('.cmw-demo-fallback').data('fellback') );
416
- html = (settings.ol_root ? '<ol' : '<ul') + ' class="' + $.trim(listClass.join(' ')) + '">' + html + (settings.ol_root ? '</ol>' : '</ul>');
417
- output.html(html);
418
- output.find('li').filter(function(){
419
- return !!$(this).children('ul, ol').length;
420
- }).addClass('cmw-has-submenu');
421
- }
422
- if(output.length && title && (items.length || !settings.hide_empty)){
423
- output.prepend((titleLinked ? '<h3 class="cmw-title-linked">' : '<h3>') + title + '</h3>');
424
- }
425
- }, //end showOutput()
426
- /**
427
- * swap in the apropriate optgroup of items according to the selected menu
428
- * @param {object} menuItems jQuery of the SELECT being modified
429
- * @param {integer} selectedItem Currently selected item in menuItems
430
- * @param {integer} selectedMenu Index of selected menu
431
- * @return {integer} selectedItem
432
- */
433
- swapItems = function(menuItems, selectedItem, selectedMenu){
434
- var groupClone;
435
- if(!menuItems.find('optgroup').filter(function(){
436
- var keepit = $(this).data().cmwOptgroupIndex === selectedMenu;
437
- if(!keepit){
438
- $(this).remove();
439
- }
440
- return keepit;
441
- }).length){
442
- groupClone = $('#' + menuItems.attr('id') + '_ignore').find('optgroup').eq(selectedMenu).clone();
443
- if(groupClone.length){
444
- if(selectedItem > 0){
445
- selectedItem = 0;
446
- }
447
- menuItems.append(groupClone).val(selectedItem);
448
- }
449
- }
450
- return selectedItem;
451
- }, //end swapItems()
452
- /**
453
- * toggles a set of widgets options open or closed
454
- * @this {element} Header of the set
455
- * @param {object} e Event object
456
- * @return {boolean} false
457
- */
458
- clickFieldset = function(e){
459
- var self = $(this),
460
- chkbox = self.next('.cmw-fieldset-state'),
461
- collapse = !chkbox.prop('checked');
462
- if(chkbox.length){
463
- chkbox.prop('checked', collapse);
464
- self.toggleClass('cmw-collapsed-fieldset', collapse);
465
- chkbox.next('div')[collapse ? 'slideUp' : 'slideDown']();
466
- }
467
- this.blur();
468
- return false;
469
- }, //end clickFieldSet()
470
- /**
471
- * fetches and displays a list of posts that contain old/new shortcodes
472
- * @this {element} The link/button
473
- * @param {object} e Event object
474
- * @return {boolean} false
475
- */
476
- clickFindShortcodes = function(e){
477
- var self = $(this),
478
- grandad = self.parent().parent();
479
- //if currently fetching, do nothing...
480
- if(ajaxurl && !grandad.hasClass('cmw-ajax-fetching')){
481
- //if currently showing previous results, remove the results from view...
482
- if(grandad.hasClass('cmw-ajax-showing')){
483
- grandad.removeClass('cmw-ajax-showing');
484
- }else{
485
- //fetch results via ajax, showing spinner while doing so...
486
- grandad.addClass('cmw-ajax-fetching');
487
- $.get(
488
- ajaxurl,
489
- { 'action': 'cmw-find-shortcodes', '_wpnonce': self.data().nonce }
490
- ).done(function(response){
491
- if(!!response && response !== '0'){
492
- grandad.find('.cmw-demo-found-shortcodes').html($(response).find('response_data').text());
493
- grandad.addClass('cmw-ajax-showing');
494
- }
495
- }
496
- ).always(function(response){
497
- grandad.removeClass('cmw-ajax-fetching');
498
- }
499
- );
500
- }
501
- }
502
- this.blur();
503
- return false;
504
- },
505
- /**
506
- * toggles fixed/absolute positioning for the dialog (work around for draggable problems in jQuery UI v1.10.3/4)
507
- * @this {element} The "fixed" button
508
- * @param {object} e Event object
509
- * @return {boolean} false
510
- */
511
- clickFixedAbsolute = function(e){
512
- var self = $(this),
513
- data = self.data(),
514
- toAbsolute = !data.cmwAbsolute,
515
- dialogBox = self.closest('.ui-dialog'),
516
- dialog = dialogBox.find('.ui-dialog-content'),
517
- //if fixed -> absolute, add scrollTop to [css]top; if absolute -> fixed, substract scrollTop from [css]top...
518
- newTop = parseInt(dialogBox.css('top'), 10) + ( (toAbsolute ? 1 : -1) * $(document).scrollTop() );
519
- data.cmwAbsolute = toAbsolute;
520
- if(!data.cmwMaxHeight){
521
- //store the initial maxHeight setting...
522
- data.cmwMaxHeight = dialog.dialog('option', 'maxHeight');
523
- }
524
- //swap the icon...
525
- self.button('option', 'icons', {primary:toAbsolute ? 'ui-icon-circle-close' : 'ui-icon-circle-check'});
526
- //toggle the class to force either fixed (add class) or absolute (remove class)...
527
- dialogBox.toggleClass('cmw-assistance-dialog-fixed', !toAbsolute);
528
- //have to reset dialog's maxHeight here, *before* re-positioning, because UI will screw up the position when we set it!...
529
- dialog.dialog('option', {maxHeight: toAbsolute ? !toAbsolute : data.cmwMaxHeight});
530
- //re-position the dialog (have to use CSS because UI dialog's position option doesn't hack it!)...
531
- dialogBox.css('top', newTop);
532
- return false;
533
- },
534
- /**
535
- * click handler for an item in the menu structure : sets or clears current menu item and its ancestors
536
- * @this {element} Anchor element clicked on
537
- * @param {object} e Event object
538
- * @return {boolean} false
539
- */
540
- clickMenu = function(e){
541
- var self = $(this),
542
- cls = ['current-menu-item', 'current-menu-parent', 'current-menu-ancestor'],
543
- dialog = self.closest('.ui-dialog-content'),
544
- topOfMenu = dialog.find('.cmw-demo-themenu-ul'),
545
- inPath = self.find('span').not('.' + cls[0]).parentsUntil(topOfMenu, 'li'),
546
- i, n,
547
- appendCls = function(){
548
- this.title = this.title + ' ' + n.replace(' ', ' & ').replace(/-/g, ' ');
549
- };
550
- topOfMenu.find('.' + cls.join(',.')).removeClass(cls.join(' ')).each(function(){
551
- this.title = this.title.replace(/\s.*$/, '');
552
- });
553
- for(i = 0; i < inPath.length; i++){
554
- n = i === 1 ? cls.join(' ') : cls[0];
555
- inPath.eq(i).children('.cmw-item').find('span').addClass(n).each(appendCls);
556
- if(cls.length > 1){
557
- cls.shift();
558
- }
559
- }
560
- //run update() via assistance()...
561
- assistance.call( $(dialog.data().cmwOnchange).get(0) );
562
- return false;
563
- }, //end clickMenu()
564
- /**
565
- * click handler for an item in the Basic Output list : triggers a click on the respective menu structure item
566
- * @this {element} Anchor element clicked on
567
- * @param {object} e Event object
568
- * @return {boolean} false
569
- */
570
- clickOutput = function(e){
571
- $(this).closest('.ui-dialog-content').find('.cmw-item')
572
- .eq( this.href.split('#')[1] )
573
- .not(':has(.current-menu-item)').trigger('click');
574
- this.blur();
575
- return false;
576
- }, //end clickOutput()
577
- /**
578
- * click handler for assist's menu tree expander/collapser
579
- *
580
- * @this {element} Element clicked on
581
- * @param {object} e Event object
582
- * @return {boolean} false
583
- */
584
- clickTreeExpander = function(e){
585
- var direction = /1-e/.test(this.className) ? 'slideUp' : 'slideDown';
586
- $(this).toggleClass('ui-icon-triangle-1-s ui-icon-triangle-1-e').prev('ul')[direction]();
587
- return false;
588
- },
589
- /**
590
- * click handler for a tick or cross against any item in the menu structure (does not modify any classes)
591
- * @this {element} Tick or Cross clicked on
592
- * @param {object} e Event object
593
- * @return {boolean} false
594
- */
595
- clickTickCross = function(e){
596
- var self = $(this),
597
- tickOrCross = self.hasClass('cmw-tick') ? 'tick' : 'cross',
598
- item = self.parent(),
599
- topOfMenu = item.closest('.cmw-demo-themenu-ul'),
600
- hasInheritTickCross = item.hasClass('cmw-inherit-' + tickOrCross),
601
- hasTickCross = hasInheritTickCross || item.hasClass('cmw-has-' + tickOrCross),
602
- inheritsTickCrossFrom = hasTickCross ? $([]) : item.parentsUntil(topOfMenu, '.cmw-inherit-' + tickOrCross),
603
- widgetField = $( item.closest('.ui-dialog-content').data().cmwOnchange )
604
- .find(tickOrCross === 'tick' ? '.cmw-setitems' : '.cmw-exclusions'),
605
- sampleSet;
606
- //if we're using the alternative settings, then click a tick/cross is disabled because they are set according to the
607
- //alternative settings, which are not modifiable via this instance of the assist!
608
- if(!topOfMenu.hasClass('cmw-using-alternative') && widgetField.length){ //should never not find it!
609
- //A. if this item hasInheritTickCross then this click will remove tickCross entirely and all inheritance will be lost
610
- //B. else* if this item hasTickCross then this click will either
611
- // B1. if the item has no descendants, or we're running legacy version, remove the tickCross
612
- // B2. else add inheritance to it
613
- //C. else* if this item inheritsTickCross then this click will
614
- // - disinherit the relevant ancestor (keeping tickCross!)
615
- // - and set all its descendants - bar this* item! - to tickCross
616
- //D. else* this click will add tickCross (without inheritance) to this item
617
-
618
- //find everything that currently has tickCross, and for (A) remove this item, or for (D) add this item...
619
- //note : for (B) this item is already included, and for (C) this item is not already included and we don't want it to be
620
- sampleSet = topOfMenu
621
- .find('.cmw-has-' + tickOrCross)[ hasInheritTickCross || inheritsTickCrossFrom.length ? 'not' : 'add' ](item);
622
- //(A) & (D) are now sorted and can be forgotten about
623
- //for (B1), remove this item...
624
- if(hasTickCross && (!item.children('ul').length || topOfMenu.parent().hasClass('cmw-version-210'))){
625
- sampleSet = sampleSet.not(item);
626
- }
627
- else
628
- //for (B2), because we're adding inheritance, any descendant of this item that is currently tickCross now needs to be excluded...
629
- if(hasTickCross && !hasInheritTickCross){
630
- sampleSet = sampleSet.not( item.find('.cmw-has-' + tickOrCross) );
631
- }
632
- //(B) is now also sorted and can be forgotten about
633
- //for (C) we need to find the ancestor providing the inheritance and add in all its descendants bar this item...
634
- sampleSet = sampleSet.add( inheritsTickCrossFrom.find('li').not(item) );
635
- //map what's left to the associated item id, appending a plus sign for inheritance...
636
- sampleSet = sampleSet.map(function(){
637
- var plus = this === item[0] || this === inheritsTickCrossFrom.get(0) ? hasTickCross : $(this).hasClass('cmw-inherit-' + tickOrCross);
638
- return $(this).data().itemid + (plus ? '+' : '');
639
- })
640
- .get().join( /(,|^\d+\+?$)/.test( $.trim(widgetField.val()) || ',' ) ? ',' : ' ' );
641
- widgetField.val(sampleSet).trigger('change');
642
- }
643
- this.blur();
644
- return false;
645
- }, //end clickTickCross()
646
- /**
647
- * closes an open dialog when the widget is closed
648
- * @this {element} The .widget-action or .widget-control-close clicked on
649
- * @param {object} e Event object
650
- */
651
- closeDialog = function(e){
652
- var widgetContainer = $(this).closest('div.widget'),
653
- containerParent = widgetContainer.parent();
654
- //in widget customizer, opening one widget closes any other open widget without triggering anything that
655
- //would make CMW close the assist ... which is good! however, I also don't want the assist to close when
656
- //[re]opening a CMW widget, so for the customizer I need to check that the widget is "expanded" before
657
- //making the assist close...
658
- if(!containerParent.hasClass('customize-control-widget_form') || containerParent.hasClass('expanded')){
659
- findOnchange(widgetContainer).each(function(){
660
- var dialog = $('#' + $(this).data().cmwDialogId);
661
- if(dialog.length && dialog.dialog('isOpen')){
662
- dialog.dialog('close');
663
- }
664
- });
665
- }
666
- }, //end closeDialog()
667
- /**
668
- * creates the UI Dialog for the widget's assist
669
- * @param {object} data Information required to build the dialog
670
- * @return {object} jQuery object of the created Dialog
671
- */
672
- createDialog = function(data){
673
- var dialogOpts = {
674
- autoOpen: false,
675
- //initial width is the lowest of 600px and 90% of window width...
676
- width: Math.min($(window).width() * 0.9, 600),
677
- //starting out at fixed, so max the resizable height at 40px less than window height so that entire dialog box is visible...
678
- maxHeight: $(window).height() - 40,
679
- modal: false,
680
- containment: 'window',
681
- create: function(){
682
- //add a "fixed" button to the titlebar, enabling switching between fixed and absolute positioning for the dialog...
683
- var dialogBox = $(this).closest('.ui-dialog');
684
- if(dialogBox.hasClass('cmw-assistance-dialog-fixed')){
685
- $('<button/>').addClass('cmw-dialog-fixed-absolute')
686
- .button({label:data.cmwDialogFixed, icons:{primary:'ui-icon-circle-check'}})
687
- .appendTo( dialogBox.find('.ui-dialog-titlebar') )
688
- .on('click', clickFixedAbsolute);
689
- }
690
- },
691
- dialogClass: 'cmw-assistance-dialog cmw-assistance-dialog-fixed'
692
- },
693
- msgs = $.map(['SetCurrent', 'Inclusions', 'Exclusions', 'Fallback', 'Alternative'], function(v){
694
- return '<div class="cmw-demo-' + v.toLowerCase() + ' cmw-demo-small">' + (data['cmwDialog' + v] || '') + '</div>';
695
- }),
696
- dialog = $('<div/>', {id:data.cmwDialogId}).addClass(widgetCustomMenuWizardClass('dialog'))
697
- .append(
698
- $('<div/>').addClass('cmw-demo-themenu cmw-version-' + data.cmwDialogVersion.replace(/\./g, ''))
699
- .html('<em class="cmw-demo-small">' + data.cmwDialogPrompt + '</em>')
700
- )
701
- .append(
702
- $('<div/>').addClass('cmw-demo-theoutput')
703
- .html('<em class="cmw-demo-small">' + data.cmwDialogOutput + '</em><em class="cmw-demo-plugin-version cmw-demo-small">v' + data.cmwDialogVersion + '</em>' + msgs.shift() + '<div class="cmw-demo-theoutput-wrap ui-corner-all"></div>' + msgs.join(''))
704
- )
705
- .append(
706
- $('<div/>').addClass('cmw-demo-theshortcode')
707
- .html('<code class="ui-corner-all"></code><div class="cmw-find-shortcodes"><a href="#" class="button-secondary ' + widgetCustomMenuWizardClass('find-shortcodes') + '" data-nonce="' + (data.cmwDialogNonce || '') + '" title="' + data.cmwDialogShortcodes + '"><span class="spinner"></span><span>[&hellip;]</span></a></div><div class="cmw-demo-found-shortcodes cmw-demo-small ui-corner-all"></div>')
708
- );
709
- dialog.dialog(dialogOpts);
710
- dialog.find('.cmw-demo-themenu')
711
- .on('click', '.cmw-tick,.cmw-cross', clickTickCross)
712
- .on('click', '.cmw-item', clickMenu);
713
- dialog.find('.cmw-demo-theoutput').on('click', 'a', clickOutput);
714
- return dialog;
715
- }, //end createDialog()
716
- /**
717
- * removes associated dialog when the widget is deleted
718
- * @this {element} The .widget-control-remove clicked on
719
- * @param {object} e Event object
720
- */
721
- removeDialog = function(e){
722
- findOnchange($(this).closest('div.widget')).each(function(){
723
- var dialog = $('#' + $(this).data().cmwDialogId);
724
- if(dialog.length){
725
- dialog.dialog('destroy');
726
- dialog.remove();
727
- }
728
- });
729
- }, //end removeDialog()
730
- /**
731
- * (re)sets the dialog's title
732
- * @param {object} dialog jQuery object of the dialog
733
- * @param {object} oc jQuery object of the onchange wrapper
734
- */
735
- setDialogTitle = function(dialog, oc){
736
- dialog.dialog('option', 'title', 'CMW : ' + ( oc.find('.cmw-widget-title').val() || dialog.data().cmwUntitled ) + ' [' + oc.find('.cmw-select-menu').find('option:selected').text() + ']' );
737
- }, //end setDialogTitle()
738
- /**
739
- * creates a new list of menu items and inserts it into the dialog content in place of any previous one
740
- * @param {object} dialog jQuery object of the dialog
741
- */
742
- createMenu = function(dialog){
743
- var oc = $(dialog.data().cmwOnchange),
744
- menuid = parseInt(oc.find('.cmw-select-menu').val(), 10),
745
- currentmenu = dialog.find('.cmw-demo-themenu-ul'),
746
- trace = [],
747
- maxLevel = 0,
748
- menu = '',
749
- outdentedExpander = function(x){
750
- //x is the level of the *previous* item; so if previous item was at level 4, then its parent was at level 3, and
751
- //was therefore indented twice (given that a level 1 item is not indented), so number of outdents is x - 2!
752
- //each level is indented by 2.4em; subtract 2 from x and multiply by 2.4em, then add another 2em...
753
- return x > 1 ? '<a href="#" class="' + widgetCustomMenuWizardClass('colexp') + ' ui-icon ui-icon-triangle-1-e" style="left:-' + (((x - 2) * 2.4) + 2) + 'em;">&nbsp;</a>' : '';
754
- };
755
- if(!currentmenu.length || currentmenu.data('menuid') !== menuid){
756
- oc.find('.cmw-assist-items optgroup').find('option').each(function(i){
757
- var self = $(this),
758
- level = self.data().cmwLevel;
759
- while(level < trace.length){
760
- menu += '</li></ul>' + outdentedExpander(trace.length);
761
- trace.pop();
762
- }
763
- if(level > trace.length){
764
- menu += '<ul>';
765
- }else{
766
- menu += '</li>';
767
- trace.pop();
768
- }
769
- //data-level is 1-based, with 1 being root
770
- //data-trace is the menu item ids of this item's ancestors, from root down to parent (inclusive)
771
- menu += '<li class="level-' + level + '" data-itemid="' + this.value + '" data-level="' + level + '" data-trace="' + trace.join(',') + '">';
772
- menu += '<a href="#" class="cmw-cross ui-corner-all"></a>';
773
- menu += '<a class="cmw-item ui-corner-all" href="#" data-indx="' + i + '"><span class="ui-corner-all" title="#' + this.value + '">' + $.trim(self.text());
774
- menu += '</span></a><a href="#" class="cmw-tick ui-corner-all"></a>';
775
- trace.push(this.value);
776
- if(level > maxLevel){
777
- maxLevel = level;
778
- }
779
- });
780
- while(trace.length){
781
- menu += '</li></ul>' + outdentedExpander(trace.length);
782
- trace.pop();
783
- }
784
- currentmenu.remove();
785
- dialog.find('.cmw-demo-themenu')
786
- .append( $(menu).addClass('cmw-demo-themenu-ul').data({maxLevel:maxLevel, menuid:menuid}) );
787
- }
788
- }, //end createMenu()
789
- /**
790
- * toggles the assist dialog open/closed, creating it if necessary
791
- * @this {element} A The assist anchor
792
- * @param {object} e Event object
793
- * @return {boolean} false
794
- */
795
- clickAssist = function(e){
796
- var self = $(this),
797
- oc = findOnchange(0, self),
798
- data = oc.data(),
799
- dialog = $( '#' + data.cmwDialogId );
800
- if(!dialog.length){
801
- dialog = createDialog(data).data({
802
- cmwOnchange: '#' + oc.attr('id'),
803
- cmwUntitled: '[' + data.cmwDialogUntitled + ']'
804
- });
805
- }
806
- if(dialog.dialog('isOpen')){
807
- dialog.dialog('close');
808
- }else{
809
- createMenu(dialog);
810
- setDialogTitle(dialog, oc);
811
- dialog.dialog('open');
812
- assistance.call(oc[0]);
813
- }
814
- this.blur();
815
- return false;
816
- }; //end clickAssist()
817
-
818
- /**
819
- * The assist object, containing functions that do most of the version-specific work.
820
- * There is a default set of functions, and any different legacy versions that are still
821
- * supported should be under their own 'vNNN' object (eg. 'v210'). Which set of functions
822
- * is called is determined by assistance(), which is the only entry point (into update()),
823
- * and is governed by the widget form (specifically, the version property of the data
824
- * attached to the -onchange wrapper).
825
- */
826
- cmwAssist = {
827
- /**
828
- * sets any fields that are dependent on the selected menu
829
- * @param {object} oc jQuery of the widget's onchange wrapper
830
- * @param {integer} maxLevel Maximum level of the selected menu
831
- */
832
- setLevels : function(oc, maxLevel){
833
- if(!maxLevel){
834
- return;
835
- }
836
- var theSelect = oc.find('.cmw-branch-start'),
837
- level = theSelect.val();
838
- theSelect.find('optgroup').each(function(i){
839
- var self = $(this),
840
- opts = self.find('option'),
841
- ct = opts.length,
842
- texts = self.data();
843
- if(i){
844
- //absolute...
845
- opts.slice(maxLevel).remove();
846
- while(ct < maxLevel){
847
- ++ct;
848
- self.append( $('<option/>', {value:ct}).text(ct) );
849
- }
850
- }else{
851
- //relative...
852
- //if maxLevel is 1 : the item (1 option)
853
- //if maxLevel is 2 : -1, the item, +1 (3 options)
854
- //if maxLevel is 3 : -2, -1, the item, +1, +2 (5 options)
855
- //etc, etc
856
- ct = (ct + 1) / 2;
857
- if(ct > maxLevel){
858
- opts.each(function(i, el){
859
- if(Math.abs(el.value) >= maxLevel){
860
- $(el).remove();
861
- }
862
- });
863
- }
864
- while(ct < maxLevel){
865
- self.prepend( $('<option/>', {value:-ct}).text(-ct + (ct > 1 ? '' : ' (' + texts.cmwTextParent + ')')) )
866
- .append( $('<option/>', {value:'+' + ct}).text('+' + ct + (ct > 1 ? '' : ' (' + texts.cmwTextChildren + ')')) );
867
- ++ct;
868
- }
869
- }
870
- });
871
- //if level is absolute and > maxLevel, set to 1...
872
- if(/^\d+$/.test(level)){
873
- if(level > maxLevel){
874
- theSelect.val('1');
875
- }
876
- //if level is relative, not 'the item', and absolutely >= maxLevel, set to '' (the item)...
877
- }else if(level !== '' && Math.abs(level) >= maxLevel){
878
- theSelect.val('');
879
- }
880
-
881
- oc.find('.cmw-ancestors,.cmw-ancestor-siblings').each(function(){
882
- var self = $(this),
883
- ct = (self.find('option').length + 1) / 2, //should never be below 2
884
- v = self.val(),
885
- minLevel = Math.max(2, maxLevel);
886
- if(Math.abs(v) >= maxLevel){
887
- self.val( v < 0 ? 1 - maxLevel : maxLevel - 1 );
888
- }
889
- if(ct !== minLevel){
890
- self.find('optgroup').each(function(i, el){
891
- var optgroup = $(el),
892
- text = optgroup.data().cmwTextForOption,
893
- j;
894
- if(ct > minLevel){
895
- optgroup.find('option').slice( minLevel - ct ).remove();
896
- }
897
- for(j = ct; j < minLevel; j++){
898
- optgroup.append( $('<option/>', {value:i ? j : -j}).text( text.replace('%d', i ? j : -j) ) );
899
- }
900
- });
901
- }
902
- });
903
-
904
- oc.find('.cmw-include-level,.cmw-exclude-level').each(function(){
905
- var self = $(this),
906
- options = self.find('option'),
907
- ct = (options.length - 1) / 3,
908
- v = self.val(),
909
- above = options.eq(2).text(),
910
- below = options.eq(3).text();
911
- options.slice( (maxLevel * 3) + 1 ).remove();
912
- while(ct < maxLevel){
913
- ++ct;
914
- self.append( $('<option/>', {value:ct}).text(ct) )
915
- .append( $('<option/>', {value:ct + '-'}).text(above.replace(/\d+/, ct)) )
916
- .append( $('<option/>', {value:ct + '+'}).text(below.replace(/\d+/, ct)) );
917
- }
918
- if(parseInt(v, 10) > maxLevel){
919
- self.val('');
920
- }
921
- });
922
-
923
- //do the easy levels...
924
- oc.find('.cmw-set-levels').each(function(){
925
- var self = $(this),
926
- data = self.data(),
927
- txt = data.cmwTextLevels || '',
928
- leave = data.cmwSetLevels || 0,
929
- opts = self.find('option'),
930
- ct = opts.length - leave;
931
- //if current value exceeds maxLevel, reset to first option...
932
- if(self.val() > maxLevel){
933
- self.val( opts.eq(0).val() );
934
- }
935
- //remove anything above maxLevel...
936
- self.find('option').slice(leave + maxLevel).remove();
937
- //append enough new options to bring up to maxLevel...
938
- while(ct < maxLevel){
939
- ++ct;
940
- self.append( $('<option/>', {value:ct}).text(ct + txt) );
941
- }
942
- });
943
- }, //end cmwAssist.setLevels()
944
- /**
945
- * enables/disables fields and swaps selected menus
946
- * @param {object} target jQuery of element responsible for 'change' event
947
- * @param {object} oc jQuery of the widget's onchange wrapper
948
- */
949
- setFields : function(target, oc){
950
- var byBranchCheckbox = oc.find('.cmw-bybranch'),
951
- byItems = oc.find('.cmw-byitems').prop('checked'),
952
- notByBranch = byItems || !byBranchCheckbox.prop('checked'),
953
- menuItems = oc.find('.cmw-assist-items'),
954
- selectedItem = parseInt(menuItems.val(), 10),
955
- fallback;
956
- //change of menu? : make sure the correct optgroup of menu items is used...
957
- if(target.hasClass('cmw-select-menu')){
958
- selectedItem = swapItems(menuItems, selectedItem, target[0].selectedIndex);
959
- this.setLevels(oc, (menuItems.find('optgroup').data() || {}).cmwMaxLevel);
960
- //if level is changed, switch to by-level filtering...
961
- }else if(target.hasClass('cmw-level')){
962
- notByBranch = true;
963
- byItems = !notByBranch;
964
- oc.find('.cmw-bylevel').prop('checked', notByBranch);
965
- //if by-branch's branch is changed, switch to by-branch filtering...
966
- }else if(target.is(menuItems)){
967
- notByBranch = byItems = false;
968
- byBranchCheckbox.prop('checked', !notByBranch);
969
- //change of items?, switch to by-items filtering...
970
- }else if(target.hasClass('cmw-setitems')){
971
- byItems = true;
972
- notByBranch = byItems;
973
- oc.find('.cmw-byitems').prop('checked', byItems);
974
- //if ancestors is cleared, clear ancestor siblings...
975
- }else if(target.hasClass('cmw-ancestors') && target.val() === '0'){
976
- oc.find('.cmw-ancestor-siblings').val('0');
977
- //if include ancestor's siblings is changed to a value, and ancestors is empty, set ancestors from ancestor siblings...
978
- }else if(target.hasClass('cmw-ancestor-siblings') && target.val() !== '0' && oc.find('.cmw-ancestors').val() === '0'){
979
- oc.find('.cmw-ancestors').val( target.val() );
980
- }
981
-
982
- fallback = oc.find('.cmw-fallback').val();
983
- $.each( //disable if...
984
- { '-ss' : byItems, //...is Items
985
- '-ud' : byItems || oc.find('.cmw-depth').val() < 1, //...is Unlimited Depth
986
- 'not-br' : notByBranch, //...is NOT Branch
987
- 'not-br-ci' : notByBranch || !!selectedItem, //...is NOT Branch:Current Item
988
- 'not-fb-pc' : notByBranch || !!selectedItem || (fallback !== 'parent' && fallback !== 'current'), //...is NOT fallback to parent or current
989
- 'not-sw' : !!oc.find('.cmw-switchable').filter(function(){ return !$(this).val(); }).length //...is NOT switchable (missing rither condition or stage)
990
- },
991
- function(k, v){
992
- //as of v3.1.0, the input fields (+ selects, etc) are no longer disabled, because the customizer "pseudo-saves" the
993
- //form which wipes out values that you may not have wanted to lose!
994
- oc.find('.cmw-disableif' + k).toggleClass('cmw-colour-grey', v);
995
- });
996
- }, //end cmwAssist.setFields()
997
- /**
998
- * create and return the shortcode equivalent
999
- * @param {object} settings Widget settings
1000
- * @return {string} Shortcode
1001
- */
1002
- shortcode : function(settings){
1003
- var args = {
1004
- 'menu' : settings.menu
1005
- },
1006
- byBranch = settings.filter === 'branch',
1007
- byItems = settings.filter === 'items',
1008
- byLevel = !byBranch && !byItems,
1009
- v, m, n, c;
1010
- //take notice of the widget's hide_title flag...
1011
- if(settings.title && !settings.hide_title){
1012
- args.title = [settings.title];
1013
- }
1014
- //byLevel is the default (no branch & no items), as is level=1, so we only *have* to specify level if it's greater than 1...
1015
- if(byLevel && settings.level > 1){
1016
- args.level = settings.level;
1017
- }
1018
- //specifying branch sets byBranch, overriding byLevel...
1019
- if(byBranch){
1020
- args.branch = settings.branch || 'current';
1021
- //start_at only *has* to be specified if not empty...
1022
- if(settings.branch_start){
1023
- args.start_at = [settings.branch_start];
1024
- }
1025
- //start_mode may be brought into play by a fallback so always specify it...
1026
- if(settings.start_mode === 'level'){
1027
- args.start_mode = 'level';
1028
- }
1029
- //allow_all_root is only applicable to byBranch...
1030
- if( settings.allow_all_root ){
1031
- args.allow_all_root = 1;
1032
- }
1033
- }
1034
- //specifying items set byItems, overriding byLevel & byBranch...
1035
- if(byItems){
1036
- args.items = [settings._items];
1037
- }
1038
- //depth if greater than 0...
1039
- if(settings.depth > 0){
1040
- args.depth = settings.depth;
1041
- }
1042
- //depth relative to current item is only applicable if depth is not unlimited...
1043
- if(settings.depth_rel_current && settings.depth > 0){
1044
- args.depth_rel_current = 1;
1045
- }
1046
- //fallbacks...
1047
- //no children : branch = current item...
1048
- if(byBranch && !settings.branch){
1049
- //format = quit|parent|current [,+siblings] [,depth] eg. "parent,+siblings,1" or "current,2" or "current,+siblings" or "parent" or "quit"
1050
- if(settings.fallback){
1051
- args.fallback = [settings.fallback];
1052
- if(settings.fallback !== 'quit'){
1053
- if(settings.fallback_siblings){
1054
- args.fallback.push('+siblings');
1055
- }
1056
- if(settings.fallback_depth){
1057
- args.fallback.push( settings.fallback_depth );
1058
- }
1059
- }
1060
- }
1061
- }
1062
- //branch ancestor inclusions...
1063
- if(byBranch && settings.ancestors){
1064
- args.ancestors = settings.ancestors;
1065
- if(settings.ancestor_siblings){
1066
- args.ancestor_siblings = settings.ancestor_siblings;
1067
- }
1068
- }
1069
- //inclusions by level...
1070
- if(settings.include_level){
1071
- args.include_level = [settings.include_level];
1072
- }
1073
- //exclusions by id...
1074
- if(settings._exclude){
1075
- args.exclude = [settings._exclude];
1076
- }
1077
- //...and by level...
1078
- if(settings.exclude_level){
1079
- args.exclude_level = [settings.exclude_level];
1080
- }
1081
- //title from...
1082
- n = [];
1083
- //...current and current root (in that order of priority) are exclusive...
1084
- if(settings.title_from_current){
1085
- n.push('current');
1086
- }else if(settings.title_from_current_root){
1087
- n.push('current-root');
1088
- }
1089
- //...branch and branch root (in that order of priority) are exclusive...
1090
- if(byBranch && settings.title_from_branch){
1091
- n.push('branch');
1092
- }else if(byBranch && settings.title_from_branch_root){
1093
- n.push('branch-root');
1094
- }
1095
- if(n.length){
1096
- args.title_from = n;
1097
- //...title_linked is only relevant if title_from is set...
1098
- if(settings.title_linked){
1099
- args.title_linked = 1;
1100
- }
1101
- }
1102
- //switches...
1103
- for(n in {siblings:1, flat_output:1, ol_root:1, ol_sub:1, fallback_ci_parent:1}){
1104
- if(settings[n]){
1105
- args[n] = 1;
1106
- }
1107
- }
1108
- //strings...
1109
- v = {contains_current:'', container:'div', container_id:'', container_class:'', menu_class:'menu-widget', widget_class:''};
1110
- for(n in v){
1111
- if(settings[n] !== v[n]){
1112
- args[n] = [settings[n]];
1113
- }
1114
- }
1115
- v = {wrap_link:'before', wrap_link_text:'link_before'};
1116
- for(n in v){
1117
- m = settings[v[n]].toString().match(/^<(\w+)/);
1118
- if(m && m[1]){
1119
- args[n] = [m[1]];
1120
- }
1121
- }
1122
- //alternative...
1123
- if(settings.switch_if){
1124
- if(settings.switch_at){
1125
- args.alternative = [settings.switch_if, settings.switch_at];
1126
- c = alternativeStripDown(settings.switch_to);
1127
- }
1128
- }
1129
- //build the shortcode...
1130
- v = [];
1131
- for(n in args){
1132
- //array indicates join (with comma sep) & surround it in double quotes, otherwise leave 'as-is'...
1133
- v.push( $.isArray(args[n]) ? n + '="' + args[n].join(',') + '"' : n + '=' + args[n] );
1134
- }
1135
- //NB at v3.0.0, the shortcode changed from custom_menu_wizard to cmwizard (the previous version is still supported)
1136
- return '[cmwizard ' + v.join(' ') + (!c ? '/]' : ']' + c + '[/cmwizard]');
1137
- }, //end cmwAssist.shortcode()
1138
- /**
1139
- * performs the filtering and update of the menu structure
1140
- * note to self : this function must not alter whatever settings are passed into it, otherwise subsequent update
1141
- * actions, such as shortcode output, will be screwed!
1142
- * @param {object} dialog jQuery of dialog
1143
- * @param {array} settings Widget config
1144
- * @param {integer} usingAlts Indicates whether alternative settings are being used or not
1145
- * @return {boolean|array} False if completed, or the alternative settings if they should be applied
1146
- */
1147
- structureUpdate : function(dialog, settings, usingAlts){
1148
-
1149
- var tobLevel = -1,
1150
- lastVisibleLevel = 9999,
1151
- hasIncl = 0,
1152
- hasExcl = 0,
1153
- fallback = '',
1154
- altSettings = null,
1155
- theBranchItem, hasCurrent, topOfBranch, i, j, k, x, y,
1156
- stage = 'menu',
1157
- byBranch = settings.filter === 'branch',
1158
- byItems = settings.filter === 'items',
1159
- byLevel = !byBranch && !byItems,
1160
- ciBranch = byBranch && !settings.branch,
1161
- canSwitch = !usingAlts && !!settings.switch_if && !!settings.switch_at,
1162
- topOfMenu = dialog.find('.cmw-demo-themenu-ul'),
1163
- maxLevel = topOfMenu.data().maxLevel,
1164
- currentItemLI = topOfMenu.find('.current-menu-item').closest('li'),
1165
- currentItemLevel = currentItemLI.length ? currentItemLI.data().level : -1,
1166
- items = topOfMenu.find('li').removeData('included').removeClass('title-from-item'),
1167
- local_depth = settings.depth,
1168
- local_depth_rel_current = settings.depth_rel_current,
1169
- //ticks and crosses (need to be run against the full set of items)...
1170
- exclusions = filterTickCross(items, settings, 'cross');
1171
-
1172
- //check for current item and switch...
1173
- hasCurrent = items.length && currentItemLI.is(items);
1174
- if(settings.contains_current === stage && !hasCurrent){
1175
- items = $([]);
1176
- }
1177
- if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1178
- altSettings = alternativeUse(settings, dialog);
1179
- if(altSettings !== false){
1180
- //cop out with alternative settings!...
1181
- return altSettings;
1182
- }
1183
- }
1184
-
1185
- stage = 'primary';
1186
- //primary filter : items...
1187
- if(byItems && items.length){
1188
- items = filterTickCross(items, settings, 'tick');
1189
- }
1190
-
1191
- //primary filter : branch...
1192
- if(byBranch && items.length){
1193
- topOfBranch = ciBranch ? currentItemLI : items.filter('[data-itemid=' + settings.branch + ']');
1194
- if(topOfBranch.length){
1195
- tobLevel = topOfBranch.data().level || 0;
1196
- items = topOfBranch.add( topOfBranch.find('li') );
1197
- //since topOfBranch can change later on...
1198
- theBranchItem = topOfBranch;
1199
- }else{
1200
- items = $([]);
1201
- }
1202
- }
1203
-
1204
- //primary filter : level...
1205
- if(byLevel && items.length && settings.level > 1){
1206
- for(i = 1, j = []; i < settings.level; i++){
1207
- j.push('.level-' + i);
1208
- }
1209
- items = items.not( j.join(',') );
1210
- }
1211
-
1212
- //check for current item and switch...
1213
- hasCurrent = items.length && currentItemLI.is(items);
1214
- if(settings.contains_current === stage && !hasCurrent){
1215
- items = $([]);
1216
- }
1217
- if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1218
- altSettings = alternativeUse(settings, dialog);
1219
- if(altSettings !== false){
1220
- //cop out with alternative settings!...
1221
- return altSettings;
1222
- }
1223
- }
1224
-
1225
- stage = 'secondary';
1226
- //secondary filter : level...
1227
- if(byLevel && items.length && local_depth){
1228
- i = local_depth_rel_current && currentItemLevel >= settings.level
1229
- //if the limited depth is relative to current item, and current item can be found at or below the start level...
1230
- ? currentItemLevel
1231
- //set relative to start level...
1232
- : settings.level;
1233
- i += local_depth;
1234
- //note that i has been set to the first level *not* wanted!
1235
- if(i <= maxLevel){
1236
- for(j = []; i <= maxLevel; i++){
1237
- j.push('.level-' + i);
1238
- }
1239
- //filter to remove...
1240
- items = items.not( j.join(',') );
1241
- }
1242
- }
1243
-
1244
- //secondary filter : branch...
1245
- if(byBranch && items.length){
1246
- //convert start level to integer...
1247
- j = parseInt(settings.branch_start, 10);
1248
- //convert relative to absolute (max'd against 1)...
1249
- j = isNaN(j) || !j ? tobLevel : (settings.branch_start.match(/^(\+|-)/) ? Math.max(1, tobLevel + j) : j);
1250
-
1251
- //in order to be eligible for a no-kids fallback:
1252
- // - branch must be current item
1253
- // - fallback must be set
1254
- // - current item has no kids
1255
- if(ciBranch && settings.fallback && !currentItemLI.find('li').length){
1256
- //yes, we have a fallback situation...
1257
- fallback = 'cmw-fellback-to-' + settings.fallback;
1258
- if(settings.fallback === 'quit'){
1259
- //copout : just set secondary start level beyond maxLevel...
1260
- j = maxLevel + 1;
1261
- }else{
1262
- //for current, fall back to tob; for parent, fall back to tob - 1, ensuring that we don't fall back further than root...
1263
- j = settings.fallback === 'current' || tobLevel < 2 ? tobLevel : tobLevel - 1;
1264
- //if fallback depth is specified, override depth and set to relative-to-current...
1265
- if(settings.fallback_depth){
1266
- local_depth = settings.fallback_depth;
1267
- local_depth_rel_current = 1;
1268
- }
1269
- }
1270
- }
1271
-
1272
- //j is the secondary start level, tobLevel is the primary level
1273
- //easy result : if j > maxLevel then there are no matches...
1274
- if(j > maxLevel){
1275
- items = $([]);
1276
- }else{
1277
- //if secondary start is higher up the structure than primary start, reset the tob...
1278
- if(j < tobLevel){
1279
- topOfBranch = topOfBranch.parentsUntil(topOfMenu, 'li.level-' + j);
1280
- }
1281
- //do we want (and need) to force starting with the entire level...
1282
- // - only relevant if secondary start is at or above primary start
1283
- // - and if secondary level is root then allow_all_root must be set
1284
- if(settings.start_mode === 'level' && j <= tobLevel && (j > 1 || settings.allow_all_root)){
1285
- //...reset items to eveything at tob's level, plus all their descendants...
1286
- items = topOfBranch.parent().find('li');
1287
- }else if(j < tobLevel){
1288
- //tob has changed so reset items (to just tob and descendants)...
1289
- items = topOfBranch.add( topOfBranch.find('li') );
1290
- }
1291
- //if falling back and siblings are required, add them in...
1292
- //note that root level sibling inclusion is still governed by allow_all_root!
1293
- if(!!fallback && settings.fallback_siblings && items.length && (j > 1 || settings.allow_all_root)){
1294
- items = items.add( topOfBranch.siblings('li.level-' + j) );
1295
- }
1296
- }
1297
- //may have a tob but might not have any items!...
1298
- if(items.length){
1299
- //reset tob level (regardless of whether tob has changed)...
1300
- tobLevel = j;
1301
- //is depth unlimited?...
1302
- k = 9999;
1303
- if(local_depth){
1304
- //is (limited) depth relative to current item, and is there an eligible current item to measure against?...
1305
- k = local_depth_rel_current && currentItemLevel >= tobLevel && items.filter(currentItemLI).length
1306
- ? currentItemLevel
1307
- : tobLevel;
1308
- k += local_depth;
1309
- lastVisibleLevel = k - 1;
1310
- }
1311
- //note that k has been set to the first level (after those wanted) that is *not* wanted!
1312
- for(i = 1, j = []; i <= maxLevel; i++){
1313
- if(i >= tobLevel && i < k){
1314
- j.push('.level-' + i);
1315
- }
1316
- }
1317
- //filter to keep...
1318
- items = items.filter( j.join(',') );
1319
- }
1320
- }
1321
-
1322
- //check for current item and switch...
1323
- hasCurrent = items.length && currentItemLI.is(items);
1324
- if(settings.contains_current === stage && !hasCurrent){
1325
- items = $([]);
1326
- }
1327
- if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1328
- altSettings = alternativeUse(settings, dialog);
1329
- if(altSettings !== false){
1330
- //cop out with alternative settings!...
1331
- return altSettings;
1332
- }
1333
- }
1334
-
1335
- stage = 'inclusions';
1336
- //branch inclusions...
1337
- //NB: only applicable if there are already items
1338
- if(byBranch && items.length){
1339
- //branch ancestors, possibly with their siblings : but only if the original branch item is either
1340
- //in items or is below lastVisibleLevel; ALSO, do not show ancestors below lastVisibleLevel!
1341
- j = theBranchItem.data().level;
1342
- if(settings.ancestors && (theBranchItem.is(items) || j > lastVisibleLevel)){
1343
- x = settings.ancestors;
1344
- //convert a relative level to an absolute one...
1345
- if(x < 0){
1346
- x = Math.max(1, j + x);
1347
- }
1348
- //ancestor siblings?...
1349
- y = settings.ancestor_siblings;
1350
- //convert a relative level to an absolute one...
1351
- if(y < 0){
1352
- y = Math.max(1, j + y);
1353
- }
1354
- //get the level classes for ancestors and siblings that need to be included...
1355
- for(i = x, j = [], k = []; i <= maxLevel; i++){
1356
- if(i <= lastVisibleLevel){
1357
- //ancestors...
1358
- j.push('.level-' + i);
1359
- if(y > 0 && i >= y){
1360
- //siblings...
1361
- k.push('.level-' + i);
1362
- }
1363
- }
1364
- }
1365
- //store current length of items...
1366
- x = items.length;
1367
- //find the ancestors...
1368
- j = theBranchItem.parentsUntil(topOfMenu, j.join(','));
1369
- //add new ones into items...
1370
- items = items.add( j.not(items).data('included', ' cmw-an-included-ancestor') );
1371
- //got ancestors, now what about their siblings?...
1372
- if(k.length){
1373
- //filter ancestors for those we want siblings of, and add new siblings into items...
1374
- items = items.add( j.filter( k.join(',') ).siblings('li').not(items).data('included', ' cmw-an-included-ancestor-sibling') );
1375
- }
1376
- //note how many have been added to items as a result of the includes...
1377
- hasIncl += items.length - x;
1378
- }
1379
- //branch siblings : only if the original branch item is currently in items...
1380
- if(settings.siblings && theBranchItem.is(items)){
1381
- j = items.length;
1382
- items = items.add( theBranchItem.siblings('li').data('included', ' cmw-an-included-sibling') );
1383
- hasIncl += items.length - j;
1384
- }
1385
- }
1386
- //other inclusions...
1387
- if(items.length && !!settings.include_level){
1388
- k = getLevelClasses(settings.include_level, maxLevel);
1389
- if(k){
1390
- //find and add...
1391
- j = items.length;
1392
- items = items.add( topOfMenu.find(k) );
1393
- hasIncl += items.length - j;
1394
- }
1395
- }
1396
-
1397
- //check for current item and switch...
1398
- hasCurrent = items.length && currentItemLI.is(items);
1399
- if(settings.contains_current === stage && !hasCurrent){
1400
- items = $([]);
1401
- }
1402
- if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1403
- altSettings = alternativeUse(settings, dialog);
1404
- if(altSettings !== false){
1405
- //cop out with alternative settings!...
1406
- return altSettings;
1407
- }
1408
- }
1409
-
1410
- stage = 'output';
1411
- //exclusions...
1412
- if(items.length && exclusions.length){
1413
- //filter to remove...
1414
- j = items.length;
1415
- items = items.not(exclusions);
1416
- hasExcl += j - items.length;
1417
- }
1418
- if(items.length && !!settings.exclude_level){
1419
- k = getLevelClasses(settings.exclude_level, maxLevel);
1420
- if(k){
1421
- //filter to remove...
1422
- j = items.length;
1423
- items = items.not(k);
1424
- hasExcl += j - items.length;
1425
- }
1426
- }
1427
-
1428
- //check for current item and switch...
1429
- hasCurrent = items.length && currentItemLI.is(items);
1430
- if(settings.contains_current === stage && !hasCurrent){
1431
- items = $([]);
1432
- }
1433
- if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1434
- altSettings = alternativeUse(settings, dialog);
1435
- if(altSettings !== false){
1436
- //cop out with alternative settings!...
1437
- return altSettings;
1438
- }
1439
- }
1440
-
1441
- //title from...
1442
- //...check current then current root...
1443
- if(settings.title_from_current && currentItemLI.length){
1444
- currentItemLI.addClass('title-from-item');
1445
- }else if(settings.title_from_current_root && currentItemLI.length){
1446
- currentItemLI.closest('.level-1').addClass('title-from-item');
1447
- }else if(byBranch && theBranchItem){
1448
- //...then branch, then branch root...
1449
- if(settings.title_from_branch){
1450
- theBranchItem.addClass('title-from-item');
1451
- }else if(settings.title_from_branch_root){
1452
- theBranchItem.closest('.level-1').addClass('title-from-item');
1453
- }
1454
- }
1455
-
1456
- //show/hide the fall back message...
1457
- dialog.find('.cmw-demo-fallback').data('fellback', fallback).toggleClass('updated', !!fallback);
1458
- //show/hide the 'select current item' prompt...
1459
- dialog.find('.cmw-demo-setcurrent').toggleClass('error', !currentItemLI.length && (!!settings.contains_current || ciBranch));
1460
- //hide the alternative message if not using - and haven't tried to use! - alternative settings...
1461
- if(!usingAlts && altSettings === null){
1462
- dialog.find('.cmw-demo-alternative').removeClass('error updated');
1463
- }
1464
- //...and toggle the demo menu structure's class (eg. applies an opacity to the ticks/crosses if alternative settings
1465
- // are being used, and prevents them being clicked on - because they can't be updated back into the current settings)...
1466
- dialog.find('.cmw-demo-themenu-ul').toggleClass('cmw-using-alternative', !!usingAlts);
1467
- //show/hide the inclusions/exclusions messages...
1468
- i = {inclusions:hasIncl, exclusions:hasExcl};
1469
- for(j in i){
1470
- k = dialog.find('.cmw-demo-' + j);
1471
- k.text( k.text().replace(/\d+$/, i[j]) ).toggleClass('updated', i[j] > 0);
1472
- }
1473
-
1474
- //toggle ticks and 'pick' the remaining items...
1475
- topOfMenu.toggleClass('cmw-demo-filteritems', byItems)
1476
- .find('.picked').not( items.addClass('picked') ).removeClass('picked');
1477
-
1478
- //returning false means we're done!...
1479
- return false;
1480
-
1481
- }, //end cmwAssist.structureUpdate()
1482
- /**
1483
- * updates the graphic menu structure from the widget data
1484
- * @param {element} el Element responsible for being here
1485
- */
1486
- update : function(el){
1487
- var target = $(el),
1488
- oc = findOnchange(0, target),
1489
- dialog = $('#' + oc.data().cmwDialogId),
1490
- settings, altSettings;
1491
-
1492
- if(target.hasClass('cmw-listen')){
1493
- //the widget field that changed is likely to have an effect on other widget fields...
1494
- this.setFields(target, oc);
1495
- }
1496
- settings = getSettings(oc);
1497
-
1498
- //dialog specific...
1499
- if(dialog.length && dialog.dialog('isOpen')){
1500
- dialog.dialog('moveToTop');
1501
- //if selected menu has changed, modify assist's structure...
1502
- if(target.hasClass('cmw-select-menu')){
1503
- createMenu(dialog);
1504
- }
1505
-
1506
- //if it's determined that altSettings should be used then they are intially returned and the structure update
1507
- //is re-run with those settings (and an indicator to say that alts are bing used). otherwise, the structure
1508
- //update simply returns false to say that the original settings should be used (either because alts don't apply,
1509
- //or because the alts that we've got are not valid and can't be used)
1510
- altSettings = this.structureUpdate(dialog, settings);
1511
- if(altSettings !== false){
1512
- //don't care what this returns (should only be false!) because we're not going to run it again...
1513
- this.structureUpdate(dialog, altSettings, true);
1514
- }
1515
-
1516
- //produce output...
1517
- setDialogTitle(dialog, oc);
1518
- //show output with whichever settings we ended up using...
1519
- showOutput(dialog, altSettings || settings);
1520
- altSettings = null;
1521
- } //end dialog specific
1522
-
1523
- //always use original settings to update the shortcode displays...
1524
- oc.add(dialog).find('code').text( this.shortcode( settings ) );
1525
-
1526
- } //end cmwAssist.update()
1527
-
1528
- }; //end cmwAssist
1529
-
1530
- /**
1531
- * pre-v3.0.0 version
1532
- */
1533
- cmwAssist.v210 = {
1534
- /**
1535
- * sets any fields that are dependent on the selected menu
1536
- * @param {object} oc jQuery of the widget's onchange wrapper
1537
- * @param {integer} maxLevel Maximum level of the selected menu
1538
- */
1539
- setLevels : function(oc, maxLevel){
1540
- var theSelect = oc.find('.cmw-start-level'),
1541
- level = theSelect.val(),
1542
- ct = theSelect.find('option').length,
1543
- txt;
1544
- if(level > maxLevel){
1545
- theSelect.val(1);
1546
- }
1547
- theSelect.find('option').slice(maxLevel).remove();
1548
- while(ct < maxLevel){
1549
- ++ct;
1550
- theSelect.append( $('<option/>', {value:ct}).text(ct) );
1551
- }
1552
- theSelect = oc.find('.cmw-depth');
1553
- level = theSelect.val();
1554
- ct = theSelect.find('option').length;
1555
- txt = theSelect.data().cmwTextLevels;
1556
- if(level > maxLevel){
1557
- theSelect.val(0); //=unlimited
1558
- }
1559
- theSelect.find('option').slice(maxLevel + 1).remove();
1560
- while(ct <= maxLevel){
1561
- theSelect.append( $('<option/>', {value:ct}).text(ct + txt) );
1562
- ++ct;
1563
- }
1564
- }, //end cmwAssist.v210.setLevels()
1565
- /**
1566
- * enables/disables fields and swaps selected menus
1567
- * @param {object} target jQuery of element responsible for 'change' event
1568
- * @param {object} oc jQuery of the widget's onchange wrapper
1569
- */
1570
- setFields : function(target, oc){
1571
- var showAll = oc.find('.cmw-showall').prop('checked'),
1572
- showSpecific = oc.find('.cmw-showspecific').prop('checked'),
1573
- menuItems = oc.find('.cmw-assist-items'),
1574
- selectedItem = parseInt(menuItems.val(), 10);
1575
- //change of menu? : make sure the correct optgroup of menu items is used...
1576
- if(target.hasClass('cmw-select-menu')){
1577
- selectedItem = swapItems(menuItems, selectedItem, target[0].selectedIndex);
1578
- this.setLevels(oc, (menuItems.find('optgroup').data() || {}).cmwMaxLevel);
1579
- }
1580
- $.each(
1581
- { '' : showAll || showSpecific,
1582
- '-ss' : showSpecific,
1583
- 'not-rp' : showAll || showSpecific || selectedItem >= 0,
1584
- 'not-ci' : showAll || showSpecific || !!selectedItem
1585
- },
1586
- function(k, v){
1587
- oc.find('.cmw-disableif' + k).toggleClass('cmw-colour-grey', v).find('input,select').prop('disabled', v);
1588
- });
1589
- }, //end cmwAssist.v210.setFields()
1590
- /**
1591
- * create and return the shortcode equivalent
1592
- * @param {object} settings Widget settings
1593
- * @return {string} Shortcode
1594
- */
1595
- shortcode : function(settings){
1596
- var args = {
1597
- 'menu' : settings.menu
1598
- },
1599
- byLevel = !settings.filter,
1600
- byBranch = settings.filter > 0,
1601
- byItems = !byLevel && !byBranch,
1602
- v, m, n;
1603
- if(settings.title){
1604
- args.title = [settings.title];
1605
- }
1606
- if(byBranch){
1607
- switch(settings.filter_item){
1608
- case 0: args.children_of = ['current']; break;
1609
- case -1: args.children_of = ['parent']; break;
1610
- case -2: args.children_of = ['root']; break;
1611
- default:
1612
- args.children_of = settings.filter_item;
1613
- }
1614
- }
1615
- if(byItems){
1616
- args.items = [settings._items];
1617
- }
1618
- if(byBranch && settings.filter_item < 0 && settings.fallback_no_ancestor){
1619
- if(settings.fallback_include_parent_siblings){
1620
- args.fallback_parent = ['siblings'];
1621
- }else if(settings.fallback_include_parent){
1622
- args.fallback_parent = ['parent'];
1623
- }else{
1624
- args.fallback_parent = 1;
1625
- }
1626
- }
1627
- if(byBranch && !settings.filter_item && settings.fallback_no_children){
1628
- if(settings.fallback_nc_include_parent_siblings){
1629
- args.fallback_current = ['siblings'];
1630
- }else if(settings.fallback_nc_include_parent){
1631
- args.fallback_current = ['parent'];
1632
- }else{
1633
- args.fallback_current = 1;
1634
- }
1635
- }
1636
- if(settings.start_level > 1){
1637
- args.start_level = settings.start_level;
1638
- }
1639
- if(settings.depth > 0){
1640
- args.depth = settings.depth;
1641
- }
1642
- //depth relative to current item is only applicable if depth is not unlimited...
1643
- if(settings.depth_rel_current && settings.depth > 0){
1644
- args.depth_rel_current = 1;
1645
- }
1646
- n = [];
1647
- if(byBranch){
1648
- if(settings.include_parent_siblings){
1649
- n.push('siblings');
1650
- }else if(settings.include_parent){
1651
- n.push('parent');
1652
- }
1653
- if(settings.include_ancestors){
1654
- n.push('ancestors');
1655
- }
1656
- if(n.length){
1657
- args.include = n;
1658
- }
1659
- }
1660
- n = [];
1661
- if(byBranch && settings.title_from_parent){
1662
- n.push('parent');
1663
- }
1664
- if(settings.title_from_current){
1665
- n.push('current');
1666
- }
1667
- if(n.length){
1668
- args.title_from = n;
1669
- }
1670
- for(n in {flat_output:1, contains_current:1, ol_root:1, ol_sub:1}){
1671
- if(settings[n]){
1672
- args[n] = 1;
1673
- }
1674
- }
1675
- v = {container:'div', container_id:'', container_class:'', menu_class:'menu-widget', widget_class:''};
1676
- for(n in v){
1677
- if(settings[n] !== v[n]){
1678
- args[n] = [settings[n]];
1679
- }
1680
- }
1681
- v = {wrap_link:'before', wrap_link_text:'link_before'};
1682
- for(n in v){
1683
- m = settings[v[n]].toString().match(/^<(\w+)/);
1684
- if(m && m[1]){
1685
- args[n] = [m[1]];
1686
- }
1687
- }
1688
- v = [];
1689
- for(n in args){
1690
- //array indicates join (with space sep) & surround it in double quotes, otherwise leave 'as-is'...
1691
- v.push( $.isArray(args[n]) ? n + '="' + args[n].join(' ') + '"' : n + '=' + args[n] );
1692
- }
1693
- return '[custom_menu_wizard ' + v.join(' ') + ']';
1694
- }, //end cmwAssist.v210.shortcode()
1695
- /**
1696
- * updates the graphic menu structure from the widget data
1697
- * @param {element} el Element responsible for being here
1698
- */
1699
- update : function(el){
1700
- var target = $(el),
1701
- oc = findOnchange(0, target),
1702
- dialog = $('#' + oc.data().cmwDialogId),
1703
- byLevel, byBranch, byItems,
1704
- maxLevel, settings, includeParent, includeParentSiblings, topOfMenu, items,
1705
- currentItemLI, currentItemLevel, fallback, parent, i, j;
1706
-
1707
- if(target.hasClass('cmw-listen')){
1708
- //the widget field that changed is likely to have an effect on other widget fields...
1709
- this.setFields(target, oc);
1710
- }
1711
-
1712
- //everything below this point is dialogue-related...
1713
- if(!dialog.length || !dialog.dialog('isOpen')){
1714
- return;
1715
- }
1716
- dialog.dialog('moveToTop');
1717
-
1718
- //if selected menu has changed, modify assist's structure...
1719
- if(target.hasClass('cmw-select-menu')){
1720
- createMenu(dialog);
1721
- }
1722
- settings = getSettings(oc);
1723
- byLevel = !settings.filter;
1724
- byBranch = settings.filter > 0;
1725
- byItems = !byLevel && !byBranch;
1726
- includeParent = settings.include_parent;
1727
- includeParentSiblings = settings.include_parent_siblings;
1728
- topOfMenu = dialog.find('.cmw-demo-themenu-ul');
1729
- maxLevel = topOfMenu.data().maxLevel;
1730
- currentItemLI = topOfMenu.find('.current-menu-item').closest('li');
1731
- currentItemLevel = currentItemLI.length ? currentItemLI.data().level : -1;
1732
- items = topOfMenu.find('li').removeData('included').removeClass('title-from-item');
1733
-
1734
- if(byItems){
1735
- items = filterTickCross(items, settings, 'tick');
1736
- }
1737
-
1738
- if(items.length && !currentItemLI.length && (settings.contains_current || (byBranch && settings.filter_item < 1))){
1739
- items = $([]);
1740
- }
1741
-
1742
- if(items.length && byBranch){
1743
- //kids of...
1744
- if(settings.filter_item > 0){
1745
- //specific item...
1746
- parent = items.filter('[data-itemid=' + settings.filter_item + ']');
1747
- }else if(!settings.filter_item){
1748
- //current...
1749
- if(currentItemLI.find('li').length){
1750
- parent = currentItemLI;
1751
- }else if(settings.fallback_no_children){
1752
- //fall back to current parent...
1753
- parent = topOfMenu.find('.current-menu-parent').closest('li');
1754
- if(!parent.length){
1755
- parent = topOfMenu; //beware!
1756
- }
1757
- includeParent = includeParent || settings.fallback_nc_include_parent;
1758
- includeParentSiblings = includeParentSiblings || settings.fallback_nc_include_parent_siblings;
1759
- fallback = 'cmw-fellback-to-parent';
1760
- }
1761
- }else{
1762
- //parent or root...
1763
- if(currentItemLevel === 1 && settings.fallback_no_ancestor){
1764
- parent = currentItemLI;
1765
- includeParent = includeParent || settings.fallback_include_parent;
1766
- includeParentSiblings = includeParentSiblings || settings.fallback_include_parent_siblings;
1767
- fallback = 'cmw-fellback-to-current';
1768
- }else if(currentItemLevel === 1){
1769
- parent = topOfMenu; //beware!
1770
- }else if(settings.filter_item < -1){
1771
- parent = topOfMenu.find('.current-menu-ancestor').eq(0).closest('li');
1772
- }else{
1773
- parent = topOfMenu.find('.current-menu-parent').closest('li');
1774
- }
1775
- }
1776
- }
1777
-
1778
- if(items.length){
1779
- if(byLevel){
1780
- //showall : use the levels...
1781
- if(settings.depth_rel_current && settings.depth && currentItemLI.length && currentItemLevel >= settings.start_level){
1782
- j = currentItemLevel + settings.depth - 1;
1783
- }else{
1784
- j = settings.depth ? settings.start_level + settings.depth - 1 : 9999;
1785
- }
1786
- for(i = 1; i <= maxLevel; i++){
1787
- if(i < settings.start_level || i > j){
1788
- items = items.not('.level-' + i);
1789
- }
1790
- }
1791
- }else if(parent && parent.length){
1792
- //kids of...
1793
- if(settings.depth_rel_current && settings.depth && currentItemLI.length && parent.has(currentItemLI[0]).length){
1794
- j = currentItemLevel - 1 + settings.depth;
1795
- }else{
1796
- j = settings.depth ? Math.max( (parent.data().level || 0) + settings.depth, settings.start_level + settings.depth - 1 ) : 9999;
1797
- }
1798
- items = parent.find('li').filter(function(){
1799
- var level = $(this).data().level;
1800
- return level >= settings.start_level && level <= j;
1801
- });
1802
- }else if(byBranch){
1803
- //kids-of, but no parent found...
1804
- items = $([]);
1805
- }
1806
- }
1807
-
1808
- if(items.length){
1809
- if(byBranch && parent && parent.is('li')){
1810
- //kids of an item...
1811
- if(includeParentSiblings){
1812
- items = items.add( parent.siblings('li').data('included', ' cmw-an-included-parent-sibling') );
1813
- includeParent = true;
1814
- }
1815
- if(settings.include_ancestors){
1816
- items = items.add( parent.parentsUntil(topOfMenu, 'li').data('included', ' cmw-an-included-ancestor') );
1817
- includeParent = true;
1818
- }
1819
- if(includeParent){
1820
- items = items.add( parent.data('included', ' cmw-the-included-parent') );
1821
- }
1822
- }
1823
- }
1824
-
1825
- //must contain current item?...
1826
- if(items.length && settings.contains_current && (!currentItemLI.length || !items.filter(currentItemLI).length)){
1827
- items = $([]);
1828
- }
1829
-
1830
- //title from parent has higher priority than title from current...
1831
- if(settings.title_from_parent && items.length && parent && parent.is('li')){
1832
- parent.addClass('title-from-item');
1833
- }else if(settings.title_from_current && items.length){
1834
- currentItemLI.addClass('title-from-item');
1835
- }
1836
- //fallback?...
1837
- fallback = items.length ? fallback : '';
1838
- dialog.find('.cmw-demo-fallback').data('fellback', fallback).toggleClass('updated', !!fallback);
1839
- //show/hide the 'select current item' prompt...
1840
- dialog.find('.cmw-demo-setcurrent').toggleClass('error', !currentItemLI.length && (settings.contains_current || (byBranch && settings.filter_item < 1)));
1841
-
1842
- //toggle ticks and 'pick' the remaining items...
1843
- topOfMenu.toggleClass('cmw-demo-filteritems', byItems)
1844
- .find('.picked').not( items.addClass('picked') ).removeClass('picked');
1845
- //produce output...
1846
- setDialogTitle(dialog, oc);
1847
- showOutput(dialog, settings);
1848
- dialog.find('code').text( this.shortcode(settings) );
1849
- } //end cmwAssist.v210.update()
1850
- }; //end cmwAssist.v210
1851
-
1852
- $(document)
1853
- //any change event on widget's inputs or selects...
1854
- .on('change', widgetCustomMenuWizardClass('onchange', 1), assistance)
1855
- //open/close assist dialog...
1856
- .on('click', widgetCustomMenuWizardClass('assist', 1), clickAssist)
1857
- //expand/collapse fieldsets...
1858
- .on('click', widgetCustomMenuWizardClass('fieldset', 1), clickFieldset)
1859
- //when a widget is closed, close its open dialog...
1860
- .on('click', '.widget-action,.widget-control-close', closeDialog)
1861
- //when a widget is deleted, remove its dialog...
1862
- .on('click', '.widget-control-remove', removeDialog)
1863
- //collapse/expand assist menu tree items...
1864
- .on('click', widgetCustomMenuWizardClass('colexp', 1), clickTreeExpander)
1865
- //find posts containing shortcodes by ajax...
1866
- .on('click', widgetCustomMenuWizardClass('find-shortcodes', 1), clickFindShortcodes)
1867
- //remove the legacy warning...
1868
- .on('click', widgetCustomMenuWizardClass('legacy-close', 1), function(){
1869
- $(this).parent().remove();
1870
- return false;
1871
- });
1872
-
1873
- //editing in accessibility mode...
1874
- if(!!window.Custom_Menu_Wizard_Widget){
1875
- $(window.Custom_Menu_Wizard_Widget.trigger || []).trigger('change');
1876
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1877
  });
1
  /* Custom Menu Wizard plugin
2
  * Script for controlling this widget's options (in Admin -> Widgets)
3
+ * NB : in some functions a seemingly unnecessary var declaration has been used in order
4
+ * to avoid Google Close Compiler producing a "Misplaced function annotation" warning!
5
  */
6
  /*global jQuery, window, document, ajaxurl */
7
  /*jslint forin: true, nomen: true, plusplus: true, regexp: true, unparam: true, white: true */
8
  /*jshint curly: true, eqeqeq: true, es3: true, freeze: true, immed: true, latedef: true, newcap: true, noarg: true, noempty: true, nonbsp: true, nonew: true, quotmark: single, undef: true, strict: true, trailing: true, laxbreak: true */
9
  jQuery(function($){
10
+ 'use strict';
11
+ var cmwAssist,
12
+ //only test for : key="value", key='value' and key=value...
13
+ parseSwitchTo = /(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)/g,
14
+
15
+ isNumeric = function(x){
16
+ return (/^[+\-]?\d+$/).test(x.toString());
17
+ },
18
+
19
+ widgetCustomMenuWizardClass = function(suffix, dot){
20
+ return (!dot ? '' : '.') + 'widget-custom-menu-wizard-' + suffix;
21
+ },
22
+
23
+ assistance = function(e){
24
+ /**
25
+ * updates the graphic menu structure from the widget settings
26
+ * @this {element} div.widget-custom-menu-wizard-onchange
27
+ * @param {object} e Event object
28
+ */
29
+ var v = $(this).data().cmwDialogVersion.replace(/\./g, '');
30
+ v = /^\d+$/.test(v) ? 'v' + v : v;
31
+ //run the update() method of the relevant assist object, based on a version number...
32
+ if(cmwAssist[v]){
33
+ cmwAssist[v].update(e ? e.target : this);
34
+ }else{
35
+ cmwAssist.update(e ? e.target : this);
36
+ }
37
+ }, //end assistance()
38
+
39
+ getSettings = function(oc, altFields){
40
+ /**
41
+ * gets the widget's field values, or their equivalents from an alternative set
42
+ * @param {object|boolean} oc jQuery of the widget's onchange wrapper (false if altFields are supplied)
43
+ * @param {object} altFields Optional parsed set of alternative field settings
44
+ * @return {object} key=>value pairs of the field element values
45
+ */
46
+ var useAlternative = oc === false,
47
+ ocd = useAlternative ? {} : oc.data(),
48
+ settings = {},
49
+ legacyVersion = !useAlternative && ocd.cmwDialogVersion === '2.1.0',
50
+ csv = {items:1, exclude:1},
51
+ keepAsString = $.extend({branch_start:1, exclude_level:1, include_level:1}, csv);
52
+ $.each(useAlternative ? altFields : oc.find(':input').serializeArray(), function(i, v){
53
+ var name = v.name.replace(/.*\[([^\]]+)\]$/, '$1'),
54
+ val = !keepAsString[name] && /^-?\d+$/.test(v.value) ? parseInt(v.value, 10) : v.value;
55
+ settings[name] = val;
56
+ if(name === 'hide_empty'){
57
+ settings[name] = useAlternative || !!ocd.cmwV36plus || val;
58
+ }else if(csv[name]){
59
+ settings['_' + name + '_sep'] = !val || /(^\d+\+?$|,)/.test($.trim(val)) ? ',' : ' ';
60
+ val = $.map(val.split(/[,\s]+/), function(x){
61
+ var inherit = !legacyVersion && /\+$/.test(x);
62
+ x = x ? parseInt(x, 10) : 0;
63
+ return isNaN(x) || x < 1 ? null : (inherit ? x + '+' : x);
64
+ });
65
+ settings['_' + name] = val.join(settings['_' + name + '_sep']);
66
+ }
67
+ });
68
+ return settings;
69
+ }, //end getSettings()
70
+
71
+ alternativeCheckFor = function(at, hasCurrent, itemsLength, settings){
72
+ /**
73
+ * checks for switching to the alternative settings (v3.1.0)
74
+ * @param {string} at Processing stage
75
+ * @param {boolean} hasCurrent Current Item is in items
76
+ * @param {integer} itemsLength Items length
77
+ * @param {array} settings Current settings
78
+ * @return {boolean} True if should switch to alternative settings
79
+ */
80
+ var switchIf = settings.switch_if;
81
+ return (settings.switch_at === at && (
82
+ ( switchIf === 'current' && hasCurrent ) ||
83
+ ( switchIf === 'no-current' && !hasCurrent ) ||
84
+ ( switchIf === 'no-output' && !itemsLength )
85
+ ) );
86
+ }, //end alternativeCheckFor()
87
+
88
+ alternativeParse = function(switchTo, themenu){
89
+ /**
90
+ * takes a switch_to setting, parses it, and returns settings (equiv. Custom_Menu_Wizard_Plugin->shortcode_instance())
91
+ * @param {string} switchTo switch_to value
92
+ * @param {object} themenu jQuery of demo menu structure (.cmw-demo-themenu-ul)
93
+ * @return {object} Settings, or False if can't be determined
94
+ */
95
+ switchTo = $.trim(switchTo || '');
96
+ var alts = {
97
+ 'title' : '',
98
+ 'level' : 1, //default setting
99
+ 'branch' : 0,
100
+ 'items' : '',
101
+ 'depth' : 0,
102
+ 'depth_rel_current' : 0,
103
+ 'start_at' : '',
104
+ 'start_mode' : '',
105
+ 'allow_all_root' : 0,
106
+ 'ancestors' : 0,
107
+ 'ancestor_siblings' : 0,
108
+ 'include_root' : 0,
109
+ 'include_level' : '',
110
+ 'siblings' : 0,
111
+ 'exclude' : '',
112
+ 'exclude_level' : '',
113
+ 'contains_current' : '',
114
+ 'fallback' : '',
115
+ 'flat_output' : 0,
116
+ 'title_from' : '',
117
+ 'title_linked' : 0,
118
+ 'ol_root' : 0,
119
+ 'ol_sub' : 0
120
+ },
121
+ attribute = parseSwitchTo.exec(switchTo),
122
+ attr = {},
123
+ i = 0,
124
+ byItems, byBranch, byLevel, n;
125
+ while(attribute){
126
+ i++;
127
+ // key = "value" [1] [2] ...
128
+ if(attribute[1]){
129
+ attr[ attribute[1] ] = attribute[2];
130
+ // key = 'value' [3] [4] ...
131
+ }else if(attribute[3]){
132
+ attr[ attribute[3] ] = attribute[4];
133
+ // key = value [5] [6] ...
134
+ }else if(attribute[5]){
135
+ attr[ attribute[5] ] = attribute[6];
136
+ }else{
137
+ i--;
138
+ }
139
+ attribute = parseSwitchTo.exec(switchTo);
140
+ }
141
+ if(i){
142
+ for(n in attr){
143
+ if(alts.hasOwnProperty(n)){
144
+ alts[n] = attr[n];
145
+ }
146
+ }
147
+ }
148
+
149
+ //in order of priority...
150
+ byItems = !!alts.items;
151
+ byBranch = !byItems && !!alts.branch;
152
+ byLevel = !byItems && !byBranch;
153
+
154
+ if(byItems){
155
+ alts.filter = 'items';
156
+ }
157
+ if(byBranch){
158
+ alts.filter = 'branch';
159
+ n = alts.start_at.toString();
160
+ //default...
161
+ alts.branch_start = n;
162
+ //override...
163
+ if(n === '0' || n === 'branch'){
164
+ alts.branch_start = '';
165
+ }
166
+ if(n === 'root'){
167
+ alts.branch_start = '1';
168
+ }
169
+ if(n === 'children'){
170
+ alts.branch_start = '+1';
171
+ }
172
+ if(n === 'parent'){
173
+ alts.branch_start = '-1';
174
+ }
175
+ if(alts.branch === 'current' || alts.branch === 'current-item'){
176
+ alts.branch = 0;
177
+ }else if( !isNumeric( alts.branch ) ){
178
+ //if branch is non-numeric then it could be the title of a menu item, but we need it to be the menu item's id...
179
+ //NB : if branch *is* numeric, but the item id is not within this menu, then ... tough! Basically, this assist mimics what
180
+ // the widget does, and the widget doesn't check numeric branch values until it gets into the walker - at which point
181
+ // it either produces the relevant output or doesn't. only if branch is non-numeric and possibly an item title does
182
+ // the widget pre-check for it being in the menu (because the walker requires an item id) : so this does the same.
183
+ alts.branch = alts.branch.toLowerCase();
184
+ n = themenu.find('a.cmw-item').filter(function(){
185
+ return $(this).text().toLowerCase() === alts.branch;
186
+ });
187
+ if(n.length){
188
+ alts.branch = n.parent().data('itemid');
189
+ }else{
190
+ //COP OUT!
191
+ return false;
192
+ }
193
+ }
194
+ }
195
+ if(byLevel){
196
+ alts.filter = '';
197
+ alts.level = Math.max(1, parseInt(alts.level, 10));
198
+ }
199
+ alts.start_at = null;
200
+
201
+ //include_level, and the deprecated include_root switch...
202
+ //if level is empty but root is set, set include_level to '1'...
203
+ if( !alts.include_level && alts.include_root === '1' ){
204
+ alts.include_level = '1';
205
+ }
206
+ alts.include_root = null;
207
+ //fallback => fallback and fallback_siblings and fallback_depth...
208
+ //allows "X", "X,Y" or "X,Y,Z" where comma could be space, and X|Y|Z could be "quit"|"current"|"parent", or "+siblings", or digit(s)
209
+ //but "quit", "current" or "parent" must be present (others are optional)
210
+ if(byBranch && !alts.branch && alts.fallback ){
211
+ attr = alts.fallback.toLowerCase().split(/[\s,]+/);
212
+ n = ' ' + attr.join(' ') + ' ';
213
+ alts.fallback = '';
214
+ if(n.indexOf(' quit ') >= 0){
215
+ alts.fallback = 'quit';
216
+ }else if(n.indexOf(' parent ') >= 0){
217
+ alts.fallback = 'parent';
218
+ }else if(n.indexOf(' current ') >= 0){
219
+ alts.fallback = 'current';
220
+ }
221
+ if(alts.fallback !== '' && alts.fallback !== 'quit'){
222
+ if( n.indexOf(' +siblings ') >= 0){
223
+ alts.fallback_siblings = 1;
224
+ }
225
+ for(i = 0; i < attr.length; i++){
226
+ if(/^\d+$/.test(attr[i])){
227
+ n = parseInt(attr[i], 10);
228
+ if(n > 0){
229
+ alts.fallback_depth = n;
230
+ break;
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
236
+ //title_from => title_...
237
+ if(alts.title_from){
238
+ attr = alts.title_from.toLowerCase().split(/[\s,]+/);
239
+ for(i = 0; i < attr.length; i++){
240
+ if(attr[i]){
241
+ if(attr[i] === 'branch' || attr[i] === 'current'){
242
+ alts['title_' + attr[i]] = '0';
243
+ }else{
244
+ n = attr[i].match(/^(branch|current)(-root|-parent|[+\-]?\d+)$/);
245
+ if(n){
246
+ if(n[2] === '-root'){
247
+ alts['title_' + n[1]] = 1;
248
+ }else if(n[2] === '-parent'){
249
+ alts['title_' + n[1]] = -1;
250
+ }else{
251
+ alts['title_' + n[1]] = n[2];
252
+ if(!alts['title_' + n[1]]){
253
+ alts['title_' + n[1]] = '';
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+ }
260
+ }
261
+ alts.title_from = null;
262
+ alts.hide_empty = 1;
263
+
264
+ return getSettings( false, $.map(alts, function(v, k){ return v === null ? v : {name:k, value:v}; }) );
265
+ }, //end alternativeParse()
266
+
267
+ alternativeStripDown = function(x){
268
+ /**
269
+ * strips an alternative settings shortcode down to its bare essentials (v3.1.0)
270
+ * @param {string} x The alternative
271
+ * @return {string}
272
+ */
273
+ var rtn = '';
274
+ if(!!x){
275
+ //remove tabs, CRLFs, containing square brackets, self-terminator and spaces, then split on square bracket, taking first element...
276
+ rtn = x.replace(/[\r\n\t]+/g, ' ').replace(/^[\[\s]+/, '').replace(/[\s\/\]]+$/, '').split(/[\[\]]/)[0];
277
+ //trim trailing slash, surrounding spaces, and append a space...
278
+ rtn = $.trim(rtn.replace(/\/+$/, '')) + ' ';
279
+ //remove leading cmwizard, any occurrence of menu=something, and any occurrence of alternative="something" (optional quotes)...
280
+ rtn = $.trim( (' ' + rtn.replace(/^cmwizard\s/, '') + ' ').replace(/\smenu=[^\s]*\s/, ' ').replace(/\salternative=("[^"]*"|[^\s]*)\s/, ' ') );
281
+ //remove multiple spaces...
282
+ rtn = rtn.replace(/\s\s+/g, ' ');
283
+ }
284
+ return rtn;
285
+ },
286
+ alternativeUse = function(settings, dialog){
287
+ /**
288
+ * retrieves alternative settings
289
+ * @param {array} settings Current settings
290
+ * @param {object} dialog jQuery of dialog
291
+ * @return {boolean|array} Alternative settings
292
+ */
293
+ //alt settings are cached in the data of the menu display (which gets reconstructed when menu id changes)
294
+ var themenu = dialog.find('.cmw-demo-themenu-ul'),
295
+ dataStore = themenu.data(),
296
+ altCode = alternativeStripDown( settings.switch_to );
297
+ altCode = 'cmwizard menu=' + settings.menu + (altCode === '' ? altCode : ' ' + altCode);
298
+
299
+ //if don't have cached code, or the code has changed, get new set...
300
+ if(!dataStore.altCode || dataStore.altCode !== altCode){
301
+ dataStore.altCode = altCode;
302
+ dataStore.altSettings = alternativeParse(altCode, themenu);
303
+ }
304
+ //show that we are - or should be - using the alternative settings...
305
+ dialog.find('.cmw-demo-alternative').addClass('updated')
306
+ //...but if they're bad settings (from a bad switch_to code?) then show as an error...
307
+ .toggleClass('error', dataStore.altSettings === false);
308
+
309
+ return dataStore.altSettings === false ? false : $.extend({}, dataStore.altSettings);
310
+ }, //end alternativeUse()
311
+
312
+ filterTickCross = function( items, settings, tickOrCross ){
313
+ /**
314
+ * sets the tick or cross classes and returns a filtered set of the items that *are* ticked/crossed
315
+ * @param {object} items jQuery of elements to filter
316
+ * @param {array} settings Widget settings
317
+ * @param {string} tickOrCross Either 'tick' or 'cross'
318
+ * @return {object} jQuery of filtered items
319
+ */
320
+ var sep = tickOrCross === 'tick' ? settings._items_sep : settings._exclude_sep,
321
+ inheritance = [],
322
+ haystack = tickOrCross === 'tick' ? '_items' : '_exclude';
323
+ haystack = settings[haystack]
324
+ ? //extract those with inheritance...
325
+ $.grep(settings[haystack].split(sep), function(v){
326
+ if(/\+$/.test(v)){
327
+ inheritance.push(parseInt(v, 10));
328
+ return !v;
329
+ }
330
+ return !!v;
331
+ })
332
+ : [];
333
+ haystack = sep + haystack.join(sep) + sep;
334
+ inheritance = sep + inheritance.join(sep) + sep;
335
+ //need to remember that we're turning off as well as on, because there's no generic clear-down...
336
+ items = items.each(function(){
337
+ var item = $(this),
338
+ data = item.data(),
339
+ inherit = inheritance.indexOf(sep + data.itemid + sep) > -1,
340
+ matched = inherit || haystack.indexOf(sep + data.itemid + sep) > -1;
341
+ item.toggleClass('cmw-has-' + tickOrCross, matched)
342
+ .toggleClass('cmw-inherit-' + tickOrCross, inherit);
343
+ });
344
+ //returning items that *are* ticked/crossed, so get the inheritance items...
345
+ return items.filter('.cmw-inherit-' + tickOrCross)
346
+ //...get their descendants and clear any tick/cross classes that may have been set...
347
+ .find('li').removeClass('cmw-has-' + tickOrCross + ' cmw-inherit-' + tickOrCross)
348
+ //...add back in (to the inheritance descendants) any (other) items that still have tick/cross
349
+ //set, which will include the uppermost of any inheritance items...
350
+ .add( items.filter('.cmw-has-' + tickOrCross) );
351
+ }, //end filterTickCross
352
+
353
+ findOnchange = function(el, below){
354
+ /**
355
+ * gets the -onchange wrapper
356
+ * @param {object} el jQuery of element to search above (default) or below
357
+ * @param {integer} below Indicates whether to search below (exclusive, ie. find()) or above (inclusive, ie. closest())
358
+ * @return {object} jQuery of the -onchange wrapper
359
+ */
360
+ var f = !below ? 'closest' : 'find';
361
+ return el[f](widgetCustomMenuWizardClass('onchange', 1));
362
+ },
363
+
364
+ getLevelClasses = function(option, maxLevel){
365
+ /**
366
+ * given an option in the form of one-or-more digits, optionally followed by a plus or minus, return the relevant classes
367
+ * @param {string} option A setting, eg. settings.exclude_level
368
+ * @param {integer} maxLevel Maximum number of levels available
369
+ * @return {string} CSV of classes, eg. '.level-1,'level-2' for option='2-' (or option='1+' if maxLevel=2)
370
+ */
371
+ var rtn = [],
372
+ k = option.match(/^(\d+)(\+|-)?$/),
373
+ i;
374
+ k = k ? [parseInt(k[1], 10), k[2] || ''] : [];
375
+ if(k[0] > 0){
376
+ for(i = 1; i <= maxLevel; i++){
377
+ if( i === k[0] || (k[1] === '-' && i < k[0]) || (k[1] === '+' && i > k[0]) ){
378
+ rtn.push('.level-' + i);
379
+ }
380
+ }
381
+ }
382
+ return rtn.join(',');
383
+ },
384
+
385
+ showOutput = function(dialog, settings){
386
+ /**
387
+ * produces the final output
388
+ * @param {object} dialog jQuery of target dialog
389
+ * @param {object} settings Field element values
390
+ */
391
+ var topOfMenu = dialog.find('.cmw-demo-themenu-ul'),
392
+ items = topOfMenu.find('.picked'),
393
+ html = '',
394
+ title = settings.hide_title ? '' : settings.title,
395
+ titleLinked = 0,
396
+ currLevel = 0,
397
+ output = dialog.find('.cmw-demo-theoutput-wrap').empty(),
398
+ listClass = ['menu-widget'],
399
+ itemList = {};
400
+ if(items.length && output.length){
401
+ //determine title: update() might have set a class for it...
402
+ title = topOfMenu.find('.title-from-item').children('.cmw-item').text() || '';
403
+ titleLinked = !!title && !!settings.title_linked;
404
+ //...otherwise, check the actual widget title...
405
+ if(!title && !settings.hide_title){
406
+ title = settings.title || '';
407
+ }
408
+
409
+ items.each(function(i){
410
+ var self = $(this),
411
+ data = self.data(),
412
+ trace = data.trace ? data.trace.toString().split(',') : [],
413
+ iid = data.itemid.toString(),
414
+ level = 1,
415
+ anchor = self.children('.cmw-item');
416
+ if(!settings.flat_output){
417
+ itemList[iid] = 1;
418
+ for(i = 0; i < trace.length; i++){
419
+ if(itemList[trace[i]]){
420
+ level++;
421
+ }
422
+ }
423
+ }
424
+ if(currLevel){
425
+ if(level > currLevel){
426
+ html += settings.ol_sub ? '<ol>' : '<ul>';
427
+ }else{
428
+ while(currLevel > level){
429
+ --currLevel;
430
+ html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
431
+ }
432
+ html += '</li>';
433
+ }
434
+ }
435
+ html += '<li class="cmw-level-' + level + (data.included || '') + '"><a href="#' + anchor.data('indx') + '">' + anchor.text() + '</a>';
436
+ currLevel = level;
437
+ });
438
+ while(currLevel > 1){
439
+ --currLevel;
440
+ html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
441
+ }
442
+ html += '</li>';
443
+ listClass.push( dialog.find('.cmw-demo-fallback').data('fellback') );
444
+ html = (settings.ol_root ? '<ol' : '<ul') + ' class="' + $.trim(listClass.join(' ')) + '">' + html + (settings.ol_root ? '</ol>' : '</ul>');
445
+ output.html(html);
446
+ output.find('li').filter(function(){
447
+ return !!$(this).children('ul, ol').length;
448
+ }).addClass('cmw-has-submenu');
449
+ }
450
+ if(output.length && title && (items.length || !settings.hide_empty)){
451
+ output.prepend((titleLinked ? '<h3 class="cmw-title-linked">' : '<h3>') + title + '</h3>');
452
+ }
453
+ }, //end showOutput()
454
+
455
+ swapItems = function(menuItems, selectedItem, selectedMenu){
456
+ /**
457
+ * swap in the apropriate optgroup of items according to the selected menu
458
+ * @param {object} menuItems jQuery of the SELECT being modified
459
+ * @param {integer} selectedItem Currently selected item in menuItems
460
+ * @param {integer} selectedMenu Index of selected menu
461
+ * @return {integer} selectedItem
462
+ */
463
+ var groupClone;
464
+ if(!menuItems.find('optgroup').filter(function(){
465
+ var keepit = $(this).data().cmwOptgroupIndex === selectedMenu;
466
+ if(!keepit){
467
+ $(this).remove();
468
+ }
469
+ return keepit;
470
+ }).length){
471
+ groupClone = $('#' + menuItems.attr('id') + '_ignore').find('optgroup').eq(selectedMenu).clone();
472
+ if(groupClone.length){
473
+ if(selectedItem > 0){
474
+ selectedItem = 0;
475
+ }
476
+ menuItems.append(groupClone).val(selectedItem);
477
+ }
478
+ }
479
+ return selectedItem;
480
+ }, //end swapItems()
481
+
482
+ clickFieldset = function(e){
483
+ /**
484
+ * toggles a set of widgets options open or closed
485
+ * @this {element} Header of the set
486
+ * @param {object} e Event object
487
+ * @return {boolean} false
488
+ */
489
+ var self = $(this),
490
+ chkbox = self.next('.cmw-fieldset-state'),
491
+ collapse = !chkbox.prop('checked');
492
+ if(chkbox.length){
493
+ chkbox.prop('checked', collapse);
494
+ self.toggleClass('cmw-collapsed-fieldset', collapse);
495
+ chkbox.next('div')[collapse ? 'slideUp' : 'slideDown']();
496
+ }
497
+ this.blur();
498
+ return false;
499
+ }, //end clickFieldSet()
500
+
501
+ clickFindShortcodes = function(e){
502
+ /**
503
+ * fetches and displays a list of posts that contain old/new shortcodes
504
+ * @this {element} The link/button
505
+ * @param {object} e Event object
506
+ * @return {boolean} false
507
+ */
508
+ var self = $(this),
509
+ grandad = self.parent().parent();
510
+ //if currently fetching, do nothing...
511
+ if(ajaxurl && !grandad.hasClass('cmw-ajax-fetching')){
512
+ //if currently showing previous results, remove the results from view...
513
+ if(grandad.hasClass('cmw-ajax-showing')){
514
+ grandad.removeClass('cmw-ajax-showing');
515
+ }else{
516
+ //fetch results via ajax, showing spinner while doing so...
517
+ grandad.addClass('cmw-ajax-fetching');
518
+ $.get(
519
+ ajaxurl,
520
+ { 'action': 'cmw-find-shortcodes', '_wpnonce': self.data().nonce }
521
+ ).done(function(response){
522
+ if(!!response && response !== '0'){
523
+ grandad.find('.cmw-demo-found-shortcodes').html($(response).find('response_data').text());
524
+ grandad.addClass('cmw-ajax-showing');
525
+ }
526
+ }
527
+ ).always(function(response){
528
+ grandad.removeClass('cmw-ajax-fetching');
529
+ }
530
+ );
531
+ }
532
+ }
533
+ this.blur();
534
+ return false;
535
+ },
536
+
537
+ clickFixedAbsolute = function(e){
538
+ /**
539
+ * toggles fixed/absolute positioning for the dialog (work around for draggable problems in jQuery UI v1.10.3/4)
540
+ * @this {element} The "fixed" button
541
+ * @param {object} e Event object
542
+ * @return {boolean} false
543
+ */
544
+ var self = $(this),
545
+ data = self.data(),
546
+ toAbsolute = !data.cmwAbsolute,
547
+ dialogBox = self.closest('.ui-dialog'),
548
+ dialog = dialogBox.find('.ui-dialog-content'),
549
+ //if fixed -> absolute, add scrollTop to [css]top; if absolute -> fixed, substract scrollTop from [css]top...
550
+ newTop = parseInt(dialogBox.css('top'), 10) + ( (toAbsolute ? 1 : -1) * $(document).scrollTop() );
551
+ data.cmwAbsolute = toAbsolute;
552
+ if(!data.cmwMaxHeight){
553
+ //store the initial maxHeight setting...
554
+ data.cmwMaxHeight = dialog.dialog('option', 'maxHeight');
555
+ }
556
+ //swap the icon...
557
+ self.button('option', 'icons', {primary:toAbsolute ? 'ui-icon-circle-close' : 'ui-icon-circle-check'});
558
+ //toggle the class to force either fixed (add class) or absolute (remove class)...
559
+ dialogBox.toggleClass('cmw-assistance-dialog-fixed', !toAbsolute);
560
+ //have to reset dialog's maxHeight here, *before* re-positioning, because UI will screw up the position when we set it!...
561
+ dialog.dialog('option', {maxHeight: toAbsolute ? !toAbsolute : data.cmwMaxHeight});
562
+ //re-position the dialog (have to use CSS because UI dialog's position option doesn't hack it!)...
563
+ dialogBox.css('top', newTop);
564
+ return false;
565
+ },
566
+
567
+ clickMenu = function(e){
568
+ /**
569
+ * click handler for an item in the menu structure : sets or clears current menu item and its ancestors
570
+ * @this {element} Anchor element clicked on
571
+ * @param {object} e Event object
572
+ * @return {boolean} false
573
+ */
574
+ var self = $(this),
575
+ cls = ['current-menu-item', 'current-menu-parent', 'current-menu-ancestor'],
576
+ dialog = self.closest('.ui-dialog-content'),
577
+ topOfMenu = dialog.find('.cmw-demo-themenu-ul'),
578
+ inPath = self.find('span').not('.' + cls[0]).parentsUntil(topOfMenu, 'li'),
579
+ i, n,
580
+ appendCls = function(){
581
+ this.title = this.title + ' ' + n.replace(' ', ' & ').replace(/-/g, ' ');
582
+ };
583
+ topOfMenu.find('.' + cls.join(',.')).removeClass(cls.join(' ')).each(function(){
584
+ this.title = this.title.replace(/\s.*$/, '');
585
+ });
586
+ for(i = 0; i < inPath.length; i++){
587
+ n = i === 1 ? cls.join(' ') : cls[0];
588
+ inPath.eq(i).children('.cmw-item').find('span').addClass(n).each(appendCls);
589
+ if(cls.length > 1){
590
+ cls.shift();
591
+ }
592
+ }
593
+ //run update() via assistance()...
594
+ assistance.call( $(dialog.data().cmwOnchange).get(0) );
595
+ return false;
596
+ }, //end clickMenu()
597
+
598
+ clickOutput = function(){
599
+ /**
600
+ * click handler for an item in the Basic Output list : triggers a click on the respective menu structure item
601
+ * @this {element} Anchor element clicked on
602
+ * @param {object} e Event object
603
+ * @return {boolean} false
604
+ */
605
+ var rtn = false;
606
+ $(this).closest('.ui-dialog-content').find('.cmw-item')
607
+ .eq( this.href.split('#')[1] )
608
+ .not(':has(.current-menu-item)').trigger('click');
609
+ this.blur();
610
+ return rtn;
611
+ }, //end clickOutput()
612
+
613
+ clickTreeExpander = function(e){
614
+ /**
615
+ * click handler for assist's menu tree expander/collapser
616
+ * @this {element} Element clicked on
617
+ * @param {object} e Event object
618
+ * @return {boolean} false
619
+ */
620
+ var direction = /1-e/.test(this.className) ? 'slideUp' : 'slideDown';
621
+ $(this).toggleClass('ui-icon-triangle-1-s ui-icon-triangle-1-e').prev('ul')[direction]();
622
+ return false;
623
+ },
624
+
625
+ clickTickCross = function(e){
626
+ /**
627
+ * click handler for a tick or cross against any item in the menu structure (does not modify any classes)
628
+ * @this {element} Tick or Cross clicked on
629
+ * @param {object} e Event object
630
+ * @return {boolean} false
631
+ */
632
+ var self = $(this),
633
+ tickOrCross = self.hasClass('cmw-tick') ? 'tick' : 'cross',
634
+ item = self.parent(),
635
+ topOfMenu = item.closest('.cmw-demo-themenu-ul'),
636
+ hasInheritTickCross = item.hasClass('cmw-inherit-' + tickOrCross),
637
+ hasTickCross = hasInheritTickCross || item.hasClass('cmw-has-' + tickOrCross),
638
+ inheritsTickCrossFrom = hasTickCross ? $([]) : item.parentsUntil(topOfMenu, '.cmw-inherit-' + tickOrCross),
639
+ widgetField = $( item.closest('.ui-dialog-content').data().cmwOnchange )
640
+ .find(tickOrCross === 'tick' ? '.cmw-setitems' : '.cmw-exclusions'),
641
+ sampleSet;
642
+ //if we're using the alternative settings, then click a tick/cross is disabled because they are set according to the
643
+ //alternative settings, which are not modifiable via this instance of the assist!
644
+ if(!topOfMenu.hasClass('cmw-using-alternative') && widgetField.length){ //should never not find it!
645
+ //A. if this item hasInheritTickCross then this click will remove tickCross entirely and all inheritance will be lost
646
+ //B. else* if this item hasTickCross then this click will either
647
+ // B1. if the item has no descendants, or we're running legacy version, remove the tickCross
648
+ // B2. else add inheritance to it
649
+ //C. else* if this item inheritsTickCross then this click will
650
+ // - disinherit the relevant ancestor (keeping tickCross!)
651
+ // - and set all its descendants - bar this* item! - to tickCross
652
+ //D. else* this click will add tickCross (without inheritance) to this item
653
+
654
+ //find everything that currently has tickCross, and for (A) remove this item, or for (D) add this item...
655
+ //note : for (B) this item is already included, and for (C) this item is not already included and we don't want it to be
656
+ sampleSet = topOfMenu
657
+ .find('.cmw-has-' + tickOrCross)[ hasInheritTickCross || inheritsTickCrossFrom.length ? 'not' : 'add' ](item);
658
+ //(A) & (D) are now sorted and can be forgotten about
659
+ //for (B1), remove this item...
660
+ if(hasTickCross && (!item.children('ul').length || topOfMenu.parent().hasClass('cmw-version-210'))){
661
+ sampleSet = sampleSet.not(item);
662
+ }
663
+ else
664
+ //for (B2), because we're adding inheritance, any descendant of this item that is currently tickCross now needs to be excluded...
665
+ if(hasTickCross && !hasInheritTickCross){
666
+ sampleSet = sampleSet.not( item.find('.cmw-has-' + tickOrCross) );
667
+ }
668
+ //(B) is now also sorted and can be forgotten about
669
+ //for (C) we need to find the ancestor providing the inheritance and add in all its descendants bar this item...
670
+ sampleSet = sampleSet.add( inheritsTickCrossFrom.find('li').not(item) );
671
+ //map what's left to the associated item id, appending a plus sign for inheritance...
672
+ sampleSet = sampleSet.map(function(){
673
+ var plus = this === item[0] || this === inheritsTickCrossFrom.get(0) ? hasTickCross : $(this).hasClass('cmw-inherit-' + tickOrCross);
674
+ return $(this).data().itemid + (plus ? '+' : '');
675
+ })
676
+ .get().join( /(,|^\d+\+?$)/.test( $.trim(widgetField.val()) || ',' ) ? ',' : ' ' );
677
+ widgetField.val(sampleSet).trigger('change');
678
+ }
679
+ this.blur();
680
+ return false;
681
+ }, //end clickTickCross()
682
+
683
+ closeDialog = function(e){
684
+ /**
685
+ * closes an open dialog when the widget is closed
686
+ * @this {element} The .widget-action or .widget-control-close clicked on
687
+ * @param {object} e Event object
688
+ */
689
+ var widgetContainer = $(this).closest('div.widget'),
690
+ containerParent = widgetContainer.parent();
691
+ //in widget customizer, opening one widget closes any other open widget without triggering anything that
692
+ //would make CMW close the assist ... which is good! however, I also don't want the assist to close when
693
+ //[re]opening a CMW widget, so for the customizer I need to check that the widget is "expanded" before
694
+ //making the assist close...
695
+ if(!containerParent.hasClass('customize-control-widget_form') || containerParent.hasClass('expanded')){
696
+ findOnchange(widgetContainer, 1).each(function(){
697
+ var dialog = $('#' + $(this).data().cmwDialogId);
698
+ if(dialog.length && dialog.dialog('isOpen')){
699
+ dialog.dialog('close');
700
+ }
701
+ });
702
+ }
703
+ }, //end closeDialog()
704
+
705
+ createDialog = function(data){
706
+ /**
707
+ * creates the UI Dialog for the widget's assist
708
+ * @param {object} data Information required to build the dialog
709
+ * @return {object} jQuery object of the created Dialog
710
+ */
711
+ var dialogOpts = {
712
+ autoOpen: false,
713
+ //initial width is the lowest of 600px and 90% of window width...
714
+ width: Math.min($(window).width() * 0.9, 600),
715
+ //starting out at fixed, so max the resizable height at 40px less than window height so that entire dialog box is visible...
716
+ maxHeight: $(window).height() - 40,
717
+ modal: false,
718
+ containment: 'window',
719
+ create: function(){
720
+ //add a "fixed" button to the titlebar, enabling switching between fixed and absolute positioning for the dialog...
721
+ var dialogBox = $(this).closest('.ui-dialog');
722
+ if(dialogBox.hasClass('cmw-assistance-dialog-fixed')){
723
+ $('<button/>').addClass('cmw-dialog-fixed-absolute')
724
+ .button({label:data.cmwDialogFixed, icons:{primary:'ui-icon-circle-check'}})
725
+ .appendTo( dialogBox.find('.ui-dialog-titlebar') )
726
+ .on('click', clickFixedAbsolute);
727
+ }
728
+ },
729
+ dialogClass: 'cmw-assistance-dialog cmw-assistance-dialog-fixed'
730
+ },
731
+ msgs = $.map(['SetCurrent', 'Inclusions', 'Exclusions', 'Fallback', 'Alternative'], function(v){
732
+ return '<div class="cmw-demo-' + v.toLowerCase() + ' cmw-demo-small">' + (data['cmwDialog' + v] || '') + '</div>';
733
+ }),
734
+ dialog = $('<div/>', {id:data.cmwDialogId}).addClass(widgetCustomMenuWizardClass('dialog'))
735
+ .append(
736
+ $('<div/>').addClass('cmw-demo-themenu cmw-version-' + data.cmwDialogVersion.replace(/\./g, ''))
737
+ .html('<em class="cmw-demo-small">' + data.cmwDialogPrompt + '</em>')
738
+ )
739
+ .append(
740
+ $('<div/>').addClass('cmw-demo-theoutput')
741
+ .html('<em class="cmw-demo-small">' + data.cmwDialogOutput + '</em><em class="cmw-demo-plugin-version cmw-demo-small">v' + data.cmwDialogVersion + '</em>' + msgs.shift() + '<div class="cmw-demo-theoutput-wrap ui-corner-all"></div>' + msgs.join(''))
742
+ )
743
+ .append(
744
+ $('<div/>').addClass('cmw-demo-theshortcode')
745
+ .html('<code class="ui-corner-all"></code><div class="cmw-find-shortcodes"><a href="#" class="button-secondary ' + widgetCustomMenuWizardClass('find-shortcodes') + '" data-nonce="' + (data.cmwDialogNonce || '') + '" title="' + data.cmwDialogShortcodes + '"><span class="spinner"></span><span>[&hellip;]</span></a></div><div class="cmw-demo-found-shortcodes cmw-demo-small ui-corner-all"></div>')
746
+ );
747
+ dialog.dialog(dialogOpts);
748
+ dialog.find('.cmw-demo-themenu')
749
+ .on('click', '.cmw-tick,.cmw-cross', clickTickCross)
750
+ .on('click', '.cmw-item', clickMenu);
751
+ dialog.find('.cmw-demo-theoutput').on('click', 'a', clickOutput);
752
+ return dialog;
753
+ }, //end createDialog()
754
+
755
+ removeDialog = function(e){
756
+ /**
757
+ * removes associated dialog when the widget is deleted
758
+ * @this {element} The .widget-control-remove clicked on
759
+ * @param {object} e Event object
760
+ */
761
+ var widget = $(this).closest('div.widget');
762
+ findOnchange(widget, 1).each(function(){
763
+ var dialog = $('#' + $(this).data().cmwDialogId);
764
+ if(dialog.length){
765
+ dialog.dialog('destroy');
766
+ dialog.remove();
767
+ }
768
+ });
769
+ }, //end removeDialog()
770
+
771
+ setDialogTitle = function(dialog, oc){
772
+ /**
773
+ * (re)sets the dialog's title
774
+ * @param {object} dialog jQuery object of the dialog
775
+ * @param {object} oc jQuery object of the onchange wrapper
776
+ */
777
+ var title = oc.find('.cmw-widget-title').val() || dialog.data().cmwUntitled;
778
+ dialog.dialog('option', 'title', 'CMW : ' + title + ' [' + oc.find('.cmw-select-menu').find('option:selected').text() + ']' );
779
+ }, //end setDialogTitle()
780
+
781
+ createMenu = function(dialog){
782
+ /**
783
+ * creates a new list of menu items and inserts it into the dialog content in place of any previous one
784
+ * @param {object} dialog jQuery object of the dialog
785
+ */
786
+ var oc = $(dialog.data().cmwOnchange),
787
+ menuid = parseInt(oc.find('.cmw-select-menu').val(), 10),
788
+ currentmenu = dialog.find('.cmw-demo-themenu-ul'),
789
+ trace = [],
790
+ maxLevel = 0,
791
+ menu = '',
792
+ outdentedExpander = function(x){
793
+ //x is the level of the *previous* item; so if previous item was at level 4, then its parent was at level 3, and
794
+ //was therefore indented twice (given that a level 1 item is not indented), so number of outdents is x - 2!
795
+ //each level is indented by 2.4em; subtract 2 from x and multiply by 2.4em, then add another 2em...
796
+ return x > 1 ? '<a href="#" class="' + widgetCustomMenuWizardClass('colexp') + ' ui-icon ui-icon-triangle-1-e" style="left:-' + (((x - 2) * 2.4) + 2) + 'em;">&nbsp;</a>' : '';
797
+ };
798
+ if(!currentmenu.length || currentmenu.data('menuid') !== menuid){
799
+ oc.find('.cmw-assist-items optgroup').find('option').each(function(i){
800
+ var self = $(this),
801
+ level = self.data().cmwLevel;
802
+ while(level < trace.length){
803
+ menu += '</li></ul>' + outdentedExpander(trace.length);
804
+ trace.pop();
805
+ }
806
+ if(level > trace.length){
807
+ menu += '<ul>';
808
+ }else{
809
+ menu += '</li>';
810
+ trace.pop();
811
+ }
812
+ //data-level is 1-based, with 1 being root
813
+ //data-trace is the menu item ids of this item's ancestors, from root down to parent (inclusive)
814
+ menu += '<li class="level-' + level + '" data-itemid="' + this.value + '" data-level="' + level + '" data-trace="' + trace.join(',') + '">';
815
+ menu += '<a href="#" class="cmw-cross ui-corner-all"></a>';
816
+ menu += '<a class="cmw-item ui-corner-all" href="#" data-indx="' + i + '"><span class="ui-corner-all" title="#' + this.value + '">' + $.trim(self.text());
817
+ menu += '</span></a><a href="#" class="cmw-tick ui-corner-all"></a>';
818
+ trace.push(this.value);
819
+ if(level > maxLevel){
820
+ maxLevel = level;
821
+ }
822
+ });
823
+ while(trace.length){
824
+ menu += '</li></ul>' + outdentedExpander(trace.length);
825
+ trace.pop();
826
+ }
827
+ currentmenu.remove();
828
+ dialog.find('.cmw-demo-themenu')
829
+ .append( $(menu).addClass('cmw-demo-themenu-ul').data({maxLevel:maxLevel, menuid:menuid}) );
830
+ }
831
+ }, //end createMenu()
832
+
833
+ clickAssist = function(e){
834
+ /**
835
+ * toggles the assist dialog open/closed, creating it if necessary
836
+ * @this {element} A The assist anchor
837
+ * @param {object} e Event object
838
+ * @return {boolean} false
839
+ */
840
+ var self = $(this),
841
+ oc = findOnchange(self), //above
842
+ data = oc.data(),
843
+ dialog = $( '#' + data.cmwDialogId );
844
+ if(!dialog.length){
845
+ dialog = createDialog(data).data({
846
+ cmwOnchange: '#' + oc.attr('id'),
847
+ cmwUntitled: '[' + data.cmwDialogUntitled + ']'
848
+ });
849
+ }
850
+ if(dialog.dialog('isOpen')){
851
+ dialog.dialog('close');
852
+ }else{
853
+ createMenu(dialog);
854
+ setDialogTitle(dialog, oc);
855
+ dialog.dialog('open');
856
+ assistance.call(oc[0]);
857
+ }
858
+ this.blur();
859
+ return false;
860
+ }; //end clickAssist()
861
+
862
+ /**
863
+ * The assist object, containing functions that do most of the version-specific work.
864
+ * There is a default set of functions, and any different legacy versions that are still
865
+ * supported should be under their own 'vNNN' object (eg. 'v210'). Which set of functions
866
+ * is called is determined by assistance(), which is the only entry point (into update()),
867
+ * and is governed by the widget form (specifically, the version property of the data
868
+ * attached to the -onchange wrapper).
869
+ */
870
+ cmwAssist = {
871
+ setLevels : function(oc, maxLevel){
872
+ /**
873
+ * sets any fields that are dependent on the selected menu
874
+ * @param {object} oc jQuery of the widget's onchange wrapper
875
+ * @param {integer} maxLevel Maximum level of the selected menu
876
+ */
877
+ var theSelect = oc.find('.cmw-branch-start'),
878
+ level = theSelect.val();
879
+ if(!maxLevel){
880
+ return;
881
+ }
882
+ theSelect.find('optgroup').each(function(i){
883
+ var self = $(this),
884
+ opts = self.find('option'),
885
+ ct = opts.length,
886
+ texts = self.data();
887
+ if(i){
888
+ //absolute...
889
+ opts.slice(maxLevel).remove();
890
+ while(ct < maxLevel){
891
+ ++ct;
892
+ self.append( $('<option/>', {value:ct}).text(ct) );
893
+ }
894
+ }else{
895
+ //relative...
896
+ //if maxLevel is 1 : the item (1 option)
897
+ //if maxLevel is 2 : -1, the item, +1 (3 options)
898
+ //if maxLevel is 3 : -2, -1, the item, +1, +2 (5 options)
899
+ //etc, etc
900
+ ct = (ct + 1) / 2;
901
+ if(ct > maxLevel){
902
+ opts.each(function(i, el){
903
+ if(Math.abs(el.value) >= maxLevel){
904
+ $(el).remove();
905
+ }
906
+ });
907
+ }
908
+ while(ct < maxLevel){
909
+ self.prepend( $('<option/>', {value:-ct}).text(-ct + (ct > 1 ? '' : ' (' + texts.cmwTextParent + ')')) )
910
+ .append( $('<option/>', {value:'+' + ct}).text('+' + ct + (ct > 1 ? '' : ' (' + texts.cmwTextChildren + ')')) );
911
+ ++ct;
912
+ }
913
+ }
914
+ });
915
+ //if level is absolute and > maxLevel, set to 1...
916
+ if(/^\d+$/.test(level)){
917
+ if(level > maxLevel){
918
+ theSelect.val('1');
919
+ }
920
+ //if level is relative, not 'the item', and absolutely >= maxLevel, set to '' (the item)...
921
+ }else if(level !== '' && Math.abs(level) >= maxLevel){
922
+ theSelect.val('');
923
+ }
924
+
925
+ oc.find('.cmw-set-rel-abs-levels').each(function(){
926
+ var self = $(this),
927
+ optgroups = self.find('optgroup'),
928
+ ct = (optgroups.find('option').length / 2) + 1, //should never be below 2
929
+ v = self.val(),
930
+ minLevel = Math.max(2, maxLevel);
931
+ if(Math.abs(v) >= maxLevel){
932
+ self.val( v < 0 ? 1 - maxLevel : maxLevel - 1 );
933
+ }
934
+ if(ct !== minLevel){
935
+ optgroups.each(function(i, el){
936
+ var optgroup = $(el),
937
+ text = optgroup.data().cmwTextForOption,
938
+ j;
939
+ if(ct > minLevel){
940
+ optgroup.find('option').slice( minLevel - ct ).remove();
941
+ }
942
+ for(j = ct; j < minLevel; j++){
943
+ optgroup.append( $('<option/>', {value:i ? j : -j}).text( text.replace('%d', i ? j : -j) ) );
944
+ }
945
+ });
946
+ }
947
+ });
948
+
949
+ oc.find('.cmw-include-level,.cmw-exclude-level').each(function(){
950
+ var self = $(this),
951
+ options = self.find('option'),
952
+ ct = (options.length - 1) / 3,
953
+ v = self.val(),
954
+ above = options.eq(2).text(),
955
+ below = options.eq(3).text();
956
+ options.slice( (maxLevel * 3) + 1 ).remove();
957
+ while(ct < maxLevel){
958
+ ++ct;
959
+ self.append( $('<option/>', {value:ct}).text(ct) )
960
+ .append( $('<option/>', {value:ct + '-'}).text(above.replace(/\d+/, ct)) )
961
+ .append( $('<option/>', {value:ct + '+'}).text(below.replace(/\d+/, ct)) );
962
+ }
963
+ if(parseInt(v, 10) > maxLevel){
964
+ self.val('');
965
+ }
966
+ });
967
+
968
+ //do the easy levels...
969
+ oc.find('.cmw-set-levels').each(function(){
970
+ var self = $(this),
971
+ data = self.data(),
972
+ txt = data.cmwTextLevels || '',
973
+ leave = data.cmwSetLevels || 0,
974
+ opts = self.find('option'),
975
+ ct = opts.length - leave;
976
+ //if current value exceeds maxLevel, reset to first option...
977
+ if(self.val() > maxLevel){
978
+ self.val( opts.eq(0).val() );
979
+ }
980
+ //remove anything above maxLevel...
981
+ self.find('option').slice(leave + maxLevel).remove();
982
+ //append enough new options to bring up to maxLevel...
983
+ while(ct < maxLevel){
984
+ ++ct;
985
+ self.append( $('<option/>', {value:ct}).text(ct + txt) );
986
+ }
987
+ });
988
+ }, //end cmwAssist.setLevels()
989
+
990
+ setFields : function(target, oc){
991
+ /**
992
+ * enables/disables fields and swaps selected menus
993
+ * @param {object} target jQuery of element responsible for 'change' event
994
+ * @param {object} oc jQuery of the widget's onchange wrapper
995
+ */
996
+ var byBranchCheckbox = oc.find('.cmw-bybranch'),
997
+ byItems = oc.find('.cmw-byitems').prop('checked'),
998
+ notByBranch = byItems || !byBranchCheckbox.prop('checked'),
999
+ menuItems = oc.find('.cmw-assist-items'),
1000
+ selectedItem = parseInt(menuItems.val(), 10),
1001
+ fallback;
1002
+ //change of menu? : make sure the correct optgroup of menu items is used...
1003
+ if(target.hasClass('cmw-select-menu')){
1004
+ selectedItem = swapItems(menuItems, selectedItem, target[0].selectedIndex);
1005
+ this.setLevels(oc, (menuItems.find('optgroup').data() || {}).cmwMaxLevel);
1006
+ //if level is changed, switch to by-level filtering...
1007
+ }else if(target.hasClass('cmw-level')){
1008
+ notByBranch = true;
1009
+ byItems = !notByBranch;
1010
+ oc.find('.cmw-bylevel').prop('checked', notByBranch);
1011
+ //if by-branch's branch is changed, switch to by-branch filtering...
1012
+ }else if(target.is(menuItems)){
1013
+ notByBranch = byItems = false;
1014
+ byBranchCheckbox.prop('checked', !notByBranch);
1015
+ //change of items?, switch to by-items filtering...
1016
+ }else if(target.hasClass('cmw-setitems')){
1017
+ byItems = true;
1018
+ notByBranch = byItems;
1019
+ oc.find('.cmw-byitems').prop('checked', byItems);
1020
+ //if ancestors is cleared, clear ancestor siblings...
1021
+ }else if(target.hasClass('cmw-ancestors') && target.val() === '0'){
1022
+ oc.find('.cmw-ancestor-siblings').val('0');
1023
+ //if include ancestor's siblings is changed to a value, and ancestors is empty, set ancestors from ancestor siblings...
1024
+ }else if(target.hasClass('cmw-ancestor-siblings') && target.val() !== '0' && oc.find('.cmw-ancestors').val() === '0'){
1025
+ oc.find('.cmw-ancestors').val( target.val() );
1026
+ }
1027
+
1028
+ fallback = oc.find('.cmw-fallback').val();
1029
+ $.each( //disable if...
1030
+ { '-ss' : byItems, //...is Items
1031
+ '-ud' : byItems || oc.find('.cmw-depth').val() < 1, //...is Unlimited Depth
1032
+ 'not-br' : notByBranch, //...is NOT Branch
1033
+ 'not-br-ci' : notByBranch || !!selectedItem, //...is NOT Branch:Current Item
1034
+ 'not-fb-pc' : notByBranch || !!selectedItem || (fallback !== 'parent' && fallback !== 'current'), //...is NOT fallback to parent or current
1035
+ 'not-sw' : !!oc.find('.cmw-switchable').filter(function(){ return !$(this).val(); }).length //...is NOT switchable (missing rither condition or stage)
1036
+ },
1037
+ function(k, v){
1038
+ //as of v3.1.0, the input fields (+ selects, etc) are no longer disabled, because the customizer "pseudo-saves" the
1039
+ //form which wipes out values that you may not have wanted to lose!
1040
+ oc.find('.cmw-disableif' + k).toggleClass('cmw-colour-grey', v);
1041
+ });
1042
+ }, //end cmwAssist.setFields()
1043
+
1044
+ shortcode : function(settings){
1045
+ /**
1046
+ * create and return the shortcode equivalent
1047
+ * @param {object} settings Widget settings
1048
+ * @return {string} Shortcode
1049
+ */
1050
+ var args = {
1051
+ 'menu' : settings.menu
1052
+ },
1053
+ byBranch = settings.filter === 'branch',
1054
+ byItems = settings.filter === 'items',
1055
+ byLevel = !byBranch && !byItems,
1056
+ v, m, n, c;
1057
+ //take notice of the widget's hide_title flag...
1058
+ if(settings.title && !settings.hide_title){
1059
+ args.title = [settings.title];
1060
+ }
1061
+ //byLevel is the default (no branch & no items), as is level=1, so we only *have* to specify level if it's greater than 1...
1062
+ if(byLevel && settings.level > 1){
1063
+ args.level = settings.level;
1064
+ }
1065
+ //specifying branch sets byBranch, overriding byLevel...
1066
+ if(byBranch){
1067
+ args.branch = settings.branch || 'current';
1068
+ //start_at only *has* to be specified if not empty...
1069
+ if(settings.branch_start){
1070
+ args.start_at = [settings.branch_start];
1071
+ }
1072
+ //start_mode may be brought into play by a fallback so always specify it...
1073
+ if(settings.start_mode === 'level'){
1074
+ args.start_mode = ['level'];
1075
+ }
1076
+ //allow_all_root is only applicable to byBranch...
1077
+ if( settings.allow_all_root ){
1078
+ args.allow_all_root = 1;
1079
+ }
1080
+ }
1081
+ //specifying items set byItems, overriding byLevel & byBranch...
1082
+ if(byItems){
1083
+ args.items = [settings._items];
1084
+ }
1085
+ //depth is not relevant to byItems...
1086
+ else{
1087
+ //depth if greater than 0...
1088
+ if(settings.depth > 0){
1089
+ args.depth = settings.depth;
1090
+ }
1091
+ //depth relative to current item is only applicable if depth is not unlimited...
1092
+ if(settings.depth_rel_current && settings.depth > 0){
1093
+ args.depth_rel_current = 1;
1094
+ }
1095
+ }
1096
+ //fallbacks...
1097
+ //no children : branch = current item...
1098
+ if(byBranch && !settings.branch){
1099
+ //format = quit|parent|current [,+siblings] [,depth] eg. "parent,+siblings,1" or "current,2" or "current,+siblings" or "parent" or "quit"
1100
+ if(settings.fallback){
1101
+ args.fallback = [settings.fallback];
1102
+ if(settings.fallback !== 'quit'){
1103
+ if(settings.fallback_siblings){
1104
+ args.fallback.push('+siblings');
1105
+ }
1106
+ if(settings.fallback_depth){
1107
+ args.fallback.push( settings.fallback_depth );
1108
+ }
1109
+ }
1110
+ }
1111
+ }
1112
+ //branch ancestor inclusions...
1113
+ if(byBranch && settings.ancestors){
1114
+ args.ancestors = settings.ancestors;
1115
+ if(settings.ancestor_siblings){
1116
+ args.ancestor_siblings = settings.ancestor_siblings;
1117
+ }
1118
+ }
1119
+ //inclusions by level...
1120
+ if(settings.include_level){
1121
+ args.include_level = [settings.include_level];
1122
+ }
1123
+ //exclusions by id...
1124
+ if(settings._exclude){
1125
+ args.exclude = [settings._exclude];
1126
+ }
1127
+ //...and by level...
1128
+ if(settings.exclude_level){
1129
+ args.exclude_level = [settings.exclude_level];
1130
+ }
1131
+ //title from...
1132
+ n = [];
1133
+ if(settings.title_current !== ''){
1134
+ n.push('current' + (!settings.title_current ? '' : settings.title_current));
1135
+ }
1136
+ if(byBranch && settings.title_branch !== ''){
1137
+ n.push('branch' + (!settings.title_branch ? '' : settings.title_branch));
1138
+ }
1139
+ if(n.length){
1140
+ args.title_from = n;
1141
+ //...title_linked is only relevant if title_from is set...
1142
+ if(settings.title_linked){
1143
+ args.title_linked = 1;
1144
+ }
1145
+ }
1146
+ //switches...
1147
+ for(n in {siblings:1, flat_output:1, ol_root:1, ol_sub:1, fallback_ci_parent:1, fallback_ci_lifo:1}){
1148
+ if(settings[n]){
1149
+ args[n] = 1;
1150
+ }
1151
+ }
1152
+ //strings...
1153
+ v = {contains_current:'', container:'div', container_id:'', container_class:'', menu_class:'menu-widget', widget_class:''};
1154
+ for(n in v){
1155
+ if(settings[n] !== v[n]){
1156
+ args[n] = [settings[n]];
1157
+ }
1158
+ }
1159
+ v = {wrap_link:'before', wrap_link_text:'link_before'};
1160
+ for(n in v){
1161
+ m = settings[v[n]].toString().match(/^<(\w+)/);
1162
+ if(m && m[1]){
1163
+ args[n] = [m[1]];
1164
+ }
1165
+ }
1166
+ //alternative...
1167
+ if(settings.switch_if){
1168
+ if(settings.switch_at){
1169
+ args.alternative = [settings.switch_if, settings.switch_at];
1170
+ c = alternativeStripDown(settings.switch_to);
1171
+ }
1172
+ }
1173
+ //build the shortcode...
1174
+ v = [];
1175
+ for(n in args){
1176
+ //array indicates join (with comma sep) & surround it in double quotes, otherwise leave 'as-is'...
1177
+ v.push( $.isArray(args[n]) ? n + '="' + args[n].join(',') + '"' : n + '=' + args[n] );
1178
+ }
1179
+ //NB at v3.0.0, the shortcode changed from custom_menu_wizard to cmwizard (the previous version is still supported)
1180
+ return '[cmwizard ' + v.join(' ') + (!c ? '/]' : ']' + c + '[/cmwizard]');
1181
+ }, //end cmwAssist.shortcode()
1182
+
1183
+ structureUpdate : function(dialog, settings, usingAlts){
1184
+ /**
1185
+ * performs the filtering and update of the menu structure
1186
+ * note to self : this function must not alter whatever settings are passed into it, otherwise subsequent update
1187
+ * actions, such as shortcode output, will be screwed!
1188
+ * @param {object} dialog jQuery of dialog
1189
+ * @param {array} settings Widget config
1190
+ * @param {integer} usingAlts Indicates whether alternative settings are being used or not
1191
+ * @return {boolean|array} False if completed, or the alternative settings if they should be applied
1192
+ */
1193
+ var tobLevel = -1,
1194
+ lastVisibleLevel = 9999,
1195
+ hasIncl = 0,
1196
+ hasExcl = 0,
1197
+ fallback = '',
1198
+ altSettings = null,
1199
+ theBranchItem, hasCurrent, topOfBranch, i, j, k, x, y,
1200
+ stage = 'menu',
1201
+ byBranch = settings.filter === 'branch',
1202
+ byItems = settings.filter === 'items',
1203
+ byLevel = !byBranch && !byItems,
1204
+ ciBranch = byBranch && !settings.branch,
1205
+ canSwitch = !usingAlts && !!settings.switch_if && !!settings.switch_at,
1206
+ topOfMenu = dialog.find('.cmw-demo-themenu-ul'),
1207
+ maxLevel = topOfMenu.data().maxLevel,
1208
+ currentItemLI = topOfMenu.find('.current-menu-item').closest('li'),
1209
+ currentItemLevel = currentItemLI.length ? currentItemLI.data().level : -1,
1210
+ items = topOfMenu.find('li').removeData('included').removeClass('title-from-item'),
1211
+ local_depth = settings.depth,
1212
+ local_depth_rel_current = settings.depth_rel_current,
1213
+ //ticks and crosses (need to be run against the full set of items)...
1214
+ exclusions = filterTickCross(items, settings, 'cross');
1215
+
1216
+ //check for current item and switch...
1217
+ hasCurrent = items.length && currentItemLI.is(items);
1218
+ if(settings.contains_current === stage && !hasCurrent){
1219
+ items = $([]);
1220
+ }
1221
+ if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1222
+ altSettings = alternativeUse(settings, dialog);
1223
+ if(altSettings !== false){
1224
+ //cop out with alternative settings!...
1225
+ return altSettings;
1226
+ }
1227
+ }
1228
+
1229
+ stage = 'primary';
1230
+ //primary filter : items...
1231
+ if(byItems && items.length){
1232
+ items = filterTickCross(items, settings, 'tick');
1233
+ }
1234
+
1235
+ //primary filter : branch...
1236
+ if(byBranch && items.length){
1237
+ topOfBranch = ciBranch ? currentItemLI : items.filter('[data-itemid=' + settings.branch + ']');
1238
+ if(topOfBranch.length){
1239
+ tobLevel = topOfBranch.data().level || 0;
1240
+ items = topOfBranch.add( topOfBranch.find('li') );
1241
+ //since topOfBranch can change later on...
1242
+ theBranchItem = topOfBranch;
1243
+ }else{
1244
+ items = $([]);
1245
+ }
1246
+ }
1247
+
1248
+ //primary filter : level...
1249
+ if(byLevel && items.length && settings.level > 1){
1250
+ for(i = 1, j = []; i < settings.level; i++){
1251
+ j.push('.level-' + i);
1252
+ }
1253
+ items = items.not( j.join(',') );
1254
+ }
1255
+
1256
+ //check for current item and switch...
1257
+ hasCurrent = items.length && currentItemLI.is(items);
1258
+ if(settings.contains_current === stage && !hasCurrent){
1259
+ items = $([]);
1260
+ }
1261
+ if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1262
+ altSettings = alternativeUse(settings, dialog);
1263
+ if(altSettings !== false){
1264
+ //cop out with alternative settings!...
1265
+ return altSettings;
1266
+ }
1267
+ }
1268
+
1269
+ stage = 'secondary';
1270
+ //secondary filter : level...
1271
+ if(byLevel && items.length && local_depth){
1272
+ i = local_depth_rel_current && currentItemLevel >= settings.level
1273
+ //if the limited depth is relative to current item, and current item can be found at or below the start level...
1274
+ ? currentItemLevel
1275
+ //set relative to start level...
1276
+ : settings.level;
1277
+ i += local_depth;
1278
+ //note that i has been set to the first level *not* wanted!
1279
+ if(i <= maxLevel){
1280
+ for(j = []; i <= maxLevel; i++){
1281
+ j.push('.level-' + i);
1282
+ }
1283
+ //filter to remove...
1284
+ items = items.not( j.join(',') );
1285
+ }
1286
+ }
1287
+
1288
+ //secondary filter : branch...
1289
+ if(byBranch && items.length){
1290
+ //convert start level to integer...
1291
+ j = parseInt(settings.branch_start, 10);
1292
+ //convert relative to absolute (max'd against 1)...
1293
+ j = isNaN(j) || !j ? tobLevel : (settings.branch_start.match(/^(\+|-)/) ? Math.max(1, tobLevel + j) : j);
1294
+
1295
+ //in order to be eligible for a no-kids fallback:
1296
+ // - branch must be current item
1297
+ // - fallback must be set
1298
+ // - current item has no kids
1299
+ if(ciBranch && settings.fallback && !currentItemLI.find('li').length){
1300
+ //yes, we have a fallback situation...
1301
+ fallback = 'cmw-fellback-to-' + settings.fallback;
1302
+ if(settings.fallback === 'quit'){
1303
+ //copout : just set secondary start level beyond maxLevel...
1304
+ j = maxLevel + 1;
1305
+ }else{
1306
+ //for current, fall back to tob; for parent, fall back to tob - 1, ensuring that we don't fall back further than root...
1307
+ j = settings.fallback === 'current' || tobLevel < 2 ? tobLevel : tobLevel - 1;
1308
+ //if fallback depth is specified, override depth and set to relative-to-current...
1309
+ if(settings.fallback_depth){
1310
+ local_depth = settings.fallback_depth;
1311
+ local_depth_rel_current = 1;
1312
+ }
1313
+ }
1314
+ }
1315
+
1316
+ //j is the secondary start level, tobLevel is the primary level
1317
+ //easy result : if j > maxLevel then there are no matches...
1318
+ if(j > maxLevel){
1319
+ items = $([]);
1320
+ }else{
1321
+ //if secondary start is higher up the structure than primary start, reset the tob...
1322
+ if(j < tobLevel){
1323
+ topOfBranch = topOfBranch.parentsUntil(topOfMenu, 'li.level-' + j);
1324
+ }
1325
+ //do we want (and need) to force starting with the entire level...
1326
+ // - only relevant if secondary start is at or above primary start
1327
+ // - and if secondary level is root then allow_all_root must be set
1328
+ if(settings.start_mode === 'level' && j <= tobLevel && (j > 1 || settings.allow_all_root)){
1329
+ //...reset items to eveything at tob's level, plus all their descendants...
1330
+ items = topOfBranch.parent().find('li');
1331
+ }else if(j < tobLevel){
1332
+ //tob has changed so reset items (to just tob and descendants)...
1333
+ items = topOfBranch.add( topOfBranch.find('li') );
1334
+ }
1335
+ //if falling back and siblings are required, add them in...
1336
+ //note that root level sibling inclusion is still governed by allow_all_root!
1337
+ if(!!fallback && settings.fallback_siblings && items.length && (j > 1 || settings.allow_all_root)){
1338
+ items = items.add( topOfBranch.siblings('li.level-' + j) );
1339
+ }
1340
+ }
1341
+ //may have a tob but might not have any items!...
1342
+ if(items.length){
1343
+ //reset tob level (regardless of whether tob has changed)...
1344
+ tobLevel = j;
1345
+ //is depth unlimited?...
1346
+ k = 9999;
1347
+ if(local_depth){
1348
+ //is (limited) depth relative to current item, and is there an eligible current item to measure against?...
1349
+ k = local_depth_rel_current && currentItemLevel >= tobLevel && items.filter(currentItemLI).length
1350
+ ? currentItemLevel
1351
+ : tobLevel;
1352
+ k += local_depth;
1353
+ lastVisibleLevel = k - 1;
1354
+ }
1355
+ //note that k has been set to the first level (after those wanted) that is *not* wanted!
1356
+ for(i = 1, j = []; i <= maxLevel; i++){
1357
+ if(i >= tobLevel && i < k){
1358
+ j.push('.level-' + i);
1359
+ }
1360
+ }
1361
+ //filter to keep...
1362
+ items = items.filter( j.join(',') );
1363
+ }
1364
+ }
1365
+
1366
+ //check for current item and switch...
1367
+ hasCurrent = items.length && currentItemLI.is(items);
1368
+ if(settings.contains_current === stage && !hasCurrent){
1369
+ items = $([]);
1370
+ }
1371
+ if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1372
+ altSettings = alternativeUse(settings, dialog);
1373
+ if(altSettings !== false){
1374
+ //cop out with alternative settings!...
1375
+ return altSettings;
1376
+ }
1377
+ }
1378
+
1379
+ stage = 'inclusions';
1380
+ //branch inclusions...
1381
+ //NB: only applicable if there are already items
1382
+ if(byBranch && items.length){
1383
+ //branch ancestors, possibly with their siblings : but only if the original branch item is either
1384
+ //in items or is below lastVisibleLevel; ALSO, do not show ancestors below lastVisibleLevel!
1385
+ j = theBranchItem.data().level;
1386
+ if(settings.ancestors && (theBranchItem.is(items) || j > lastVisibleLevel)){
1387
+ x = settings.ancestors;
1388
+ //convert a relative level to an absolute one...
1389
+ if(x < 0){
1390
+ x = Math.max(1, j + x);
1391
+ }
1392
+ //ancestor siblings?...
1393
+ y = settings.ancestor_siblings;
1394
+ //convert a relative level to an absolute one...
1395
+ if(y < 0){
1396
+ y = Math.max(1, j + y);
1397
+ }
1398
+ //get the level classes for ancestors and siblings that need to be included...
1399
+ for(i = x, j = [], k = []; i <= maxLevel; i++){
1400
+ if(i <= lastVisibleLevel){
1401
+ //ancestors...
1402
+ j.push('.level-' + i);
1403
+ if(y > 0 && i >= y){
1404
+ //siblings...
1405
+ k.push('.level-' + i);
1406
+ }
1407
+ }
1408
+ }
1409
+ //store current length of items...
1410
+ x = items.length;
1411
+ //find the ancestors...
1412
+ j = theBranchItem.parentsUntil(topOfMenu, j.join(','));
1413
+ //add new ones into items...
1414
+ items = items.add( j.not(items).data('included', ' cmw-an-included-ancestor') );
1415
+ //got ancestors, now what about their siblings?...
1416
+ if(k.length){
1417
+ //filter ancestors for those we want siblings of, and add new siblings into items...
1418
+ items = items.add( j.filter( k.join(',') ).siblings('li').not(items).data('included', ' cmw-an-included-ancestor-sibling') );
1419
+ }
1420
+ //note how many have been added to items as a result of the includes...
1421
+ hasIncl += items.length - x;
1422
+ }
1423
+ //branch siblings : only if the original branch item is currently in items...
1424
+ if(settings.siblings && theBranchItem.is(items)){
1425
+ j = items.length;
1426
+ items = items.add( theBranchItem.siblings('li').data('included', ' cmw-an-included-sibling') );
1427
+ hasIncl += items.length - j;
1428
+ }
1429
+ }
1430
+ //other inclusions...
1431
+ if(items.length && !!settings.include_level){
1432
+ k = getLevelClasses(settings.include_level, maxLevel);
1433
+ if(k){
1434
+ //find and add...
1435
+ j = items.length;
1436
+ items = items.add( topOfMenu.find(k) );
1437
+ hasIncl += items.length - j;
1438
+ }
1439
+ }
1440
+
1441
+ //check for current item and switch...
1442
+ hasCurrent = items.length && currentItemLI.is(items);
1443
+ if(settings.contains_current === stage && !hasCurrent){
1444
+ items = $([]);
1445
+ }
1446
+ if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1447
+ altSettings = alternativeUse(settings, dialog);
1448
+ if(altSettings !== false){
1449
+ //cop out with alternative settings!...
1450
+ return altSettings;
1451
+ }
1452
+ }
1453
+
1454
+ stage = 'output';
1455
+ //exclusions...
1456
+ if(items.length && exclusions.length){
1457
+ //filter to remove...
1458
+ j = items.length;
1459
+ items = items.not(exclusions);
1460
+ hasExcl += j - items.length;
1461
+ }
1462
+ if(items.length && !!settings.exclude_level){
1463
+ k = getLevelClasses(settings.exclude_level, maxLevel);
1464
+ if(k){
1465
+ //filter to remove...
1466
+ j = items.length;
1467
+ items = items.not(k);
1468
+ hasExcl += j - items.length;
1469
+ }
1470
+ }
1471
+
1472
+ //check for current item and switch...
1473
+ hasCurrent = items.length && currentItemLI.is(items);
1474
+ if(settings.contains_current === stage && !hasCurrent){
1475
+ items = $([]);
1476
+ }
1477
+ if(canSwitch && alternativeCheckFor(stage, hasCurrent, items.length, settings) ){
1478
+ altSettings = alternativeUse(settings, dialog);
1479
+ if(altSettings !== false){
1480
+ //cop out with alternative settings!...
1481
+ return altSettings;
1482
+ }
1483
+ }
1484
+
1485
+ //title from...
1486
+ if(settings.title_current !== '' && currentItemLI.length){
1487
+ if(settings.title_current === 0){
1488
+ currentItemLI.addClass('title-from-item');
1489
+ }else{
1490
+ i = settings.title_current > 0 ? Math.min(currentItemLevel, settings.title_current) : Math.max(1, currentItemLevel + settings.title_current);
1491
+ currentItemLI.closest('.level-' + i).addClass('title-from-item');
1492
+ }
1493
+ }else if(byBranch && theBranchItem && settings.title_branch !== ''){
1494
+ if(settings.title_branch === 0){
1495
+ theBranchItem.addClass('title-from-item');
1496
+ }else{
1497
+ i = theBranchItem.data().level;
1498
+ i = settings.title_branch > 0 ? Math.min(i, settings.title_branch) : Math.max(1, i + settings.title_branch);
1499
+ theBranchItem.closest('.level-' + i).addClass('title-from-item');
1500
+ }
1501
+ }
1502
+
1503
+ //show/hide the fall back message...
1504
+ dialog.find('.cmw-demo-fallback').data('fellback', fallback).toggleClass('updated', !!fallback);
1505
+ //show/hide the 'select current item' prompt...
1506
+ dialog.find('.cmw-demo-setcurrent').toggleClass('error', !currentItemLI.length && (!!settings.contains_current || ciBranch));
1507
+ //hide the alternative message if not using - and haven't tried to use! - alternative settings...
1508
+ if(!usingAlts && altSettings === null){
1509
+ dialog.find('.cmw-demo-alternative').removeClass('error updated');
1510
+ }
1511
+ //...and toggle the demo menu structure's class (eg. applies an opacity to the ticks/crosses if alternative settings
1512
+ // are being used, and prevents them being clicked on - because they can't be updated back into the current settings)...
1513
+ dialog.find('.cmw-demo-themenu-ul').toggleClass('cmw-using-alternative', !!usingAlts);
1514
+ //show/hide the inclusions/exclusions messages...
1515
+ i = {inclusions:hasIncl, exclusions:hasExcl};
1516
+ for(j in i){
1517
+ k = dialog.find('.cmw-demo-' + j);
1518
+ k.text( k.text().replace(/\d+$/, i[j]) ).toggleClass('updated', i[j] > 0);
1519
+ }
1520
+
1521
+ //toggle ticks and 'pick' the remaining items...
1522
+ topOfMenu.toggleClass('cmw-demo-filteritems', byItems)
1523
+ .find('.picked').not( items.addClass('picked') ).removeClass('picked');
1524
+
1525
+ //returning false means we're done!...
1526
+ return false;
1527
+
1528
+ }, //end cmwAssist.structureUpdate()
1529
+
1530
+ update : function(el){
1531
+ /**
1532
+ * updates the graphic menu structure from the widget data
1533
+ * @param {element} el Element responsible for being here
1534
+ */
1535
+ var target = $(el),
1536
+ oc = findOnchange(target), //above
1537
+ dialog = $('#' + oc.data().cmwDialogId),
1538
+ settings, altSettings;
1539
+
1540
+ if(target.hasClass('cmw-listen')){
1541
+ //the widget field that changed is likely to have an effect on other widget fields...
1542
+ this.setFields(target, oc);
1543
+ }
1544
+ settings = getSettings(oc);
1545
+
1546
+ //dialog specific...
1547
+ if(dialog.length && dialog.dialog('isOpen')){
1548
+ dialog.dialog('moveToTop');
1549
+ //if selected menu has changed, modify assist's structure...
1550
+ if(target.hasClass('cmw-select-menu')){
1551
+ createMenu(dialog);
1552
+ }
1553
+
1554
+ //if it's determined that altSettings should be used then they are intially returned and the structure update
1555
+ //is re-run with those settings (and an indicator to say that alts are bing used). otherwise, the structure
1556
+ //update simply returns false to say that the original settings should be used (either because alts don't apply,
1557
+ //or because the alts that we've got are not valid and can't be used)
1558
+ altSettings = this.structureUpdate(dialog, settings);
1559
+ if(altSettings !== false){
1560
+ //don't care what this returns (should only be false!) because we're not going to run it again...
1561
+ this.structureUpdate(dialog, altSettings, true);
1562
+ }
1563
+
1564
+ //produce output...
1565
+ setDialogTitle(dialog, oc);
1566
+ //show output with whichever settings we ended up using...
1567
+ showOutput(dialog, altSettings || settings);
1568
+ altSettings = null;
1569
+ } //end dialog specific
1570
+
1571
+ //always use original settings to update the shortcode displays...
1572
+ oc.add(dialog).find('code').not('.cmw-instance-shortcode').text( this.shortcode( settings ) );
1573
+
1574
+ } //end cmwAssist.update()
1575
+
1576
+ }; //end cmwAssist
1577
+
1578
+ /**
1579
+ * pre-v3.0.0 version
1580
+ */
1581
+ cmwAssist.v210 = {
1582
+ setLevels : function(oc, maxLevel){
1583
+ /**
1584
+ * sets any fields that are dependent on the selected menu
1585
+ * @param {object} oc jQuery of the widget's onchange wrapper
1586
+ * @param {integer} maxLevel Maximum level of the selected menu
1587
+ */
1588
+ var theSelect = oc.find('.cmw-start-level'),
1589
+ level = theSelect.val(),
1590
+ ct = theSelect.find('option').length,
1591
+ txt;
1592
+ if(level > maxLevel){
1593
+ theSelect.val(1);
1594
+ }
1595
+ theSelect.find('option').slice(maxLevel).remove();
1596
+ while(ct < maxLevel){
1597
+ ++ct;
1598
+ theSelect.append( $('<option/>', {value:ct}).text(ct) );
1599
+ }
1600
+ theSelect = oc.find('.cmw-depth');
1601
+ level = theSelect.val();
1602
+ ct = theSelect.find('option').length;
1603
+ txt = theSelect.data().cmwTextLevels;
1604
+ if(level > maxLevel){
1605
+ theSelect.val(0); //=unlimited
1606
+ }
1607
+ theSelect.find('option').slice(maxLevel + 1).remove();
1608
+ while(ct <= maxLevel){
1609
+ theSelect.append( $('<option/>', {value:ct}).text(ct + txt) );
1610
+ ++ct;
1611
+ }
1612
+ }, //end cmwAssist.v210.setLevels()
1613
+
1614
+ setFields : function(target, oc){
1615
+ /**
1616
+ * enables/disables fields and swaps selected menus
1617
+ * @param {object} target jQuery of element responsible for 'change' event
1618
+ * @param {object} oc jQuery of the widget's onchange wrapper
1619
+ */
1620
+ var showAll = oc.find('.cmw-showall').prop('checked'),
1621
+ showSpecific = oc.find('.cmw-showspecific').prop('checked'),
1622
+ menuItems = oc.find('.cmw-assist-items'),
1623
+ selectedItem = parseInt(menuItems.val(), 10);
1624
+ //change of menu? : make sure the correct optgroup of menu items is used...
1625
+ if(target.hasClass('cmw-select-menu')){
1626
+ selectedItem = swapItems(menuItems, selectedItem, target[0].selectedIndex);
1627
+ this.setLevels(oc, (menuItems.find('optgroup').data() || {}).cmwMaxLevel);
1628
+ }
1629
+ $.each(
1630
+ { '' : showAll || showSpecific,
1631
+ '-ss' : showSpecific,
1632
+ 'not-rp' : showAll || showSpecific || selectedItem >= 0,
1633
+ 'not-ci' : showAll || showSpecific || !!selectedItem
1634
+ },
1635
+ function(k, v){
1636
+ oc.find('.cmw-disableif' + k).toggleClass('cmw-colour-grey', v).find('input,select').prop('disabled', v);
1637
+ });
1638
+ }, //end cmwAssist.v210.setFields()
1639
+
1640
+ shortcode : function(settings){
1641
+ /**
1642
+ * create and return the shortcode equivalent
1643
+ * @param {object} settings Widget settings
1644
+ * @return {string} Shortcode
1645
+ */
1646
+ var args = {
1647
+ 'menu' : settings.menu
1648
+ },
1649
+ byLevel = !settings.filter,
1650
+ byBranch = settings.filter > 0,
1651
+ byItems = !byLevel && !byBranch,
1652
+ v, m, n;
1653
+ if(settings.title){
1654
+ args.title = [settings.title];
1655
+ }
1656
+ if(byBranch){
1657
+ switch(settings.filter_item){
1658
+ case 0: args.children_of = ['current']; break;
1659
+ case -1: args.children_of = ['parent']; break;
1660
+ case -2: args.children_of = ['root']; break;
1661
+ default:
1662
+ args.children_of = settings.filter_item;
1663
+ }
1664
+ }
1665
+ if(byItems){
1666
+ args.items = [settings._items];
1667
+ }
1668
+ if(byBranch && settings.filter_item < 0 && settings.fallback_no_ancestor){
1669
+ if(settings.fallback_include_parent_siblings){
1670
+ args.fallback_parent = ['siblings'];
1671
+ }else if(settings.fallback_include_parent){
1672
+ args.fallback_parent = ['parent'];
1673
+ }else{
1674
+ args.fallback_parent = 1;
1675
+ }
1676
+ }
1677
+ if(byBranch && !settings.filter_item && settings.fallback_no_children){
1678
+ if(settings.fallback_nc_include_parent_siblings){
1679
+ args.fallback_current = ['siblings'];
1680
+ }else if(settings.fallback_nc_include_parent){
1681
+ args.fallback_current = ['parent'];
1682
+ }else{
1683
+ args.fallback_current = 1;
1684
+ }
1685
+ }
1686
+ if(settings.start_level > 1){
1687
+ args.start_level = settings.start_level;
1688
+ }
1689
+ if(settings.depth > 0){
1690
+ args.depth = settings.depth;
1691
+ }
1692
+ //depth relative to current item is only applicable if depth is not unlimited...
1693
+ if(settings.depth_rel_current && settings.depth > 0){
1694
+ args.depth_rel_current = 1;
1695
+ }
1696
+ n = [];
1697
+ if(byBranch){
1698
+ if(settings.include_parent_siblings){
1699
+ n.push('siblings');
1700
+ }else if(settings.include_parent){
1701
+ n.push('parent');
1702
+ }
1703
+ if(settings.include_ancestors){
1704
+ n.push('ancestors');
1705
+ }
1706
+ if(n.length){
1707
+ args.include = n;
1708
+ }
1709
+ }
1710
+ n = [];
1711
+ if(byBranch && settings.title_from_parent){
1712
+ n.push('parent');
1713
+ }
1714
+ if(settings.title_from_current){
1715
+ n.push('current');
1716
+ }
1717
+ if(n.length){
1718
+ args.title_from = n;
1719
+ }
1720
+ for(n in {flat_output:1, contains_current:1, ol_root:1, ol_sub:1}){
1721
+ if(settings[n]){
1722
+ args[n] = 1;
1723
+ }
1724
+ }
1725
+ v = {container:'div', container_id:'', container_class:'', menu_class:'menu-widget', widget_class:''};
1726
+ for(n in v){
1727
+ if(settings[n] !== v[n]){
1728
+ args[n] = [settings[n]];
1729
+ }
1730
+ }
1731
+ v = {wrap_link:'before', wrap_link_text:'link_before'};
1732
+ for(n in v){
1733
+ m = settings[v[n]].toString().match(/^<(\w+)/);
1734
+ if(m && m[1]){
1735
+ args[n] = [m[1]];
1736
+ }
1737
+ }
1738
+ v = [];
1739
+ for(n in args){
1740
+ //array indicates join (with space sep) & surround it in double quotes, otherwise leave 'as-is'...
1741
+ v.push( $.isArray(args[n]) ? n + '="' + args[n].join(' ') + '"' : n + '=' + args[n] );
1742
+ }
1743
+ return '[custom_menu_wizard ' + v.join(' ') + ']';
1744
+ }, //end cmwAssist.v210.shortcode()
1745
+
1746
+ update : function(el){
1747
+ /**
1748
+ * updates the graphic menu structure from the widget data
1749
+ * @param {element} el Element responsible for being here
1750
+ */
1751
+ var target = $(el),
1752
+ oc = findOnchange(target), //above
1753
+ dialog = $('#' + oc.data().cmwDialogId),
1754
+ byLevel, byBranch, byItems,
1755
+ maxLevel, settings, includeParent, includeParentSiblings, topOfMenu, items,
1756
+ currentItemLI, currentItemLevel, fallback, parent, i, j;
1757
+
1758
+ if(target.hasClass('cmw-listen')){
1759
+ //the widget field that changed is likely to have an effect on other widget fields...
1760
+ this.setFields(target, oc);
1761
+ }
1762
+
1763
+ //everything below this point is dialogue-related...
1764
+ if(!dialog.length || !dialog.dialog('isOpen')){
1765
+ return;
1766
+ }
1767
+ dialog.dialog('moveToTop');
1768
+
1769
+ //if selected menu has changed, modify assist's structure...
1770
+ if(target.hasClass('cmw-select-menu')){
1771
+ createMenu(dialog);
1772
+ }
1773
+ settings = getSettings(oc);
1774
+ byLevel = !settings.filter;
1775
+ byBranch = settings.filter > 0;
1776
+ byItems = !byLevel && !byBranch;
1777
+ includeParent = settings.include_parent;
1778
+ includeParentSiblings = settings.include_parent_siblings;
1779
+ topOfMenu = dialog.find('.cmw-demo-themenu-ul');
1780
+ maxLevel = topOfMenu.data().maxLevel;
1781
+ currentItemLI = topOfMenu.find('.current-menu-item').closest('li');
1782
+ currentItemLevel = currentItemLI.length ? currentItemLI.data().level : -1;
1783
+ items = topOfMenu.find('li').removeData('included').removeClass('title-from-item');
1784
+
1785
+ if(byItems){
1786
+ items = filterTickCross(items, settings, 'tick');
1787
+ }
1788
+
1789
+ if(items.length && !currentItemLI.length && (settings.contains_current || (byBranch && settings.filter_item < 1))){
1790
+ items = $([]);
1791
+ }
1792
+
1793
+ if(items.length && byBranch){
1794
+ //kids of...
1795
+ if(settings.filter_item > 0){
1796
+ //specific item...
1797
+ parent = items.filter('[data-itemid=' + settings.filter_item + ']');
1798
+ }else if(!settings.filter_item){
1799
+ //current...
1800
+ if(currentItemLI.find('li').length){
1801
+ parent = currentItemLI;
1802
+ }else if(settings.fallback_no_children){
1803
+ //fall back to current parent...
1804
+ parent = topOfMenu.find('.current-menu-parent').closest('li');
1805
+ if(!parent.length){
1806
+ parent = topOfMenu; //beware!
1807
+ }
1808
+ includeParent = includeParent || settings.fallback_nc_include_parent;
1809
+ includeParentSiblings = includeParentSiblings || settings.fallback_nc_include_parent_siblings;
1810
+ fallback = 'cmw-fellback-to-parent';
1811
+ }
1812
+ }else{
1813
+ //parent or root...
1814
+ if(currentItemLevel === 1 && settings.fallback_no_ancestor){
1815
+ parent = currentItemLI;
1816
+ includeParent = includeParent || settings.fallback_include_parent;
1817
+ includeParentSiblings = includeParentSiblings || settings.fallback_include_parent_siblings;
1818
+ fallback = 'cmw-fellback-to-current';
1819
+ }else if(currentItemLevel === 1){
1820
+ parent = topOfMenu; //beware!
1821
+ }else if(settings.filter_item < -1){
1822
+ parent = topOfMenu.find('.current-menu-ancestor').eq(0).closest('li');
1823
+ }else{
1824
+ parent = topOfMenu.find('.current-menu-parent').closest('li');
1825
+ }
1826
+ }
1827
+ }
1828
+
1829
+ if(items.length){
1830
+ if(byLevel){
1831
+ //showall : use the levels...
1832
+ if(settings.depth_rel_current && settings.depth && currentItemLI.length && currentItemLevel >= settings.start_level){
1833
+ j = currentItemLevel + settings.depth - 1;
1834
+ }else{
1835
+ j = settings.depth ? settings.start_level + settings.depth - 1 : 9999;
1836
+ }
1837
+ for(i = 1; i <= maxLevel; i++){
1838
+ if(i < settings.start_level || i > j){
1839
+ items = items.not('.level-' + i);
1840
+ }
1841
+ }
1842
+ }else if(parent && parent.length){
1843
+ //kids of...
1844
+ if(settings.depth_rel_current && settings.depth && currentItemLI.length && parent.has(currentItemLI[0]).length){
1845
+ j = currentItemLevel - 1 + settings.depth;
1846
+ }else{
1847
+ j = settings.depth ? Math.max( (parent.data().level || 0) + settings.depth, settings.start_level + settings.depth - 1 ) : 9999;
1848
+ }
1849
+ items = parent.find('li').filter(function(){
1850
+ var level = $(this).data().level;
1851
+ return level >= settings.start_level && level <= j;
1852
+ });
1853
+ }else if(byBranch){
1854
+ //kids-of, but no parent found...
1855
+ items = $([]);
1856
+ }
1857
+ }
1858
+
1859
+ if(items.length){
1860
+ if(byBranch && parent && parent.is('li')){
1861
+ //kids of an item...
1862
+ if(includeParentSiblings){
1863
+ items = items.add( parent.siblings('li').data('included', ' cmw-an-included-parent-sibling') );
1864
+ includeParent = true;
1865
+ }
1866
+ if(settings.include_ancestors){
1867
+ items = items.add( parent.parentsUntil(topOfMenu, 'li').data('included', ' cmw-an-included-ancestor') );
1868
+ includeParent = true;
1869
+ }
1870
+ if(includeParent){
1871
+ items = items.add( parent.data('included', ' cmw-the-included-parent') );
1872
+ }
1873
+ }
1874
+ }
1875
+
1876
+ //must contain current item?...
1877
+ if(items.length && settings.contains_current && (!currentItemLI.length || !items.filter(currentItemLI).length)){
1878
+ items = $([]);
1879
+ }
1880
+
1881
+ //title from parent has higher priority than title from current...
1882
+ if(settings.title_from_parent && items.length && parent && parent.is('li')){
1883
+ parent.addClass('title-from-item');
1884
+ }else if(settings.title_from_current && items.length){
1885
+ currentItemLI.addClass('title-from-item');
1886
+ }
1887
+ //fallback?...
1888
+ fallback = items.length ? fallback : '';
1889
+ dialog.find('.cmw-demo-fallback').data('fellback', fallback).toggleClass('updated', !!fallback);
1890
+ //show/hide the 'select current item' prompt...
1891
+ dialog.find('.cmw-demo-setcurrent').toggleClass('error', !currentItemLI.length && (settings.contains_current || (byBranch && settings.filter_item < 1)));
1892
+
1893
+ //toggle ticks and 'pick' the remaining items...
1894
+ topOfMenu.toggleClass('cmw-demo-filteritems', byItems)
1895
+ .find('.picked').not( items.addClass('picked') ).removeClass('picked');
1896
+ //produce output...
1897
+ setDialogTitle(dialog, oc);
1898
+ showOutput(dialog, settings);
1899
+ dialog.find('code').text( this.shortcode(settings) );
1900
+ } //end cmwAssist.v210.update()
1901
+ }; //end cmwAssist.v210
1902
+
1903
+ $(document)
1904
+ //any change event on widget's inputs or selects...
1905
+ .on('change', widgetCustomMenuWizardClass('onchange', 1), assistance)
1906
+ //open/close assist dialog...
1907
+ .on('click', widgetCustomMenuWizardClass('assist', 1), clickAssist)
1908
+ //expand/collapse fieldsets...
1909
+ .on('click', widgetCustomMenuWizardClass('fieldset', 1), clickFieldset)
1910
+ //when a widget is closed, close its open dialog...
1911
+ .on('click', '.widget-action,.widget-control-close', closeDialog)
1912
+ //when a widget is deleted, remove its dialog...
1913
+ .on('click', '.widget-control-remove', removeDialog)
1914
+ //collapse/expand assist menu tree items...
1915
+ .on('click', widgetCustomMenuWizardClass('colexp', 1), clickTreeExpander)
1916
+ //find posts containing shortcodes by ajax...
1917
+ .on('click', widgetCustomMenuWizardClass('find-shortcodes', 1), clickFindShortcodes)
1918
+ //remove the legacy warning...
1919
+ .on('click', widgetCustomMenuWizardClass('legacy-close', 1), function(){
1920
+ $(this).parent().remove();
1921
+ return false;
1922
+ });
1923
+
1924
+ //editing in accessibility mode...
1925
+ if(!!window.Custom_Menu_Wizard_Widget){
1926
+ $(window.Custom_Menu_Wizard_Widget.trigger || []).trigger('change');
1927
+ }
1928
  });
custom-menu-wizard.min.js CHANGED
@@ -1,59 +1,62 @@
1
  /*Source: custom-menu-wizard.js
2
- *Compiled: 2015-04-02, Google Closure Compiler...
3
  *STATISTICS
4
- * - originalSize: 76120
5
- * - originalGzipSize: 19261
6
- * - compressedSize: 26287
7
- * - compressedGzipSize: 8440
8
  */
9
- jQuery(function(g){var x,L=/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)/g,v=function(b,a){return(a?".":"")+"widget-custom-menu-wizard-"+b},A=function(b){var a=g(this).data().cmwDialogVersion.replace(/\./g,""),a=/^\d+$/.test(a)?"v"+a:a;x[a]?x[a].update(b?b.target:this):x.update(b?b.target:this)},C=function(b,a){var c=!1===b,d=c?{}:b.data(),e={},k=!c&&"2.1.0"===d.cmwDialogVersion,f={items:1,exclude:1},m=g.extend({branch_start:1,exclude_level:1,include_level:1},
10
- f);g.each(c?a:b.find(":input").serializeArray(),function(a,b){var h=b.name.replace(/.*\[([^\]]+)\]$/,"$1"),p=!m[h]&&/^-?\d+$/.test(b.value)?parseInt(b.value,10):b.value;e[h]=p;"hide_empty"===h?e[h]=c||!!d.cmwV36plus||p:f[h]&&(e["_"+h+"_sep"]=!p||/(^\d+\+?$|,)/.test(g.trim(p))?",":" ",p=g.map(p.split(/[,\s]+/),function(a){var b=!k&&/\+$/.test(a);a=a?parseInt(a,10):0;return isNaN(a)||1>a?null:b?a+"+":a}),e["_"+h]=p.join(e["_"+h+"_sep"]))});return e},D=function(b,a,c,d){var e=d.switch_if;return d.switch_at===
11
- b&&("current"===e&&a||"no-current"===e&&!a||"no-output"===e&&!c)},Q=function(b,a){b=g.trim(b||"");for(var c={title:"",level:1,branch:0,items:"",depth:0,depth_rel_current:0,start_at:"",start_mode:"",allow_all_root:0,ancestors:0,ancestor_siblings:0,include_root:0,include_level:"",siblings:0,exclude:"",exclude_level:"",contains_current:"",fallback:"",flat_output:0,title_from:"",title_linked:0,ol_root:0,ol_sub:0},d=L.exec(b),e={},k=0,f;d;)k++,d[1]?e[d[1]]=d[2]:d[3]?e[d[3]]=d[4]:d[5]?e[d[5]]=d[6]:k--,
12
- d=L.exec(b);if(k)for(f in e)c.hasOwnProperty(f)&&(c[f]=e[f]);f=!!c.items;d=!f&&!!c.branch;e=!f&&!d;f&&(c.filter="items");if(d){c.filter="branch";f=c.start_at.toString();c.branch_start=f;if("0"===f||"branch"===f)c.branch_start="";"root"===f&&(c.branch_start="1");"children"===f&&(c.branch_start="+1");"parent"===f&&(c.branch_start="-1");if("current"===c.branch||"current-item"===c.branch)c.branch=0;else if(!/^[+\-]?\d+$/.test(c.branch.toString()))if(c.branch=c.branch.toLowerCase(),f=a.find("a.cmw-item").filter(function(){return g(this).text().toLowerCase()===
13
- c.branch}),f.length)c.branch=f.parent().data("itemid");else return!1}e&&(c.filter="",c.level=Math.max(1,parseInt(c.level,10)));c.start_at=null;c.include_level||"1"!==c.include_root||(c.include_level="1");c.include_root=null;if(d&&!c.branch&&c.fallback&&(e=c.fallback.toLowerCase().split(/[\s,]+/),f=" "+e.join(" ")+" ",c.fallback="",0<=f.indexOf(" quit ")?c.fallback="quit":0<=f.indexOf(" parent ")?c.fallback="parent":0<=f.indexOf(" current ")&&(c.fallback="current"),""!==c.fallback&&"quit"!==c.fallback))for(0<=
14
- f.indexOf(" +siblings ")&&(c.fallback_siblings=1),k=0;k<e.length;k++)if(/^\d+$/.test(e[k])&&(f=parseInt(e[k],10),0<f)){c.fallback_depth=f;break}if(c.title_from)for(e=c.title_from.toLowerCase().split(/[\s,]+/),k=0;k<e.length;k++)if("branch"===e[k]||"current"===e[k])c["title_from_"+e[k]]=1;else if("branch-root"===e[k]||"current-root"===e[k])c["title_from_"+e[k].replace("-","_")]=1;c.title_from=null;c.hide_empty=1;return C(!1,g.map(c,function(a,b){return null===a?a:{name:b,value:a}}))},M=function(b){var a=
15
- "";b&&(a=b.replace(/[\r\n\t]+/g," ").replace(/^[\[\s]+/,"").replace(/[\s\/\]]+$/,"").split(/[\[\]]/)[0],a=g.trim(a.replace(/\/+$/,""))+" ",a=g.trim((" "+a.replace(/^cmwizard\s/,"")+" ").replace(/\smenu=[^\s]*\s/," ").replace(/\salternative=("[^"]*"|[^\s]*)\s/," ")),a=a.replace(/\s\s+/g," "));return a},E=function(b,a){var c=a.find(".cmw-demo-themenu-ul"),d=c.data(),e=M(b.switch_to),e="cmwizard menu="+b.menu+(""===e?e:" "+e);d.altCode&&d.altCode===e||(d.altCode=e,d.altSettings=Q(e,c));a.find(".cmw-demo-alternative").addClass("updated").toggleClass("error",
16
- !1===d.altSettings);return!1===d.altSettings?!1:g.extend({},d.altSettings)},I=function(b,a,c){var d="tick"===c?a._items_sep:a._exclude_sep,e=[],k="tick"===c?"_items":"_exclude",k=a[k]?g.grep(a[k].split(d),function(a){return/\+$/.test(a)?(e.push(parseInt(a,10)),!a):!!a}):[],k=d+k.join(d)+d,e=d+e.join(d)+d;b=b.each(function(){var a=g(this),b=a.data(),q=-1<e.indexOf(d+b.itemid+d),b=q||-1<k.indexOf(d+b.itemid+d);a.toggleClass("cmw-has-"+c,b).toggleClass("cmw-inherit-"+c,q)});return b.filter(".cmw-inherit-"+
17
- c).find("li").removeClass("cmw-has-"+c+" cmw-inherit-"+c).add(b.filter(".cmw-has-"+c))},y=function(b,a){return(b?b:a)[b?"find":"closest"](v("onchange",1))},N=function(b,a){var c=[],d=b.match(/^(\d+)(\+|-)?$/),e,d=d?[parseInt(d[1],10),d[2]||""]:[];if(0<d[0])for(e=1;e<=a;e++)(e===d[0]||"-"===d[1]&&e<d[0]||"+"===d[1]&&e>d[0])&&c.push(".level-"+e);return c.join(",")},O=function(b,a){var c=b.find(".cmw-demo-themenu-ul"),d=c.find(".picked"),e="",k=a.hide_title?"":a.title,f=0,m=0,q=b.find(".cmw-demo-theoutput-wrap").empty(),
18
- t=["menu-widget"],h={};if(d.length&&q.length){k=c.find(".title-from-item").children(".cmw-item").text()||"";f=!!k&&!!a.title_linked;k||a.hide_title||(k=a.title||"");for(d.each(function(b){var c=g(this),d=c.data(),f=d.trace?d.trace.toString().split(","):[];b=d.itemid.toString();var k=1,c=c.children(".cmw-item");if(!a.flat_output)for(h[b]=1,b=0;b<f.length;b++)h[f[b]]&&k++;if(m)if(k>m)e+=a.ol_sub?"<ol>":"<ul>";else{for(;m>k;)--m,e+="</li>"+(a.ol_sub?"</ol>":"</ul>");e+="</li>"}e+='<li class="cmw-level-'+
19
- k+(d.included||"")+'"><a href="#'+c.data("indx")+'">'+c.text()+"</a>";m=k});1<m;)--m,e+="</li>"+(a.ol_sub?"</ol>":"</ul>");e+="</li>";t.push(b.find(".cmw-demo-fallback").data("fellback"));e=(a.ol_root?"<ol":"<ul")+' class="'+g.trim(t.join(" "))+'">'+e+(a.ol_root?"</ol>":"</ul>");q.html(e);q.find("li").filter(function(){return!!g(this).children("ul, ol").length}).addClass("cmw-has-submenu")}q.length&&k&&(d.length||!a.hide_empty)&&q.prepend((f?'<h3 class="cmw-title-linked">':"<h3>")+k+"</h3>")},P=function(b,
20
- a,c){var d;b.find("optgroup").filter(function(){var a=g(this).data().cmwOptgroupIndex===c;a||g(this).remove();return a}).length||(d=g("#"+b.attr("id")+"_ignore").find("optgroup").eq(c).clone(),d.length&&(0<a&&(a=0),b.append(d).val(a)));return a},R=function(b){b=g(this);var a=b.data(),c=!a.cmwAbsolute,d=b.closest(".ui-dialog"),e=d.find(".ui-dialog-content"),k=parseInt(d.css("top"),10)+(c?1:-1)*g(document).scrollTop();a.cmwAbsolute=c;a.cmwMaxHeight||(a.cmwMaxHeight=e.dialog("option","maxHeight"));b.button("option",
21
- "icons",{primary:c?"ui-icon-circle-close":"ui-icon-circle-check"});d.toggleClass("cmw-assistance-dialog-fixed",!c);e.dialog("option",{maxHeight:c?!c:a.cmwMaxHeight});d.css("top",k);return!1},S=function(b){var a=g(this);b=["current-menu-item","current-menu-parent","current-menu-ancestor"];var c=a.closest(".ui-dialog-content"),d=c.find(".cmw-demo-themenu-ul"),a=a.find("span").not("."+b[0]).parentsUntil(d,"li"),e,k=function(){this.title=this.title+" "+e.replace(" "," & ").replace(/-/g," ")};d.find("."+
22
- b.join(",.")).removeClass(b.join(" ")).each(function(){this.title=this.title.replace(/\s.*$/,"")});for(d=0;d<a.length;d++)e=1===d?b.join(" "):b[0],a.eq(d).children(".cmw-item").find("span").addClass(e).each(k),1<b.length&&b.shift();A.call(g(c.data().cmwOnchange).get(0));return!1},T=function(b){g(this).closest(".ui-dialog-content").find(".cmw-item").eq(this.href.split("#")[1]).not(":has(.current-menu-item)").trigger("click");this.blur();return!1},U=function(b){b=g(this);var a=b.hasClass("cmw-tick")?
23
- "tick":"cross",c=b.parent();b=c.closest(".cmw-demo-themenu-ul");var d=c.hasClass("cmw-inherit-"+a),e=d||c.hasClass("cmw-has-"+a),k=e?g([]):c.parentsUntil(b,".cmw-inherit-"+a),f=g(c.closest(".ui-dialog-content").data().cmwOnchange).find("tick"===a?".cmw-setitems":".cmw-exclusions"),m;!b.hasClass("cmw-using-alternative")&&f.length&&(m=b.find(".cmw-has-"+a)[d||k.length?"not":"add"](c),!e||c.children("ul").length&&!b.parent().hasClass("cmw-version-210")?e&&!d&&(m=m.not(c.find(".cmw-has-"+a))):m=m.not(c),
24
- m=m.add(k.find("li").not(c)),m=m.map(function(){var b=this===c[0]||this===k.get(0)?e:g(this).hasClass("cmw-inherit-"+a);return g(this).data().itemid+(b?"+":"")}).get().join(/(,|^\d+\+?$)/.test(g.trim(f.val())||",")?",":" "),f.val(m).trigger("change"));this.blur();return!1},V=function(b){var a={autoOpen:!1,width:Math.min(.9*g(window).width(),600),maxHeight:g(window).height()-40,modal:!1,containment:"window",create:function(){var a=g(this).closest(".ui-dialog");if(a.hasClass("cmw-assistance-dialog-fixed"))g("<button/>").addClass("cmw-dialog-fixed-absolute").button({label:b.cmwDialogFixed,
25
- icons:{primary:"ui-icon-circle-check"}}).appendTo(a.find(".ui-dialog-titlebar")).on("click",R)},dialogClass:"cmw-assistance-dialog cmw-assistance-dialog-fixed"},c=g.map(["SetCurrent","Inclusions","Exclusions","Fallback","Alternative"],function(a){return'<div class="cmw-demo-'+a.toLowerCase()+' cmw-demo-small">'+(b["cmwDialog"+a]||"")+"</div>"}),c=g("<div/>",{id:b.cmwDialogId}).addClass(v("dialog")).append(g("<div/>").addClass("cmw-demo-themenu cmw-version-"+b.cmwDialogVersion.replace(/\./g,"")).html('<em class="cmw-demo-small">'+
26
- b.cmwDialogPrompt+"</em>")).append(g("<div/>").addClass("cmw-demo-theoutput").html('<em class="cmw-demo-small">'+b.cmwDialogOutput+'</em><em class="cmw-demo-plugin-version cmw-demo-small">v'+b.cmwDialogVersion+"</em>"+c.shift()+'<div class="cmw-demo-theoutput-wrap ui-corner-all"></div>'+c.join(""))).append(g("<div/>").addClass("cmw-demo-theshortcode").html('<code class="ui-corner-all"></code><div class="cmw-find-shortcodes"><a href="#" class="button-secondary '+v("find-shortcodes")+'" data-nonce="'+
27
- (b.cmwDialogNonce||"")+'" title="'+b.cmwDialogShortcodes+'"><span class="spinner"></span><span>[&hellip;]</span></a></div><div class="cmw-demo-found-shortcodes cmw-demo-small ui-corner-all"></div>'));c.dialog(a);c.find(".cmw-demo-themenu").on("click",".cmw-tick,.cmw-cross",U).on("click",".cmw-item",S);c.find(".cmw-demo-theoutput").on("click","a",T);return c},J=function(b,a){b.dialog("option","title","CMW : "+(a.find(".cmw-widget-title").val()||b.data().cmwUntitled)+" ["+a.find(".cmw-select-menu").find("option:selected").text()+
28
- "]")},K=function(b){var a=g(b.data().cmwOnchange),c=parseInt(a.find(".cmw-select-menu").val(),10),d=b.find(".cmw-demo-themenu-ul"),e=[],k=0,f="",m=function(a){return 1<a?'<a href="#" class="'+v("colexp")+' ui-icon ui-icon-triangle-1-e" style="left:-'+(2.4*(a-2)+2)+'em;">&nbsp;</a>':""};if(!d.length||d.data("menuid")!==c){for(a.find(".cmw-assist-items optgroup").find("option").each(function(a){for(var b=g(this),c=b.data().cmwLevel;c<e.length;)f+="</li></ul>"+m(e.length),e.pop();c>e.length?f+="<ul>":
29
- (f+="</li>",e.pop());f+='<li class="level-'+c+'" data-itemid="'+this.value+'" data-level="'+c+'" data-trace="'+e.join(",")+'">';f+='<a href="#" class="cmw-cross ui-corner-all"></a>';f+='<a class="cmw-item ui-corner-all" href="#" data-indx="'+a+'"><span class="ui-corner-all" title="#'+this.value+'">'+g.trim(b.text());f+='</span></a><a href="#" class="cmw-tick ui-corner-all"></a>';e.push(this.value);c>k&&(k=c)});e.length;)f+="</li></ul>"+m(e.length),e.pop();d.remove();b.find(".cmw-demo-themenu").append(g(f).addClass("cmw-demo-themenu-ul").data({maxLevel:k,
30
- menuid:c}))}};x={setLevels:function(b,a){if(a){var c=b.find(".cmw-branch-start"),d=c.val();c.find("optgroup").each(function(b){var c=g(this),d=c.find("option"),m=d.length,q=c.data();if(b)for(d.slice(a).remove();m<a;)++m,c.append(g("<option/>",{value:m}).text(m));else for(m=(m+1)/2,m>a&&d.each(function(b,c){Math.abs(c.value)>=a&&g(c).remove()});m<a;)c.prepend(g("<option/>",{value:-m}).text(-m+(1<m?"":" ("+q.cmwTextParent+")"))).append(g("<option/>",{value:"+"+m}).text("+"+m+(1<m?"":" ("+q.cmwTextChildren+
31
- ")"))),++m});/^\d+$/.test(d)?d>a&&c.val("1"):""!==d&&Math.abs(d)>=a&&c.val("");b.find(".cmw-ancestors,.cmw-ancestor-siblings").each(function(){var b=g(this),c=(b.find("option").length+1)/2,d=b.val(),m=Math.max(2,a);Math.abs(d)>=a&&b.val(0>d?1-a:a-1);c!==m&&b.find("optgroup").each(function(a,b){var d=g(b),e=d.data().cmwTextForOption,f;c>m&&d.find("option").slice(m-c).remove();for(f=c;f<m;f++)d.append(g("<option/>",{value:a?f:-f}).text(e.replace("%d",a?f:-f)))})});b.find(".cmw-include-level,.cmw-exclude-level").each(function(){var b=
32
- g(this),c=b.find("option"),d=(c.length-1)/3,m=b.val(),q=c.eq(2).text(),t=c.eq(3).text();for(c.slice(3*a+1).remove();d<a;)++d,b.append(g("<option/>",{value:d}).text(d)).append(g("<option/>",{value:d+"-"}).text(q.replace(/\d+/,d))).append(g("<option/>",{value:d+"+"}).text(t.replace(/\d+/,d)));parseInt(m,10)>a&&b.val("")});b.find(".cmw-set-levels").each(function(){var b=g(this),c=b.data(),d=c.cmwTextLevels||"",c=c.cmwSetLevels||0,m=b.find("option"),q=m.length-c;b.val()>a&&b.val(m.eq(0).val());for(b.find("option").slice(c+
33
- a).remove();q<a;)++q,b.append(g("<option/>",{value:q}).text(q+d))})}},setFields:function(b,a){var c=a.find(".cmw-bybranch"),d=a.find(".cmw-byitems").prop("checked"),e=d||!c.prop("checked"),k=a.find(".cmw-assist-items"),f=parseInt(k.val(),10);b.hasClass("cmw-select-menu")?(f=P(k,f,b[0].selectedIndex),this.setLevels(a,(k.find("optgroup").data()||{}).cmwMaxLevel)):b.hasClass("cmw-level")?(e=!0,d=!e,a.find(".cmw-bylevel").prop("checked",e)):b.is(k)?(e=d=!1,c.prop("checked",!e)):b.hasClass("cmw-setitems")?
34
- (e=d=!0,a.find(".cmw-byitems").prop("checked",d)):b.hasClass("cmw-ancestors")&&"0"===b.val()?a.find(".cmw-ancestor-siblings").val("0"):b.hasClass("cmw-ancestor-siblings")&&"0"!==b.val()&&"0"===a.find(".cmw-ancestors").val()&&a.find(".cmw-ancestors").val(b.val());c=a.find(".cmw-fallback").val();g.each({"-ss":d,"-ud":d||1>a.find(".cmw-depth").val(),"not-br":e,"not-br-ci":e||!!f,"not-fb-pc":e||!!f||"parent"!==c&&"current"!==c,"not-sw":!!a.find(".cmw-switchable").filter(function(){return!g(this).val()}).length},
35
- function(b,c){a.find(".cmw-disableif"+b).toggleClass("cmw-colour-grey",c)})},shortcode:function(b){var a={menu:b.menu},c="branch"===b.filter,d="items"===b.filter,e,k;b.title&&!b.hide_title&&(a.title=[b.title]);!c&&!d&&1<b.level&&(a.level=b.level);c&&(a.branch=b.branch||"current",b.branch_start&&(a.start_at=[b.branch_start]),"level"===b.start_mode&&(a.start_mode="level"),b.allow_all_root&&(a.allow_all_root=1));d&&(a.items=[b._items]);0<b.depth&&(a.depth=b.depth);b.depth_rel_current&&0<b.depth&&(a.depth_rel_current=
36
- 1);c&&!b.branch&&b.fallback&&(a.fallback=[b.fallback],"quit"!==b.fallback&&(b.fallback_siblings&&a.fallback.push("+siblings"),b.fallback_depth&&a.fallback.push(b.fallback_depth)));c&&b.ancestors&&(a.ancestors=b.ancestors,b.ancestor_siblings&&(a.ancestor_siblings=b.ancestor_siblings));b.include_level&&(a.include_level=[b.include_level]);b._exclude&&(a.exclude=[b._exclude]);b.exclude_level&&(a.exclude_level=[b.exclude_level]);d=[];b.title_from_current?d.push("current"):b.title_from_current_root&&d.push("current-root");
37
- c&&b.title_from_branch?d.push("branch"):c&&b.title_from_branch_root&&d.push("branch-root");d.length&&(a.title_from=d,b.title_linked&&(a.title_linked=1));for(d in{siblings:1,flat_output:1,ol_root:1,ol_sub:1,fallback_ci_parent:1})b[d]&&(a[d]=1);c={contains_current:"",container:"div",container_id:"",container_class:"",menu_class:"menu-widget",widget_class:""};for(d in c)b[d]!==c[d]&&(a[d]=[b[d]]);c={wrap_link:"before",wrap_link_text:"link_before"};for(d in c)(e=b[c[d]].toString().match(/^<(\w+)/))&&
38
- e[1]&&(a[d]=[e[1]]);b.switch_if&&b.switch_at&&(a.alternative=[b.switch_if,b.switch_at],k=M(b.switch_to));c=[];for(d in a)c.push(g.isArray(a[d])?d+'="'+a[d].join(",")+'"':d+"="+a[d]);return"[cmwizard "+c.join(" ")+(k?"]"+k+"[/cmwizard]":"/]")},structureUpdate:function(b,a,c){var d=-1,e=9999,k=0,f=0,m="",q=null,t,h,p,n,u="menu",r="branch"===a.filter,v="items"===a.filter,y=!r&&!v,x=r&&!a.branch,F=!c&&!!a.switch_if&&!!a.switch_at,B=b.find(".cmw-demo-themenu-ul"),z=B.data().maxLevel,w=B.find(".current-menu-item").closest("li"),
39
- H=w.length?w.data().level:-1,l=B.find("li").removeData("included").removeClass("title-from-item"),G=a.depth,A=a.depth_rel_current,C=I(l,a,"cross");h=l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(F&&D(u,h,l.length,a)&&(q=E(a,b),!1!==q))return q;u="primary";v&&l.length&&(l=I(l,a,"tick"));r&&l.length&&(p=x?w:l.filter("[data-itemid="+a.branch+"]"),p.length?(d=p.data().level||0,l=p.add(p.find("li")),t=p):l=g([]));if(y&&l.length&&1<a.level){h=1;for(n=[];h<a.level;h++)n.push(".level-"+h);l=l.not(n.join(","))}h=
40
- l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(F&&D(u,h,l.length,a)&&(q=E(a,b),!1!==q))return q;u="secondary";if(y&&l.length&&G&&(h=A&&H>=a.level?H:a.level,h+=G,h<=z)){for(n=[];h<=z;h++)n.push(".level-"+h);l=l.not(n.join(","))}if(r&&l.length&&(n=parseInt(a.branch_start,10),n=isNaN(n)||!n?d:a.branch_start.match(/^(\+|-)/)?Math.max(1,d+n):n,x&&a.fallback&&!w.find("li").length&&(m="cmw-fellback-to-"+a.fallback,"quit"===a.fallback?n=z+1:(n="current"===a.fallback||2>d?d:d-1,a.fallback_depth&&
41
- (G=a.fallback_depth,A=1))),n>z?l=g([]):(n<d&&(p=p.parentsUntil(B,"li.level-"+n)),"level"===a.start_mode&&n<=d&&(1<n||a.allow_all_root)?l=p.parent().find("li"):n<d&&(l=p.add(p.find("li"))),m&&a.fallback_siblings&&l.length&&(1<n||a.allow_all_root)&&(l=l.add(p.siblings("li.level-"+n)))),l.length)){d=n;p=9999;G&&(p=A&&H>=d&&l.filter(w).length?H:d,p+=G,e=p-1);h=1;for(n=[];h<=z;h++)h>=d&&h<p&&n.push(".level-"+h);l=l.filter(n.join(","))}h=l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(F&&D(u,
42
- h,l.length,a)&&(q=E(a,b),!1!==q))return q;u="inclusions";if(r&&l.length){n=t.data().level;if(a.ancestors&&(t.is(l)||n>e)){h=a.ancestors;0>h&&(h=Math.max(1,n+h));d=a.ancestor_siblings;0>d&&(d=Math.max(1,n+d));n=[];for(p=[];h<=z;h++)h<=e&&(n.push(".level-"+h),0<d&&h>=d&&p.push(".level-"+h));h=l.length;n=t.parentsUntil(B,n.join(","));l=l.add(n.not(l).data("included"," cmw-an-included-ancestor"));p.length&&(l=l.add(n.filter(p.join(",")).siblings("li").not(l).data("included"," cmw-an-included-ancestor-sibling")));
43
- k+=l.length-h}a.siblings&&t.is(l)&&(n=l.length,l=l.add(t.siblings("li").data("included"," cmw-an-included-sibling")),k+=l.length-n)}l.length&&a.include_level&&(p=N(a.include_level,z))&&(n=l.length,l=l.add(B.find(p)),k+=l.length-n);h=l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(F&&D(u,h,l.length,a)&&(q=E(a,b),!1!==q))return q;u="output";l.length&&C.length&&(n=l.length,l=l.not(C),f+=n-l.length);l.length&&a.exclude_level&&(p=N(a.exclude_level,z))&&(n=l.length,l=l.not(p),f+=n-l.length);h=
44
- l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(F&&D(u,h,l.length,a)&&(q=E(a,b),!1!==q))return q;a.title_from_current&&w.length?w.addClass("title-from-item"):a.title_from_current_root&&w.length?w.closest(".level-1").addClass("title-from-item"):r&&t&&(a.title_from_branch?t.addClass("title-from-item"):a.title_from_branch_root&&t.closest(".level-1").addClass("title-from-item"));b.find(".cmw-demo-fallback").data("fellback",m).toggleClass("updated",!!m);b.find(".cmw-demo-setcurrent").toggleClass("error",
45
- !w.length&&(!!a.contains_current||x));c||null!==q||b.find(".cmw-demo-alternative").removeClass("error updated");b.find(".cmw-demo-themenu-ul").toggleClass("cmw-using-alternative",!!c);h={inclusions:k,exclusions:f};for(n in h)p=b.find(".cmw-demo-"+n),p.text(p.text().replace(/\d+$/,h[n])).toggleClass("updated",0<h[n]);B.toggleClass("cmw-demo-filteritems",v).find(".picked").not(l.addClass("picked")).removeClass("picked");return!1},update:function(b){var a=g(b);b=y(0,a);var c=g("#"+b.data().cmwDialogId),
46
- d;a.hasClass("cmw-listen")&&this.setFields(a,b);d=C(b);c.length&&c.dialog("isOpen")&&(c.dialog("moveToTop"),a.hasClass("cmw-select-menu")&&K(c),a=this.structureUpdate(c,d),!1!==a&&this.structureUpdate(c,a,!0),J(c,b),O(c,a||d));b.add(c).find("code").text(this.shortcode(d))},v210:{setLevels:function(b,a){var c=b.find(".cmw-start-level"),d=c.val(),e=c.find("option").length,k;d>a&&c.val(1);for(c.find("option").slice(a).remove();e<a;)++e,c.append(g("<option/>",{value:e}).text(e));c=b.find(".cmw-depth");
47
- d=c.val();e=c.find("option").length;k=c.data().cmwTextLevels;d>a&&c.val(0);for(c.find("option").slice(a+1).remove();e<=a;)c.append(g("<option/>",{value:e}).text(e+k)),++e},setFields:function(b,a){var c=a.find(".cmw-showall").prop("checked"),d=a.find(".cmw-showspecific").prop("checked"),e=a.find(".cmw-assist-items"),k=parseInt(e.val(),10);b.hasClass("cmw-select-menu")&&(k=P(e,k,b[0].selectedIndex),this.setLevels(a,(e.find("optgroup").data()||{}).cmwMaxLevel));g.each({"":c||d,"-ss":d,"not-rp":c||d||
48
- 0<=k,"not-ci":c||d||!!k},function(b,c){a.find(".cmw-disableif"+b).toggleClass("cmw-colour-grey",c).find("input,select").prop("disabled",c)})},shortcode:function(b){var a={menu:b.menu},c=0<b.filter,d=!!b.filter&&!c,e;b.title&&(a.title=[b.title]);if(c)switch(b.filter_item){case 0:a.children_of=["current"];break;case -1:a.children_of=["parent"];break;case -2:a.children_of=["root"];break;default:a.children_of=b.filter_item}d&&(a.items=[b._items]);c&&0>b.filter_item&&b.fallback_no_ancestor&&(a.fallback_parent=
49
- b.fallback_include_parent_siblings?["siblings"]:b.fallback_include_parent?["parent"]:1);c&&!b.filter_item&&b.fallback_no_children&&(a.fallback_current=b.fallback_nc_include_parent_siblings?["siblings"]:b.fallback_nc_include_parent?["parent"]:1);1<b.start_level&&(a.start_level=b.start_level);0<b.depth&&(a.depth=b.depth);b.depth_rel_current&&0<b.depth&&(a.depth_rel_current=1);d=[];c&&(b.include_parent_siblings?d.push("siblings"):b.include_parent&&d.push("parent"),b.include_ancestors&&d.push("ancestors"),
50
- d.length&&(a.include=d));d=[];c&&b.title_from_parent&&d.push("parent");b.title_from_current&&d.push("current");d.length&&(a.title_from=d);for(d in{flat_output:1,contains_current:1,ol_root:1,ol_sub:1})b[d]&&(a[d]=1);c={container:"div",container_id:"",container_class:"",menu_class:"menu-widget",widget_class:""};for(d in c)b[d]!==c[d]&&(a[d]=[b[d]]);c={wrap_link:"before",wrap_link_text:"link_before"};for(d in c)(e=b[c[d]].toString().match(/^<(\w+)/))&&e[1]&&(a[d]=[e[1]]);c=[];for(d in a)c.push(g.isArray(a[d])?
51
- d+'="'+a[d].join(" ")+'"':d+"="+a[d]);return"[custom_menu_wizard "+c.join(" ")+"]"},update:function(b){var a=g(b);b=y(0,a);var c=g("#"+b.data().cmwDialogId),d,e,k,f,m,q,t,h,p,n,u,r,v;a.hasClass("cmw-listen")&&this.setFields(a,b);if(c.length&&c.dialog("isOpen")){c.dialog("moveToTop");a.hasClass("cmw-select-menu")&&K(c);f=C(b);d=!f.filter;a=0<f.filter;e=!d&&!a;m=f.include_parent;q=f.include_parent_siblings;t=c.find(".cmw-demo-themenu-ul");k=t.data().maxLevel;p=t.find(".current-menu-item").closest("li");
52
- n=p.length?p.data().level:-1;h=t.find("li").removeData("included").removeClass("title-from-item");e&&(h=I(h,f,"tick"));h.length&&!p.length&&(f.contains_current||a&&1>f.filter_item)&&(h=g([]));h.length&&a&&(0<f.filter_item?r=h.filter("[data-itemid="+f.filter_item+"]"):f.filter_item?1===n&&f.fallback_no_ancestor?(r=p,m=m||f.fallback_include_parent,q=q||f.fallback_include_parent_siblings,u="cmw-fellback-to-current"):r=1===n?t:-1>f.filter_item?t.find(".current-menu-ancestor").eq(0).closest("li"):t.find(".current-menu-parent").closest("li"):
53
- p.find("li").length?r=p:f.fallback_no_children&&(r=t.find(".current-menu-parent").closest("li"),r.length||(r=t),m=m||f.fallback_nc_include_parent,q=q||f.fallback_nc_include_parent_siblings,u="cmw-fellback-to-parent"));if(h.length)if(d)for(v=f.depth_rel_current&&f.depth&&p.length&&n>=f.start_level?n+f.depth-1:f.depth?f.start_level+f.depth-1:9999,d=1;d<=k;d++){if(d<f.start_level||d>v)h=h.not(".level-"+d)}else r&&r.length?(v=f.depth_rel_current&&f.depth&&p.length&&r.has(p[0]).length?n-1+f.depth:f.depth?
54
- Math.max((r.data().level||0)+f.depth,f.start_level+f.depth-1):9999,h=r.find("li").filter(function(){var a=g(this).data().level;return a>=f.start_level&&a<=v})):a&&(h=g([]));h.length&&a&&r&&r.is("li")&&(q&&(h=h.add(r.siblings("li").data("included"," cmw-an-included-parent-sibling")),m=!0),f.include_ancestors&&(h=h.add(r.parentsUntil(t,"li").data("included"," cmw-an-included-ancestor")),m=!0),m&&(h=h.add(r.data("included"," cmw-the-included-parent"))));!h.length||!f.contains_current||p.length&&h.filter(p).length||
55
- (h=g([]));f.title_from_parent&&h.length&&r&&r.is("li")?r.addClass("title-from-item"):f.title_from_current&&h.length&&p.addClass("title-from-item");u=h.length?u:"";c.find(".cmw-demo-fallback").data("fellback",u).toggleClass("updated",!!u);c.find(".cmw-demo-setcurrent").toggleClass("error",!p.length&&(f.contains_current||a&&1>f.filter_item));t.toggleClass("cmw-demo-filteritems",e).find(".picked").not(h.addClass("picked")).removeClass("picked");J(c,b);O(c,f);c.find("code").text(this.shortcode(f))}}}};
56
- g(document).on("change",v("onchange",1),A).on("click",v("assist",1),function(b){b=g(this);b=y(0,b);var a=b.data(),c=g("#"+a.cmwDialogId);c.length||(c=V(a).data({cmwOnchange:"#"+b.attr("id"),cmwUntitled:"["+a.cmwDialogUntitled+"]"}));c.dialog("isOpen")?c.dialog("close"):(K(c),J(c,b),c.dialog("open"),A.call(b[0]));this.blur();return!1}).on("click",v("fieldset",1),function(b){b=g(this);var a=b.next(".cmw-fieldset-state"),c=!a.prop("checked");a.length&&(a.prop("checked",c),b.toggleClass("cmw-collapsed-fieldset",
57
- c),a.next("div")[c?"slideUp":"slideDown"]());this.blur();return!1}).on("click",".widget-action,.widget-control-close",function(b){b=g(this).closest("div.widget");var a=b.parent();a.hasClass("customize-control-widget_form")&&!a.hasClass("expanded")||y(b).each(function(){var a=g("#"+g(this).data().cmwDialogId);a.length&&a.dialog("isOpen")&&a.dialog("close")})}).on("click",".widget-control-remove",function(b){y(g(this).closest("div.widget")).each(function(){var a=g("#"+g(this).data().cmwDialogId);a.length&&
58
- (a.dialog("destroy"),a.remove())})}).on("click",v("colexp",1),function(b){b=/1-e/.test(this.className)?"slideUp":"slideDown";g(this).toggleClass("ui-icon-triangle-1-s ui-icon-triangle-1-e").prev("ul")[b]();return!1}).on("click",v("find-shortcodes",1),function(b){b=g(this);var a=b.parent().parent();ajaxurl&&!a.hasClass("cmw-ajax-fetching")&&(a.hasClass("cmw-ajax-showing")?a.removeClass("cmw-ajax-showing"):(a.addClass("cmw-ajax-fetching"),g.get(ajaxurl,{action:"cmw-find-shortcodes",_wpnonce:b.data().nonce}).done(function(b){b&&
 
 
 
59
  "0"!==b&&(a.find(".cmw-demo-found-shortcodes").html(g(b).find("response_data").text()),a.addClass("cmw-ajax-showing"))}).always(function(b){a.removeClass("cmw-ajax-fetching")})));this.blur();return!1}).on("click",v("legacy-close",1),function(){g(this).parent().remove();return!1});window.Custom_Menu_Wizard_Widget&&g(window.Custom_Menu_Wizard_Widget.trigger||[]).trigger("change")});
1
  /*Source: custom-menu-wizard.js
2
+ *Compiled: 2015-09-10, Google Closure Compiler...
3
  *STATISTICS
4
+ * - originalSize: 85715
5
+ * - originalGzipSize: 19918
6
+ * - compressedSize: 26591
7
+ * - compressedGzipSize: 8560
8
  */
9
+ jQuery(function(g){
10
+ 'use strict';
11
+ var x,L=/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)/g,v=function(b,a){return(a?".":"")+"widget-custom-menu-wizard-"+b},A=function(b){var a=g(this).data().cmwDialogVersion.replace(/\./g,""),a=/^\d+$/.test(a)?"v"+a:a;x[a]?x[a].update(b?b.target:this):x.update(b?b.target:this)},C=function(b,a){var c=!1===b,d=c?{}:b.data(),e={},k=!c&&"2.1.0"===d.cmwDialogVersion,f={items:1,exclude:1},m=g.extend({branch_start:1,
12
+ exclude_level:1,include_level:1},f);g.each(c?a:b.find(":input").serializeArray(),function(a,b){var h=b.name.replace(/.*\[([^\]]+)\]$/,"$1"),p=!m[h]&&/^-?\d+$/.test(b.value)?parseInt(b.value,10):b.value;e[h]=p;"hide_empty"===h?e[h]=c||!!d.cmwV36plus||p:f[h]&&(e["_"+h+"_sep"]=!p||/(^\d+\+?$|,)/.test(g.trim(p))?",":" ",p=g.map(p.split(/[,\s]+/),function(a){var b=!k&&/\+$/.test(a);a=a?parseInt(a,10):0;return isNaN(a)||1>a?null:b?a+"+":a}),e["_"+h]=p.join(e["_"+h+"_sep"]))});return e},E=function(b,a,c,
13
+ d){var e=d.switch_if;return d.switch_at===b&&("current"===e&&a||"no-current"===e&&!a||"no-output"===e&&!c)},Q=function(b,a){b=g.trim(b||"");for(var c={title:"",level:1,branch:0,items:"",depth:0,depth_rel_current:0,start_at:"",start_mode:"",allow_all_root:0,ancestors:0,ancestor_siblings:0,include_root:0,include_level:"",siblings:0,exclude:"",exclude_level:"",contains_current:"",fallback:"",flat_output:0,title_from:"",title_linked:0,ol_root:0,ol_sub:0},d=L.exec(b),e={},k=0,f;d;)k++,d[1]?e[d[1]]=d[2]:
14
+ d[3]?e[d[3]]=d[4]:d[5]?e[d[5]]=d[6]:k--,d=L.exec(b);if(k)for(f in e)c.hasOwnProperty(f)&&(c[f]=e[f]);f=!!c.items;d=!f&&!!c.branch;e=!f&&!d;f&&(c.filter="items");if(d){c.filter="branch";f=c.start_at.toString();c.branch_start=f;if("0"===f||"branch"===f)c.branch_start="";"root"===f&&(c.branch_start="1");"children"===f&&(c.branch_start="+1");"parent"===f&&(c.branch_start="-1");if("current"===c.branch||"current-item"===c.branch)c.branch=0;else if(!/^[+\-]?\d+$/.test(c.branch.toString()))if(c.branch=c.branch.toLowerCase(),
15
+ f=a.find("a.cmw-item").filter(function(){return g(this).text().toLowerCase()===c.branch}),f.length)c.branch=f.parent().data("itemid");else return!1}e&&(c.filter="",c.level=Math.max(1,parseInt(c.level,10)));c.start_at=null;c.include_level||"1"!==c.include_root||(c.include_level="1");c.include_root=null;if(d&&!c.branch&&c.fallback&&(e=c.fallback.toLowerCase().split(/[\s,]+/),f=" "+e.join(" ")+" ",c.fallback="",0<=f.indexOf(" quit ")?c.fallback="quit":0<=f.indexOf(" parent ")?c.fallback="parent":0<=
16
+ f.indexOf(" current ")&&(c.fallback="current"),""!==c.fallback&&"quit"!==c.fallback))for(0<=f.indexOf(" +siblings ")&&(c.fallback_siblings=1),k=0;k<e.length;k++)if(/^\d+$/.test(e[k])&&(f=parseInt(e[k],10),0<f)){c.fallback_depth=f;break}if(c.title_from)for(e=c.title_from.toLowerCase().split(/[\s,]+/),k=0;k<e.length;k++)if(e[k])if("branch"===e[k]||"current"===e[k])c["title_"+e[k]]="0";else if(f=e[k].match(/^(branch|current)(-root|-parent|[+\-]?\d+)$/))"-root"===f[2]?c["title_"+f[1]]=1:"-parent"===f[2]?
17
+ c["title_"+f[1]]=-1:(c["title_"+f[1]]=f[2],c["title_"+f[1]]||(c["title_"+f[1]]=""));c.title_from=null;c.hide_empty=1;return C(!1,g.map(c,function(a,b){return null===a?a:{name:b,value:a}}))},M=function(b){var a="";b&&(a=b.replace(/[\r\n\t]+/g," ").replace(/^[\[\s]+/,"").replace(/[\s\/\]]+$/,"").split(/[\[\]]/)[0],a=g.trim(a.replace(/\/+$/,""))+" ",a=g.trim((" "+a.replace(/^cmwizard\s/,"")+" ").replace(/\smenu=[^\s]*\s/," ").replace(/\salternative=("[^"]*"|[^\s]*)\s/," ")),a=a.replace(/\s\s+/g," "));
18
+ return a},F=function(b,a){var c=a.find(".cmw-demo-themenu-ul"),d=c.data(),e=M(b.switch_to),e="cmwizard menu="+b.menu+(""===e?e:" "+e);d.altCode&&d.altCode===e||(d.altCode=e,d.altSettings=Q(e,c));a.find(".cmw-demo-alternative").addClass("updated").toggleClass("error",!1===d.altSettings);return!1===d.altSettings?!1:g.extend({},d.altSettings)},I=function(b,a,c){var d="tick"===c?a._items_sep:a._exclude_sep,e=[],k="tick"===c?"_items":"_exclude",k=a[k]?g.grep(a[k].split(d),function(a){return/\+$/.test(a)?
19
+ (e.push(parseInt(a,10)),!a):!!a}):[],k=d+k.join(d)+d,e=d+e.join(d)+d;b=b.each(function(){var a=g(this),b=a.data(),q=-1<e.indexOf(d+b.itemid+d),b=q||-1<k.indexOf(d+b.itemid+d);a.toggleClass("cmw-has-"+c,b).toggleClass("cmw-inherit-"+c,q)});return b.filter(".cmw-inherit-"+c).find("li").removeClass("cmw-has-"+c+" cmw-inherit-"+c).add(b.filter(".cmw-has-"+c))},y=function(b,a){return b[a?"find":"closest"](v("onchange",1))},N=function(b,a){var c=[],d=b.match(/^(\d+)(\+|-)?$/),e,d=d?[parseInt(d[1],10),d[2]||
20
+ ""]:[];if(0<d[0])for(e=1;e<=a;e++)(e===d[0]||"-"===d[1]&&e<d[0]||"+"===d[1]&&e>d[0])&&c.push(".level-"+e);return c.join(",")},O=function(b,a){var c=b.find(".cmw-demo-themenu-ul"),d=c.find(".picked"),e="",k=a.hide_title?"":a.title,f=0,m=0,q=b.find(".cmw-demo-theoutput-wrap").empty(),r=["menu-widget"],h={};if(d.length&&q.length){k=c.find(".title-from-item").children(".cmw-item").text()||"";f=!!k&&!!a.title_linked;k||a.hide_title||(k=a.title||"");for(d.each(function(b){var c=g(this),d=c.data(),f=d.trace?
21
+ d.trace.toString().split(","):[];b=d.itemid.toString();var k=1,c=c.children(".cmw-item");if(!a.flat_output)for(h[b]=1,b=0;b<f.length;b++)h[f[b]]&&k++;if(m)if(k>m)e+=a.ol_sub?"<ol>":"<ul>";else{for(;m>k;)--m,e+="</li>"+(a.ol_sub?"</ol>":"</ul>");e+="</li>"}e+='<li class="cmw-level-'+k+(d.included||"")+'"><a href="#'+c.data("indx")+'">'+c.text()+"</a>";m=k});1<m;)--m,e+="</li>"+(a.ol_sub?"</ol>":"</ul>");e+="</li>";r.push(b.find(".cmw-demo-fallback").data("fellback"));e=(a.ol_root?"<ol":"<ul")+' class="'+
22
+ g.trim(r.join(" "))+'">'+e+(a.ol_root?"</ol>":"</ul>");q.html(e);q.find("li").filter(function(){return!!g(this).children("ul, ol").length}).addClass("cmw-has-submenu")}q.length&&k&&(d.length||!a.hide_empty)&&q.prepend((f?'<h3 class="cmw-title-linked">':"<h3>")+k+"</h3>")},P=function(b,a,c){var d;b.find("optgroup").filter(function(){var a=g(this).data().cmwOptgroupIndex===c;a||g(this).remove();return a}).length||(d=g("#"+b.attr("id")+"_ignore").find("optgroup").eq(c).clone(),d.length&&(0<a&&(a=0),
23
+ b.append(d).val(a)));return a},R=function(b){b=g(this);var a=b.data(),c=!a.cmwAbsolute,d=b.closest(".ui-dialog"),e=d.find(".ui-dialog-content"),k=parseInt(d.css("top"),10)+(c?1:-1)*g(document).scrollTop();a.cmwAbsolute=c;a.cmwMaxHeight||(a.cmwMaxHeight=e.dialog("option","maxHeight"));b.button("option","icons",{primary:c?"ui-icon-circle-close":"ui-icon-circle-check"});d.toggleClass("cmw-assistance-dialog-fixed",!c);e.dialog("option",{maxHeight:c?!c:a.cmwMaxHeight});d.css("top",k);return!1},S=function(b){var a=
24
+ g(this);b=["current-menu-item","current-menu-parent","current-menu-ancestor"];var c=a.closest(".ui-dialog-content"),d=c.find(".cmw-demo-themenu-ul"),a=a.find("span").not("."+b[0]).parentsUntil(d,"li"),e,k=function(){this.title=this.title+" "+e.replace(" "," & ").replace(/-/g," ")};d.find("."+b.join(",.")).removeClass(b.join(" ")).each(function(){this.title=this.title.replace(/\s.*$/,"")});for(d=0;d<a.length;d++)e=1===d?b.join(" "):b[0],a.eq(d).children(".cmw-item").find("span").addClass(e).each(k),
25
+ 1<b.length&&b.shift();A.call(g(c.data().cmwOnchange).get(0));return!1},T=function(){g(this).closest(".ui-dialog-content").find(".cmw-item").eq(this.href.split("#")[1]).not(":has(.current-menu-item)").trigger("click");this.blur();return!1},U=function(b){b=g(this);var a=b.hasClass("cmw-tick")?"tick":"cross",c=b.parent();b=c.closest(".cmw-demo-themenu-ul");var d=c.hasClass("cmw-inherit-"+a),e=d||c.hasClass("cmw-has-"+a),k=e?g([]):c.parentsUntil(b,".cmw-inherit-"+a),f=g(c.closest(".ui-dialog-content").data().cmwOnchange).find("tick"===
26
+ a?".cmw-setitems":".cmw-exclusions"),m;!b.hasClass("cmw-using-alternative")&&f.length&&(m=b.find(".cmw-has-"+a)[d||k.length?"not":"add"](c),!e||c.children("ul").length&&!b.parent().hasClass("cmw-version-210")?e&&!d&&(m=m.not(c.find(".cmw-has-"+a))):m=m.not(c),m=m.add(k.find("li").not(c)),m=m.map(function(){var b=this===c[0]||this===k.get(0)?e:g(this).hasClass("cmw-inherit-"+a);return g(this).data().itemid+(b?"+":"")}).get().join(/(,|^\d+\+?$)/.test(g.trim(f.val())||",")?",":" "),f.val(m).trigger("change"));
27
+ this.blur();return!1},V=function(b){var a={autoOpen:!1,width:Math.min(.9*g(window).width(),600),maxHeight:g(window).height()-40,modal:!1,containment:"window",create:function(){var a=g(this).closest(".ui-dialog");if(a.hasClass("cmw-assistance-dialog-fixed"))g("<button/>").addClass("cmw-dialog-fixed-absolute").button({label:b.cmwDialogFixed,icons:{primary:"ui-icon-circle-check"}}).appendTo(a.find(".ui-dialog-titlebar")).on("click",R)},dialogClass:"cmw-assistance-dialog cmw-assistance-dialog-fixed"},
28
+ c=g.map(["SetCurrent","Inclusions","Exclusions","Fallback","Alternative"],function(a){return'<div class="cmw-demo-'+a.toLowerCase()+' cmw-demo-small">'+(b["cmwDialog"+a]||"")+"</div>"}),c=g("<div/>",{id:b.cmwDialogId}).addClass(v("dialog")).append(g("<div/>").addClass("cmw-demo-themenu cmw-version-"+b.cmwDialogVersion.replace(/\./g,"")).html('<em class="cmw-demo-small">'+b.cmwDialogPrompt+"</em>")).append(g("<div/>").addClass("cmw-demo-theoutput").html('<em class="cmw-demo-small">'+b.cmwDialogOutput+
29
+ '</em><em class="cmw-demo-plugin-version cmw-demo-small">v'+b.cmwDialogVersion+"</em>"+c.shift()+'<div class="cmw-demo-theoutput-wrap ui-corner-all"></div>'+c.join(""))).append(g("<div/>").addClass("cmw-demo-theshortcode").html('<code class="ui-corner-all"></code><div class="cmw-find-shortcodes"><a href="#" class="button-secondary '+v("find-shortcodes")+'" data-nonce="'+(b.cmwDialogNonce||"")+'" title="'+b.cmwDialogShortcodes+'"><span class="spinner"></span><span>[&hellip;]</span></a></div><div class="cmw-demo-found-shortcodes cmw-demo-small ui-corner-all"></div>'));
30
+ c.dialog(a);c.find(".cmw-demo-themenu").on("click",".cmw-tick,.cmw-cross",U).on("click",".cmw-item",S);c.find(".cmw-demo-theoutput").on("click","a",T);return c},J=function(b,a){var c=a.find(".cmw-widget-title").val()||b.data().cmwUntitled;b.dialog("option","title","CMW : "+c+" ["+a.find(".cmw-select-menu").find("option:selected").text()+"]")},K=function(b){var a=g(b.data().cmwOnchange),c=parseInt(a.find(".cmw-select-menu").val(),10),d=b.find(".cmw-demo-themenu-ul"),e=[],k=0,f="",m=function(a){return 1<
31
+ a?'<a href="#" class="'+v("colexp")+' ui-icon ui-icon-triangle-1-e" style="left:-'+(2.4*(a-2)+2)+'em;">&nbsp;</a>':""};if(!d.length||d.data("menuid")!==c){for(a.find(".cmw-assist-items optgroup").find("option").each(function(a){for(var b=g(this),c=b.data().cmwLevel;c<e.length;)f+="</li></ul>"+m(e.length),e.pop();c>e.length?f+="<ul>":(f+="</li>",e.pop());f+='<li class="level-'+c+'" data-itemid="'+this.value+'" data-level="'+c+'" data-trace="'+e.join(",")+'">';f+='<a href="#" class="cmw-cross ui-corner-all"></a>';
32
+ f+='<a class="cmw-item ui-corner-all" href="#" data-indx="'+a+'"><span class="ui-corner-all" title="#'+this.value+'">'+g.trim(b.text());f+='</span></a><a href="#" class="cmw-tick ui-corner-all"></a>';e.push(this.value);c>k&&(k=c)});e.length;)f+="</li></ul>"+m(e.length),e.pop();d.remove();b.find(".cmw-demo-themenu").append(g(f).addClass("cmw-demo-themenu-ul").data({maxLevel:k,menuid:c}))}};x={setLevels:function(b,a){var c=b.find(".cmw-branch-start"),d=c.val();a&&(c.find("optgroup").each(function(b){var c=
33
+ g(this),d=c.find("option"),m=d.length,q=c.data();if(b)for(d.slice(a).remove();m<a;)++m,c.append(g("<option/>",{value:m}).text(m));else for(m=(m+1)/2,m>a&&d.each(function(b,c){Math.abs(c.value)>=a&&g(c).remove()});m<a;)c.prepend(g("<option/>",{value:-m}).text(-m+(1<m?"":" ("+q.cmwTextParent+")"))).append(g("<option/>",{value:"+"+m}).text("+"+m+(1<m?"":" ("+q.cmwTextChildren+")"))),++m}),/^\d+$/.test(d)?d>a&&c.val("1"):""!==d&&Math.abs(d)>=a&&c.val(""),b.find(".cmw-set-rel-abs-levels").each(function(){var b=
34
+ g(this),c=b.find("optgroup"),d=c.find("option").length/2+1,m=b.val(),q=Math.max(2,a);Math.abs(m)>=a&&b.val(0>m?1-a:a-1);d!==q&&c.each(function(a,b){var c=g(b),e=c.data().cmwTextForOption,k;d>q&&c.find("option").slice(q-d).remove();for(k=d;k<q;k++)c.append(g("<option/>",{value:a?k:-k}).text(e.replace("%d",a?k:-k)))})}),b.find(".cmw-include-level,.cmw-exclude-level").each(function(){var b=g(this),c=b.find("option"),d=(c.length-1)/3,m=b.val(),q=c.eq(2).text(),r=c.eq(3).text();for(c.slice(3*a+1).remove();d<
35
+ a;)++d,b.append(g("<option/>",{value:d}).text(d)).append(g("<option/>",{value:d+"-"}).text(q.replace(/\d+/,d))).append(g("<option/>",{value:d+"+"}).text(r.replace(/\d+/,d)));parseInt(m,10)>a&&b.val("")}),b.find(".cmw-set-levels").each(function(){var b=g(this),c=b.data(),d=c.cmwTextLevels||"",c=c.cmwSetLevels||0,m=b.find("option"),q=m.length-c;b.val()>a&&b.val(m.eq(0).val());for(b.find("option").slice(c+a).remove();q<a;)++q,b.append(g("<option/>",{value:q}).text(q+d))}))},setFields:function(b,a){var c=
36
+ a.find(".cmw-bybranch"),d=a.find(".cmw-byitems").prop("checked"),e=d||!c.prop("checked"),k=a.find(".cmw-assist-items"),f=parseInt(k.val(),10);b.hasClass("cmw-select-menu")?(f=P(k,f,b[0].selectedIndex),this.setLevels(a,(k.find("optgroup").data()||{}).cmwMaxLevel)):b.hasClass("cmw-level")?(e=!0,d=!e,a.find(".cmw-bylevel").prop("checked",e)):b.is(k)?(e=d=!1,c.prop("checked",!e)):b.hasClass("cmw-setitems")?(e=d=!0,a.find(".cmw-byitems").prop("checked",d)):b.hasClass("cmw-ancestors")&&"0"===b.val()?a.find(".cmw-ancestor-siblings").val("0"):
37
+ b.hasClass("cmw-ancestor-siblings")&&"0"!==b.val()&&"0"===a.find(".cmw-ancestors").val()&&a.find(".cmw-ancestors").val(b.val());c=a.find(".cmw-fallback").val();g.each({"-ss":d,"-ud":d||1>a.find(".cmw-depth").val(),"not-br":e,"not-br-ci":e||!!f,"not-fb-pc":e||!!f||"parent"!==c&&"current"!==c,"not-sw":!!a.find(".cmw-switchable").filter(function(){return!g(this).val()}).length},function(b,c){a.find(".cmw-disableif"+b).toggleClass("cmw-colour-grey",c)})},shortcode:function(b){var a={menu:b.menu},c="branch"===
38
+ b.filter,d="items"===b.filter,e,k;b.title&&!b.hide_title&&(a.title=[b.title]);!c&&!d&&1<b.level&&(a.level=b.level);c&&(a.branch=b.branch||"current",b.branch_start&&(a.start_at=[b.branch_start]),"level"===b.start_mode&&(a.start_mode=["level"]),b.allow_all_root&&(a.allow_all_root=1));d?a.items=[b._items]:(0<b.depth&&(a.depth=b.depth),b.depth_rel_current&&0<b.depth&&(a.depth_rel_current=1));c&&!b.branch&&b.fallback&&(a.fallback=[b.fallback],"quit"!==b.fallback&&(b.fallback_siblings&&a.fallback.push("+siblings"),
39
+ b.fallback_depth&&a.fallback.push(b.fallback_depth)));c&&b.ancestors&&(a.ancestors=b.ancestors,b.ancestor_siblings&&(a.ancestor_siblings=b.ancestor_siblings));b.include_level&&(a.include_level=[b.include_level]);b._exclude&&(a.exclude=[b._exclude]);b.exclude_level&&(a.exclude_level=[b.exclude_level]);d=[];""!==b.title_current&&d.push("current"+(b.title_current?b.title_current:""));c&&""!==b.title_branch&&d.push("branch"+(b.title_branch?b.title_branch:""));d.length&&(a.title_from=d,b.title_linked&&
40
+ (a.title_linked=1));for(d in{siblings:1,flat_output:1,ol_root:1,ol_sub:1,fallback_ci_parent:1,fallback_ci_lifo:1})b[d]&&(a[d]=1);c={contains_current:"",container:"div",container_id:"",container_class:"",menu_class:"menu-widget",widget_class:""};for(d in c)b[d]!==c[d]&&(a[d]=[b[d]]);c={wrap_link:"before",wrap_link_text:"link_before"};for(d in c)(e=b[c[d]].toString().match(/^<(\w+)/))&&e[1]&&(a[d]=[e[1]]);b.switch_if&&b.switch_at&&(a.alternative=[b.switch_if,b.switch_at],k=M(b.switch_to));c=[];for(d in a)c.push(g.isArray(a[d])?
41
+ d+'="'+a[d].join(",")+'"':d+"="+a[d]);return"[cmwizard "+c.join(" ")+(k?"]"+k+"[/cmwizard]":"/]")},structureUpdate:function(b,a,c){var d=-1,e=9999,k=0,f=0,m="",q=null,r,h,p,n,u="menu",t="branch"===a.filter,v="items"===a.filter,y=!t&&!v,x=t&&!a.branch,G=!c&&!!a.switch_if&&!!a.switch_at,B=b.find(".cmw-demo-themenu-ul"),z=B.data().maxLevel,w=B.find(".current-menu-item").closest("li"),D=w.length?w.data().level:-1,l=B.find("li").removeData("included").removeClass("title-from-item"),H=a.depth,A=a.depth_rel_current,
42
+ C=I(l,a,"cross");h=l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(G&&E(u,h,l.length,a)&&(q=F(a,b),!1!==q))return q;u="primary";v&&l.length&&(l=I(l,a,"tick"));t&&l.length&&(p=x?w:l.filter("[data-itemid="+a.branch+"]"),p.length?(d=p.data().level||0,l=p.add(p.find("li")),r=p):l=g([]));if(y&&l.length&&1<a.level){h=1;for(n=[];h<a.level;h++)n.push(".level-"+h);l=l.not(n.join(","))}h=l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(G&&E(u,h,l.length,a)&&(q=F(a,b),!1!==q))return q;u="secondary";
43
+ if(y&&l.length&&H&&(h=A&&D>=a.level?D:a.level,h+=H,h<=z)){for(n=[];h<=z;h++)n.push(".level-"+h);l=l.not(n.join(","))}if(t&&l.length&&(n=parseInt(a.branch_start,10),n=isNaN(n)||!n?d:a.branch_start.match(/^(\+|-)/)?Math.max(1,d+n):n,x&&a.fallback&&!w.find("li").length&&(m="cmw-fellback-to-"+a.fallback,"quit"===a.fallback?n=z+1:(n="current"===a.fallback||2>d?d:d-1,a.fallback_depth&&(H=a.fallback_depth,A=1))),n>z?l=g([]):(n<d&&(p=p.parentsUntil(B,"li.level-"+n)),"level"===a.start_mode&&n<=d&&(1<n||a.allow_all_root)?
44
+ l=p.parent().find("li"):n<d&&(l=p.add(p.find("li"))),m&&a.fallback_siblings&&l.length&&(1<n||a.allow_all_root)&&(l=l.add(p.siblings("li.level-"+n)))),l.length)){d=n;p=9999;H&&(p=A&&D>=d&&l.filter(w).length?D:d,p+=H,e=p-1);h=1;for(n=[];h<=z;h++)h>=d&&h<p&&n.push(".level-"+h);l=l.filter(n.join(","))}h=l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(G&&E(u,h,l.length,a)&&(q=F(a,b),!1!==q))return q;u="inclusions";if(t&&l.length){n=r.data().level;if(a.ancestors&&(r.is(l)||n>e)){h=a.ancestors;
45
+ 0>h&&(h=Math.max(1,n+h));d=a.ancestor_siblings;0>d&&(d=Math.max(1,n+d));n=[];for(p=[];h<=z;h++)h<=e&&(n.push(".level-"+h),0<d&&h>=d&&p.push(".level-"+h));h=l.length;n=r.parentsUntil(B,n.join(","));l=l.add(n.not(l).data("included"," cmw-an-included-ancestor"));p.length&&(l=l.add(n.filter(p.join(",")).siblings("li").not(l).data("included"," cmw-an-included-ancestor-sibling")));k+=l.length-h}a.siblings&&r.is(l)&&(n=l.length,l=l.add(r.siblings("li").data("included"," cmw-an-included-sibling")),k+=l.length-
46
+ n)}l.length&&a.include_level&&(p=N(a.include_level,z))&&(n=l.length,l=l.add(B.find(p)),k+=l.length-n);h=l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(G&&E(u,h,l.length,a)&&(q=F(a,b),!1!==q))return q;u="output";l.length&&C.length&&(n=l.length,l=l.not(C),f+=n-l.length);l.length&&a.exclude_level&&(p=N(a.exclude_level,z))&&(n=l.length,l=l.not(p),f+=n-l.length);h=l.length&&w.is(l);a.contains_current!==u||h||(l=g([]));if(G&&E(u,h,l.length,a)&&(q=F(a,b),!1!==q))return q;""!==a.title_current&&
47
+ w.length?0===a.title_current?w.addClass("title-from-item"):(h=0<a.title_current?Math.min(D,a.title_current):Math.max(1,D+a.title_current),w.closest(".level-"+h).addClass("title-from-item")):t&&r&&""!==a.title_branch&&(0===a.title_branch?r.addClass("title-from-item"):(h=r.data().level,h=0<a.title_branch?Math.min(h,a.title_branch):Math.max(1,h+a.title_branch),r.closest(".level-"+h).addClass("title-from-item")));b.find(".cmw-demo-fallback").data("fellback",m).toggleClass("updated",!!m);b.find(".cmw-demo-setcurrent").toggleClass("error",
48
+ !w.length&&(!!a.contains_current||x));c||null!==q||b.find(".cmw-demo-alternative").removeClass("error updated");b.find(".cmw-demo-themenu-ul").toggleClass("cmw-using-alternative",!!c);h={inclusions:k,exclusions:f};for(n in h)p=b.find(".cmw-demo-"+n),p.text(p.text().replace(/\d+$/,h[n])).toggleClass("updated",0<h[n]);B.toggleClass("cmw-demo-filteritems",v).find(".picked").not(l.addClass("picked")).removeClass("picked");return!1},update:function(b){var a=g(b);b=y(a);var c=g("#"+b.data().cmwDialogId),
49
+ d;a.hasClass("cmw-listen")&&this.setFields(a,b);d=C(b);c.length&&c.dialog("isOpen")&&(c.dialog("moveToTop"),a.hasClass("cmw-select-menu")&&K(c),a=this.structureUpdate(c,d),!1!==a&&this.structureUpdate(c,a,!0),J(c,b),O(c,a||d));b.add(c).find("code").not(".cmw-instance-shortcode").text(this.shortcode(d))},v210:{setLevels:function(b,a){var c=b.find(".cmw-start-level"),d=c.val(),e=c.find("option").length,k;d>a&&c.val(1);for(c.find("option").slice(a).remove();e<a;)++e,c.append(g("<option/>",{value:e}).text(e));
50
+ c=b.find(".cmw-depth");d=c.val();e=c.find("option").length;k=c.data().cmwTextLevels;d>a&&c.val(0);for(c.find("option").slice(a+1).remove();e<=a;)c.append(g("<option/>",{value:e}).text(e+k)),++e},setFields:function(b,a){var c=a.find(".cmw-showall").prop("checked"),d=a.find(".cmw-showspecific").prop("checked"),e=a.find(".cmw-assist-items"),k=parseInt(e.val(),10);b.hasClass("cmw-select-menu")&&(k=P(e,k,b[0].selectedIndex),this.setLevels(a,(e.find("optgroup").data()||{}).cmwMaxLevel));g.each({"":c||d,
51
+ "-ss":d,"not-rp":c||d||0<=k,"not-ci":c||d||!!k},function(b,c){a.find(".cmw-disableif"+b).toggleClass("cmw-colour-grey",c).find("input,select").prop("disabled",c)})},shortcode:function(b){var a={menu:b.menu},c=0<b.filter,d=!!b.filter&&!c,e;b.title&&(a.title=[b.title]);if(c)switch(b.filter_item){case 0:a.children_of=["current"];break;case -1:a.children_of=["parent"];break;case -2:a.children_of=["root"];break;default:a.children_of=b.filter_item}d&&(a.items=[b._items]);c&&0>b.filter_item&&b.fallback_no_ancestor&&
52
+ (a.fallback_parent=b.fallback_include_parent_siblings?["siblings"]:b.fallback_include_parent?["parent"]:1);c&&!b.filter_item&&b.fallback_no_children&&(a.fallback_current=b.fallback_nc_include_parent_siblings?["siblings"]:b.fallback_nc_include_parent?["parent"]:1);1<b.start_level&&(a.start_level=b.start_level);0<b.depth&&(a.depth=b.depth);b.depth_rel_current&&0<b.depth&&(a.depth_rel_current=1);d=[];c&&(b.include_parent_siblings?d.push("siblings"):b.include_parent&&d.push("parent"),b.include_ancestors&&
53
+ d.push("ancestors"),d.length&&(a.include=d));d=[];c&&b.title_from_parent&&d.push("parent");b.title_from_current&&d.push("current");d.length&&(a.title_from=d);for(d in{flat_output:1,contains_current:1,ol_root:1,ol_sub:1})b[d]&&(a[d]=1);c={container:"div",container_id:"",container_class:"",menu_class:"menu-widget",widget_class:""};for(d in c)b[d]!==c[d]&&(a[d]=[b[d]]);c={wrap_link:"before",wrap_link_text:"link_before"};for(d in c)(e=b[c[d]].toString().match(/^<(\w+)/))&&e[1]&&(a[d]=[e[1]]);c=[];for(d in a)c.push(g.isArray(a[d])?
54
+ d+'="'+a[d].join(" ")+'"':d+"="+a[d]);return"[custom_menu_wizard "+c.join(" ")+"]"},update:function(b){var a=g(b);b=y(a);var c=g("#"+b.data().cmwDialogId),d,e,k,f,m,q,r,h,p,n,u,t,v;a.hasClass("cmw-listen")&&this.setFields(a,b);if(c.length&&c.dialog("isOpen")){c.dialog("moveToTop");a.hasClass("cmw-select-menu")&&K(c);f=C(b);d=!f.filter;a=0<f.filter;e=!d&&!a;m=f.include_parent;q=f.include_parent_siblings;r=c.find(".cmw-demo-themenu-ul");k=r.data().maxLevel;p=r.find(".current-menu-item").closest("li");
55
+ n=p.length?p.data().level:-1;h=r.find("li").removeData("included").removeClass("title-from-item");e&&(h=I(h,f,"tick"));h.length&&!p.length&&(f.contains_current||a&&1>f.filter_item)&&(h=g([]));h.length&&a&&(0<f.filter_item?t=h.filter("[data-itemid="+f.filter_item+"]"):f.filter_item?1===n&&f.fallback_no_ancestor?(t=p,m=m||f.fallback_include_parent,q=q||f.fallback_include_parent_siblings,u="cmw-fellback-to-current"):t=1===n?r:-1>f.filter_item?r.find(".current-menu-ancestor").eq(0).closest("li"):r.find(".current-menu-parent").closest("li"):
56
+ p.find("li").length?t=p:f.fallback_no_children&&(t=r.find(".current-menu-parent").closest("li"),t.length||(t=r),m=m||f.fallback_nc_include_parent,q=q||f.fallback_nc_include_parent_siblings,u="cmw-fellback-to-parent"));if(h.length)if(d)for(v=f.depth_rel_current&&f.depth&&p.length&&n>=f.start_level?n+f.depth-1:f.depth?f.start_level+f.depth-1:9999,d=1;d<=k;d++){if(d<f.start_level||d>v)h=h.not(".level-"+d)}else t&&t.length?(v=f.depth_rel_current&&f.depth&&p.length&&t.has(p[0]).length?n-1+f.depth:f.depth?
57
+ Math.max((t.data().level||0)+f.depth,f.start_level+f.depth-1):9999,h=t.find("li").filter(function(){var a=g(this).data().level;return a>=f.start_level&&a<=v})):a&&(h=g([]));h.length&&a&&t&&t.is("li")&&(q&&(h=h.add(t.siblings("li").data("included"," cmw-an-included-parent-sibling")),m=!0),f.include_ancestors&&(h=h.add(t.parentsUntil(r,"li").data("included"," cmw-an-included-ancestor")),m=!0),m&&(h=h.add(t.data("included"," cmw-the-included-parent"))));!h.length||!f.contains_current||p.length&&h.filter(p).length||
58
+ (h=g([]));f.title_from_parent&&h.length&&t&&t.is("li")?t.addClass("title-from-item"):f.title_from_current&&h.length&&p.addClass("title-from-item");u=h.length?u:"";c.find(".cmw-demo-fallback").data("fellback",u).toggleClass("updated",!!u);c.find(".cmw-demo-setcurrent").toggleClass("error",!p.length&&(f.contains_current||a&&1>f.filter_item));r.toggleClass("cmw-demo-filteritems",e).find(".picked").not(h.addClass("picked")).removeClass("picked");J(c,b);O(c,f);c.find("code").text(this.shortcode(f))}}}};
59
+ g(document).on("change",v("onchange",1),A).on("click",v("assist",1),function(b){b=g(this);b=y(b);var a=b.data(),c=g("#"+a.cmwDialogId);c.length||(c=V(a).data({cmwOnchange:"#"+b.attr("id"),cmwUntitled:"["+a.cmwDialogUntitled+"]"}));c.dialog("isOpen")?c.dialog("close"):(K(c),J(c,b),c.dialog("open"),A.call(b[0]));this.blur();return!1}).on("click",v("fieldset",1),function(b){b=g(this);var a=b.next(".cmw-fieldset-state"),c=!a.prop("checked");a.length&&(a.prop("checked",c),b.toggleClass("cmw-collapsed-fieldset",
60
+ c),a.next("div")[c?"slideUp":"slideDown"]());this.blur();return!1}).on("click",".widget-action,.widget-control-close",function(b){b=g(this).closest("div.widget");var a=b.parent();a.hasClass("customize-control-widget_form")&&!a.hasClass("expanded")||y(b,1).each(function(){var a=g("#"+g(this).data().cmwDialogId);a.length&&a.dialog("isOpen")&&a.dialog("close")})}).on("click",".widget-control-remove",function(b){b=g(this).closest("div.widget");y(b,1).each(function(){var a=g("#"+g(this).data().cmwDialogId);
61
+ a.length&&(a.dialog("destroy"),a.remove())})}).on("click",v("colexp",1),function(b){b=/1-e/.test(this.className)?"slideUp":"slideDown";g(this).toggleClass("ui-icon-triangle-1-s ui-icon-triangle-1-e").prev("ul")[b]();return!1}).on("click",v("find-shortcodes",1),function(b){b=g(this);var a=b.parent().parent();ajaxurl&&!a.hasClass("cmw-ajax-fetching")&&(a.hasClass("cmw-ajax-showing")?a.removeClass("cmw-ajax-showing"):(a.addClass("cmw-ajax-fetching"),g.get(ajaxurl,{action:"cmw-find-shortcodes",_wpnonce:b.data().nonce}).done(function(b){b&&
62
  "0"!==b&&(a.find(".cmw-demo-found-shortcodes").html(g(b).find("response_data").text()),a.addClass("cmw-ajax-showing"))}).always(function(b){a.removeClass("cmw-ajax-fetching")})));this.blur();return!1}).on("click",v("legacy-close",1),function(){g(this).parent().remove();return!1});window.Custom_Menu_Wizard_Widget&&g(window.Custom_Menu_Wizard_Widget.trigger||[]).trigger("change")});
custom-menu-wizard.php CHANGED
@@ -3,61 +3,66 @@
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: 3.1.4
7
  * Author: Roger Barrett
8
  * Author URI: http://www.wizzud.com/
9
  * License: GPL2+
10
  */
11
  defined( 'ABSPATH' ) or exit();
12
  /*
 
 
 
 
 
13
  * v3.1.4 change log
14
  * - fixed bug : in the shortcode format, the Alternative wasn't actually being used (always ran the default)! thanks corrideat
15
  * - fixed bug : prevent texturization of the shortcode's content (the Alternative, when supplied)
16
  * - added the ability to make the title a link if it is set from a menu item (using Set Title from)
17
- *
18
  * v3.1.3 change log
19
  * - tweak : css tweak for the assist when in customizer, for WordPress 4.1
20
- *
21
  * v3.1.2 change log
22
  * - modification of the readme to avoid WordPress truncating documentation under Other Notes
23
  * No code changes.
24
- *
25
  * v3.1.1 change log
26
  * - fixed bug : only show the allow_all_root setting in the shortcode equivalent if the primary filter is by branch
27
  * - added work-around for occasions when a theme causes de-registration of the widget which prevents the shortcode working in content
28
- *
29
  * v3.1.0 change log
30
  * - added an Alternative section which takes a cmwizard shortcode and conditionally applies it as an entirely new widget configuration
31
  * - added fallback determination (has to be enabled) for no current item found as using items marked as current_item_parent (first found)
32
  * - fixed bug in determination of home page pagination pages (?paged=2, etc) as home page still being current item
33
  * - fixed bug introduced in v3.0.4 that prevented CMW script loading on the customizer page - when the Widget Customizer plugin is loaded - for WordPress v3.8 and below
34
  * - fixed bug : stop disabling selected fields based on other settings, because this caused the customizer to wipe values that may have been still required
35
- *
36
  * v3.0.4 change log
37
  * - fixed bug in the display of the "No Current Item!" warning in the "assist"
38
  * - corrected the enabling/disabling of a couple of fields in the widget form, and tweaked the indentation for better responsiveness
39
  * - fixed a bug with accessibility mode when javascript is enabled, and added a warning about the accuracy of the shortcode when javascript is disabled
40
  * - extended the All Root Items inclusion to be a selectable number of levels (as per the Exclusions by Level)
41
- *
42
  * v3.0.3 change log
43
  * - removed all occurrences of "Plugin " followed by "Name" from everywhere except the main plugin file (this one!) to avoid update() incorrectly reporting "invalid header" when activating straight from installation (rather than from the Plugin admin page)
44
  * - tweak : eliminate the over-use of get_title() when determining the widget title
45
  * - tweak : added self-terminating forward slash to generated shortcodes
46
  * - prepare for WPv4 (avoid deprecated functions)
47
- *
48
  * v3.0.2 change log
49
  * - fixed bug where the shortcode shown on new instances didn't initially reflect the automatically selected menu
50
- *
51
  * v3.0.1 change log
52
  * - fixed bug in determination of pre-existing legacy widgets versus brand new widget instances
53
  * - replaced widget property _cmw_allow_legacy_update with a filter, custom_menu_wizard_prevent_legacy_updates : return TRUE to prevent updates of legacy widgets
54
  * - added new filter, custom_menu_wizard_wipe_on_update : return TRUE to cleanse an instance of old settings
55
- *
56
  * v3.0.0 change log
57
- * - Major rethink/rewrite : the Children Of filter is now a Branch filter, and the selected menu item becomes the key focus point rather
58
  * than its children. The Levels available for a Branch filter now include relative levels as well as absolute levels, and there are more
59
  * options available for requiring that the menu contains the current menu item. With the exception of some anomalies (edge cases) the
60
- * output achievable with v2 of the widget is still available with v3. Although there is no automatice upgrade available, v2 is still
61
  * fully supported; however, any new instances of the widget will be created as v3 only. Note that the shortcode for v3 has changed to
62
  * [cmwizard ... ], but, again, v2's [custom_menu_wizard] is still supported! NB: There is no separate 2.1.0 release - it is incorporated
63
  * into this release.
@@ -70,7 +75,7 @@ defined( 'ABSPATH' ) or exit();
70
  * - compatibile with Widget Customizer plugin, and its implementation in WP 3.9 core
71
  * - added title_tag to shortcode options
72
  * - added findme to shortcode options, [cmwizard findme=1], output restricted to edit_pages capability
73
- *
74
  * v2.1.0 change log
75
  * - fixed bug where duplicate menu item ids were causing elements to be ignored
76
  * - fixed IE8 bug with levels indentation in assist
@@ -80,58 +85,58 @@ defined( 'ABSPATH' ) or exit();
80
  * - added collapsible menu structures to dialog, and set fixed position (with toggle back to absolute)
81
  * - added utility to "assist" to locate posts containing a CMW shortcode
82
  * - minimum requirement for WP raised to v3.6
83
- *
84
  * v2.0.6 change log:
85
  * - modified determination of current item to cope better with multiple occurences (still first-found, but within prioritised groups)
86
  * - replaced display of update information on plugins list with styled request (and link) to read changelog (update info sometimes didn't display, and some considered it "scary" for users)
87
- *
88
  * v2.0.5 change log:
89
  * - prevent PHP warnings of Undefined index/offset when building $substructure
90
- *
91
  * v2.0.4 change log:
92
  * - fixed bug where clearing the container field failed to remove the container from the output
93
  * - remove WordPress's menu-item-has-children class (WP v3.7+) when the filtered item no longer has children
94
  * - added automatic selection of the shortcode text when it is clicked
95
  * - tweaked admin styling and javascript for WordPress v3.8
96
- *
97
  * v2.0.3 change log:
98
  * - fixed bug with missing global when enqueuing scripts and styles for admin page
99
- *
100
  * v2.0.2 change log:
101
  * - fixed bug where Include Ancestors was not automatically including the Parent
102
  * - fixed bug where the "assist" was incorrectly calculating Depth Relative to Current Item when the current menu item was outside the scope of the Filtered items
103
  * - behaviour change : only recognise the first "current" item found (used to allow subsequent "current" items to override any already encountered)
104
- *
105
  * v2.0.1 change log:
106
  * - fixed bug that set a specific items filter when it shouldn't have been set, and prevented show-all working
107
- *
108
  * v2.0.0 change log:
109
  * - 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,
110
  * 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
111
  * be output, starting with the grand-kids (at level 4)
112
- * - 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
113
  * 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
114
  * backward compatibility.
115
  * - added option for calculating depth relative to current menu item
116
  * - added option allowing list output to be dependent on current menu item being present somewhere in the list
117
  * - refactored the code
118
- *
119
  * v1.2.2 change log:
120
  * - bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
121
- *
122
  * v1.2.1 change log:
123
  * - 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)
124
  * - corrected 'show all from start level 1' processing so that custom classes get applied and 'Title from "Current" Item' works (regardless of filter settings)
125
  * - changed the defaults for new widgets such that only the Filter section is open by default; all the others are collapsed
126
  * - updated demo.html and readme.txt, and made demo available from readme
127
- *
128
  * v1.2.0 change log:
129
  * - added custom_menu_wizard shortcode, to run the widget from within content
130
  * - moved the 'no ancestor' fallback into new Fallback collapsible section, and added a fallback for Current Item with no children
131
  * - fixed bug with optgroups/options made available for the 'Children of' selector after the widget has been saved (also affecting disabled fields & styling)
132
  * - don't include menus with no items
133
  * - updated demo.html
134
- *
135
  * v1.1.0 change log:
136
  * - added 'Current Root Item' and 'Current Parent Item' to the 'Children of' filter
137
  * - added an Output option to include both the parent item and the parent's siblings (for a successful 'Children of' filter)
@@ -145,963 +150,1092 @@ defined( 'ABSPATH' ) or exit();
145
 
146
  if( !class_exists( 'Custom_Menu_Wizard_Plugin' ) ){
147
 
148
- //include the widget class and its walker...
149
- include( plugin_dir_path( __FILE__ ) . 'include/class.widget.php' );
150
- include( plugin_dir_path( __FILE__ ) . 'include/class.walker.php' );
151
-
152
- //instantiate...
153
- add_action( 'plugins_loaded', array( 'Custom_Menu_Wizard_Plugin', 'init' ) );
154
-
155
- //declare the main plugin class...
156
- class Custom_Menu_Wizard_Plugin {
157
-
158
- public static $version = '3.1.4';
159
- public static $script_handle = 'custom-menu-wizard-plugin-script';
160
- public static $widget_class = 'Custom_Menu_Wizard_Widget';
161
- protected static $instance;
162
-
163
- /**
164
- * constructor : adds actions
165
- */
166
- public function __construct(){
167
-
168
- add_action( 'widgets_init', array( &$this, 'widget_and_shortcode' ) );
169
- add_action( 'wp_ajax_cmw-find-shortcodes', array( &$this, 'ajax_find_shortcodes' ) );
170
- add_action( 'admin_enqueue_scripts', array( &$this, 'register_scripts' ) );
171
- add_action( 'admin_print_styles-widgets.php', array( &$this, 'enqueue_styles' ) );
172
- add_action( 'admin_print_scripts-widgets.php', array( &$this, 'enqueue_scripts' ) );
173
- add_action( 'admin_menu', array( &$this, 'admin_menu' ) );
174
- //add customizer support...
175
- add_action( 'customize_controls_enqueue_scripts', array( &$this, 'enqueue_styles' ) );
176
- add_action( 'customize_controls_enqueue_scripts', array( &$this, 'enqueue_scripts' ) );
177
-
178
- //add filter to prevent texturisation of cmwizard shortcode (screws up alternatives!)...
179
- add_filter( 'no_texturize_shortcodes', array( &$this, 'no_texturize_shortcode' ) );
180
- //add filter for encoding a cmwizard shortcode into instance settings...
181
- add_filter( 'custom_menu_wizard_encode_shortcode', array( $this, 'encode_shortcode' ), 10, 1 );
182
- //add filter for sanitizing an alternative shortcode setting...
183
- add_filter( 'custom_menu_wizard_sanitize_alternative', array( $this, 'sanitize_alternative' ), 10, 1 );
184
-
185
- } //end __construct()
186
-
187
- /**
188
- * hooked into plugins_loaded action : creates the plugin instance
189
- */
190
- public static function init(){
191
-
192
- is_null( self::$instance ) && self::$instance = new self;
193
- return self::$instance;
194
-
195
- } //end init()
196
-
197
- /**
198
- * hooked into admin_menu action : add action for when an update to this plugin is available
199
- */
200
- public function admin_menu(){
201
-
202
- add_action( 'in_plugin_update_message-' . plugin_basename( __FILE__ ), array( &$this, 'update_message' ), 10, 2 );
203
-
204
- } //end admin_menu()
205
-
206
- /**
207
- * hooked into admin_print_scripts-widgets.php & customize_controls_enqueue_scripts actions : queues scripts needed by the plugin
208
- */
209
- public function enqueue_scripts(){
210
-
211
- //script is pre-registered - see this->register_scripts() - so that it can be localized if need be (like for accessibility mode)
212
- //BUT on customize screens pre WPv3.9 the script does not get the chance to pre-register before it is
213
- //asked to enqueue, so this has to check that it is actually registered!...
214
- if( !wp_script_is( self::$script_handle, 'registered' ) ){
215
- $this->register_scripts();
216
- }
217
- wp_enqueue_script( self::$script_handle );
218
-
219
- } //end enqueue_scripts()
220
-
221
- /**
222
- * hooked into admin_print_styles-widgets.php & customize_controls_enqueue_scripts actions : queues styles needed by the plugin
223
- */
224
- public function enqueue_styles(){
225
- global $wp_scripts;
226
-
227
- wp_enqueue_style( 'custom-menu-wizard-plugin-styles', plugins_url( '/custom-menu-wizard.css', __FILE__ ), array(), self::$version );
228
- //if there's no jquery-ui style already registered, register Smoothness...
229
- if( !wp_style_is( 'jquery-ui', 'registered' ) ) {
230
- //get the jquery ui core version (default to 1.9.2 if not found)...
231
- $jquery_ui_version = isset( $wp_scripts->registered['jquery-ui-core']->ver ) ? $wp_scripts->registered['jquery-ui-core']->ver : '1.9.2';
232
- //register Smoothness theme for the determined ui version...
233
- // wp_register_style( 'jquery-ui', '//ajax.googleapis.com/ajax/libs/jqueryui/' . $jquery_ui_version . '/themes/smoothness/jquery-ui.css' );
234
- wp_register_style( 'jquery-ui', '//code.jquery.com/ui/' . $jquery_ui_version . '/themes/smoothness/jquery-ui.css' );
235
- }
236
- wp_enqueue_style( 'jquery-ui' );
237
-
238
- } //end enqueue_styles()
239
-
240
- /**
241
- * hooked into admin_enqueue_scripts : registers the plugin script
242
- */
243
- public function register_scripts(){
244
-
245
- $min = defined( 'WP_DEBUG' ) && WP_DEBUG ? '' : '.min';
246
- wp_register_script( self::$script_handle, plugins_url( "/custom-menu-wizard$min.js", __FILE__ ), array('jquery-ui-dialog'), self::$version, true );
247
-
248
- } //end register_scripts()
249
-
250
- /**
251
- * hooked into in_plugin_update_message-custom-menu-wizard action : request read changelog before updating
252
- * @param array $plugin_data Plugin metadata
253
- * @param array $r Metadata about the available plugin update
254
- */
255
- public function update_message( $plugin_data, $r ){
256
-
257
- $url = 'http://wordpress.org/plugins/' . $r->slug. '/changelog/';
258
- $style = implode( ';', array(
259
- '-webkit-box-sizing:border-box',
260
- '-moz-box-sizing:border-box',
261
- 'box-sizing:border-box',
262
- 'background-color:#D54E21',
263
- 'border-radius:2px',
264
- 'color:#FFFFFF',
265
- 'display:inline-block',
266
- 'margin:0',
267
- 'max-width:100%',
268
- 'overflow:hidden',
269
- 'padding:0 0.5em',
270
- 'text-overflow:ellipsis',
271
- 'text-shadow:0 1px 0 rgba(0, 0, 0, 0.5)',
272
- 'vertical-align:text-bottom',
273
- 'white-space:nowrap'
274
- ) ) . ';';
 
 
 
 
 
 
 
 
 
 
 
 
 
275
 
276
  ?>
277
  <p style="<?php echo $style; ?>"><em><?php printf( __('Please <a href="%s" style="color:#FFFFFF;text-decoration:underline;" target="_blank">read the Changelog</a> <strong>before</strong> updating!'), $url ); ?></em></p>
278
  <?php
279
 
280
- } //end update_message()
281
-
282
- /**
283
- * hooked into wp_ajax_cmw-find-shortcodes action : handle ajax request to find posts containing CMW shortcodes, returning XML
284
- */
285
- public function ajax_find_shortcodes(){
286
-
287
- check_admin_referer( 'cmw-find-shortcodes' );
288
- $response = array(
289
- 'what' => 'cmw_find_shortcodes',
290
- 'action' => 'list_posts',
291
- 'id' => '1',
292
- 'data' => $this->find_shortcodes()
293
- );
294
- $xmlResponse = new WP_Ajax_Response($response);
295
- $xmlResponse->send();
296
-
297
- }
298
-
299
- /**
300
- * list any post that contains a CMW shortcode; can be called from a shortcode or via an ajax call
301
- *
302
- * @param array $shortcodeInst Array of shortcode attributes
303
- * @return string HTML
304
- */
305
-
306
- public function find_shortcodes( $shortcodeInst = false ){
307
- global $wpdb;
308
-
309
- $html = '';
310
-
311
- //from a shortcode, the user must have edit_pages capability (implies Editor or above)...
312
- if( $shortcodeInst !== false && !current_user_can( 'edit_pages' ) ){
313
- return $html;
314
- }
315
-
316
- $codes = array(
317
- 'cmw-demo-found-old' => '[custom_menu_wizard',
318
- 'cmw-demo-found-new' => '[cmwizard'
319
- );
320
- foreach( $codes as $k => $v ){
321
- $j = str_replace( '-', '_', $k );
322
- //like_escape deprecated in v4...
323
- if( method_exists( $wpdb, 'esc_like' ) ){
324
- $$j = '%' . $wpdb->esc_like( $v ) . '%';
325
- }else{
326
- $$j = '%' . like_escape( esc_sql ( $v ) ) . '%';
327
- }
328
- }
329
-
330
- //search in all custom fields...
331
- $sql = "SELECT DISTINCT post_id FROM {$wpdb->postmeta}";
332
- $sql .= " WHERE meta_value LIKE '%s' OR meta_value LIKE '%s'";
333
- $post_ids_meta = $wpdb->get_col( $wpdb->prepare( $sql, $cmw_demo_found_old, $cmw_demo_found_new ) );
334
- //search in post_content...
335
- $sql = "SELECT DISTINCT ID FROM {$wpdb->posts}";
336
- $sql .= " WHERE post_content LIKE '%s' OR post_content LIKE '%s'";
337
- $post_ids_post = $wpdb->get_col( $wpdb->prepare( $sql, $cmw_demo_found_old, $cmw_demo_found_new ) );
338
-
339
- $post_ids = array_merge( $post_ids_meta, $post_ids_post );
340
-
341
- if( empty( $post_ids ) ){
342
- $html .= '<p>' . __('No CMW shortcodes found.') . '</p>';
343
- }else{
344
- $args = array(
345
- 'ignore_sticky_posts' => true,
346
- 'nopaging' => true,
347
- 'orderby' => 'date',
348
- 'post_type' => 'any',
349
- 'post_status' => array( 'publish', 'draft', 'future', 'pending', 'private' ),
350
- 'post__in' => $post_ids
351
- );
352
-
353
- $the_query = new WP_Query( $args );
354
- if( $the_query->have_posts() ){
355
- $html .= '<dl>';
356
- while( $the_query->have_posts() ){
357
- $the_query->the_post();
358
- $id = get_the_ID();
359
- $inPost = in_array( $id, $post_ids_post );
360
- $inMeta = in_array( $id, $post_ids_meta );
361
- $dtClass = array();
362
- $anchorTitle = array();
363
- if( $inPost ){
364
- $content = get_the_content();
365
- foreach( $codes as $k => $v ){
366
- if( strpos( $content, $v ) !== false ){
367
- $dtClass[ $k ] = 1;
368
- $anchorTitle[ $v . ']' ] = 1;
369
- }
370
- }
371
- }
372
- if( $inMeta ){
373
- $content = get_post_meta( $id );
374
- foreach( $content as $k => $v ){
375
- $content[ $k ] = implode(' ', $v );
376
- }
377
- $content = implode( ' ', $content );
378
- foreach( $codes as $k => $v ){
379
- if( strpos( $content, $v ) !== false ){
380
- $dtClass[ $k ] = 1;
381
- $anchorTitle[ $v . ']' ] = 1;
382
- }
383
- }
384
- }
385
- $anchorTarget = get_post_type( $id );
386
- if( empty( $anchorTarget ) ){
387
- $anchorTarget = __('unknown type');
388
- }else{
389
- $anchorTarget = (string)$anchorTarget;
390
- }
391
- $content = $inPost ? ( $inMeta ? __( 'content+meta' ) : __( 'content' ) ) : __( 'meta' );
392
- $anchorTitle = $anchorTarget . ' #' . $id . ', ' . $content . ', ' . implode( __(' and '), array_keys( $anchorTitle ) );
393
- $anchorTarget = '';
394
- if( $shortcodeInst === false ){
395
- //is from assistant via ajax...
396
- $anchorTarget = 'target="_blank"';
397
- $anchorTitle .= ' ... ' . __('opens new tab/window');
398
- }
399
- $html .= '<dt class="' . implode( ' ', array_keys( $dtClass ) ) . '"><a href="' . get_permalink() . '" ' . $anchorTarget . ' title="' . $anchorTitle . '">' . get_the_title() . '</a></dt>';
400
- }
401
- $html .= '</dl>';
402
- }else{
403
- $html .= '<p>' . __('No CMW shortcodes found.') . '</p>';
404
- }
405
-
406
- wp_reset_postdata();
407
- }
408
-
409
- //if originator is shortcode, put a simple wrapper (no styling!) around the results, and optionally an H3 title...
410
- if( $shortcodeInst !== false ){
411
- $anchorTitle = is_array( $shortcodeInst ) && !empty( $shortcodeInst['title'] ) ? esc_attr( strip_tags( trim( $shortcodeInst['title'] ) ) ) : '';
412
- $anchorTitle = empty( $anchorTitle ) ? '' : '<h3>' . $anchorTitle . '</h3>';
413
- $html = '<div class="cmw-list-posts-with-shortcodes">' . $anchorTitle . $html . '</div>';
414
- }
415
-
416
- return $html;
417
-
418
- } //end find_shortcodes()
419
-
420
- /**
421
- * hooked into widgets_init action : registers widget and adds shortcode(s)
422
- */
423
- public function widget_and_shortcode(){
424
-
425
- //register the widget class...
426
- register_widget( self::$widget_class );
427
- //add shortcode...
428
- add_shortcode( 'cmwizard', array( &$this, 'shortcode' ) );
429
- //add shortcode, v2.1.0 version (deprecated!)...
430
- add_shortcode( 'custom_menu_wizard', array( &$this, 'shortcode_legacy' ) );
431
-
432
- } //end widget_and_shortcode()
433
-
434
- /**
435
- * hooked into custom_menu_wizard_sanitize_alternative filter : sanitizes an alternative shortcode setting
436
- * used by this->shortcode_instance, and the widget class
437
- *
438
- * @param string $alt Alternative (switch_to setting)
439
- * @return string
440
- */
441
- public function sanitize_alternative( $alt = '' ){
442
-
443
- if( empty( $alt ) || !is_string( $alt ) ){
444
- return '';
445
- }
446
-
447
- //kill containing square brackets, self-terminators and spaces, then split on square bracket...
448
- $alt = preg_split( '/[\[\]]/', trim( $alt, ' []/' ) );
449
- //use the first element, kill tabs, CRLFs and multiple spaces, and retrim for self-terminators and spaces...
450
- $alt = trim( preg_replace( array( '/[\r\n\t]+/', '/\s\s+/' ), ' ', $alt[0] ), ' /' );
451
- //remove leading 'cmwizard' tag...
452
- $alt = preg_replace( '/^cmwizard\s/', '', $alt . ' ' );
453
- //remove any occurrences of 'menu=whatever' and 'alternative="whatever"' (optional double quotes), and trim spaces...
454
- $alt = trim( preg_replace( array('/\smenu=[^\s]*\s/', '/\salternative=("[^"]*"|[^\s]*)\s/' ), ' ', ' ' . $alt . ' ' ) );
455
-
456
- return $alt;
457
-
458
- } //end sanitize_alternative()
459
-
460
- /**
461
- * hooked into no_texturize_shortcodes filter : prevents texturisation of cmwizard shortcode
462
- *
463
- * @param array $arr Array of shortcodes
464
- * @return array
465
- */
466
- public function no_texturize_shortcode( $arr = false ){
467
- return empty( $arr ) ? array('cmwizard') : array_merge( (array)$arr, array('cmwizard') );
468
- }
469
-
470
- /**
471
- * shortcode processing for [cmwizard option="" option="" ...] (as of v3.0.0)
472
- *
473
- * see wp-includes/widgets.php for the_widget() code
474
- * Note that hide_empty is set to ON and can not be overridden!
475
- *
476
- * differences from [custom_menu_wizard] shortcode (ie. v2.1.0)
477
- * deprecated:
478
- * - children_of : now branch, and limited to current[-item] or digits; parent|current-parent|root|current-ancestor all require conversion
479
- * - start_level : now level (integer) for a by-level filter, or start_at (string) for a by-branch filter (determining branch_start)
480
- * - include_root : (as of v3.0.4) replaced by include_level (like exclude_level); include_root On equiv. is include_level == '1'
481
- * changed:
482
- * - contains_current : was a switch, now a string (empty or menu|primary|secondary|output); switch ON = 'output'
483
- * - include : now accepts siblings, ancestors and/or ancestor-siblings (csv); parent is gone, and hyphen separator no longer allowed
484
- * - title_from : should now be csv, hyphen separator no longer allowed
485
- * added:
486
- * - title_tag & findme
487
- * - include_level (v3.0.4)
488
- *
489
- * default (ie. no options) is:
490
- * - show all
491
- * - of first populated menu found (alphabetically)
492
- * - from root, for unlimited depth
493
- * - as hierarchical nested ULs inside a DIV.widget_custom_menu_wizard.shortcode_custom_menu_wizard
494
- *
495
- * @filters : custom_menu_wizard_shortcode_attributes array of attributes (unfiltered!) supplied to the shortcode
496
- * shortcode_atts_cmwizard array of output attributes, array of supported attributes, array of supplied attributes
497
- * custom_menu_wizard_shortcode_settings array of widget settings derived from the attributes
498
- * custom_menu_wizard_shortcode_widget_args array of the sidebar args used to wrap widgets and their titles (before|after_widget, before|after_title)
499
- * NB each of the arrays passed to the above filters has a extra key-value pair of 'cmwv' => the current plugin version, eg. '3.0.0'
500
- *
501
- * @param array $atts options supplied to the shortcode
502
- * @param string $content Within start-end shortcode tags
503
- * @param string $tag Shortcode tag
504
- * @return string HTML that comes from running the_widget()
505
- */
506
- public function shortcode($atts, $content, $tag){
507
-
508
- $html = '';
509
- $instance = $this->shortcode_instance( $atts, $tag, $content, true );
510
- $ok = !empty( $instance );
511
-
512
- if( $ok && !empty( $instance['findme'] ) ){
513
- //return the findme output...
514
- return $this->find_shortcodes( $instance );
515
- }
516
-
517
- if( $ok ){
518
-
519
- //if widget isn't registered(!), try re-registering; if still not registered, cop out...
520
- if( !$this->widget_registered() ){
521
- if( did_action( 'widgets_init' ) > 0 ){
522
- if( apply_filters( 'custom_menu_wizard_widget_reregister', true ) ){
523
- //re-register the widget...
524
- register_widget( self::$widget_class );
525
- if( !$this->widget_registered() ){
526
- return WP_DEBUG ? __('[cmwizard PROBLEM="widget de-registered, and failed to re-register!"/]') : $html;
527
- }
528
- }else{
529
- return WP_DEBUG ? __('[cmwizard PROBLEM="widget de-registered, and not allowed to re-register!"/]') : $html;
530
- }
531
- }else{
532
- //hasn't had a chance to register yet!...
533
- return WP_DEBUG ? __('[cmwizard PROBLEM="widgets have not been initialised yet!"/]') : $html;
534
- }
535
- }
536
-
537
- //handle widget_class here because we have full control over $before_widget...
538
- $before_widget_class = array(
539
- 'widget_custom_menu_wizard',
540
- 'shortcode_custom_menu_wizard'
541
- );
542
- $instance['widget_class'] = empty( $instance['widget_class'] ) ? '' : esc_attr( trim ( $instance['widget_class'] ) );
543
- if( !empty( $instance['widget_class'] ) ){
544
- foreach( explode(' ', $instance['widget_class'] ) as $i ){
545
- if( !empty( $i ) && !in_array( $i, $before_widget_class ) ){
546
- $before_widget_class[] = $i;
547
- }
548
- }
549
- }
550
- unset( $instance['widget_class'] );
551
- }
552
-
553
- if( $ok ){
554
- //not used by the plugin, but could be used in the widget code to tell whether it was being
555
- //run as a result of a widget or a shortcode?...
556
- $instance['shortcode'] = true;
557
- //allow the element that wraps the widget title to be changed from an h2 (the WP default) to another tag...
558
- //note : does not allow for changing the class, or for removing the wrapping element
559
- // for a class override, add CSS rule for
560
- // .shortcode_custom_menu_wizard .widgettitle { ..... }
561
- // can also be overriden using the 'custom_menu_wizard_shortcode_widget_args' filter (applied below)
562
- $instance['title_tag'] = esc_attr( trim( $instance['title_tag'] ) );
563
- if( empty( $instance['title_tag'] ) ){
564
- //default to H2...
565
- $instance['title_tag'] = 'h2';
566
- }
567
- //apart from before_widget, these are lifted from the_widget() (wp-includes/widgets.php)...
568
- $sidebar_args = array(
569
- 'before_widget' => '<div class="' . implode( ' ', $before_widget_class ) . '">',
570
- 'after_widget' => '</div>',
571
- 'before_title' => '<' . $instance['title_tag'] . ' class="widgettitle">',
572
- 'after_title' => '</' . $instance['title_tag'] . '>'
573
- );
574
- unset( $instance['title_tag'] );
575
-
576
- ob_start();
577
- the_widget(
578
- self::$widget_class,
579
- apply_filters(
580
- 'custom_menu_wizard_shortcode_settings',
581
- array_merge( $instance, array('cmwv' => self::$version) )
582
- ),
583
- apply_filters(
584
- 'custom_menu_wizard_shortcode_widget_args',
585
- array_merge( $sidebar_args, array('cmwv' => self::$version) )
586
- ) );
587
- $html = ob_get_clean();
588
- }
589
-
590
- return empty( $html ) ? '' : $html;
591
-
592
- } //end shortcode()
593
-
594
- /**
595
- * does most of the attribute processing/checking for the cmwizard (only) shortcode
596
- * is called from shortcode() method *AND* encode_shortcode() method, which is run from a filter enabling
597
- * settings to be changed at start (after determination of current item) of the widget's walker process.
598
- *
599
- * @param array $atts options supplied to the shortcode
600
- * @param string $tag Shortcode tag
601
- * @param string $content Within start-end shortcode tags
602
- * @param boolean $doShortcode True if called from shortcode(), false otherwise
603
- * @return array|boolean A set of widget instance settings, or false if shortcode is invalid
604
- */
605
- public function shortcode_instance( $atts, $tag, $content = '', $doShortcode = false ){
606
-
607
- $ok = false;
608
-
609
- // NB csv = comma or space separated list...
610
- $instance = shortcode_atts( array(
611
- 'title' => '',
612
- 'menu' => 0, // menu id, slug or name
613
- 'level' => 0,
614
- //determines filter (in conjunction with items)...
615
- 'branch' => 0, // a menu item id, or current|current-item
616
- //determines filter (in conjunction with branch)...
617
- 'items' => '', // csv of menu item ids (an id may have a '+' appended, for inheritance, eg. '23+')
618
- 'depth' => 0, // 0 = unlimited
619
- 'depth_rel_current' => 0,
620
- //determines branch_start...
621
- 'start_at' => '',
622
- 'start_mode' => '', // 'level' or empty
623
- 'allow_all_root' => 0,
624
- //inclusions...
625
- 'ancestors' => 0, //integer (negative = relative)
626
- 'ancestor_siblings' => 0, //integer (negative = relative)
627
- 'include_root' => 0, //switch (means *all* root items!) v3.0.4 DEPRECATED still allowed (for back compat.), equiv. is include_level='1'
628
- 'include_level' => '', // v3.0.4 digit, possibly appended with a '+' or '-', eg. '2', '2+', or '2-'
629
- 'siblings' => 0, //switch
630
- //exclusions...
631
- 'exclude' => '', // csv of menu item ids (an id may have a '+' appended, for inheritance, eg. '23+')
632
- 'exclude_level' => '', // digit, possibly appended with a '+' or '-', eg. '2', '2+', or '2-'
633
- 'contains_current' => '', // menu|primary|secondary|inclusions|output
634
- //determines fallback (current|parent|quit) and, optionally, fallback_siblings and/or fallback_depth...
635
- 'fallback' => '', //eg. 'quit', or 'current' or 'current+siblings' or 'parent+siblings,2' or 'parent,1'
636
- //switches...
637
- 'flat_output' => 0,
638
- //determines title_from_[branch|current|branch-root|current-root]...
639
- 'title_from' => '', // csv of branch|current|branch-root|current-root
640
- 'title_linked' => 0, //v3.1.4
641
- 'ol_root' => 0,
642
- 'ol_sub' => 0,
643
- //strings...
644
- 'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
645
- 'container_id' => '',
646
- 'container_class' => '',
647
- 'menu_class' => 'menu-widget',
648
- 'widget_class' => '',
649
- //determines switch_if, switch_at & switch_to (depending on $content)...
650
- 'alternative' => '', //csv of current|no-current|no-output and menu|primary|secondary|inclusions|output, eg. 'current,menu'
651
- //determines before & after...
652
- 'wrap_link' => '', // a tag name (eg. div, p, span, etc)
653
- //determines link_before & link_after...
654
- 'wrap_link_text' => '', // a tag name (eg. span, em, strong)
655
- //modifies the before/after_title $sidebar_args, changing the default H2 tag to something else(?)...
656
- 'title_tag' => '', // a tag name (eg. h1, h3, etc)
657
- //utility : doesn't run widget! instead, lists all posts/pages that contain a CMW shortcode...
658
- 'findme' => 0
659
- ),
660
- $doShortcode
661
- ? apply_filters(
662
- 'custom_menu_wizard_shortcode_attributes',
663
- array_merge( (array)$atts, array('cmwv' => self::$version) )
664
- )
665
- : (array)$atts,
666
- $doShortcode
667
- ? $tag // since WP3.6 this allows use of shortcode_atts_cmwizard filter, applied by shortcode_atts()
668
- : ''
669
- );
670
-
671
- //if not decoding a main shortcode then we're looking at an alternative, and alternatives can't be
672
- //nested, nor can they run findme or change the title's tag element...
673
- if( !$doShortcode ){
674
- unset( $instance['findme'], $instance['title_tag'], $instance['alternative'] );
675
- }
676
-
677
- if( !empty( $instance['findme'] ) ){
678
- return $instance;
679
- }
680
-
681
- //in order of priority...
682
- $byItems = !empty( $instance['items'] );
683
- $byBranch = !$byItems && !empty( $instance['branch'] );
684
- $byLevel = !$byItems && !$byBranch;
685
-
686
- if( empty( $instance['menu'] ) ){
687
- //gonna find the first menu (alphabetically) that has items...
688
- $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
689
- }else{
690
- //allow for menu being something other than an id (eg. slug or name), but we need the id for the widget...
691
- $menus = wp_get_nav_menu_object( $instance['menu'] );
692
- if( !empty( $menus) ){
693
- $menus = array( $menus );
694
- }
695
- }
696
- if( !empty( $menus ) ){
697
- foreach( $menus as $i=>$menu ){
698
- $items = wp_get_nav_menu_items( $menu->term_id );
699
- $ok = !empty( $items );
700
- if( $ok ){
701
- $instance['menu'] = $menu->term_id;
702
- break;
703
- }
704
- }
705
- }
706
- unset( $menus );
707
-
708
- if( $ok ){
709
- if( $byItems ){
710
- $instance['filter'] = 'items';
711
- }
712
- if( $byBranch ){
713
- $instance['filter'] = 'branch';
714
- switch( "{$instance['start_at']}" ){
715
- case '0':
716
- case 'branch': $instance['branch_start'] = ''; break;
717
- case 'root' : $instance['branch_start'] = '1'; break;
718
- case 'children': $instance['branch_start'] = '+1'; break;
719
- case 'parent': $instance['branch_start'] = '-1'; break;
720
- default: $instance['branch_start'] = "{$instance['start_at']}";
721
- }
722
- if( $instance['branch'] == 'current' || $instance['branch'] == 'current-item' ){
723
- $instance['branch'] = 0;
724
- }elseif( !is_numeric( $instance['branch'] ) ){
725
- //if branch is non-numeric then it could be the title of a menu item, but we need it to be the menu item's id...
726
- $instance['branch'] = strtolower( $instance['branch'] );
727
- foreach( $items as $item ){
728
- $ok = strtolower( $item->title ) == $instance['branch'];
729
- if( $ok ){
730
- $instance['branch'] = $item->ID;
731
- break;
732
- }
733
- }
734
- }
735
- }
736
- if( $byLevel ){
737
- $instance['filter'] = '';
738
- $instance['level'] = max(1, intval( $instance['level'] ) );
739
- }
740
- unset( $instance['start_at'] );
741
- }
742
-
743
- if( $ok ){
744
- //include_level, and the deprecated include_root switch...
745
- //if level is empty but root is set, set include_level to '1'...
746
- if( empty( $instance['include_level'] ) && $instance['include_root'] ){
747
- $instance['include_level'] = '1';
748
- }
749
- unset( $instance['include_root'] );
750
- //fallback => fallback and fallback_siblings and fallback_depth...
751
- //allows "X", "X,Y" or "X,Y,Z" where comma could be space, and X|Y|Z could be "quit"|"current"|"parent", or "+siblings", or digit(s)
752
- //but "quit", "current" or "parent" must be present (others are optional)
753
- if( $byBranch && empty( $instance['branch'] ) && !empty( $instance['fallback'] ) ){
754
- $i = preg_split( '/[\s,]+/', strtolower( $instance['fallback'] ), -1, PREG_SPLIT_NO_EMPTY );
755
- $instance['fallback'] = '';
756
- if( in_array( 'quit', $i ) ){
757
- $instance['fallback'] = 'quit';
758
- }elseif( in_array( 'parent', $i ) ){
759
- $instance['fallback'] = 'parent';
760
- }elseif( in_array( 'current', $i ) ){
761
- $instance['fallback'] = 'current';
762
- }
763
- if( !empty( $instance['fallback'] ) && $instance['fallback'] != 'quit' ){
764
- if( in_array( '+siblings', $i ) ){
765
- $instance['fallback_siblings'] = 1;
766
- }
767
- $i = array_diff( $i, array( 'quit', 'parent', 'current', '+siblings' ) );
768
- if( !empty( $i ) ){
769
- foreach( $i as $v ){
770
- $v = trim( $v );
771
- if( preg_match( '/^\d+$/', $v ) > 0 && intval( $v ) > 0 ){
772
- $instance['fallback_depth'] = intval( $v );
773
- break;
774
- }
775
- }
776
- }
777
- }
778
- }
779
- }
780
-
781
- if( $ok ){
782
- //title_from => title_from_...
783
- if( !empty( $instance['title_from'] ) ){
784
- $i = preg_split( '/[\s,]+/', strtolower( $instance['title_from'] ), -1, PREG_SPLIT_NO_EMPTY );
785
- foreach( $i as $j ){
786
- if( $j == 'branch' || $j == 'current' ){
787
- $instance[ 'title_from_' . $j ] = 1;
788
- }elseif( $j == 'branch-root' || $j == 'current-root' ){
789
- $instance[ 'title_from_' . str_replace( '-', '_', $j ) ] = 1;
790
- }
791
- }
792
- }
793
- unset( $instance['title_from'] );
794
-
795
- //wrap_link => before & after...
796
- $instance['wrap_link'] = esc_attr( trim( $instance['wrap_link'] ) );
797
- if( !empty( $instance['wrap_link'] ) ){
798
- $instance['before'] = '<' . $instance['wrap_link'] . '>';
799
- $instance['after'] = '</' . $instance['wrap_link'] . '>';
800
- }
801
- unset( $instance['wrap_link'] );
802
-
803
- //wrap_link_text => link_before & link_after...
804
- $instance['wrap_link_text'] = esc_attr( trim( $instance['wrap_link_text'] ) );
805
- if( !empty( $instance['wrap_link_text'] ) ){
806
- $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
807
- $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
808
- }
809
- unset( $instance['wrap_link_text'] );
810
-
811
- //alternative => switch_if, switch_at & switch_to...
812
- if( !empty( $instance['alternative'] ) ){
813
- $i = preg_split( '/[\s,]+/', strtolower( $instance['alternative'] ), -1, PREG_SPLIT_NO_EMPTY );
814
- foreach( $i as $j ){
815
- if( in_array( $j, array('current', 'no-current', 'no-output' ) ) ){
816
- $instance['switch_if'] = $j;
817
- }elseif( in_array( $j, array('menu', 'primary', 'secondary', 'inclusions', 'output') ) ){
818
- $instance['switch_at'] = $j;
819
- }
820
- }
821
- if( !empty( $instance['switch_if'] ) && !empty( $instance['switch_at'] ) ){
822
- //v3.1.4 : fixed bug where $content - the alternative shortcode - wasn't being passed into the filter...
823
- $instance['switch_to'] = apply_filters( 'custom_menu_wizard_sanitize_alternative', $content );
824
- }else{
825
- $instance['switch_if'] = $instance['switch_at'] = $instance['switch_to'] = '';
826
- }
827
- unset( $instance['alternative'] );
828
- }
829
-
830
- //turn on hide_empty...
831
- $instance['hide_empty'] = 1;
832
- }
833
-
834
- return $ok ? $instance : false;
835
-
836
- } //end shortcode_instance()
837
-
838
- /**
839
- * hooked into custom_menu_wizard_encode_shortcode filter : converts a cmwizard shortcode into instance settings fit for
840
- * the widget() method of Custom_Menu_Wizard_Widget
841
- *
842
- * it's important to note that a shortcode processed this way does *NOT* hit the filters that a cmwizard shortcode would
843
- * normally hit, namely custom_menu_wizard_shortcode_attributes & shortcode_atts_cmwizard
844
- *
845
- * @param string $shortcode A full [cmwizard .../] shortcode
846
- * @return array|boolean Instance settings, or false if error
847
- */
848
- public function encode_shortcode( $shortcode = '' ){
849
-
850
- if( class_exists( self::$widget_class ) &&
851
- preg_match( '/^cmwizard\s?(.*)$/', rtrim( ltrim( $shortcode, '[ ' ), '] /' ), $m ) > 0 ){
852
- $instance = $this->shortcode_instance( shortcode_parse_atts( trim( $m[1] ) ), 'cmwizard' );
853
- if( !empty( $instance ) ){
854
- $instance['cmwv'] = self::$version;
855
- $instance = Custom_Menu_Wizard_Widget::cmw_settings( $instance, false, 'widget' );
856
- }
857
- }
858
- return empty( $instance ) ? false : $instance;
859
-
860
- } //end encode_shortcode()
861
-
862
- /**
863
- * checks that the widget is registered
864
- *
865
- * @return boolean True if registered
866
- */
867
- public function widget_registered(){
868
- global $wp_widget_factory;
869
-
870
- return ( isset( $wp_widget_factory->widgets[ self::$widget_class ] ) &&
871
- is_a( $wp_widget_factory->widgets[ self::$widget_class ], self::$widget_class ) );
872
-
873
- }
874
-
875
- /**
876
- * shortcode processing for [custom_menu_wizard option="" option="" ...] (as of v2.1.0)
877
- * see wp-includes/widgets.php for the_widget() code
878
- * Note that hide_empty is set to ON and can not be overridden!
879
- *
880
- * default (ie. no options) is:
881
- * - show all
882
- * - of first populated menu found (alphabetically)
883
- * - from root, for unlimited depth
884
- * - as hierarchical nested ULs inside a DIV.widget_custom_menu_wizard.shortcode_custom_menu_wizard
885
- *
886
- * @filters : custom_menu_wizard_shortcode_attributes array of attributes supplied to the shortcode
887
- * custom_menu_wizard_shortcode_settings array of widget settings derived from the attributes
888
- * custom_menu_wizard_shortcode_widget_args array of the sidebar args used to wrap widgets and their titles (before|after_widget, before|after_title)
889
- *
890
- * @param array $atts options supplied to the shortcode
891
- * @param string $content Within start-end shortcode tags
892
- * @param string $tag Shortcode tag
893
- * @return string HTML that comes from running the_widget()
894
- */
895
- function shortcode_legacy($atts, $content, $tag){
896
- $html = '';
897
- $ok = false;
898
- $instance = shortcode_atts( array(
899
- 'title' => '',
900
- 'menu' => 0, // menu id, slug or name
901
- //determines filter & filter_item ('items' takes precedence over 'children_of' because it's more specific)...
902
- 'children_of' => '', // empty = show all (dep. on 'items'); menu item id or title (caseless), or current|current-item|parent|current-parent|root|current-ancestor
903
- '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)
904
- 'start_level' => 1,
905
- 'depth' => 0, // 0 = unlimited
906
- //only if children_of is (parent|current-parent|root|current-ancestor); determines fallback_no_ancestor, fallback_include_parent & fallback_include_parent_siblings...
907
- 'fallback_parent' => 0, // 1 = use current-item; 'parent' = *and* include parent, 'siblings' = *and* include both parent and its siblings
908
- //only if children_of is (current|current-item); determines fallback_no_children, fallback_nc_include_parent & fallback_nc_include_parent_siblings...
909
- 'fallback_current' => 0, // 1 = use current-parent; 'parent' = *and* include parent (if available), 'siblings' = *and* include both parent (if available) and its siblings
910
- //switches...
911
- 'flat_output' => 0,
912
- 'contains_current' => 0, // v2.0.0
913
- //determines include_parent, include_parent_siblings & include_ancestors...
914
- 'include' =>'', //comma|space|hyphen separated list of 'parent', 'siblings', 'ancestors'
915
- 'ol_root' => 0,
916
- 'ol_sub' => 0,
917
- //determines title_from_parent & title_from_current...
918
- 'title_from' => '', //comma|space|hyphen separated list of 'parent', 'current'
919
- 'depth_rel_current' => 0, // v2.0.0
920
- //strings...
921
- 'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
922
- 'container_id' => '',
923
- 'container_class' => '',
924
- 'menu_class' => 'menu-widget',
925
- 'widget_class' => '',
926
- //determines before & after...
927
- 'wrap_link' => '', // a tag name (eg. div, p, span, etc)
928
- //determines link_before & link_after...
929
- 'wrap_link_text' => '' // a tag name (eg. span, em, strong)
930
- ),
931
- $atts,
932
- $tag // since WP3.6 this allows use of shortcode_atts_custom_menu_wizard filter, applied by shortcode_atts()
933
- );
934
-
935
- $instance = apply_filters( 'custom_menu_wizard_shortcode_attributes', $instance );
936
-
937
- if( empty( $instance['menu'] ) ){
938
- //gonna find the first menu (alphabetically) that has items...
939
- $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
940
- }else{
941
- //allow for menu being something other than an id (eg. slug or name), but we need the id for the widget...
942
- $menus = wp_get_nav_menu_object( $instance['menu'] );
943
- if( !empty( $menus) ){
944
- $menus = array( $menus );
945
- }
946
- }
947
- if( !empty( $menus ) ){
948
- foreach( $menus as $i=>$menu ){
949
- $items = wp_get_nav_menu_items( $menu->term_id );
950
- $ok = !empty( $items );
951
- if( $ok ){
952
- $instance['menu'] = $menu->term_id;
953
- break;
954
- }
955
- }
956
- }
957
- unset( $menus );
958
-
959
- if( $ok ){
960
- $instance['filter'] = $instance['filter_item'] = 0;
961
- if( empty( $instance['items'] ) ){
962
- //children_of => filter & filter_item...
963
- if( empty( $instance['children_of'] ) ){
964
- $instance['children_of'] = '';
965
- }
966
- switch( $instance['children_of'] ){
967
- case '':
968
- break;
969
- case 'root': case 'current-ancestor':
970
- --$instance['filter_item']; //ends up as -2
971
- case 'parent': case 'current-parent':
972
- --$instance['filter_item']; //ends up as -1
973
- case 'current': case 'current-item':
974
- $instance['filter'] = 1;
975
- break;
976
- default:
977
- $instance['filter'] = 1;
978
- $instance['filter_item'] = strtolower( $instance['children_of'] );
979
- }
980
- //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...
981
- if( !is_numeric( $instance['filter_item'] ) ){
982
- foreach( $items as $item ){
983
- $ok = strtolower( $item->title ) == $instance['filter_item'];
984
- if( $ok ){
985
- $instance['filter_item'] = $item->ID;
986
- break;
987
- }
988
- }
989
- }
990
- }else{
991
- $instance['filter'] = -1;
992
- }
993
- unset( $instance['children_of'] );
994
- }
995
-
996
- if( $ok ){
997
- //fallback_parent => fallback_no_ancestor switch (and extension switches)...
998
- $instance['fallback_no_ancestor'] = $instance['fallback_include_parent'] = $instance['fallback_include_parent_siblings'] = 0;
999
- if( $instance['filter_item'] < 0 && !empty( $instance['fallback_parent'] ) ){
1000
- $instance['fallback_no_ancestor'] = 1;
1001
- $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_parent'] ), -1, PREG_SPLIT_NO_EMPTY );
1002
- foreach( $i as $j ){
1003
- if( $j == 'parent' ){
1004
- $instance['fallback_include_parent'] = 1;
1005
- }elseif( $j == 'siblings' ){
1006
- $instance['fallback_include_parent_siblings'] = 1;
1007
- }
1008
- }
1009
- }
1010
- //fallback_current => fallback_no_children switch (and extension switches)...
1011
- $instance['fallback_no_children'] = $instance['fallback_nc_include_parent'] = $instance['fallback_nc_include_parent_siblings'] = 0;
1012
- if( $instance['filter'] == 1 && $instance['filter_item'] == 0 && !empty( $instance['fallback_current'] ) ){
1013
- $instance['fallback_no_children'] = 1;
1014
- $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_current'] ), -1, PREG_SPLIT_NO_EMPTY );
1015
- foreach( $i as $j ){
1016
- if( $j == 'parent' ){
1017
- $instance['fallback_nc_include_parent'] = 1;
1018
- }elseif( $j == 'siblings' ){
1019
- $instance['fallback_nc_include_parent_siblings'] = 1;
1020
- }
1021
- }
1022
- }
1023
- unset( $instance['fallback_parent'], $instance['fallback_current'] );
1024
- //include => include_* ...
1025
- $instance['include_parent'] = $instance['include_parent_siblings'] = $instance['include_ancestors'] = 0;
1026
- if( $instance['filter'] == 1 && !empty( $instance['include'] ) ){
1027
- $i = preg_split( '/[\s,-]+/', strtolower( $instance['include'] ), -1, PREG_SPLIT_NO_EMPTY );
1028
- foreach( $i as $j ){
1029
- if( $j == 'parent' ){
1030
- $instance['include_parent'] = 1;
1031
- }elseif( $j == 'siblings' ){
1032
- $instance['include_parent_siblings'] = 1;
1033
- }elseif( $j == 'ancestors' ){
1034
- $instance['include_ancestors'] = 1;
1035
- }
1036
- }
1037
- }
1038
- unset( $instance['include'] );
1039
- //title_from => title_from_parent, title_from_current ...
1040
- $instance['title_from_parent'] = $instance['title_from_current'] = 0;
1041
- if( !empty( $instance['title_from'] ) ){
1042
- $i = preg_split( '/[\s,-]+/', strtolower( $instance['title_from'] ), -1, PREG_SPLIT_NO_EMPTY );
1043
- foreach( $i as $j ){
1044
- if( $j == 'parent' ){
1045
- $instance['title_from_parent'] = 1;
1046
- }elseif( $j == 'current' ){
1047
- $instance['title_from_current'] = 1;
1048
- }
1049
- }
1050
- }
1051
- unset( $instance['title_from'] );
1052
- //wrap_link => before & after...
1053
- $instance['before'] = $instance['after'] = '';
1054
- $instance['wrap_link'] = esc_attr( trim( $instance['wrap_link'] ) );
1055
- if( !empty( $instance['wrap_link'] ) ){
1056
- $instance['before'] = '<' . $instance['wrap_link'] . '>';
1057
- $instance['after'] = '</' . $instance['wrap_link'] . '>';
1058
- }
1059
- //wrap_link_text => link_before & link_after...
1060
- $instance['link_before'] = $instance['link_after'] = '';
1061
- $instance['wrap_link_text'] = esc_attr( trim( $instance['wrap_link_text'] ) );
1062
- if( !empty( $instance['wrap_link_text'] ) ){
1063
- $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
1064
- $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
1065
- }
1066
-
1067
- //handle widget_class here because we have full control over $before_widget...
1068
- $before_widget_class = array(
1069
- 'widget_custom_menu_wizard',
1070
- 'shortcode_custom_menu_wizard'
1071
- );
1072
- $instance['widget_class'] = empty( $instance['widget_class'] ) ? '' : esc_attr( trim ( $instance['widget_class'] ) );
1073
- if( !empty( $instance['widget_class'] ) ){
1074
- foreach( explode(' ', $instance['widget_class'] ) as $i ){
1075
- if( !empty( $i ) && !in_array( $i, $before_widget_class ) ){
1076
- $before_widget_class[] = $i;
1077
- }
1078
- }
1079
- }
1080
- $instance['widget_class'] = '';
1081
- //turn on hide_empty...
1082
- $instance['hide_empty'] = 1;
1083
- }
1084
-
1085
- if( $ok ){
1086
- //apart from before_title, these are lifted from the_widget()...
1087
- $sidebar_args = array(
1088
- 'before_widget' => '<div class="' . implode( ' ', $before_widget_class ) . '">',
1089
- 'after_widget' => '</div>',
1090
- 'before_title' => '<h2 class="widgettitle">',
1091
- 'after_title' => '</h2>'
1092
- );
1093
- ob_start();
1094
- the_widget(
1095
- 'Custom_Menu_Wizard_Widget',
1096
- apply_filters('custom_menu_wizard_shortcode_settings', $instance ),
1097
- apply_filters('custom_menu_wizard_shortcode_widget_args', $sidebar_args )
1098
- );
1099
- $html = ob_get_clean();
1100
- }
1101
- return empty($html) ? '' : $html;
1102
-
1103
- } //end shortcode_legacy()
1104
-
1105
- } //end class Custom_Menu_Wizard_Plugin
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1106
 
1107
  }
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: 3.1.5
7
  * Author: Roger Barrett
8
  * Author URI: http://www.wizzud.com/
9
  * License: GPL2+
10
  */
11
  defined( 'ABSPATH' ) or exit();
12
  /*
13
+ * v3.1.5 change log
14
+ * - expanded Title From to allow absolute ancestor levels (besides root) and relative ancestor levels
15
+ * - added a fallback option to switch determination of Current Item from first-found to last-found
16
+ * - added a shortcode attribute that loads an existing widget instance : [cmwizard widget=N/]
17
+ *
18
  * v3.1.4 change log
19
  * - fixed bug : in the shortcode format, the Alternative wasn't actually being used (always ran the default)! thanks corrideat
20
  * - fixed bug : prevent texturization of the shortcode's content (the Alternative, when supplied)
21
  * - added the ability to make the title a link if it is set from a menu item (using Set Title from)
22
+ *
23
  * v3.1.3 change log
24
  * - tweak : css tweak for the assist when in customizer, for WordPress 4.1
25
+ *
26
  * v3.1.2 change log
27
  * - modification of the readme to avoid WordPress truncating documentation under Other Notes
28
  * No code changes.
29
+ *
30
  * v3.1.1 change log
31
  * - fixed bug : only show the allow_all_root setting in the shortcode equivalent if the primary filter is by branch
32
  * - added work-around for occasions when a theme causes de-registration of the widget which prevents the shortcode working in content
33
+ *
34
  * v3.1.0 change log
35
  * - added an Alternative section which takes a cmwizard shortcode and conditionally applies it as an entirely new widget configuration
36
  * - added fallback determination (has to be enabled) for no current item found as using items marked as current_item_parent (first found)
37
  * - fixed bug in determination of home page pagination pages (?paged=2, etc) as home page still being current item
38
  * - fixed bug introduced in v3.0.4 that prevented CMW script loading on the customizer page - when the Widget Customizer plugin is loaded - for WordPress v3.8 and below
39
  * - fixed bug : stop disabling selected fields based on other settings, because this caused the customizer to wipe values that may have been still required
40
+ *
41
  * v3.0.4 change log
42
  * - fixed bug in the display of the "No Current Item!" warning in the "assist"
43
  * - corrected the enabling/disabling of a couple of fields in the widget form, and tweaked the indentation for better responsiveness
44
  * - fixed a bug with accessibility mode when javascript is enabled, and added a warning about the accuracy of the shortcode when javascript is disabled
45
  * - extended the All Root Items inclusion to be a selectable number of levels (as per the Exclusions by Level)
46
+ *
47
  * v3.0.3 change log
48
  * - removed all occurrences of "Plugin " followed by "Name" from everywhere except the main plugin file (this one!) to avoid update() incorrectly reporting "invalid header" when activating straight from installation (rather than from the Plugin admin page)
49
  * - tweak : eliminate the over-use of get_title() when determining the widget title
50
  * - tweak : added self-terminating forward slash to generated shortcodes
51
  * - prepare for WPv4 (avoid deprecated functions)
52
+ *
53
  * v3.0.2 change log
54
  * - fixed bug where the shortcode shown on new instances didn't initially reflect the automatically selected menu
55
+ *
56
  * v3.0.1 change log
57
  * - fixed bug in determination of pre-existing legacy widgets versus brand new widget instances
58
  * - replaced widget property _cmw_allow_legacy_update with a filter, custom_menu_wizard_prevent_legacy_updates : return TRUE to prevent updates of legacy widgets
59
  * - added new filter, custom_menu_wizard_wipe_on_update : return TRUE to cleanse an instance of old settings
60
+ *
61
  * v3.0.0 change log
62
+ * - Major rethink/rewrite : the Children Of filter is now a Branch filter, and the selected menu item becomes the key focus point rather
63
  * than its children. The Levels available for a Branch filter now include relative levels as well as absolute levels, and there are more
64
  * options available for requiring that the menu contains the current menu item. With the exception of some anomalies (edge cases) the
65
+ * output achievable with v2 of the widget is still available with v3. Although there is no automatic upgrade available, v2 is still
66
  * fully supported; however, any new instances of the widget will be created as v3 only. Note that the shortcode for v3 has changed to
67
  * [cmwizard ... ], but, again, v2's [custom_menu_wizard] is still supported! NB: There is no separate 2.1.0 release - it is incorporated
68
  * into this release.
75
  * - compatibile with Widget Customizer plugin, and its implementation in WP 3.9 core
76
  * - added title_tag to shortcode options
77
  * - added findme to shortcode options, [cmwizard findme=1], output restricted to edit_pages capability
78
+ *
79
  * v2.1.0 change log
80
  * - fixed bug where duplicate menu item ids were causing elements to be ignored
81
  * - fixed IE8 bug with levels indentation in assist
85
  * - added collapsible menu structures to dialog, and set fixed position (with toggle back to absolute)
86
  * - added utility to "assist" to locate posts containing a CMW shortcode
87
  * - minimum requirement for WP raised to v3.6
88
+ *
89
  * v2.0.6 change log:
90
  * - modified determination of current item to cope better with multiple occurences (still first-found, but within prioritised groups)
91
  * - replaced display of update information on plugins list with styled request (and link) to read changelog (update info sometimes didn't display, and some considered it "scary" for users)
92
+ *
93
  * v2.0.5 change log:
94
  * - prevent PHP warnings of Undefined index/offset when building $substructure
95
+ *
96
  * v2.0.4 change log:
97
  * - fixed bug where clearing the container field failed to remove the container from the output
98
  * - remove WordPress's menu-item-has-children class (WP v3.7+) when the filtered item no longer has children
99
  * - added automatic selection of the shortcode text when it is clicked
100
  * - tweaked admin styling and javascript for WordPress v3.8
101
+ *
102
  * v2.0.3 change log:
103
  * - fixed bug with missing global when enqueuing scripts and styles for admin page
104
+ *
105
  * v2.0.2 change log:
106
  * - fixed bug where Include Ancestors was not automatically including the Parent
107
  * - fixed bug where the "assist" was incorrectly calculating Depth Relative to Current Item when the current menu item was outside the scope of the Filtered items
108
  * - behaviour change : only recognise the first "current" item found (used to allow subsequent "current" items to override any already encountered)
109
+ *
110
  * v2.0.1 change log:
111
  * - fixed bug that set a specific items filter when it shouldn't have been set, and prevented show-all working
112
+ *
113
  * v2.0.0 change log:
114
  * - 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,
115
  * 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
116
  * be output, starting with the grand-kids (at level 4)
117
+ * - 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
118
  * 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
119
  * backward compatibility.
120
  * - added option for calculating depth relative to current menu item
121
  * - added option allowing list output to be dependent on current menu item being present somewhere in the list
122
  * - refactored the code
123
+ *
124
  * v1.2.2 change log:
125
  * - bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
126
+ *
127
  * v1.2.1 change log:
128
  * - 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)
129
  * - corrected 'show all from start level 1' processing so that custom classes get applied and 'Title from "Current" Item' works (regardless of filter settings)
130
  * - changed the defaults for new widgets such that only the Filter section is open by default; all the others are collapsed
131
  * - updated demo.html and readme.txt, and made demo available from readme
132
+ *
133
  * v1.2.0 change log:
134
  * - added custom_menu_wizard shortcode, to run the widget from within content
135
  * - moved the 'no ancestor' fallback into new Fallback collapsible section, and added a fallback for Current Item with no children
136
  * - fixed bug with optgroups/options made available for the 'Children of' selector after the widget has been saved (also affecting disabled fields & styling)
137
  * - don't include menus with no items
138
  * - updated demo.html
139
+ *
140
  * v1.1.0 change log:
141
  * - added 'Current Root Item' and 'Current Parent Item' to the 'Children of' filter
142
  * - added an Output option to include both the parent item and the parent's siblings (for a successful 'Children of' filter)
150
 
151
  if( !class_exists( 'Custom_Menu_Wizard_Plugin' ) ){
152
 
153
+ //include the widget class and its walker...
154
+ include( plugin_dir_path( __FILE__ ) . 'include/class.widget.php' );
155
+ include( plugin_dir_path( __FILE__ ) . 'include/class.walker.php' );
156
+
157
+ //instantiate...
158
+ add_action( 'plugins_loaded', array( 'Custom_Menu_Wizard_Plugin', 'init' ) );
159
+
160
+ //declare the main plugin class...
161
+ class Custom_Menu_Wizard_Plugin {
162
+
163
+ public static $version = '3.1.5';
164
+ public static $script_handle = 'custom-menu-wizard-plugin-script';
165
+ public static $widget_class = 'Custom_Menu_Wizard_Widget';
166
+ protected static $instance;
167
+
168
+ /**
169
+ * constructor : adds actions
170
+ */
171
+ public function __construct(){
172
+
173
+ add_action( 'widgets_init', array( &$this, 'widget_and_shortcode' ) );
174
+ add_action( 'wp_ajax_cmw-find-shortcodes', array( &$this, 'ajax_find_shortcodes' ) );
175
+ add_action( 'admin_enqueue_scripts', array( &$this, 'register_scripts' ) );
176
+ add_action( 'admin_print_styles-widgets.php', array( &$this, 'enqueue_styles' ) );
177
+ add_action( 'admin_print_scripts-widgets.php', array( &$this, 'enqueue_scripts' ) );
178
+ add_action( 'admin_menu', array( &$this, 'admin_menu' ) );
179
+ //add customizer support...
180
+ add_action( 'customize_controls_enqueue_scripts', array( &$this, 'enqueue_styles' ) );
181
+ add_action( 'customize_controls_enqueue_scripts', array( &$this, 'enqueue_scripts' ) );
182
+ //add doc link to plugin's action links...
183
+ add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( &$this, 'plugin_actions' ), 10, 4 );
184
+
185
+ //add filter to prevent texturisation of cmwizard shortcode (screws up alternatives!)...
186
+ add_filter( 'no_texturize_shortcodes', array( &$this, 'no_texturize_shortcode' ) );
187
+ //add filter for encoding a cmwizard shortcode into instance settings...
188
+ add_filter( 'custom_menu_wizard_encode_shortcode', array( $this, 'encode_shortcode' ), 10, 1 );
189
+ //add filter for sanitizing an alternative shortcode setting...
190
+ add_filter( 'custom_menu_wizard_sanitize_alternative', array( $this, 'sanitize_alternative' ), 10, 1 );
191
+
192
+ } //end __construct()
193
+
194
+ /**
195
+ * hooked into plugins_loaded action : creates the plugin instance
196
+ */
197
+ public static function init(){
198
+
199
+ is_null( self::$instance ) && self::$instance = new self;
200
+ return self::$instance;
201
+
202
+ } //end init()
203
+
204
+ /**
205
+ * hooked into admin_menu action : add action for when an update to this plugin is available
206
+ */
207
+ public function admin_menu(){
208
+
209
+ add_action( 'in_plugin_update_message-' . plugin_basename( __FILE__ ), array( &$this, 'update_message' ), 10, 2 );
210
+
211
+ } //end admin_menu()
212
+
213
+ /**
214
+ * hooked into admin_print_scripts-widgets.php & customize_controls_enqueue_scripts actions : queues scripts needed by the plugin
215
+ */
216
+ public function enqueue_scripts(){
217
+
218
+ //script is pre-registered - see this->register_scripts() - so that it can be localized if need be (like for accessibility mode)
219
+ //BUT on customize screens pre WPv3.9 the script does not get the chance to pre-register before it is
220
+ //asked to enqueue, so this has to check that it is actually registered!...
221
+ if( !wp_script_is( self::$script_handle, 'registered' ) ){
222
+ $this->register_scripts();
223
+ }
224
+ wp_enqueue_script( self::$script_handle );
225
+
226
+ } //end enqueue_scripts()
227
+
228
+ /**
229
+ * hooked into admin_print_styles-widgets.php & customize_controls_enqueue_scripts actions : queues styles needed by the plugin
230
+ */
231
+ public function enqueue_styles(){
232
+ global $wp_scripts;
233
+
234
+ wp_enqueue_style( 'custom-menu-wizard-plugin-styles', plugins_url( '/custom-menu-wizard.css', __FILE__ ), array(), self::$version );
235
+ //if there's no jquery-ui style already registered, register Smoothness...
236
+ if( !wp_style_is( 'jquery-ui', 'registered' ) ) {
237
+ //get the jquery ui core version (default to 1.9.2 if not found)...
238
+ $jquery_ui_version = isset( $wp_scripts->registered['jquery-ui-core']->ver ) ? $wp_scripts->registered['jquery-ui-core']->ver : '1.9.2';
239
+ //register Smoothness theme for the determined ui version...
240
+ // wp_register_style( 'jquery-ui', '//ajax.googleapis.com/ajax/libs/jqueryui/' . $jquery_ui_version . '/themes/smoothness/jquery-ui.css' );
241
+ wp_register_style( 'jquery-ui', '//code.jquery.com/ui/' . $jquery_ui_version . '/themes/smoothness/jquery-ui.css' );
242
+ }
243
+ wp_enqueue_style( 'jquery-ui' );
244
+
245
+ } //end enqueue_styles()
246
+
247
+ /**
248
+ * hooked into plugin_action_links_custom-menu-wizard : adds documentation link to Plugins listing
249
+ */
250
+ public function plugin_actions( $actions, $plugin_file, $plugin_data, $context ) {
251
+
252
+ $doc = array( 'documentation' => '<a href="' . plugins_url( '/doc/cmw-doc.html', __FILE__ ) . '" target="_blank">' . __('Documentation') . '</a>' );
253
+ $actions = array_merge( $actions, $doc );
254
+ return $actions;
255
+
256
+ } //end plugin_actions()
257
+
258
+ /**
259
+ * hooked into admin_enqueue_scripts : registers the plugin script
260
+ */
261
+ public function register_scripts(){
262
+
263
+ $min = defined( 'WP_DEBUG' ) && WP_DEBUG ? '' : '.min';
264
+ wp_register_script( self::$script_handle, plugins_url( "/custom-menu-wizard$min.js", __FILE__ ), array('jquery-ui-dialog'), self::$version, true );
265
+
266
+ } //end register_scripts()
267
+
268
+ /**
269
+ * hooked into in_plugin_update_message-custom-menu-wizard action : request read changelog before updating
270
+ * @param array $plugin_data Plugin metadata
271
+ * @param array $r Metadata about the available plugin update
272
+ */
273
+ public function update_message( $plugin_data, $r ){
274
+
275
+ $url = 'http://wordpress.org/plugins/' . $r->slug. '/changelog/';
276
+ $style = implode( ';', array(
277
+ '-webkit-box-sizing:border-box',
278
+ '-moz-box-sizing:border-box',
279
+ 'box-sizing:border-box',
280
+ 'background-color:#D54E21',
281
+ 'border-radius:2px',
282
+ 'color:#FFFFFF',
283
+ 'display:inline-block',
284
+ 'margin:0',
285
+ 'max-width:100%',
286
+ 'overflow:hidden',
287
+ 'padding:0 0.5em',
288
+ 'text-overflow:ellipsis',
289
+ 'text-shadow:0 1px 0 rgba(0, 0, 0, 0.5)',
290
+ 'vertical-align:text-bottom',
291
+ 'white-space:nowrap'
292
+ ) ) . ';';
293
 
294
  ?>
295
  <p style="<?php echo $style; ?>"><em><?php printf( __('Please <a href="%s" style="color:#FFFFFF;text-decoration:underline;" target="_blank">read the Changelog</a> <strong>before</strong> updating!'), $url ); ?></em></p>
296
  <?php
297
 
298
+ } //end update_message()
299
+
300
+ /**
301
+ * hooked into wp_ajax_cmw-find-shortcodes action : handle ajax request to find posts containing CMW shortcodes, returning XML
302
+ */
303
+ public function ajax_find_shortcodes(){
304
+
305
+ check_admin_referer( 'cmw-find-shortcodes' );
306
+ $response = array(
307
+ 'what' => 'cmw_find_shortcodes',
308
+ 'action' => 'list_posts',
309
+ 'id' => '1',
310
+ 'data' => $this->find_shortcodes()
311
+ );
312
+ $xmlResponse = new WP_Ajax_Response($response);
313
+ $xmlResponse->send();
314
+
315
+ }
316
+
317
+ /**
318
+ * list any post that contains a CMW shortcode; can be called from a shortcode or via an ajax call
319
+ *
320
+ * @param array $shortcodeInst Array of shortcode attributes
321
+ * @return string HTML
322
+ */
323
+
324
+ public function find_shortcodes( $shortcodeInst = false ){
325
+ global $wpdb;
326
+
327
+ $html = '';
328
+
329
+ //from a shortcode, the user must have edit_pages capability (implies Editor or above)...
330
+ if( $shortcodeInst !== false && !current_user_can( 'edit_pages' ) ){
331
+ return $html;
332
+ }
333
+
334
+ $codes = array(
335
+ 'cmw-demo-found-old' => '[custom_menu_wizard',
336
+ 'cmw-demo-found-new' => '[cmwizard'
337
+ );
338
+ foreach( $codes as $k => $v ){
339
+ $j = str_replace( '-', '_', $k );
340
+ //like_escape deprecated in v4...
341
+ if( method_exists( $wpdb, 'esc_like' ) ){
342
+ $$j = '%' . $wpdb->esc_like( $v ) . '%';
343
+ }else{
344
+ $$j = '%' . like_escape( esc_sql ( $v ) ) . '%';
345
+ }
346
+ }
347
+
348
+ //search in all custom fields...
349
+ $sql = "SELECT DISTINCT post_id FROM {$wpdb->postmeta}";
350
+ $sql .= " WHERE meta_value LIKE '%s' OR meta_value LIKE '%s'";
351
+ $post_ids_meta = $wpdb->get_col( $wpdb->prepare( $sql, $cmw_demo_found_old, $cmw_demo_found_new ) );
352
+ //search in post_content...
353
+ $sql = "SELECT DISTINCT ID FROM {$wpdb->posts}";
354
+ $sql .= " WHERE post_content LIKE '%s' OR post_content LIKE '%s'";
355
+ $post_ids_post = $wpdb->get_col( $wpdb->prepare( $sql, $cmw_demo_found_old, $cmw_demo_found_new ) );
356
+
357
+ $post_ids = array_merge( $post_ids_meta, $post_ids_post );
358
+
359
+ if( empty( $post_ids ) ){
360
+ $html .= '<p>' . __('No CMW shortcodes found.') . '</p>';
361
+ }else{
362
+ $args = array(
363
+ 'ignore_sticky_posts' => true,
364
+ 'nopaging' => true,
365
+ 'orderby' => 'date',
366
+ 'post_type' => 'any',
367
+ 'post_status' => array( 'publish', 'draft', 'future', 'pending', 'private' ),
368
+ 'post__in' => $post_ids
369
+ );
370
+
371
+ $the_query = new WP_Query( $args );
372
+ if( $the_query->have_posts() ){
373
+ $html .= '<dl>';
374
+ while( $the_query->have_posts() ){
375
+ $the_query->the_post();
376
+ $id = get_the_ID();
377
+ $inPost = in_array( $id, $post_ids_post );
378
+ $inMeta = in_array( $id, $post_ids_meta );
379
+ $dtClass = array();
380
+ $anchorTitle = array();
381
+ if( $inPost ){
382
+ $content = get_the_content();
383
+ foreach( $codes as $k => $v ){
384
+ if( strpos( $content, $v ) !== false ){
385
+ $dtClass[ $k ] = 1;
386
+ $anchorTitle[ $v . ']' ] = 1;
387
+ }
388
+ }
389
+ }
390
+ if( $inMeta ){
391
+ $content = get_post_meta( $id );
392
+ foreach( $content as $k => $v ){
393
+ $content[ $k ] = implode(' ', $v );
394
+ }
395
+ $content = implode( ' ', $content );
396
+ foreach( $codes as $k => $v ){
397
+ if( strpos( $content, $v ) !== false ){
398
+ $dtClass[ $k ] = 1;
399
+ $anchorTitle[ $v . ']' ] = 1;
400
+ }
401
+ }
402
+ }
403
+ $anchorTarget = get_post_type( $id );
404
+ if( empty( $anchorTarget ) ){
405
+ $anchorTarget = __('unknown type');
406
+ }else{
407
+ $anchorTarget = (string)$anchorTarget;
408
+ }
409
+ $content = $inPost ? ( $inMeta ? __( 'content+meta' ) : __( 'content' ) ) : __( 'meta' );
410
+ $anchorTitle = $anchorTarget . ' #' . $id . ', ' . $content . ', ' . implode( __(' and '), array_keys( $anchorTitle ) );
411
+ $anchorTarget = '';
412
+ if( $shortcodeInst === false ){
413
+ //is from assistant via ajax...
414
+ $anchorTarget = 'target="_blank"';
415
+ $anchorTitle .= ' ... ' . __('opens new tab/window');
416
+ }
417
+ $html .= '<dt class="' . implode( ' ', array_keys( $dtClass ) ) . '"><a href="' . get_permalink() . '" ' . $anchorTarget . ' title="' . $anchorTitle . '">' . get_the_title() . '</a></dt>';
418
+ }
419
+ $html .= '</dl>';
420
+ }else{
421
+ $html .= '<p>' . __('No CMW shortcodes found.') . '</p>';
422
+ }
423
+
424
+ wp_reset_postdata();
425
+ }
426
+
427
+ //if originator is shortcode, put a simple wrapper (no styling!) around the results, and optionally an H3 title...
428
+ if( $shortcodeInst !== false ){
429
+ $anchorTitle = is_array( $shortcodeInst ) && !empty( $shortcodeInst['title'] ) ? esc_attr( strip_tags( trim( $shortcodeInst['title'] ) ) ) : '';
430
+ $anchorTitle = empty( $anchorTitle ) ? '' : '<h3>' . $anchorTitle . '</h3>';
431
+ $html = '<div class="cmw-list-posts-with-shortcodes">' . $anchorTitle . $html . '</div>';
432
+ }
433
+
434
+ return $html;
435
+
436
+ } //end find_shortcodes()
437
+
438
+ /**
439
+ * hooked into widgets_init action : registers widget and adds shortcode(s)
440
+ */
441
+ public function widget_and_shortcode(){
442
+
443
+ //register the widget class...
444
+ register_widget( self::$widget_class );
445
+ //add shortcode...
446
+ add_shortcode( 'cmwizard', array( &$this, 'shortcode' ) );
447
+ //add shortcode, v2.1.0 version (deprecated!)...
448
+ add_shortcode( 'custom_menu_wizard', array( &$this, 'shortcode_legacy' ) );
449
+
450
+ } //end widget_and_shortcode()
451
+
452
+ /**
453
+ * hooked into custom_menu_wizard_sanitize_alternative filter : sanitizes an alternative shortcode setting
454
+ * used by this->shortcode_instance, and the widget class
455
+ *
456
+ * @param string $alt Alternative (switch_to setting)
457
+ * @return string
458
+ */
459
+ public function sanitize_alternative( $alt = '' ){
460
+
461
+ if( empty( $alt ) || !is_string( $alt ) ){
462
+ return '';
463
+ }
464
+
465
+ //kill containing square brackets, self-terminators and spaces, then split on square bracket...
466
+ $alt = preg_split( '/[\[\]]/', trim( $alt, ' []/' ) );
467
+ //use the first element, kill tabs, CRLFs and multiple spaces, and retrim for self-terminators and spaces...
468
+ $alt = trim( preg_replace( array( '/[\r\n\t]+/', '/\s\s+/' ), ' ', $alt[0] ), ' /' );
469
+ //remove leading 'cmwizard' tag...
470
+ $alt = preg_replace( '/^cmwizard\s/', '', $alt . ' ' );
471
+ //remove any occurrences of 'menu=whatever' and 'alternative="whatever"' (optional double quotes), and trim spaces...
472
+ $alt = trim( preg_replace( array('/\smenu=[^\s]*\s/', '/\salternative=("[^"]*"|[^\s]*)\s/' ), ' ', ' ' . $alt . ' ' ) );
473
+
474
+ return $alt;
475
+
476
+ } //end sanitize_alternative()
477
+
478
+ /**
479
+ * hooked into no_texturize_shortcodes filter : prevents texturisation of cmwizard shortcode
480
+ *
481
+ * @param array $arr Array of shortcodes
482
+ * @return array
483
+ */
484
+ public function no_texturize_shortcode( $arr = false ){
485
+ return empty( $arr ) ? array('cmwizard') : array_merge( (array)$arr, array('cmwizard') );
486
+ }
487
+
488
+ /**
489
+ * shortcode processing for [cmwizard option="" option="" ...] (as of v3.0.0)
490
+ *
491
+ * see wp-includes/widgets.php for the_widget() code
492
+ * Note that hide_empty is set to ON and can not be overridden!
493
+ *
494
+ * differences from [custom_menu_wizard] shortcode (ie. v2.1.0)
495
+ * deprecated:
496
+ * - children_of : now branch, and limited to current[-item] or digits; parent|current-parent|root|current-ancestor all require conversion
497
+ * - start_level : now level (integer) for a by-level filter, or start_at (string) for a by-branch filter (determining branch_start)
498
+ * - include_root : (as of v3.0.4) replaced by include_level (like exclude_level); include_root On equiv. is include_level == '1'
499
+ * changed:
500
+ * - contains_current : was a switch, now a string (empty or menu|primary|secondary|output); switch ON = 'output'
501
+ * - include : now accepts siblings, ancestors and/or ancestor-siblings (csv); parent is gone, and hyphen separator no longer allowed
502
+ * - title_from : should now be csv, hyphen separator no longer allowed
503
+ * added:
504
+ * - title_tag & findme
505
+ * - include_level (v3.0.4)
506
+ *
507
+ * default (ie. no options) is:
508
+ * - show all
509
+ * - of first populated menu found (alphabetically)
510
+ * - from root, for unlimited depth
511
+ * - as hierarchical nested ULs inside a DIV.widget_custom_menu_wizard.shortcode_custom_menu_wizard
512
+ *
513
+ * @filters : custom_menu_wizard_shortcode_attributes array of attributes (unfiltered!) supplied to the shortcode
514
+ * shortcode_atts_cmwizard array of output attributes, array of supported attributes, array of supplied attributes
515
+ * custom_menu_wizard_shortcode_settings array of widget settings derived from the attributes
516
+ * custom_menu_wizard_shortcode_widget_args array of the sidebar args used to wrap widgets and their titles (before|after_widget, before|after_title)
517
+ * NB each of the arrays passed to the above filters has a extra key-value pair of 'cmwv' => the current plugin version, eg. '3.0.0'
518
+ *
519
+ * @param array $atts options supplied to the shortcode
520
+ * @param string $content Within start-end shortcode tags
521
+ * @param string $tag Shortcode tag
522
+ * @return string HTML that comes from running the_widget()
523
+ */
524
+ public function shortcode($atts, $content, $tag){
525
+
526
+ //if widget isn't registered(!?), try re-registering; if still not registered, cop out...
527
+ if( !$this->widget_registered() ){
528
+ if( did_action( 'widgets_init' ) > 0 ){
529
+ if( apply_filters( 'custom_menu_wizard_widget_reregister', true ) ){
530
+ //re-register the widget...
531
+ register_widget( self::$widget_class );
532
+ if( !$this->widget_registered() ){
533
+ return WP_DEBUG ? __('[cmwizard PROBLEM="widget de-registered, and failed to re-register!"/]') : '';
534
+ }
535
+ }else{
536
+ return WP_DEBUG ? __('[cmwizard PROBLEM="widget de-registered, and not allowed to re-register!"/]') : '';
537
+ }
538
+ }else{
539
+ //hasn't had a chance to register yet!...
540
+ return WP_DEBUG ? __('[cmwizard PROBLEM="widgets have not been initialised yet!"/]') : '';
541
+ }
542
+ }
543
+
544
+ $html = '';
545
+ $instance = $this->shortcode_instance( $atts, $tag, $content, true );
546
+ $ok = !empty( $instance );
547
+
548
+ if( $ok && !empty( $instance['findme'] ) ){
549
+ //return the findme output...
550
+ return $this->find_shortcodes( $instance );
551
+ }
552
+
553
+ if( $ok ){
554
+
555
+ //handle widget_class here because we have full control over $before_widget...
556
+ $before_widget_class = array(
557
+ 'widget_custom_menu_wizard',
558
+ 'shortcode_custom_menu_wizard'
559
+ );
560
+ $instance['widget_class'] = empty( $instance['widget_class'] ) ? '' : esc_attr( trim ( $instance['widget_class'] ) );
561
+ if( !empty( $instance['widget_class'] ) ){
562
+ foreach( explode(' ', $instance['widget_class'] ) as $i ){
563
+ if( !empty( $i ) && !in_array( $i, $before_widget_class ) ){
564
+ $before_widget_class[] = $i;
565
+ }
566
+ }
567
+ }
568
+ unset( $instance['widget_class'] );
569
+ }
570
+
571
+ if( $ok ){
572
+ //not used by the plugin, but could be used in the widget code to tell whether it was being
573
+ //run as a result of a widget or a shortcode?...
574
+ $instance['shortcode'] = true;
575
+ //allow the element that wraps the widget title to be changed from an h2 (the WP default) to another tag...
576
+ //note : does not allow for changing the class, or for removing the wrapping element
577
+ // for a class override, add CSS rule for
578
+ // .shortcode_custom_menu_wizard .widgettitle { ..... }
579
+ // can also be overriden using the 'custom_menu_wizard_shortcode_widget_args' filter (applied below)
580
+ $instance['title_tag'] = esc_attr( trim( $instance['title_tag'] ) );
581
+ if( empty( $instance['title_tag'] ) ){
582
+ //default to H2...
583
+ $instance['title_tag'] = 'h2';
584
+ }
585
+ //apart from before_widget, these are lifted from the_widget() (wp-includes/widgets.php)...
586
+ $sidebar_args = array(
587
+ 'before_widget' => '<div class="' . implode( ' ', $before_widget_class ) . '">',
588
+ 'after_widget' => '</div>',
589
+ 'before_title' => '<' . $instance['title_tag'] . ' class="widgettitle">',
590
+ 'after_title' => '</' . $instance['title_tag'] . '>'
591
+ );
592
+ unset( $instance['title_tag'] );
593
+
594
+ ob_start();
595
+ the_widget(
596
+ self::$widget_class,
597
+ apply_filters(
598
+ 'custom_menu_wizard_shortcode_settings',
599
+ array_merge( $instance, array('cmwv' => self::$version) )
600
+ ),
601
+ apply_filters(
602
+ 'custom_menu_wizard_shortcode_widget_args',
603
+ array_merge( $sidebar_args, array('cmwv' => self::$version) )
604
+ ) );
605
+ $html = ob_get_clean();
606
+ }
607
+
608
+ return empty( $html ) ? '' : $html;
609
+
610
+ } //end shortcode()
611
+
612
+ /**
613
+ * does most of the attribute processing/checking for the cmwizard (only) shortcode
614
+ * is called from shortcode() method *AND* encode_shortcode() method, which is run from a filter enabling
615
+ * settings to be changed at start (after determination of current item) of the widget's walker process.
616
+ *
617
+ * @param array $atts options supplied to the shortcode
618
+ * @param string $tag Shortcode tag
619
+ * @param string $content Within start-end shortcode tags
620
+ * @param boolean $doShortcode True if called from shortcode(), false otherwise
621
+ * @return array|boolean A set of widget instance settings, or false if shortcode is invalid
622
+ */
623
+ public function shortcode_instance( $atts, $tag, $content = '', $doShortcode = false ){
624
+
625
+ if( $doShortcode ){
626
+ $instance = shortcode_atts(
627
+ array(
628
+ //utility : doesn't run widget! instead, lists all posts/pages that contain a CMW shortcode...
629
+ 'findme' => 0,
630
+ 'title' => '' //used (if provided) by findme
631
+ ),
632
+ (array)$atts
633
+ );
634
+ if( !empty( $instance['findme'] ) ){
635
+ return $instance;
636
+ }
637
+ }
638
+
639
+ //the philosophy behind loading an existing widget instance from a shortcode is to (a) find the
640
+ //instance, (b) sanitize its settings, (c) get its shortcode equivalent, (d) parse the resulting
641
+ //shortcode for its attributes and content (as WP would), and (e) allow the original shortcode
642
+ //to overwrite/add to any of those attributes. Seems long-winded but it's the simplest way of
643
+ //allowing modification of the existing widget, because all the code for (b) and (c) is already
644
+ //in place, so I only need to provide (a) and (d) and then do an array_merge right here for (e).
645
+ if( $doShortcode && array_key_exists( 'widget', (array)$atts ) && !empty( $atts['widget'] )
646
+ && ( $originalParams = $this->widget_fetch_settings( $atts ) ) !== false ){
647
+ //seems we found the widget...
648
+ //I'm going to allow *any* overrides!
649
+ $atts = array_merge( $originalParams['atts'], (array)$atts );
650
+ //NB the switch-to setting (in $content) only overrides the original widget's setting if
651
+ // it is not empty, so if the original widget has a switch-to, and the shortcode needs to
652
+ // override that switch-to to be empty (which is the widget's equiv. of "output everything")
653
+ // then it is necessary to override with at least one setting even though the widget would
654
+ // not normally need it (eg. [cmwizard widget=N ... alternative="no-output,output"]level=1[/cmwizard])
655
+ if( empty( $content ) && !is_null( $originalParams['content'] ) ){
656
+ $content = $originalParams['content'];
657
+ }
658
+ }
659
+
660
+ $ok = false;
661
+
662
+ // NB csv = comma or space separated list...
663
+ $defaults = array(
664
+ 'title' => '',
665
+ 'menu' => 0, // menu id, slug or name
666
+ 'level' => 0,
667
+ //determines filter (in conjunction with items)...
668
+ 'branch' => 0, // a menu item id, or current|current-item
669
+ //determines filter (in conjunction with branch)...
670
+ 'items' => '', // csv of menu item ids (an id may have a '+' appended, for inheritance, eg. '23+')
671
+ 'depth' => 0, // 0 = unlimited
672
+ 'depth_rel_current' => 0,
673
+ //determines branch_start...
674
+ 'start_at' => '',
675
+ 'start_mode' => '', // 'level' or empty
676
+ 'allow_all_root' => 0,
677
+ //inclusions...
678
+ 'ancestors' => 0, //integer (negative = relative)
679
+ 'ancestor_siblings' => 0, //integer (negative = relative)
680
+ 'include_root' => 0, //switch (means *all* root items!) v3.0.4 DEPRECATED still allowed (for back compat.), equiv. is include_level='1'
681
+ 'include_level' => '', // v3.0.4 digit, possibly appended with a '+' or '-', eg. '2', '2+', or '2-'
682
+ 'siblings' => 0, //switch
683
+ //exclusions...
684
+ 'exclude' => '', // csv of menu item ids (an id may have a '+' appended, for inheritance, eg. '23+')
685
+ 'exclude_level' => '', // digit, possibly appended with a '+' or '-', eg. '2', '2+', or '2-'
686
+ 'contains_current' => '', // menu|primary|secondary|inclusions|output
687
+ //determines fallback (current|parent|quit) and, optionally, fallback_siblings and/or fallback_depth...
688
+ 'fallback' => '', //eg. 'quit', or 'current' or 'current+siblings' or 'parent+siblings,2' or 'parent,1'
689
+ //switches...
690
+ 'flat_output' => 0,
691
+ //determines title_[branch|current]...
692
+ 'title_from' => '', // csv of branch|current|branch-root|current-root; v3.1.5 also allows [branch|current][-parent|signed? digit], eg. current-parent, current1 or branch-2
693
+ 'title_linked' => 0, //v3.1.4
694
+ 'ol_root' => 0,
695
+ 'ol_sub' => 0,
696
+ //strings...
697
+ 'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
698
+ 'container_id' => '',
699
+ 'container_class' => '',
700
+ 'menu_class' => 'menu-widget',
701
+ 'widget_class' => '',
702
+ //determines before & after...
703
+ 'wrap_link' => '', // a tag name (eg. div, p, span, etc)
704
+ //determines link_before & link_after...
705
+ 'wrap_link_text' => '' // a tag name (eg. span, em, strong)
706
+ //modifies the before/after_title $sidebar_args, changing the default H2 tag to something else(?)...
707
+ );
708
+ //if *not* decoding a main shortcode then we're looking at an alternative, and alternatives can't be nested,
709
+ //nor can they change the title's tag element, so only add these in if we *are* running shortcode...
710
+ if( $doShortcode ){
711
+ $defaults = array_merge( $defaults, array(
712
+ //determines switch_if, switch_at & switch_to (depending on $content)...
713
+ 'alternative' => '', //csv of current|no-current|no-output and menu|primary|secondary|inclusions|output, eg. 'current,menu'
714
+ //modifies the before/after_title $sidebar_args, changing the default H2 tag to something else(?)...
715
+ 'title_tag' => '' // a tag name (eg. h1, h3, etc)
716
+ ));
717
+ }
718
+
719
+ $instance = shortcode_atts(
720
+ $defaults,
721
+ $doShortcode
722
+ ? apply_filters(
723
+ 'custom_menu_wizard_shortcode_attributes',
724
+ array_merge( (array)$atts, array('cmwv' => self::$version) )
725
+ )
726
+ : (array)$atts,
727
+ $doShortcode
728
+ ? $tag // since WP3.6 this allows use of shortcode_atts_cmwizard filter, applied by shortcode_atts()
729
+ : ''
730
+ );
731
+
732
+ //in order of priority...
733
+ $byItems = !empty( $instance['items'] );
734
+ $byBranch = !$byItems && !empty( $instance['branch'] );
735
+ $byLevel = !$byItems && !$byBranch;
736
+
737
+ if( empty( $instance['menu'] ) ){
738
+ //gonna find the first menu (alphabetically) that has items...
739
+ $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
740
+ }else{
741
+ //allow for menu being something other than an id (eg. slug or name), but we need the id for the widget...
742
+ $menus = wp_get_nav_menu_object( $instance['menu'] );
743
+ if( !empty( $menus) ){
744
+ $menus = array( $menus );
745
+ }
746
+ }
747
+ if( !empty( $menus ) ){
748
+ foreach( $menus as $i=>$menu ){
749
+ $items = wp_get_nav_menu_items( $menu->term_id );
750
+ $ok = !empty( $items );
751
+ if( $ok ){
752
+ $instance['menu'] = $menu->term_id;
753
+ break;
754
+ }
755
+ }
756
+ }
757
+ unset( $menus );
758
+
759
+ if( $ok ){
760
+ if( $byItems ){
761
+ $instance['filter'] = 'items';
762
+ }
763
+ if( $byBranch ){
764
+ $instance['filter'] = 'branch';
765
+ switch( "{$instance['start_at']}" ){
766
+ case '0':
767
+ case 'branch': $instance['branch_start'] = ''; break;
768
+ case 'root' : $instance['branch_start'] = '1'; break;
769
+ case 'children': $instance['branch_start'] = '+1'; break;
770
+ case 'parent': $instance['branch_start'] = '-1'; break;
771
+ default: $instance['branch_start'] = "{$instance['start_at']}";
772
+ }
773
+ if( $instance['branch'] == 'current' || $instance['branch'] == 'current-item' ){
774
+ $instance['branch'] = 0;
775
+ }elseif( !is_numeric( $instance['branch'] ) ){
776
+ //if branch is non-numeric then it could be the title of a menu item, but we need it to be the menu item's id...
777
+ $instance['branch'] = strtolower( $instance['branch'] );
778
+ foreach( $items as $item ){
779
+ $ok = strtolower( $item->title ) == $instance['branch'];
780
+ if( $ok ){
781
+ $instance['branch'] = $item->ID;
782
+ break;
783
+ }
784
+ }
785
+ }
786
+ }
787
+ if( $byLevel ){
788
+ $instance['filter'] = '';
789
+ $instance['level'] = max(1, intval( $instance['level'] ) );
790
+ }
791
+ unset( $instance['start_at'] );
792
+ }
793
+
794
+ if( $ok ){
795
+ //include_level, and the deprecated include_root switch...
796
+ //if level is empty but root is set, set include_level to '1'...
797
+ if( empty( $instance['include_level'] ) && $instance['include_root'] ){
798
+ $instance['include_level'] = '1';
799
+ }
800
+ unset( $instance['include_root'] );
801
+ //fallback => fallback and fallback_siblings and fallback_depth...
802
+ //allows "X", "X,Y" or "X,Y,Z" where comma could be space, and X|Y|Z could be "quit"|"current"|"parent", or "+siblings", or digit(s)
803
+ //but "quit", "current" or "parent" must be present (others are optional)
804
+ if( $byBranch && empty( $instance['branch'] ) && !empty( $instance['fallback'] ) ){
805
+ $i = preg_split( '/[\s,]+/', strtolower( $instance['fallback'] ), -1, PREG_SPLIT_NO_EMPTY );
806
+ $instance['fallback'] = '';
807
+ if( in_array( 'quit', $i ) ){
808
+ $instance['fallback'] = 'quit';
809
+ }elseif( in_array( 'parent', $i ) ){
810
+ $instance['fallback'] = 'parent';
811
+ }elseif( in_array( 'current', $i ) ){
812
+ $instance['fallback'] = 'current';
813
+ }
814
+ if( !empty( $instance['fallback'] ) && $instance['fallback'] != 'quit' ){
815
+ if( in_array( '+siblings', $i ) ){
816
+ $instance['fallback_siblings'] = 1;
817
+ }
818
+ $i = array_diff( $i, array( 'quit', 'parent', 'current', '+siblings' ) );
819
+ if( !empty( $i ) ){
820
+ foreach( $i as $v ){
821
+ $v = trim( $v );
822
+ if( preg_match( '/^\d+$/', $v ) > 0 && intval( $v ) > 0 ){
823
+ $instance['fallback_depth'] = intval( $v );
824
+ break;
825
+ }
826
+ }
827
+ }
828
+ }
829
+ }
830
+ }
831
+
832
+ if( $ok ){
833
+ //3.1.5 : title_from => title_...
834
+ // 'branch' => title_branch='0'
835
+ // 'branch-1' => title_branch='-1' (relative)
836
+ // 'branch1' or 'branch+1' => title_branch='1' (absolute)
837
+ // 'branch-root' => title_branch='1' (absolute, back compat)
838
+ // 'branch-parent' => title_branch='-1' (relative, addition)
839
+ //similarly for current*
840
+ if( !empty( $instance['title_from'] ) ){
841
+ $i = preg_split( '/[\s,]+/', strtolower( $instance['title_from'] ), -1, PREG_SPLIT_NO_EMPTY );
842
+ foreach( $i as $j ){
843
+ if( !empty( $j ) ){
844
+ if( $j == 'branch' || $j == 'current' ){
845
+ $instance[ 'title_' . $j ] = '0';
846
+ }elseif( preg_match( '/^(branch|current)(-root|-parent|[+\-]?\d+)$/', $j, $m ) > 0 ){
847
+ if( $m[2] == '-root' ){
848
+ $v = 1;
849
+ }elseif( $m[2] == '-parent' ){
850
+ $v = -1;
851
+ }else{
852
+ $v = intval( $m[2] );
853
+ }
854
+ if( !empty( $v ) ){
855
+ $instance[ 'title_' . $m[1] ] = "$v";
856
+ }
857
+ }
858
+ }
859
+ }
860
+ }
861
+ unset( $instance['title_from'] );
862
+
863
+ //wrap_link => before & after...
864
+ $instance['wrap_link'] = esc_attr( trim( $instance['wrap_link'] ) );
865
+ if( !empty( $instance['wrap_link'] ) ){
866
+ $instance['before'] = '<' . $instance['wrap_link'] . '>';
867
+ $instance['after'] = '</' . $instance['wrap_link'] . '>';
868
+ }
869
+ unset( $instance['wrap_link'] );
870
+
871
+ //wrap_link_text => link_before & link_after...
872
+ $instance['wrap_link_text'] = esc_attr( trim( $instance['wrap_link_text'] ) );
873
+ if( !empty( $instance['wrap_link_text'] ) ){
874
+ $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
875
+ $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
876
+ }
877
+ unset( $instance['wrap_link_text'] );
878
+
879
+ //alternative => switch_if, switch_at & switch_to...
880
+ if( !empty( $instance['alternative'] ) ){
881
+ $i = preg_split( '/[\s,]+/', strtolower( $instance['alternative'] ), -1, PREG_SPLIT_NO_EMPTY );
882
+ foreach( $i as $j ){
883
+ if( in_array( $j, array('current', 'no-current', 'no-output' ) ) ){
884
+ $instance['switch_if'] = $j;
885
+ }elseif( in_array( $j, array('menu', 'primary', 'secondary', 'inclusions', 'output') ) ){
886
+ $instance['switch_at'] = $j;
887
+ }
888
+ }
889
+ if( !empty( $instance['switch_if'] ) && !empty( $instance['switch_at'] ) ){
890
+ //v3.1.4 : fixed bug where $content - the alternative shortcode - wasn't being passed into the filter...
891
+ $instance['switch_to'] = apply_filters( 'custom_menu_wizard_sanitize_alternative', $content );
892
+ }else{
893
+ $instance['switch_if'] = $instance['switch_at'] = $instance['switch_to'] = '';
894
+ }
895
+ unset( $instance['alternative'] );
896
+ }
897
+
898
+ //turn on hide_empty...
899
+ $instance['hide_empty'] = 1;
900
+ }
901
+
902
+ return $ok ? $instance : false;
903
+
904
+ } //end shortcode_instance()
905
+
906
+ /**
907
+ * hooked into custom_menu_wizard_encode_shortcode filter : converts a cmwizard shortcode into instance settings fit for
908
+ * the widget() method of Custom_Menu_Wizard_Widget
909
+ *
910
+ * it's important to note that a shortcode processed this way does *NOT* hit the filters that a cmwizard shortcode would
911
+ * normally hit, namely custom_menu_wizard_shortcode_attributes & shortcode_atts_cmwizard
912
+ *
913
+ * @param string $shortcode A full [cmwizard .../] shortcode
914
+ * @return array|boolean Instance settings, or false if error
915
+ */
916
+ public function encode_shortcode( $shortcode = '' ){
917
+
918
+ if( class_exists( self::$widget_class ) &&
919
+ preg_match( '/^cmwizard\s?(.*)$/', rtrim( ltrim( $shortcode, '[ ' ), '] /' ), $m ) > 0 ){
920
+ $instance = $this->shortcode_instance( shortcode_parse_atts( trim( $m[1] ) ), 'cmwizard' );
921
+ if( !empty( $instance ) ){
922
+ $instance['cmwv'] = self::$version;
923
+ $instance = Custom_Menu_Wizard_Widget::cmw_settings( $instance, false, 'widget' );
924
+ }
925
+ }
926
+ return empty( $instance ) ? false : $instance;
927
+
928
+ } //end encode_shortcode()
929
+
930
+ /**
931
+ * checks that the widget is registered
932
+ *
933
+ * @return boolean True if registered
934
+ */
935
+ public function widget_registered(){
936
+ global $wp_widget_factory;
937
+
938
+ return ( isset( $wp_widget_factory->widgets[ self::$widget_class ] ) &&
939
+ is_a( $wp_widget_factory->widgets[ self::$widget_class ], self::$widget_class ) );
940
+
941
+ }
942
+
943
+ /**
944
+ * fetches the settings, transformed into shortcode parameters, for a specific instance of a widget
945
+ *
946
+ * @param array $atts Shortcode attributes
947
+ * @return array New parameters for the shortcode (atts and content), or false if error or not found
948
+ */
949
+ public function widget_fetch_settings( $atts ){
950
+ global $wp_registered_widgets, $shortcode_tags;
951
+
952
+ if( empty( $atts ) || !is_array( $atts ) || !array_key_exists( 'widget', $atts ) ){
953
+ return false;
954
+ }
955
+ //the id must be numeric (and positive!) and I need the widget class and shortcode tags...
956
+ if( !empty( $atts['widget'] ) && is_numeric( $atts['widget'] )
957
+ && class_exists( self::$widget_class ) && !empty( $shortcode_tags ) ){
958
+ $id = intval( $atts['widget'] );
959
+ if( $id > 0 ){
960
+ //make the full id...
961
+ $widget_id = 'custom-menu-wizard-' . intval( $id );
962
+ //get the widget instances for all sidebars...
963
+ $sidebars_widgets = wp_get_sidebars_widgets();
964
+
965
+ if( is_array( $sidebars_widgets ) ){
966
+ //by default, DON'T look in orphaned sidebars - but can be turned on...
967
+ $orphaned = array_key_exists( 'orphaned', $atts ) && !empty( $atts['orphaned'] );
968
+ //by default, DO look in WP's inactive sidebar - but can be turned off...
969
+ $inactive = !array_key_exists( 'inactive', $atts ) || !empty( $atts['inactive'] );
970
+ //search through all widget instances for one with our full id...
971
+ foreach( $sidebars_widgets as $sidebar => $widgets ){
972
+ //check whether we should be including inactive/orphaned sidebars in our search...
973
+ if( ( $inactive || $sidebar !== 'wp_inactive_widgets' )
974
+ && ( $orphaned || substr( $sidebar, 0, 16 ) !== 'orphaned_widgets' )
975
+ && is_array( $widgets )
976
+ && in_array( $widget_id, $widgets )
977
+ && isset( $wp_registered_widgets[ $widget_id ] ) ){
978
+ //found it : get its option settings...
979
+ $settings = get_option( $wp_registered_widgets[ $widget_id ]['callback'][0]->option_name );
980
+ if( !empty( $settings ) && array_key_exists( $id, $settings )
981
+ && !empty( $settings[ $id ] ) && is_array( $settings[ $id ] ) ){
982
+ //seems we have a widget! :
983
+ //...create a regex pattern using WP's standard but replacing all shortcodes with just ours...
984
+ $repl = array_keys( $shortcode_tags );
985
+ $repl = join( '|', array_map( 'preg_quote', $repl ) );
986
+ $pattern = str_replace( "($repl)", '(cmwizard)', get_shortcode_regex() );
987
+ //...sanitize the settings, create the equivalent shortcode, then match
988
+ // against the modified pattern to get attributes and content...
989
+ if( preg_match( "/$pattern/s", Custom_Menu_Wizard_Widget::cmw_shortcode( Custom_Menu_Wizard_Widget::cmw_settings( $settings[ $id ] ) ), $m ) > 0 ){
990
+ //...and return them
991
+ return array(
992
+ 'atts' => shortcode_parse_atts( trim( $m[3] ) ),
993
+ 'content' => isset( $m[5] ) ? $m[5] : null
994
+ );
995
+ }
996
+ }else{
997
+ //seems there are no valid option settings for this widget instance!...
998
+ break;
999
+ }
1000
+ }
1001
+ }
1002
+ }
1003
+ }
1004
+ }
1005
+ return false;
1006
+
1007
+ }
1008
+
1009
+ /**
1010
+ * shortcode processing for [custom_menu_wizard option="" option="" ...] (as of v2.1.0)
1011
+ * see wp-includes/widgets.php for the_widget() code
1012
+ * Note that hide_empty is set to ON and can not be overridden!
1013
+ *
1014
+ * default (ie. no options) is:
1015
+ * - show all
1016
+ * - of first populated menu found (alphabetically)
1017
+ * - from root, for unlimited depth
1018
+ * - as hierarchical nested ULs inside a DIV.widget_custom_menu_wizard.shortcode_custom_menu_wizard
1019
+ *
1020
+ * @filters : custom_menu_wizard_shortcode_attributes array of attributes supplied to the shortcode
1021
+ * custom_menu_wizard_shortcode_settings array of widget settings derived from the attributes
1022
+ * custom_menu_wizard_shortcode_widget_args array of the sidebar args used to wrap widgets and their titles (before|after_widget, before|after_title)
1023
+ *
1024
+ * @param array $atts options supplied to the shortcode
1025
+ * @param string $content Within start-end shortcode tags
1026
+ * @param string $tag Shortcode tag
1027
+ * @return string HTML that comes from running the_widget()
1028
+ */
1029
+ function shortcode_legacy($atts, $content, $tag){
1030
+ $html = '';
1031
+ $ok = false;
1032
+ $instance = shortcode_atts( array(
1033
+ 'title' => '',
1034
+ 'menu' => 0, // menu id, slug or name
1035
+ //determines filter & filter_item ('items' takes precedence over 'children_of' because it's more specific)...
1036
+ 'children_of' => '', // empty = show all (dep. on 'items'); menu item id or title (caseless), or current|current-item|parent|current-parent|root|current-ancestor
1037
+ '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)
1038
+ 'start_level' => 1,
1039
+ 'depth' => 0, // 0 = unlimited
1040
+ //only if children_of is (parent|current-parent|root|current-ancestor); determines fallback_no_ancestor, fallback_include_parent & fallback_include_parent_siblings...
1041
+ 'fallback_parent' => 0, // 1 = use current-item; 'parent' = *and* include parent, 'siblings' = *and* include both parent and its siblings
1042
+ //only if children_of is (current|current-item); determines fallback_no_children, fallback_nc_include_parent & fallback_nc_include_parent_siblings...
1043
+ 'fallback_current' => 0, // 1 = use current-parent; 'parent' = *and* include parent (if available), 'siblings' = *and* include both parent (if available) and its siblings
1044
+ //switches...
1045
+ 'flat_output' => 0,
1046
+ 'contains_current' => 0, // v2.0.0
1047
+ //determines include_parent, include_parent_siblings & include_ancestors...
1048
+ 'include' =>'', //comma|space|hyphen separated list of 'parent', 'siblings', 'ancestors'
1049
+ 'ol_root' => 0,
1050
+ 'ol_sub' => 0,
1051
+ //determines title_from_parent & title_from_current...
1052
+ 'title_from' => '', //comma|space|hyphen separated list of 'parent', 'current'
1053
+ 'depth_rel_current' => 0, // v2.0.0
1054
+ //strings...
1055
+ 'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
1056
+ 'container_id' => '',
1057
+ 'container_class' => '',
1058
+ 'menu_class' => 'menu-widget',
1059
+ 'widget_class' => '',
1060
+ //determines before & after...
1061
+ 'wrap_link' => '', // a tag name (eg. div, p, span, etc)
1062
+ //determines link_before & link_after...
1063
+ 'wrap_link_text' => '' // a tag name (eg. span, em, strong)
1064
+ ),
1065
+ $atts,
1066
+ $tag // since WP3.6 this allows use of shortcode_atts_custom_menu_wizard filter, applied by shortcode_atts()
1067
+ );
1068
+
1069
+ $instance = apply_filters( 'custom_menu_wizard_shortcode_attributes', $instance );
1070
+
1071
+ if( empty( $instance['menu'] ) ){
1072
+ //gonna find the first menu (alphabetically) that has items...
1073
+ $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
1074
+ }else{
1075
+ //allow for menu being something other than an id (eg. slug or name), but we need the id for the widget...
1076
+ $menus = wp_get_nav_menu_object( $instance['menu'] );
1077
+ if( !empty( $menus) ){
1078
+ $menus = array( $menus );
1079
+ }
1080
+ }
1081
+ if( !empty( $menus ) ){
1082
+ foreach( $menus as $i=>$menu ){
1083
+ $items = wp_get_nav_menu_items( $menu->term_id );
1084
+ $ok = !empty( $items );
1085
+ if( $ok ){
1086
+ $instance['menu'] = $menu->term_id;
1087
+ break;
1088
+ }
1089
+ }
1090
+ }
1091
+ unset( $menus );
1092
+
1093
+ if( $ok ){
1094
+ $instance['filter'] = $instance['filter_item'] = 0;
1095
+ if( empty( $instance['items'] ) ){
1096
+ //children_of => filter & filter_item...
1097
+ if( empty( $instance['children_of'] ) ){
1098
+ $instance['children_of'] = '';
1099
+ }
1100
+ switch( $instance['children_of'] ){
1101
+ case '':
1102
+ break;
1103
+ case 'root': case 'current-ancestor':
1104
+ --$instance['filter_item']; //ends up as -2
1105
+ case 'parent': case 'current-parent':
1106
+ --$instance['filter_item']; //ends up as -1
1107
+ case 'current': case 'current-item':
1108
+ $instance['filter'] = 1;
1109
+ break;
1110
+ default:
1111
+ $instance['filter'] = 1;
1112
+ $instance['filter_item'] = strtolower( $instance['children_of'] );
1113
+ }
1114
+ //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...
1115
+ if( !is_numeric( $instance['filter_item'] ) ){
1116
+ foreach( $items as $item ){
1117
+ $ok = strtolower( $item->title ) == $instance['filter_item'];
1118
+ if( $ok ){
1119
+ $instance['filter_item'] = $item->ID;
1120
+ break;
1121
+ }
1122
+ }
1123
+ }
1124
+ }else{
1125
+ $instance['filter'] = -1;
1126
+ }
1127
+ unset( $instance['children_of'] );
1128
+ }
1129
+
1130
+ if( $ok ){
1131
+ //fallback_parent => fallback_no_ancestor switch (and extension switches)...
1132
+ $instance['fallback_no_ancestor'] = $instance['fallback_include_parent'] = $instance['fallback_include_parent_siblings'] = 0;
1133
+ if( $instance['filter_item'] < 0 && !empty( $instance['fallback_parent'] ) ){
1134
+ $instance['fallback_no_ancestor'] = 1;
1135
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_parent'] ), -1, PREG_SPLIT_NO_EMPTY );
1136
+ foreach( $i as $j ){
1137
+ if( $j == 'parent' ){
1138
+ $instance['fallback_include_parent'] = 1;
1139
+ }elseif( $j == 'siblings' ){
1140
+ $instance['fallback_include_parent_siblings'] = 1;
1141
+ }
1142
+ }
1143
+ }
1144
+ //fallback_current => fallback_no_children switch (and extension switches)...
1145
+ $instance['fallback_no_children'] = $instance['fallback_nc_include_parent'] = $instance['fallback_nc_include_parent_siblings'] = 0;
1146
+ if( $instance['filter'] == 1 && $instance['filter_item'] == 0 && !empty( $instance['fallback_current'] ) ){
1147
+ $instance['fallback_no_children'] = 1;
1148
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_current'] ), -1, PREG_SPLIT_NO_EMPTY );
1149
+ foreach( $i as $j ){
1150
+ if( $j == 'parent' ){
1151
+ $instance['fallback_nc_include_parent'] = 1;
1152
+ }elseif( $j == 'siblings' ){
1153
+ $instance['fallback_nc_include_parent_siblings'] = 1;
1154
+ }
1155
+ }
1156
+ }
1157
+ unset( $instance['fallback_parent'], $instance['fallback_current'] );
1158
+ //include => include_* ...
1159
+ $instance['include_parent'] = $instance['include_parent_siblings'] = $instance['include_ancestors'] = 0;
1160
+ if( $instance['filter'] == 1 && !empty( $instance['include'] ) ){
1161
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['include'] ), -1, PREG_SPLIT_NO_EMPTY );
1162
+ foreach( $i as $j ){
1163
+ if( $j == 'parent' ){
1164
+ $instance['include_parent'] = 1;
1165
+ }elseif( $j == 'siblings' ){
1166
+ $instance['include_parent_siblings'] = 1;
1167
+ }elseif( $j == 'ancestors' ){
1168
+ $instance['include_ancestors'] = 1;
1169
+ }
1170
+ }
1171
+ }
1172
+ unset( $instance['include'] );
1173
+ //title_from => title_from_parent, title_from_current ...
1174
+ $instance['title_from_parent'] = $instance['title_from_current'] = 0;
1175
+ if( !empty( $instance['title_from'] ) ){
1176
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['title_from'] ), -1, PREG_SPLIT_NO_EMPTY );
1177
+ foreach( $i as $j ){
1178
+ if( $j == 'parent' ){
1179
+ $instance['title_from_parent'] = 1;
1180
+ }elseif( $j == 'current' ){
1181
+ $instance['title_from_current'] = 1;
1182
+ }
1183
+ }
1184
+ }
1185
+ unset( $instance['title_from'] );
1186
+ //wrap_link => before & after...
1187
+ $instance['before'] = $instance['after'] = '';
1188
+ $instance['wrap_link'] = esc_attr( trim( $instance['wrap_link'] ) );
1189
+ if( !empty( $instance['wrap_link'] ) ){
1190
+ $instance['before'] = '<' . $instance['wrap_link'] . '>';
1191
+ $instance['after'] = '</' . $instance['wrap_link'] . '>';
1192
+ }
1193
+ //wrap_link_text => link_before & link_after...
1194
+ $instance['link_before'] = $instance['link_after'] = '';
1195
+ $instance['wrap_link_text'] = esc_attr( trim( $instance['wrap_link_text'] ) );
1196
+ if( !empty( $instance['wrap_link_text'] ) ){
1197
+ $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
1198
+ $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
1199
+ }
1200
+
1201
+ //handle widget_class here because we have full control over $before_widget...
1202
+ $before_widget_class = array(
1203
+ 'widget_custom_menu_wizard',
1204
+ 'shortcode_custom_menu_wizard'
1205
+ );
1206
+ $instance['widget_class'] = empty( $instance['widget_class'] ) ? '' : esc_attr( trim ( $instance['widget_class'] ) );
1207
+ if( !empty( $instance['widget_class'] ) ){
1208
+ foreach( explode(' ', $instance['widget_class'] ) as $i ){
1209
+ if( !empty( $i ) && !in_array( $i, $before_widget_class ) ){
1210
+ $before_widget_class[] = $i;
1211
+ }
1212
+ }
1213
+ }
1214
+ $instance['widget_class'] = '';
1215
+ //turn on hide_empty...
1216
+ $instance['hide_empty'] = 1;
1217
+ }
1218
+
1219
+ if( $ok ){
1220
+ //apart from before_title, these are lifted from the_widget()...
1221
+ $sidebar_args = array(
1222
+ 'before_widget' => '<div class="' . implode( ' ', $before_widget_class ) . '">',
1223
+ 'after_widget' => '</div>',
1224
+ 'before_title' => '<h2 class="widgettitle">',
1225
+ 'after_title' => '</h2>'
1226
+ );
1227
+ ob_start();
1228
+ the_widget(
1229
+ 'Custom_Menu_Wizard_Widget',
1230
+ apply_filters('custom_menu_wizard_shortcode_settings', $instance ),
1231
+ apply_filters('custom_menu_wizard_shortcode_widget_args', $sidebar_args )
1232
+ );
1233
+ $html = ob_get_clean();
1234
+ }
1235
+ return empty($html) ? '' : $html;
1236
+
1237
+ } //end shortcode_legacy()
1238
+
1239
+ } //end class Custom_Menu_Wizard_Plugin
1240
 
1241
  }
doc/cmw-doc.html ADDED
@@ -0,0 +1,1548 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <meta charset="utf-8">
4
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Custom Menu Wizard WordPress Plugin, Documentation</title>
7
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
8
+ <!--[if lt IE 9]>
9
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
10
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
11
+ <![endif]-->
12
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
13
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
14
+ <style type="text/css">
15
+ .gototop, .gotoprev {
16
+ display: inline-block;
17
+ opacity: 0;
18
+ position: fixed;
19
+ text-shadow: 1px 1px 1px #000000;
20
+ -webkit-transition: opacity 1s ease 0s, bottom 1s ease 0s, left 1s ease 0s, right 1s ease 0s, top 1s ease 0s;
21
+ -o-transition: opacity 1s ease 0s, bottom 1s ease 0s, left 1s ease 0s, right 1s ease 0s, top 1s ease 0s;
22
+ transition: opacity 1s ease 0s, bottom 1s ease 0s, left 1s ease 0s, right 1s ease 0s, top 1s ease 0s;
23
+ }
24
+ .gototop a, .gotoprev a {
25
+ -webkit-transition: border-radius 1s ease 0s;
26
+ -o-transition: border-radius 1s ease 0s;
27
+ transition: border-radius 1s ease 0s;
28
+ }
29
+ .gototop {
30
+ bottom: -3em;
31
+ right: 0;
32
+ }
33
+ .gototop.affix {
34
+ bottom: 0;
35
+ opacity: 0.6;
36
+ }
37
+ .gotoprev {
38
+ left: -3em;
39
+ top: 0;
40
+ }
41
+ .gotoprev.affix {
42
+ left: 0;
43
+ opacity: 0.6;
44
+ }
45
+ .offsite-link {
46
+ margin-left: 0.3em;
47
+ }
48
+ .toggle-a-z {
49
+ color: #808080;
50
+ margin: -16px -16px 2px 15px;
51
+ padding: 5px 8px 1px;
52
+ }
53
+ .tabpane-index .panel-body > span:last-child .hidden-md {
54
+ display: none;
55
+ }
56
+ .panel-heading[aria-expanded="false"] .glyphicon-triangle-top {
57
+ display: none;
58
+ }
59
+ .panel-heading[aria-expanded="true"] .glyphicon-triangle-bottom {
60
+ display: none;
61
+ }
62
+ h1 .small {
63
+ line-height: 2;
64
+ }
65
+ ol, ul {
66
+ margin-left: 0;
67
+ padding-left: 1.1em;
68
+ }
69
+ div > ul > li > ul, div > ol > li > ul {
70
+ margin-bottom: 10px;
71
+ }
72
+ @media (max-width: 767px) {
73
+ .gotoprev a {
74
+ border-top-left-radius: 0;
75
+ border-top-right-radius: 0;
76
+ border-bottom-left-radius: 0;
77
+ }
78
+ .gototop a {
79
+ border-top-right-radius: 0;
80
+ border-bottom-right-radius: 0;
81
+ border-bottom-left-radius: 0;
82
+ }
83
+ }
84
+ @media (min-width: 768px) {
85
+ .gotoprev {
86
+ top: 1em;
87
+ }
88
+ .gotoprev.affix {
89
+ left: 1em;
90
+ }
91
+ .gototop {
92
+ right: 1em;
93
+ }
94
+ .gototop.affix {
95
+ bottom: 1em;
96
+ }
97
+ }
98
+ </style>
99
+ </head>
100
+ <body>
101
+ <div id='Top' class="container">
102
+
103
+ <div class="page-header">
104
+ <h1>Custom Menu Wizard Widget <em class="small visible-xs-block visible-sm-block visible-md-inline visible-lg-inline">&hellip;a <a href="https://wordpress.org/plugins/custom-menu-wizard/" target="_blank">WordPress Plugin<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a></em></h1>
105
+
106
+ <h4>Show branches or levels of your menu in a widget, or in content using a shortcode, with full customisation.</h4>
107
+
108
+ </div>
109
+
110
+
111
+ <p>
112
+ <strong>Contributors:</strong> <a href='https://profiles.wordpress.org/wizzud/' target='_blank'>wizzud<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a></p>
113
+ <p>
114
+ <strong>Donate link:</strong> <a class="btn btn-success btn-xs" href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&#038;hosted_button_id=KP2LVCBXNCEB4">Donate to this plugin</a></p>
115
+ <p>
116
+ <strong>Tags:</strong> menu, widget, navigation, custom menu, partial menu, current item, current page, menu level, menu branch, menu shortcode, menu widget, advanced, enhanced</p>
117
+ <p>
118
+ <strong>Requires at least:</strong> 3.6</p>
119
+ <p>
120
+ <strong>Tested up to:</strong> 4.3</p>
121
+ <p>
122
+ <strong>Stable tag:</strong> 3.1.5</p>
123
+ <p>
124
+ <strong>License:</strong> GPLv2 or Later </p>
125
+
126
+ <hr id="scroll-default" />
127
+
128
+ <div role="tabpanel">
129
+
130
+ <!-- Nav tabs -->
131
+ <nav>
132
+ <ul class="nav nav-pills" role="tablist">
133
+ <li role="presentation" class="active"><a href="#Description" aria-controls="Description" role="tab" data-toggle="pill">Description</a></li>
134
+ <li role="presentation"><a href="#Installation" aria-controls="Installation" role="tab" data-toggle="pill">Installation</a></li>
135
+ <li role="presentation"><a href="#Widget-Options" aria-controls="Widget-Options" role="tab" data-toggle="pill">Widget Options</a></li>
136
+ <li role="presentation"><a href="#Shortcode-Attributes" aria-controls="Shortcode-Attributes" role="tab" data-toggle="pill">Shortcode Attributes</a></li>
137
+ <li role="presentation"><a href="#FAQs" aria-controls="FAQs" role="tab" data-toggle="pill">FAQs</a></li>
138
+ <li role="presentation"><a href="#Changelog" aria-controls="Changelog" role="tab" data-toggle="pill">Changelog</a></li>
139
+ </ul>
140
+ </nav>
141
+
142
+ <!-- Tab panes -->
143
+ <div class="tab-content">
144
+ <div role="tabpanel" class="tab-pane fade in active" id="Description">
145
+ <h2>Description</h2>
146
+ <p>This plugin is a boosted version of the WordPress "Custom Menu" widget.
147
+ It provides full control over most of the parameters available when calling WP's <a href="http://codex.wordpress.org/Function_Reference/wp_nav_menu" target='_blank'>wp_nav_menu()<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a> function, as well as providing pre-filtering of the menu items in order to be able to select a specific portion of the custom menu. It also automatically adds a couple of custom classes. And there's a shortcode that enables you to include the widget's output in your content.</p>
148
+
149
+ <p>Features include:</p>
150
+
151
+ <ul>
152
+ <li>Display an entire menu, just a branch of it, just certain level(s) of it, or even just specific items from it!</li>
153
+ <li>Select a branch based on a specific menu item, or the current menu item (currently displayed page)</li>
154
+ <li>Specify a relative or absolute level to start at, and the number of levels to output</li>
155
+ <li>Include ancestor item(s) in the output, with or without siblings</li>
156
+ <li>Exclude certain menu items, or levels of items</li>
157
+ <li>Make the output conditional upon the current menu item being found in different stages of the filter selection process</li>
158
+ <li>Automatically add cmw-level-N and cmw-has-submenu classes to output menu items</li>
159
+ <li>Allow the widget title to be entered but not output, or to be set from the current menu item or selected branch item</li>
160
+ <li>Select hierarchical or flat output, both options still abiding by the specified number of levels to output</li>
161
+ <li>Specify custom class(es) for the widget block, the menu container, and the menu itself</li>
162
+ <li>Modify the link's output with additional HTML around the link's text and/or the link element itself</li>
163
+ <li>Use Ordered Lists (OL) for the top and/or sub levels instead of Unordered Lists (UL)</li>
164
+ <li>Shortcode, <code>[cmwizard]</code>, available to run the widget from within content</li>
165
+ <li>Shortcode can reference a widget instance, making maintenance of multiple occurences of the same (or very similar) shortcode a lot easier</li>
166
+ <li>Interactive "assist" to help with the widget settings and/or shortcode definition</li>
167
+ <li>Utility to find posts containing this plugin's shortcode</li>
168
+ <li>Specify an alternative configuration to use under certain conditions (dual-scenario capability)</li>
169
+ </ul>
170
+
171
+ <p>Current documentation for the <strong>Widget Options</strong> can be found
172
+ under <a href="#Widget-Options">Widget Options</a>.
173
+ The associated <strong>Shortcode Attributes</strong> are documented
174
+ under <a href="#Shortcode-Attributes">Shortcode Attributes</a>.</p>
175
+
176
+
177
+
178
+ <p><strong>Please, do not be put off by the number of options available!</strong> I suspect (and I admit that I'm guessing!) that for the majority of users
179
+ there are probably a couple of very common scenarios:</p>
180
+
181
+ <ol>
182
+ <li><p>Show an entire menu...</p>
183
+
184
+ <ul>
185
+ <li>Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)</li>
186
+ <li>Select the menu you wish to use (if it's not already selected)</li>
187
+ <li>Save the widget!</li>
188
+ <li><em>Equivalent shortcode resembles...</em></li>
189
+ </ul>
190
+
191
+ <pre><code>[cmwizard menu=N title="Your Title"/]</code></pre></li>
192
+ <li><p>Show the current menu item, plus any descendants...</p>
193
+
194
+ <ul>
195
+ <li>Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)</li>
196
+ <li>Select the menu you wish to use (if it's not already selected)</li>
197
+ <li>Open the FILTERS section :
198
+
199
+ <ul>
200
+ <li>under Primary Filter, click on the <em>Branch</em> radio</li>
201
+ </ul></li>
202
+ <li>Save the widget!</li>
203
+ <li><em>Equivalent shortcode resembles...</em></li>
204
+ </ul>
205
+
206
+ <pre><code>[cmwizard menu=N title="Your Title" branch=current/]</code></pre></li>
207
+ <li><p>Show just the descendants of the current menu item (if there are any)...</p>
208
+
209
+ <ul>
210
+ <li>Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)</li>
211
+ <li>Select the menu you wish to use (if it's not already selected)</li>
212
+ <li>Open the FILTERS section :
213
+
214
+ <ul>
215
+ <li>under Primary Filter, click on the <em>Branch</em> radio</li>
216
+ <li>under Secondary Filter, set <em>Starting at</em> to "+1 (children)"</li>
217
+ </ul></li>
218
+ <li>Save the widget!</li>
219
+ <li><em>Equivalent shortcode resembles...</em></li>
220
+ </ul>
221
+
222
+ <pre><code>[cmwizard menu=N title="Your Title" branch=current start_at="+1"/]</code></pre></li>
223
+ <li><p>Always show the top level items, but when the menu contains the current item then also show that current item, with its ancestors and immediate children...</p>
224
+
225
+ <ul>
226
+ <li>Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)</li>
227
+ <li>Select the menu you wish to use (if it's not already selected)</li>
228
+ <li>Open the FILTERS section :
229
+
230
+ <ul>
231
+ <li>under Primary Filter, click on the <em>Branch</em> radio</li>
232
+ <li>under Secondary Filter, set <em>Depth</em> to "2 levels" (ie. current item plus immediate children)</li>
233
+ <li>under Inclusions, set <em>Branch Ancestors</em> to "to level 1 (root)", and set <em>Level</em> to "1"</li>
234
+ </ul></li>
235
+ <li>Open the ALTERNATIVE section :
236
+
237
+ <ul>
238
+ <li>set <em>On condition</em> to "Current Item is NOT in..." and "Menu" (the 2nd dropdown)</li>
239
+ <li>in the <em>Then switch settings to</em> textarea, type in "[cmwizard depth=1/]" (without the quotes!)</li>
240
+ </ul></li>
241
+ <li>Save the widget!</li>
242
+ <li><em>Equivalent shortcode resembles...</em></li>
243
+ </ul>
244
+
245
+ <pre><code>[cmwizard menu=N branch=current depth=2 ancestors=1 include_level="1" alternative="no-current,menu"]depth=1[/cmwizard]</code></pre></li>
246
+ </ol>
247
+
248
+ <p>If you like this widget (or if you don't?), please consider taking a moment or two to give it a
249
+ <a href="https://wordpress.org/support/view/plugin-reviews/custom-menu-wizard" target='_blank'>Review<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a> : it helps others, and gives me valuable feedback.</p>
250
+
251
+ <p><em>Documentation for version 2 of the widget
252
+ can be found <a href="http://plugins.svn.wordpress.org/custom-menu-wizard/tags/2.0.6/readme.txt" target='_blank'>here<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a>
253
+ or <a href="http://www.wizzud.com/v210-readme.html" target='_blank'>here<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a>.</em></p> </div>
254
+ <div role="tabpanel" class="tab-pane fade" id="Installation">
255
+ <h2>Installation</h2>
256
+ <ol>
257
+ <li><p>EITHER Upload the zip package via 'Plugins &gt; Add New &gt; Upload' in your WP Admin</p>
258
+
259
+ <p>OR Extract the zip package and upload <code>custom-menu-wizard</code> folder to the <code>/wp-content/plugins/</code> directory</p></li>
260
+ <li><p>Activate the plugin through the 'Plugins' menu in your WP Admin</p></li>
261
+ </ol>
262
+
263
+ <p>The widget will now be available in the 'Widgets' admin page.
264
+ As long as you already have at least one Custom Menu defined, you can add the new widget to a sidebar and configure it however you want.
265
+ Alternatively, you can use the shortcode in your content.</p>
266
+
267
+ <p>Current documentation for the <strong>Widget Options</strong> can be found
268
+ under <a href="#Widget-Options">Widget Options</a>.</p> </div>
269
+ <div role="tabpanel" class="tab-pane fade" id="Widget-Options">
270
+ <h2>Widget Options</h2>
271
+ <div class="row"><div class="col-md-4 col-md-push-8 col-lg-3 col-lg-push-9"><div class="tabpane-index panel panel-default"><div class="panel-body">
272
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Widget-Options" data-scroll="#woption-12-Title">Title</a><span class="hidden-md hidden-lg"> ,</span></span>
273
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Widget-Options" data-scroll="#woption-13-Hide">Hide</a><span class="hidden-md hidden-lg"> ,</span></span>
274
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Widget-Options" data-scroll="#woption-14-Select-Menu">Select Menu</a></span>
275
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><br class="hidden-md hidden-lg"/><strong>Filters Section</strong><span class="hidden-md hidden-lg"> :</span></span>
276
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>Primary Filter</em><span class="hidden-md hidden-lg">:</span></span>
277
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-15-Level">Level</a><span class="hidden-md hidden-lg"> ,</span></span>
278
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-16-Branch">Branch</a><span class="hidden-md hidden-lg"> ,</span></span>
279
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-17-Items">Items</a><span class="hidden-md hidden-lg"> ,</span></span>
280
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>Secondary Filter</em><span class="hidden-md hidden-lg">:</span></span>
281
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-18-Starting-at">Starting at</a><span class="hidden-md hidden-lg"> ,</span></span>
282
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-19-Item-if-possible">Item, if possible</a><span class="hidden-md hidden-lg"> ,</span></span>
283
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-20-Level">Level</a><span class="hidden-md hidden-lg"> ,</span></span>
284
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-21-Allow-all-Root-Items">Allow all Root Items</a><span class="hidden-md hidden-lg"> ,</span></span>
285
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-22-For-Depth">For Depth</a><span class="hidden-md hidden-lg"> ,</span></span>
286
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-23-Relative-to-Current-Item">Relative to Current Item</a><span class="hidden-md hidden-lg"> ,</span></span>
287
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>Inclusions</em><span class="hidden-md hidden-lg">:</span></span>
288
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-24-Branch-Ancestors">Branch Ancestors</a><span class="hidden-md hidden-lg"> ,</span></span>
289
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-25--with-Siblings">... with Siblings</a><span class="hidden-md hidden-lg"> ,</span></span>
290
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-26-Branch-Siblings">Branch Siblings</a><span class="hidden-md hidden-lg"> ,</span></span>
291
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-27-Level">Level</a><span class="hidden-md hidden-lg"> ,</span></span>
292
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>Exclusions</em><span class="hidden-md hidden-lg">:</span></span>
293
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-28-Item-Ids">Item Ids</a><span class="hidden-md hidden-lg"> ,</span></span>
294
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-29-Level">Level</a><span class="hidden-md hidden-lg"> ,</span></span>
295
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>Qualifier</em><span class="hidden-md hidden-lg">:</span></span>
296
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-30-Current-Item-is-in">Current Item is in</a></span>
297
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><br class="hidden-md hidden-lg"/><strong>Fallbacks Section</strong><span class="hidden-md hidden-lg"> :</span></span>
298
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>If Current Item has no children</em><span class="hidden-md hidden-lg">:</span></span>
299
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-31-Unlabelled-Select">Unlabelled Select</a><span class="hidden-md hidden-lg"> ,</span></span>
300
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-32-and-Include-its-Siblings">...and Include its Siblings</a><span class="hidden-md hidden-lg"> ,</span></span>
301
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-33-For-Depth">For Depth</a><span class="hidden-md hidden-lg"> ,</span></span>
302
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>If no Current Item can be found</em><span class="hidden-md hidden-lg">:</span></span>
303
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-34-Try-items-marked-Parent-of-Current">Try items marked Parent of Current</a><span class="hidden-md hidden-lg"> ,</span></span>
304
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>If more than 1 possible Current Item</em><span class="hidden-md hidden-lg">:</span></span>
305
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-35-Use-the-last-one-found">Use the last one found</a></span>
306
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><br class="hidden-md hidden-lg"/><strong>Output Section</strong><span class="hidden-md hidden-lg"> :</span></span>
307
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-36-Hierarchical">Hierarchical</a><span class="hidden-md hidden-lg"> ,</span></span>
308
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-37-Flat">Flat</a><span class="hidden-md hidden-lg"> ,</span></span>
309
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>Set Title from</em><span class="hidden-md hidden-lg">:</span></span>
310
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-38-Current-Item">Current Item</a><span class="hidden-md hidden-lg"> ,</span></span>
311
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-39-Branch-Item">Branch Item</a><span class="hidden-md hidden-lg"> ,</span></span>
312
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-40-Make-it-a-Link">Make it a Link</a><span class="hidden-md hidden-lg"> ,</span></span>
313
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>Change UL to OL</em><span class="hidden-md hidden-lg">:</span></span>
314
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-41-Top-Level">Top Level</a><span class="hidden-md hidden-lg"> ,</span></span>
315
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-42-Sub-Levels">Sub-Levels</a><span class="hidden-md hidden-lg"> ,</span></span>
316
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><em>Pre WordPress 3.6</em><span class="hidden-md hidden-lg">:</span></span>
317
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:1em;"></span><a href="#Widget-Options" data-scroll="#woption-43-Hide-Widget-if-Empty">Hide Widget if Empty</a></span>
318
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><br class="hidden-md hidden-lg"/><strong>Container Section</strong><span class="hidden-md hidden-lg"> :</span></span>
319
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-44-Element">Element</a><span class="hidden-md hidden-lg"> ,</span></span>
320
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-45-Unique-ID">Unique ID</a><span class="hidden-md hidden-lg"> ,</span></span>
321
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-46-Class">Class</a></span>
322
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><br class="hidden-md hidden-lg"/><strong>Classes Section</strong><span class="hidden-md hidden-lg"> :</span></span>
323
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-47-Menu-Class">Menu Class</a><span class="hidden-md hidden-lg"> ,</span></span>
324
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-48-Widget-Class">Widget Class</a></span>
325
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><br class="hidden-md hidden-lg"/><strong>Links Section</strong><span class="hidden-md hidden-lg"> :</span></span>
326
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-49-Before-the-Link">Before the Link</a><span class="hidden-md hidden-lg"> ,</span></span>
327
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-50-After-the-Link">After the Link</a><span class="hidden-md hidden-lg"> ,</span></span>
328
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-51-Before-the-Link-Text">Before the Link Text</a><span class="hidden-md hidden-lg"> ,</span></span>
329
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-52-After-the-Link-Text">After the Link Text</a></span>
330
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><br class="hidden-md hidden-lg"/><strong>Alternative Section</strong><span class="hidden-md hidden-lg"> :</span></span>
331
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-53-On-condition">On condition</a><span class="hidden-md hidden-lg"> ,</span></span>
332
+ <span class="text-nowrap visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><span class="hidden-xs hidden-sm" style="margin-left:0.5em;"></span><a href="#Widget-Options" data-scroll="#woption-54-Then-switch-settings-to">Then switch settings to</a></span>
333
+ </div></div></div>
334
+ <div class="col-md-8 col-md-pull-4 col-lg-9 col-lg-pull-3">
335
+ <p>There are quite a few options, which makes the widget settings box very long. I have therefore grouped most of the options into
336
+ collapsible logical sections (with remembered state once saved).</p>
337
+
338
+ <p>The associated <strong>Shortcode Attributes</strong> are documented
339
+ under <a href="#Shortcode-Attributes">Shortcode Attributes</a>.</p>
340
+
341
+ <p><em><strong>Always Visible</strong></em></p>
342
+
343
+ <ul>
344
+ <li><p><strong id="woption-12-Title">Title</strong> <em>(textbox)</em></p>
345
+
346
+ <p>Set the title for your widget.</p></li>
347
+ <li><p><strong id="woption-13-Hide">Hide</strong> <em>(checkbox)</em></p>
348
+
349
+ <p>Prevents the entered <code>Title</code> being displayed in the front-end widget output.</p>
350
+
351
+ <p>In the Widgets admin page, I find it useful to still be able to see the <code>Title</code> in the sidebar when the widget is closed, but I
352
+ don't necessarily want that <code>Title</code> to actually be output when the widget is displayed at the front-end. Hence this checkbox.</p></li>
353
+ <li><p><strong id="woption-14-Select-Menu">Select Menu</strong> <em>(select)</em></p>
354
+
355
+ <p>Choose the appropriate menu from the dropdown list of Custom Menus currently defined in your WordPress application. The
356
+ first one available (alphabetically) is already selected for you by default.</p></li>
357
+ </ul>
358
+ <h3 id='Filters-Section'>Filters Section</h3>
359
+ <p>Filters are applied in the order they are presented.</p>
360
+
361
+ <p><strong><em id="woption-1-Primary-Filter">Primary Filter</em></strong></p>
362
+
363
+ <ul>
364
+ <li><p><strong id="woption-15-Level">Level</strong> <em>(radio, default On, &amp; select)</em></p>
365
+
366
+ <p>Filters by level within the selected menu, starting at the level selected here. This is the default setting
367
+ for a new widget instance, which, if left alone and with all other options at their default, will show the entire selected menu.</p>
368
+
369
+ <p>Example : If you wanted to show all the options that were at level 3 or below, you could check this radio and set the select to "3".</p></li>
370
+ <li><p><strong id="woption-16-Branch">Branch</strong> <em>(radio &amp; select)</em></p>
371
+
372
+ <p>Filters by branch, with the head item of the branch being selected from the dropdown. The dropdown presents all the
373
+ items from the selected menu, plus a "Current Item" option (the default). Selecting "Current Item" means that the head item of the
374
+ branch is the current menu item (as indicated by WordPress), provided, of course, that the current menu item actually corresponds to
375
+ a menu item from the currently selected menu!</p></li>
376
+ <li><p><strong id="woption-17-Items">Items</strong> <em>(radio &amp; textbox)</em></p>
377
+
378
+ <p>Filters by the menu items that you specifically pick (by menu item id, as a comma-separated list). The simplest way
379
+ to get your list of ids is to use the "assist", and [un]check the green tick box at the right hand side of each depicted menu item that
380
+ you want. Alternatively, just type your list of ids into the box.</p>
381
+
382
+ <p>If the id is appended with a '+', eg. '23+', then all the item's descendants will also be included.</p>
383
+
384
+ <p>Example : If you only wanted to show, say, 5 of your many available menu items, and those 5 items are not in one handy branch of the menu,
385
+ then you might want to use this option.</p>
386
+
387
+ <p>Example : If your menu has 6 root branches - "A" thru to "F" - and you were only interested in branches "B" (id of, say, 11) and
388
+ "E" (id of, say, 19), you could set <code>Items</code> to be "11+,19+", which would include "B" with all its descendants, and "E" with all its
389
+ descendants.</p></li>
390
+ </ul>
391
+
392
+ <p><strong><em id="woption-2-Secondary-Filter">Secondary Filter</em></strong> <em>(not applicable to an <code>Items</code> filter)</em></p>
393
+
394
+ <ul>
395
+ <li><p><strong id="woption-18-Starting-at">Starting at</strong> <em>(select)</em></p>
396
+
397
+ <p>This is only applicable to a <code>Branch</code> filter and it allows you to shift the starting point of your output within the confines
398
+ of the selected branch. By default it is set to the selected branch item itself, but it can be changed to a relative of the branch item (eg.
399
+ parent, grandparent, children, etc) or to an absolute, fixed level within the branch containing the selected branch item (eg. the root
400
+ level item for the branch, or one level below the branch's root item, etc).</p>
401
+
402
+ <p>Example : If you wanted the entire "current" branch then, with <code>Branch</code> set to "Current Item", you might set <code>Starting at</code> to "1 (root)".
403
+ Alternatively, if you wanted the children of the current menu item then <code>Starting at</code> could be set to "+1 (children)".</p></li>
404
+ <li><p><strong id="woption-19-Item-if-possible">Item, if possible</strong> <em>(radio, default On)</em></p>
405
+
406
+ <p>This is the default filter mechanism whereby, if <code>Starting at</code> can only result in a single item (ie. it is the branch item itself, or
407
+ an ancestor thereof) then only that item and its descendants are considered for filtering.</p></li>
408
+ <li><p><strong id="woption-20-Level">Level</strong> <em>(radio)</em></p>
409
+
410
+ <p>Changes the default filter mechanism such that if <code>Starting at</code> results in the selection of the branch item or one of its ancestors,
411
+ then all siblings of that resultant item are also included in the secondary filtering process.</p>
412
+
413
+ <p>Example : If Joe and Fred are siblings (ie. they have a common parent) and Joe is the selected branch item - with <code>Starting at</code> set
414
+ to Joe - then the secondary filter would normally only consider Joe and its descendants. However, if <code>Level</code> was enabled instead of
415
+ <code>Item</code>, then both Joe and Fred, <em>and all their descendants</em>, would be considered for filtering.</p>
416
+
417
+ <p>Note that there is one exception, and that is that if <code>Starting at</code> results in a root-level item, then <code>Allow all Root Items</code> must
418
+ be enabled in order to allow the other sibling root items to be added into the filter process.</p></li>
419
+ <li><p><strong id="woption-21-Allow-all-Root-Items">Allow all Root Items</strong> <em>(checkbox)</em></p>
420
+
421
+ <p>In the right conditions - see <code>Level</code> above - this allows sibling root items to be considered for secondary filtering.</p></li>
422
+ <li><p><strong id="woption-22-For-Depth">For Depth</strong> <em>(select)</em></p>
423
+
424
+ <p>This the number of levels of the menu structure that will be considered for inclusion in the final output (in complete
425
+ ignorance of any subsequent Inclusions or Exclusions).</p>
426
+
427
+ <p>The first level of output is the starting level, regardless of
428
+ how that starting level is determined (see <code>Starting at</code> and <code>Relative to Current Item</code> options). So if you ask
429
+ for a Depth of 1 level, you get just the starting level; if you ask for a Depth of 2, you get the starting level and
430
+ the one below it.</p></li>
431
+ <li><p><strong id="woption-23-Relative-to-Current-Item">Relative to Current Item</strong> <em>(checkbox)</em></p>
432
+
433
+ <p>By default, <code>For Depth</code> (above) is relative to the first item found, but this may be overridden to be relative to the
434
+ current menu item <strong><em>if</em></strong> <code>For Depth</code> is not unlimited <strong>and</strong> the current menu item can found within the selected menu.
435
+ If the current menu item is not within the selected menu then it falls back to being relative to the first item found.</p>
436
+
437
+ <p>Please note that the current item must also be within the constraints set by the <code>Starting at</code> option. In other words, if
438
+ current item is <em>above</em> the <code>Starting at</code> level in the menu structure then it will <strong>not</strong> be used to alter the determination of
439
+ Depth.</p></li>
440
+ </ul>
441
+
442
+ <p><strong><em id="woption-3-Inclusions">Inclusions</em></strong></p>
443
+
444
+ <p>These allow certain other items to be added to the output from the secondary filters.</p>
445
+
446
+ <p>The first 3 are only applicable to a <code>Branch</code> filter. Please note that they only come into effect when the <code>Branch</code> filter item is at
447
+ or below the <code>Starting at</code> level, and do not include any items that would break the depth limit set in the Secondary Filter options.</p>
448
+
449
+ <ul>
450
+ <li><p><strong id="woption-24-Branch-Ancestors">Branch Ancestors</strong> <em>(select)</em></p>
451
+
452
+ <p>Include any ancestors (parent, grandparent, etc) of the items selected as the <code>Branch</code> filter.
453
+ Ancestors can be set to go up to an absolute level, or to go up a certain number of levels relative to the <code>Branch</code> filter item.</p></li>
454
+ <li><p><strong id="woption-25--with-Siblings">... with Siblings</strong> <em>(select)</em></p>
455
+
456
+ <p>In conjunction with <code>Branch Ancestors</code>, also include all siblings of those ancestors.
457
+ As with Ancestors, their siblings can be set to go up to an absolute level, or to go up a certain number of levels relative
458
+ to the <code>Branch</code> filter item. Note that while it is possibe to set a larger range for siblings than ancestors, the final output
459
+ is limited by <code>Branch Ancestors</code> setting.</p></li>
460
+ <li><p><strong id="woption-26-Branch-Siblings">Branch Siblings</strong> <em>(checkbox)</em></p>
461
+
462
+ <p>Include any siblings of the item selected as the <code>Branch</code> filter (ie. any items at the same level and within
463
+ the same branch as the <code>Branch</code> item).</p></li>
464
+ <li><p><strong id="woption-27-Level">Level</strong> <em>(select)</em></p>
465
+
466
+ <p>This allows an entire level of items to be included, optionally also including all levels either above or below it.
467
+ This replaces the <code>All Root Items</code> checkbox (pre v3.0.4), which only allowed for the inclusion of the root level items.</p></li>
468
+ </ul>
469
+
470
+ <p><strong><em id="woption-4-Exclusions">Exclusions</em></strong></p>
471
+
472
+ <ul>
473
+ <li><p><strong id="woption-28-Item-Ids">Item Ids</strong> <em>(textbox)</em></p>
474
+
475
+ <p>This is a comma-separated list of the ids of menu items that you do <em>not</em> want to appear in the final output.
476
+ The simplest way to get your list of ids is to use the "assist", and [un]check
477
+ the red cross box at the left hand side of each depicted menu item. Alternatively, just type your list of ids into the box.</p>
478
+
479
+ <p>If the id is appended with a '+', eg. '23+', then all the item's descendants will also be excluded.</p>
480
+
481
+ <p>Example : If you wanted to show the entire "A" branch, with the sole exception of one grandchild of "A", say "ABC", then you could
482
+ set <code>Branch</code> to "A", and <code>Exclusions</code> to the id of the "ABC" item.</p>
483
+
484
+ <p>Example : If you have a menu with 4 root items - "A", "B", "C" &amp; "D" - and you wanted to show all items, with descendants, for all bar
485
+ the "C" branch, then you could set <code>Level</code> to "1 (root)" and <code>Exclusions</code> to, say, "12+", where "12" is the menu item id for "C" and
486
+ the "+" indicates that all the descendants of "C" should also be excluded.</p></li>
487
+ <li><p><strong id="woption-29-Level">Level</strong> <em>(select)</em></p>
488
+
489
+ <p>This allows an entire level of items to be excluded, optionally also excluding all levels either above or below it.</p></li>
490
+ </ul>
491
+
492
+ <p><strong><em id="woption-5-Qualifier">Qualifier</em></strong></p>
493
+
494
+ <ul>
495
+ <li><p><strong id="woption-30-Current-Item-is-in">Current Item is in</strong> <em>(select)</em></p>
496
+
497
+ <p>This allows you to specify that there only be any output shown when/if the current menu item is one of the menu items selected
498
+ for output at a particular stage in the filter processing.</p>
499
+
500
+ <ul>
501
+ <li><em>"Menu"</em> : the current menu item has to be somewhere within the selected menu.</li>
502
+ <li><em>"Primary Filter"</em> : the current menu item has to be within the scope of the selected primary filter. So if you selected, say, a child
503
+ of "A" as the <code>Branch</code> item, then if "A" was the current menu item there would be no output with this qualifier.</li>
504
+ <li><em>"Secondary Filter"</em> : the current menu item has to be within the items as restricted by the secondary filters. So if you
505
+ selected <code>Branch</code> as "A", with <code>Starting at</code> set to "+1 (children)", then if "A" was the current menu item there would be no output with this qualifier.</li>
506
+ <li><em>"Inclusions"</em> : the current menu item has to be in within the items as set by the primary and secondary filters, and the inclusions.</li>
507
+ <li><em>"Final Output"</em> : the current menu item has to be in the final output.</li>
508
+ </ul></li>
509
+ </ul>
510
+ <h3 id='Fallbacks-Section'>Fallbacks Section</h3>
511
+ <p><strong><em id="woption-6-If-Current-Item-has-no-children">If Current Item has no children</em></strong></p>
512
+
513
+ <p>This gets applied at the Secondary Filter stage, and its eligibility and
514
+ application are therefore determined and governed by the other Secondary Filter settings.</p>
515
+
516
+ <p>It only comes into play (possibly) when a <code>Branch</code> filter is set as "Current Item", and the <code>Starting at</code>
517
+ and <code>For Depth</code> settings are such that the output should start at or below the current item,
518
+ and would normally include some of the current item's descendants (eg. <code>Starting at</code> "the Branch",</p>
519
+
520
+ <pre><code>For Depth</code></pre>
521
+
522
+ <p>"1 level" does <em>not</em> invoke the fallback).
523
+ The fallback allows for the occasion when the current menu item <em>does not have</em> any immediate children.</p>
524
+
525
+ <ul>
526
+ <li><p><strong id="woption-31-Unlabelled-Select">Unlabelled Select</strong> <em>(select)</em></p>
527
+
528
+ <p>Enable the fallback by selecting one of</p>
529
+
530
+ <ul>
531
+ <li><em>Start at : -1 (parent)</em> : overrides the <code>Starting at</code> option to be the immediate parent of the Current Item</li>
532
+ <li><em>Start at : the Current Item</em> : overrides the <code>Starting at</code> option to be the Current Item</li>
533
+ <li><em>No output!</em> : self-explanatory</li>
534
+ </ul></li>
535
+ <li><p><strong id="woption-32-and-Include-its-Siblings">...and Include its Siblings</strong> <em>(checkbox)</em></p>
536
+
537
+ <p>This will add in the siblings of the item selected above (excluding the "No output!" setting!).</p>
538
+
539
+ <p>Note : This <em>only</em> adds the siblings, not the siblings' descendants! However, if the <code>Level</code> radio (in Secondary Filter stage above) is
540
+ set, then all the item's siblings <em>and their descendants</em> will automatically be included, and [un]setting this option will have no effect.
541
+ Also note that if the fallback results in a root-level item being selected as the new <code>Starting at</code> item, then the inclusion of siblings
542
+ outside the current branch depends on the setting of the <code>Allow all Root Items</code> checkbox.</p></li>
543
+ <li><p><strong id="woption-33-For-Depth">For Depth</strong> <em>(select)</em></p>
544
+
545
+ <p>Override the current <code>For Depth</code> setting. Note that any depth value set here will be relative to the current item, regardless
546
+ of the current setting of <code>...Relative to</code>!</p>
547
+
548
+ <p>As an example, this option may be useful in the following scenario : item A has 2 children, B and C; B is the current menu item but has
549
+ no children, whereas C has loads of children/grandchildren. If you fallback to B's parent - A - with Unlimited depth set, then you will
550
+ get A, B, C, and <em>all</em> C's dependents! You may well want to override depth to limit the output to, say, just A, B and C, by setting this
551
+ fallback option to "1"? Or maybe A, B, C, and C's immediate children, by setting "2"?</p></li>
552
+ </ul>
553
+
554
+ <p><strong><em id="woption-7-If-no-Current-Item-can-be-found">If no Current Item can be found</em></strong></p>
555
+
556
+ <ul>
557
+ <li><p><strong id="woption-34-Try-items-marked-Parent-of-Current">Try items marked Parent of Current</strong> <em>(checkbox)</em></p>
558
+
559
+ <p>This gets applied right at the start of processing, when determining
560
+ which of the menu items (if any) should be regarded as the unique "Current Item" by this widget.
561
+ Under certain conditions, WordPress will mark an item as being the parent of a current item ...
562
+ but there won't actually be a current item marked! This occurs, for example, when displaying a full post for which there is
563
+ no specific related menu item, yet there <em>is</em> a menu item for a Category that the displayed post belongs to :
564
+ WordPress will then mark the related Category as being the parent of the current item (the post) even though
565
+ it can't mark the post as being the current item (because there's no specific item for it within the menu).</p>
566
+
567
+ <p>Enabling this fallback will make the widget look for these situations - only as a last resort! -
568
+ and set (one of) the found "parent" item(s) as the Current Item.</p></li>
569
+ </ul>
570
+
571
+ <p><strong><em id="woption-8-If-more-than-1-possible-Current-Item">If more than 1 possible Current Item</em></strong></p>
572
+
573
+ <ul>
574
+ <li><p><strong id="woption-35-Use-the-last-one-found">Use the last one found</strong> <em>(checkbox)</em></p>
575
+
576
+ <p>Occasionally it is possible for CMW to have more than one possible candidate for Current Item. Since there can only be one
577
+ Current Item, CMW picks the <em>first one</em> encountered. However, this may cause a problem where, for example, a root level item <strong>and</strong>
578
+ one of its sub-menu items are <em>both</em> set to list items from Category A, and the page being displayed is a full post that belongs
579
+ to category A : CMW will more than likely determine that the root level item is the Current Item, whereas you really need the
580
+ sub-menu item to be the Current Item (probably to maintain consistency with what is produced when other sub-menu items are "current").</p>
581
+
582
+ <p>Enabling this fallback will make CMW use the last-found (instead of first-found) candidate for Current item, ie. when
583
+ the choice is between a submenu item or its parent item, the submenu item will be used.</p>
584
+
585
+ <p>Note that this option is most likely to only have any effect when the <code>If no Current Item can be found</code> fallback (above) is
586
+ enabled, but given that any other plugin/theme could affect the menu item structure that gets passed thru to CMW it is not
587
+ impossible for other configurations to also be affected.</p></li>
588
+ </ul>
589
+ <h3 id='Output-Section'>Output Section</h3>
590
+ <ul>
591
+ <li><p><strong id="woption-36-Hierarchical">Hierarchical</strong> <em>(radio, default On)</em></p>
592
+
593
+ <p>Output in the standard nested list format. The alternative is <code>Flat</code>.</p></li>
594
+ <li><p><strong id="woption-37-Flat">Flat</strong> <em>(radio)</em></p>
595
+
596
+ <p>Output in a single list format, ignoring any parent-child relationship other than to maintain the same physical order as would be
597
+ presented in a <code>Hierarchical</code> output (which is the alternative and default).</p></li>
598
+ </ul>
599
+
600
+ <p><strong><em id="woption-9-Set-Title-from">Set Title from</em></strong></p>
601
+
602
+ <p>These allow you to set the <code>Title</code> option from a menu item, and, if brought into play, the <code>Hide</code> flag is ignored.
603
+ Note that the item providing the <code>Title</code> only has to be within the selected menu; it does not have to be present in the final output!
604
+ Note also that a <code>Current Item</code> setting will be used in preference to a <code>Branch Item</code> setting.</p>
605
+
606
+ <p>A relative setting - such as <code>Currrent Item</code> "-2 levels" - will top out at the root-level ancestor (which
607
+ could be the Current/Branch Item!) if there aren't enough ancestors available.
608
+ Also, an absolute setting - such as <code>Branch Item</code> "level 4" - will bottom out at the Current/Branch Item
609
+ if it's at/above the absolute level specified.</p>
610
+
611
+ <ul>
612
+ <li><p><strong id="woption-38-Current-Item">Current Item</strong> <em>(select)</em></p>
613
+
614
+ <p>Sets <code>Title</code> from the current menu item (if current menu item is in the selected menu), or an ancestor
615
+ of that item, either at an absolute or relative level.</p></li>
616
+ <li><p><strong id="woption-39-Branch-Item">Branch Item</strong> *(select)</p>
617
+
618
+ <p>Only applicable to a <code>Branch</code> filter, and sets <code>Title</code> from the <code>Branch</code> item, or an ancestor
619
+ of that item, either at an absolute or relative level.</p></li>
620
+ <li><p><strong id="woption-40-Make-it-a-Link">Make it a Link</strong> <em>(checkbox)</em></p>
621
+
622
+ <p>If the widget <code>Title</code> does actually get set using one of the options above, then this will
623
+ put an anchor around the title, using the information from the menu item that supplies the title.</p></li>
624
+ </ul>
625
+
626
+ <p><strong><em id="woption-10-Change-UL-to-OL">Change UL to OL</em></strong></p>
627
+
628
+ <p>The standard for menus is to use UL (unordered list) elements to display the output. These settings give you the option to
629
+ swap the ULs out for OLs (ordered lists).</p>
630
+
631
+ <ul>
632
+ <li><p><strong id="woption-41-Top-Level">Top Level</strong> <em>(checkbox)</em></p>
633
+
634
+ <p>Swap the outermost UL for an OL.</p></li>
635
+ <li><p><strong id="woption-42-Sub-Levels">Sub-Levels</strong> <em>(checkbox)</em></p>
636
+
637
+ <p>Swap any nested (ie. not the outermost) ULs for an OLs.</p></li>
638
+ </ul>
639
+
640
+ <p><strong><em id="woption-11-Pre-WordPress-3.6">Pre WordPress 3.6</em></strong></p>
641
+
642
+ <p>Please note that as of WordPress v3.6, the <code>Hide Widget if Empty</code> option becomes superfluous and will <strong>not</strong>
643
+ be presented (the wp_nav_menu() function has been modified to automatically suppress all HTML output if there
644
+ are no items to be displayed).</p>
645
+
646
+ <ul>
647
+ <li><p><strong id="woption-43-Hide-Widget-if-Empty">Hide Widget if Empty</strong> <em>(checkbox)</em></p>
648
+
649
+ <p>If checked, the widget will not output <em>any</em> HTML unless it finds at least one menu item that matches the Filter settings.</p></li>
650
+ </ul>
651
+ <h3 id='Container-Section'>Container Section</h3>
652
+ <ul>
653
+ <li><p><strong id="woption-44-Element">Element</strong> <em>(textbox, default "div")</em></p>
654
+
655
+ <p>The menu list is usually wrapped in a "container" element, and this is the tag for that element.
656
+ You may change it for another tag, or you may clear it out and the container will be completely removed. Please note that
657
+ WordPress is set by default to only accept "div" or "nav", but that could be changed or extended by any theme or plugin.</p></li>
658
+ <li><p><strong id="woption-45-Unique-ID">Unique ID</strong> <em>(textbox)</em></p>
659
+
660
+ <p>This allows you to specify your own id (which should be unique) for the container.</p></li>
661
+ <li><p><strong id="woption-46-Class">Class</strong> <em>(textbox)</em></p>
662
+
663
+ <p>This allows you to add your own class to the container element.</p></li>
664
+ </ul>
665
+ <h3 id='Classes-Section'>Classes Section</h3>
666
+ <ul>
667
+ <li><p><strong id="woption-47-Menu-Class">Menu Class</strong> <em>(textbox, default "menu-widget")</em></p>
668
+
669
+ <p>This is the class that will be applied to the list element that holds the entire menu.</p></li>
670
+ <li><p><strong id="woption-48-Widget-Class">Widget Class</strong> <em>(textbox)</em></p>
671
+
672
+ <p>This allows you to add your own class to the outermost element of the widget, the one that wraps the entire widget output.</p></li>
673
+ </ul>
674
+ <h3 id='Links-Section'>Links Section</h3>
675
+ <ul>
676
+ <li><p><strong id="woption-49-Before-the-Link">Before the Link</strong> <em>(textbox)</em></p>
677
+
678
+ <p>Text or HTML that will be placed immediately before each menu item's link.</p></li>
679
+ <li><p><strong id="woption-50-After-the-Link">After the Link</strong> <em>(textbox)</em></p>
680
+
681
+ <p>Text or HTML that will be placed immediately after each menu item's link.</p></li>
682
+ <li><p><strong id="woption-51-Before-the-Link-Text">Before the Link Text</strong> <em>(textbox)</em></p>
683
+
684
+ <p>Text or HTML that will be placed immediately before each menu item's link text.</p></li>
685
+ <li><p><strong id="woption-52-After-the-Link-Text">After the Link Text</strong> <em>(textbox)</em></p>
686
+
687
+ <p>Text or HTML that will be placed immediately after each menu item's link text.</p></li>
688
+ </ul>
689
+ <h3 id='Alternative-Section'>Alternative Section</h3>
690
+ <p>This is new at v3.1.0 and provides a limited dual-scenario capability, based on a couple of conditions. For example, let's say you
691
+ want to show the Current Item and its immediate children, <em>but</em> if there isn't a Current Item then you want to show the top 2 levels
692
+ of the menu : previously this was not possible solely with CMW, but now you can configure the main widget settings for the "current item"
693
+ scenario, and add an Alternative setting for when no Current Item can be determined.</p>
694
+
695
+ <ul>
696
+ <li><p><strong id="woption-53-On-condition">On condition</strong> <em>(2 selects)</em></p>
697
+
698
+ <p>Select the appropriate condition for when your Alternative configuration should be used, and also the stage within the
699
+ Filter processing when this condition should be tested for (similar to the Qualifier, <code>Current Item is in</code>). You need
700
+ values in both selects for the Alternative to be considered.</p></li>
701
+ <li><p><strong id="woption-54-Then-switch-settings-to">Then switch settings to</strong> <em>(textarea)</em></p>
702
+
703
+ <p>This should contain a CMW-generated shortcode equivalent of the configuration that you want to switch to. Please note that leaving
704
+ this empty will <strong>not</strong> prevent the Alternative kicking in if the conditions are set and met! An empty <code>switch to</code> will merely default
705
+ to the CMW's base settings (Level 1, unlimited Depth). Also note that Alternatives cannot be nested : a primary configuration is
706
+ allowed one chance to switch and that's it, so providing an Alternative-that-has-an-Alternative will not work.</p>
707
+
708
+ <p>The Assist <em>will work</em> with an Alternative - in that it displays the appropriate output - but it can get confusing as to which
709
+ configuration set is being used. There is a message displayed whenever the Alternative kicks in (green if successful, red if it
710
+ should have kicked in but couldn't due to an error in the alternative settings) so please take note of it.</p></li>
711
+ </ul>
712
+
713
+ <p>A bit more information about the <strong>Alternative</strong> is available
714
+ in <a href="http://www.wizzud.com/2014/10/03/custom-menu-wizard-wordpress-plugin-version-3-1/" target='_blank'>this article<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a>.</p>
715
+ </div></div>
716
+ </div>
717
+ <div role="tabpanel" class="tab-pane fade" id="Shortcode-Attributes">
718
+ <h2>Shortcode Attributes</h2>
719
+ <div class="row"><div class="col-md-3 col-lg-2 col-md-push-9 col-lg-push-10"><div class="tabpane-index panel panel-default"><div class="panel-body"><button class="toggle-a-z btn btn-default btn-lg pull-right" data-toggle="button" aria-pressed="false"><span class="glyphicon glyphicon-sort-by-alphabet"></span></button>
720
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-widget" data-natural-pos="0" data-az-pos="32">widget</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
721
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-title" data-natural-pos="1" data-az-pos="28">title</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
722
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-menu" data-natural-pos="2" data-az-pos="21">menu</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
723
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-level" data-natural-pos="3" data-az-pos="20">level</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
724
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-branch" data-natural-pos="4" data-az-pos="4">branch</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
725
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-items" data-natural-pos="5" data-az-pos="19">items</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
726
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-start_at" data-natural-pos="6" data-az-pos="26">start_at</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
727
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-start_mode" data-natural-pos="7" data-az-pos="27">start_mode</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
728
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-allow_all_root" data-natural-pos="8" data-az-pos="0">allow_all_root</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
729
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-depth" data-natural-pos="9" data-az-pos="9">depth</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
730
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-depth_rel_current" data-natural-pos="10" data-az-pos="10">depth_rel_current</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
731
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-ancestors" data-natural-pos="11" data-az-pos="3">ancestors</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
732
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-ancestor_siblings" data-natural-pos="12" data-az-pos="2">ancestor_siblings</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
733
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-siblings" data-natural-pos="13" data-az-pos="25">siblings</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
734
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-include_level" data-natural-pos="14" data-az-pos="18">include_level</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
735
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-exclude" data-natural-pos="15" data-az-pos="11">exclude</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
736
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-exclude_level" data-natural-pos="16" data-az-pos="12">exclude_level</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
737
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-contains_current" data-natural-pos="17" data-az-pos="8">contains_current</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
738
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-fallback" data-natural-pos="18" data-az-pos="13">fallback</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
739
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-fallback_ci_parent" data-natural-pos="19" data-az-pos="15">fallback_ci_parent</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
740
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-fallback_ci_lifo" data-natural-pos="20" data-az-pos="14">fallback_ci_lifo</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
741
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-flat_output" data-natural-pos="21" data-az-pos="17">flat_output</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
742
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-title_from" data-natural-pos="22" data-az-pos="29">title_from</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
743
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-title_linked" data-natural-pos="23" data-az-pos="30">title_linked</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
744
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-ol_root" data-natural-pos="24" data-az-pos="23">ol_root</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
745
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-ol_sub" data-natural-pos="25" data-az-pos="24">ol_sub</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
746
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-container" data-natural-pos="26" data-az-pos="5">container</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
747
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-container_id" data-natural-pos="27" data-az-pos="7">container_id</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
748
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-container_class" data-natural-pos="28" data-az-pos="6">container_class</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
749
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-menu_class" data-natural-pos="29" data-az-pos="22">menu_class</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
750
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-widget_class" data-natural-pos="30" data-az-pos="33">widget_class</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
751
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-wrap_link" data-natural-pos="31" data-az-pos="34">wrap_link</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
752
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-wrap_link_text" data-natural-pos="32" data-az-pos="35">wrap_link_text</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
753
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-alternative" data-natural-pos="33" data-az-pos="1">alternative</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
754
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-title_tag" data-natural-pos="34" data-az-pos="31">title_tag</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
755
+ <span class="visible-xs-inline visible-sm-inline visible-md-block visible-lg-block"><a href="#Shortcode-Attributes" data-scroll="#sattribute-findme" data-natural-pos="35" data-az-pos="16">findme</a><span class="hidden-md hidden-lg">&nbsp;, </span></span>
756
+ </div></div></div>
757
+ <div class="col-md-9 col-lg-10 col-md-pull-3 col-lg-pull-2">
758
+
759
+ <p>The shortcode is <strong><code>[cmwizard]</code></strong>.
760
+ Most of the attributes reflect the options available to the widget, but some have been simplified for easier use in the shortcode format.
761
+ Please note that the <strong>Hide Widget if Empty</strong> option is not available to the shortcode : it is set to enabled, and if there are no menu items
762
+ found then there will be no output from the shortcode.</p>
763
+
764
+ <p>The simplest way to build a shortcode is to use a widget : as you set options, the equivalent shortcode is displayed at the base of
765
+ the widget (v3+) and the base of the "assist". The widget itself need not be assigned to a widget area, so you can construct your
766
+ shortcode using a widget in the Inactive Widgets area if you have no need for an active one. Please remember that any options you play
767
+ with while constructing your shortcode <strong><em>do not have to be Saved</em></strong> to the widget itself! Just copy-paste the shortcode when
768
+ you are happy with it.</p>
769
+
770
+ <h4 id="sattribute-widget">widget</h4>
771
+
772
+ <p><em>integer</em> : <strong>!NEW!</strong> <em>from v3.1.5</em>, the shortcode will accept a <code>widget=N</code> attribute which will load an
773
+ existing widget instance.</p>
774
+
775
+ <p>The shortcode - resembling <strong><code>[cmwizard widget=N/]</code></strong>, where <strong>N</strong> is an integer - is provided at the base
776
+ of each widget, below the widget's "equivalent" shortcode.</p>
777
+
778
+ <p>It will look for the instance in all active sidebars, <em>and</em> the Inactive Widgets area.
779
+ You can prevent inspection of the Inactive Widgets area by adding an <code>inactive=0</code> attribute.
780
+ It will <strong><em>not</em></strong> look in any other <em>Inactive Sidebar...</em> area unless you specifically tell it to do so by
781
+ adding an <code>orphaned=1</code> attribute.</p>
782
+
783
+ <p>Using this attribute reduces the shortcode length, and may cut down on maintenance where
784
+ you have the same shortcode in a number of places ... as long as you are prepared to keep the widget instance (even if it's in the
785
+ Inactive Widgets area). You can override the widget instance's settings by supplying any of the standard shortcode attributes.</p>
786
+
787
+ <h4 id="sattribute-title">title</h4>
788
+
789
+ <p><em>string</em> : The output's <code>Title</code>, which may be overridden by <strong>title_from</strong>. Note that there is no shortcode equivalent of the widget's <code>Hide</code> option for the title.</p>
790
+
791
+ <h4 id="sattribute-menu">menu</h4>
792
+
793
+ <p><em>string or integer</em> : Accepts a menu name or id. If not provided, the shortcode will attempt to find the first menu (alphabetically)
794
+ that has menu items attached to it, and use that.</p>
795
+
796
+ <h4 id="sattribute-level">level</h4>
797
+
798
+ <p><em>integer</em> : Sets the <code>Level</code> filter to the specified (greater than zero) value. Defaults to 1, and is ignored if either <strong>branch</strong> or <strong>items</strong> is specified.</p>
799
+
800
+ <h4 id="sattribute-branch">branch</h4>
801
+
802
+ <p><em>string or integer</em> : If not empty then <code>Branch</code> is set as the primary filter, with the branch item being set from the assigned value:</p>
803
+
804
+ <ul>
805
+ <li>If numeric, it is taken as being the id of a menu item.</li>
806
+ <li>If set to either <em>"current"</em> or <em>"current-item"</em> then the <code>Branch</code> item is set to "Current Item".</li>
807
+ <li>If any other string, it is taken to be the title of a menu item (within the selected menu). The widget will look for the first <em>caseless</em> title match, so specifying <code>branch="my menu item"</code> will match against a menu item with the title "My Menu Item".</li>
808
+ </ul>
809
+
810
+ <h4 id="sattribute-items">items</h4>
811
+
812
+ <p><em>string</em> : Comma-separated list of meu item ids, where an id can optionally be followed by a '+' to include all its descendants (eg. "23+"). Takes priority over <strong>branch</strong>.</p>
813
+
814
+ <h4 id="sattribute-start_at">start_at</h4>
815
+
816
+ <p><em>string</em> : This is only relevant to a <code>Branch</code> filter, and consists of a signed or unsigned integer that indicates either a relative
817
+ (to the selected branch item) or absolute level to start your output at (ref. the widget's <code>Starting at</code> option under <em>Secondary Filter</em>,
818
+ <a href="#Widget-Options" data-scroll="#Filters-Section">Filters Section</a>).
819
+ By default the starting level for output is the branch item's level. A relative level is indicated by a signed (ie. preceded by
820
+ a "+" or "-") integer, eg. <code>start_at="+1"</code>, while an absolute level is unsigned, eg. <code>start_at="1"</code>. Some examples :</p>
821
+
822
+ <ul>
823
+ <li><code>start_at="+1"</code> : (relative) start at the branch item's level + 1 (also accepts <code>start_at="children"</code>)</li>
824
+ <li><code>start_at="-1"</code> : (relative) start at the branch item's level - 1 (also accepts <code>start_at="parent"</code>)</li>
825
+ <li><code>start_at="-2"</code> : (relative) would be the "grandparent" level</li>
826
+ <li><code>start_at="1"</code> : (absolute) start at the root item of the selected branch (also accepts <code>start_at="root"</code>)</li>
827
+ <li><code>start_at="2"</code> : (absolute) start at one level below root (still within the selected branch)</li>
828
+ </ul>
829
+
830
+ <h4 id="sattribute-start_mode">start_mode</h4>
831
+
832
+ <p><em>string</em> : This has only one accepted value - "level" - and is only applicable for a <code>Branch</code> filter whose <strong>start_at</strong> setting returns
833
+ in an item that is at or above the selected branch item (relatively or absolutely).
834
+ Setting <code>start_mode="level"</code> forces the widget to use not only the resultant starting item
835
+ and its relevant descendants, but also all that item's siblings <em>and their descendants</em>
836
+ (ref. the widget's <code>Level</code> radio option under <em>Secondary Filter</em>,
837
+ <a href="#Widget-Options" data-scroll="#Filters-Section">Filters Section</a>).</p>
838
+
839
+ <h4 id="sattribute-allow_all_root">allow_all_root</h4>
840
+
841
+ <p><em>switch, off by default, 1 to enable</em> : See widget's <code>Allow all Root Items</code> option, under <em>Secondary Filter</em>,
842
+ <a href="#Widget-Options" data-scroll="#Filters-Section">Filters Section</a>.</p>
843
+
844
+ <h4 id="sattribute-depth">depth</h4>
845
+
846
+ <p><em>integer, default 0 (unlimited)</em> : See widget's <code>For Depth</code> option, under <em>Secondary Filter</em>,
847
+ <a href="#Widget-Options" data-scroll="#Filters-Section">Filters Section</a>.</p>
848
+
849
+ <h4 id="sattribute-depth_rel_current">depth_rel_current</h4>
850
+
851
+ <p><em>switch, off by default, 1 to enable</em> : See widget's <code>Relative to Current Item</code> option, under <em>Secondary Filter</em>,
852
+ <a href="#Widget-Options" data-scroll="#Filters-Section">Filters Section</a>.</p>
853
+
854
+ <h4 id="sattribute-ancestors">ancestors</h4>
855
+
856
+ <p><em>integer, default 0 (off)</em> : Sets an absolute level (positive integer), or a relative number of levels (negative integer), for which
857
+ the ancestors of the <code>Branch</code> filter item should be included. See widget's <code>Branch Ancestors</code> option, under <em>Inclusions</em>,
858
+ <a href="#Widget-Options" data-scroll="#Filters-Section">Filters Section</a>. (only relevant to a <code>Branch</code> filter)</p>
859
+
860
+ <h4 id="sattribute-ancestor_siblings">ancestor_siblings</h4>
861
+
862
+ <p><em>integer, default 0 (off)</em> : Sets an absolute level (positive integer), or a relative number of levels (negative integer), for which
863
+ the siblings of ancestors of the <code>Branch</code> filter item should be included. See widget's <code>... with Siblings</code> option, under <em>Inclusions</em>,
864
+ <a href="#Widget-Options" data-scroll="#Filters-Section">Filters Section</a>. (only relevant to a <code>Branch</code> filter)</p>
865
+
866
+ <h4 id="sattribute-siblings">siblings</h4>
867
+
868
+ <p><em>switch, off by default, 1 to enable</em> : See widget's <code>Branch Siblings</code> option, under <em>Inclusions</em>,
869
+ <a href="#Widget-Options" data-scroll="#Filters-Section">Filters Section</a>. (only relevant to a <code>Branch</code> filter)</p>
870
+
871
+ <h4 id="sattribute-include_level">include_level</h4>
872
+
873
+ <p><em>string</em> : A level (1, 2, 3, etc), optionally followed by a "+" or "-" to include all subsequent (lower) or prior (higher)
874
+ levels respectively. For example :</p>
875
+
876
+ <ul>
877
+ <li><code>include_level="2"</code> : include all items at level 2</li>
878
+ <li><code>include_level="2-"</code> : include all level 1 <strong>and</strong> level 2 items</li>
879
+ <li><code>include_level="2+"</code> : include all items at level 2 or greater.</li>
880
+ </ul>
881
+
882
+ <p>Note that prior to v3.0.4, this was <strong>include_root</strong> (a switch), which only included the root level : <code>include_root=1</code> is still accepted, even
883
+ though now deprecated, and is equivalent to setting <code>include_level="1"</code>. However, if <strong>include_level</strong> is specified then it takes precedence.</p>
884
+
885
+ <h4 id="sattribute-exclude">exclude</h4>
886
+
887
+ <p><em>string</em> : Comma-separated list of meu item ids, where an id can optionally be followed by a '+' to include all its descendants (eg. "23+").</p>
888
+
889
+ <h4 id="sattribute-exclude_level">exclude_level</h4>
890
+
891
+ <p><em>string</em> : A level (1, 2, 3, etc), optionally followed by a "+" or "-" to exclude all subsequent (lower) or prior (higher)
892
+ levels respectively. See the examples for <strong>include_level</strong> above.</p>
893
+
894
+ <h4 id="sattribute-contains_current">contains_current</h4>
895
+
896
+ <p><em>string</em> : Accepted values : "menu", "primary", "secondary", "inclusions", or "output". See widget's <em>Qualifier</em> options,
897
+ under <a href="#Widget-Options" data-scroll="#Filters-Section">Filters Section</a>,
898
+ for an explanation of the respective settings.</p>
899
+
900
+ <h4 id="sattribute-fallback">fallback</h4>
901
+
902
+ <p><em>string</em> : This enables the widget's <em>If Current Item has no children</em> fallback (ref. <a href="#Widget-Options" data-scroll="#Fallbacks-Section">Fallbacks Section</a>)...</p>
903
+
904
+ <ul>
905
+ <li><em>"parent"</em> : Sets the widget's Fallback option to "Start at : -1 (parent)"</li>
906
+ <li><em>"current"</em> : Sets the widget's Fallback option to "Start at : the Current Item"</li>
907
+ <li><em>"quit"</em> : Sets the widget's Fallback option to "No output!"</li>
908
+ </ul>
909
+
910
+ <p>The first two values can be further qualified by appending a comma and a digit, eg. <code>fallback="current,1"</code>
911
+ or <code>fallback="parent,2"</code>, which will also set the widget's <code>For Depth</code> fallback option to the value of the
912
+ digit(s).</p>
913
+
914
+ <p>Optionally, "+siblings" can also be used (comma-separated, with or without a depth digit) to indicate that
915
+ siblings of the "parent" or "current" fallback item should also be included. The order of the comma-separated
916
+ values is not important, so <code>fallback="current,+siblings,1"</code> is the same as <code>fallback="current,1,+siblings"</code>,
917
+ and <code>fallback="2,parent"</code> is the same as <code>fallback="parent,2"</code>, etc.</p>
918
+
919
+ <h4 id="sattribute-fallback_ci_parent">fallback_ci_parent</h4>
920
+
921
+ <p><em>switch, off by default, 1 to enable</em> : See widget's <em>If no Current Item can be found</em> entry in the
922
+ <a href="#Widget-Options" data-scroll="#Fallbacks-Section">Fallbacks Section</a>.</p>
923
+
924
+ <h4 id="sattribute-fallback_ci_lifo">fallback_ci_lifo</h4>
925
+
926
+ <p><em>switch, off by default, 1 to enable</em> : See widget's <em>If more than 1 possible Current Item</em> entry in the
927
+ <a href="#Widget-Options" data-scroll="#Fallbacks-Section">Fallbacks Section</a>.</p>
928
+
929
+ <h4 id="sattribute-flat_output">flat_output</h4>
930
+
931
+ <p><em>switch, off by default, 1 to enable</em> : See widget's <code>Flat</code> option, under <a href="#Widget-Options" data-scroll="#Output-Section">Output Section</a>.</p>
932
+
933
+ <h4 id="sattribute-title_from">title_from</h4>
934
+
935
+ <p><em>string</em> : Supply a "current" and/or a "branch" item (comma-separated), corresponding to the 2 selects in the widget's <code>Set Title from</code> options,
936
+ under <a href="#Widget-Options" data-scroll="#Output-Section">Output Section</a>.</p>
937
+
938
+ <ul>
939
+ <li><em>"current"</em> : take the title from the Current Item</li>
940
+ <li><em>"currentN"</em> : take the title from an ancestor of the Current Item, where <strong>N</strong> is the literal level of the ancestor, eg. "current2" would be the Current Item's ancestor that sits at level 2</li>
941
+ <li><em>"current-N"</em> : take the title from an ancestor of the Current Item, where <strong>N</strong> is the number of levels above the current item, eg. "current-2" would be the Current Item's grand-parent</li>
942
+ <li><em>"current-root"</em> : equivalent to <em>"current1"</em>; takes the title from the Current Item's root-level ancestor</li>
943
+ <li><em>"current-parent"</em> : equivalent to <em>"current-1"</em>; takes the title from the Current Item's parent</li>
944
+ </ul>
945
+
946
+ <p>All the above are also available for the Branch Item, eg. <em>"branch"</em>, <em>"branch1"</em>, <em>"branch-2"</em>, etc.
947
+ As an example, <code>title_from="current-1,branch"</code> will take the title from either the Current Item's parent - if
948
+ there is a Current Item found in the menu - or the Primary Filter's Branch setting if there isn't a Current
949
+ Item available.</p>
950
+
951
+ <h4 id="sattribute-title_linked">title_linked</h4>
952
+
953
+ <p><em>switch, off by default, 1 to enable</em> : Makes the title into a link if the title comes from one of the <code>title_from</code> options.</p>
954
+
955
+ <h4 id="sattribute-ol_root">ol_root</h4>
956
+
957
+ <p><em>switch, off by default, 1 to enable</em> : See widget's <code>Top Level</code> option, under <em>Change UL to OL</em> in the <a href="#Widget-Options" data-scroll="#Output-Section">Output Section</a>.</p>
958
+
959
+ <h4 id="sattribute-ol_sub">ol_sub</h4>
960
+
961
+ <p><em>switch, off by default, 1 to enable</em> : See widget's <code>Sub-Levels</code> option, under <em>Change UL to OL</em> in the <a href="#Widget-Options" data-scroll="#Output-Section">Output Section</a>.</p>
962
+
963
+ <h4 id="sattribute-container">container</h4>
964
+
965
+ <p><em>string</em> : See widget's <code>Element</code> option, under <a href="#Widget-Options" data-scroll="#Container-Section">Container Section</a>.</p>
966
+
967
+ <h4 id="sattribute-container_id">container_id</h4>
968
+
969
+ <p><em>string</em> : See widget's <code>Unique ID</code> option, under <a href="#Widget-Options" data-scroll="#Container-Section">Container Section</a>.</p>
970
+
971
+ <h4 id="sattribute-container_class">container_class</h4>
972
+
973
+ <p><em>string</em> : See widget's <code>Class</code> option, under <a href="#Widget-Options" data-scroll="#Container-Section">Container Section</a>.</p>
974
+
975
+ <h4 id="sattribute-menu_class">menu_class</h4>
976
+
977
+ <p><em>string</em> : See widget's <code>Menu Class</code> option, under <a href="#Widget-Options" data-scroll="#Classes-Section">Classes Section</a>.</p>
978
+
979
+ <h4 id="sattribute-widget_class">widget_class</h4>
980
+
981
+ <p><em>string</em> : See widget's <code>Widget Class</code> option, under <a href="#Widget-Options" data-scroll="#Classes-Section">Classes Section</a>.</p>
982
+
983
+ <h4 id="sattribute-wrap_link">wrap_link</h4>
984
+
985
+ <p><em>string</em> : This is an optional tag name (eg. "div", "p", "span") that, if provided, will be made into HTML start/end tags
986
+ and sent through to the widget as its <code>Before the Link</code> and <code>After the Link</code> options (ref. <a href="#Widget-Options" data-scroll="#Links-Section">Links Section</a>).
987
+ Please note that the shortcode usage - a simple tag name - is much more restrictive than the widget's options, which allow HTML.</p>
988
+
989
+ <h4 id="sattribute-wrap_link_text">wrap_link_text</h4>
990
+
991
+ <p><em>string</em> : This is an optional tag name (eg. "span", "em", "strong") that, if provided, will be made into HTML start/end tags
992
+ and sent through to the widget as its <code>Before the Link Text</code> and <code>After the Link Text</code> options (ref. <a href="#Widget-Options" data-scroll="#Links-Section">Links Section</a>).
993
+ Please note that the shortcode usage - a simple tag name - is much more restrictive than the widget's options, which allow HTML.</p>
994
+
995
+ <h4 id="sattribute-alternative">alternative</h4>
996
+
997
+ <p><em>string</em> : This is 2 settings separated by a comma, reflecting the <code>On condition</code> options under the
998
+ <a href="#Widget-Options" data-scroll="#Alternative-Section">Alternative Section</a>.
999
+ Possible values are:</p>
1000
+
1001
+ <ul>
1002
+ <li>One of "current", "no-current" or "no-output" : the condition to test for</li>
1003
+ <li>One of "menu", "primary", "secondary", "inclusions", or "output" : the stage at which to test the condition</li>
1004
+ </ul>
1005
+
1006
+ <p>Eg. <code>alternative="no-current,inclusions"</code> would test for the absence of a Current Item in the filtered menu items, having completed
1007
+ the Inclusions stage, and attempt to switch to the Alternative settings.</p>
1008
+
1009
+ <p>The actual Alternative settings - a cut-down shortcode - are placed as content between the shortcodes start and end tags, and this is
1010
+ the only time that the use of a self-terminating shortcode is not sufficient. When specifiying the Alternative settings, <em>do not</em>
1011
+ include the square brackets, otherwise WordPress will interpret it as a nested shortcode!</p>
1012
+
1013
+ <p>For example, to set a primary configuration of "show Current Branch plus any kids", with an Alternative of "show top 2 levels" if no
1014
+ current item can be found anywhere in the menu...</p>
1015
+
1016
+ <pre><code>[cmwizard menu=NN branch=current alternative=&quot;no-current,menu&quot;]depth=2[/cmwizard]</code></pre>
1017
+
1018
+ <p>Alternatively, you could switch it around and say the primary configuration is "show top 2 levels", with an Alternative of
1019
+ "show Current Branch plus kids" if a current item <em>can</em> be found within the menu...</p>
1020
+
1021
+ <pre><code>[cmwizard menu=NN depth=2 alternative=&quot;current,menu&quot;]branch=current[/cmwizard]</code></pre>
1022
+
1023
+ <p>Note that Alternative (eg. "branch=current") does not require a <code>menu</code> option, because you can't change the menu so the primary
1024
+ configuration's setting is always used.</p>
1025
+
1026
+ <p>As ever, the best way to construct a full shortcode, including an alternative, is to use the Assist : Use one instance of the CMW
1027
+ widget to build your Alternative settings, copy the equivalent shortcode into the Alternative option of a second instance of the CMW
1028
+ widget, and then continue configuring that second instance to be your primary configuration; your final shortcode can simply be lifted
1029
+ from the second instance!</p>
1030
+
1031
+ <p>A bit more information about the Alternative option is available
1032
+ in <a href="http://www.wizzud.com/2014/10/03/custom-menu-wizard-wordpress-plugin-version-3-1/" target='_blank'>this article<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a>.</p>
1033
+
1034
+ <h4 id="sattribute-title_tag">title_tag</h4>
1035
+
1036
+ <p><em>string</em> : An optional tag name (eg. "h1", "h3", etc) to replace the default "h2" used to enclose the widget title.
1037
+ Please note that this attribute has no equivalent in the widget options, because it <em>only</em> applies when a widget is instantiated via a shortcode.</p>
1038
+
1039
+ <h4 id="sattribute-findme">findme</h4>
1040
+
1041
+ <p><em>switch, off by default, 1 to enable</em> : This is a utility intended for editors only, and output is restricted to those with edit_pages capability.
1042
+ If enabled it will return a list of posts that contain a CMW shortcode. If <code>findme</code> is set, the only other attribute that is taken any
1043
+ notice of is <code>title</code>, which will be output (if supplied) as an H3 in front of the list. Example :</p>
1044
+
1045
+ <pre><code>[cmwizard findme=1 title=&quot;Posts containing a CMW shortcode...&quot;/]</code></pre>
1046
+
1047
+ <p>Note that the information provided by this utility is also available from any widget's "assist".</p>
1048
+
1049
+
1050
+ </div></div>
1051
+ <h3>Shortcode Examples</h3>
1052
+
1053
+ <ul>
1054
+ <li><p>Show the entire "main" menu</p>
1055
+
1056
+ <pre><code>[cmwizard menu=main/]</code></pre></li>
1057
+ <li><p>Show the children of the current menu item within the "main" menu, for unlimited depth, setting the widget title from the current menu item</p>
1058
+
1059
+ <pre><code>[cmwizard menu=main branch=current start_at=children title_from=current/]</code></pre></li>
1060
+ <li><p>From the "animals" menu, show all the items <em>immediately</em> below "Small Dogs", plus "Small Dogs" and its sibling items, as ordered lists</p>
1061
+
1062
+ <pre><code>[cmwizard menu="animals" branch="small dogs" depth=2 include="siblings" ol_root=1 ol_sub=1/]</code></pre></li>
1063
+ <li><p>From the "animals" menu, show the entire "Small Animals" branch, with the sole exception of the "Small Animals" item itself, whenever "Small Animals" or one of its descendants is the current menu item</p>
1064
+
1065
+ <pre><code>[cmwizard menu="animals" branch="small animals" start_at=children contains_current=primary/]</code></pre></li>
1066
+ <li><p>Show the entire "main" menu entitled "Main Menu" <em>unless</em> there's a current menu item, in which case show the current menu item, its siblings and its immediate children, and entitle it "Nearest and Dearest!"</p>
1067
+
1068
+ <pre><code>[cmwizard menu=main title="Main Menu" alternative="current,menu"]title="Nearest and Dearest!" branch=current depth=2 siblings=1[/cmwizard]</code></pre></li>
1069
+ </ul> </div>
1070
+ <div role="tabpanel" class="tab-pane fade" id="FAQs">
1071
+ <div class="panel-group" role="tablist" aria-multiselectable="true">
1072
+ <h2>FAQs</h2>
1073
+ <div class="row"><div class="col-sm-9">
1074
+ <p>If you have a question or problem that is not covered here, please use the <a href="https://wordpress.org/support/plugin/custom-menu-wizard" target='_blank'>Support forum<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a>.</p>
1075
+
1076
+ </div>
1077
+ <div class="col-sm-3 text-right"><div class="btn-group" role="group" aria-label="..."><button type="button" class="faqs-all btn btn-default">Show All</button><button type="button" class="faqs-all btn btn-default">Hide All</button></div></div>
1078
+ <div class="col-xs-12">&nbsp;</div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-1-heading" data-toggle="collapse" href="#faq-1" aria-expanded="false" aria-controls="faq-1"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>Are there any known problems/restrictions?</h4></a><div id="faq-1" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-1-heading"><div class="panel-body">
1079
+
1080
+
1081
+ <p>Yep, 'fraid so :</p>
1082
+
1083
+ <ol>
1084
+ <li>The widget will only recognise one "current" item (prior to v2.0.2 it was the last one found; as of v2.0.2, it's the first one encountered, but v3.1.5 add a switch that lets you opt for the last one found). It is perfectly possible to have more than one menu item marked as "current", but if CMW has been configured to filter on anything related to a "current menu item" it can only choose one. The simplest example of multiple "current" items is if you add the same page to a menu more than once, but any other plugin that adds and/or manipulates menu items could potentially cause problems for CMW.</li>
1085
+ <li>The widget's "assist" uses jQuery UI's Dialog, which unfortunately (in versions 1.10.3/4) has a <em>really</em> annoying bug in its handling of a draggable (ie. when you drag the Dialog's title bar to reposition it on the page) when the page has been scrolled. It is due to be fixed in UI v1.11.0, but meantime I have defaulted the Dialog to fixed position, with an option to toggle back to absolute : it's not perfect but it's the best compromise I can come up with to maintain some sort of useability.</li>
1086
+ </ol>
1087
+
1088
+
1089
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-2-heading" data-toggle="collapse" href="#faq-2" aria-expanded="false" aria-controls="faq-2"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>Why isn't it working? Why is there no output?</h4></a><div id="faq-2" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-2-heading"><div class="panel-body">
1090
+
1091
+
1092
+ <p>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
1093
+ the option settings on the widget/shortcode. The quickest way to resolve any such issues is to use the widget's interactive "assist", and
1094
+ 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
1095
+ infallible (and it's been proven a fair few times!), so if you still have problems then please let me have as much information as possible
1096
+ (the shortcode equivalent of your settings is a good start?) and I will endeavour to help.</p>
1097
+
1098
+ <p>Please note that simply reporting "It doesn't work" is not
1099
+ the most useful of feedbacks, and is unlikely to get a response other than, possibly, a request for more details.</p>
1100
+
1101
+
1102
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-3-heading" data-toggle="collapse" href="#faq-3" aria-expanded="false" aria-controls="faq-3"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>How do I use the "assist"?</h4></a><div id="faq-3" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-3-heading"><div class="panel-body">
1103
+
1104
+
1105
+ <p>The widget's interactive "assist" is specific to each widget instance. It is a javascript-driven <em>emulator</em> that uses the widget instance's
1106
+ option settings - including the menu selected - to build a pictorial representation of the menu and show you, in blue, which menu items will
1107
+ 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
1108
+ some of the more advanced HTML-modifying options such as can be found under the Container, Classes or Links sections.
1109
+ 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
1110
+ item to change). The "current menu item" is shaded red, with its parent shaded orange and ancestors shaded yellow. All changes in the
1111
+ "current menu item" and the widget options are immediately reflected by the "assist" (text fields in the widget options simply need to lose
1112
+ focus).</p>
1113
+
1114
+ <p>The red cross to the left of each menu item toggles the Exclusions setting for the item and/or its descendants. The button has 3 settings :</p>
1115
+
1116
+ <ul>
1117
+ <li>Off (dimmed)</li>
1118
+ <li>Just this item (white on red)</li>
1119
+ <li>This item <em>plus</em> all its descendants (white on red, with a small yellow plus sign)</li>
1120
+ </ul>
1121
+
1122
+ <p>Just click through the toggle states. When the Primary Filter is set to "Items", the green tick buttons to the right of each menu item
1123
+ work in the same way.</p>
1124
+
1125
+ <p>Note that if a green "Alternate settings" message is showing then the ticks and crosses buttons will show the approriate Alternative
1126
+ settings but they will be slightly opaque and they will <em>not</em> be clickable!</p>
1127
+
1128
+ <p>Once you are happy with the results, having tested all possible settings of "current menu item" (if it applies), then simply Save the widget.
1129
+ If you are using a shortcode implementation, then copy-paste the shortcode text - at the base of either the "assist" or the widget form - straight into your post.</p>
1130
+
1131
+
1132
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-4-heading" data-toggle="collapse" href="#faq-4" aria-expanded="false" aria-controls="faq-4"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>Is there an easy way to construct the shortcode?</h4></a><div id="faq-4" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-4-heading"><div class="panel-body">
1133
+
1134
+
1135
+ <p>Yes, use a widget form. The shortcode for all the selected/specified options is show at the base of the widget (v3+) and the base of the
1136
+ "assist". The widget does not have to be placed within a widget area, it can also be used from the Inactive Widgets area.</p>
1137
+
1138
+
1139
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-5-heading" data-toggle="collapse" href="#faq-5" aria-expanded="false" aria-controls="faq-5"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>Do I have to Save the widget if I am using a shortcode?</h4></a><div id="faq-5" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-5-heading"><div class="panel-body">
1140
+
1141
+
1142
+ <p>Only if (as of v3.1.5) you are using the <code>widget=N</code> attribute, which refers back to an existing widget instance
1143
+ for its settings.</p>
1144
+
1145
+
1146
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-6-heading" data-toggle="collapse" href="#faq-6" aria-expanded="false" aria-controls="faq-6"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>How do I get the menu item ids for the 'Items' option?</h4></a><div id="faq-6" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-6-heading"><div class="panel-body">
1147
+
1148
+
1149
+ <p>Use the widget's interactive "assist" (see above). Within the representative menu structure, each menu item's id is set in its title
1150
+ attribute, so should be seen when the cursor is moved over the item. A simpler way is to check the <code>Items</code> option : the "assist" will
1151
+ then show a green tick "checkbox" to the right of each menu item and you simply [un]check the items as required. Each selection will be reflected back into the
1152
+ widget's <code>Items</code> settings, and also in the shortcode texts.</p>
1153
+
1154
+ <p>The more painstaking way is to go to Appearance, Menus and select the relevant menu; hover over the <em>edit</em>, <em>Remove</em>, or <em>Cancel</em> link for an item and look in
1155
+ the URL (the link's href) for <code>menu-item=NNN</code> ... the NNN is the menu item id.</p>
1156
+
1157
+
1158
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-7-heading" data-toggle="collapse" href="#faq-7" aria-expanded="false" aria-controls="faq-7"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>How do I get the menu item ids for the 'Exclude Ids' option?</h4></a><div id="faq-7" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-7-heading"><div class="panel-body">
1159
+
1160
+
1161
+ <p>The "assist" shows a red cross "checkbox" to the left of each menu item, and [un]checking the items will reflect back into the options and
1162
+ shortcode texts. Otherwise, it's the same principle as outlined above for <code>Items</code> ids.</p>
1163
+
1164
+
1165
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-8-heading" data-toggle="collapse" href="#faq-8" aria-expanded="false" aria-controls="faq-8"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>What's the difference between including Branch Siblings (or Branch Ancestors + Siblings), and switching to 'Level' instead of 'Item' in the Secondary Filter section?</h4></a><div id="faq-8" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-8-heading"><div class="panel-body">
1166
+
1167
+
1168
+ <p>If you elect to include Branch [Ancestor] Siblings, you will <em>only</em> get the siblings, <strong>not</strong> their descendants (assuming they have any).
1169
+ On the other hand, if you make <code>Starting at</code> use 'Level' instead of 'Item' then siblings <em>and their descendants</em> will be added to the filter.</p>
1170
+
1171
+ <p>For example, let's say that Bravo and Charlie are sibling items immediately below Alpha, and that Bravo is the selected Branch Item,
1172
+ with <code>Starting at</code> set to "the Branch" (ie. Bravo). If you switch from "Item" to "Level" then both Bravo, Charlie, <em>and all their descendants</em>,
1173
+ will become eligible for filtering. If you left "Item" enabled, and switched on the inclusion of Branch Siblings, then Bravo and Charlie
1174
+ would both still be eligible, but only <em>Bravo's descendants</em> would be; not Charlie's!</p>
1175
+
1176
+
1177
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-9-heading" data-toggle="collapse" href="#faq-9" aria-expanded="false" aria-controls="faq-9"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>Where is the styling of the output coming from, and how do I change it?</h4></a><div id="faq-9" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-9-heading"><div class="panel-body">
1178
+
1179
+
1180
+ <p>The widget does not supply any ouput styling (at all!). This is because I have absolutely no idea where you are going to place either the
1181
+ widget (sidebar, footer, header, ad-hoc, etc?) or the shortcode (page content, post content, widget content, custom field, etc?) and everyone's
1182
+ requirements for styling are likely to be different ... possibly even within the same web page's output. So all styling is down to your theme,
1183
+ and if you wish to modify it you will need to add to your theme's stylesheet.</p>
1184
+
1185
+ <p>The safest way to do this is via a child theme, so that any changes you make will not be lost if/when the main theme gets updated. The best
1186
+ way to test your changes is by utilising the developer capabilities that are available in most modern browsers (personally, I could not
1187
+ do without Firefox and the Firebug extension!) and dynamically applying/modifying styles, possibly utilising the custom classes that the
1188
+ widget applies to its output, or the Container options for a user-defined id or class.</p>
1189
+
1190
+
1191
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-10-heading" data-toggle="collapse" href="#faq-10" aria-expanded="false" aria-controls="faq-10"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>How can I find all my posts/pages that have a CMW shortcode so that I can upgrade them?</h4></a><div id="faq-10" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-10-heading"><div class="panel-body">
1192
+
1193
+
1194
+ <p>There is a button on the widget's "assist" - <code>[...]</code> - that will provide a list of posts/pages whose content, or meta data (custom fields),
1195
+ contains any CMW shortcode. Each entry is a link that opens the item in a new tab/window. The link's title gives a bit more information :
1196
+ post type, id, whether the shortcode(s) are in content and/or meta data, and the shortcode(s) concerned.
1197
+ This utility does not check things like text widgets, plugin-specific tables, theme-provided textareas, etc.</p>
1198
+
1199
+ <p>There is also an extension to the shortcode - <code>[cmwizard findme=1/]</code> - that will output the same information, should you not be able to use
1200
+ the "assist" (for some unknown reason). You may optionally provide a title attribute; any other attributes are ignored.
1201
+ Note that output from this shortcode extension is restricted to users with edit_pages capability.</p>
1202
+
1203
+
1204
+ </div></div></div><div class="panel panel-info"><a class="panel-heading show" role="tab" id="faq-11-heading" data-toggle="collapse" href="#faq-11" aria-expanded="false" aria-controls="faq-11"><h4 class="panel-title"><span class="glyphicon glyphicon-triangle-bottom pull-right"></span><span class="glyphicon glyphicon-triangle-top pull-right"></span>Is Version 2 of the widget, including the old [custom_menu_wizard/] shortcode, still supported?</h4></a><div id="faq-11" class="panel-collapse collapse" role="tabpanel" aria-labelledby="faq-11-heading"><div class="panel-body">
1205
+
1206
+
1207
+ <p>In Version 3, Yes. However, I highly recommend that you upgrade your widgets &amp; shortcodes to the latest versions,
1208
+ because Version 2 will <strong>not</strong> be supported beyond Version 3.</p>
1209
+ </div></div></div></div> </div>
1210
+ <div role="tabpanel" class="tab-pane fade" id="Changelog">
1211
+ <h2>Changelog</h2>
1212
+ <h4>3.1.5</h4>
1213
+
1214
+ <ul>
1215
+ <li>addition : expanded Title From to allow absolute ancestor levels (besides root) and relative ancestor levels</li>
1216
+ <li>addition : added a fallback option to switch determination of Current Item from first-found to last-found</li>
1217
+ <li>addition : added a shortcode attribute that loads an existing widget instance : <code>[cmwizard widget=N/]</code></li>
1218
+ <li>documentation : updated, and provided an html version in the plugin download</li>
1219
+ </ul>
1220
+
1221
+ <h4>3.1.4</h4>
1222
+
1223
+ <ul>
1224
+ <li>bugfix : in shortcode processing, any supplied Alternative settings weren't being used. thanks corrideat</li>
1225
+ <li>bugfix : prevent texturization of shortcode's content, for when it is being used with an Alternative setting</li>
1226
+ <li>addition : the ability to make a title into a link when the title has been set from a menu item</li>
1227
+ </ul>
1228
+
1229
+ <h4>3.1.3</h4>
1230
+
1231
+ <ul>
1232
+ <li>tweak : minor change to css for the assist when running under the Customizer (WordPress 4.1)</li>
1233
+ </ul>
1234
+
1235
+ <h4>3.1.2</h4>
1236
+
1237
+ <ul>
1238
+ <li>modified the readme : documentation for the Shortcode Attributes has been moved to the Installation page (to avoid being truncated)</li>
1239
+ </ul>
1240
+
1241
+ <h4>3.1.1</h4>
1242
+
1243
+ <ul>
1244
+ <li>bugfix : only show the allow_all_root setting in the shortcode equivalent if the primary filter is by branch</li>
1245
+ <li>addition : work-around for when a theme inadvertently(!) de-registers the widget, which then prevents the shortcode working</li>
1246
+ </ul>
1247
+
1248
+ <h4>3.1.0</h4>
1249
+
1250
+ <ul>
1251
+ <li>addition : new Alternative section which takes a cmwizard shortcode and conditionally applies it as an entirely new widget configuration</li>
1252
+ <li>addition : new fallback switch which enables an item marked as current_item_parent to be used as current item when no other current item is found</li>
1253
+ <li>bugfix : updated the determination of current item so that a paged (?paged=2, etc) Home page still shows Home page as being current</li>
1254
+ <li>bugfix : fixed code introduced in v3.0.4 that prevented CMW script loading on the customizer page - when the Widget Customizer plugin is loaded - for WordPress v3.8 and below</li>
1255
+ <li>bugfix : stop disabling selected fields based on other settings, because this caused the customizer to wipe values that may have been still required</li>
1256
+ </ul>
1257
+
1258
+ <h4>3.0.4</h4>
1259
+
1260
+ <ul>
1261
+ <li>bugfix : corrected the display of the "No Current Item!" warning in the "assist"</li>
1262
+ <li>bugfix : corrected the enabling/disabling of a couple of fields in the widget form, and tweaked the indentation for better responsiveness</li>
1263
+ <li>bugfix : corrected the options setup when in accessibility mode with javascript enabled</li>
1264
+ <li>addition : added a warning about the accuracy of the shortcode when javascript is disabled</li>
1265
+ <li>addition : extended the All Root Items inclusion to be a selectable number of levels (as per the Exclusions by Level)</li>
1266
+ </ul>
1267
+
1268
+ <h4>3.0.3</h4>
1269
+
1270
+ <ul>
1271
+ <li>bugfix : removed all occurrences of "Plugin " followed by "Name" from everywhere except the main plugin file to avoid update() reporting Invalid Header when activating straight from installation (rather than from the Plugins page)</li>
1272
+ <li>tweak : eliminate the over-use of get_title() when determining the widget title</li>
1273
+ <li>tweak : added self-terminating forward slash to generated shortcodes</li>
1274
+ <li>change : prepare for WordPress v4 (avoid use of deprecated functions)</li>
1275
+ </ul>
1276
+
1277
+ <h4>3.0.2</h4>
1278
+
1279
+ <ul>
1280
+ <li>bugfix : the shortcode display on new instances of the widget (in admin) did not initially reflect the automatically-selected menu</li>
1281
+ </ul>
1282
+
1283
+ <h4>3.0.1</h4>
1284
+
1285
+ <ul>
1286
+ <li>bugfix : changed the determination of pre-existing legacy widgets versus brand new widget instances, to get round problems encountered when other plugins utilise the widget_form_callback filter to inject fields into a widget</li>
1287
+ <li>addition : added a couple of filters</li>
1288
+ </ul>
1289
+
1290
+ <h4>3.0.0</h4>
1291
+
1292
+ <ul>
1293
+ <li><p><strong>! Rewrite, and Change of Approach !</strong> The widget has had a major rewrite! The <code>Children of</code> filter has been replaced with a <code>Branch</code> filter, with a subsequent shift in focus for the secondary filter options, from the children's level (0, 1 or more items) up to the branch level (a single item!). This should provide a more intuitive interface, and is definitely easier to code for. <strong>However</strong>, it only affects <em>new instances</em> of the widget; v2 instances are still <strong><em>fully supported</em></strong>.</p>
1294
+
1295
+ <p>Please also note that the shortcode tag for v3 has changed to <strong><code>[cmwizard]</code></strong>, with a revised set of attributes. The old shortcode tag is still supported, but only with the v2 attribute set, and only providing v2 functionality, ie. it is the shortcode tag that determines which widget version to use, and the appropriate attribute set for that version.</p>
1296
+
1297
+ <p>There is no automatic upgrade of widget settings from v2 to v3! I suggest bringing up the "assist" for the existing v2 widget, running it side-by-side with the "assist" of a new instance of the widget, and using them to the compare the desired outputs. I would also strongly recommend that you put your old widgets into the inactive area until you are completely happy with their new replacements. If you are upgrading from version 2, and you would like a bit more information, <a href="http://www.wizzud.com/2014/06/16/custom-menu-wizard-wordpress-plugin-version-3/" target='_blank'>this article<small class="glyphicon glyphicon-new-window offsite-link" aria-hidden="true"></small></a> might help.</p></li>
1298
+ <li>change : <strong>the minmum requirement for WordPress is v3.6</strong></li>
1299
+ <li>addition : more options for requiring that the "current" menu item be present at some point in the filter process</li>
1300
+ <li>addition : Branch filter levels can be either relative (to the selected Branch item) or absolute (within the menu structure)</li>
1301
+ <li>addition : menu items can now be excluded from the final output, either explicitly by id (optionally including descendants), or by level</li>
1302
+ <li>addition : the ids of Items can be set to include all descendants</li>
1303
+ <li>addition : the inclusion of branch ancestors, and optionally their siblings, can be set by absolute level or relative number of levels</li>
1304
+ <li>addition : the widget title can now be automatically set from the root level item of the Branch item or current menu item</li>
1305
+ <li>addition : the shortcode for a widget's current settings is now also displayed at the base of the widget (as well as at the base of the "assist")</li>
1306
+ <li>addition : "title_tag" has been added to the shortcode options, enabling the default H2 to be changed without having to resort to coding a filter</li>
1307
+ <li>addition : as an alternative to using the "assist", "findme" has been addded to the shortcode options to aid editors with the location of posts containing a CMW shortcode ([cmwizard findme=1])</li>
1308
+ <li>This release includes an upgrade to v2.1.0 for all existing version 2 widgets/shortcodes - please read the v2.1.0 changes below.</li>
1309
+ </ul>
1310
+
1311
+ <h4>2.1.0 (incorporated into v3.0.0 release)</h4>
1312
+
1313
+ <ul>
1314
+ <li>change : <strong>the minmum requirement for WordPress is v3.6</strong></li>
1315
+ <li>bugfix : handle duplicate menu item ids which were causing elements to be ignored</li>
1316
+ <li>bugfix : fix IE8 levels indentation in the "assist"</li>
1317
+ <li>bugfix : the "assist" is now "fixed" position (toggle-able back to absolute), mainly to get around a bug in jQuery UI draggable</li>
1318
+ <li>remove : take out the automatic selection of shortcode text (inconsistent cross-browser, so just triple click as usual; paste-as-text if possible!)</li>
1319
+ <li>addition : in the "assist", provide collapsible options for those larger menus</li>
1320
+ <li>addition : added utility to the "assist" enabling posts containing a CMW shortcode to be located</li>
1321
+ <li>change : in the "assist", swap the menu Items checkboxes for clickable Ticks</li>
1322
+ <li>change : in the "assist", tweak styling and make more responsive to re-sizing</li>
1323
+ <li>change : made compatible with Widget Customizer</li>
1324
+ <li>Note : there is no separate release available for this version!</li>
1325
+ </ul>
1326
+
1327
+ <h4>2.0.6</h4>
1328
+
1329
+ <ul>
1330
+ <li>change : modified determination of current item to cope better with multiple occurences (still first-found, but within prioritised groups)</li>
1331
+ <li>change : display of the upgrade notice in the plugins list has been replaced with a simple request to read the changelog before upgrading</li>
1332
+ </ul>
1333
+
1334
+ <h4>2.0.5</h4>
1335
+
1336
+ <ul>
1337
+ <li>bugfix : prevent PHP warnings of Undefined index/offset</li>
1338
+ </ul>
1339
+
1340
+ <h4>2.0.4</h4>
1341
+
1342
+ <ul>
1343
+ <li>bugfix : clearing the container field failed to remove the container from the output</li>
1344
+ <li>addition : in the "assist", added automatic selection of the shortcode text when it is clicked</li>
1345
+ <li>addition : remove WordPress's menu-item-has-children class (introduced in v3.7) when the filtered item no longer has children</li>
1346
+ <li>change : tweaked styles and javascript in admin for WordPress v3.8</li>
1347
+ </ul>
1348
+
1349
+ <h4>2.0.3</h4>
1350
+
1351
+ <ul>
1352
+ <li>bugfix : missing global when enqueuing scripts and styles for admin</li>
1353
+ </ul>
1354
+
1355
+ <h4>2.0.2</h4>
1356
+
1357
+ <ul>
1358
+ <li>bugfix : the Include Ancestors option was not automatically including the Parent</li>
1359
+ <li>bugfix : the "assist" was incorrectly calculating Depth Relative to Current Item when the current menu item was outside the scope of the Filtered items</li>
1360
+ <li>behaviour change : only recognise the first "current" item found (used to allow subsequent "current" items to override any already encountered)</li>
1361
+ </ul>
1362
+
1363
+ <h4>2.0.1</h4>
1364
+
1365
+ <ul>
1366
+ <li>bugfix : an incorrect test for a specific-items filter prevented show-all producing any output</li>
1367
+ </ul>
1368
+
1369
+ <h4>2.0.0</h4>
1370
+
1371
+ <ul>
1372
+ <li><strong>! Possible Breaker !</strong> The calculation of <code>Start Level</code> has been made consistent across the <code>Show all</code> and <code>Children of</code> 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 <em>will</em> be output, starting with the grand-children (at level 4).</li>
1373
+ <li><strong>! Possible Breaker !</strong> There is now deemed to be an artificial "root" item above the level 1 items, which mean that a <code>Children of</code> 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 <em>don't</em> currently use the "no ancestor" fallback, then where there was previously no output there will now be some!</li>
1374
+ <li>added new option : Items, a comma- or space-delimited list of menu item ids, as an alternative Filter</li>
1375
+ <li>added new option : Depth Relative to Current Item to the Filter section (depth_rel_current=1 in the shortcode)</li>
1376
+ <li>added new option : Must Contain Current Item to the Output section (contains_current=1 in the shortcode)</li>
1377
+ <li>changed the widget's "demo" facility to "assist" and brought it into WordPress admin, with full interactivity with the widget</li>
1378
+ <li>refactored code</li>
1379
+ </ul>
1380
+
1381
+ <h4>1.2.2</h4>
1382
+
1383
+ <ul>
1384
+ <li>bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly</li>
1385
+ </ul>
1386
+
1387
+ <h4>1.2.1</h4>
1388
+
1389
+ <ul>
1390
+ <li>added some extra custom classes, when applicable : cmw-fellback-to-current &amp; cmw-fellback-to-parent (on outer UL/OL) and cmw-the-included-parent, cmw-an-included-parent-sibling &amp; cmw-an-included-ancestor (on relevant LIs)</li>
1391
+ <li>corrected 'show all from start level 1' processing so that custom classes get applied and 'Title from "Current" Item' works (regardless of filter settings)</li>
1392
+ <li>changed the defaults for new widgets such that only the Filter section is open by default; all the others are collapsed</li>
1393
+ <li>in demo.html, added output of the shortcode applicable to the selections made</li>
1394
+ <li>in demo.html, added a link to the documentation page</li>
1395
+ <li>corrected 2 of the shortcode examples in the readme.txt, and made emulator (demo) available from the readme</li>
1396
+ </ul>
1397
+
1398
+ <h4>1.2.0</h4>
1399
+
1400
+ <ul>
1401
+ <li>added custom_menu_wizard shortcode, to run the widget from within content</li>
1402
+ <li>moved the 'no ancestor' fallback into new Fallback collapsible section, and added a fallback for Current Item with no children</li>
1403
+ <li>added an option allowing setting of title from current menu item's title</li>
1404
+ <li>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)</li>
1405
+ <li>don't include menus with no items</li>
1406
+ <li>updated demo.html</li>
1407
+ </ul>
1408
+
1409
+ <h4>1.1.0</h4>
1410
+
1411
+ <ul>
1412
+ <li>added 'Current Root Item' and 'Current Parent Item' to the <code>Children of</code> filter</li>
1413
+ <li>added <code>Fallback to Current Item</code> option, with subsidiary options for overriding a couple of Output options, as a means to enable Current Root &amp; Current Parent to match a Current Item at root level</li>
1414
+ <li>added an Output option to include both the parent item <strong>and</strong> the parent's siblings (for a successful <code>Children of</code> filter)</li>
1415
+ <li>added max-width style (100%) to the <code>Children of</code> SELECT in the widget options</li>
1416
+ <li>added widget version to the admin js enqueuer</li>
1417
+ <li>ignore/disable Hide Empty option for WP &gt;= v3.6 (wp_nav_menu() now does it automatically)</li>
1418
+ <li>included a stand-alone helper/demo html page</li>
1419
+ <li>rebuilt the <code>Children of</code> SELECT in the widget options to cope with IE's lack of OPTGROUP/OPTION styling</li>
1420
+ <li>moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript</li>
1421
+ </ul>
1422
+
1423
+ <h4>1.0.0</h4>
1424
+
1425
+ <ul>
1426
+ <li>Initial release</li>
1427
+ </ul> </div>
1428
+ </div>
1429
+
1430
+ </div>
1431
+
1432
+ <hr />
1433
+ </div>
1434
+
1435
+ <div class="gotoprev"><a class='btn btn-warning' href='#Description'><span class="glyphicon glyphicon-chevron-left"></span><span class="sr-only">return to previous tab and location</span></a></div>
1436
+ <div class="gototop" data-spy="affix" data-offset-top="100"><a class='btn btn-info' href='#Top'><span class="glyphicon glyphicon-chevron-up"></span><span class="sr-only">go to top of page</span></a></div>
1437
+
1438
+ <script type="text/javascript">
1439
+ jQuery(function($){
1440
+ var scrollWin = function(to){
1441
+ if(to.toString().substr(0, 1) === '#'){
1442
+ to = $(to).offset().top;
1443
+ }
1444
+ $('html,body').animate({'scrollTop':to});
1445
+ };
1446
+ //show/hide all faqs...
1447
+ $('.faqs-all').on('click', function(){
1448
+ $('#FAQs .collapse').collapse( $(this).text().toLowerCase().split(' ')[0] );
1449
+ this.blur();
1450
+ return false;
1451
+ });
1452
+ //toggle A-Z clicked...
1453
+ $('.toggle-a-z').on('click', function(){
1454
+ var self = $(this),
1455
+ az = !self.hasClass('active'),
1456
+ items = self.siblings(),
1457
+ parent = self.parent(),
1458
+ reorder = [],
1459
+ i;
1460
+ this.blur();
1461
+ items.each(function(i, el){
1462
+ reorder[ $(el).find('a').data()[az ? 'azPos' : 'naturalPos'] ] = i;
1463
+ });
1464
+ for(i = 0; i < items.length; i++){
1465
+ parent.append( items.eq( reorder[i] ) );
1466
+ }
1467
+ });
1468
+ //gototop clicked...
1469
+ $('.gototop a').on('click', function(){
1470
+ this.blur();
1471
+ scrollWin(0);
1472
+ return false;
1473
+ });
1474
+ //gotoprev clicked...
1475
+ $('.gotoprev a').on('click', function(){
1476
+ var div = $(this).parent(),
1477
+ data = div.data(),
1478
+ target;
1479
+ this.blur();
1480
+ if(!data.prev){
1481
+ data.prev = [];
1482
+ }else{
1483
+ target = data.prev.pop();
1484
+ //if a tab switch is required, set the target tab's scroller data and trigger the tab switch...
1485
+ if(!$('.nav-pills').children().not('.active')
1486
+ .find('a[href="' + target.tab + '"]').data('scroller', {scrollto:target.scrollto,back:1}).tab('show').length){
1487
+ //...otherwise simply scroll to the target...
1488
+ scrollWin(target.scrollto);
1489
+ }
1490
+ }
1491
+ if(!data.prev.length){
1492
+ div.removeClass('affix');
1493
+ }
1494
+ return false;
1495
+ });
1496
+ //any tab-pane anchor clicked...
1497
+ $('.tab-pane').on('click', 'a', function(e){
1498
+ var self = $(this),
1499
+ //this is the id of the *tab-pane*...
1500
+ href = self.attr('href'),
1501
+ scrollto, pill, prevdata;
1502
+ //check for bookmark...
1503
+ if(href.substr(0,1) === '#' && !self.hasClass('panel-heading')){
1504
+ e.preventDefault();
1505
+ //get the actual target within the tab-pane...
1506
+ scrollto = self.data('scroll') || '#scroll-default';
1507
+ //if the target is within the current tab-pane, store the prevdata and do the scroll...
1508
+ if(href === '#' + $(this).closest('.tab-pane').attr('id')){
1509
+ prevdata = $('.gotoprev').addClass('affix').data();
1510
+ if(!prevdata.prev){
1511
+ prevdata.prev = [];
1512
+ }
1513
+ prevdata.prev.push({tab:href, scrollto:$(window).scrollTop()});
1514
+ scrollWin(scrollto);
1515
+ }else{
1516
+ //otherwise, set data on the target pill and switch tabs to it (the
1517
+ //tab handlers will store the prevdata and do the final scroll)...
1518
+ $('.nav-pills').find('a[href="' + href + '"]')
1519
+ .data('scroller', {scrollto:scrollto}).tab('show');
1520
+ }
1521
+ }
1522
+ });
1523
+ $('.nav-pills').on('hide.bs.tab', function(e){
1524
+ var next = $(e.relatedTarget),
1525
+ prevdata = next.data('scroller');
1526
+ //the only time we DON'T want to store current tab is switching tabs as a result of
1527
+ //a.gotoprev being clicked, which would be HAS prevdata AND prevdata.back is ON...
1528
+ if(!prevdata || !prevdata.back){
1529
+ //put the *current* tab data into prevdata...
1530
+ prevdata = $('.gotoprev').addClass('affix').data();
1531
+ if(!prevdata.prev){
1532
+ prevdata.prev = [];
1533
+ }
1534
+ prevdata.prev.push({tab:$(e.target).attr('href'), scrollto:$(window).scrollTop()});
1535
+ }
1536
+ });
1537
+ $('.nav-pills').on('shown.bs.tab', function(e){
1538
+ var active = $(e.target),
1539
+ prevdata = active.data('scroller');
1540
+ if(prevdata){
1541
+ active.removeData('scroller');
1542
+ scrollWin(prevdata.scrollto);
1543
+ }
1544
+ });
1545
+ });
1546
+ </script>
1547
+ </body>
1548
+ </html>
v210-readme.html → doc/v210-readme.html RENAMED
File without changes
include/class.walker.php CHANGED
@@ -3,1228 +3,1251 @@
3
  * Custom Menu Wizard plugin
4
  *
5
  * Custom Menu Wizard Walker class
6
- * NB: Walker_Nav_Menu class is in wp-includes/nav-menu-template.php, and is itself an
7
  * extension of the Walker class (wp-includes/class-wp-walker.php)
8
  */
9
  class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
10
 
11
- /**
12
- * CMW custom variables
13
- */
14
- private $_cmw_tree;
15
- private $_cmw_lowest;
16
- private $_cmw_highest;
17
-
18
- /**
19
- * opens a sub-level with a UL or OL start-tag
20
- *
21
- * @param string $output Passed by reference. Used to append additional content.
22
- * @param int $depth Depth of page. Used for padding.
23
- */
24
- public function start_lvl( &$output, $depth = 0, $args = array() ) {
25
-
26
- $indent = str_repeat("\t", $depth);
27
- $listtag = empty( $args->_custom_menu_wizard['ol_sub'] ) ? 'ul' : 'ol';
28
- $output .= "\n$indent<$listtag class=\"sub-menu\">\n";
29
-
30
- } //end start_lvl()
31
-
32
- /**
33
- * closes a sub-level with a UL or OL end-tag
34
- *
35
- * @param string $output Passed by reference. Used to append additional content.
36
- * @param int $depth Depth of page. Used for padding.
37
- */
38
- public function end_lvl( &$output, $depth = 0, $args = array() ) {
39
-
40
- $indent = str_repeat("\t", $depth);
41
- $listtag = empty( $args->_custom_menu_wizard['ol_sub'] ) ? 'ul' : 'ol';
42
- $output .= "$indent</$listtag>\n";
43
-
44
- } //end end_lvl()
45
-
46
- /**
47
- * pre-filters elements then calls parent::walk()
48
- *
49
- * @filters : custom_menu_wizard_walker_items array of filtered menu elements; array of args
50
- *
51
- * @param array $elements Menu items
52
- * @param integer $max_depth
53
- * @return string
54
- */
55
- public function walk($elements, $max_depth){
56
-
57
- $args = array_slice(func_get_args(), 2);
58
- $args = $args[0];
59
-
60
- if( $max_depth >= -1 && !empty( $elements ) && isset($args->_custom_menu_wizard) ){
61
-
62
- if( empty( $args->_custom_menu_wizard['cmwv'] ) ){
63
- $elements = $this->_cmw_walk_legacy( $args, $elements );
64
- }else{
65
- $elements = $this->_cmw_walk( $args, $elements );
66
- }
67
-
68
- //since we've done all the depth filtering, set max_depth to unlimited (unless flat output was requested!)...
69
- if( empty( $args->_custom_menu_wizard['flat_output'] ) ){
70
- $max_depth = 0;
71
- }else{
72
- //for v3.1.0 we now need to specifically reset max_depth in case we're using the alternative and it has changed
73
- //from hierarchic output to flat output...
74
- $max_depth = -1;
75
- }
76
-
77
- } //ends the check for bad max depth, empty elements, or empty cmw args
78
-
79
- return empty( $elements ) ? '' : parent::walk( apply_filters( 'custom_menu_wizard_walker_items', $elements, $args ), $max_depth, $args );
80
-
81
- } //end walk()
82
-
83
- /**
84
- * current & legacy : finds and returns ID of current menu item while creating the tree and levels arrays
85
- *
86
- * @param array $elements Array of menu items
87
- * @param array $cmw Array of widget instance settings
88
- * @return integer ID of current menu item (false if not found)
89
- */
90
- private function _cmw_find_current_item( &$elements, &$cmw ){
91
- //$elements is an array of objects, indexed by position within the menu (menu_order),
92
- //starting at 1 and incrementing sequentially regardless of parentage (ie. first item is [1],
93
- //second item is [2] whether it's at root or subordinate to first item)
94
-
95
- $id_field = $this->db_fields['id']; //eg. = 'db_id'
96
- $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
97
- $currentItem = array();
98
-
99
- $this->_cmw_tree = array(
100
- 0 => array(
101
- 'level' => 0,
102
- 'ancestors' => array(),
103
- 'kids' => array(),
104
- 'element' => array(),
105
- 'keepCount' => 0,
106
- 'keep' => false
107
- )
108
- );
109
- $this->_cmw_levels = array(
110
- array() //for the artificial level-0
111
- );
112
-
113
- if( isset( $cmw['_exclude'] ) ){ //only came in at v3.0.0!
114
- //v3+ allows for inheritance on the $cmw items and exclude csv fields, which means that the _items and _exclude
115
- //arrays do not necessarly contain all the relevant items ids, eg. an element such as '23+' in the _exclude array means
116
- //that menu item id = 23 is excluded ... AND so are all its descendants!
117
- $cmw['__items'] = array();
118
- $cmw['__exclude'] = array();
119
- $inheritItems = array();
120
- $inheritExclude = array();
121
- foreach( $cmw['_items'] as $i ){
122
- if( substr( $i, -1 ) == '+' ){
123
- $i = substr( $i, 0, -1 );
124
- $inheritItems[] = $i;
125
- }
126
- $cmw['__items'][] = $i;
127
- }
128
- foreach( $cmw['_exclude'] as $i ){
129
- if( substr( $i, -1 ) == '+' ){
130
- $i = substr( $i, 0, -1 );
131
- $inheritExclude[] = $i;
132
- }
133
- $cmw['__exclude'][] = $i;
134
- }
135
- }
136
-
137
- foreach( $elements as $i => $item ){
138
- $itemID = $item->$id_field;
139
- $parentID = empty( $item->$parent_field ) ? 0 : $item->$parent_field;
140
-
141
- //if $this->_cmw_tree[] for the parent hasn't been set then it's an orphan...
142
- //NOTE The parent Walker will only handle orphans in a "hierarchical Show All" situation (ie. non-flat output, unlimited depth)
143
- // and it does so by appending each orphan as a new top-level item (and note that a child of an orphan is also an orphan!).
144
- // However, CMW excludes all orphans (which includes descendants of orphans) so they never reach the parent Walker.
145
- // If orphans are required, use WordPress's own Custom Menu widget.
146
- if( isset( $this->_cmw_tree[ $parentID ] ) ){
147
- //check for current item (as a menu item ID, ie. a key into the tree)...
148
- if( $item->current ||
149
- ( isset( $inheritItems ) && ( $hasCurrentClass = in_array( 'current-menu-item', (array)$item->classes ) ) === true ) ||
150
- ( isset( $inheritItems ) && !empty( $cmw['fallback_ci_parent'] ) && $item->current_item_parent )
151
- ){
152
- //should(!) never get either parent and/or ancestor on an item marked as "current", but unfortunately it does occur (grrr!).
153
- //so this has to cope, not only with more than 1 "current" item, but also with "current" items that are incorrectly marked
154
- //as (their own?!) parent and/or ancestor.
155
- //
156
- //v3.1.0 : there are also occasions when the item is given a class of current-menu-item but the 'current' property is not set
157
- //on the item - one such occasion being when the home page is set to latest posts (the default 'blogging' setting for Front
158
- //page displays, see Settings/Reading page) and you navigate to the second (or subsequent) page of listed posts. A menu item
159
- //that has the "Home Page" url (it's a "custom" menu item type) will fail to match any sort of current url (.../page/2/
160
- //or .../?paged=2) but does get recognised as being a "front page" url, so gets the class but no property! ON BY DEFAULT!
161
- //v3.1.0 : also occasions where $item->current_item_parent is set without there being any item with $item->current!
162
- //eg. open a post, and if there is a Category menu item available (the post has that category) then Category menu item
163
- //gets marked as current_item_parent. HAS TO BE ENABLED!
164
- //
165
- //we're going to look for correctly (solely) marked "current" items and take the first one found
166
- //failing that, look for a "current" item that is also marked as parent, and, again, use the first one found
167
- //failing that, look for a "current" item that is also marked as an ancestor, and, again, use the first one found
168
- //failing that, look for an item classed as "current-menu-item", again using the first one found
169
- //finally, if enabled, look for an item marked as parent (if it gets used then there's no current!), using first one found
170
- //
171
- //array keys, priority order :
172
- // just current -> parent, not ancestor -> parent and ancestor -> ancestor -> "current-menu-item" -> parent
173
- // first found...
174
- // - a001 : just current
175
- // - b001 : current & parent (not ancestor)
176
- // - c001 : current & parent & ancestor
177
- // - d001 : current & ancestor (not parent)
178
- // - e001 : "current-menu-item"
179
- // - f001 : parent (not current)
180
- // next found...
181
- // - a002 : just current
182
- // - b002 : current & parent (not ancestor)
183
- // - c002 : current & parent & ancestor
184
- // - d002 : current & ancestor (not parent)
185
- // - e002 : "current-menu-item"
186
- // - f002 : parent (not current)
187
- // etc
188
- //example :
189
- // - 1st found : current & ancestor = d001
190
- // - 2nd found : current & parent & ancestor = c002
191
- // - 3rd found : just current = a003
192
- // - 4th found : current & parent = b004
193
- // - 5th found : just current = a005
194
- //reverse sort keys alphabetically and a003 comes out on bottom, so third found gets used! (copes with 999 "current"
195
- //items; should be enough!)
196
- if( $item->current ){
197
- if( $item->current_item_ancestor ){
198
- if( $item->current_item_parent ){
199
- $j = 'c';
200
- }else{
201
- $j = 'd';
202
- }
203
- }elseif( $item->current_item_parent ){
204
- $j = 'b';
205
- }else{
206
- $j = 'a';
207
- }
208
- }elseif( $hasCurrentClass ){
209
- $j = 'e';
210
- }else{
211
- $j = 'f';
212
- }
213
- $currentItem[ $j . sprintf( '%03d' , count( $currentItem ) + 1 ) ] = $itemID;
214
- }
215
-
216
- //because it is possible for $itemID not to be unique, we have to play canny :
217
- //we need to be able to store multiple references back into $elements for each $itemID, and we therefore have to
218
- //check whether we already have a $this->_cmw_tree entry for the $itemID, and if we do then simply store the pointer
219
- //back to $elements in the existing $this->_cmw_tree entry for the item ID.
220
- //This has consequences :
221
- // - the individual menu items (for non-unique item IDs) will be in or out as a group, not as individual items
222
- // - if we happen to need, say, the branch title from the branch being filtered, we have to take it from the
223
- // first of the group and there need not be any relation between that item's title and the original menu item's
224
- // title (as set on the WP Menu Admin page). Can't help that.
225
- // - any child will be a child of the group, which actually means it *should* become (at some point?!) the child
226
- // of the last member of the group(?!). BUT what if the child is actually intended to be the child of the item it follows,
227
- // eg. itemID N, child, itemID N, child, itemID N, itemID N, child, child, itemID N, etc?
228
- // CMW can't cope with this : its position is that if WP's Walker can't cope with it then there is no reason why CMW should
229
- // attempt to rectify the few (rare) occasions where these situations may arise. (It's possible, but I have chosen not to).
230
- // NB The parent Walker assigns children of a non-unique-id menu item to the 1st occurence of the item ID (1st in group)
231
- if( isset( $this->_cmw_tree[ $itemID ] ) ){
232
- //it's a non-unique item ID! just add the $elements pointer to the element array...
233
- $this->_cmw_tree[ $itemID ]['element'][] = $i;
234
- }else{
235
- //this level...
236
- $thisLevel = $this->_cmw_tree[ $parentID ]['level'] + 1;
237
- if( empty( $this->_cmw_levels[ $thisLevel ] ) ){
238
- $this->_cmw_levels[ $thisLevel ] = array();
239
- }
240
- $this->_cmw_levels[ $thisLevel ][] = $itemID;
241
-
242
- $this->_cmw_tree[ $itemID ] = array(
243
- //level within structure...
244
- 'level' => $thisLevel,
245
- //ancestors (from the artificial level-0, right down to parent, inclusive) within structure...
246
- 'ancestors' => $this->_cmw_tree[ $parentID ]['ancestors'],
247
- //kids within structure, ie array of itemID's...
248
- 'kids' => array(),
249
- //index of item within elements...
250
- 'element' => array( $i ),
251
- //classes added by widget...
252
- 'classes' => array(),
253
- //assume no match...
254
- 'keep' => false
255
- );
256
- //append this item's parent onto its ancestors...
257
- $this->_cmw_tree[ $itemID ]['ancestors'][] = $parentID;
258
- //add this item to its parent's kids...
259
- $this->_cmw_tree[ $parentID ]['kids'][] = $itemID;
260
-
261
- //check for inheritance on items and exclude...
262
- if( isset( $inheritItems ) ){
263
- if( in_array( $parentID, $inheritItems ) ){
264
- $inheritItems[] = $itemID;
265
- $cmw['__items'][] = $itemID;
266
- }
267
- if( in_array( $parentID, $inheritExclude ) ){
268
- $inheritExclude[] = $itemID;
269
- $cmw['__exclude'][] = $itemID;
270
- }
271
- }
272
- }
273
- }
274
- } //end foreach
275
-
276
- if( isset( $inheritItems ) ){
277
- unset( $inheritItems, $inheritExclude );
278
- }
279
-
280
- if( empty( $currentItem ) ){
281
- $currentItem = false;
282
- }else{
283
- krsort( $currentItem );
284
- $currentItem = array_pop( $currentItem );
285
- }
286
-
287
- return $currentItem;
288
-
289
- } //end _cmw_find_current_item()
290
-
291
- /**
292
- * current: test for one item being below another item and/or its siblings
293
- *
294
- * @param integer $lookBelow Menu item ID that needs checking, possibly its siblings as well
295
- * @param integer $searchFor Menu item ID that is being looked for
296
- * @param boolean $andSiblings Whether to check in $lookBelow's siblings as well
297
- * @return boolean Found (or not)
298
- */
299
- private function _cmw_check_contains_item( $lookBelow, $searchFor, $andSiblings ){
300
-
301
- $rtn = in_array( $lookBelow, $this->_cmw_tree[ $searchFor ]['ancestors'] );
302
- if( !$rtn && $andSiblings ){
303
- foreach( $this->_cmw_tree[ $this->_cmw_get_parent( $lookBelow ) ]['kids'] as $kid ){
304
- //check whether one of $searchFor's ancestors is $kid...
305
- if( $kid != $lookBelow && in_array( $kid, $this->_cmw_tree[ $searchFor ]['ancestors'] ) ){
306
- $rtn = true;
307
- break;
308
- }
309
- }
310
- }
311
- return $rtn;
312
-
313
- }
314
-
315
- /**
316
- * clear any keep flags currently set in the tree
317
- */
318
- private function _cmw_clear_down_tree(){
319
-
320
- if( $this->_cmw_tree[0]['keepCount'] > 0 ){
321
- foreach( $this->_cmw_tree as $k => $v ){
322
- $this->_cmw_tree[ $k ]['keep'] = false;
323
- $this->_cmw_tree[ $k ]['classes'] = array();
324
- }
325
- $this->_cmw_tree[0]['keepCount'] = 0;
326
- }
327
-
328
- }
329
-
330
- /**
331
- * resolve digit(s) optionally followed by a plus/minus into a 'from' level an a 'to' level
332
- * IMPORTANT : 'from' is inclusive, 'to' is exclusive, so a for() would be for( $i = $rtn['from']; $i < $rtn['to']; $i++ )
333
- *
334
- * @param {string} $option Level with optional +/- appended
335
- * @return {array} False if $option doesn't parse
336
- */
337
- private function _cmw_decipher_plusminus_level( $option ){
338
-
339
- $rtn = array();
340
- if( !empty( $option ) && preg_match( '/^(\d+)(\+|-)?$/', $option, $m ) > 0 ){
341
- $m[1] = intval( $m[1] );
342
- if( $m[1] > 0 ){
343
- if( empty( $m[2] ) ){
344
- //no plus/minus : 'from' is the level, 'to' is the next level...
345
- $rtn['from'] = $m[1];
346
- $rtn['to'] = $m[1] + 1;
347
- }elseif( $m[2] == '+' ){
348
- //plus : 'from' is the level, 'to' is the number of levels
349
- //NB: there is an artificial level zero, so if the menu has 10 levels, a count of levels will give 11!
350
- $rtn['from'] = $m[1];
351
- $rtn['to'] = count( $this->_cmw_levels );
352
- }else{
353
- //minus : 'from' is level 1, 'to' is the level plus 1
354
- $rtn['from'] = 1;
355
- $rtn['to'] = $m[1] + 1;
356
- }
357
- }
358
- }
359
- return empty( $rtn ) ? false : $rtn;
360
-
361
- }
362
-
363
- /**
364
- * returns the menu item id if an item's parent
365
- *
366
- * @param integer $kid Menu item ID
367
- * @return integer Menu item ID of kid's parent
368
- */
369
- private function _cmw_get_parent( $kid ){
370
-
371
- $immediateParent = array_slice( $this->_cmw_tree[ $kid ]['ancestors'], -1, 1);
372
- return $immediateParent[0];
373
-
374
- }
375
-
376
- /**
377
- * current: set keep flag of an item's siblings
378
- *
379
- * @param integer $itemID Menu item ID
380
- * @param string $classSuffix Suffix of class to be added to the kept items
381
- */
382
- private function _cmw_include_siblings_of( $itemID, $classSuffix='sibling' ){
383
-
384
- foreach( $this->_cmw_tree[ $this->_cmw_get_parent( $itemID ) ]['kids'] as $i ){
385
- if( !$this->_cmw_tree[ $i ]['keep'] ){
386
- $this->_cmw_tree[ $i ]['keep'] = true;
387
- $this->_cmw_tree[0]['keepCount']++;
388
- if( !empty( $classSuffix ) ){
389
- $this->_cmw_tree[ $i ]['classes'][] = 'cmw-an-included-' . $classSuffix;
390
- }
391
- }
392
- }
393
-
394
- } //end _cmw_include_siblings_of()
395
-
396
- /**
397
- * runs exclusions, if there are any
398
- *
399
- * @param {array} $cmw Settings
400
- * @return {boolean} keepCount > 0?
401
- */
402
- private function _cmw_run_exclusions( &$cmw ){
403
-
404
- $rtn = $this->_cmw_tree[0]['keepCount'] > 0;
405
- if( $rtn && !empty( $cmw['__exclude'] )){
406
- foreach( $cmw['__exclude'] as $itemID ){
407
- if( $itemID > 0 && isset( $this->_cmw_tree[ $itemID ] ) && $this->_cmw_tree[ $itemID ]['keep'] ){
408
- $this->_cmw_tree[ $itemID ]['keep'] = false;
409
- $this->_cmw_tree[0]['keepCount']--;
410
- }
411
- }
412
- $rtn = $this->_cmw_tree[0]['keepCount'] > 0;
413
- }
414
- if( $rtn && ( $fromTo = $this->_cmw_decipher_plusminus_level( $cmw['exclude_level'] ) ) !== false ){
415
- while( isset( $this->_cmw_levels[ $fromTo['from'] ] ) && $fromTo['from'] < $fromTo['to'] ){
416
- foreach( $this->_cmw_levels[ $fromTo['from'] ] as $itemID ){
417
- if( $this->_cmw_tree[ $itemID ]['keep'] ){
418
- $this->_cmw_tree[ $itemID ]['keep'] = false;
419
- $this->_cmw_tree[0]['keepCount']--;
420
- }
421
- }
422
- $fromTo['from']++;
423
- }
424
- $rtn = $this->_cmw_tree[0]['keepCount'] > 0;
425
- }
426
- return $rtn;
427
-
428
- }
429
-
430
- /**
431
- * current : recursively set the keep flag if within specified level/depth
432
- * if item passed in is eligible, sets that item as kept and runs through its kids recursively
433
- * uses _cmw_lowest & _cmw_highest : note that _cmw_lowest is the lowest level in the structure - *not*
434
- * the numerically lowest value of level - and that both are inclusive!
435
- *
436
- * @param integer $itemID Menu item ID
437
- */
438
- private function _cmw_set_keep_recursive( $itemID ){
439
-
440
- //at or above lowest?...
441
- if( $this->_cmw_tree[ $itemID ]['level'] <= $this->_cmw_lowest ){
442
- //at or below highest?...
443
- if( $this->_cmw_tree[ $itemID ]['level'] >= $this->_cmw_highest ){
444
- //keep (if not already)...
445
- if( !$this->_cmw_tree[ $itemID ]['keep'] ){
446
- $this->_cmw_tree[ $itemID ]['keep'] = true;
447
- $this->_cmw_tree[0]['keepCount']++;
448
- }
449
- }
450
- //unless this item is above the lowest level there's no point checking its kids...
451
- if( $this->_cmw_tree[ $itemID ]['level'] < $this->_cmw_lowest ){
452
- for( $i = 0, $ct = count( $this->_cmw_tree[ $itemID ]['kids'] ); $i < $ct; $i++ ){
453
- $this->_cmw_set_keep_recursive( $this->_cmw_tree[ $itemID ]['kids'][ $i ] );
454
- }
455
- }
456
- }
457
-
458
- } //end _cmw_set_keep_recursive()
459
-
460
- /**
461
- * switch the current settings for those indicated by a cmwizard shortcode
462
- *
463
- * @param object $args The args passed into walk()
464
- * @param string $at The current processing stage
465
- * @param boolean $hasCurrent Whether or not current item is in this stage
466
- * @param boolean $hasOutput Whether or not there will be any output (as best we know so far)
467
- * @return boolean True if we can use alternative settings
468
- */
469
- private function _cmw_switch_settings( &$args, $at = '', $hasCurrent = false, $hasOutput = false ){
470
-
471
- if( $args->_custom_menu_wizard['switch_at'] == $at && (
472
- ( $args->_custom_menu_wizard['switch_if'] == 'current' && $hasCurrent ) ||
473
- ( $args->_custom_menu_wizard['switch_if'] == 'no-current' && !$hasCurrent ) ||
474
- ( $args->_custom_menu_wizard['switch_if'] == 'no-output' && !$hasOutput )
475
- ) ){
476
-
477
- $plugin = Custom_Menu_Wizard_Plugin::init();
478
-
479
- //if switch_to is empty, it gets defaulted to a minimum
480
- //trim off square brackets, self-terminators, and spaces...
481
- $switchTo = trim( $args->_custom_menu_wizard['switch_to'], ' ][/' );
482
- //if it doesn't start with our shortcode, prepend it...
483
- if( substr( $switchTo . ' ', 0, 9 ) != 'cmwizard ' ){
484
- $switchTo = 'cmwizard ' . $switchTo;
485
- }
486
- //append our current menu so that it will be used when the shortcode atts are parsed...
487
- $switchTo = trim( $switchTo ) . ' menu=' . $args->_custom_menu_wizard['menu'];
488
-
489
- if( ( $new_cmw = $plugin->encode_shortcode( $switchTo ) ) !== false ){
490
- //store old...
491
- $old_cmw = array_merge( array(), $args->_custom_menu_wizard );
492
- //merge new into old, overriding _walker...
493
- $new_cmw = array_merge( $old_cmw, (array)$new_cmw, array('_walker' => array('alternative' => true)) );
494
- //overwrite current with new...
495
- $args->_custom_menu_wizard = $new_cmw;
496
- //put new and old into the current _walker, so that they become available to the widget
497
- //instance (as long as there's some output!)...
498
- $args->_custom_menu_wizard['_walker']['instances'] = array( 'old' => $old_cmw, 'new' => $new_cmw );
499
- unset( $old_cmw );
500
-
501
- return true;
502
- }
503
- }
504
-
505
- return false;
506
-
507
- } //end _cmw_switch_settings()
508
-
509
- /**
510
- * legacy : recursively set the keep flag if within specified level/depth
511
- * runs through kids of item passed in : if kid is eligible, sets kid to kept; if grandkids might be eligible, recurse with kid
512
- *
513
- * @param integer $itemID Menu item ID
514
- * @param integer $topLevel Uppermost level that can be kept
515
- * @param integer $bottomLevel Lowermost level that can be kept
516
- */
517
- private function _cmw_legacy_set_keep_kids( $itemId, $topLevel, $bottomLevel ){
518
-
519
- for( $i = 0, $ct = count( $this->_cmw_tree[ $itemId ]['kids'] ); $i < $ct; $i++ ){
520
- $j = $this->_cmw_tree[ $itemId ]['kids'][ $i ];
521
- if( $this->_cmw_tree[ $j ]['level'] <= $bottomLevel ){
522
- if( ( $this->_cmw_tree[ $j ]['keep'] = $this->_cmw_tree[ $j ]['level'] >= $topLevel ) !== false){
523
- $this->_cmw_tree[0]['keepCount']++;
524
- }
525
- }
526
- if( $this->_cmw_tree[ $j ]['level'] < $bottomLevel ){
527
- $this->_cmw_legacy_set_keep_kids( $j, $topLevel, $bottomLevel );
528
- }
529
- }
530
-
531
- } //end _cmw_legacy_set_keep_kids()
532
-
533
- /**
534
- * pre-filters elements
535
- *
536
- * @filters : custom_menu_wizard_walker_change_settings array of current CMW settings; id of current menu item; array of original menu elements
537
- * gets applied immediately after determination of the current item, and can be used to provide an alternate set of CMW settings
538
- * based, maybe, on the value (presence/absence?) of a current menu item, or some other specific value in the current settings.
539
- * if the returned settings don't exactly match those currently in use, then the new ones are used and current item is re-determined.
540
- *
541
- * @param {object} $args Params supplied to wp_nav_menu()
542
- * @param {array} $elements Menu items
543
- * @return {array} Modified menu items
544
- */
545
- private function _cmw_walk( &$args, $elements ){
546
-
547
- $id_field = $this->db_fields['id']; //eg. = 'db_id'
548
- $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
549
- $unlimited = 65532;
550
- //max number of run-throughs is 2!...
551
- $runCount = 2;
552
-
553
- $cmw =& $args->_custom_menu_wizard;
554
-
555
- while( $runCount > 0 ){
556
-
557
- $runCount--;
558
- $topOfBranch = -1;
559
- $continue = true;
560
-
561
- //find the current menu item (ID of the menu item) while creating the tree and levels arrays...
562
- $currentItem = $this->_cmw_find_current_item( $elements, $cmw );
563
-
564
- //allow (once only!) a filter to change the cmw settings based on the presence (or absence) of a current item...
565
- //note that not all changes to settings will have any influence; if the walker doesn't use them, and
566
- //the widget processing subsequent to the wp_nav_menu() call doesn't use them, then they will have no effect!
567
- //also note that utilising this filter will prevent any subsequent switchable from being actioned (because of the runCount).
568
- //BE AWARE : indiscriminate changes to the cmw settings have the potential to totally screw up the output!
569
- $new_cmw = $runCount > 0 ? apply_filters( 'custom_menu_wizard_walker_change_settings', $cmw, $currentItem, $elements ) : false;
570
- if( $new_cmw !== false && $new_cmw !== $cmw ){
571
- //store old...
572
- $old_cmw = array_merge( array(), $cmw );
573
- //merge new into old, and don't allow modification of _walker...
574
- $new_cmw = array_merge( $old_cmw, (array)$new_cmw, array('_walker' => array()) );
575
- //overwrite current with new...
576
- $args->_custom_menu_wizard = $new_cmw;
577
- //put new and old into the current _walker, so that they become available to the widget
578
- //instance (as long as there's some output!)...
579
- $cmw['_walker']['instances'] = array( 'old' => $old_cmw, 'new' => $new_cmw );
580
- unset( $old_cmw );
581
- //back to top of while loop...
582
- continue;
583
- }
584
- unset( $new_cmw );
585
-
586
- $cmw['_walker']['fellback'] = false;
587
-
588
- $find_items = $cmw['filter'] == 'items';
589
- $find_branch = $cmw['filter'] == 'branch';
590
- $find_level = !$find_items && !$find_branch;
591
- $find_current = $find_branch && empty( $cmw['branch'] );
592
-
593
- //measuring depth relative to the current item only applies if depth is *not* unlimited...
594
- $depth = intval( $cmw['depth'] );
595
- $depth_rel_current = $cmw['depth_rel_current'] && $depth > 0 && $currentItem !== false; //v2.0.0
596
- //no-kids fallback?...
597
- $canFallback = $find_current && in_array( $cmw['fallback'], array('current', 'parent', 'quit') );
598
- //switchable?...
599
- //note that switchable does not require switch_to to contain a value!
600
- $canSwitch = $runCount > 0 && !empty( $cmw['switch_if'] ) && !empty( $cmw['switch_at'] );
601
-
602
- //check for current item and switch...
603
- $hasCurrent = $currentItem !== false;
604
- //no current item means that a current branch filter CAN'T produce output...
605
- if( $find_current ){
606
- $continue = $hasCurrent;
607
- }
608
- if( $continue && $cmw['contains_current'] == 'menu' ){
609
- $continue = $hasCurrent;
610
- }
611
- if( $canSwitch && $this->_cmw_switch_settings( $args, 'menu', $hasCurrent, $continue ) ){
612
- continue;
613
- }
614
-
615
- //PRIMARY FILTERS...
616
- if( $continue ){
617
- //levels...
618
- if( $find_level ){
619
- $continue = $cmw['level'] < count( $this->_cmw_levels );
620
- }
621
- //items...
622
- if( $find_items ){
623
- $continue = !empty( $cmw['__items'] );
624
- }
625
- //branch...
626
- if( $find_branch ){
627
- //topOfBranch gets set to -1 if it can't be determined...
628
- $topOfBranch = $find_current
629
- ? ( $currentItem === false ? -1 : $currentItem )
630
- : ( isset( $this->_cmw_tree[ $cmw['branch'] ] ) ? $cmw['branch'] : -1 );
631
- $theBranchItem = $topOfBranch;
632
- $continue = $topOfBranch > 0;
633
- }
634
- } //end PRIMARIES
635
-
636
- //check for current item and switch...
637
- $hasCurrent = $currentItem !== false;
638
- if( $hasCurrent ){
639
- if( ( $find_level && $this->_cmw_tree[ $currentItem ]['level'] < $cmw['level'] ) ||
640
- ( $find_items && !in_array( $currentItem, $cmw['__items'] ) ) ||
641
- ( $find_branch && $topOfBranch !== $currentItem && !in_array( $topOfBranch, $this->_cmw_tree[ $currentItem ]['ancestors'] ) )
642
- ){
643
- $hasCurrent = false;
644
- }
645
- }
646
- if( $continue && $cmw['contains_current'] == 'primary' ){
647
- $continue = $hasCurrent;
648
- }
649
- if( $canSwitch && $this->_cmw_switch_settings( $args, 'primary', $hasCurrent, $continue ) ){
650
- continue;
651
- }
652
-
653
- //SECONDARY FILTERS...
654
- if( $continue ){
655
- //right, let's set some keep flags
656
- //for specific items, go straight in on the item id (levels and depth don't apply here)...
657
- if( $find_items ){
658
- foreach( $cmw['__items'] as $itemID ){
659
- $itemID = intval( $itemID );
660
- //avoid double counting of duplicates...
661
- if( !empty( $itemID ) && isset( $this->_cmw_tree[ $itemID ] ) && !$this->_cmw_tree[ $itemID ]['keep'] ){
662
- $this->_cmw_tree[ $itemID ]['keep'] = true;
663
- $this->_cmw_tree[0]['keepCount']++;
664
- }
665
- }
666
- }
667
- //for by-level filter, use the levels...
668
- if( $find_level ){
669
- //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
670
- if( $depth_rel_current && $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['level'] ){
671
- $bottomLevel = $this->_cmw_tree[ $currentItem ]['level'] + $depth - 1;
672
- }else{
673
- $bottomLevel = $depth > 0 ? $cmw['level'] + $depth - 1 : $unlimited;
674
- }
675
- for( $i = $cmw['level']; isset( $this->_cmw_levels[ $i ] ) && $i <= $bottomLevel; $i++ ){
676
- foreach( $this->_cmw_levels[ $i ] as $itemID ){
677
- $this->_cmw_tree[ $itemID ]['keep'] = true;
678
- $this->_cmw_tree[0]['keepCount']++;
679
- }
680
- }
681
- }
682
- //for branch filters, run a recursive through the structure...
683
- if( $find_branch ){
684
- $i = $this->_cmw_tree[ $topOfBranch ]['level'];
685
- //convert branch_start to an actual level...
686
- $j = intval( $cmw['branch_start'] );
687
- //convert relative to absolute (max'd against 1)...
688
- $j = empty( $j ) ? $i : ( preg_match( '/^(\+|-)/', $cmw['branch_start'] ) > 0 ? max( 1, $i + $j ) : $j );
689
-
690
- //do we have a current-item-no-kids fallback?...
691
- if( $canFallback && empty( $this->_cmw_tree[ $currentItem ]['kids'] ) ){
692
- //yes, we do...
693
- $cmw['_walker']['fellback'] = 'to-' . $cmw['fallback'];
694
- //is it a copout?...
695
- if( $cmw['fallback'] == 'quit' ){
696
- //just set the secondary start level beyond the maximum level available...
697
- $j = count( $this->_cmw_levels );
698
- }else{
699
- //for current, fall back to primary start level; for parent, fall back to primary start level - 1, ensuring
700
- //that we don't fall back further than root...
701
- $j = $cmw['fallback'] == 'current' || $i < 2 ? $i : $i - 1;
702
- //if fallback_depth is specified, override depth and set to depth-rel-current...
703
- if( !empty( $cmw['fallback_depth'] ) ){
704
- $depth = intval( $cmw['fallback_depth'] );
705
- $depth_rel_current = true;
706
- }
707
- }
708
- }
709
-
710
- //$i is the primary level, and $j is the secondary start level
711
- //easy result : if secondary start level > max level then there are no matches...
712
- if( $j < count( $this->_cmw_levels ) ){
713
- //if secondary start level is higher up the tree than the primary level, then
714
- //reset the tob to be the current tob's ancestor at the level of $j...
715
- if( $j < $i ){
716
- $topOfBranch = array_slice( $this->_cmw_tree[ $topOfBranch ]['ancestors'], $j, 1 );
717
- $topOfBranch = $topOfBranch[0];
718
- //NB $theBranchItem is still set to the original branch item!
719
- }
720
-
721
- $this->_cmw_lowest = $unlimited;
722
- $this->_cmw_highest = $j;
723
-
724
- //$topOfBranch is a menu item id.
725
- //if secondary start is at or above primary, and start_mode is set to "level" then it effectively means that
726
- //the top of branch becomes the current topOfBranch *plus* all its siblings. the one qualifier for
727
- //this is that if topOfBranch's current level is 1 (root) then the allow_all_root switch must be
728
- //enabled in order to expand to all root items
729
- $forceLevel = $j <= $i && $cmw['start_mode'] == 'level' && ( $j > 1 || $cmw['allow_all_root'] );
730
-
731
- //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
732
- //NB for depth_rel_current to be applicable we need a current item that is :
733
- // (a) at or below the secondary start level, and
734
- // (b) within the (modified?) branch (ie. has topOfBranch - or possibly one of its siblings! - as an ancestor)
735
- if( $depth_rel_current
736
- //...this is the (a) part...
737
- && $this->_cmw_tree[ $currentItem ]['level'] >= $this->_cmw_highest
738
- //...this is the (b) part, and it might be complicated by the setting of $forceLevel, which would
739
- // require the testing, not only of topOfBranch, but also topOfBranch's siblings...
740
- && $this->_cmw_check_contains_item( $topOfBranch, $currentItem, $forceLevel ) ){
741
- $this->_cmw_lowest = $this->_cmw_tree[ $currentItem ]['level'];
742
- }elseif( $depth > 0 ){
743
- $this->_cmw_lowest = $this->_cmw_highest;
744
- }
745
- $this->_cmw_lowest += $depth - 1;
746
- //$this->_cmw_tree[0]['keepCount'] gets incremented during this recursive...
747
- if( $forceLevel ){
748
- foreach( $this->_cmw_tree[ $this->_cmw_get_parent( $topOfBranch ) ]['kids'] as $k ){
749
- $this->_cmw_set_keep_recursive( $k );
750
- }
751
- }else{
752
- $this->_cmw_set_keep_recursive( $topOfBranch );
753
- }
754
- //if falling back and siblings are required, add them in...
755
- //note that root level sibling inclusion is still governed by allow_all_root!
756
- if( !empty( $cmw['_walker']['fellback'] ) && $cmw['fallback_siblings'] && $this->_cmw_tree[0]['keepCount'] > 0
757
- && ( $j > 1 || $cmw['allow_all_root'] ) ){
758
- $this->_cmw_include_siblings_of( $topOfBranch );
759
- }
760
- }
761
- }
762
- $continue = $this->_cmw_tree[0]['keepCount'] > 0;
763
- } //end SECONDARIES
764
-
765
- //check for current item and switch...
766
- $hasCurrent = $currentItem !== false && $this->_cmw_tree[ $currentItem ]['keep'];
767
- if( $continue && $cmw['contains_current'] == 'secondary' ){
768
- $continue = $hasCurrent;
769
- }
770
- if( $canSwitch && $this->_cmw_switch_settings( $args, 'secondary', $hasCurrent, $continue ) ){
771
- continue;
772
- }
773
-
774
- //INCLUSIONS...
775
- if( $continue ){
776
- if( $find_branch ){
777
- //branch ancestors, possibly with their siblings : but only if the original branch item is either being kept or
778
- //is below the lowest visible level; ALSO, do not keep any ancestors below the lowest visible level...
779
- if( $cmw['ancestors'] != 0 && ( $this->_cmw_tree[ $theBranchItem ]['keep'] || $this->_cmw_tree[ $theBranchItem ]['level'] > $this->_cmw_lowest ) ){
780
- //convert relative to absolute...
781
- $absAncestors = $cmw['ancestors'] < 0
782
- ? max( 1, $this->_cmw_tree[ $theBranchItem ]['level'] + $cmw['ancestors'] )
783
- : $cmw['ancestors'];
784
- //convert relative to absolute...
785
- $absSiblings = $cmw['ancestor_siblings'] < 0
786
- ? max( 1, $this->_cmw_tree[ $theBranchItem ]['level'] + $cmw['ancestor_siblings'] )
787
- : $cmw['ancestor_siblings'];
788
- foreach( $this->_cmw_tree[ $theBranchItem ]['ancestors'] as $itemID ){
789
- if( $itemID > 0
790
- && $this->_cmw_tree[ $itemID ]['level'] >= $absAncestors
791
- && $this->_cmw_tree[ $itemID ]['level'] <= $this->_cmw_lowest ){
792
- if( !$this->_cmw_tree[ $itemID ]['keep'] ){
793
- $this->_cmw_tree[ $itemID ]['keep'] = true;
794
- $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-ancestor';
795
- $this->_cmw_tree[0]['keepCount']++;
796
- }
797
- //only keep ancestor siblings if the ancestor itself is being kept
798
- if( $absSiblings > 0
799
- && $this->_cmw_tree[ $itemID ]['level'] >= $absSiblings
800
- && $this->_cmw_tree[ $itemID ]['keep'] ){
801
- $this->_cmw_include_siblings_of( $itemID, 'ancestor-sibling' );
802
- }
803
- }
804
- }
805
- }
806
- //branch siblings : only if the original branch item is being kept...
807
- if( $cmw['siblings'] && $this->_cmw_tree[ $theBranchItem ]['keep'] ){
808
- $this->_cmw_include_siblings_of( $theBranchItem );
809
- }
810
- }
811
- //include_level (replacement/extension of include_root, as of v3.0.4)...
812
- if( ( $fromTo = $this->_cmw_decipher_plusminus_level( $cmw['include_level'] ) ) !== false ){
813
- while( isset( $this->_cmw_levels[ $fromTo['from'] ] ) && $fromTo['from'] < $fromTo['to'] ){
814
- foreach( $this->_cmw_levels[ $fromTo['from'] ] as $itemID ){
815
- if( !$this->_cmw_tree[ $itemID ]['keep'] ){
816
- $this->_cmw_tree[ $itemID ]['keep'] = true;
817
- $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-level';
818
- $this->_cmw_tree[0]['keepCount']++;
819
- }
820
- }
821
- $fromTo['from']++;
822
- }
823
- }
824
- } //end INCLUSIONS
825
-
826
- //check for current item and switch...
827
- $hasCurrent = $currentItem !== false && $this->_cmw_tree[ $currentItem ]['keep'];
828
- if( $continue && $cmw['contains_current'] == 'inclusions' ){
829
- $continue = $hasCurrent;
830
- }
831
- if( $canSwitch && $this->_cmw_switch_settings( $args, 'inclusions', $hasCurrent, $continue ) ){
832
- continue;
833
- }
834
-
835
- //EXCLUSIONS...
836
- if( $continue){
837
- $continue = $this->_cmw_run_exclusions( $cmw );
838
- } //end EXCLUSIONS
839
-
840
- //check for current item and switch...
841
- $hasCurrent = $currentItem !== false && $this->_cmw_tree[ $currentItem ]['keep'];
842
- if( $continue && $cmw['contains_current'] == 'output' ){
843
- $continue = $hasCurrent;
844
- }
845
- if( $canSwitch && $this->_cmw_switch_settings( $args, 'output', $hasCurrent, $continue ) ){
846
- continue;
847
- }
848
-
849
- //check for title_from...
850
- if( $continue ){
851
- //v3.1.4 : this used to pass back just the title; it now passes back the element object!
852
- $cmw['_walker']['get_title_from'] = array();
853
- //might we want the (original) branch item's, or root item's, title as the widget title?...
854
- if( $find_branch && $theBranchItem > 0 ){
855
- $cmw['_walker']['get_title_from']['branch'] = $elements[ $this->_cmw_tree[ $theBranchItem ]['element'][0] ];
856
- if( $this->_cmw_tree[ $theBranchItem ]['level'] > 1 ){
857
- $topOfBranch = array_slice( $this->_cmw_tree[ $theBranchItem ]['ancestors'], 1, 1 );
858
- $topOfBranch = $topOfBranch[0];
859
- $cmw['_walker']['get_title_from']['branch_root'] = $elements[ $this->_cmw_tree[ $topOfBranch ]['element'][0] ];
860
- }else{
861
- $cmw['_walker']['get_title_from']['branch_root'] = $cmw['_walker']['get_title_from']['branch'];
862
- }
863
- }
864
- //might we want the current item's, or root item's, title as the widget title?...
865
- if( $currentItem !== false ){
866
- $cmw['_walker']['get_title_from']['current'] = $elements[ $this->_cmw_tree[ $currentItem ]['element'][0] ];
867
- if( $this->_cmw_tree[ $currentItem ]['level'] > 1 ){
868
- $topOfBranch = array_slice( $this->_cmw_tree[ $currentItem ]['ancestors'], 1, 1 );
869
- $topOfBranch = $topOfBranch[0];
870
- $cmw['_walker']['get_title_from']['current_root'] = $elements[ $this->_cmw_tree[ $topOfBranch ]['element'][0] ];
871
- }else{
872
- $cmw['_walker']['get_title_from']['current_root'] = $cmw['_walker']['get_title_from']['current'];
873
- }
874
- }
875
- }
876
-
877
- $this->_cmw_levels = null;
878
- $substructure = array();
879
- if( $continue ){
880
- //now we need to gather together all the 'keep' items from the tree;
881
- //while doing so, we need to set up levels and kids, ready for adding classes...
882
- foreach( $this->_cmw_tree as $k => $v ){
883
- if( $v['keep'] ){
884
- $substructure[ $k ] = $v;
885
- //use kids as a has-submenu flag...
886
- $substructure[ $k ]['kids'] = 0;
887
- //any surviving parent (except the artificial level-0) should have submenu class set on it...
888
- array_shift( $v['ancestors'] ); //remove the level-0
889
- for( $i = count( $v['ancestors'] ) - 1; $i >= 0; $i-- ){
890
- if( isset( $substructure[ $v['ancestors'][ $i ] ] ) ){
891
- $substructure[ $v['ancestors'][ $i ] ]['kids']++;
892
- }else{
893
- //not a 'kept' ancestor so remove it...
894
- array_splice( $v['ancestors'], $i, 1 );
895
- }
896
- }
897
- //ancestors now only has 'kept' ancestors...
898
- $substructure[ $k ]['level'] = count( $v['ancestors'] ) + 1;
899
- //need to ensure that the parent_field of all the new top-level (ie. root) items is set to
900
- //zero, otherwise the parent::walk() will assume they're orphans.
901
- //however, we also need to check that parent_field of a child actually points to the closest
902
- //'kept' ancestor; otherwise, given A (kept) > B (not kept) > C (kept) the parent_field of C
903
- //would point to a non-existent B and would subsequently be considered an orphan!
904
- if( $substructure[ $k ]['level'] == 1){
905
- $ancestor = 0;
906
- }else{
907
- //set to the closest ancestor, ie. the new(?) parent...
908
- $ancestor = array_slice( $v['ancestors'], -1, 1 );
909
- $ancestor = $ancestor[0];
910
- }
911
- //take a copy of the elements item(s)...
912
- $substructure[ $k ]['element'] = array();
913
- foreach( $v['element'] as $i => $j ){
914
- $elements[ $j ]->$parent_field = $ancestor;
915
- $substructure[ $k ]['element'][] = $elements[ $j ];
916
- }
917
- }
918
- }
919
- }
920
- $this->_cmw_tree = null;
921
-
922
- //put substructure's elements back into $elements (remember that $elements is a 1-based array!)...
923
- $elements = array();
924
- $n = 1;
925
- foreach( $substructure as $k => $v ){
926
- $ct = count( $v['element'] ) - 1;
927
- foreach( $v['element'] as $i => $j ){
928
- $elements[ $n ] = $j;
929
- //add the level class...
930
- $elements[ $n ]->classes[] = 'cmw-level-' . $v['level'];
931
- //add the submenu class? (only to last in group!)...
932
- if( $v['kids'] > 0 && $i == $ct ){
933
- $elements[ $n ]->classes[] = 'cmw-has-submenu';
934
- }else{
935
- //3.7 adds a menu-item-has-children class to (original) menu items that have kids : remove it as the item is now childless...
936
- $elements[ $n ]->classes = array_diff( $elements[ $n ]->classes, array('menu-item-has-children') );
937
- }
938
- //add any other CMW classes...
939
- $elements[ $n ]->classes = array_merge( $elements[ $n ]->classes, $v['classes'] );
940
- $n++;
941
- }
942
- }
943
- unset( $substructure );
944
-
945
- //kill the run count...
946
- $runCount--;
947
-
948
- } //end while
949
-
950
- return $elements;
951
-
952
- } //end _cmw_walk()
953
-
954
- /**
955
- * pre-filters elements (v2.1.0)
956
- *
957
- * @param {object} $args Params supplied to wp_nav_menu()
958
- * @param {array} $elements Menu items
959
- * @return {array} Modified menu items
960
- */
961
- private function _cmw_walk_legacy( &$args, $elements ){
962
-
963
- //NB : $elements is an array of objects, indexed by position within the menu (menu_order),
964
- //starting at 1 and incrementing sequentially regardless of parentage (ie. first item is [1],
965
- //second item is [2] whether it's at root or subordinate to first item)
966
-
967
- $cmw =& $args->_custom_menu_wizard;
968
-
969
- $cmw['_walker']['fellback'] = false;
970
-
971
- $find_kids_of = $cmw['filter'] > 0;
972
- $find_specific_items = $cmw['filter'] < 0; //v2.0.0 //v2.0.1:bug fixed (changed < 1 to < 0)
973
- $find_current_item = $find_kids_of && empty( $cmw['filter_item'] );
974
- $find_current_parent = $find_kids_of && $cmw['filter_item'] == -1; //v1.1.0
975
- $find_current_root = $find_kids_of && $cmw['filter_item'] == -2; //v1.1.0
976
- $depth_rel_current = $cmw['depth_rel_current'] && $cmw['depth'] > 0; //v2.0.0
977
- //these could change depending on whether a fallback comes into play (v1.1.0)
978
- $include_parent = $cmw['include_parent'] || $cmw['include_ancestors'];
979
- $include_parent_siblings = $cmw['include_parent_siblings'];
980
-
981
- $id_field = $this->db_fields['id']; //eg. = 'db_id'
982
- $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
983
-
984
- //find the current menu item while creating the tree and levels arrays...
985
- $currentItem = $this->_cmw_find_current_item( $elements, $cmw );
986
-
987
- $allLevels = 9999;
988
- $startWithKidsOf = -1;
989
-
990
- //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...
991
- $continue = true;
992
- if( empty( $currentItem ) && ( $find_current_item || $find_current_parent || $find_current_root || $cmw['contains_current'] ) ){
993
- $continue = false;
994
- }elseif( $find_specific_items && empty( $cmw['items'] ) ){
995
- $continue = false;
996
- }
997
-
998
- // IMPORTANT : as of v2.0.0, start level has been rationalised so that it acts the same across all filters (except for specific items!).
999
- // Previously ...
1000
- // start level for a show-all filter literally started at the specified level and reported all levels until depth was reached.
1001
- // however, start level for a kids-of filter specified the level that the *immediate* kids of the selected filter had to be at
1002
- // or below. That was consistent for a specific item, current-item and current-parent filter, but for a current-root filter what
1003
- // it actually did was test the current item against the start level, not the current item's root ancestor! Inconsistent!
1004
- // But regardless of the current-root filter's use of start level, there was still the inconsistency between show-all and
1005
- // kids-of usage.
1006
- // Now (as of v2.0.0) ...
1007
- // start level and depth have been changed to definitively be secondary filters to the show-all & kids-of primary filter.
1008
- // The primary filter - show-all, or a kids-of - will provide the initial set of items, and the secondary - start level & depth -
1009
- // will further refine that set, with start level being an absolute, and depth still being relative to the first item found.
1010
- // The sole exception to this is when Depth Relative to Current Menu Item is set, which modifies the calculation of depth (only)
1011
- // 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
1012
- // below start level).
1013
- // 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,
1014
- // would fail to return any items because the immediate kids (at level 3) were outside the start level. Now, the returned items
1015
- // will begin with the grand-kids (ie. those at level 4).
1016
- // Note that neither start level nor depth are applicable to a specific items filter (also new at v2.0.0)!
1017
-
1018
- //the kids-of filters...
1019
- if( $continue && $find_kids_of ){
1020
- //specific item...
1021
- if( $cmw['filter_item'] > 0 && isset( $this->_cmw_tree[ $cmw['filter_item'] ] ) && !empty( $this->_cmw_tree[ $cmw['filter_item'] ]['kids'] ) ){
1022
- $startWithKidsOf = $cmw['filter_item'];
1023
- }
1024
- if( $find_current_item ){
1025
- if( !empty( $this->_cmw_tree[ $currentItem ]['kids'] ) ){
1026
- $startWithKidsOf = $currentItem;
1027
- }elseif( $cmw['fallback_no_children'] ){
1028
- //no kids, and fallback to current parent is set...
1029
- //note that there is no "double fallback", so current parent "can" be the artifical zero element (level-0) *if*
1030
- // the current item is a singleton( ie. no kids & no ancestors)!
1031
- $ancestor = array_slice( $this->_cmw_tree[ $currentItem ]['ancestors'], -1, 1 );
1032
- $startWithKidsOf = $ancestor[0]; //can be zero!
1033
- $include_parent = $include_parent || $cmw['fallback_nc_include_parent'];
1034
- $include_parent_siblings = $include_parent_siblings || $cmw['fallback_nc_include_parent_siblings'];
1035
- $cmw['_walker']['fellback'] = 'to-parent';
1036
- }
1037
- }elseif( $find_current_parent || $find_current_root ){
1038
- //as of v2.0.0 the fallback to current item - for current menu items at the top level - is deprecated, but
1039
- //retained for a while to maintain backward compatibility
1040
- //if no parent : fall back to current item (if set)...
1041
- if( $this->_cmw_tree[ $currentItem ]['level'] == 1 && $cmw['fallback_no_ancestor'] ){
1042
- $startWithKidsOf = $currentItem;
1043
- $include_parent = $include_parent || $cmw['fallback_include_parent'];
1044
- $include_parent_siblings = $include_parent_siblings || $cmw['fallback_include_parent_siblings'];
1045
- $cmw['_walker']['fellback'] = 'to-current';
1046
- }else{
1047
- //as of v2.0.0, the artificial level-0 counts as parent of a top-level current menu item...
1048
- if( $find_current_parent ){
1049
- $ancestor = -1;
1050
- }elseif( $this->_cmw_tree[ $currentItem ]['level'] > 1 ){
1051
- $ancestor = 1;
1052
- }else{
1053
- $ancestor = 0;
1054
- }
1055
- $ancestor = array_slice( $this->_cmw_tree[ $currentItem ]['ancestors'], $ancestor, 1 );
1056
- if( !empty( $ancestor ) ){
1057
- $startWithKidsOf = $ancestor[0]; //as of v2.0.0, this can now be zero!
1058
- }
1059
- }
1060
- }
1061
- }
1062
-
1063
- if( $continue ){
1064
- //right, let's set the keep flags
1065
- //for specific items, go straight in on the item id (start level and depth do not apply here)...
1066
- if( $find_specific_items ){
1067
- foreach( preg_split('/[,\s]+/', $cmw['items'] ) as $itemID ){
1068
- if( isset( $this->_cmw_tree[ $itemID ] ) ){
1069
- $this->_cmw_tree[ $itemID ]['keep'] = true;
1070
- $this->_cmw_tree[0]['keepCount']++;
1071
- }
1072
- }
1073
- //for show-all filter, just use the levels...
1074
- }elseif( !$find_kids_of ){
1075
- //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
1076
- if( $depth_rel_current && !empty( $currentItem ) && $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['start_level'] ){
1077
- $bottomLevel = $this->_cmw_tree[ $currentItem ]['level'] + $cmw['depth'] - 1;
1078
- }else{
1079
- $bottomLevel = $cmw['depth'] > 0 ? $cmw['start_level'] + $cmw['depth'] - 1 : $allLevels;
1080
- }
1081
- for( $i = $cmw['start_level']; isset( $this->_cmw_levels[ $i ] ) && $i <= $bottomLevel; $i++ ){
1082
- foreach( $this->_cmw_levels[ $i ] as $itemID ){
1083
- $this->_cmw_tree[ $itemID ]['keep'] = true;
1084
- $this->_cmw_tree[0]['keepCount']++;
1085
- }
1086
- }
1087
- //for kids-of filters, run a recursive through the structure's kids...
1088
- }elseif( $startWithKidsOf > -1 ){
1089
- //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
1090
- //NB the in_array() of ancestors prevents depth_rel_current when startWithKidsOf == currentItem
1091
- if( $depth_rel_current && !empty( $currentItem ) && $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['start_level']
1092
- && in_array( $startWithKidsOf, $this->_cmw_tree[ $currentItem ]['ancestors'] ) ){
1093
- $bottomLevel = $this->_cmw_tree[ $currentItem ]['level'] - 1 + $cmw['depth'];
1094
- }else{
1095
- $bottomLevel = $cmw['depth'] > 0
1096
- ? max( $this->_cmw_tree[ $startWithKidsOf ]['level'] + $cmw['depth'], $cmw['start_level'] + $cmw['depth'] - 1 )
1097
- : $allLevels;
1098
- }
1099
- //$this->_cmw_tree[0]['keepCount'] gets incremented in this recursive method...
1100
- $this->_cmw_legacy_set_keep_kids( $startWithKidsOf, $cmw['start_level'], $bottomLevel );
1101
- }
1102
-
1103
- if( $this->_cmw_tree[0]['keepCount'] > 0 ){
1104
- //we have some items! we now may need to set some more keep flags, depending on the include settings...
1105
-
1106
- //do we need to include parent, parent siblings, and/or ancestors?...
1107
- //NB these are not restricted by start_level!
1108
- if( $find_kids_of && $startWithKidsOf > 0 ){
1109
- if( $include_parent ){
1110
- $this->_cmw_tree[ $startWithKidsOf ]['keep'] = true;
1111
- $this->_cmw_tree[ $startWithKidsOf ]['classes'][] = 'cmw-the-included-parent';
1112
- }
1113
- if( $include_parent_siblings ){
1114
- $ancestor = array_slice( $this->_cmw_tree[ $startWithKidsOf ]['ancestors'], -1, 1);
1115
- foreach($this->_cmw_tree[ $ancestor[0] ]['kids'] as $itemID ){
1116
- //may have already been kept by include_parent...
1117
- if( !$this->_cmw_tree[ $itemID ]['keep'] ){
1118
- $this->_cmw_tree[ $itemID ]['keep'] = true;
1119
- $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-parent-sibling';
1120
- }
1121
- }
1122
- }
1123
- if( $cmw['include_ancestors'] ){
1124
- foreach( $this->_cmw_tree[ $startWithKidsOf ]['ancestors'] as $itemID ){
1125
- if( $itemID > 0 && !$this->_cmw_tree[ $itemID ]['keep'] ){
1126
- $this->_cmw_tree[ $itemID ]['keep'] = true;
1127
- $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-parent-ancestor';
1128
- }
1129
- }
1130
- }
1131
- }
1132
- }
1133
- }
1134
-
1135
- //check that (a) we have items, and (b) if we must have current menu item, we've got it...
1136
- $continue = $this->_cmw_tree[0]['keepCount'] > 0 && ( !$cmw['contains_current'] || $this->_cmw_tree[ $currentItem ]['keep'] );
1137
- //check for title_from...
1138
- if( $continue ){
1139
- //might we want the parent's title as the widget title?...
1140
- if( $find_kids_of && $cmw['title_from_parent'] && $startWithKidsOf > 0 ){
1141
- $cmw['_walker']['parent_title'] = apply_filters(
1142
- 'the_title',
1143
- $elements[ $this->_cmw_tree[ $startWithKidsOf ]['element'][0] ]->title,
1144
- $elements[ $this->_cmw_tree[ $startWithKidsOf ]['element'][0] ]->ID
1145
- );
1146
- }
1147
- //might we want the current item's title as the widget title?...
1148
- if( !empty( $currentItem ) && $cmw['title_from_current'] ){
1149
- $cmw['_walker']['current_title'] = apply_filters(
1150
- 'the_title',
1151
- $elements[ $this->_cmw_tree[ $currentItem ]['element'][0] ]->title,
1152
- $elements[ $this->_cmw_tree[ $currentItem ]['element'][0] ]->ID
1153
- );
1154
- }
1155
- }
1156
-
1157
- $this->_cmw_levels = null;
1158
- $substructure = array();
1159
- if( $continue ){
1160
- //now we need to gather together all the 'keep' items from the tree;
1161
- //while doing so, we need to set up levels and kids, ready for adding classes...
1162
- foreach( $this->_cmw_tree as $k => $v ){
1163
- if( $k > 0 && $v['keep'] ){
1164
- $substructure[ $k ] = $v;
1165
- //use kids as a has-submenu flag...
1166
- $substructure[ $k ]['kids'] = 0;
1167
- //any surviving parent (except the artificial level-0) should have submenu class set on it...
1168
- array_shift( $v['ancestors'] ); //remove the level-0
1169
- for( $i = count( $v['ancestors'] ) - 1; $i >= 0; $i-- ){
1170
- if( isset( $substructure[ $v['ancestors'][ $i ] ] ) ){
1171
- $substructure[ $v['ancestors'][ $i ] ]['kids']++;
1172
- }else{
1173
- //not a 'kept' ancestor so remove it...
1174
- array_splice( $v['ancestors'], $i, 1 );
1175
- }
1176
- }
1177
- //ancestors now only has 'kept' ancestors...
1178
- $substructure[ $k ]['level'] = count( $v['ancestors'] ) + 1;
1179
- //need to ensure that the parent_field of all the new top-level (ie. root) items is set to
1180
- //zero, otherwise the parent::walk() will assume they're orphans.
1181
- //however, we also need to check that parent_field of a child actually points to the closest
1182
- //'kept' ancestor; otherwise, given A (kept) > B (not kept) > C (kept) the parent_field of C
1183
- //would point to a non-existent B and would subsequently be considered an orphan!
1184
- if( $substructure[ $k ]['level'] == 1){
1185
- $ancestor = 0;
1186
- }else{
1187
- //set to the closest ancestor, ie. the new(?) parent...
1188
- $ancestor = array_slice( $v['ancestors'], -1, 1 );
1189
- $ancestor = $ancestor[0];
1190
- }
1191
- //take a copy of the elements item(s)...
1192
- $substructure[ $k ]['element'] = array();
1193
- foreach( $v['element'] as $i => $j ){
1194
- $elements[ $j ]->$parent_field = $ancestor;
1195
- $substructure[ $k ]['element'][] = $elements[ $j ];
1196
- }
1197
- }
1198
- }
1199
- }
1200
- $this->_cmw_tree = null;
1201
-
1202
- //put substructure's elements back into $elements (remember that $elements is a 1-based array!)...
1203
- $elements = array();
1204
- $n = 1;
1205
- foreach( $substructure as $k => $v ){
1206
- $ct = count( $v['element'] ) - 1;
1207
- foreach( $v['element'] as $i => $j ){
1208
- $elements[ $n ] = $j;
1209
- //add the level class...
1210
- $elements[ $n ]->classes[] = 'cmw-level-' . $v['level'];
1211
- //add the submenu class? (only to last in group!)...
1212
- if( $v['kids'] > 0 && $i == $ct ){
1213
- $elements[ $n ]->classes[] = 'cmw-has-submenu';
1214
- }elseif( $v['kids'] == 0 ){
1215
- //3.7 adds a menu-item-has-children class to (original) menu items that have kids : remove it as the item is now childless...
1216
- $elements[ $n ]->classes = array_diff( $elements[ $n ]->classes, array('menu-item-has-children') );
1217
- }
1218
- //add any other CMW classes...
1219
- $elements[ $n ]->classes = array_merge( $elements[ $n ]->classes, $v['classes'] );
1220
- $n++;
1221
- }
1222
- }
1223
- unset( $substructure );
1224
-
1225
- return $elements;
1226
-
1227
- } //end _cmw_walk_legacy()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1228
 
1229
  } //end Custom_Menu_Wizard_Walker class
1230
  ?>
3
  * Custom Menu Wizard plugin
4
  *
5
  * Custom Menu Wizard Walker class
6
+ * NB: Walker_Nav_Menu class is in wp-includes/nav-menu-template.php, and is itself an
7
  * extension of the Walker class (wp-includes/class-wp-walker.php)
8
  */
9
  class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
10
 
11
+ /**
12
+ * CMW custom variables
13
+ */
14
+ private $_cmw_tree;
15
+ private $_cmw_lowest;
16
+ private $_cmw_highest;
17
+
18
+ /**
19
+ * opens a sub-level with a UL or OL start-tag
20
+ *
21
+ * @param string $output Passed by reference. Used to append additional content.
22
+ * @param int $depth Depth of page. Used for padding.
23
+ */
24
+ public function start_lvl( &$output, $depth = 0, $args = array() ) {
25
+
26
+ $indent = str_repeat("\t", $depth);
27
+ $listtag = empty( $args->_custom_menu_wizard['ol_sub'] ) ? 'ul' : 'ol';
28
+ $output .= "\n$indent<$listtag class=\"sub-menu\">\n";
29
+
30
+ } //end start_lvl()
31
+
32
+ /**
33
+ * closes a sub-level with a UL or OL end-tag
34
+ *
35
+ * @param string $output Passed by reference. Used to append additional content.
36
+ * @param int $depth Depth of page. Used for padding.
37
+ */
38
+ public function end_lvl( &$output, $depth = 0, $args = array() ) {
39
+
40
+ $indent = str_repeat("\t", $depth);
41
+ $listtag = empty( $args->_custom_menu_wizard['ol_sub'] ) ? 'ul' : 'ol';
42
+ $output .= "$indent</$listtag>\n";
43
+
44
+ } //end end_lvl()
45
+
46
+ /**
47
+ * pre-filters elements then calls parent::walk()
48
+ *
49
+ * @filters : custom_menu_wizard_walker_items array of filtered menu elements; array of args
50
+ *
51
+ * @param array $elements Menu items
52
+ * @param integer $max_depth
53
+ * @return string
54
+ */
55
+ public function walk($elements, $max_depth){
56
+
57
+ $args = array_slice(func_get_args(), 2);
58
+ $args = $args[0];
59
+
60
+ if( $max_depth >= -1 && !empty( $elements ) && isset($args->_custom_menu_wizard) ){
61
+
62
+ if( empty( $args->_custom_menu_wizard['cmwv'] ) ){
63
+ $elements = $this->_cmw_walk_legacy( $args, $elements );
64
+ }else{
65
+ $elements = $this->_cmw_walk( $args, $elements );
66
+ }
67
+
68
+ //since we've done all the depth filtering, set max_depth to unlimited (unless flat output was requested!)...
69
+ if( empty( $args->_custom_menu_wizard['flat_output'] ) ){
70
+ $max_depth = 0;
71
+ }else{
72
+ //for v3.1.0 we now need to specifically reset max_depth in case we're using the alternative and it has changed
73
+ //from hierarchic output to flat output...
74
+ $max_depth = -1;
75
+ }
76
+
77
+ } //ends the check for bad max depth, empty elements, or empty cmw args
78
+
79
+ return empty( $elements ) ? '' : parent::walk( apply_filters( 'custom_menu_wizard_walker_items', $elements, $args ), $max_depth, $args );
80
+
81
+ } //end walk()
82
+
83
+ /**
84
+ * current & legacy : finds and returns ID of current menu item while creating the tree and levels arrays
85
+ *
86
+ * @param array $elements Array of menu items
87
+ * @param array $cmw Array of widget instance settings
88
+ * @return integer ID of current menu item (false if not found)
89
+ */
90
+ private function _cmw_find_current_item( &$elements, &$cmw ){
91
+ //$elements is an array of objects, indexed by position within the menu (menu_order),
92
+ //starting at 1 and incrementing sequentially regardless of parentage (ie. first item is [1],
93
+ //second item is [2] whether it's at root or subordinate to first item)
94
+
95
+ $id_field = $this->db_fields['id']; //eg. = 'db_id'
96
+ $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
97
+ $currentItem = array();
98
+ $ciSequencer = empty( $cmw['fallback_ci_lifo'] ) ? 1 : -999; //v3.1.5
99
+
100
+ $this->_cmw_tree = array(
101
+ 0 => array(
102
+ 'level' => 0,
103
+ 'ancestors' => array(),
104
+ 'kids' => array(),
105
+ 'element' => array(),
106
+ 'keepCount' => 0,
107
+ 'keep' => false
108
+ )
109
+ );
110
+ $this->_cmw_levels = array(
111
+ array() //for the artificial level-0
112
+ );
113
+
114
+ if( isset( $cmw['_exclude'] ) ){ //only came in at v3.0.0!
115
+ //v3+ allows for inheritance on the $cmw items and exclude csv fields, which means that the _items and _exclude
116
+ //arrays do not necessarly contain all the relevant items ids, eg. an element such as '23+' in the _exclude array means
117
+ //that menu item id = 23 is excluded ... AND so are all its descendants!
118
+ $cmw['__items'] = array();
119
+ $cmw['__exclude'] = array();
120
+ $inheritItems = array();
121
+ $inheritExclude = array();
122
+ foreach( $cmw['_items'] as $i ){
123
+ if( substr( $i, -1 ) == '+' ){
124
+ $i = substr( $i, 0, -1 );
125
+ $inheritItems[] = $i;
126
+ }
127
+ $cmw['__items'][] = $i;
128
+ }
129
+ foreach( $cmw['_exclude'] as $i ){
130
+ if( substr( $i, -1 ) == '+' ){
131
+ $i = substr( $i, 0, -1 );
132
+ $inheritExclude[] = $i;
133
+ }
134
+ $cmw['__exclude'][] = $i;
135
+ }
136
+ }
137
+
138
+ foreach( $elements as $i => $item ){
139
+ $itemID = $item->$id_field;
140
+ $parentID = empty( $item->$parent_field ) ? 0 : $item->$parent_field;
141
+
142
+ //if $this->_cmw_tree[] for the parent hasn't been set then it's an orphan...
143
+ //NOTE The parent Walker will only handle orphans in a "hierarchical Show All" situation (ie. non-flat output, unlimited depth)
144
+ // and it does so by appending each orphan as a new top-level item (and note that a child of an orphan is also an orphan!).
145
+ // However, CMW excludes all orphans (which includes descendants of orphans) so they never reach the parent Walker.
146
+ // If orphans are required, use WordPress's own Custom Menu widget.
147
+ if( isset( $this->_cmw_tree[ $parentID ] ) ){
148
+ //check for current item (as a menu item ID, ie. a key into the tree)...
149
+ if( $item->current ||
150
+ ( isset( $inheritItems ) && ( $hasCurrentClass = in_array( 'current-menu-item', (array)$item->classes ) ) === true ) ||
151
+ ( isset( $inheritItems ) && !empty( $cmw['fallback_ci_parent'] ) && $item->current_item_parent )
152
+ ){
153
+ //should(!) never get either parent and/or ancestor on an item marked as "current", but unfortunately it does occur (grrr!).
154
+ //so this has to cope, not only with more than 1 "current" item, but also with "current" items that are incorrectly marked
155
+ //as (their own?!) parent and/or ancestor.
156
+ //
157
+ //v3.1.0 : there are also occasions when the item is given a class of current-menu-item but the 'current' property is not set
158
+ //on the item - one such occasion being when the home page is set to latest posts (the default 'blogging' setting for Front
159
+ //page displays, see Settings/Reading page) and you navigate to the second (or subsequent) page of listed posts. A menu item
160
+ //that has the "Home Page" url (it's a "custom" menu item type) will fail to match any sort of current url (.../page/2/
161
+ //or .../?paged=2) but does get recognised as being a "front page" url, so gets the class but no property! ON BY DEFAULT!
162
+ //v3.1.0 : also occasions where $item->current_item_parent is set without there being any item with $item->current!
163
+ //eg. open a post, and if there is a Category menu item available (the post has that category) then Category menu item
164
+ //gets marked as current_item_parent. HAS TO BE ENABLED!
165
+ //
166
+ //we're going to look for correctly (solely) marked "current" items and take the first one found
167
+ //failing that, look for a "current" item that is also marked as parent, and, again, use the first one found
168
+ //failing that, look for a "current" item that is also marked as an ancestor, and, again, use the first one found
169
+ //failing that, look for an item classed as "current-menu-item", again using the first one found
170
+ //finally, if enabled, look for an item marked as parent (if it gets used then there's no current!), using first one found
171
+ //
172
+ //array keys, priority order :
173
+ // just current -> parent, not ancestor -> parent and ancestor -> ancestor -> .current-menu-item -> parent
174
+ // first found...
175
+ // - a001 : just current
176
+ // - b001 : current & parent (not ancestor)
177
+ // - c001 : current & parent & ancestor
178
+ // - d001 : current & ancestor (not parent)
179
+ // - e001 : .current-menu-item
180
+ // - f001 : parent (not current)
181
+ // next found...
182
+ // - a002 : just current
183
+ // - b002 : current & parent (not ancestor)
184
+ // - c002 : current & parent & ancestor
185
+ // - d002 : current & ancestor (not parent)
186
+ // - e002 : .current-menu-item
187
+ // - f002 : parent (not current)
188
+ // etc
189
+ //example :
190
+ // - 1st found : current & ancestor = d001
191
+ // - 2nd found : current & parent & ancestor = c002
192
+ // - 3rd found : just current = a003
193
+ // - 4th found : current & parent = b004
194
+ // - 5th found : just current = a005
195
+ //sort keys alphabetically and a003 comes out on top, so third found gets used! (copes with 999 "current"
196
+ //items; should be enough!)
197
+ //
198
+ //as of v3.1.5 there's a twist : here's the scenario :
199
+ // - root level is a Category item (showing , say, posts belonging to CatA)
200
+ // - it has a sub-menu item that does exactly the same!
201
+ // - navigate to a single-post display of a post belonging to CatA and CMW will determine that the root level item is current
202
+ //Now, if all the other sub-items were, say, Category items then you could reasonably expect CMW to produce roughly
203
+ //comparable menus if any sub-item was "current", but because of using the first-found then "current" is 1 level above, and
204
+ //could result in a different menu structure than all the other sub-items produce.
205
+ //Note that this is only a problem where the page being displayed is not directly related to a menu item, because then
206
+ //the relevant menu item would be definitively marked as the current item. As such, it will probably only occur when the
207
+ //fallback_ci_parent option is enabled.
208
+ //So, I am providing the means to change first-found to last-found, $cmw['current_lifo'] (as opposed to FIFO).
209
+ //NOTE this only affects the sequence number not the conditions! We just count backwards from 999!
210
+ //example :
211
+ // - 1st found : current & ancestor = d999
212
+ // - 2nd found : current & parent & ancestor = c998
213
+ // - 3rd found : just current = a997
214
+ // - 4th found : current & parent = b996
215
+ // - 5th found : just current = a995
216
+ //sort keys alphabetically and a995 comes out on top, so fifth found gets used!
217
+ if( $item->current ){
218
+ if( $item->current_item_ancestor ){
219
+ if( $item->current_item_parent ){
220
+ $j = 'c';
221
+ }else{
222
+ $j = 'd';
223
+ }
224
+ }elseif( $item->current_item_parent ){
225
+ $j = 'b';
226
+ }else{
227
+ $j = 'a';
228
+ }
229
+ }elseif( $hasCurrentClass ){
230
+ $j = 'e';
231
+ }else{
232
+ $j = 'f';
233
+ }
234
+ $currentItem[ $j . sprintf( '%03d' , abs( count( $currentItem ) + $ciSequencer ) ) ] = $itemID;
235
+ }
236
+
237
+ //because it is possible for $itemID not to be unique, we have to play canny :
238
+ //we need to be able to store multiple references back into $elements for each $itemID, and we therefore have to
239
+ //check whether we already have a $this->_cmw_tree entry for the $itemID, and if we do then simply store the pointer
240
+ //back to $elements in the existing $this->_cmw_tree entry for the item ID.
241
+ //This has consequences :
242
+ // - the individual menu items (for non-unique item IDs) will be in or out as a group, not as individual items
243
+ // - if we happen to need, say, the branch title from the branch being filtered, we have to take it from the
244
+ // first of the group and there need not be any relation between that item's title and the original menu item's
245
+ // title (as set on the WP Menu Admin page). Can't help that.
246
+ // - any child will be a child of the group, which actually means it *should* become (at some point?!) the child
247
+ // of the last member of the group(?!). BUT what if the child is actually intended to be the child of the item it follows,
248
+ // eg. itemID N, child, itemID N, child, itemID N, itemID N, child, child, itemID N, etc?
249
+ // CMW can't cope with this : its position is that if WP's Walker can't cope with it then there is no reason why CMW should
250
+ // attempt to rectify the few (rare) occasions where these situations may arise. (It's possible, but I have chosen not to).
251
+ // NB The parent Walker assigns children of a non-unique-id menu item to the 1st occurence of the item ID (1st in group)
252
+ if( isset( $this->_cmw_tree[ $itemID ] ) ){
253
+ //it's a non-unique item ID! just add the $elements pointer to the element array...
254
+ $this->_cmw_tree[ $itemID ]['element'][] = $i;
255
+ }else{
256
+ //this level...
257
+ $thisLevel = $this->_cmw_tree[ $parentID ]['level'] + 1;
258
+ if( empty( $this->_cmw_levels[ $thisLevel ] ) ){
259
+ $this->_cmw_levels[ $thisLevel ] = array();
260
+ }
261
+ $this->_cmw_levels[ $thisLevel ][] = $itemID;
262
+
263
+ $this->_cmw_tree[ $itemID ] = array(
264
+ //level within structure...
265
+ 'level' => $thisLevel,
266
+ //ancestors (from the artificial level-0, right down to parent, inclusive) within structure...
267
+ 'ancestors' => $this->_cmw_tree[ $parentID ]['ancestors'],
268
+ //kids within structure, ie array of itemID's...
269
+ 'kids' => array(),
270
+ //index of item within elements...
271
+ 'element' => array( $i ),
272
+ //classes added by widget...
273
+ 'classes' => array(),
274
+ //assume no match...
275
+ 'keep' => false
276
+ );
277
+ //append this item's parent onto its ancestors...
278
+ $this->_cmw_tree[ $itemID ]['ancestors'][] = $parentID;
279
+ //add this item to its parent's kids...
280
+ $this->_cmw_tree[ $parentID ]['kids'][] = $itemID;
281
+
282
+ //check for inheritance on items and exclude...
283
+ if( isset( $inheritItems ) ){
284
+ if( in_array( $parentID, $inheritItems ) ){
285
+ $inheritItems[] = $itemID;
286
+ $cmw['__items'][] = $itemID;
287
+ }
288
+ if( in_array( $parentID, $inheritExclude ) ){
289
+ $inheritExclude[] = $itemID;
290
+ $cmw['__exclude'][] = $itemID;
291
+ }
292
+ }
293
+ }
294
+ }
295
+ } //end foreach
296
+
297
+ if( isset( $inheritItems ) ){
298
+ unset( $inheritItems, $inheritExclude );
299
+ }
300
+
301
+ if( empty( $currentItem ) ){
302
+ $currentItem = false;
303
+ }else{
304
+ ksort( $currentItem );
305
+ $currentItem = array_shift( $currentItem );
306
+ }
307
+
308
+ return $currentItem;
309
+
310
+ } //end _cmw_find_current_item()
311
+
312
+ /**
313
+ * current: test for one item being below another item and/or its siblings
314
+ *
315
+ * @param integer $lookBelow Menu item ID that needs checking, possibly its siblings as well
316
+ * @param integer $searchFor Menu item ID that is being looked for
317
+ * @param boolean $andSiblings Whether to check in $lookBelow's siblings as well
318
+ * @return boolean Found (or not)
319
+ */
320
+ private function _cmw_check_contains_item( $lookBelow, $searchFor, $andSiblings ){
321
+
322
+ $rtn = in_array( $lookBelow, $this->_cmw_tree[ $searchFor ]['ancestors'] );
323
+ if( !$rtn && $andSiblings ){
324
+ foreach( $this->_cmw_tree[ $this->_cmw_get_parent( $lookBelow ) ]['kids'] as $kid ){
325
+ //check whether one of $searchFor's ancestors is $kid...
326
+ if( $kid != $lookBelow && in_array( $kid, $this->_cmw_tree[ $searchFor ]['ancestors'] ) ){
327
+ $rtn = true;
328
+ break;
329
+ }
330
+ }
331
+ }
332
+ return $rtn;
333
+
334
+ }
335
+
336
+ /**
337
+ * clear any keep flags currently set in the tree
338
+ */
339
+ private function _cmw_clear_down_tree(){
340
+
341
+ if( $this->_cmw_tree[0]['keepCount'] > 0 ){
342
+ foreach( $this->_cmw_tree as $k => $v ){
343
+ $this->_cmw_tree[ $k ]['keep'] = false;
344
+ $this->_cmw_tree[ $k ]['classes'] = array();
345
+ }
346
+ $this->_cmw_tree[0]['keepCount'] = 0;
347
+ }
348
+
349
+ }
350
+
351
+ /**
352
+ * resolve digit(s) optionally followed by a plus/minus into a 'from' level an a 'to' level
353
+ * IMPORTANT : 'from' is inclusive, 'to' is exclusive, so a for() would be for( $i = $rtn['from']; $i < $rtn['to']; $i++ )
354
+ *
355
+ * @param {string} $option Level with optional +/- appended
356
+ * @return {array} False if $option doesn't parse
357
+ */
358
+ private function _cmw_decipher_plusminus_level( $option ){
359
+
360
+ $rtn = array();
361
+ if( !empty( $option ) && preg_match( '/^(\d+)(\+|-)?$/', $option, $m ) > 0 ){
362
+ $m[1] = intval( $m[1] );
363
+ if( $m[1] > 0 ){
364
+ if( empty( $m[2] ) ){
365
+ //no plus/minus : 'from' is the level, 'to' is the next level...
366
+ $rtn['from'] = $m[1];
367
+ $rtn['to'] = $m[1] + 1;
368
+ }elseif( $m[2] == '+' ){
369
+ //plus : 'from' is the level, 'to' is the number of levels
370
+ //NB: there is an artificial level zero, so if the menu has 10 levels, a count of levels will give 11!
371
+ $rtn['from'] = $m[1];
372
+ $rtn['to'] = count( $this->_cmw_levels );
373
+ }else{
374
+ //minus : 'from' is level 1, 'to' is the level plus 1
375
+ $rtn['from'] = 1;
376
+ $rtn['to'] = $m[1] + 1;
377
+ }
378
+ }
379
+ }
380
+ return empty( $rtn ) ? false : $rtn;
381
+
382
+ }
383
+
384
+ /**
385
+ * returns the menu item id if an item's parent
386
+ *
387
+ * @param integer $kid Menu item ID
388
+ * @return integer Menu item ID of kid's parent
389
+ */
390
+ private function _cmw_get_parent( $kid ){
391
+
392
+ $immediateParent = array_slice( $this->_cmw_tree[ $kid ]['ancestors'], -1, 1);
393
+ return $immediateParent[0];
394
+
395
+ }
396
+
397
+ /**
398
+ * current: set keep flag of an item's siblings
399
+ *
400
+ * @param integer $itemID Menu item ID
401
+ * @param string $classSuffix Suffix of class to be added to the kept items
402
+ */
403
+ private function _cmw_include_siblings_of( $itemID, $classSuffix='sibling' ){
404
+
405
+ foreach( $this->_cmw_tree[ $this->_cmw_get_parent( $itemID ) ]['kids'] as $i ){
406
+ if( !$this->_cmw_tree[ $i ]['keep'] ){
407
+ $this->_cmw_tree[ $i ]['keep'] = true;
408
+ $this->_cmw_tree[0]['keepCount']++;
409
+ if( !empty( $classSuffix ) ){
410
+ $this->_cmw_tree[ $i ]['classes'][] = 'cmw-an-included-' . $classSuffix;
411
+ }
412
+ }
413
+ }
414
+
415
+ } //end _cmw_include_siblings_of()
416
+
417
+ /**
418
+ * runs exclusions, if there are any
419
+ *
420
+ * @param {array} $cmw Settings
421
+ * @return {boolean} keepCount > 0?
422
+ */
423
+ private function _cmw_run_exclusions( &$cmw ){
424
+
425
+ $rtn = $this->_cmw_tree[0]['keepCount'] > 0;
426
+ if( $rtn && !empty( $cmw['__exclude'] )){
427
+ foreach( $cmw['__exclude'] as $itemID ){
428
+ if( $itemID > 0 && isset( $this->_cmw_tree[ $itemID ] ) && $this->_cmw_tree[ $itemID ]['keep'] ){
429
+ $this->_cmw_tree[ $itemID ]['keep'] = false;
430
+ $this->_cmw_tree[0]['keepCount']--;
431
+ }
432
+ }
433
+ $rtn = $this->_cmw_tree[0]['keepCount'] > 0;
434
+ }
435
+ if( $rtn && ( $fromTo = $this->_cmw_decipher_plusminus_level( $cmw['exclude_level'] ) ) !== false ){
436
+ while( isset( $this->_cmw_levels[ $fromTo['from'] ] ) && $fromTo['from'] < $fromTo['to'] ){
437
+ foreach( $this->_cmw_levels[ $fromTo['from'] ] as $itemID ){
438
+ if( $this->_cmw_tree[ $itemID ]['keep'] ){
439
+ $this->_cmw_tree[ $itemID ]['keep'] = false;
440
+ $this->_cmw_tree[0]['keepCount']--;
441
+ }
442
+ }
443
+ $fromTo['from']++;
444
+ }
445
+ $rtn = $this->_cmw_tree[0]['keepCount'] > 0;
446
+ }
447
+ return $rtn;
448
+
449
+ }
450
+
451
+ /**
452
+ * current : recursively set the keep flag if within specified level/depth
453
+ * if item passed in is eligible, sets that item as kept and runs through its kids recursively
454
+ * uses _cmw_lowest & _cmw_highest : note that _cmw_lowest is the lowest level in the structure - *not*
455
+ * the numerically lowest value of level - and that both are inclusive!
456
+ *
457
+ * @param integer $itemID Menu item ID
458
+ */
459
+ private function _cmw_set_keep_recursive( $itemID ){
460
+
461
+ //at or above lowest?...
462
+ if( $this->_cmw_tree[ $itemID ]['level'] <= $this->_cmw_lowest ){
463
+ //at or below highest?...
464
+ if( $this->_cmw_tree[ $itemID ]['level'] >= $this->_cmw_highest ){
465
+ //keep (if not already)...
466
+ if( !$this->_cmw_tree[ $itemID ]['keep'] ){
467
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
468
+ $this->_cmw_tree[0]['keepCount']++;
469
+ }
470
+ }
471
+ //unless this item is above the lowest level there's no point checking its kids...
472
+ if( $this->_cmw_tree[ $itemID ]['level'] < $this->_cmw_lowest ){
473
+ for( $i = 0, $ct = count( $this->_cmw_tree[ $itemID ]['kids'] ); $i < $ct; $i++ ){
474
+ $this->_cmw_set_keep_recursive( $this->_cmw_tree[ $itemID ]['kids'][ $i ] );
475
+ }
476
+ }
477
+ }
478
+
479
+ } //end _cmw_set_keep_recursive()
480
+
481
+ /**
482
+ * switch the current settings for those indicated by a cmwizard shortcode
483
+ *
484
+ * @param object $args The args passed into walk()
485
+ * @param string $at The current processing stage
486
+ * @param boolean $hasCurrent Whether or not current item is in this stage
487
+ * @param boolean $hasOutput Whether or not there will be any output (as best we know so far)
488
+ * @return boolean True if we can use alternative settings
489
+ */
490
+ private function _cmw_switch_settings( &$args, $at = '', $hasCurrent = false, $hasOutput = false ){
491
+
492
+ if( $args->_custom_menu_wizard['switch_at'] == $at && (
493
+ ( $args->_custom_menu_wizard['switch_if'] == 'current' && $hasCurrent ) ||
494
+ ( $args->_custom_menu_wizard['switch_if'] == 'no-current' && !$hasCurrent ) ||
495
+ ( $args->_custom_menu_wizard['switch_if'] == 'no-output' && !$hasOutput )
496
+ ) ){
497
+
498
+ $plugin = Custom_Menu_Wizard_Plugin::init();
499
+
500
+ //if switch_to is empty, it gets defaulted to a minimum
501
+ //trim off square brackets, self-terminators, and spaces...
502
+ $switchTo = trim( $args->_custom_menu_wizard['switch_to'], ' ][/' );
503
+ //if it doesn't start with our shortcode, prepend it...
504
+ if( substr( $switchTo . ' ', 0, 9 ) != 'cmwizard ' ){
505
+ $switchTo = 'cmwizard ' . $switchTo;
506
+ }
507
+ //append our current menu so that it will be used when the shortcode atts are parsed...
508
+ $switchTo = trim( $switchTo ) . ' menu=' . $args->_custom_menu_wizard['menu'];
509
+
510
+ if( ( $new_cmw = $plugin->encode_shortcode( $switchTo ) ) !== false ){
511
+ //store old...
512
+ $old_cmw = array_merge( array(), $args->_custom_menu_wizard );
513
+ //merge new into old, overriding _walker...
514
+ $new_cmw = array_merge( $old_cmw, (array)$new_cmw, array('_walker' => array('alternative' => true)) );
515
+ //overwrite current with new...
516
+ $args->_custom_menu_wizard = $new_cmw;
517
+ //put new and old into the current _walker, so that they become available to the widget
518
+ //instance (as long as there's some output!)...
519
+ $args->_custom_menu_wizard['_walker']['instances'] = array( 'old' => $old_cmw, 'new' => $new_cmw );
520
+ unset( $old_cmw );
521
+
522
+ return true;
523
+ }
524
+ }
525
+
526
+ return false;
527
+
528
+ } //end _cmw_switch_settings()
529
+
530
+ /**
531
+ * legacy : recursively set the keep flag if within specified level/depth
532
+ * runs through kids of item passed in : if kid is eligible, sets kid to kept; if grandkids might be eligible, recurse with kid
533
+ *
534
+ * @param integer $itemID Menu item ID
535
+ * @param integer $topLevel Uppermost level that can be kept
536
+ * @param integer $bottomLevel Lowermost level that can be kept
537
+ */
538
+ private function _cmw_legacy_set_keep_kids( $itemId, $topLevel, $bottomLevel ){
539
+
540
+ for( $i = 0, $ct = count( $this->_cmw_tree[ $itemId ]['kids'] ); $i < $ct; $i++ ){
541
+ $j = $this->_cmw_tree[ $itemId ]['kids'][ $i ];
542
+ if( $this->_cmw_tree[ $j ]['level'] <= $bottomLevel ){
543
+ if( ( $this->_cmw_tree[ $j ]['keep'] = $this->_cmw_tree[ $j ]['level'] >= $topLevel ) !== false){
544
+ $this->_cmw_tree[0]['keepCount']++;
545
+ }
546
+ }
547
+ if( $this->_cmw_tree[ $j ]['level'] < $bottomLevel ){
548
+ $this->_cmw_legacy_set_keep_kids( $j, $topLevel, $bottomLevel );
549
+ }
550
+ }
551
+
552
+ } //end _cmw_legacy_set_keep_kids()
553
+
554
+ /**
555
+ * pre-filters elements
556
+ *
557
+ * @filters : custom_menu_wizard_walker_change_settings array of current CMW settings; id of current menu item; array of original menu elements
558
+ * gets applied immediately after determination of the current item, and can be used to provide an alternate set of CMW settings
559
+ * based, maybe, on the value (presence/absence?) of a current menu item, or some other specific value in the current settings.
560
+ * if the returned settings don't exactly match those currently in use, then the new ones are used and current item is re-determined.
561
+ *
562
+ * @param {object} $args Params supplied to wp_nav_menu()
563
+ * @param {array} $elements Menu items
564
+ * @return {array} Modified menu items
565
+ */
566
+ private function _cmw_walk( &$args, $elements ){
567
+
568
+ $id_field = $this->db_fields['id']; //eg. = 'db_id'
569
+ $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
570
+ $unlimited = 65532;
571
+ //max number of run-throughs is 2!...
572
+ $runCount = 2;
573
+
574
+ $cmw =& $args->_custom_menu_wizard;
575
+
576
+ while( $runCount > 0 ){
577
+
578
+ $runCount--;
579
+ $topOfBranch = -1;
580
+ $continue = true;
581
+
582
+ //find the current menu item (ID of the menu item) while creating the tree and levels arrays...
583
+ $currentItem = $this->_cmw_find_current_item( $elements, $cmw );
584
+
585
+ //allow (once only!) a filter to change the cmw settings based on the presence (or absence) of a current item...
586
+ //note that not all changes to settings will have any influence; if the walker doesn't use them, and
587
+ //the widget processing subsequent to the wp_nav_menu() call doesn't use them, then they will have no effect!
588
+ //also note that utilising this filter will prevent any subsequent switchable from being actioned (because of the runCount).
589
+ //BE AWARE : indiscriminate changes to the cmw settings have the potential to totally screw up the output!
590
+ $new_cmw = $runCount > 0 ? apply_filters( 'custom_menu_wizard_walker_change_settings', $cmw, $currentItem, $elements ) : false;
591
+ if( $new_cmw !== false && $new_cmw !== $cmw ){
592
+ //store old...
593
+ $old_cmw = array_merge( array(), $cmw );
594
+ //merge new into old, and don't allow modification of _walker...
595
+ $new_cmw = array_merge( $old_cmw, (array)$new_cmw, array('_walker' => array()) );
596
+ //overwrite current with new...
597
+ $args->_custom_menu_wizard = $new_cmw;
598
+ //put new and old into the current _walker, so that they become available to the widget
599
+ //instance (as long as there's some output!)...
600
+ $cmw['_walker']['instances'] = array( 'old' => $old_cmw, 'new' => $new_cmw );
601
+ unset( $old_cmw );
602
+ //back to top of while loop...
603
+ continue;
604
+ }
605
+ unset( $new_cmw );
606
+
607
+ $cmw['_walker']['fellback'] = false;
608
+
609
+ $find_items = $cmw['filter'] == 'items';
610
+ $find_branch = $cmw['filter'] == 'branch';
611
+ $find_level = !$find_items && !$find_branch;
612
+ $find_current = $find_branch && empty( $cmw['branch'] );
613
+
614
+ //measuring depth relative to the current item only applies if depth is *not* unlimited...
615
+ $depth = intval( $cmw['depth'] );
616
+ $depth_rel_current = $cmw['depth_rel_current'] && $depth > 0 && $currentItem !== false; //v2.0.0
617
+ //no-kids fallback?...
618
+ $canFallback = $find_current && in_array( $cmw['fallback'], array('current', 'parent', 'quit') );
619
+ //switchable?...
620
+ //note that switchable does not require switch_to to contain a value!
621
+ $canSwitch = $runCount > 0 && !empty( $cmw['switch_if'] ) && !empty( $cmw['switch_at'] );
622
+
623
+ //check for current item and switch...
624
+ $hasCurrent = $currentItem !== false;
625
+ //no current item means that a current branch filter CAN'T produce output...
626
+ if( $find_current ){
627
+ $continue = $hasCurrent;
628
+ }
629
+ if( $continue && $cmw['contains_current'] == 'menu' ){
630
+ $continue = $hasCurrent;
631
+ }
632
+ if( $canSwitch && $this->_cmw_switch_settings( $args, 'menu', $hasCurrent, $continue ) ){
633
+ continue;
634
+ }
635
+
636
+ //PRIMARY FILTERS...
637
+ if( $continue ){
638
+ //levels...
639
+ if( $find_level ){
640
+ $continue = $cmw['level'] < count( $this->_cmw_levels );
641
+ }
642
+ //items...
643
+ if( $find_items ){
644
+ $continue = !empty( $cmw['__items'] );
645
+ }
646
+ //branch...
647
+ if( $find_branch ){
648
+ //topOfBranch gets set to -1 if it can't be determined...
649
+ $topOfBranch = $find_current
650
+ ? ( $currentItem === false ? -1 : $currentItem )
651
+ : ( isset( $this->_cmw_tree[ $cmw['branch'] ] ) ? $cmw['branch'] : -1 );
652
+ $theBranchItem = $topOfBranch;
653
+ $continue = $topOfBranch > 0;
654
+ }
655
+ } //end PRIMARIES
656
+
657
+ //check for current item and switch...
658
+ $hasCurrent = $currentItem !== false;
659
+ if( $hasCurrent ){
660
+ if( ( $find_level && $this->_cmw_tree[ $currentItem ]['level'] < $cmw['level'] ) ||
661
+ ( $find_items && !in_array( $currentItem, $cmw['__items'] ) ) ||
662
+ ( $find_branch && $topOfBranch !== $currentItem && !in_array( $topOfBranch, $this->_cmw_tree[ $currentItem ]['ancestors'] ) )
663
+ ){
664
+ $hasCurrent = false;
665
+ }
666
+ }
667
+ if( $continue && $cmw['contains_current'] == 'primary' ){
668
+ $continue = $hasCurrent;
669
+ }
670
+ if( $canSwitch && $this->_cmw_switch_settings( $args, 'primary', $hasCurrent, $continue ) ){
671
+ continue;
672
+ }
673
+
674
+ //SECONDARY FILTERS...
675
+ if( $continue ){
676
+ //right, let's set some keep flags
677
+ //for specific items, go straight in on the item id (levels and depth don't apply here)...
678
+ if( $find_items ){
679
+ foreach( $cmw['__items'] as $itemID ){
680
+ $itemID = intval( $itemID );
681
+ //avoid double counting of duplicates...
682
+ if( !empty( $itemID ) && isset( $this->_cmw_tree[ $itemID ] ) && !$this->_cmw_tree[ $itemID ]['keep'] ){
683
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
684
+ $this->_cmw_tree[0]['keepCount']++;
685
+ }
686
+ }
687
+ }
688
+ //for by-level filter, use the levels...
689
+ if( $find_level ){
690
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
691
+ if( $depth_rel_current && $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['level'] ){
692
+ $bottomLevel = $this->_cmw_tree[ $currentItem ]['level'] + $depth - 1;
693
+ }else{
694
+ $bottomLevel = $depth > 0 ? $cmw['level'] + $depth - 1 : $unlimited;
695
+ }
696
+ for( $i = $cmw['level']; isset( $this->_cmw_levels[ $i ] ) && $i <= $bottomLevel; $i++ ){
697
+ foreach( $this->_cmw_levels[ $i ] as $itemID ){
698
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
699
+ $this->_cmw_tree[0]['keepCount']++;
700
+ }
701
+ }
702
+ }
703
+ //for branch filters, run a recursive through the structure...
704
+ if( $find_branch ){
705
+ $i = $this->_cmw_tree[ $topOfBranch ]['level'];
706
+ //convert branch_start to an actual level...
707
+ $j = intval( $cmw['branch_start'] );
708
+ //convert relative to absolute (max'd against 1)...
709
+ $j = empty( $j ) ? $i : ( preg_match( '/^(\+|-)/', $cmw['branch_start'] ) > 0 ? max( 1, $i + $j ) : $j );
710
+
711
+ //do we have a current-item-no-kids fallback?...
712
+ if( $canFallback && empty( $this->_cmw_tree[ $currentItem ]['kids'] ) ){
713
+ //yes, we do...
714
+ $cmw['_walker']['fellback'] = 'to-' . $cmw['fallback'];
715
+ //is it a copout?...
716
+ if( $cmw['fallback'] == 'quit' ){
717
+ //just set the secondary start level beyond the maximum level available...
718
+ $j = count( $this->_cmw_levels );
719
+ }else{
720
+ //for current, fall back to primary start level; for parent, fall back to primary start level - 1, ensuring
721
+ //that we don't fall back further than root...
722
+ $j = $cmw['fallback'] == 'current' || $i < 2 ? $i : $i - 1;
723
+ //if fallback_depth is specified, override depth and set to depth-rel-current...
724
+ if( !empty( $cmw['fallback_depth'] ) ){
725
+ $depth = intval( $cmw['fallback_depth'] );
726
+ $depth_rel_current = true;
727
+ }
728
+ }
729
+ }
730
+
731
+ //$i is the primary level, and $j is the secondary start level
732
+ //easy result : if secondary start level > max level then there are no matches...
733
+ if( $j < count( $this->_cmw_levels ) ){
734
+ //if secondary start level is higher up the tree than the primary level, then
735
+ //reset the tob to be the current tob's ancestor at the level of $j...
736
+ if( $j < $i ){
737
+ $topOfBranch = array_slice( $this->_cmw_tree[ $topOfBranch ]['ancestors'], $j, 1 );
738
+ $topOfBranch = $topOfBranch[0];
739
+ //NB $theBranchItem is still set to the original branch item!
740
+ }
741
+
742
+ $this->_cmw_lowest = $unlimited;
743
+ $this->_cmw_highest = $j;
744
+
745
+ //$topOfBranch is a menu item id.
746
+ //if secondary start is at or above primary, and start_mode is set to "level" then it effectively means that
747
+ //the top of branch becomes the current topOfBranch *plus* all its siblings. the one qualifier for
748
+ //this is that if topOfBranch's current level is 1 (root) then the allow_all_root switch must be
749
+ //enabled in order to expand to all root items
750
+ $forceLevel = $j <= $i && $cmw['start_mode'] == 'level' && ( $j > 1 || $cmw['allow_all_root'] );
751
+
752
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
753
+ //NB for depth_rel_current to be applicable we need a current item that is :
754
+ // (a) at or below the secondary start level, and
755
+ // (b) within the (modified?) branch (ie. has topOfBranch - or possibly one of its siblings! - as an ancestor)
756
+ if( $depth_rel_current
757
+ //...this is the (a) part...
758
+ && $this->_cmw_tree[ $currentItem ]['level'] >= $this->_cmw_highest
759
+ //...this is the (b) part, and it might be complicated by the setting of $forceLevel, which would
760
+ // require the testing, not only of topOfBranch, but also topOfBranch's siblings...
761
+ && $this->_cmw_check_contains_item( $topOfBranch, $currentItem, $forceLevel ) ){
762
+ $this->_cmw_lowest = $this->_cmw_tree[ $currentItem ]['level'];
763
+ }elseif( $depth > 0 ){
764
+ $this->_cmw_lowest = $this->_cmw_highest;
765
+ }
766
+ $this->_cmw_lowest += $depth - 1;
767
+ //$this->_cmw_tree[0]['keepCount'] gets incremented during this recursive...
768
+ if( $forceLevel ){
769
+ foreach( $this->_cmw_tree[ $this->_cmw_get_parent( $topOfBranch ) ]['kids'] as $k ){
770
+ $this->_cmw_set_keep_recursive( $k );
771
+ }
772
+ }else{
773
+ $this->_cmw_set_keep_recursive( $topOfBranch );
774
+ }
775
+ //if falling back and siblings are required, add them in...
776
+ //note that root level sibling inclusion is still governed by allow_all_root!
777
+ if( !empty( $cmw['_walker']['fellback'] ) && $cmw['fallback_siblings'] && $this->_cmw_tree[0]['keepCount'] > 0
778
+ && ( $j > 1 || $cmw['allow_all_root'] ) ){
779
+ $this->_cmw_include_siblings_of( $topOfBranch );
780
+ }
781
+ }
782
+ }
783
+ $continue = $this->_cmw_tree[0]['keepCount'] > 0;
784
+ } //end SECONDARIES
785
+
786
+ //check for current item and switch...
787
+ $hasCurrent = $currentItem !== false && $this->_cmw_tree[ $currentItem ]['keep'];
788
+ if( $continue && $cmw['contains_current'] == 'secondary' ){
789
+ $continue = $hasCurrent;
790
+ }
791
+ if( $canSwitch && $this->_cmw_switch_settings( $args, 'secondary', $hasCurrent, $continue ) ){
792
+ continue;
793
+ }
794
+
795
+ //INCLUSIONS...
796
+ if( $continue ){
797
+ if( $find_branch ){
798
+ //branch ancestors, possibly with their siblings : but only if the original branch item is either being kept or
799
+ //is below the lowest visible level; ALSO, do not keep any ancestors below the lowest visible level...
800
+ if( $cmw['ancestors'] != 0 && ( $this->_cmw_tree[ $theBranchItem ]['keep'] || $this->_cmw_tree[ $theBranchItem ]['level'] > $this->_cmw_lowest ) ){
801
+ //convert relative to absolute...
802
+ $absAncestors = $cmw['ancestors'] < 0
803
+ ? max( 1, $this->_cmw_tree[ $theBranchItem ]['level'] + $cmw['ancestors'] )
804
+ : $cmw['ancestors'];
805
+ //convert relative to absolute...
806
+ $absSiblings = $cmw['ancestor_siblings'] < 0
807
+ ? max( 1, $this->_cmw_tree[ $theBranchItem ]['level'] + $cmw['ancestor_siblings'] )
808
+ : $cmw['ancestor_siblings'];
809
+ foreach( $this->_cmw_tree[ $theBranchItem ]['ancestors'] as $itemID ){
810
+ if( $itemID > 0
811
+ && $this->_cmw_tree[ $itemID ]['level'] >= $absAncestors
812
+ && $this->_cmw_tree[ $itemID ]['level'] <= $this->_cmw_lowest ){
813
+ if( !$this->_cmw_tree[ $itemID ]['keep'] ){
814
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
815
+ $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-ancestor';
816
+ $this->_cmw_tree[0]['keepCount']++;
817
+ }
818
+ //only keep ancestor siblings if the ancestor itself is being kept
819
+ if( $absSiblings > 0
820
+ && $this->_cmw_tree[ $itemID ]['level'] >= $absSiblings
821
+ && $this->_cmw_tree[ $itemID ]['keep'] ){
822
+ $this->_cmw_include_siblings_of( $itemID, 'ancestor-sibling' );
823
+ }
824
+ }
825
+ }
826
+ }
827
+ //branch siblings : only if the original branch item is being kept...
828
+ if( $cmw['siblings'] && $this->_cmw_tree[ $theBranchItem ]['keep'] ){
829
+ $this->_cmw_include_siblings_of( $theBranchItem );
830
+ }
831
+ }
832
+ //include_level (replacement/extension of include_root, as of v3.0.4)...
833
+ if( ( $fromTo = $this->_cmw_decipher_plusminus_level( $cmw['include_level'] ) ) !== false ){
834
+ while( isset( $this->_cmw_levels[ $fromTo['from'] ] ) && $fromTo['from'] < $fromTo['to'] ){
835
+ foreach( $this->_cmw_levels[ $fromTo['from'] ] as $itemID ){
836
+ if( !$this->_cmw_tree[ $itemID ]['keep'] ){
837
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
838
+ $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-level';
839
+ $this->_cmw_tree[0]['keepCount']++;
840
+ }
841
+ }
842
+ $fromTo['from']++;
843
+ }
844
+ }
845
+ } //end INCLUSIONS
846
+
847
+ //check for current item and switch...
848
+ $hasCurrent = $currentItem !== false && $this->_cmw_tree[ $currentItem ]['keep'];
849
+ if( $continue && $cmw['contains_current'] == 'inclusions' ){
850
+ $continue = $hasCurrent;
851
+ }
852
+ if( $canSwitch && $this->_cmw_switch_settings( $args, 'inclusions', $hasCurrent, $continue ) ){
853
+ continue;
854
+ }
855
+
856
+ //EXCLUSIONS...
857
+ if( $continue){
858
+ $continue = $this->_cmw_run_exclusions( $cmw );
859
+ } //end EXCLUSIONS
860
+
861
+ //check for current item and switch...
862
+ $hasCurrent = $currentItem !== false && $this->_cmw_tree[ $currentItem ]['keep'];
863
+ if( $continue && $cmw['contains_current'] == 'output' ){
864
+ $continue = $hasCurrent;
865
+ }
866
+ if( $canSwitch && $this->_cmw_switch_settings( $args, 'output', $hasCurrent, $continue ) ){
867
+ continue;
868
+ }
869
+
870
+ //check for title_from...
871
+ if( $continue ){
872
+ //v3.1.4 : this used to pass back just the title; it now passes back the element object!
873
+ //v3.1.5 : now only passes back a single element, rather than all the possibilities
874
+ $i = '';
875
+ //current has priority over branch...
876
+ if( $currentItem !== false && $cmw['title_current'] !== '' ){
877
+ $i = 'title_current';
878
+ $j = $currentItem;
879
+ }elseif( $find_branch && $theBranchItem > 0 && $cmw['title_branch'] !== '' ){
880
+ $i = 'title_branch';
881
+ $j = $theBranchItem;
882
+ }
883
+ if( $i != '' ){
884
+ $i = intval( $cmw[ $i ] );
885
+ if( $i > 0 ){
886
+ //absolute : current/branch level or above...
887
+ $i = min( $i, $this->_cmw_tree[ $j ]['level'] );
888
+ }else{
889
+ //relative (incl. 0) : convert to absolute, and cap at root (1)...
890
+ $i = max( 1, $this->_cmw_tree[ $j ]['level'] + $i );
891
+ }
892
+ if( $i < $this->_cmw_tree[ $j ]['level'] ){
893
+ $j = array_slice( $this->_cmw_tree[ $j ]['ancestors'], $i, 1 );
894
+ $j = $j[0];
895
+ }
896
+ $cmw['_walker']['get_title_from'] = $elements[ $this->_cmw_tree[ $j ]['element'][0] ];
897
+ }
898
+ }
899
+
900
+ $this->_cmw_levels = null;
901
+ $substructure = array();
902
+ if( $continue ){
903
+ //now we need to gather together all the 'keep' items from the tree;
904
+ //while doing so, we need to set up levels and kids, ready for adding classes...
905
+ foreach( $this->_cmw_tree as $k => $v ){
906
+ if( $v['keep'] ){
907
+ $substructure[ $k ] = $v;
908
+ //use kids as a has-submenu flag...
909
+ $substructure[ $k ]['kids'] = 0;
910
+ //any surviving parent (except the artificial level-0) should have submenu class set on it...
911
+ array_shift( $v['ancestors'] ); //remove the level-0
912
+ for( $i = count( $v['ancestors'] ) - 1; $i >= 0; $i-- ){
913
+ if( isset( $substructure[ $v['ancestors'][ $i ] ] ) ){
914
+ $substructure[ $v['ancestors'][ $i ] ]['kids']++;
915
+ }else{
916
+ //not a 'kept' ancestor so remove it...
917
+ array_splice( $v['ancestors'], $i, 1 );
918
+ }
919
+ }
920
+ //ancestors now only has 'kept' ancestors...
921
+ $substructure[ $k ]['level'] = count( $v['ancestors'] ) + 1;
922
+ //need to ensure that the parent_field of all the new top-level (ie. root) items is set to
923
+ //zero, otherwise the parent::walk() will assume they're orphans.
924
+ //however, we also need to check that parent_field of a child actually points to the closest
925
+ //'kept' ancestor; otherwise, given A (kept) > B (not kept) > C (kept) the parent_field of C
926
+ //would point to a non-existent B and would subsequently be considered an orphan!
927
+ if( $substructure[ $k ]['level'] == 1){
928
+ $ancestor = 0;
929
+ }else{
930
+ //set to the closest ancestor, ie. the new(?) parent...
931
+ $ancestor = array_slice( $v['ancestors'], -1, 1 );
932
+ $ancestor = $ancestor[0];
933
+ }
934
+ //take a copy of the elements item(s)...
935
+ $substructure[ $k ]['element'] = array();
936
+ foreach( $v['element'] as $i => $j ){
937
+ $elements[ $j ]->$parent_field = $ancestor;
938
+ $substructure[ $k ]['element'][] = $elements[ $j ];
939
+ }
940
+ }
941
+ }
942
+ }
943
+ $this->_cmw_tree = null;
944
+
945
+ //put substructure's elements back into $elements (remember that $elements is a 1-based array!)...
946
+ $elements = array();
947
+ $n = 1;
948
+ foreach( $substructure as $k => $v ){
949
+ $ct = count( $v['element'] ) - 1;
950
+ foreach( $v['element'] as $i => $j ){
951
+ $elements[ $n ] = $j;
952
+ //add the level class...
953
+ $elements[ $n ]->classes[] = 'cmw-level-' . $v['level'];
954
+ //add the submenu class? (only to last in group!)...
955
+ if( $v['kids'] > 0 && $i == $ct ){
956
+ $elements[ $n ]->classes[] = 'cmw-has-submenu';
957
+ }else{
958
+ //3.7 adds a menu-item-has-children class to (original) menu items that have kids : remove it as the item is now childless...
959
+ $elements[ $n ]->classes = array_diff( $elements[ $n ]->classes, array('menu-item-has-children') );
960
+ }
961
+ //add any other CMW classes...
962
+ $elements[ $n ]->classes = array_merge( $elements[ $n ]->classes, $v['classes'] );
963
+ $n++;
964
+ }
965
+ }
966
+ unset( $substructure );
967
+
968
+ //kill the run count...
969
+ $runCount--;
970
+
971
+ } //end while
972
+
973
+ return $elements;
974
+
975
+ } //end _cmw_walk()
976
+
977
+ /**
978
+ * pre-filters elements (v2.1.0)
979
+ *
980
+ * @param {object} $args Params supplied to wp_nav_menu()
981
+ * @param {array} $elements Menu items
982
+ * @return {array} Modified menu items
983
+ */
984
+ private function _cmw_walk_legacy( &$args, $elements ){
985
+
986
+ //NB : $elements is an array of objects, indexed by position within the menu (menu_order),
987
+ //starting at 1 and incrementing sequentially regardless of parentage (ie. first item is [1],
988
+ //second item is [2] whether it's at root or subordinate to first item)
989
+
990
+ $cmw =& $args->_custom_menu_wizard;
991
+
992
+ $cmw['_walker']['fellback'] = false;
993
+
994
+ $find_kids_of = $cmw['filter'] > 0;
995
+ $find_specific_items = $cmw['filter'] < 0; //v2.0.0 //v2.0.1:bug fixed (changed < 1 to < 0)
996
+ $find_current_item = $find_kids_of && empty( $cmw['filter_item'] );
997
+ $find_current_parent = $find_kids_of && $cmw['filter_item'] == -1; //v1.1.0
998
+ $find_current_root = $find_kids_of && $cmw['filter_item'] == -2; //v1.1.0
999
+ $depth_rel_current = $cmw['depth_rel_current'] && $cmw['depth'] > 0; //v2.0.0
1000
+ //these could change depending on whether a fallback comes into play (v1.1.0)
1001
+ $include_parent = $cmw['include_parent'] || $cmw['include_ancestors'];
1002
+ $include_parent_siblings = $cmw['include_parent_siblings'];
1003
+
1004
+ $id_field = $this->db_fields['id']; //eg. = 'db_id'
1005
+ $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
1006
+
1007
+ //find the current menu item while creating the tree and levels arrays...
1008
+ $currentItem = $this->_cmw_find_current_item( $elements, $cmw );
1009
+
1010
+ $allLevels = 9999;
1011
+ $startWithKidsOf = -1;
1012
+
1013
+ //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...
1014
+ $continue = true;
1015
+ if( empty( $currentItem ) && ( $find_current_item || $find_current_parent || $find_current_root || $cmw['contains_current'] ) ){
1016
+ $continue = false;
1017
+ }elseif( $find_specific_items && empty( $cmw['items'] ) ){
1018
+ $continue = false;
1019
+ }
1020
+
1021
+ // IMPORTANT : as of v2.0.0, start level has been rationalised so that it acts the same across all filters (except for specific items!).
1022
+ // Previously ...
1023
+ // start level for a show-all filter literally started at the specified level and reported all levels until depth was reached.
1024
+ // however, start level for a kids-of filter specified the level that the *immediate* kids of the selected filter had to be at
1025
+ // or below. That was consistent for a specific item, current-item and current-parent filter, but for a current-root filter what
1026
+ // it actually did was test the current item against the start level, not the current item's root ancestor! Inconsistent!
1027
+ // But regardless of the current-root filter's use of start level, there was still the inconsistency between show-all and
1028
+ // kids-of usage.
1029
+ // Now (as of v2.0.0) ...
1030
+ // start level and depth have been changed to definitively be secondary filters to the show-all & kids-of primary filter.
1031
+ // The primary filter - show-all, or a kids-of - will provide the initial set of items, and the secondary - start level & depth -
1032
+ // will further refine that set, with start level being an absolute, and depth still being relative to the first item found.
1033
+ // The sole exception to this is when Depth Relative to Current Menu Item is set, which modifies the calculation of depth (only)
1034
+ // 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
1035
+ // below start level).
1036
+ // 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,
1037
+ // would fail to return any items because the immediate kids (at level 3) were outside the start level. Now, the returned items
1038
+ // will begin with the grand-kids (ie. those at level 4).
1039
+ // Note that neither start level nor depth are applicable to a specific items filter (also new at v2.0.0)!
1040
+
1041
+ //the kids-of filters...
1042
+ if( $continue && $find_kids_of ){
1043
+ //specific item...
1044
+ if( $cmw['filter_item'] > 0 && isset( $this->_cmw_tree[ $cmw['filter_item'] ] ) && !empty( $this->_cmw_tree[ $cmw['filter_item'] ]['kids'] ) ){
1045
+ $startWithKidsOf = $cmw['filter_item'];
1046
+ }
1047
+ if( $find_current_item ){
1048
+ if( !empty( $this->_cmw_tree[ $currentItem ]['kids'] ) ){
1049
+ $startWithKidsOf = $currentItem;
1050
+ }elseif( $cmw['fallback_no_children'] ){
1051
+ //no kids, and fallback to current parent is set...
1052
+ //note that there is no "double fallback", so current parent "can" be the artifical zero element (level-0) *if*
1053
+ // the current item is a singleton( ie. no kids & no ancestors)!
1054
+ $ancestor = array_slice( $this->_cmw_tree[ $currentItem ]['ancestors'], -1, 1 );
1055
+ $startWithKidsOf = $ancestor[0]; //can be zero!
1056
+ $include_parent = $include_parent || $cmw['fallback_nc_include_parent'];
1057
+ $include_parent_siblings = $include_parent_siblings || $cmw['fallback_nc_include_parent_siblings'];
1058
+ $cmw['_walker']['fellback'] = 'to-parent';
1059
+ }
1060
+ }elseif( $find_current_parent || $find_current_root ){
1061
+ //as of v2.0.0 the fallback to current item - for current menu items at the top level - is deprecated, but
1062
+ //retained for a while to maintain backward compatibility
1063
+ //if no parent : fall back to current item (if set)...
1064
+ if( $this->_cmw_tree[ $currentItem ]['level'] == 1 && $cmw['fallback_no_ancestor'] ){
1065
+ $startWithKidsOf = $currentItem;
1066
+ $include_parent = $include_parent || $cmw['fallback_include_parent'];
1067
+ $include_parent_siblings = $include_parent_siblings || $cmw['fallback_include_parent_siblings'];
1068
+ $cmw['_walker']['fellback'] = 'to-current';
1069
+ }else{
1070
+ //as of v2.0.0, the artificial level-0 counts as parent of a top-level current menu item...
1071
+ if( $find_current_parent ){
1072
+ $ancestor = -1;
1073
+ }elseif( $this->_cmw_tree[ $currentItem ]['level'] > 1 ){
1074
+ $ancestor = 1;
1075
+ }else{
1076
+ $ancestor = 0;
1077
+ }
1078
+ $ancestor = array_slice( $this->_cmw_tree[ $currentItem ]['ancestors'], $ancestor, 1 );
1079
+ if( !empty( $ancestor ) ){
1080
+ $startWithKidsOf = $ancestor[0]; //as of v2.0.0, this can now be zero!
1081
+ }
1082
+ }
1083
+ }
1084
+ }
1085
+
1086
+ if( $continue ){
1087
+ //right, let's set the keep flags
1088
+ //for specific items, go straight in on the item id (start level and depth do not apply here)...
1089
+ if( $find_specific_items ){
1090
+ foreach( preg_split('/[,\s]+/', $cmw['items'] ) as $itemID ){
1091
+ if( isset( $this->_cmw_tree[ $itemID ] ) ){
1092
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
1093
+ $this->_cmw_tree[0]['keepCount']++;
1094
+ }
1095
+ }
1096
+ //for show-all filter, just use the levels...
1097
+ }elseif( !$find_kids_of ){
1098
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
1099
+ if( $depth_rel_current && !empty( $currentItem ) && $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['start_level'] ){
1100
+ $bottomLevel = $this->_cmw_tree[ $currentItem ]['level'] + $cmw['depth'] - 1;
1101
+ }else{
1102
+ $bottomLevel = $cmw['depth'] > 0 ? $cmw['start_level'] + $cmw['depth'] - 1 : $allLevels;
1103
+ }
1104
+ for( $i = $cmw['start_level']; isset( $this->_cmw_levels[ $i ] ) && $i <= $bottomLevel; $i++ ){
1105
+ foreach( $this->_cmw_levels[ $i ] as $itemID ){
1106
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
1107
+ $this->_cmw_tree[0]['keepCount']++;
1108
+ }
1109
+ }
1110
+ //for kids-of filters, run a recursive through the structure's kids...
1111
+ }elseif( $startWithKidsOf > -1 ){
1112
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
1113
+ //NB the in_array() of ancestors prevents depth_rel_current when startWithKidsOf == currentItem
1114
+ if( $depth_rel_current && !empty( $currentItem ) && $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['start_level']
1115
+ && in_array( $startWithKidsOf, $this->_cmw_tree[ $currentItem ]['ancestors'] ) ){
1116
+ $bottomLevel = $this->_cmw_tree[ $currentItem ]['level'] - 1 + $cmw['depth'];
1117
+ }else{
1118
+ $bottomLevel = $cmw['depth'] > 0
1119
+ ? max( $this->_cmw_tree[ $startWithKidsOf ]['level'] + $cmw['depth'], $cmw['start_level'] + $cmw['depth'] - 1 )
1120
+ : $allLevels;
1121
+ }
1122
+ //$this->_cmw_tree[0]['keepCount'] gets incremented in this recursive method...
1123
+ $this->_cmw_legacy_set_keep_kids( $startWithKidsOf, $cmw['start_level'], $bottomLevel );
1124
+ }
1125
+
1126
+ if( $this->_cmw_tree[0]['keepCount'] > 0 ){
1127
+ //we have some items! we now may need to set some more keep flags, depending on the include settings...
1128
+
1129
+ //do we need to include parent, parent siblings, and/or ancestors?...
1130
+ //NB these are not restricted by start_level!
1131
+ if( $find_kids_of && $startWithKidsOf > 0 ){
1132
+ if( $include_parent ){
1133
+ $this->_cmw_tree[ $startWithKidsOf ]['keep'] = true;
1134
+ $this->_cmw_tree[ $startWithKidsOf ]['classes'][] = 'cmw-the-included-parent';
1135
+ }
1136
+ if( $include_parent_siblings ){
1137
+ $ancestor = array_slice( $this->_cmw_tree[ $startWithKidsOf ]['ancestors'], -1, 1);
1138
+ foreach($this->_cmw_tree[ $ancestor[0] ]['kids'] as $itemID ){
1139
+ //may have already been kept by include_parent...
1140
+ if( !$this->_cmw_tree[ $itemID ]['keep'] ){
1141
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
1142
+ $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-parent-sibling';
1143
+ }
1144
+ }
1145
+ }
1146
+ if( $cmw['include_ancestors'] ){
1147
+ foreach( $this->_cmw_tree[ $startWithKidsOf ]['ancestors'] as $itemID ){
1148
+ if( $itemID > 0 && !$this->_cmw_tree[ $itemID ]['keep'] ){
1149
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
1150
+ $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-parent-ancestor';
1151
+ }
1152
+ }
1153
+ }
1154
+ }
1155
+ }
1156
+ }
1157
+
1158
+ //check that (a) we have items, and (b) if we must have current menu item, we've got it...
1159
+ $continue = $this->_cmw_tree[0]['keepCount'] > 0 && ( !$cmw['contains_current'] || $this->_cmw_tree[ $currentItem ]['keep'] );
1160
+ //check for title_from...
1161
+ if( $continue ){
1162
+ //might we want the parent's title as the widget title?...
1163
+ if( $find_kids_of && $cmw['title_from_parent'] && $startWithKidsOf > 0 ){
1164
+ $cmw['_walker']['parent_title'] = apply_filters(
1165
+ 'the_title',
1166
+ $elements[ $this->_cmw_tree[ $startWithKidsOf ]['element'][0] ]->title,
1167
+ $elements[ $this->_cmw_tree[ $startWithKidsOf ]['element'][0] ]->ID
1168
+ );
1169
+ }
1170
+ //might we want the current item's title as the widget title?...
1171
+ if( !empty( $currentItem ) && $cmw['title_from_current'] ){
1172
+ $cmw['_walker']['current_title'] = apply_filters(
1173
+ 'the_title',
1174
+ $elements[ $this->_cmw_tree[ $currentItem ]['element'][0] ]->title,
1175
+ $elements[ $this->_cmw_tree[ $currentItem ]['element'][0] ]->ID
1176
+ );
1177
+ }
1178
+ }
1179
+
1180
+ $this->_cmw_levels = null;
1181
+ $substructure = array();
1182
+ if( $continue ){
1183
+ //now we need to gather together all the 'keep' items from the tree;
1184
+ //while doing so, we need to set up levels and kids, ready for adding classes...
1185
+ foreach( $this->_cmw_tree as $k => $v ){
1186
+ if( $k > 0 && $v['keep'] ){
1187
+ $substructure[ $k ] = $v;
1188
+ //use kids as a has-submenu flag...
1189
+ $substructure[ $k ]['kids'] = 0;
1190
+ //any surviving parent (except the artificial level-0) should have submenu class set on it...
1191
+ array_shift( $v['ancestors'] ); //remove the level-0
1192
+ for( $i = count( $v['ancestors'] ) - 1; $i >= 0; $i-- ){
1193
+ if( isset( $substructure[ $v['ancestors'][ $i ] ] ) ){
1194
+ $substructure[ $v['ancestors'][ $i ] ]['kids']++;
1195
+ }else{
1196
+ //not a 'kept' ancestor so remove it...
1197
+ array_splice( $v['ancestors'], $i, 1 );
1198
+ }
1199
+ }
1200
+ //ancestors now only has 'kept' ancestors...
1201
+ $substructure[ $k ]['level'] = count( $v['ancestors'] ) + 1;
1202
+ //need to ensure that the parent_field of all the new top-level (ie. root) items is set to
1203
+ //zero, otherwise the parent::walk() will assume they're orphans.
1204
+ //however, we also need to check that parent_field of a child actually points to the closest
1205
+ //'kept' ancestor; otherwise, given A (kept) > B (not kept) > C (kept) the parent_field of C
1206
+ //would point to a non-existent B and would subsequently be considered an orphan!
1207
+ if( $substructure[ $k ]['level'] == 1){
1208
+ $ancestor = 0;
1209
+ }else{
1210
+ //set to the closest ancestor, ie. the new(?) parent...
1211
+ $ancestor = array_slice( $v['ancestors'], -1, 1 );
1212
+ $ancestor = $ancestor[0];
1213
+ }
1214
+ //take a copy of the elements item(s)...
1215
+ $substructure[ $k ]['element'] = array();
1216
+ foreach( $v['element'] as $i => $j ){
1217
+ $elements[ $j ]->$parent_field = $ancestor;
1218
+ $substructure[ $k ]['element'][] = $elements[ $j ];
1219
+ }
1220
+ }
1221
+ }
1222
+ }
1223
+ $this->_cmw_tree = null;
1224
+
1225
+ //put substructure's elements back into $elements (remember that $elements is a 1-based array!)...
1226
+ $elements = array();
1227
+ $n = 1;
1228
+ foreach( $substructure as $k => $v ){
1229
+ $ct = count( $v['element'] ) - 1;
1230
+ foreach( $v['element'] as $i => $j ){
1231
+ $elements[ $n ] = $j;
1232
+ //add the level class...
1233
+ $elements[ $n ]->classes[] = 'cmw-level-' . $v['level'];
1234
+ //add the submenu class? (only to last in group!)...
1235
+ if( $v['kids'] > 0 && $i == $ct ){
1236
+ $elements[ $n ]->classes[] = 'cmw-has-submenu';
1237
+ }elseif( $v['kids'] == 0 ){
1238
+ //3.7 adds a menu-item-has-children class to (original) menu items that have kids : remove it as the item is now childless...
1239
+ $elements[ $n ]->classes = array_diff( $elements[ $n ]->classes, array('menu-item-has-children') );
1240
+ }
1241
+ //add any other CMW classes...
1242
+ $elements[ $n ]->classes = array_merge( $elements[ $n ]->classes, $v['classes'] );
1243
+ $n++;
1244
+ }
1245
+ }
1246
+ unset( $substructure );
1247
+
1248
+ return $elements;
1249
+
1250
+ } //end _cmw_walk_legacy()
1251
 
1252
  } //end Custom_Menu_Wizard_Walker class
1253
  ?>
include/class.widget.php CHANGED
@@ -6,2307 +6,2377 @@
6
  */
7
  class Custom_Menu_Wizard_Widget extends WP_Widget {
8
 
9
- /**
10
- * class constructor
11
- */
12
- public function __construct() {
13
-
14
- parent::__construct(
15
- 'custom-menu-wizard',
16
- 'Custom Menu Wizard',
17
- array(
18
- 'classname' => 'widget_custom_menu_wizard',
19
- 'description' => __('Add a custom menu, or part of one, as a widget'),
20
- 'customizer_support' => true
21
- )
22
- );
23
- $this->_cmw_legacy_warnreadmore = 'http://wordpress.org/plugins/' . $this->id_base . '/changelog/';
24
- //accessibility mode doesn't necessarily mean that javascript is disabled, but if javascript *is* disabled
25
- //then accessibility mode *will* be on...
26
- $this->_cmw_accessibility = isset( $_GET['editwidget'] ) && $_GET['editwidget'];
27
- $this->_cmw_hash_ct = 0;
28
-
29
- } //end __construct()
30
-
31
- /**
32
- * produces the backend admin form(s)
33
- *
34
- * @param array $instance Widget settings
35
- */
36
- public function form( $instance ) {
37
-
38
- //raised June 2014 : problem...
39
- //using the widget_form_callback filter (as Widget Title Links plugin does, which raised the issue) it is perfectly
40
- //possible for $instance to be non-empty - with any number of properties - for a new widget! The widget_form_callback
41
- //filter allows any other plugin to add fields to $instance *before* the actual widget itself gets a chance to (and
42
- //returning false from that filter will prevent the widget ever being called, but not relevant here).
43
- //(ref: WP_Widget::form_callback() in wp-includes/widgets.php)
44
- //this means that I can't rely on a !empty($instance) test being indicative of an existing widget (because it could be
45
- //a new widget but filtered with widget_form_callback).
46
- //So, I have changed the "legacy" test from
47
- // if( !empty( $instance ) && empty( $instance['cmwv'] ) ){
48
- //to
49
- // if( is_numeric( $this->number ) && $this->number > 0 && empty( $instance['cmwv'] ) ){
50
- //(checking for $this->number > 0 is probably overkill but it doesn't hurt)
51
- //Note that this could still be circumvented by some other plugin using the widget_form_callback filter to set 'cmwv',
52
- //but I can't do anything about that!
53
-
54
- //only call the legacy form method if the widget has a number (ie. this instance has been saved, could be either active
55
- // or inactive) and it does *not* have a version number ('cmwv') set in $instance...
56
- if( is_numeric( $this->number ) && $this->number > 0 && empty( $instance['cmwv'] ) ){
57
- $this->cmw_legacy_form( $instance );
58
- return;
59
- }
60
-
61
- //sanitize $instance...
62
- $instance = self::cmw_settings( $instance, array(), __FUNCTION__ );
63
-
64
- //if no populated menus exist, suggest the user go create one...
65
- if( ( $menus = $this->cmw_scan_menus( $instance['menu'], $instance['branch'] ) ) === false ){
66
  ?>
67
  <p class="widget-<?php echo $this->id_base; ?>-no-menus">
68
- <em><?php printf( __('No populated menus have been created yet! <a href="%s">Create one...</a>'), admin_url('nav-menus.php') ); ?></em>
69
- <input id="<?php echo $this->get_field_id('cmwv'); ?>" name="<?php echo $this->get_field_name('cmwv'); ?>"
70
- type="hidden" value="<?php echo Custom_Menu_Wizard_Plugin::$version; ?>" />
71
- <?php foreach( array('filters', 'fallbacks', 'output', 'container', 'classes', 'links') as $v ){ ?>
72
- <input id="<?php echo $this->get_field_id("fs_$v"); ?>" name="<?php echo $this->get_field_name("fs_$v"); ?>"
73
- type="hidden" value="<?php echo $instance["fs_$v"] ? '1' : '0' ?>" />
74
- <?php } ?>
75
  </p>
76
  <?php
77
- //all done : quit...
78
- return;
79
- }
80
-
81
- //create the OPTIONs for the relative & absolute optgroups in the branch level select...
82
- $absGroup = array();
83
- $relGroup = array();
84
- if( empty( $instance['branch_start'] ) ){
85
- $branchLevel = '';
86
- }else{
87
- $branchLevel = $instance['branch_start'];
88
- $i = substr( $branchLevel, 0, 1 );
89
- //is it currently set relative?...
90
- if( $i == '+' || $i == '-' ){
91
- //if we only have 1 level then set to branch item...
92
- if( $menus['selectedLevels'] < 2 ){
93
- $branchLevel = '';
94
- //otherwise, limit the 'relativeness' to 1 less than the number of levels
95
- //available (ie. 5 levels available gives a range -4 thru to +4)...
96
- }elseif( abs( intval( $branchLevel ) ) > $menus['selectedLevels'] - 1 ){
97
- $branchLevel = $i . ($menus['selectedLevels'] - 1);
98
- }
99
- //max an absolute against the number of levels available...
100
- }elseif( intval( $branchLevel ) > $menus['selectedLevels'] ){
101
- $branchLevel = $menus['selectedLevels'];
102
- }
103
- }
104
- //start with the middle option of the relatives (the level of the branch item)...
105
- $relGroup[] = '<option value="" ' . selected( $branchLevel, '', false ) . '>' . __('the Branch') . '</option>';
106
- //now do the absolutes and relatives...
107
- for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){
108
- //topmost of the absolutes gets ' (root)' appended to its descriptor...
109
- $t = $i == 1 ? "$i (" . __('root') . ')' : $i;
110
- //append to the absolutes...
111
- $absGroup[] = '<option value="' . $i . '" ' . selected( $branchLevel, "$i", false ) . '>' . $t . '</option>';
112
- //for anything LESS THAN the number of levels...
113
- if( $i < $menus['selectedLevels'] ){
114
- //immediately above the branch item gets ' (parent)' appended to its descriptor...
115
- $t = $i == 1 ? "-$i (" . __('parent') . ')' : "-$i";
116
- //prepend to the relatives...
117
- array_unshift( $relGroup, '<option value="-' . $i . '" ' . selected( $branchLevel, "-$i", false ) . '>' . $t . '</option>' );
118
- //immediately below the branch item gets ' (children)' appended to its descriptor...
119
- $t = $i == 1 ? "+$i (" . __('children') . ')' : "+$i";
120
- //append to the relatives...
121
- array_push( $relGroup, '<option value="+' . $i . '" ' . selected( $branchLevel, "+$i", false ) . '>' . $t . '</option>' );
122
- }
123
- }
124
-
125
- //set up some simple booleans for use at the disableif___ classes...
126
- $isByItems = $instance['filter'] == 'items'; // disableif-ss (IS Items filter)
127
- $isUnlimitedDepth = $isByItems || empty( $instance['depth'] ); // disableif-ud (IS unlimited depth)
128
- $isNotByBranch = $instance['filter'] != 'branch'; // disableifnot-br (is NOT Branch filter)
129
- $isNotBranchCurrentItem = $isNotByBranch || !empty( $instance['branch'] ); // disableifnot-br-ci (is NOT "Branch:Current Item")
130
- $isNotFallbackParentCurrent = $isNotBranchCurrentItem || !in_array( $instance['fallback'], array('parent', 'current') ); //disableifnot-fb-pc (is NOT set to fall back to parent or current)
131
- $isNotSwitchable = empty( $instance['switch_if'] ) || empty( $instance['switch_at'] ); // disableifnot-sw (missing either the condition of the processing stage)
132
-
133
- //NB the 'onchange' wrapper holds any text required by the "assist"
134
  ?>
135
  <div id="<?php echo $this->get_field_id('onchange'); ?>"
136
- class="widget-<?php echo $this->id_base; ?>-onchange<?php echo $this->cmw_wp_version('3.8') ? ' cmw-pre-wp-v38' : ''; ?>"
137
- data-cmw-v36plus='<?php echo $this->cmw_wp_version('3.6', true) ? 'true' : 'false'; ?>'
138
- data-cmw-dialog-prompt='<?php _e('Click an item to toggle &quot;Current Menu Item&quot;'); ?>'
139
- data-cmw-dialog-output='<?php _e('Basic Output'); ?>'
140
- data-cmw-dialog-alternative='<?php _e('Alternative settings'); ?>'
141
- data-cmw-dialog-fallback='<?php _e('Fallback invoked'); ?>'
142
- data-cmw-dialog-inclusions='<?php _e('Inclusions : 0'); ?>'
143
- data-cmw-dialog-exclusions='<?php _e('Exclusions : 0'); ?>'
144
- data-cmw-dialog-set-current='<?php _e('No Current Item!'); ?>'
145
- data-cmw-dialog-shortcodes='<?php _e('Find posts/pages containing a CMW shortcode'); ?>'
146
- data-cmw-dialog-untitled='<?php _e('untitled'); ?>'
147
- data-cmw-dialog-fixed='<?php _e('fixed'); ?>'
148
- data-cmw-dialog-nonce='<?php echo wp_create_nonce( 'cmw-find-shortcodes' ); ?>'
149
- data-cmw-dialog-version='<?php echo Custom_Menu_Wizard_Plugin::$version; ?>'
150
- data-cmw-dialog-id='<?php echo $this->get_field_id('dialog'); ?>'>
151
 
152
  <?php
153
- /**
154
- * permanently visible section : Title (with Hide) and Menu
155
- */
156
  ?>
157
- <p>
158
- <input id="<?php echo $this->get_field_id('cmwv'); ?>" name="<?php echo $this->get_field_name('cmwv'); ?>"
159
- type="hidden" value="<?php echo Custom_Menu_Wizard_Plugin::$version; ?>" />
160
- <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:') ?></label>
161
- <?php $this->cmw_formfield_checkbox( $instance, 'hide_title',
162
- array(
163
- 'label' => __('Hide'),
164
- 'lclass' => 'alignright'
165
- ) ); ?>
166
- <?php $this->cmw_formfield_textbox( $instance, 'title',
167
- array(
168
- 'fclass' => 'widefat cmw-widget-title'
169
- ) ); ?>
170
- </p>
171
-
172
- <p>
173
- <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
174
- <select id="<?php echo $this->get_field_id('menu'); ?>"
175
- class="cmw-select-menu cmw-listen" name="<?php echo $this->get_field_name('menu'); ?>">
176
- <?php echo $menus['names']; ?>
177
- </select>
178
- </p>
179
 
180
  <?php
181
- /**
182
- * start collapsible section : 'Filter'
183
- */
184
- $this->cmw_open_a_field_section( $instance, __('Filters'), 'fs_filters' ); ?>
185
-
186
- <div>
187
- <?php $this->cmw_assist_link(); ?>
188
- <strong><?php _e('Primary Filter'); ?></strong>
189
-
190
- <div class="cmw-indented">
191
- <label class="cmw-verticalalign-baseline">
192
- <input id="<?php echo $this->get_field_id('filter'); ?>_0" class="cmw-bylevel cmw-listen"
193
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('filter'); ?>"
194
- type="radio" value="" <?php checked( $instance['filter'], '' ); ?>
195
- /><?php _e('Level:'); ?></label>
196
- <select id="<?php echo $this->get_field_id('level'); ?>" class="cmw-level cmw-set-levels cmw-listen"
197
- <?php $this->cmw_disableif(); ?> data-cmw-set-levels="0"
198
- name="<?php echo $this->get_field_name('level'); ?>">
199
- <?php for( $i = 1, $j = $instance['level'] > $menus['selectedLevels'] ? 1 : $instance['level']; $i <= $menus['selectedLevels']; $i++ ){ ?>
200
- <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php echo $i > 1 ? $i : $i . __(' (root)'); ?></option>
201
- <?php } ?>
202
- </select>
203
- </div>
204
-
205
- <div class="cmw-indented">
206
- <label class="cmw-verticalalign-baseline">
207
- <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="cmw-bybranch cmw-listen"
208
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('filter'); ?>"
209
- type="radio" value="branch" <?php checked( $instance['filter'], 'branch' ); ?>
210
- /><?php _e('Branch:'); ?></label>
211
- <select id="<?php echo $this->get_field_id('branch'); ?>" class="cmw-branches cmw-assist-items cmw-listen"
212
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('branch'); ?>">
213
- <option value="0" <?php selected( $instance['branch'], 0 ); ?>><?php _e('Current Item'); ?></option>
214
- <?php echo $menus['selectedOptgroup']; ?>
215
- </select>
216
- <select id="<?php echo $this->get_field_id('branch_ignore'); ?>" class='cmw-off-the-page' disabled="disabled"
217
- name="<?php echo $this->get_field_name('branch_ignore'); ?>">
218
- <?php echo $menus['optgroups']; ?>
219
- </select>
220
- </div>
221
-
222
- <div class="cmw-indented">
223
- <label class="cmw-verticalalign-baseline">
224
- <input id="<?php echo $this->get_field_id('filter'); ?>_2" class="cmw-byitems cmw-listen"
225
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('filter'); ?>"
226
- type="radio" value="items" <?php checked( $instance['filter'], 'items' ); ?>
227
- /><?php _e('Items:'); ?></label>
228
- <?php $this->cmw_formfield_textbox( $instance, 'items',
229
- array(
230
- 'fclass' => 'cmw-maxwidth-twothirds cmw-setitems cmw-listen'
231
- ) ); ?>
232
- </div>
233
- </div>
234
-
235
- <div class="cmw-disableif-ss<?php $this->cmw_disableif( 'push', $isByItems ); ?>">
236
- <?php $this->cmw_assist_link(); ?>
237
- <strong><?php _e('Secondary Filter'); ?></strong>
238
-
239
- <div class="cmw-indented">
240
- <label class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>"><?php _e('Starting at:'); ?>
241
- <select id="<?php echo $this->get_field_id('branch_start'); ?>" class="cmw-branch-start cmw-listen"
242
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('branch_start'); ?>">
243
- <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-children="<?php _e('children'); ?>"
244
- data-cmw-text-parent="<?php _e('parent'); ?>">
245
- <?php echo implode( '', $relGroup ); ?>
246
- </optgroup>
247
- <optgroup label="<?php _e('absolute...'); ?>">
248
- <?php echo implode( '', $absGroup ); ?>
249
- </optgroup>
250
- </select></label><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
251
-
252
- <br />
253
- <span class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>">
254
- <label class="cmw-followed-by">
255
- <input id="<?php echo $this->get_field_id('start_mode'); ?>_0"
256
- name="<?php echo $this->get_field_name('start_mode'); ?>"
257
- <?php $this->cmw_disableif(); ?> type="radio" value="" <?php checked( $instance['start_mode'] !== 'level' ); ?>
258
- /><?php printf( __('Item %1$s(if possible)%2$s'), '<small>', '</small>' ); ?></label>
259
-
260
- <label class="cmw-followed-by cmw-whitespace-nowrap">
261
- <input id="<?php echo $this->get_field_id('start_mode'); ?>_1" name="<?php echo $this->get_field_name('start_mode'); ?>"
262
- <?php $this->cmw_disableif(); ?> type="radio" value="level" <?php checked( $instance['start_mode'] === 'level' ); ?>
263
- /><?php _e('Level'); ?></label>
264
-
265
- <?php $this->cmw_formfield_checkbox( $instance, 'allow_all_root',
266
- array(
267
- 'label' => __('Allow all Root Items'),
268
- 'lclass' => 'cmw-whitespace-nowrap'
269
- ) ); ?>
270
- </span><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
271
- </div>
272
-
273
- <div class="cmw-indented">
274
- <label class="cmw-followed-by"><?php _e('For Depth:'); ?>
275
- <select id="<?php echo $this->get_field_id('depth'); ?>" data-cmw-text-levels="<?php _e(' levels'); ?>"
276
- data-cmw-set-levels="1" <?php $this->cmw_disableif(); ?>
277
- class="cmw-depth cmw-set-levels cmw-listen" name="<?php echo $this->get_field_name('depth'); ?>">
278
- <option value="0" <?php selected( $instance['depth'] > $menus['selectedLevels'] ? 0 : $instance['depth'], 0 ); ?>><?php _e('unlimited'); ?></option>
279
- <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
280
- <option value="<?php echo $i; ?>" <?php selected( $instance['depth'], $i ); ?>><?php printf( _n('%d level', '%d levels', $i), $i ); ?></option>
281
- <?php } ?>
282
- </select></label>
283
-
284
-
285
- <?php $this->cmw_formfield_checkbox( $instance, 'depth_rel_current',
286
- array(
287
- 'label' => __('Relative to Current Item'),
288
- 'lclass' => 'cmw-disableif-ud cmw-whitespace-nowrap',
289
- 'disableif' => $isUnlimitedDepth
290
- ) ); ?>
291
- </div>
292
- </div><!-- end .cmw-disableif-ss --><?php $this->cmw_disableif( 'pop' ); ?>
293
-
294
- <div>
295
- <?php $this->cmw_assist_link(); ?>
296
- <strong><?php _e('Inclusions'); ?></strong>
297
-
298
- <div class="cmw-indented">
299
- <label class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>"><?php _e('Branch Ancestors:'); ?>
300
- <select id="<?php echo $this->get_field_id('ancestors'); ?>" class="cmw-ancestors cmw-listen"
301
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('ancestors'); ?>"
302
- data-cmw-text-tolevel="<?php _e('to level'); ?>">
303
- <option value="0" <?php selected( $j, 0 ); ?>>&nbsp;</option>
304
- <?php
305
- $j = $instance['ancestors'];
306
- $j = max( min( $j, $menus['selectedLevels'] - 1 ), 1 - $menus['selectedLevels'] ); ?>
307
- <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-for-option=" <?php _e('%d levels'); ?>">
308
- <option value="-1" <?php selected( $j, -1 ); ?>><?php printf( __('%d level (parent)'), -1 ); ?></option>
309
- <?php for( $i = -2; $i > 0 - $menus['selectedLevels']; $i-- ){ ?>
310
- <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('%d levels'), $i ); ?></option>
311
- <?php } ?>
312
- </optgroup>
313
- <optgroup label="<?php _e('absolute...'); ?>" data-cmw-text-for-option="<?php _e('to level %d'); ?> ">
314
- <option value="1" <?php selected( $j, 1 ); ?>><?php printf( __('to level %d (root)'), 1 ); ?></option>
315
- <?php for( $i = 2; $i < $menus['selectedLevels']; $i++ ){ ?>
316
- <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('to level %d'), $i ); ?></option>
317
- <?php } ?>
318
- </optgroup>
319
- </select></label><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
320
-
321
- <br />
322
- <span class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>">
323
- <label><?php _e('... with Siblings:'); ?>
324
- <select id="<?php echo $this->get_field_id('ancestor_siblings'); ?>" class="cmw-ancestor-siblings cmw-listen"
325
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('ancestor_siblings'); ?>">
326
- <option value="0" <?php selected( $j, 0 ); ?>>&nbsp;</option>
327
- <?php
328
- $j = $instance['ancestor_siblings'];
329
- $j = max( min( $j, $menus['selectedLevels'] - 1 ), 1 - $menus['selectedLevels'] ); ?>
330
- <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-for-option=" <?php _e('%d levels'); ?>">
331
- <option value="-1" <?php selected( $j, -1 ); ?>><?php printf( __('%d level (parent)'), -1 ); ?></option>
332
- <?php for( $i = -2; $i > 0 - $menus['selectedLevels']; $i-- ){ ?>
333
- <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('%d levels'), $i ); ?></option>
334
- <?php } ?>
335
- </optgroup>
336
- <optgroup label="<?php _e('absolute...'); ?>" data-cmw-text-for-option="<?php _e('to level %d'); ?> ">
337
- <option value="1" <?php selected( $j, 1 ); ?>><?php printf( __('to level %d (root)'), 1 ); ?></option>
338
- <?php for( $i = 2; $i < $menus['selectedLevels']; $i++ ){ ?>
339
- <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('to level %d'), $i ); ?></option>
340
- <?php } ?>
341
- </optgroup>
342
- </select></label>
343
- </span><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
344
- </div>
345
-
346
- <?php $this->cmw_formfield_checkbox( $instance, 'siblings',
347
- array(
348
- 'label' => __('Branch Siblings'),
349
- 'lclass' => 'cmw-disableifnot-br',
350
- 'disableif' => $isNotByBranch
351
- ) ); ?>
352
-
353
- <div class="cmw-indented">
354
- <label><?php _e('Level:'); ?>
355
- <select id="<?php echo $this->get_field_id('include_level'); ?>" class="cmw-include-level"
356
- name="<?php echo $this->get_field_name('include_level'); ?>">
357
- <?php $j = intval($instance['include_level']) > $menus['selectedLevels'] ? '' : $instance['include_level']; ?>
358
- <option value="" <?php selected( $j, '' ); ?>>&nbsp;</option>
359
- <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
360
- <option value="<?php echo $i; ?>" <?php selected( $j, "$i" ); ?>><?php echo $i; ?></option>
361
- <option value="<?php echo $i . '-'; ?>" <?php selected( $j, $i . '-' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and above'); ?></option>
362
- <option value="<?php echo $i . '+'; ?>" <?php selected( $j, $i . '+' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and below'); ?></option>
363
- <?php } ?>
364
- </select></label>
365
- </div>
366
- </div>
367
-
368
- <div>
369
- <?php $this->cmw_assist_link(); ?>
370
- <strong><?php _e('Exclusions'); ?></strong>
371
-
372
- <div class="cmw-indented">
373
- <?php $this->cmw_formfield_textbox( $instance, 'exclude',
374
- array(
375
- 'label' => __('Item Ids:'),
376
- 'fclass' => 'cmw-maxwidth-twothirds cmw-exclusions'
377
- ) ); ?>
378
- </div>
379
-
380
- <div class="cmw-indented">
381
- <label><?php _e('Level:'); ?>
382
- <select id="<?php echo $this->get_field_id('exclude_level'); ?>" class="cmw-exclude-level"
383
- name="<?php echo $this->get_field_name('exclude_level'); ?>">
384
- <?php $j = intval($instance['exclude_level']) > $menus['selectedLevels'] ? '' : $instance['exclude_level']; ?>
385
- <option value="" <?php selected( $j, '' ); ?>>&nbsp;</option>
386
- <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
387
- <option value="<?php echo $i; ?>" <?php selected( $j, "$i" ); ?>><?php echo $i; ?></option>
388
- <option value="<?php echo $i . '-'; ?>" <?php selected( $j, $i . '-' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and above'); ?></option>
389
- <option value="<?php echo $i . '+'; ?>" <?php selected( $j, $i . '+' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and below'); ?></option>
390
- <?php } ?>
391
- </select></label>
392
- </div>
393
- </div>
394
-
395
- <div>
396
- <?php $this->cmw_assist_link(); ?>
397
- <strong><?php _e('Qualifier'); ?></strong>
398
- <br /><label for="<?php echo $this->get_field_id('contains_current'); ?>"><?php _e('Current Item is in:'); ?></label>
399
- <select id="<?php echo $this->get_field_id('contains_current'); ?>"
400
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('contains_current'); ?>">
401
- <option value="" <?php selected( $instance['contains_current'], '' ); ?>>&nbsp;</option>
402
- <option value="menu" <?php selected( $instance['contains_current'], 'menu' ); ?>><?php echo _e('Menu'); ?></option>
403
- <option value="primary" <?php selected( $instance['contains_current'], 'primary' ); ?>><?php echo _e('Primary Filter'); ?></option>
404
- <option value="secondary" <?php selected( $instance['contains_current'], 'secondary' ); ?>><?php echo _e('Secondary Filter'); ?></option>
405
- <option value="inclusions" <?php selected( $instance['contains_current'], 'inclusions' ); ?>><?php echo _e('Inclusions'); ?></option>
406
- <option value="output" <?php selected( $instance['contains_current'], 'output' ); ?>><?php echo _e('Final Output'); ?></option>
407
- </select>
408
- </div>
409
-
410
- <?php $this->cmw_close_a_field_section(); ?>
411
 
412
  <?php
413
- /**
414
- * v1.2.0 start collapsible section : 'Fallbacks'
415
- */
416
- $this->cmw_open_a_field_section( $instance, __('Fallbacks'), 'fs_fallbacks' ); ?>
417
-
418
- <div class="cmw-disableifnot-br-ci<?php $this->cmw_disableif( 'push', $isNotBranchCurrentItem ); ?>">
419
- <?php $this->cmw_assist_link(); ?>
420
-
421
- <div class="cmw-indented">
422
- <label for="<?php echo $this->get_field_id('fallback'); ?>"><?php _e('If Current Item has no children:'); ?></label>
423
- <select id="<?php echo $this->get_field_id('fallback'); ?>" class="cmw-fallback cmw-listen"
424
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('fallback'); ?>">
425
- <option value="" <?php selected( $instance['fallback'], '' ); ?>>&nbsp;</option>
426
- <option value="parent" <?php selected( $instance['fallback'], 'parent' ); ?>><?php _e('Start at : -1 (parent)'); ?></option>
427
- <option value="current" <?php selected( $instance['fallback'], 'current' ); ?>><?php _e('Start at : the Current Item'); ?></option>
428
- <option value="quit" <?php selected( $instance['fallback'], 'quit' ); ?>><?php _e('No output!'); ?></option>
429
- </select>
430
-
431
- <br />
432
- <span class="cmw-disableifnot-fb-pc<?php $this->cmw_disableif( 'push', $isNotFallbackParentCurrent ); ?>">
433
- <?php $this->cmw_formfield_checkbox( $instance, 'fallback_siblings',
434
- array(
435
- 'label' => '&hellip;' . __('and Include its Siblings')
436
- ) ); ?>
437
-
438
- <br />
439
- <label><?php _e('For Depth:'); ?>
440
- <select id="<?php echo $this->get_field_id('fallback_depth'); ?>" data-cmw-text-levels="<?php _e(' levels'); ?>"
441
- data-cmw-set-levels="1" <?php $this->cmw_disableif(); ?>
442
- class="cmw-set-levels" name="<?php echo $this->get_field_name('fallback_depth'); ?>">
443
- <option value="0" <?php selected( $instance['fallback_depth'] > $menus['selectedLevels'] ? 0 : $instance['fallback_depth'], 0 ); ?>>&nbsp;</option>
444
- <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
445
- <option value="<?php echo $i; ?>" <?php selected( $instance['fallback_depth'], $i ); ?>><?php printf( _n('%d level', '%d levels', $i), $i ); ?></option>
446
- <?php } ?>
447
- </select></label>
448
- <span class="cmw-small-block cmw-indented"><em class="cmw-colour-grey"><?php _e('Fallback Depth is Relative to Current Item!'); ?></em></span>
449
- </span><!-- end .cmw-disableifnot-fb-pc --><?php $this->cmw_disableif( 'pop' ); ?>
450
- </div>
451
-
452
- </div><!-- end .cmw-disableifnot-br-ci --><?php $this->cmw_disableif( 'pop' ); ?>
453
-
454
- <div>
455
- <div class="cmw-indented"><?php _e('If no Current Item can be found:'); ?>
456
- <br />
457
- <?php $this->cmw_formfield_checkbox( $instance, 'fallback_ci_parent',
458
- array(
459
- 'label' => __('Try items marked as Parent of Current')
460
- ) ); ?>
461
- <span class="cmw-small-block cmw-indented"><em class="cmw-colour-grey"><?php _e('This is a last resort to determine a "Current Item"'); ?></em></span>
462
- </div>
463
- </div>
464
-
465
- <?php $this->cmw_close_a_field_section(); ?>
 
 
 
 
 
 
 
 
 
 
 
466
 
467
  <?php
468
- /**
469
- * start collapsible section : 'Output'
470
- */
471
- $this->cmw_open_a_field_section( $instance, __('Output'), 'fs_output' ); ?>
472
-
473
- <div>
474
- <?php $this->cmw_assist_link(); ?>
475
- <label class="cmw-followed-by">
476
- <input id="<?php echo $this->get_field_id('flat_output'); ?>_0"
477
- name="<?php echo $this->get_field_name('flat_output'); ?>"
478
- <?php $this->cmw_disableif(); ?> type="radio" value="0" <?php checked( !$instance['flat_output'] ); ?>
479
- /><?php _e('Hierarchical'); ?></label>
480
- <label class="cmw-whitespace-nowrap">
481
- <input id="<?php echo $this->get_field_id('flat_output'); ?>_1"
482
- name="<?php echo $this->get_field_name('flat_output'); ?>"
483
- <?php $this->cmw_disableif(); ?> type="radio" value="1" <?php checked( $instance['flat_output'] ); ?>
484
- /><?php _e('Flat'); ?></label>
485
- </div>
486
-
487
- <div>
488
- <?php _e('Set Title from:'); ?>
489
-
490
- <div class="cmw-indented">
491
- <?php $this->cmw_formfield_checkbox( $instance, 'title_from_current',
492
- array(
493
- 'label' => __('Current Item'),
494
- 'lclass' => 'cmw-followed-by'
495
- ) ); ?>
496
- <?php $this->cmw_formfield_checkbox( $instance, 'title_from_current_root',
497
- array(
498
- 'label' => '&hellip;' . __('or its Root'),
499
- 'lclass' => 'cmw-whitespace-nowrap'
500
- ) ); ?>
501
- </div>
502
- <div class="cmw-indented">
503
- <?php $this->cmw_formfield_checkbox( $instance, 'title_from_branch',
504
- array(
505
- 'label' => __('Branch'),
506
- 'lclass' => 'cmw-followed-by cmw-disableifnot-br',
507
- 'disableif' => $isNotByBranch
508
- ) ); ?>
509
- <?php $this->cmw_formfield_checkbox( $instance, 'title_from_branch_root',
510
- array(
511
- 'label' => '&hellip;' . __('or its Root'),
512
- 'lclass' => 'cmw-whitespace-nowrap cmw-disableifnot-br',
513
- 'disableif' => $isNotByBranch
514
- ) ); ?>
515
- </div>
516
- <div class="cmw-indented">
517
- &hellip; <?php _e('and:'); ?>
518
- <?php $this->cmw_formfield_checkbox( $instance, 'title_linked',
519
- array(
520
- 'label' => __('Make it a Link')
521
- ) ); ?>
522
- </div>
523
- </div>
524
-
525
- <div>
526
- <?php _e('Change UL to OL:'); ?>
527
- <br />
528
- <?php $this->cmw_formfield_checkbox( $instance, 'ol_root',
529
- array(
530
- 'label' => __('Top Level'),
531
- 'lclass' => 'cmw-followed-by'
532
- ) ); ?>
533
- <?php $this->cmw_formfield_checkbox( $instance, 'ol_sub',
534
- array(
535
- 'label' => __('Sub-Levels'),
536
- 'lclass' => 'cmw-whitespace-nowrap'
537
- ) ); ?>
538
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
 
540
  <?php
541
- //v1.1.0 As of WP v3.6, wp_nav_menu() automatically cops out (without outputting any HTML) if there are no items,
542
- // so the hide_empty option becomes superfluous; however, I'll keep the previous setting (if there was one)
543
- // in case of reversion to an earlier version of WP...
544
- if( $this->cmw_wp_version('3.6') ){
545
  ?>
546
- <div>
547
- <?php $this->cmw_formfield_checkbox( $instance, 'hide_empty',
548
- array(
549
- 'label' => __('Hide Widget if Empty'),
550
- 'desc' => __('Prevents any output when no items are found')
551
- ) ); ?>
552
- </div>
553
  <?php }else{ ?>
554
- <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
555
- type="hidden" value="<?php echo $instance['hide_empty'] ? '1' : ''; ?>" />
556
  <?php } ?>
557
 
558
- <?php $this->cmw_close_a_field_section(); ?>
559
 
560
  <?php
561
- /**
562
- * start collapsible section : 'Container'
563
- */
564
- $this->cmw_open_a_field_section( $instance, __('Container'), 'fs_container' ); ?>
565
-
566
- <div>
567
- <?php $this->cmw_formfield_textbox( $instance, 'container',
568
- array(
569
- 'label' => __('Element:'),
570
- 'desc' => __('Eg. div or nav; leave empty for no container')
571
- ) ); ?>
572
- </div>
573
- <div>
574
- <?php $this->cmw_formfield_textbox( $instance, 'container_id',
575
- array(
576
- 'label' => __('Unique ID:'),
577
- 'desc' => __('An optional ID for the container')
578
- ) ); ?>
579
- </div>
580
- <div>
581
- <?php $this->cmw_formfield_textbox( $instance, 'container_class',
582
- array(
583
- 'label' => __('Class:'),
584
- 'desc' => __('Extra class for the container')
585
- ) ); ?>
586
- </div>
587
-
588
- <?php $this->cmw_close_a_field_section(); ?>
589
 
590
  <?php
591
- /**
592
- * start collapsible section : 'Classes'
593
- */
594
- $this->cmw_open_a_field_section( $instance, __('Classes'), 'fs_classes' ); ?>
595
-
596
- <div>
597
- <?php $this->cmw_formfield_textbox( $instance, 'menu_class',
598
- array(
599
- 'label' => __('Menu Class:'),
600
- 'desc' => __('Class for the list element forming the menu')
601
- ) ); ?>
602
- </div>
603
- <div>
604
- <?php $this->cmw_formfield_textbox( $instance, 'widget_class',
605
- array(
606
- 'label' => __('Widget Class:'),
607
- 'desc' => __('Extra class for the widget itself')
608
- ) ); ?>
609
- </div>
610
-
611
- <?php $this->cmw_close_a_field_section(); ?>
612
 
613
  <?php
614
- /**
615
- * start collapsible section : 'Links'
616
- */
617
- $this->cmw_open_a_field_section( $instance, __('Links'), 'fs_links' ); ?>
618
-
619
- <div>
620
- <?php $this->cmw_formfield_textbox( $instance, 'before',
621
- array(
622
- 'label' => __('Before the Link:'),
623
- 'desc' =>__( htmlspecialchars('Text/HTML to go before the </a> of the link') ),
624
- 'fclass' => 'widefat'
625
- ) ); ?>
626
- </div>
627
- <div>
628
- <?php $this->cmw_formfield_textbox( $instance, 'after',
629
- array(
630
- 'label' => __('After the Link:'),
631
- 'desc' => __( htmlspecialchars('Text/HTML to go after the </a> of the link') ),
632
- 'fclass' => 'widefat'
633
- ) ); ?>
634
- </div>
635
- <div>
636
- <?php $this->cmw_formfield_textbox( $instance, 'link_before',
637
- array(
638
- 'label' => __('Before the Link Text:'),
639
- 'desc' => __('Text/HTML to go before the link text'),
640
- 'fclass' => 'widefat'
641
- ) ); ?>
642
- </div>
643
- <div>
644
- <?php $this->cmw_formfield_textbox( $instance, 'link_after',
645
- array(
646
- 'label' => __('After the Link Text:'),
647
- 'desc' => __('Text/HTML to go after the link text'),
648
- 'fclass' => 'widefat'
649
- ) ); ?>
650
- </div>
651
-
652
- <?php $this->cmw_close_a_field_section(); ?>
653
-
654
  <?php
655
- /**
656
- * v3.1.0 start collapsible section : 'Alternative'
657
- */
658
- $this->cmw_open_a_field_section( $instance, __('Alternative'), 'fs_alternative' ); ?>
659
-
660
- <div>
661
- <?php $this->cmw_assist_link(); ?>
662
-
663
- <label for="<?php echo $this->get_field_id('switch_if'); ?>" class="cmw-followed-by"><?php _e('On condition:'); ?></label>
664
- <br /><select id="<?php echo $this->get_field_id('switch_if'); ?>" class="cmw-switchable cmw-listen"
665
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('switch_if'); ?>">
666
- <option value="" <?php selected( $instance['switch_if'], '' ); ?>>&nbsp;</option>
667
- <option value="current" <?php selected( $instance['switch_if'], 'current' ); ?>><?php _e('Current Item is in...'); ?></option>
668
- <option value="no-current" <?php selected( $instance['switch_if'], 'no-current' ); ?>><?php _e('Current Item is NOT in...'); ?></option>
669
- <option value="no-output" <?php selected( $instance['switch_if'], 'no-output' ); ?>><?php _e('No Output from...'); ?></option>
670
- </select>
671
-
672
- <select id="<?php echo $this->get_field_id('switch_at'); ?>" class="cmw-switchable cmw-listen"
673
- <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('switch_at'); ?>">
674
- <option value="" <?php selected( $instance['switch_at'], '' ); ?>>&nbsp;</option>
675
- <option value="menu" <?php selected( $instance['switch_at'], 'menu' ); ?>><?php echo _e('Menu'); ?></option>
676
- <option value="primary" <?php selected( $instance['switch_at'], 'primary' ); ?>><?php echo _e('Primary Filter'); ?></option>
677
- <option value="secondary" <?php selected( $instance['switch_at'], 'secondary' ); ?>><?php echo _e('Secondary Filter'); ?></option>
678
- <option value="inclusions" <?php selected( $instance['switch_at'], 'inclusions' ); ?>><?php echo _e('Inclusions'); ?></option>
679
- <option value="output" <?php selected( $instance['switch_at'], 'output' ); ?>><?php echo _e('Final Output'); ?></option>
680
- </select>
681
-
682
- <br />
683
- <label class="cmw-disableifnot-sw<?php $this->cmw_disableif( 'push', $isNotSwitchable ); ?>"><?php _e('Then switch settings to:'); ?>
684
- <br /><textarea rows="3" cols="20" <?php $this->cmw_disableif(); ?> id="<?php echo $this->get_field_id('switch_to'); ?>"
685
- name="<?php echo $this->get_field_name('switch_to'); ?>"
686
- class="widefat"><?php echo $instance['switch_to']; ?></textarea>
687
- </label><!-- end .cmw-disableifnot-sw --><?php $this->cmw_disableif( 'pop' ); ?>
688
- <span class="cmw-small-block cmw-indented"><em class="cmw-colour-grey">Enter/Paste a [cmwizard.../] shortcode</em></span>
689
-
690
- </div>
691
-
692
- <?php $this->cmw_close_a_field_section(); ?>
693
-
694
- <div class="cmw-border-top">
695
- <div class="cmw-shortcode-nojs cmw-small-block"><?php _e('With Javascript disabled, the shortcode below is only guaranteed to be accurate when you <em>initially enter</em> Edit mode!'); ?></div>
696
- <div class="cmw-shortcode-wrap"><code class="widget-<?php echo $this->id_base; ?>-shortcode ui-corner-all"
697
- title="<?php _e('shortcode'); ?>"><?php echo $this->cmw_shortcode( array_merge( $instance, array( 'menu' => $menus['selectedMenu'] ) ) ); ?></code></div>
698
- </div>
 
 
 
 
 
 
 
699
 
700
  </div>
701
  <?php
702
 
703
- if( $this->_cmw_accessibility ){
704
- wp_localize_script( Custom_Menu_Wizard_Plugin::$script_handle, __CLASS__, array( 'trigger' => '#' . $this->get_field_id('menu') ) );
705
- }
706
-
707
- } //end form()
708
-
709
- /**
710
- * sanitizes/updates the widget settings sent from the backend admin
711
- *
712
- * @filters : custom_menu_wizard_wipe_on_update false
713
- *
714
- * @param array $new_instance New widget settings
715
- * @param array $old_instance Old widget settings
716
- * @return array Sanitized widget settings
717
- */
718
- public function update( $new_instance, $old_instance ) {
719
-
720
- //call the legacy update method for updates to existing widgets that don't have a version number (old format)...
721
- if( empty( $new_instance['cmwv'] ) ){
722
- return $this->cmw_legacy_update( $new_instance, $old_instance );
723
- }
724
-
725
- return self::cmw_settings(
726
- $new_instance,
727
- //allow a filter to return true, whereby any previous settings (now possibly unused) will be wiped instead of being allowed to remain...
728
- //eg. add_filter( 'custom_menu_wizard_wipe_on_update', [filter_function], 10, 1 ) => true
729
- apply_filters( 'custom_menu_wizard_wipe_on_update', false ) ? array() : $old_instance,
730
- __FUNCTION__ );
731
-
732
- } //end update()
733
-
734
- /**
735
- * produces the widget HTML at the front end
736
- *
737
- * @filters : custom_menu_wizard_nav_params array of params that will be sent to wp_nav_menu(), array of instance settings, id base
738
- * custom_menu_wizard_settings_pre_widget array of instance settings, id base
739
- * custom_menu_wizard_widget_output HTML output string, array of instance settings, id base, $args
740
- *
741
- * @param object $args Widget arguments
742
- * @param array $instance Configuration for this widget instance
743
- */
744
- public function widget( $args, $instance ) {
745
-
746
- //call the legacy widget method for producing existing widgets that don't have a version number (old format)...
747
- if( empty( $instance['cmwv'] ) ){
748
- $this->cmw_legacy_widget( $args, $instance );
749
- return;
750
- }
751
-
752
- //sanitize $instance...
753
- $instance = self::cmw_settings( $instance, array(), __FUNCTION__ );
754
- //holds information determined by the walker...
755
- $this->_cmw_walker = array();
756
-
757
- //v1.1.0 As of WP v3.6, wp_nav_menu() automatically prevents any HTML output if there are no items...
758
- $instance['hide_empty'] = $instance['hide_empty'] && $this->cmw_wp_version('3.6');
759
-
760
- //allow a filter to amend the instance settings prior to producing the widget output...
761
- //eg. add_filter( 'custom_menu_wizard_settings_pre_widget', [filter_function], 10, 2 ) => $instance (array)
762
- $instance = apply_filters( 'custom_menu_wizard_settings_pre_widget', $instance, $this->id_base );
763
-
764
- //fetch menu...
765
- if( !empty( $instance['menu'] ) ){
766
- $menu = wp_get_nav_menu_object( $instance['menu'] );
767
-
768
- //no menu, no output...
769
- if ( !empty( $menu ) ){
770
-
771
- //unless told not to, put the shortcode equiv. into a data item...
772
- //NB: to turn this off (example):
773
- // add_filter( 'custom_menu_wizard_settings_pre_widget', 'cmw_no_cmws', 10, 2 );
774
- // function cmw_no_cmws( $instance, $id_base ){ $instance['cmws_off'] = true; return $instance; }
775
- $dataCMWS = empty( $instance['cmws_off'] ) ? " data-cmws='" . esc_attr( $this->cmw_shortcode( $instance, true ) ) . "'" : '';
776
-
777
- if( !empty( $instance['container_class'] ) ){
778
- //the menu-[menu->slug]-container class gets applied by WP UNLESS an alternative
779
- //container class is supplied in the params - I'm going to set the param such that
780
- //this instance's container class (if specified) gets applied IN ADDITION TO the
781
- //default one...
782
- $instance['container_class'] = "menu-{$menu->slug}-container {$instance['container_class']}";
783
- }
784
-
785
- $instance['menu_class'] = preg_split( '/\s+/', $instance['menu_class'], -1, PREG_SPLIT_NO_EMPTY );
786
- //add cmw-alternate-maybe & cmw-fellback-maybe classes to the menu and we'll remove or replace later...
787
- $instance['menu_class'][] = 'cmw-alternate-maybe';
788
- $instance['menu_class'][] = 'cmw-fellback-maybe';
789
- $instance['menu_class'] = implode( ' ', $instance['menu_class'] );
790
-
791
- $walker = new Custom_Menu_Wizard_Walker;
792
- $params = array(
793
- 'menu' => $menu,
794
- 'container' => empty( $instance['container'] ) ? false : $instance['container'],
795
- 'container_id' => $instance['container_id'],
796
- 'menu_class' => $instance['menu_class'],
797
- 'echo' => false,
798
- 'fallback_cb' => false,
799
- 'before' => $instance['before'],
800
- 'after' => $instance['after'],
801
- 'link_before' => $instance['link_before'],
802
- 'link_after' => $instance['link_after'],
803
- 'depth' => $instance['flat_output'] ? -1 : $instance['depth'],
804
- 'walker' =>$walker,
805
- //widget specific stuff...
806
- '_custom_menu_wizard' => $instance
807
- );
808
- //for the walker's use...
809
- $params['_custom_menu_wizard']['_walker'] = array();
810
- //set wrapper to UL or OL...
811
- if( $instance['ol_root'] ){
812
- $params['items_wrap'] = '<ol id="%1$s" class="%2$s" data-cmwv="' . $instance['cmwv'] . '"' . $dataCMWS . '>%3$s</ol>';
813
- }else{
814
- $params['items_wrap'] = '<ul id="%1$s" class="%2$s" data-cmwv="' . $instance['cmwv'] . '"' . $dataCMWS . '>%3$s</ul>';
815
- }
816
- //add a container class...
817
- if( !empty( $instance['container_class'] ) ){
818
- $params['container_class'] = $instance['container_class'];
819
- }
820
-
821
- //add my filters...
822
- add_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
823
- if( $instance['hide_empty'] ){
824
- add_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
825
- }
826
-
827
- //allow a filter to amend the wp_nav_menu() params prior to calling it...
828
- //eg. add_filter( 'custom_menu_wizard_nav_params', [filter_function], 10, 3 ) => $params (array)
829
- //NB: wp_nav_menu() is in wp-includes/nav-menu-template.php
830
- $out = wp_nav_menu( apply_filters( 'custom_menu_wizard_nav_params', $params, $instance, $this->id_base ) );
831
-
832
- //remove my filters...
833
- remove_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
834
- if( $instance['hide_empty'] ){
835
- remove_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
836
- }
837
-
838
- //only put something out if there is something to put out...
839
- if( !empty( $out ) ){
840
-
841
- //check to see if the settings have been changed, either as a result of invoking an alternative
842
- //configuration, or due to the application of a custom_menu_wizard_walker_change_settings filter...
843
- if( !empty( $this->_cmw_walker['instances'] ) ){
844
- $instance = $this->_cmw_walker['instances']['new'];
845
- }
846
-
847
- //title from : priority is current -> current root -> branch -> branch root...
848
- //note that none actually have to be present in the results
849
- //v3.1.4 : used to get just the title string passed back, now get the menu item element!
850
- if( !empty( $this->_cmw_walker['get_title_from'] ) ){
851
- foreach( array('current', 'current_root', 'branch', 'branch_root') as $v){
852
- if( $instance[ 'title_from_' . $v ] && !empty( $this->_cmw_walker['get_title_from'][ $v ] )
853
- && !empty( $this->_cmw_walker['get_title_from'][ $v ]->title ) ){
854
- //allow the widget_title filter to override...
855
- $title = apply_filters( 'widget_title', $this->_cmw_walker['get_title_from'][ $v ]->title, $instance, $this->id_base );
856
- //if we've been asked for a linked title...
857
- if( $instance['title_linked'] ){
858
- $n = array(
859
- 'title' => empty( $this->_cmw_walker['get_title_from'][ $v ]->attr_title ) ? '' : $this->_cmw_walker['get_title_from'][ $v ]->attr_title,
860
- 'target' => empty( $this->_cmw_walker['get_title_from'][ $v ]->target ) ? '' : $this->_cmw_walker['get_title_from'][ $v ]->target,
861
- 'rel' => empty( $this->_cmw_walker['get_title_from'][ $v ]->xfn ) ? '' : $this->_cmw_walker['get_title_from'][ $v ]->xfn,
862
- 'href' => empty( $this->_cmw_walker['get_title_from'][ $v ]->url ) ? '' : $this->_cmw_walker['get_title_from'][ $v ]->url,
863
- 'class' => 'cmw-linked-widget-title'
864
- );
865
- $n = apply_filters( 'custom_menu_wizard_title_link_atts', $n, $this->_cmw_walker['get_title_from'][ $v ], $instance, $this->id_base );
866
- $atts = '';
867
- foreach ( (array)$n as $i => $j ) {
868
- if ( !empty( $j ) ) {
869
- $j = ( $i === 'href' ) ? esc_url( $j ) : esc_attr( $j );
870
- $atts .= ' ' . $i . '="' . $j . '"';
871
- }
872
- }
873
- if( !empty( $atts ) ){
874
- $title = '<a' . $atts . '>' . $title . '</a>';
875
- }
876
- }
877
- break;
878
- }
879
- }
880
- }
881
- if( empty( $title ) ){
882
- $title = $instance['hide_title'] ? '' : apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
883
- }
884
-
885
- //remove/replace the cmw-fellback-maybe class...
886
- $out = str_replace(
887
- array(
888
- 'cmw-fellback-maybe',
889
- 'cmw-alternate-maybe'
890
- ),
891
- array(
892
- empty( $this->_cmw_walker['fellback'] ) ? '' : 'cmw-fellback-' . $this->_cmw_walker['fellback'],
893
- empty( $this->_cmw_walker['alternative'] ) ? '' : 'cmw-invoked-alternative'
894
- ),
895
- $out );
896
-
897
- //try to add widget_class (if specified) to before_widget...
898
- if( !empty( $instance['widget_class'] ) && !empty( $args['before_widget'] ) ){
899
- //$args['before_widget'] is usually just a DIV start-tag, with an id and a class; if it
900
- //gets more complicated than that then this may not work as expected...
901
- if( preg_match( '/^<[^>]+?class=["\']/', $args['before_widget'] ) > 0 ){
902
- //...already has a class attribute : prepend mine...
903
- $args['before_widget'] = preg_replace( '/(class=["\'])/', '$1' . $instance['widget_class'] . ' ', $args['before_widget'], 1 );
904
- }else{
905
- //...doesn't currently have a class : add class attribute...
906
- $args['before_widget'] = preg_replace( '/^(<\w+)(\s|>)/', '$1 class="' . $instance['widget_class'] . '"$2', $args['before_widget'] );
907
- }
908
- }
909
-
910
- if( !empty( $title ) ){
911
- $out = $args['before_title'] . $title . $args['after_title'] . $out;
912
- }
913
- $out = $args['before_widget'] . $out . $args['after_widget'];
914
- //allow a filter to modify the entire output...
915
- //eg. add_filter( 'custom_menu_wizard_widget_output', [filter_function], 10, 4 ) => $output (HTML string)
916
- //NB 4th parameter ($args) added at v3.0.3
917
- echo apply_filters( 'custom_menu_wizard_widget_output', $out, $instance, $this->id_base, $args );
918
- }
919
- }
920
- }
921
-
922
- } //end widget()
923
-
924
- /**
925
- * outputs an assist anchor
926
- */
927
- public function cmw_assist_link(){
928
-
929
- //don't really need to worry about the id for non-javascript enabled usage because the css hides the
930
- //button, but it doesn't hurt so I've left it in...
931
- $hashid = $this->get_field_id( 'cmw' . ++$this->_cmw_hash_ct );
932
  ?>
933
  <a class="widget-<?php echo $this->id_base; ?>-assist button" id="<?php echo $hashid; ?>" href="#<?php echo $hashid; ?>"><?php _e('assist'); ?></a>
934
  <?php
935
 
936
- }
937
-
938
- /**
939
- * outputs the HTML to close off a collapsible/expandable group of settings
940
- */
941
- public function cmw_close_a_field_section(){
942
-
943
- ?></div><?php
944
-
945
- } //end cmw_close_a_field_section()
946
-
947
- /**
948
- * either pushes, pops, or echoes last of, the disabled attributes array
949
- * note that if accessibility mode is on, nothing should get disabled!
950
- * as of 3.1.0, nothing gets disabled, just coloured grey (incl. legacy versions)!
951
- *
952
- * @param string $action 'pop' or 'push'
953
- * @param boolean $test What to push
954
- */
955
- public function cmw_disableif( $action = 'echo', $test = false ){
956
-
957
- if( !isset( $this->_cmw_disableif ) ){
958
- $this->_cmw_disableif = array( '' );
959
- }
960
- if( $action == 'push' ){
961
- if( $test && !$this->_cmw_accessibility ){
962
- //append disabled attribute...
963
- //v3.1.0 : nothing gets disabled, including any legacy stuff!
964
- // $this->_cmw_disableif[] = 'disabled="disabled"';
965
- $this->_cmw_disableif[] = '';
966
- //and echo disabled class...
967
- echo ' cmw-colour-grey';
968
- }else{
969
- //append a copy of current last element (maintaining status quo)...
970
- $e = array_slice( $this->_cmw_disableif, -1 );
971
- $this->_cmw_disableif[] = $e[0];
972
- }
973
- }elseif( $action == 'pop' ){
974
- //remove last element (if count is greater than 1, so it is never left totally empty by mistake)...
975
- if( count( $this->_cmw_disableif ) > 1 ){
976
- array_pop( $this->_cmw_disableif );
977
- }
978
- }else{
979
- //echo last element...
980
- $e = array_slice( $this->_cmw_disableif, -1 );
981
- echo $e[0];
982
- }
983
-
984
- }
985
-
986
- /**
987
- * this gets run (filter: wp_nav_menu_{$menu->slug}_items) if hide_empty is set
988
- * if $items is empty then add a wp_nav_menu filter to do the actual return of an empty string
989
- * it gets run before the wp_nav_menu filter, but it gets the $items array whereas the wp_nav_menu filter does not
990
- * it gets added by $this->widget() before wp_nav_menu() is called, and removed immediately after wp_nav_menu() returns
991
- *
992
- * v1.1.0 As of WP v3.6 this method becomes superfluous because wp_nav_menu() has had code added to immediately
993
- * cop out (return false) if the output from wp_nav_menu_{$menu->slug}_items filter(s) is empty.
994
- * However, it stays in so as to cope with versions < 3.6
995
- *
996
- * @param array $items Menu items
997
- * @param object $args
998
- * @return array Menu items
999
- */
1000
- public function cmw_filter_check_for_no_items($items, $args){
1001
-
1002
- if( !empty( $args->_custom_menu_wizard ) && empty( $items ) ){
1003
- add_filter( 'wp_nav_menu', array( $this, 'cmw_filter_no_output_when_empty' ), 65532, 2 );
1004
- }
1005
- return $items;
1006
-
1007
- } //end cmw_filter_check_for_no_items()
1008
-
1009
- /**
1010
- * this (filter: wp_nav_menu) merely removes itself from the filters and returns an empty string
1011
- * it gets added by the cmw_filter_check_for_no_items method below, and only
1012
- * ever gets run when hide_empty is set on the widget instance
1013
- *
1014
- * v1.1.0 As of WP v3.6 this method becomes superfluous because wp_nav_menu() has had code added to immediately
1015
- * cop out (return false) if the output from wp_nav_menu_{$menu->slug}_items filter(s) is empty.
1016
- * However, it stays in so as to cope with versions < 3.6
1017
- *
1018
- * @param string $nav_menu HTML for the menu
1019
- * @param object $args
1020
- * @return string HTML for the menu
1021
- */
1022
- public function cmw_filter_no_output_when_empty($nav_menu, $args){
1023
-
1024
- remove_filter( 'wp_nav_menu', array( $this, __FUNCTION__ ), 65532, 2 );
1025
- return empty( $args->_custom_menu_wizard ) ? $nav_menu : '';
1026
-
1027
- } //end cmw_filter_no_output_when_empty()
1028
-
1029
- /**
1030
- * v1.2.1 stores any walker-determined information back into the widget instance
1031
- * gets run by the walker, on the filtered array of menu items, just before running parent::walk()
1032
- * only gets run *if* there are menu items found
1033
- *
1034
- * @param array $items Filtered menu items
1035
- * @param object $args
1036
- * @return array Menu items
1037
- */
1038
- public function cmw_filter_walker_items( $items, $args ){
1039
-
1040
- if( !empty( $args->_custom_menu_wizard['_walker'] ) ){
1041
- $this->_cmw_walker = $args->_custom_menu_wizard['_walker'];
1042
- }
1043
- return $items;
1044
-
1045
- } //end cmw_filter_walker_items()
1046
-
1047
- /**
1048
- * output a checkbox field
1049
- *
1050
- * @param array $instance Contains current field value
1051
- * @param string $field Field name
1052
- * @param array $params Attribute values
1053
- */
1054
- public function cmw_formfield_checkbox( &$instance, $field, $params ){
1055
-
1056
- $labelClass = empty( $params['lclass'] ) ? '' : $params['lclass'];
1057
- $fieldClass = empty( $params['fclass'] ) ? '' : $params['fclass'];
1058
- $disabling = !empty( $labelClass ) && isset( $params['disableif'] );
1059
  ?>
1060
- <label class="<?php echo $labelClass; if( $disabling ){ $this->cmw_disableif( 'push', $params['disableif'] ); } ?>">
1061
- <input id="<?php echo $this->get_field_id( $field ); ?>" class="<?php echo $fieldClass; ?>"
1062
- name="<?php echo $this->get_field_name( $field ); ?>" <?php $this->cmw_disableif(); ?>
1063
- <?php checked($instance[ $field ]); ?> type="checkbox" value="1"
1064
- /><?php echo $params['label']; ?></label><?php if( $disabling ){ $this->cmw_disableif( 'pop' ); } ?>
1065
  <?php
1066
- if( !empty( $params['desc'] ) ){
1067
  ?>
1068
- <span class="cmw-small-block"><em class="cmw-colour-grey"><?php echo $params['desc']; ?></em></span>
1069
  <?php
1070
- }
1071
 
1072
- } // end cmw_formfield_checkbox()
1073
 
1074
- /**
1075
- * output a text input field
1076
- *
1077
- * @param array $instance Contains current field value
1078
- * @param string $field Field name
1079
- * @param array $params Attribute values
1080
- */
1081
- public function cmw_formfield_textbox( &$instance, $field, $params ){
1082
 
1083
- $fieldClass = empty( $params['fclass'] ) ? '' : $params['fclass'];
1084
 
1085
- if( !empty( $params['label'] ) ){
1086
  ?>
1087
- <label for="<?php echo $this->get_field_id( $field ); ?>"><?php echo $params['label']; ?></label>
1088
  <?php
1089
- }
1090
  ?>
1091
- <input id="<?php echo $this->get_field_id( $field ); ?>" class="<?php echo $fieldClass; ?>"
1092
- name="<?php echo $this->get_field_name( $field ); ?>" <?php $this->cmw_disableif(); ?>
1093
- type="text" value="<?php echo $instance[ $field ]; ?>" />
1094
  <?php
1095
- if( !empty( $params['desc'] ) ){
1096
  ?>
1097
- <span class="cmw-small-block"><em class="cmw-colour-grey"><?php echo $params['desc']; ?></em></span>
1098
  <?php
1099
- }
1100
-
1101
- } // end cmw_formfield_textbox()
1102
-
1103
- /**
1104
- * gets menus (in name order) and their items, returning empty array if there are none (or if none have items)
1105
- *
1106
- * @param integer $selectedMenu (by reference) The instance setting to check against for a menu to be "selected"
1107
- * @return array
1108
- */
1109
- public function cmw_get_custom_menus( &$selectedMenu ){
1110
-
1111
- $findSM = $selectedMenu > 0;
1112
- $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
1113
- if( !empty( $menus ) ){
1114
- foreach( $menus as $i => $menu ){
1115
- //find the menu's items, then remove any menus that have no items...
1116
- $menus[ $i ]->_items = wp_get_nav_menu_items( $menu->term_id );
1117
- if( empty( $menus[ $i ]->_items ) ){
1118
- unset( $menus[ $i ] );
1119
- }else{
1120
- //if the items are all orphans, then remove the menu...
1121
- $rootItem = false;
1122
- foreach( $menus[ $i ]->_items as $item ){
1123
- $rootItem = $rootItem || $item->menu_item_parent == 0;
1124
- }
1125
- if( !$rootItem ){
1126
- unset( $menus[ $i ] );
1127
- }elseif( $findSM && $selectedMenu == $menu->term_id ){
1128
- $findSM = false;
1129
- }
1130
- }
1131
- }
1132
- }
1133
- //if findSM is TRUE then we were looking for a specific menu and failed to find it (or it had no eligible items)...
1134
- if( $findSM ){
1135
- //clear selectedMenu...
1136
- $selectedMenu = 0;
1137
- //this would be the place to flag a warning!
1138
- }
1139
-
1140
- return empty( $menus ) ? array() : array_values( $menus );
1141
-
1142
- } // end cmw_get_custom_menus()
1143
-
1144
- /**
1145
- * gets the various option, optgroups, max levels, etc, from the available custom menus (if any)
1146
- *
1147
- * @param integer $selectedMenu The instance setting to check against for a menu to be "selected"
1148
- * @param integer $selectedItem The instance setting to check against for an menu item to be "selected"
1149
- * @return array|boolean
1150
- */
1151
- public function cmw_scan_menus( $selectedMenu, $selectedItem ){
1152
-
1153
- //create the options for the menu select & branch select...
1154
- // IE is a pita when it comes to SELECTs because it ignores any styling on OPTGROUPs and OPTIONs, so I'm using
1155
- // a copy from which the javascript can pick the relevant OPTGROUP
1156
- $rtn = array(
1157
- 'maxlevel' => 1, //maximum number of levels (across all menus)
1158
- 'names' => array(), //HTML of OPTIONs, for selecting a menu (returned as a string)
1159
- 'optgroups' => array(), //HTML of OPTGROUPs & contained OPTIONs, for selecting an item (returned as a string)
1160
- 'selectedOptgroup' => array(''), //HTML of currently selected menu's OPTGROUP and its OPTIONs (returned as string)
1161
- 'selectedBranchName' => __('the Current Item'), //title of currently selected menu item
1162
- 'selectedLevels' => 1 //number of levels in the currently selected menu
1163
- );
1164
-
1165
- //couple of points:
1166
- // - if there's no currently selected menu (eg. it's a new, unsaved form) then use the first menu found that has eligible items
1167
- // - if there is a currently selected menu, but that menu is no longer available (no longer exists, or now has no eligible items)
1168
- // then, again, use the first menu found that does have items. PROBLEM : this means that the widget's instance settings
1169
- // won't match what the widget is currently displaying! this situation is not unique to this function because it can
1170
- // also occur for things like depth, but it does raise the question of whether the user should be informed that what
1171
- // is being presented does not match the current saved settings?
1172
- // Note that also applies to selected item (ie. the menu still exists but the currently selected item within that menu does not).
1173
-
1174
- $ct = 0;
1175
- $sogCt = 0;
1176
- $itemindents = $menu = $item = NULL;
1177
- //note that fetching the menus can clear selectedMenu!
1178
- foreach( $this->cmw_get_custom_menus( $selectedMenu ) as $i => $menu ){
1179
- $maxgrplevel = 1;
1180
- $itemindents = array( '0' => 0 );
1181
- $menuGrpOpts = '';
1182
- //don't need to check for existence of items because if there were none then the menu wouldn't be here!
1183
- foreach( $menu->_items as $item ){
1184
- //exclude orphans!
1185
- if( isset($itemindents[ $item->menu_item_parent ])){
1186
- $title = $item->title;
1187
- $level = $itemindents[ $item->menu_item_parent ] + 1;
1188
-
1189
- $itemindents[ $item->ID ] = $level;
1190
- $rtn['maxlevel'] = max( $rtn['maxlevel'], $level );
1191
- $maxgrplevel = max( $maxgrplevel, $level );
1192
-
1193
- //if there is no currently selected menu AND this is the first found item for this menu then
1194
- //set this menu as the currently selected menu (being the first one found with an eligible item)...
1195
- if( empty( $selectedMenu ) && empty( $menuGrpOpts ) ){
1196
- $selectedMenu = $menu->term_id;
1197
- }
1198
- //only if THIS is the currently selected menu do we determine "selected" for each menu item...
1199
- if( $selectedMenu == $menu->term_id ){
1200
- $selected = selected( $selectedItem, $item->ID, false );
1201
- if( !empty( $selected ) ){
1202
- $rtn['selectedBranchName'] = $title;
1203
- }
1204
- $rtn['selectedOptgroup'][ $sogCt ] .= '<option value="' . $item->ID . '" ' . $selected . ' data-cmw-level="' . $level . '">';
1205
- $rtn['selectedOptgroup'][ $sogCt ] .= str_repeat( '&nbsp;', ($level - 1) * 3 ) . esc_attr( $title ) . '</option>';
1206
- }
1207
- //don't set "selected" on the big list...
1208
- $menuGrpOpts .= '<option value="' . $item->ID . '" data-cmw-level="' . $level . '">';
1209
- $menuGrpOpts .= str_repeat( '&nbsp;', ($level - 1) * 3 ) . esc_attr( $title ) . '</option>';
1210
- }
1211
- }
1212
-
1213
- //should never be empty, but check nevertheless...
1214
- if( !empty( $menuGrpOpts ) ){
1215
- $rtn['names'][ $ct ] = '<option ' . selected( $selectedMenu, $menu->term_id, false ) . ' value="' . $menu->term_id . '">' . $menu->name . '</option>';
1216
- $rtn['optgroups'][ $ct ] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $ct . '" data-cmw-max-level="' . $maxgrplevel . '">';
1217
- $rtn['optgroups'][ $ct ] .= $menuGrpOpts;
1218
- $rtn['optgroups'][ $ct ] .= '</optgroup>';
1219
- //if this menu is selected, then store this optgroup as the selected optgroup, and the number of levels it has...
1220
- if( $selectedMenu == $menu->term_id ){
1221
- $rtn['selectedOptgroup'][ $sogCt ] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $ct . '" data-cmw-max-level="' . $maxgrplevel . '">' . $rtn['selectedOptgroup'][ $sogCt ] . '</optgroup>';
1222
- $rtn['selectedOptgroup'][ ++$sogCt ] = '';
1223
- $rtn['selectedLevels'] = $maxgrplevel;
1224
- }elseif( $this->_cmw_accessibility ){
1225
- //if accessibility is on then the selected groups need to contain *all* the groups otherwise, with javascript disabled, the
1226
- //user will not be able to select menu items from a switched menu without saving first. if javascript is not disabled, then
1227
- //the script should initially remove any optgroups not immediately required...
1228
- $rtn['selectedOptgroup'][ $sogCt ] = $rtn['optgroups'][ $ct ];
1229
- $rtn['selectedOptgroup'][ ++$sogCt ] = '';
1230
- }
1231
- $ct++;
1232
- }
1233
- }
1234
- unset( $itemindents, $menu, $item );
1235
-
1236
- if( empty( $rtn['names'] ) ){
1237
- $rtn = false;
1238
- }else{
1239
- $rtn['names'] = implode( '', $rtn['names'] );
1240
- $rtn['optgroups'] = implode( '', $rtn['optgroups'] );
1241
- $rtn['selectedOptgroup'] = implode( '', $rtn['selectedOptgroup'] );
1242
- if( $this->_cmw_accessibility ){
1243
- //reset levels of selected optgroup to be the max levels of any group...
1244
- $rtn['selectedLevels'] = $rtn['maxlevel'];
1245
- }
1246
- //send the currently selected menu id back (may be different from the value when passed
1247
- //in, if this is a new instance or if a menu set for an existing instance has been deleted)
1248
- $rtn['selectedMenu'] = $selectedMenu;
1249
- }
1250
- return $rtn;
1251
-
1252
- }
1253
-
1254
- /**
1255
- * outputs the HTML to begin a collapsible/expandable group of settings
1256
- *
1257
- * @param array $instance
1258
- * @param string $text Label
1259
- * @param string $fname Field name
1260
- */
1261
- public function cmw_open_a_field_section( &$instance, $text, $fname ){
1262
-
1263
- $hashid = $this->get_field_id( 'cmw' . ++$this->_cmw_hash_ct );
1264
  ?>
1265
  <a class="widget-<?php echo $this->id_base; ?>-fieldset<?php echo $instance[$fname] ? ' cmw-collapsed-fieldset' : ''; ?>"
1266
- id="<?php echo $hashid; ?>" href="#<?php echo $hashid; ?>"><?php echo $text; ?></a>
1267
- <input id="<?php echo $this->get_field_id($fname); ?>" class="cmw-display-none cmw-fieldset-state"
1268
- name="<?php echo $this->get_field_name($fname); ?>"
1269
- type="checkbox" value="1" <?php checked( $instance[$fname] ); ?> />
1270
  <div class="cmw-fieldset<?php echo $instance[$fname] ? ' cmw-start-fieldset-collapsed' : ''; ?>">
1271
  <?php
1272
 
1273
- } //end cmw_open_a_field_section()
1274
-
1275
- /**
1276
- * returns true if the version of WP is lower-than or greater-than-or-equal to the one provided
1277
- *
1278
- * @param string $v Version to test for lower than
1279
- * @return boolean
1280
- */
1281
- public function cmw_wp_version( $v, $gte=false ){
1282
- global $wp_version;
1283
-
1284
- $rtn = version_compare( strtolower( $wp_version ), $v . 'a', '<' );
1285
- return $gte ? !$rtn : $rtn;
1286
-
1287
- } //end cmw_wp_version()
1288
-
1289
- /**
1290
- * sanitizes the widget settings for update(), widget() and form()
1291
- *
1292
- * @param array $from_instance Widget settings
1293
- * @param array $base_instance Old widget settings or an empty array
1294
- * @param string Name of the calling method
1295
- * @return array Sanitized widget settings
1296
- */
1297
- public static function cmw_settings( $from_instance, $base_instance = false, $method = 'widget' ){
1298
-
1299
  /* old (pre v3) settings...
1300
- //switches...
1301
- 'include_ancestors' => 0 : replaced by ancestors
1302
- 'include_parent' => 0 : replaced by ancestors
1303
- 'include_parent_siblings' => 0 : replaced by ancestor_siblings
1304
- 'title_from_parent' => 0 : replaced by title_from_branch
1305
- 'fallback_no_ancestor' => 0 : no longer applicable
1306
- 'fallback_include_parent' => 0 : no longer applicable
1307
- 'fallback_include_parent_siblings' => 0 : no longer applicable (but "sort of" replicated by fallback_siblings)
1308
- 'fallback_no_children' => 0 : replaced by fallback
1309
- 'fallback_nc_include_parent' => 0 : no longer applicable
1310
- 'fallback_nc_include_parent_siblings' => 0 : no longer applicable
1311
- //integers...
1312
- 'filter_item' => -2 : replaced by branch
1313
- 'start_level' => 1 : replaced by level & branch_start
1314
  */
1315
 
1316
- $instance = is_array( $base_instance ) ? $base_instance : array();
1317
-
1318
- //switches : values are defaults...
1319
- foreach( array(
1320
- 'allow_all_root' => 0, //v3.0.0
1321
- 'depth_rel_current' => 0,
1322
- 'fallback_ci_parent' => 0, //v3.1.0 enables fallback determination of current item to item having current_item_parent set
1323
- 'fallback_siblings' => 0, //v3.0.0 sort of replaces fallback_include_parent_siblings
1324
- 'flat_output' => 0,
1325
- 'hide_title' => 0,
1326
- 'siblings' => 0, //v3.0.0 replaces include_parent_siblings
1327
- 'include_root' => 0, //v3.0.0 ; v3.0.4 replaced/expanded by include_level DEPRECATED
1328
- 'title_from_branch' => 0, //v3.0.0 replaces title_from_parent
1329
- 'title_from_branch_root' => 0, //v3.0.0 added
1330
- 'title_from_current' => 0,
1331
- 'title_from_current_root' => 0, //v3.0.0 added
1332
- 'title_linked' => 0, //v3.1.4 added
1333
- 'ol_root' => 0,
1334
- 'ol_sub' => 0,
1335
- 'hide_empty' => 0, //this only has relevance prior to WP v3.6
1336
- //field section collapsed toggles...
1337
- 'fs_filters' => 1, //v3.0.0 replaces fs_filter and now starts out collapsed
1338
- 'fs_fallbacks' => 1,
1339
- 'fs_output' => 1,
1340
- 'fs_container' => 1,
1341
- 'fs_classes' => 1,
1342
- 'fs_links' => 1,
1343
- 'fs_alternative' => 1 //v3.1.0
1344
- ) as $k => $v ){
1345
-
1346
- if( $method == 'update' ){
1347
- //store as 0 or 1...
1348
- $instance[ $k ] = empty( $from_instance[ $k ] ) ? 0 : 1;
1349
- }else{
1350
- //use internally as boolean...
1351
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? !empty( $from_instance[ $k ] ) : !empty( $v );
1352
- }
1353
- }
1354
-
1355
- //integers : values are minimums (defaults are the values maxed against 0)...
1356
- foreach( array(
1357
- 'ancestors' => -9999, //v3.0.0 replaces include_ancestors, but with levels (relative & absolute)
1358
- 'ancestor_siblings' => -9999, //v3.0.0 also has levels (relative & absolute)
1359
- 'depth' => 0,
1360
- 'branch' => 0, //v3.0.0 replaces filter_item, but without current parent|root item
1361
- 'menu' => 0,
1362
- 'level' => 1, //v3.0.0 replace start_level (for a level filter)
1363
- 'fallback_depth' => 0 //v3.0.0 added
1364
- ) as $k => $v ){
1365
-
1366
- $instance[ $k ] = isset( $from_instance[ $k ]) ? max( $v, intval( $from_instance[ $k ] ) ) : max( $v, 0 );
1367
- }
1368
-
1369
- //strings : values are defaults...
1370
- foreach( array(
1371
- 'title' => '',
1372
- 'filter' => '', //v3.0.0 changed from integer ('', 'branch', 'items'), where empty is equiv. to 'level' (was level=0, branch=1, items=-1)
1373
- 'branch_start' => '', //v3.0.0 replace start_level (for a branch filter)
1374
- 'start_mode' => '', //v3.0.0 forces branch_start to use entire level ('', 'level')
1375
- 'contains_current' => '', //v3.0.0 changed from switch ('', 'menu', 'primary', 'secondary', 'inclusions' or 'output')
1376
- 'container' => 'div',
1377
- 'container_id' => '',
1378
- 'container_class' => '',
1379
- 'exclude_level' => '', //v3.0.0 (1 or more digits, possibly with an appended '-' or '+')
1380
- 'fallback' => '', //v3.0.0 replace fallback_no_children ('', 'parent', 'current', 'quit')
1381
- 'include_level' => '', //v3.0.4 (1 or more digits, possibly with an appended '-' or '+')
1382
- 'switch_if' => '', //v3.1.0 ('', 'current', 'no-current', 'no-output')
1383
- 'switch_at' => '', //v3.1.0 (same as for contains_current)
1384
- 'switch_to' => '', //v3.1.0 (a [cmwizard .../] shortcode)
1385
- 'menu_class' => 'menu-widget',
1386
- 'widget_class' => '',
1387
- 'cmwv' => ''
1388
- ) as $k => $v ){
1389
-
1390
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? strip_tags( trim( (string)$from_instance[ $k ] ) ) : $v;
1391
- if( $method == 'form' ){
1392
- //escape strings...
1393
- $instance[ $k ] = esc_attr( trim( $instance[ $k ] ) );
1394
- }
1395
- }
1396
- if( $method == 'widget' && !empty( $instance['switch_to'] ) ){
1397
- $instance['switch_to'] = apply_filters( 'custom_menu_wizard_sanitize_alternative', $instance['switch_to'] );
1398
- }
1399
-
1400
- //html strings : values are defaults...
1401
- foreach( array(
1402
- 'before' => '',
1403
- 'after' => '',
1404
- 'link_before' => '',
1405
- 'link_after' => ''
1406
- ) as $k => $v ){
1407
-
1408
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( (string)$from_instance[ $k ] ) : $v;
1409
- if( $method == 'form' ){
1410
- //escape html strings...
1411
- $instance[ $k ] = esc_html( trim( $instance[ $k ] ) );
1412
- }
1413
- }
1414
-
1415
- //csv strings : values are defaults...
1416
- foreach( array(
1417
- 'exclude' => '', //v3.0.0 added
1418
- 'items' => ''
1419
- ) as $k => $v ){
1420
-
1421
- $inherits = array();
1422
- $instance[ "_$k" ] = array();
1423
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( (string)$from_instance[ $k ] ) : $v;
1424
- foreach( preg_split('/[,\s]+/', $instance[ $k ], -1, PREG_SPLIT_NO_EMPTY ) as $i ){
1425
- //values can be just digits, or digits followed by a '+' (for inheritance)...
1426
- if( preg_match( '/^(\d+)(\+?)$/', $i, $m ) > 0 ){
1427
- $i = intval( $m[1] );
1428
- if( $i > 0 ){
1429
- if( !empty( $m[2] ) ){
1430
- $inherits[] = $i;
1431
- $i = $i . '+';
1432
- }
1433
- if( !in_array( "$i", $instance[ "_$k" ] ) ){
1434
- $instance[ "_$k" ][] = "$i";
1435
- }
1436
- }
1437
- }
1438
- }
1439
- if( !empty( $inherits ) ){
1440
- $instance[ "_$k" ] = array_diff( $instance[ "_$k" ], $inherits );
1441
- }
1442
- unset( $inherits );
1443
- //just store as comma-separated...
1444
- $instance[ $k ] = implode( ',', $instance[ "_$k" ] );
1445
- //can dump the underbar versions if called from update()...
1446
- if( $method == 'update' ){
1447
- unset( $instance[ "_$k" ] );
1448
- }
1449
- }
1450
-
1451
- //v3.0.4 : v3.0.* back compat...
1452
- //include_root was a boolean, but has been replaced with include_level, and the equiv. of include_root On is include_level=1...
1453
- if( $instance['include_root'] && empty( $instance['include_level'] ) ){
1454
- $instance['include_level'] = '1';
1455
- }
1456
- unset( $instance['include_root'] );
1457
-
1458
- return $instance;
1459
-
1460
- } //end cmw_settings()
1461
-
1462
- /**
1463
- * returns the shortcode equivalent of the current settings (not called by legacy code!)
1464
- *
1465
- * @param array $instance
1466
- * @param boolean $asJSON Requests response in JSON format, for the data-cmws attribute
1467
- * @return string
1468
- */
1469
- public function cmw_shortcode( $instance, $asJSON=false ){
1470
-
1471
- $args = array(
1472
- 'menu' => $instance['menu']
1473
- );
1474
- $byBranch = $instance['filter'] == 'branch';
1475
- $byItems = $instance['filter'] == 'items';
1476
- $byLevel = !$byBranch && !$byItems;
1477
-
1478
- //take notice of the widget's hide_title flag...
1479
- if( !empty( $instance['title'] ) && !$instance['hide_title'] ){
1480
- $args['title'] = array( $instance['title'] );
1481
- }
1482
- //byLevel is the default (no branch & no items), as is level=1, so we only *have* to specify level if it's greater than 1...
1483
- if( $byLevel && $instance['level'] > 1 ){
1484
- $args['level'] = $instance['level'];
1485
- }
1486
- //specifying branch sets byBranch, overriding byLevel...
1487
- if( $byBranch ){
1488
- //use the alternative for 0 ("current") because it's more self-explanatory...
1489
- $args['branch'] = $instance['branch'] > 0 ? $instance['branch'] : 'current';
1490
- //start_at only *has* to be specified if not empty...
1491
- if( !empty( $instance['branch_start'] ) ){
1492
- $args['start_at'] = array( $instance['branch_start'] );
1493
- }
1494
- //start_mode may be brought into play by a fallback so always specify it...
1495
- if( $instance['start_mode'] == 'level' ){
1496
- $args['start_mode'] = 'level';
1497
- }
1498
- //allow_all_root is only applicable to byBranch...
1499
- //NB this could be refined further, in that it only comes into play if
1500
- // (a) start_mode is set to level, or
1501
- // (b) there is a no-kids fallback set that produces output AND asks for siblings
1502
- //but that gets a bit fussy so I'm leaving it as-is.
1503
- if( $instance['allow_all_root'] ){
1504
- $args['allow_all_root'] = 1;
1505
- }
1506
- }
1507
- //specifying items set byItems, overriding byLevel & byBranch...
1508
- if( $byItems ){
1509
- $args['items'] = $instance['_items'];
1510
- }
1511
- //depth if greater than 0...
1512
- if( $instance['depth'] > 0 ){
1513
- $args['depth'] = $instance['depth'];
1514
- }
1515
- //depth relative to current item is only applicable if depth is not unlimited...
1516
- if( $instance['depth_rel_current'] && $instance['depth'] > 0 ){
1517
- $args['depth_rel_current'] = 1;
1518
- }
1519
- //fallbacks...
1520
- //no children : branch = current item...
1521
- if( $byBranch && $instance['branch'] == 0 ){
1522
- if( !empty( $instance['fallback'] ) ){
1523
- $args['fallback'] = array( $instance['fallback'] );
1524
- if( $args['fallback'] != 'quit' ){
1525
- if( $instance['fallback_siblings'] ){
1526
- $args['fallback'][] = '+siblings';
1527
- }
1528
- if( $instance['fallback_depth'] > 0 ){
1529
- $args['fallback'][] = $instance['fallback_depth'];
1530
- }
1531
- }
1532
- }
1533
- }
1534
- //branch ancestor inclusions...
1535
- if( $byBranch && !empty( $instance['ancestors'] ) ){
1536
- $args['ancestors'] = $instance['ancestors'];
1537
- //only ancestor-siblings if ancestors...
1538
- if( !empty( $instance['ancestor_siblings'] ) ){
1539
- $args['ancestor_siblings'] = $instance['ancestor_siblings'];
1540
- }
1541
- }
1542
- //inclusions by level...
1543
- if( !empty( $instance['include_level'] ) ){
1544
- $args['include_level'] = array( $instance['include_level'] );
1545
- }
1546
- //exclusions by id...
1547
- if( !empty( $instance['_exclude'] ) ){
1548
- $args['exclude'] = $instance['_exclude'];
1549
- }
1550
- //...and by level...
1551
- if( !empty( $instance['exclude_level'] ) ){
1552
- $args['exclude_level'] = array( $instance['exclude_level'] );
1553
- }
1554
- //title from...
1555
- $n = array();
1556
- if( $instance['title_from_current'] ){
1557
- $n[] = 'current';
1558
- }else if( $instance['title_from_current_root'] ){
1559
- $n[] = 'current-root';
1560
- }
1561
- if( $byBranch && $instance['title_from_branch'] ){
1562
- $n[] = 'branch';
1563
- }else if( $byBranch && $instance['title_from_branch_root'] ){
1564
- $n[] = 'branch-root';
1565
- }
1566
- if( !empty( $n ) ){
1567
- $args['title_from'] = $n;
1568
- //...title_linked is only relevant if title_from is set...
1569
- if( $instance['title_linked'] ){
1570
- $args['title_linked'] = 1;
1571
- }
1572
- }
1573
- //switches...
1574
- foreach( array('siblings', 'flat_output', 'ol_root', 'ol_sub', 'fallback_ci_parent') as $n ){
1575
- if( $instance[ $n ] ){
1576
- $args[ $n ] = 1;
1577
- }
1578
- }
1579
- //strings...
1580
- foreach( array(
1581
- 'contains_current' => '',
1582
- 'container' => 'div',
1583
- 'container_id' => '',
1584
- 'container_class' => '',
1585
- 'menu_class' => 'menu-widget',
1586
- 'widget_class' => ''
1587
- ) as $n => $v ){
1588
- if( $instance[ $n ] != $v ){
1589
- $args[ $n ] = array( $instance[ $n ] );
1590
- }
1591
- }
1592
- foreach( array(
1593
- 'wrap_link' => 'before',
1594
- 'wrap_link_text' => 'link_before'
1595
- ) as $n => $v ){
1596
- if( preg_match( '/^<(\w+)/', $instance[ $v ], $m ) > 0 ){
1597
- $args[ $n ] = array( $m[1] );
1598
- }
1599
- }
1600
- //alternative...
1601
- if( !empty( $instance['switch_if'] ) && !empty( $instance['switch_at'] ) ){
1602
- $args['alternative'] = array( $instance['switch_if'], $instance['switch_at'] );
1603
- $content = apply_filters( 'custom_menu_wizard_sanitize_alternative', $instance['switch_to'] );
1604
- }
1605
- //build the shortcode...
1606
- $m = array();
1607
- foreach( $args as $n => $v ){
1608
- //array indicates join (with comma sep) & surround it in double quotes, otherwise leave 'as-is'...
1609
- if( $asJSON ){
1610
- $m[ $n ] = is_array( $v ) ? implode( ',', $v ) : $v;
1611
- }else{
1612
- $m[] = is_array( $v ) ? $n . '="' . implode( ',', $v ) . '"' : $n . '=' . $v;
1613
- }
1614
- }
1615
- unset( $args );
1616
-
1617
- //NB at v3.0.0, the shortcode changed from custom_menu_wizard to cmwizard (the previous version is still supported)
1618
- //for JSON, don't output content...
1619
- return $asJSON ? json_encode( $m ) : '[cmwizard ' . implode( ' ', $m ) . ( empty( $content ) ? '/]' : ']' . $content . '[/cmwizard]' );
1620
-
1621
- } //end cmw_shortcode()
1622
-
1623
-
1624
-
1625
- /*======================
1626
- * LEGACY CODE (v2.1.0)
1627
- *======================*/
1628
-
1629
- /**
1630
- * produces the legacy version of the backend admin form(s)
1631
- *
1632
- * @filters : custom_menu_wizard_prevent_legacy_updates false
1633
- *
1634
- * @param array $instance Widget settings
1635
- */
1636
- public function cmw_legacy_form( $instance ) {
1637
-
1638
- //sanitize $instance...
1639
- $instance = $this->cmw_legacy_settings( $instance, array(), 'form' );
1640
-
1641
- //if no populated menus exist, suggest the user go create one...
1642
- if( ( $menus = $this->cmw_scan_menus( $instance['menu'], $instance['filter_item'] ) ) === false ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1643
  ?>
1644
  <p class="widget-<?php echo $this->id_base; ?>-no-menus">
1645
- <?php printf( __('No populated menus have been created yet. <a href="%s">Create one</a>.'), admin_url('nav-menus.php') ); ?>
1646
  </p>
1647
  <?php
1648
- return;
1649
- }
1650
 
1651
- //set up some simple booleans for use at the disableif___ classes...
1652
- $isShowSpecific = $instance['filter'] < 0; // disableif-ss (IS show specific items)
1653
- $isNotChildrenOf = $instance['filter'] < 1; // disableif (is NOT Children-of)
1654
- $isNotCurrentRootParent = $isNotChildrenOf || $instance['filter_item'] >= 0; // disableifnot-rp (is NOT Children-of Current Root/Parent)
1655
- $isNotCurrentItem = $isNotChildrenOf || $instance['filter_item'] != 0; // disableifnot-ci (is NOT Children-of Current Item)
1656
 
1657
  ?>
1658
  <div id="<?php echo $this->get_field_id('onchange'); ?>"
1659
- class="widget-<?php echo $this->id_base; ?>-onchange<?php echo $this->cmw_wp_version('3.8') ? ' cmw-pre-wp-v38' : ''; ?>"
1660
- data-cmw-v36plus='<?php echo $this->cmw_wp_version('3.6', true) ? 'true' : 'false'; ?>'
1661
- data-cmw-dialog-prompt='<?php _e('Click an item to toggle &quot;Current Menu Item&quot;'); ?>'
1662
- data-cmw-dialog-output='<?php _e('Basic Output'); ?>'
1663
- data-cmw-dialog-fallback='<?php _e('Fallback invoked'); ?>'
1664
- data-cmw-dialog-set-current='<?php _e('Set Current Item?'); ?>'
1665
- data-cmw-dialog-shortcodes='<?php _e('Find posts/pages containing a CMW shortcode'); ?>'
1666
- data-cmw-dialog-untitled='<?php _e('untitled'); ?>'
1667
- data-cmw-dialog-fixed='<?php _e('fixed'); ?>'
1668
- data-cmw-dialog-nonce='<?php echo wp_create_nonce( 'cmw-find-shortcodes' ); ?>'
1669
- data-cmw-dialog-version='2.1.0'
1670
- data-cmw-dialog-id='<?php echo $this->get_field_id('dialog'); ?>'>
1671
  <?php
1672
- /**
1673
- * Legacy warning...
1674
- */
1675
  ?>
1676
- <p class="cmw-legacy-warn">
1677
- <a class="widget-<?php echo $this->id_base; ?>-legacy-close cmw-legacy-close" title="<?php _e('Dismiss'); ?>" href="#">X</a>
1678
- <em><?php _e('This is an old version of the widget!'); ?>
1679
  <?php
1680
- //allow a filter to return true, whereby updates to legacy widgets are disallowed...
1681
- //eg. apply_filter( 'custom_menu_wizard_prevent_legacy_updates', [filter function], 10, 1 ) => true
1682
- if( apply_filters( 'custom_menu_wizard_prevent_legacy_updates', false ) ){
1683
  ?>
1684
- <br /><?php _e('Any changes you make will NOT be Saved!'); ?>
1685
  <?php
1686
- }
1687
  ?>
1688
- <br /><?php _e('Please consider creating a new instance of the widget to replace this one.'); ?>
1689
- <a href="<?php echo $this->_cmw_legacy_warnreadmore; ?>" target="_blank"><?php _e('read more'); ?></a></em>
1690
- </p>
1691
  <?php
1692
 
1693
- /**
1694
- * permanently visible section : Title (with Hide) and Menu
1695
- */
1696
  ?>
1697
- <p>
1698
- <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:') ?></label>
1699
- <?php $this->cmw_formfield_checkbox( $instance, 'hide_title',
1700
- array(
1701
- 'label' => __('Hide'),
1702
- 'lclass' => 'alignright'
1703
- ) ); ?>
1704
- <?php $this->cmw_formfield_textbox( $instance, 'title',
1705
- array(
1706
- 'desc' => __('Title can be set, but need not be displayed'),
1707
- 'fclass' => 'widefat cmw-widget-title'
1708
- ) ); ?>
1709
- </p>
1710
-
1711
- <p>
1712
- <?php $this->cmw_assist_link(); ?>
1713
- <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
1714
- <select id="<?php echo $this->get_field_id('menu'); ?>" <?php $this->cmw_disableif(); ?>
1715
- class="cmw-select-menu cmw-listen" name="<?php echo $this->get_field_name('menu'); ?>">
1716
- <?php echo $menus['names']; ?>
1717
- </select>
1718
- </p>
1719
 
1720
  <?php
1721
- /**
1722
- * start collapsible section : 'Filter'
1723
- */
1724
- $this->cmw_open_a_field_section( $instance, __('Filter'), 'fs_filter' );
1725
  ?>
1726
- <p>
1727
- <?php $this->cmw_assist_link(); ?>
1728
- <label>
1729
- <input id="<?php echo $this->get_field_id('filter'); ?>_0" class="cmw-showall cmw-listen" <?php $this->cmw_disableif(); ?>
1730
- name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="0" <?php checked( $instance['filter'], 0 ); ?> />
1731
- <?php _e('Show all'); ?></label>
1732
- <br /><label>
1733
- <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="cmw-listen" <?php $this->cmw_disableif(); ?>
1734
- name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="1" <?php checked( $instance['filter'], 1 ); ?> />
1735
- <?php _e('Children of:'); ?></label>
1736
- <select id="<?php echo $this->get_field_id('filter_item'); ?>" class="cmw-childrenof cmw-assist-items cmw-listen"
1737
- name="<?php echo $this->get_field_name('filter_item'); ?>" <?php $this->cmw_disableif(); ?>>
1738
- <option value="0" <?php selected( $instance['filter_item'], 0 ); ?>><?php _e('Current Item'); ?></option>
1739
- <option value="-2" <?php selected( $instance['filter_item'], -2 ); ?>><?php _e('Current Root Item'); ?></option>
1740
- <option value="-1" <?php selected( $instance['filter_item'], -1 ); ?>><?php _e('Current Parent Item'); ?></option>
1741
- <?php echo $menus['selectedOptgroup']; ?>
1742
- </select>
1743
- <br /><label>
1744
- <input id="<?php echo $this->get_field_id('filter'); ?>_2" class="cmw-showspecific cmw-listen" <?php $this->cmw_disableif(); ?>
1745
- name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="-1" <?php checked( $instance['filter'], -1 ); ?> />
1746
- <?php _e('Items:'); ?></label>
1747
- <?php $this->cmw_formfield_textbox( $instance, 'items',
1748
- array(
1749
- 'fclass' => 'cmw-setitems'
1750
- ) ); ?>
1751
-
1752
- <select id="<?php echo $this->get_field_id('filter_item_ignore'); ?>" disabled="disabled"
1753
- class='cmw-off-the-page' name="<?php echo $this->get_field_name('filter_item_ignore'); ?>">
1754
- <?php echo $menus['optgroups']; ?>
1755
- </select>
1756
- </p>
1757
-
1758
- <p class="cmw-disableif-ss<?php $this->cmw_disableif( 'push', $isShowSpecific ); ?>">
1759
- <label for="<?php echo $this->get_field_id('start_level'); ?>"><?php _e('Starting Level:'); ?></label>
1760
- <select id="<?php echo $this->get_field_id('start_level'); ?>" <?php $this->cmw_disableif(); ?>
1761
- class="cmw-start-level" name="<?php echo $this->get_field_name('start_level'); ?>">
1762
  <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
1763
- <option value="<?php echo $i; ?>" <?php selected( $instance['start_level'] > $menus['selectedLevels'] ? 1 : $instance['start_level'], $i ); ?>><?php echo $i; ?></option>
1764
  <?php } ?>
1765
- </select>
1766
- <span class="cmw-small-block"><em><?php _e('Level to start testing items for inclusion'); ?></em></span>
1767
- </p><!-- end .cmw-disableif-ss --><?php $this->cmw_disableif( 'pop' ); ?>
1768
-
1769
- <p class="cmw-disableif-ss<?php $this->cmw_disableif( 'push', $isShowSpecific ); ?>">
1770
- <label for="<?php echo $this->get_field_id('depth'); ?>"><?php _e('For Depth:'); ?></label>
1771
- <select id="<?php echo $this->get_field_id('depth'); ?>" class="cmw-depth" data-cmw-text-levels="<?php _e(' levels'); ?>"
1772
- name="<?php echo $this->get_field_name('depth'); ?>" <?php $this->cmw_disableif(); ?>>
1773
- <option value="0" <?php selected( $instance['depth'] > $menus['selectedLevels'] ? 0 : $instance['depth'], 0 ); ?>><?php _e('unlimited'); ?></option>
1774
  <?php
1775
- for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){
1776
  ?>
1777
- <option value="<?php echo $i; ?>" <?php selected( $instance['depth'], $i ); ?>><?php echo $i; ?> <?php _e($i > 1 ? 'levels' : 'level'); ?></option>
1778
  <?php
1779
- }
1780
  ?>
1781
- </select>
1782
- <span class="cmw-small-block"><em><?php _e('Relative to first Filter item found, <strong>unless</strong>&hellip;'); ?></em></span>
1783
- <?php $this->cmw_formfield_checkbox( $instance, 'depth_rel_current',
1784
- array(
1785
- 'label' => sprintf( __('Relative to &quot;Current&quot; Item %1$s(if found)%2$s'), '<small><em>', '</em></small>' )
1786
- ) ); ?>
1787
- </p><!-- end .cmw-disableif-ss --><?php $this->cmw_disableif( 'pop' ); ?>
1788
 
1789
- <?php $this->cmw_close_a_field_section(); ?>
1790
 
1791
  <?php
1792
- /**
1793
- * v1.2.0 start collapsible section : 'Fallbacks'
1794
- */
1795
- $this->cmw_open_a_field_section( $instance, __('Fallbacks'), 'fs_fallbacks' );
1796
  ?>
1797
- <p class="cmw-disableifnot-rp<?php $this->cmw_disableif( 'push', $isNotCurrentRootParent ); ?>">
1798
- <?php $this->cmw_assist_link(); ?>
1799
- <span class="cmw-small-block"><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Root / Parent Item</em>, and no ancestor exists' ); ?> :</strong></span>
1800
- <?php $this->cmw_formfield_checkbox( $instance, 'fallback_no_ancestor',
1801
- array(
1802
- 'label' => __('Switch to Current Item, and')
1803
- ) ); ?>
1804
- <br />
1805
- <?php $this->cmw_formfield_checkbox( $instance, 'fallback_include_parent',
1806
- array(
1807
- 'label' => __('Include Parent...')
1808
- ) ); ?>
1809
- <?php $this->cmw_formfield_checkbox( $instance, 'fallback_include_parent_siblings',
1810
- array(
1811
- 'label' => __('with Siblings'),
1812
- 'lclass' => 'cmw-whitespace-nowrap'
1813
- ) ); ?>
1814
- </p><!-- end .cmw-disableifnot-rp --><?php $this->cmw_disableif( 'pop' ); ?>
1815
-
1816
- <p class="cmw-disableifnot-ci<?php $this->cmw_disableif( 'push', $isNotCurrentItem ); ?>">
1817
- <span class="cmw-small-block"><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Item</em>, and current item has no children' ); ?> :</strong></span>
1818
- <?php $this->cmw_formfield_checkbox( $instance, 'fallback_no_children',
1819
- array(
1820
- 'label' => __('Switch to Current Parent Item, and')
1821
- ) ); ?>
1822
- <br />
1823
- <?php $this->cmw_formfield_checkbox( $instance, 'fallback_nc_include_parent',
1824
- array(
1825
- 'label' => __('Include Parent...')
1826
- ) ); ?>
1827
- <?php $this->cmw_formfield_checkbox( $instance, 'fallback_nc_include_parent_siblings',
1828
- array(
1829
- 'label' => __('with Siblings'),
1830
- 'lclass' => 'cmw-whitespace-nowrap'
1831
- ) ); ?>
1832
- </p><!-- end .cmw-disableifnot-ci --><?php $this->cmw_disableif( 'pop' ); ?>
1833
-
1834
- <?php $this->cmw_close_a_field_section(); ?>
1835
 
1836
  <?php
1837
- /**
1838
- * start collapsible section : 'Output'
1839
- */
1840
- $this->cmw_open_a_field_section( $instance, __('Output'), 'fs_output' );
1841
  ?>
1842
- <p>
1843
- <?php $this->cmw_assist_link(); ?>
1844
- <label>
1845
- <input id="<?php echo $this->get_field_id('flat_output'); ?>_0" name="<?php echo $this->get_field_name('flat_output'); ?>"
1846
- type="radio" value="0" <?php checked(!$instance['flat_output']); ?> <?php $this->cmw_disableif(); ?> />
1847
- <?php _e('Hierarchical'); ?></label>
1848
- &nbsp;<label class="cmw-whitespace-nowrap">
1849
- <input id="<?php echo $this->get_field_id('flat_output'); ?>_1" name="<?php echo $this->get_field_name('flat_output'); ?>"
1850
- type="radio" value="1" <?php checked($instance['flat_output']); ?> <?php $this->cmw_disableif(); ?> />
1851
- <?php _e('Flat'); ?></label>
1852
- </p>
1853
-
1854
- <p>
1855
- <?php $this->cmw_formfield_checkbox( $instance, 'contains_current',
1856
- array(
1857
- 'label' => __('Must Contain &quot;Current&quot; Item'),
1858
- 'desc' => __('Checks both Filtered and Included items')
1859
- ) ); ?>
1860
- </p>
1861
-
1862
- <p class="cmw-disableif<?php $this->cmw_disableif( 'push', $isNotChildrenOf ); ?>">
1863
- <?php $this->cmw_formfield_checkbox( $instance, 'include_parent',
1864
- array(
1865
- 'label' => __('Include Parent...')
1866
- ) ); ?>
1867
- <?php $this->cmw_formfield_checkbox( $instance, 'include_parent_siblings',
1868
- array(
1869
- 'label' => __('with Siblings'),
1870
- 'lclass' => 'cmw-whitespace-nowrap'
1871
- ) ); ?>
1872
- <br />
1873
- <?php $this->cmw_formfield_checkbox( $instance, 'include_ancestors',
1874
- array(
1875
- 'label' => __('Include Ancestors')
1876
- ) ); ?>
1877
- <br />
1878
- <?php $this->cmw_formfield_checkbox( $instance, 'title_from_parent',
1879
- array(
1880
- 'label' => __('Title from Parent'),
1881
- 'desc' => __('Only if the &quot;Children of&quot; Filter returns items')
1882
- ) ); ?>
1883
- </p><!-- end .cmw-disableif --><?php $this->cmw_disableif( 'pop' ); ?>
1884
-
1885
- <p>
1886
- <?php $this->cmw_formfield_checkbox( $instance, 'title_from_current',
1887
- array(
1888
- 'label' => __('Title from &quot;Current&quot; Item'),
1889
- 'desc' => __('Lower priority than &quot;Title from Parent&quot;')
1890
- ) ); ?>
1891
- </p>
1892
-
1893
- <p>
1894
- <?php _e('Change UL to OL:'); ?>
1895
- <br />
1896
- <?php $this->cmw_formfield_checkbox( $instance, 'ol_root',
1897
- array(
1898
- 'label' => __('Top Level')
1899
- ) ); ?>
1900
- &nbsp;
1901
- <?php $this->cmw_formfield_checkbox( $instance, 'ol_sub',
1902
- array(
1903
- 'label' => __('Sub-Levels')
1904
- ) ); ?>
1905
- </p>
1906
 
1907
  <?php
1908
- //v1.1.0 As of WP v3.6, wp_nav_menu() automatically cops out (without outputting any HTML) if there are no items,
1909
- // so the hide_empty option becomes superfluous; however, I'll keep the previous setting (if there was one)
1910
- // in case of reversion to an earlier version of WP...
1911
- if( $this->cmw_wp_version('3.6') ){
1912
  ?>
1913
- <p>
1914
- <?php $this->cmw_formfield_checkbox( $instance, 'hide_empty',
1915
- array(
1916
- 'label' => __('Hide Widget if Empty'),
1917
- 'desc' => __('Prevents any output when no items are found')
1918
- ) ); ?>
1919
- </p>
1920
  <?php }else{ ?>
1921
- <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
1922
- type="hidden" value="<?php echo $instance['hide_empty'] ? '1' : ''; ?>" />
1923
  <?php } ?>
1924
 
1925
- <?php $this->cmw_close_a_field_section(); ?>
1926
 
1927
  <?php
1928
- /**
1929
- * start collapsible section : 'Container'
1930
- */
1931
- $this->cmw_open_a_field_section( $instance, __('Container'), 'fs_container' );
1932
  ?>
1933
- <p>
1934
- <?php $this->cmw_formfield_textbox( $instance, 'container',
1935
- array(
1936
- 'label' => __('Element:'),
1937
- 'desc' => __('Eg. div or nav; leave empty for no container')
1938
- ) ); ?>
1939
- </p>
1940
- <p>
1941
- <?php $this->cmw_formfield_textbox( $instance, 'container_id',
1942
- array(
1943
- 'label' => __('Unique ID:'),
1944
- 'desc' => __('An optional ID for the container')
1945
- ) ); ?>
1946
- </p>
1947
- <p>
1948
- <?php $this->cmw_formfield_textbox( $instance, 'container_class',
1949
- array(
1950
- 'label' => __('Class:'),
1951
- 'desc' => __('Extra class for the container')
1952
- ) ); ?>
1953
- </p>
1954
-
1955
- <?php $this->cmw_close_a_field_section(); ?>
1956
 
1957
  <?php
1958
- /**
1959
- * start collapsible section : 'Classes'
1960
- */
1961
- $this->cmw_open_a_field_section( $instance, __('Classes'), 'fs_classes' );
1962
  ?>
1963
- <p>
1964
- <?php $this->cmw_formfield_textbox( $instance, 'menu_class',
1965
- array(
1966
- 'label' => __('Menu Class:'),
1967
- 'desc' => __('Class for the list element forming the menu')
1968
- ) ); ?>
1969
- </p>
1970
- <p>
1971
- <?php $this->cmw_formfield_textbox( $instance, 'widget_class',
1972
- array(
1973
- 'label' => __('Widget Class:'),
1974
- 'desc' => __('Extra class for the widget itself')
1975
- ) ); ?>
1976
- </p>
1977
-
1978
- <?php $this->cmw_close_a_field_section(); ?>
1979
 
1980
  <?php
1981
- /**
1982
- * start collapsible section : 'Links'
1983
- */
1984
- $this->cmw_open_a_field_section( $instance, __('Links'), 'fs_links' );
1985
  ?>
1986
- <p>
1987
- <?php $this->cmw_formfield_textbox( $instance, 'before',
1988
- array(
1989
- 'label' => __('Before the Link:'),
1990
- 'desc' => __( htmlspecialchars('Text/HTML to go before the <a> of the link') ),
1991
- 'fclass' => 'widefat'
1992
- ) ); ?>
1993
- </p>
1994
- <p>
1995
- <?php $this->cmw_formfield_textbox( $instance, 'after',
1996
- array(
1997
- 'label' => __('After the Link:'),
1998
- 'desc' => __( htmlspecialchars('Text/HTML to go after the <a> of the link') ),
1999
- 'fclass' => 'widefat'
2000
- ) ); ?>
2001
- </p>
2002
- <p>
2003
- <?php $this->cmw_formfield_textbox( $instance, 'link_before',
2004
- array(
2005
- 'label' => __('Before the Link Text:'),
2006
- 'desc' => __('Text/HTML to go before the link text'),
2007
- 'fclass' => 'widefat'
2008
- ) ); ?>
2009
- </p>
2010
- <p>
2011
- <?php $this->cmw_formfield_textbox( $instance, 'link_after',
2012
- array(
2013
- 'label' => __('After the Link Text:'),
2014
- 'desc' => __('Text/HTML to go after the link text'),
2015
- 'fclass' => 'widefat'
2016
- ) ); ?>
2017
- </p>
2018
-
2019
- <?php $this->cmw_close_a_field_section(); ?>
2020
 
2021
  </div>
2022
  <?php
2023
 
2024
- } //end cmw_legacy_form()
2025
-
2026
- /**
2027
- * sanitizes the widget settings for cmw_legacy_update(), cmw_legacy_widget() and cmw_legacy_form()
2028
- *
2029
- * @param array $from_instance Widget settings
2030
- * @param array $base_instance Old widget settings or an empty array
2031
- * @param string $method Name suffix of the calling method
2032
- * @return array Sanitized widget settings
2033
- */
2034
- public function cmw_legacy_settings( $from_instance, $base_instance, $method = 'update' ){
2035
-
2036
- $instance = is_array( $base_instance ) ? $base_instance : array();
2037
-
2038
- //switches...
2039
- foreach( array(
2040
- 'hide_title' => 0,
2041
- 'contains_current' => 0, //v2.0.0 added
2042
- 'depth_rel_current' => 0, //v2.0.0 added
2043
- 'fallback_no_ancestor' => 0, //v1.1.0 added
2044
- 'fallback_include_parent' => 0, //v1.1.0 added
2045
- 'fallback_include_parent_siblings' => 0, //v1.1.0 added
2046
- 'fallback_no_children' => 0, //v1.2.0 added
2047
- 'fallback_nc_include_parent' => 0, //v1.2.0 added
2048
- 'fallback_nc_include_parent_siblings' => 0, //v1.2.0 added
2049
- 'flat_output' => 0,
2050
- 'include_parent' => 0,
2051
- 'include_parent_siblings' => 0, //v1.1.0 added
2052
- 'include_ancestors' => 0,
2053
- 'hide_empty' => 0, //v1.1.0: this now only has relevance prior to WP v3.6
2054
- 'title_from_parent' => 0,
2055
- 'title_from_current' => 0, //v1.2.0 added
2056
- 'ol_root' => 0,
2057
- 'ol_sub' => 0,
2058
- //field section toggles...
2059
- 'fs_filter' => 0,
2060
- 'fs_fallbacks' => 1, //v1.2.0 added
2061
- 'fs_output' => 1,
2062
- 'fs_container' => 1,
2063
- 'fs_classes' => 1,
2064
- 'fs_links' => 1
2065
- ) as $k => $v ){
2066
-
2067
- if( $method == 'form' ){
2068
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? !empty( $from_instance[ $k ] ) : !empty( $v );
2069
- }elseif( $method == 'widget' ){
2070
- $instance[ $k ] = !empty( $from_instance[ $k ] );
2071
- }else{
2072
- $instance[ $k ] = empty( $from_instance[ $k ] ) ? 0 : 1;
2073
- }
2074
-
2075
- }
2076
-
2077
- //strings...
2078
- foreach( array(
2079
- 'title' => '',
2080
- 'items' => '', //v2.0.0 added
2081
- 'container' => 'div',
2082
- 'container_id' => '',
2083
- 'container_class' => '',
2084
- 'menu_class' => 'menu-widget',
2085
- 'widget_class' => ''
2086
- ) as $k => $v ){
2087
-
2088
- if( $method == 'form' ){
2089
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? esc_attr( trim( $from_instance[ $k ] ) ) : $v;
2090
- }elseif( $method == 'widget' ){
2091
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( $from_instance[ $k ] ) : $v; //bug in 2.0.2 fixed!
2092
- }else{
2093
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? strip_tags( trim( $from_instance[ $k ] ) ) : $v;
2094
- }
2095
-
2096
- }
2097
-
2098
- //html strings...
2099
- foreach( array(
2100
- 'before' => '',
2101
- 'after' => '',
2102
- 'link_before' => '',
2103
- 'link_after' => ''
2104
- ) as $k => $v ){
2105
-
2106
- if( $method == 'form' ){
2107
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? esc_html( trim( $from_instance[ $k ] ) ) : $v;
2108
- }elseif( $method == 'widget' ){
2109
- $instance[ $k ] = empty( $from_instance[ $k ] ) ? $v : trim( $from_instance[ $k ] );
2110
- }else{
2111
- $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( $from_instance[ $k ] ) : $v;
2112
- }
2113
-
2114
- }
2115
-
2116
- //integers...
2117
- foreach( array(
2118
- 'depth' => 0,
2119
- 'filter' => -1, //v2.0.0 changed from switch
2120
- 'filter_item' => -2, //v1.1.0 changed from 0
2121
- 'menu' => 0,
2122
- 'start_level' => 1
2123
- ) as $k => $v ){
2124
-
2125
- if( $method == 'form' ){
2126
- $instance[ $k ] = isset( $from_instance[ $k ]) ? max( $v, intval( $from_instance[ $k ] ) ) : max($v, 0);
2127
- }elseif( $method == 'widget' ){
2128
- $instance[ $k ] = max( $v, intval( $from_instance[ $k ] ) );
2129
- }else{
2130
- $instance[ $k ] = isset( $from_instance[ $k ]) ? max( $v, intval( $from_instance[ $k ] ) ) : $v;
2131
- }
2132
-
2133
- }
2134
-
2135
- //items special case...
2136
- if( $method == 'update' && !empty( $instance['items'] ) ){
2137
- $sep = preg_match( '/(^\d+$|,)/', $instance['items'] ) > 0 ? ',' : ' ';
2138
- $a = array();
2139
- foreach( preg_split('/[,\s]+/', $instance['items'], -1, PREG_SPLIT_NO_EMPTY ) as $v ){
2140
- $i = intval( $v );
2141
- if( $i > 0 ){
2142
- $a[] = $i;
2143
- }
2144
- }
2145
- $instance['items'] = implode( $sep, $a );
2146
- }
2147
-
2148
- //v1.2.1 holds information determined by the walker...
2149
- $this->_cmw_walker = array();
2150
-
2151
- return $instance;
2152
-
2153
- } //end cmw_legacy_settings()
2154
-
2155
- /**
2156
- * updates the widget settings sent from the legacy backend admin
2157
- *
2158
- * @filters : custom_menu_wizard_prevent_legacy_updates false
2159
- * @filters : custom_menu_wizard_wipe_on_update false
2160
- *
2161
- * @param array $new_instance New widget settings
2162
- * @param array $old_instance Old widget settings
2163
- * @return array Sanitized widget settings
2164
- */
2165
- public function cmw_legacy_update( $new_instance, $old_instance ){
2166
-
2167
- //allow a filter to return true, whereby updates to legacy widgets are disallowed...
2168
- //eg. apply_filter( 'custom_menu_wizard_prevent_legacy_updates', [filter function], 10, 1 ) => true
2169
- if( !apply_filters( 'custom_menu_wizard_prevent_legacy_updates', false ) ){
2170
- return $this->cmw_legacy_settings(
2171
- $new_instance,
2172
- //allow a filter to return true, whereby any previous settings (now possibly unused) will be wiped instead of being allowed to remain...
2173
- //eg. add_filter( 'custom_menu_wizard_wipe_on_update', [filter_function], 10, 1 ) => true
2174
- apply_filters( 'custom_menu_wizard_wipe_on_update', false ) ? array() : $old_instance,
2175
- 'update' );
2176
- }else{
2177
- //prevent the save!...
2178
- return false;
2179
- }
2180
-
2181
- } //end cmw_legacy_update()
2182
-
2183
- /**
2184
- * produces the legacy widget HTML at the front end
2185
- *
2186
- * @filters : custom_menu_wizard_nav_params array of params that will be sent to wp_nav_menu(), array of instance settings, id base
2187
- * custom_menu_wizard_settings_pre_widget array of instance settings, id base
2188
- * custom_menu_wizard_widget_output HTML output string, array of instance settings, id base, $args
2189
- *
2190
- * @param object $args Widget arguments
2191
- * @param array $instance Configuration for this widget instance
2192
- */
2193
- public function cmw_legacy_widget( $args, $instance ) {
2194
-
2195
- //sanitize $instance...
2196
- $instance = $this->cmw_legacy_settings( $instance, array(), 'widget' );
2197
-
2198
- //v1.1.0 As of WP v3.6, wp_nav_menu() automatically prevents any HTML output if there are no items...
2199
- $instance['hide_empty'] = $instance['hide_empty'] && $this->cmw_wp_version('3.6');
2200
-
2201
- //allow a filter to amend the instance settings prior to producing the widget output...
2202
- //eg. add_filter( 'custom_menu_wizard_settings_pre_widget', [filter_function], 10, 2 ) => $instance (array)
2203
- $instance = apply_filters( 'custom_menu_wizard_settings_pre_widget', $instance, $this->id_base );
2204
-
2205
- //fetch menu...
2206
- if( !empty($instance['menu'] ) ){
2207
- $menu = wp_get_nav_menu_object( $instance['menu'] );
2208
-
2209
- //no menu, no output...
2210
- if ( !empty( $menu ) ){
2211
-
2212
- if( !empty( $instance['widget_class'] ) ){
2213
- //$args['before_widget'] is usually just a DIV start-tag, with an id and a class; if it
2214
- //gets more complicated than that then this may not work as expected...
2215
- if( preg_match( '/^<[^>]+?class=["\']/', $args['before_widget'] ) > 0 ){
2216
- $args['before_widget'] = preg_replace( '/(class=["\'])/', '$1' . $instance['widget_class'] . ' ', $args['before_widget'], 1 );
2217
- }else{
2218
- $args['before_widget'] = preg_replace( '/^(<\w+)(\s|>)/', '$1 class="' . $instance['widget_class'] . '"$2', $args['before_widget'] );
2219
- }
2220
- }
2221
-
2222
- if( !empty( $instance['container_class'] ) ){
2223
- $instance['container_class'] = "menu-{$menu->slug}-container {$instance['container_class']}";
2224
- }
2225
-
2226
- $instance['menu_class'] = preg_split( '/\s+/', $instance['menu_class'], -1, PREG_SPLIT_NO_EMPTY );
2227
- if( $instance['fallback_no_ancestor'] || $instance['fallback_no_children'] ){
2228
- //v1.2.1 add a cmw-fellback-maybe class to the menu and we'll remove or replace it later...
2229
- $instance['menu_class'][] = 'cmw-fellback-maybe';
2230
- }
2231
- $instance['menu_class'] = implode( ' ', $instance['menu_class'] );
2232
-
2233
- $walker = new Custom_Menu_Wizard_Walker;
2234
- $params = array(
2235
- 'menu' => $menu,
2236
- 'container' => empty( $instance['container'] ) ? false : $instance['container'], //bug in 2.0.2 fixed!
2237
- 'container_id' => $instance['container_id'],
2238
- 'menu_class' => $instance['menu_class'],
2239
- 'echo' => false,
2240
- 'fallback_cb' => false,
2241
- 'before' => $instance['before'],
2242
- 'after' => $instance['after'],
2243
- 'link_before' => $instance['link_before'],
2244
- 'link_after' => $instance['link_after'],
2245
- 'depth' => empty( $instance['flat_output'] ) ? $instance['depth'] : -1,
2246
- 'walker' =>$walker,
2247
- //widget specific stuff...
2248
- '_custom_menu_wizard' => $instance
2249
- );
2250
- //for the walker's use...
2251
- $params['_custom_menu_wizard']['_walker'] = array();
2252
-
2253
- if( $instance['ol_root'] ){
2254
- $params['items_wrap'] = '<ol id="%1$s" class="%2$s">%3$s</ol>';
2255
- }
2256
- if( !empty( $instance['container_class'] ) ){
2257
- $params['container_class'] = $instance['container_class'];
2258
- }
2259
-
2260
- add_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
2261
- if( $instance['hide_empty'] ){
2262
- add_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
2263
- }
2264
-
2265
- //allow a filter to amend the wp_nav_menu() params prior to calling it...
2266
- //eg. add_filter( 'custom_menu_wizard_nav_params', [filter_function], 10, 3 ) => $params (array)
2267
- //NB: wp_nav_menu() is in wp-includes/nav-menu-template.php
2268
- $out = wp_nav_menu( apply_filters( 'custom_menu_wizard_nav_params', $params, $instance, $this->id_base ) );
2269
-
2270
- remove_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
2271
- if( $instance['hide_empty'] ){
2272
- remove_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
2273
- }
2274
-
2275
- //only put something out if there is something to put out...
2276
- if( !empty( $out ) ){
2277
-
2278
- //title from : 'from parent' has priority over 'from current'...
2279
- //note that 'parent' is whatever you are getting the children of and therefore doesn't apply to a ShowAll, whereas
2280
- //'current' is the current menu item (as determined by WP); also note that neither parent nor current actually has
2281
- //to be present in the results
2282
- if( $instance['title_from_parent'] && !empty( $this->_cmw_walker['parent_title'] ) ){
2283
- $title = $this->_cmw_walker['parent_title'];
2284
- }
2285
- if( empty( $title ) && $instance['title_from_current'] && !empty( $this->_cmw_walker['current_title'] ) ){
2286
- $title = $this->_cmw_walker['current_title'];
2287
- }
2288
- if( empty( $title ) ){
2289
- $title = $instance['hide_title'] ? '' : $instance['title'];
2290
- }
2291
-
2292
- //remove/replace the cmw-fellback-maybe class...
2293
- $out = str_replace(
2294
- 'cmw-fellback-maybe',
2295
- empty( $this->_cmw_walker['fellback'] ) ? '' : 'cmw-fellback-' . $this->_cmw_walker['fellback'],
2296
- $out );
2297
-
2298
- if ( !empty($title) ){
2299
- $out = $args['before_title'] . apply_filters('widget_title', $title, $instance, $this->id_base) . $args['after_title'] . $out;
2300
- }
2301
- $out = $args['before_widget'] . $out . $args['after_widget'];
2302
- //allow a filter to modify the entire output...
2303
- //eg. add_filter( 'custom_menu_wizard_widget_output', [filter_function], 10, 4 ) => $output (HTML string)
2304
- echo apply_filters( 'custom_menu_wizard_widget_output', $out, $instance, $this->id_base, $args );
2305
- }
2306
- }
2307
- }
2308
-
2309
- } //end cmw_legacy_widget()
2310
 
2311
  } //end of class
2312
  ?>
6
  */
7
  class Custom_Menu_Wizard_Widget extends WP_Widget {
8
 
9
+ /**
10
+ * class constructor
11
+ */
12
+ public function __construct() {
13
+
14
+ parent::__construct(
15
+ 'custom-menu-wizard',
16
+ 'Custom Menu Wizard',
17
+ array(
18
+ 'classname' => 'widget_custom_menu_wizard',
19
+ 'description' => __('Add a custom menu, or part of one, as a widget'),
20
+ 'customizer_support' => true
21
+ )
22
+ );
23
+ $this->_cmw_legacy_warnreadmore = 'http://wordpress.org/plugins/' . $this->id_base . '/changelog/';
24
+ //accessibility mode doesn't necessarily mean that javascript is disabled, but if javascript *is* disabled
25
+ //then accessibility mode *will* be on...
26
+ $this->_cmw_accessibility = isset( $_GET['editwidget'] ) && $_GET['editwidget'];
27
+ $this->_cmw_hash_ct = 0;
28
+
29
+ } //end __construct()
30
+
31
+ /**
32
+ * produces the backend admin form(s)
33
+ *
34
+ * @param array $instance Widget settings
35
+ */
36
+ public function form( $instance ) {
37
+
38
+ //raised June 2014 : problem...
39
+ //using the widget_form_callback filter (as Widget Title Links plugin does, which raised the issue) it is perfectly
40
+ //possible for $instance to be non-empty - with any number of properties - for a new widget! The widget_form_callback
41
+ //filter allows any other plugin to add fields to $instance *before* the actual widget itself gets a chance to (and
42
+ //returning false from that filter will prevent the widget ever being called, but not relevant here).
43
+ //(ref: WP_Widget::form_callback() in wp-includes/widgets.php)
44
+ //this means that I can't rely on a !empty($instance) test being indicative of an existing widget (because it could be
45
+ //a new widget but filtered with widget_form_callback).
46
+ //So, I have changed the "legacy" test from
47
+ // if( !empty( $instance ) && empty( $instance['cmwv'] ) ){
48
+ //to
49
+ // if( is_numeric( $this->number ) && $this->number > 0 && empty( $instance['cmwv'] ) ){
50
+ //(checking for $this->number > 0 is probably overkill but it doesn't hurt)
51
+ //Note that this could still be circumvented by some other plugin using the widget_form_callback filter to set 'cmwv',
52
+ //but I can't do anything about that!
53
+
54
+ //only call the legacy form method if the widget has a number (ie. this instance has been saved, could be either active
55
+ // or inactive) and it does *not* have a version number ('cmwv') set in $instance...
56
+ if( is_numeric( $this->number ) && $this->number > 0 && empty( $instance['cmwv'] ) ){
57
+ $this->cmw_legacy_form( $instance );
58
+ return;
59
+ }
60
+
61
+ //sanitize $instance...
62
+ $instance = self::cmw_settings( $instance, array(), __FUNCTION__ );
63
+
64
+ //if no populated menus exist, suggest the user go create one...
65
+ if( ( $menus = $this->cmw_scan_menus( $instance['menu'], $instance['branch'] ) ) === false ){
66
  ?>
67
  <p class="widget-<?php echo $this->id_base; ?>-no-menus">
68
+ <em><?php printf( __('No populated menus have been created yet! <a href="%s">Create one...</a>'), admin_url('nav-menus.php') ); ?></em>
69
+ <input id="<?php echo $this->get_field_id('cmwv'); ?>" name="<?php echo $this->get_field_name('cmwv'); ?>"
70
+ type="hidden" value="<?php echo Custom_Menu_Wizard_Plugin::$version; ?>" />
71
+ <?php foreach( array('filters', 'fallbacks', 'output', 'container', 'classes', 'links') as $v ){ ?>
72
+ <input id="<?php echo $this->get_field_id("fs_$v"); ?>" name="<?php echo $this->get_field_name("fs_$v"); ?>"
73
+ type="hidden" value="<?php echo $instance["fs_$v"] ? '1' : '0' ?>" />
74
+ <?php } ?>
75
  </p>
76
  <?php
77
+ //all done : quit...
78
+ return;
79
+ }
80
+
81
+ //create the OPTIONs for the relative & absolute optgroups in the branch level select...
82
+ $absGroup = array();
83
+ $relGroup = array();
84
+ if( empty( $instance['branch_start'] ) ){
85
+ $branchLevel = '';
86
+ }else{
87
+ $branchLevel = $instance['branch_start'];
88
+ $i = substr( $branchLevel, 0, 1 );
89
+ //is it currently set relative?...
90
+ if( $i == '+' || $i == '-' ){
91
+ //if we only have 1 level then set to branch item...
92
+ if( $menus['selectedLevels'] < 2 ){
93
+ $branchLevel = '';
94
+ //otherwise, limit the 'relativeness' to 1 less than the number of levels
95
+ //available (ie. 5 levels available gives a range -4 thru to +4)...
96
+ }elseif( abs( intval( $branchLevel ) ) > $menus['selectedLevels'] - 1 ){
97
+ $branchLevel = $i . ($menus['selectedLevels'] - 1);
98
+ }
99
+ //max an absolute against the number of levels available...
100
+ }elseif( intval( $branchLevel ) > $menus['selectedLevels'] ){
101
+ $branchLevel = $menus['selectedLevels'];
102
+ }
103
+ }
104
+ //start with the middle option of the relatives (the level of the branch item)...
105
+ $relGroup[] = '<option value="" ' . selected( $branchLevel, '', false ) . '>' . __('the Branch') . '</option>';
106
+ //now do the absolutes and relatives...
107
+ for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){
108
+ //topmost of the absolutes gets ' (root)' appended to its descriptor...
109
+ $t = $i == 1 ? "$i (" . __('root') . ')' : $i;
110
+ //append to the absolutes...
111
+ $absGroup[] = '<option value="' . $i . '" ' . selected( $branchLevel, "$i", false ) . '>' . $t . '</option>';
112
+ //for anything LESS THAN the number of levels...
113
+ if( $i < $menus['selectedLevels'] ){
114
+ //immediately above the branch item gets ' (parent)' appended to its descriptor...
115
+ $t = $i == 1 ? "-$i (" . __('parent') . ')' : "-$i";
116
+ //prepend to the relatives...
117
+ array_unshift( $relGroup, '<option value="-' . $i . '" ' . selected( $branchLevel, "-$i", false ) . '>' . $t . '</option>' );
118
+ //immediately below the branch item gets ' (children)' appended to its descriptor...
119
+ $t = $i == 1 ? "+$i (" . __('children') . ')' : "+$i";
120
+ //append to the relatives...
121
+ array_push( $relGroup, '<option value="+' . $i . '" ' . selected( $branchLevel, "+$i", false ) . '>' . $t . '</option>' );
122
+ }
123
+ }
124
+
125
+ //set up some simple booleans for use at the disableif___ classes...
126
+ $isByItems = $instance['filter'] == 'items'; // disableif-ss (IS Items filter)
127
+ $isUnlimitedDepth = $isByItems || empty( $instance['depth'] ); // disableif-ud (IS unlimited depth)
128
+ $isNotByBranch = $instance['filter'] != 'branch'; // disableifnot-br (is NOT Branch filter)
129
+ $isNotBranchCurrentItem = $isNotByBranch || !empty( $instance['branch'] ); // disableifnot-br-ci (is NOT "Branch:Current Item")
130
+ $isNotFallbackParentCurrent = $isNotBranchCurrentItem || !in_array( $instance['fallback'], array('parent', 'current') ); //disableifnot-fb-pc (is NOT set to fall back to parent or current)
131
+ $isNotSwitchable = empty( $instance['switch_if'] ) || empty( $instance['switch_at'] ); // disableifnot-sw (missing either the condition of the processing stage)
132
+
133
+ //NB the 'onchange' wrapper holds any text required by the "assist"
134
  ?>
135
  <div id="<?php echo $this->get_field_id('onchange'); ?>"
136
+ class="widget-<?php echo $this->id_base; ?>-onchange<?php echo $this->cmw_wp_version('3.8') ? ' cmw-pre-wp-v38' : ''; ?>"
137
+ data-cmw-v36plus='<?php echo $this->cmw_wp_version('3.6', true) ? 'true' : 'false'; ?>'
138
+ data-cmw-dialog-prompt='<?php _e('Click an item to toggle &quot;Current Menu Item&quot;'); ?>'
139
+ data-cmw-dialog-output='<?php _e('Basic Output'); ?>'
140
+ data-cmw-dialog-alternative='<?php _e('Alternative settings'); ?>'
141
+ data-cmw-dialog-fallback='<?php _e('Fallback invoked'); ?>'
142
+ data-cmw-dialog-inclusions='<?php _e('Inclusions : 0'); ?>'
143
+ data-cmw-dialog-exclusions='<?php _e('Exclusions : 0'); ?>'
144
+ data-cmw-dialog-set-current='<?php _e('No Current Item!'); ?>'
145
+ data-cmw-dialog-shortcodes='<?php _e('Find posts/pages containing a CMW shortcode'); ?>'
146
+ data-cmw-dialog-untitled='<?php _e('untitled'); ?>'
147
+ data-cmw-dialog-fixed='<?php _e('fixed'); ?>'
148
+ data-cmw-dialog-nonce='<?php echo wp_create_nonce( 'cmw-find-shortcodes' ); ?>'
149
+ data-cmw-dialog-version='<?php echo Custom_Menu_Wizard_Plugin::$version; ?>'
150
+ data-cmw-dialog-id='<?php echo $this->get_field_id('dialog'); ?>'>
151
 
152
  <?php
153
+ /**
154
+ * permanently visible section : Title (with Hide) and Menu
155
+ */
156
  ?>
157
+ <p>
158
+ <input id="<?php echo $this->get_field_id('cmwv'); ?>" name="<?php echo $this->get_field_name('cmwv'); ?>"
159
+ type="hidden" value="<?php echo Custom_Menu_Wizard_Plugin::$version; ?>" />
160
+ <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:') ?></label>
161
+ <?php $this->cmw_formfield_checkbox( $instance, 'hide_title',
162
+ array(
163
+ 'label' => __('Hide'),
164
+ 'lclass' => 'alignright'
165
+ ) ); ?>
166
+ <?php $this->cmw_formfield_textbox( $instance, 'title',
167
+ array(
168
+ 'fclass' => 'widefat cmw-widget-title'
169
+ ) ); ?>
170
+ </p>
171
+
172
+ <p>
173
+ <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
174
+ <select id="<?php echo $this->get_field_id('menu'); ?>"
175
+ class="cmw-select-menu cmw-listen" name="<?php echo $this->get_field_name('menu'); ?>">
176
+ <?php echo $menus['names']; ?>
177
+ </select>
178
+ </p>
179
 
180
  <?php
181
+ /**
182
+ * start collapsible section : 'Filter'
183
+ */
184
+ $this->cmw_open_a_field_section( $instance, __('Filters'), 'fs_filters' ); ?>
185
+
186
+ <div>
187
+ <?php $this->cmw_assist_link(); ?>
188
+ <strong><?php _e('Primary Filter'); ?></strong>
189
+
190
+ <div class="cmw-indented">
191
+ <label class="cmw-verticalalign-baseline">
192
+ <input id="<?php echo $this->get_field_id('filter'); ?>_0" class="cmw-bylevel cmw-listen"
193
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('filter'); ?>"
194
+ type="radio" value="" <?php checked( $instance['filter'], '' ); ?>
195
+ /><?php _e('Level:'); ?></label>
196
+ <select id="<?php echo $this->get_field_id('level'); ?>" class="cmw-level cmw-set-levels cmw-listen"
197
+ <?php $this->cmw_disableif(); ?> data-cmw-set-levels="0"
198
+ name="<?php echo $this->get_field_name('level'); ?>">
199
+ <?php for( $i = 1, $j = $instance['level'] > $menus['selectedLevels'] ? 1 : $instance['level']; $i <= $menus['selectedLevels']; $i++ ){ ?>
200
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php echo $i > 1 ? $i : $i . __(' (root)'); ?></option>
201
+ <?php } ?>
202
+ </select>
203
+ </div>
204
+
205
+ <div class="cmw-indented">
206
+ <label class="cmw-verticalalign-baseline">
207
+ <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="cmw-bybranch cmw-listen"
208
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('filter'); ?>"
209
+ type="radio" value="branch" <?php checked( $instance['filter'], 'branch' ); ?>
210
+ /><?php _e('Branch:'); ?></label>
211
+ <select id="<?php echo $this->get_field_id('branch'); ?>" class="cmw-branches cmw-assist-items cmw-listen"
212
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('branch'); ?>">
213
+ <option value="0" <?php selected( $instance['branch'], 0 ); ?>><?php _e('Current Item'); ?></option>
214
+ <?php echo $menus['selectedOptgroup']; ?>
215
+ </select>
216
+ <select id="<?php echo $this->get_field_id('branch_ignore'); ?>" class='cmw-off-the-page' disabled="disabled"
217
+ name="<?php echo $this->get_field_name('branch_ignore'); ?>">
218
+ <?php echo $menus['optgroups']; ?>
219
+ </select>
220
+ </div>
221
+
222
+ <div class="cmw-indented">
223
+ <label class="cmw-verticalalign-baseline">
224
+ <input id="<?php echo $this->get_field_id('filter'); ?>_2" class="cmw-byitems cmw-listen"
225
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('filter'); ?>"
226
+ type="radio" value="items" <?php checked( $instance['filter'], 'items' ); ?>
227
+ /><?php _e('Items:'); ?></label>
228
+ <?php $this->cmw_formfield_textbox( $instance, 'items',
229
+ array(
230
+ 'fclass' => 'cmw-maxwidth-twothirds cmw-setitems cmw-listen'
231
+ ) ); ?>
232
+ </div>
233
+ </div>
234
+
235
+ <div class="cmw-disableif-ss<?php $this->cmw_disableif( 'push', $isByItems ); ?>">
236
+ <?php $this->cmw_assist_link(); ?>
237
+ <strong><?php _e('Secondary Filter'); ?></strong>
238
+
239
+ <div class="cmw-indented">
240
+ <label class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>"><?php _e('Starting at:'); ?>
241
+ <select id="<?php echo $this->get_field_id('branch_start'); ?>" class="cmw-branch-start cmw-listen"
242
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('branch_start'); ?>">
243
+ <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-children="<?php _e('children'); ?>"
244
+ data-cmw-text-parent="<?php _e('parent'); ?>">
245
+ <?php echo implode( '', $relGroup ); ?>
246
+ </optgroup>
247
+ <optgroup label="<?php _e('absolute...'); ?>">
248
+ <?php echo implode( '', $absGroup ); ?>
249
+ </optgroup>
250
+ </select></label><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
251
+
252
+ <br />
253
+ <span class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>">
254
+ <label class="cmw-followed-by">
255
+ <input id="<?php echo $this->get_field_id('start_mode'); ?>_0"
256
+ name="<?php echo $this->get_field_name('start_mode'); ?>"
257
+ <?php $this->cmw_disableif(); ?> type="radio" value="" <?php checked( $instance['start_mode'] !== 'level' ); ?>
258
+ /><?php printf( __('Item %1$s(if possible)%2$s'), '<small>', '</small>' ); ?></label>
259
+
260
+ <label class="cmw-followed-by cmw-whitespace-nowrap">
261
+ <input id="<?php echo $this->get_field_id('start_mode'); ?>_1" name="<?php echo $this->get_field_name('start_mode'); ?>"
262
+ <?php $this->cmw_disableif(); ?> type="radio" value="level" <?php checked( $instance['start_mode'] === 'level' ); ?>
263
+ /><?php _e('Level'); ?></label>
264
+
265
+ <?php $this->cmw_formfield_checkbox( $instance, 'allow_all_root',
266
+ array(
267
+ 'label' => __('Allow all Root Items'),
268
+ 'lclass' => 'cmw-whitespace-nowrap'
269
+ ) ); ?>
270
+ </span><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
271
+ </div>
272
+
273
+ <div class="cmw-indented">
274
+ <label class="cmw-followed-by"><?php _e('For Depth:'); ?>
275
+ <select id="<?php echo $this->get_field_id('depth'); ?>" data-cmw-text-levels="<?php _e(' levels'); ?>"
276
+ data-cmw-set-levels="1" <?php $this->cmw_disableif(); ?>
277
+ class="cmw-depth cmw-set-levels cmw-listen" name="<?php echo $this->get_field_name('depth'); ?>">
278
+ <option value="0" <?php selected( $instance['depth'] > $menus['selectedLevels'] ? 0 : $instance['depth'], 0 ); ?>><?php _e('unlimited'); ?></option>
279
+ <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
280
+ <option value="<?php echo $i; ?>" <?php selected( $instance['depth'], $i ); ?>><?php printf( _n('%d level', '%d levels', $i), $i ); ?></option>
281
+ <?php } ?>
282
+ </select></label>
283
+
284
+
285
+ <?php $this->cmw_formfield_checkbox( $instance, 'depth_rel_current',
286
+ array(
287
+ 'label' => __('Relative to Current Item'),
288
+ 'lclass' => 'cmw-disableif-ud cmw-whitespace-nowrap',
289
+ 'disableif' => $isUnlimitedDepth
290
+ ) ); ?>
291
+ </div>
292
+ </div><!-- end .cmw-disableif-ss --><?php $this->cmw_disableif( 'pop' ); ?>
293
+
294
+ <div>
295
+ <?php $this->cmw_assist_link(); ?>
296
+ <strong><?php _e('Inclusions'); ?></strong>
297
+
298
+ <div class="cmw-indented">
299
+ <label class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>"><?php _e('Branch Ancestors:'); ?>
300
+ <select id="<?php echo $this->get_field_id('ancestors'); ?>" class="cmw-ancestors cmw-set-rel-abs-levels cmw-listen"
301
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('ancestors'); ?>"
302
+ data-cmw-text-tolevel="<?php _e('to level'); ?>">
303
+ <option value="0" <?php selected( $j, 0 ); ?>>&nbsp;</option>
304
+ <?php
305
+ $j = $instance['ancestors'];
306
+ $j = max( min( $j, $menus['selectedLevels'] - 1 ), 1 - $menus['selectedLevels'] ); ?>
307
+ <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-for-option=" <?php _e('%d levels'); ?>">
308
+ <option value="-1" <?php selected( $j, -1 ); ?>><?php printf( __('%d level (parent)'), -1 ); ?></option>
309
+ <?php for( $i = -2; $i > 0 - $menus['selectedLevels']; $i-- ){ ?>
310
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('%d levels'), $i ); ?></option>
311
+ <?php } ?>
312
+ </optgroup>
313
+ <optgroup label="<?php _e('absolute...'); ?>" data-cmw-text-for-option="<?php _e('to level %d'); ?> ">
314
+ <option value="1" <?php selected( $j, 1 ); ?>><?php printf( __('to level %d (root)'), 1 ); ?></option>
315
+ <?php for( $i = 2; $i < $menus['selectedLevels']; $i++ ){ ?>
316
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('to level %d'), $i ); ?></option>
317
+ <?php } ?>
318
+ </optgroup>
319
+ </select></label><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
320
+
321
+ <br />
322
+ <span class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>">
323
+ <label><?php _e('... with Siblings:'); ?>
324
+ <select id="<?php echo $this->get_field_id('ancestor_siblings'); ?>" class="cmw-ancestor-siblings cmw-set-rel-abs-levels cmw-listen"
325
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('ancestor_siblings'); ?>">
326
+ <option value="0" <?php selected( $j, 0 ); ?>>&nbsp;</option>
327
+ <?php
328
+ $j = $instance['ancestor_siblings'];
329
+ $j = max( min( $j, $menus['selectedLevels'] - 1 ), 1 - $menus['selectedLevels'] ); ?>
330
+ <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-for-option=" <?php _e('%d levels'); ?>">
331
+ <option value="-1" <?php selected( $j, -1 ); ?>><?php printf( __('%d level (parent)'), -1 ); ?></option>
332
+ <?php for( $i = -2; $i > 0 - $menus['selectedLevels']; $i-- ){ ?>
333
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('%d levels'), $i ); ?></option>
334
+ <?php } ?>
335
+ </optgroup>
336
+ <optgroup label="<?php _e('absolute...'); ?>" data-cmw-text-for-option="<?php _e('to level %d'); ?> ">
337
+ <option value="1" <?php selected( $j, 1 ); ?>><?php printf( __('to level %d (root)'), 1 ); ?></option>
338
+ <?php for( $i = 2; $i < $menus['selectedLevels']; $i++ ){ ?>
339
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('to level %d'), $i ); ?></option>
340
+ <?php } ?>
341
+ </optgroup>
342
+ </select></label>
343
+ </span><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
344
+ </div>
345
+
346
+ <?php $this->cmw_formfield_checkbox( $instance, 'siblings',
347
+ array(
348
+ 'label' => __('Branch Siblings'),
349
+ 'lclass' => 'cmw-disableifnot-br',
350
+ 'disableif' => $isNotByBranch
351
+ ) ); ?>
352
+
353
+ <div class="cmw-indented">
354
+ <label><?php _e('Level:'); ?>
355
+ <select id="<?php echo $this->get_field_id('include_level'); ?>" class="cmw-include-level"
356
+ name="<?php echo $this->get_field_name('include_level'); ?>">
357
+ <?php $j = intval($instance['include_level']) > $menus['selectedLevels'] ? '' : $instance['include_level']; ?>
358
+ <option value="" <?php selected( $j, '' ); ?>>&nbsp;</option>
359
+ <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
360
+ <option value="<?php echo $i; ?>" <?php selected( $j, "$i" ); ?>><?php echo $i; ?></option>
361
+ <option value="<?php echo $i . '-'; ?>" <?php selected( $j, $i . '-' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and above'); ?></option>
362
+ <option value="<?php echo $i . '+'; ?>" <?php selected( $j, $i . '+' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and below'); ?></option>
363
+ <?php } ?>
364
+ </select></label>
365
+ </div>
366
+ </div>
367
+
368
+ <div>
369
+ <?php $this->cmw_assist_link(); ?>
370
+ <strong><?php _e('Exclusions'); ?></strong>
371
+
372
+ <div class="cmw-indented">
373
+ <?php $this->cmw_formfield_textbox( $instance, 'exclude',
374
+ array(
375
+ 'label' => __('Item Ids:'),
376
+ 'fclass' => 'cmw-maxwidth-twothirds cmw-exclusions'
377
+ ) ); ?>
378
+ </div>
379
+
380
+ <div class="cmw-indented">
381
+ <label><?php _e('Level:'); ?>
382
+ <select id="<?php echo $this->get_field_id('exclude_level'); ?>" class="cmw-exclude-level"
383
+ name="<?php echo $this->get_field_name('exclude_level'); ?>">
384
+ <?php $j = intval($instance['exclude_level']) > $menus['selectedLevels'] ? '' : $instance['exclude_level']; ?>
385
+ <option value="" <?php selected( $j, '' ); ?>>&nbsp;</option>
386
+ <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
387
+ <option value="<?php echo $i; ?>" <?php selected( $j, "$i" ); ?>><?php echo $i; ?></option>
388
+ <option value="<?php echo $i . '-'; ?>" <?php selected( $j, $i . '-' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and above'); ?></option>
389
+ <option value="<?php echo $i . '+'; ?>" <?php selected( $j, $i . '+' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and below'); ?></option>
390
+ <?php } ?>
391
+ </select></label>
392
+ </div>
393
+ </div>
394
+
395
+ <div>
396
+ <?php $this->cmw_assist_link(); ?>
397
+ <strong><?php _e('Qualifier'); ?></strong>
398
+ <br /><label for="<?php echo $this->get_field_id('contains_current'); ?>"><?php _e('Current Item is in:'); ?></label>
399
+ <select id="<?php echo $this->get_field_id('contains_current'); ?>"
400
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('contains_current'); ?>">
401
+ <option value="" <?php selected( $instance['contains_current'], '' ); ?>>&nbsp;</option>
402
+ <option value="menu" <?php selected( $instance['contains_current'], 'menu' ); ?>><?php echo _e('Menu'); ?></option>
403
+ <option value="primary" <?php selected( $instance['contains_current'], 'primary' ); ?>><?php echo _e('Primary Filter'); ?></option>
404
+ <option value="secondary" <?php selected( $instance['contains_current'], 'secondary' ); ?>><?php echo _e('Secondary Filter'); ?></option>
405
+ <option value="inclusions" <?php selected( $instance['contains_current'], 'inclusions' ); ?>><?php echo _e('Inclusions'); ?></option>
406
+ <option value="output" <?php selected( $instance['contains_current'], 'output' ); ?>><?php echo _e('Final Output'); ?></option>
407
+ </select>
408
+ </div>
409
+
410
+ <?php $this->cmw_close_a_field_section(); ?>
411
 
412
  <?php
413
+ /**
414
+ * v1.2.0 start collapsible section : 'Fallbacks'
415
+ */
416
+ $this->cmw_open_a_field_section( $instance, __('Fallbacks'), 'fs_fallbacks' ); ?>
417
+
418
+ <div class="cmw-disableifnot-br-ci<?php $this->cmw_disableif( 'push', $isNotBranchCurrentItem ); ?>">
419
+ <?php $this->cmw_assist_link(); ?>
420
+
421
+ <div class="cmw-indented">
422
+ <label for="<?php echo $this->get_field_id('fallback'); ?>"><strong><?php _e('If Current Item has no children:'); ?></strong></label>
423
+ <select id="<?php echo $this->get_field_id('fallback'); ?>" class="cmw-fallback cmw-listen"
424
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('fallback'); ?>">
425
+ <option value="" <?php selected( $instance['fallback'], '' ); ?>>&nbsp;</option>
426
+ <option value="parent" <?php selected( $instance['fallback'], 'parent' ); ?>><?php _e('Start at : -1 (parent)'); ?></option>
427
+ <option value="current" <?php selected( $instance['fallback'], 'current' ); ?>><?php _e('Start at : the Current Item'); ?></option>
428
+ <option value="quit" <?php selected( $instance['fallback'], 'quit' ); ?>><?php _e('No output!'); ?></option>
429
+ </select>
430
+
431
+ <br />
432
+ <span class="cmw-disableifnot-fb-pc<?php $this->cmw_disableif( 'push', $isNotFallbackParentCurrent ); ?>">
433
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_siblings',
434
+ array(
435
+ 'label' => '&hellip;' . __('and Include its Siblings')
436
+ ) ); ?>
437
+
438
+ <br />
439
+ <label><?php _e('For Depth:'); ?>
440
+ <select id="<?php echo $this->get_field_id('fallback_depth'); ?>" data-cmw-text-levels="<?php _e(' levels'); ?>"
441
+ data-cmw-set-levels="1" <?php $this->cmw_disableif(); ?>
442
+ class="cmw-set-levels" name="<?php echo $this->get_field_name('fallback_depth'); ?>">
443
+ <option value="0" <?php selected( $instance['fallback_depth'] > $menus['selectedLevels'] ? 0 : $instance['fallback_depth'], 0 ); ?>>&nbsp;</option>
444
+ <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
445
+ <option value="<?php echo $i; ?>" <?php selected( $instance['fallback_depth'], $i ); ?>><?php printf( _n('%d level', '%d levels', $i), $i ); ?></option>
446
+ <?php } ?>
447
+ </select></label>
448
+ <span class="cmw-small-block cmw-indented"><em class="cmw-colour-grey"><?php _e('Fallback Depth is Relative to Current Item!'); ?></em></span>
449
+ </span><!-- end .cmw-disableifnot-fb-pc --><?php $this->cmw_disableif( 'pop' ); ?>
450
+ </div>
451
+
452
+ </div><!-- end .cmw-disableifnot-br-ci --><?php $this->cmw_disableif( 'pop' ); ?>
453
+
454
+ <div>
455
+ <div class="cmw-indented"><strong><?php _e('If no Current Item can be found:'); ?></strong>
456
+ <br />
457
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_ci_parent',
458
+ array(
459
+ 'label' => __('Try items marked Parent of Current')
460
+ ) ); ?>
461
+ <span class="cmw-small-block cmw-indented"><em class="cmw-colour-grey"><?php _e('This is a last resort to determine a "Current Item"'); ?></em></span>
462
+ </div>
463
+ </div>
464
+
465
+ <div>
466
+ <div class="cmw-indented"><strong><?php _e('If more than 1 possible Current Item:'); ?></strong>
467
+ <br />
468
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_ci_lifo',
469
+ array(
470
+ 'label' => __('Use the <strong>last</strong> one found')
471
+ ) ); ?>
472
+ <span class="cmw-small-block cmw-indented"><em class="cmw-colour-grey"><?php _e('The default is to use the first candidate found'); ?></em></span>
473
+ </div>
474
+ </div>
475
+
476
+ <?php $this->cmw_close_a_field_section(); ?>
477
 
478
  <?php
479
+ /**
480
+ * start collapsible section : 'Output'
481
+ */
482
+ $this->cmw_open_a_field_section( $instance, __('Output'), 'fs_output' ); ?>
483
+
484
+ <div>
485
+ <?php $this->cmw_assist_link(); ?>
486
+ <label class="cmw-followed-by">
487
+ <input id="<?php echo $this->get_field_id('flat_output'); ?>_0"
488
+ name="<?php echo $this->get_field_name('flat_output'); ?>"
489
+ <?php $this->cmw_disableif(); ?> type="radio" value="0" <?php checked( !$instance['flat_output'] ); ?>
490
+ /><?php _e('Hierarchical'); ?></label>
491
+ <label class="cmw-whitespace-nowrap">
492
+ <input id="<?php echo $this->get_field_id('flat_output'); ?>_1"
493
+ name="<?php echo $this->get_field_name('flat_output'); ?>"
494
+ <?php $this->cmw_disableif(); ?> type="radio" value="1" <?php checked( $instance['flat_output'] ); ?>
495
+ /><?php _e('Flat'); ?></label>
496
+ </div>
497
+
498
+ <div>
499
+ <strong><?php _e('Set Title from'); ?></strong>
500
+
501
+ <div class="cmw-indented">
502
+ <label><?php _e('Current Item:'); $j = $instance['title_current']; ?>
503
+ <select id="<?php echo $this->get_field_id('title_current'); ?>" class="cmw-title-from cmw-set-rel-abs-levels cmw-listen"
504
+ name="<?php echo $this->get_field_name('title_current'); ?>"
505
+ data-cmw-text-level="<?php _e('level'); ?>">
506
+ <option value="" <?php selected( $j, "" ); ?>>&nbsp;</option>
507
+ <option value="0" <?php selected( $j, "0" ); ?>><?php _e('the Current Item'); ?></option>
508
+ <?php
509
+ $j = is_numeric( $j ) ? max( min( $j, $menus['selectedLevels'] - 1 ), 1 - $menus['selectedLevels'] ) : $j; ?>
510
+ <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-for-option=" <?php _e('%d levels'); ?>">
511
+ <option value="-1" <?php selected( $j, "-1" ); ?>><?php printf( __('%d level (parent)'), -1 ); ?></option>
512
+ <?php for( $i = -2; $i > 0 - $menus['selectedLevels']; $i-- ){ ?>
513
+ <option value="<?php echo $i; ?>" <?php selected( $j, "$i" ); ?>><?php printf( __('%d levels'), $i ); ?></option>
514
+ <?php } ?>
515
+ </optgroup>
516
+ <optgroup label="<?php _e('absolute...'); ?>" data-cmw-text-for-option="<?php _e('level %d'); ?> ">
517
+ <option value="1" <?php selected( $j, "1" ); ?>><?php printf( __('level %d (root)'), 1 ); ?></option>
518
+ <?php for( $i = 2; $i < $menus['selectedLevels']; $i++ ){ ?>
519
+ <option value="<?php echo $i; ?>" <?php selected( $j, "$i" ); ?>><?php printf( __('level %d'), $i ); ?></option>
520
+ <?php } ?>
521
+ </optgroup>
522
+ </select></label>
523
+ </div>
524
+ <div class="cmw-indented">
525
+ <label class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>"><?php _e('Branch Item:'); $j = $instance['title_branch']; ?>
526
+ <select id="<?php echo $this->get_field_id('title_branch'); ?>" class="cmw-title-from cmw-set-rel-abs-levels cmw-listen"
527
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('title_branch'); ?>"
528
+ data-cmw-text-level="<?php _e('level'); ?>">
529
+ <option value="" <?php selected( $j, "" ); ?>>&nbsp;</option>
530
+ <option value="0" <?php selected( $j, "0" ); ?>><?php _e('the Branch Item'); ?></option>
531
+ <?php
532
+ $j = is_numeric( $j ) ? max( min( $j, $menus['selectedLevels'] - 1 ), 1 - $menus['selectedLevels'] ) : $j; ?>
533
+ <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-for-option=" <?php _e('%d levels'); ?>">
534
+ <option value="-1" <?php selected( $j, "-1" ); ?>><?php printf( __('%d level (parent)'), -1 ); ?></option>
535
+ <?php for( $i = -2; $i > 0 - $menus['selectedLevels']; $i-- ){ ?>
536
+ <option value="<?php echo $i; ?>" <?php selected( $j, "$i" ); ?>><?php printf( __('%d levels'), $i ); ?></option>
537
+ <?php } ?>
538
+ </optgroup>
539
+ <optgroup label="<?php _e('absolute...'); ?>" data-cmw-text-for-option="<?php _e('level %d'); ?> ">
540
+ <option value="1" <?php selected( $j, "1" ); ?>><?php printf( __('level %d (root)'), 1 ); ?></option>
541
+ <?php for( $i = 2; $i < $menus['selectedLevels']; $i++ ){ ?>
542
+ <option value="<?php echo $i; ?>" <?php selected( $j, "$i" ); ?>><?php printf( __('level %d'), $i ); ?></option>
543
+ <?php } ?>
544
+ </optgroup>
545
+ </select></label><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
546
+ </div>
547
+ <div class="cmw-indented">
548
+ &hellip; <?php _e('and:'); ?>
549
+ <?php $this->cmw_formfield_checkbox( $instance, 'title_linked',
550
+ array(
551
+ 'label' => __('Make it a Link')
552
+ ) ); ?>
553
+ </div>
554
+ </div>
555
+
556
+ <div>
557
+ <strong><?php _e('Change UL to OL'); ?></strong>
558
+ <br />
559
+ <?php $this->cmw_formfield_checkbox( $instance, 'ol_root',
560
+ array(
561
+ 'label' => __('Top Level'),
562
+ 'lclass' => 'cmw-followed-by'
563
+ ) ); ?>
564
+ <?php $this->cmw_formfield_checkbox( $instance, 'ol_sub',
565
+ array(
566
+ 'label' => __('Sub-Levels'),
567
+ 'lclass' => 'cmw-whitespace-nowrap'
568
+ ) ); ?>
569
+ </div>
570
 
571
  <?php
572
+ //v1.1.0 As of WP v3.6, wp_nav_menu() automatically cops out (without outputting any HTML) if there are no items,
573
+ // so the hide_empty option becomes superfluous; however, I'll keep the previous setting (if there was one)
574
+ // in case of reversion to an earlier version of WP...
575
+ if( $this->cmw_wp_version('3.6') ){
576
  ?>
577
+ <div>
578
+ <?php $this->cmw_formfield_checkbox( $instance, 'hide_empty',
579
+ array(
580
+ 'label' => __('Hide Widget if Empty'),
581
+ 'desc' => __('Prevents any output when no items are found')
582
+ ) ); ?>
583
+ </div>
584
  <?php }else{ ?>
585
+ <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
586
+ type="hidden" value="<?php echo $instance['hide_empty'] ? '1' : ''; ?>" />
587
  <?php } ?>
588
 
589
+ <?php $this->cmw_close_a_field_section(); ?>
590
 
591
  <?php
592
+ /**
593
+ * start collapsible section : 'Container'
594
+ */
595
+ $this->cmw_open_a_field_section( $instance, __('Container'), 'fs_container' ); ?>
596
+
597
+ <div>
598
+ <?php $this->cmw_formfield_textbox( $instance, 'container',
599
+ array(
600
+ 'label' => __('Element:'),
601
+ 'desc' => __('Eg. div or nav; leave empty for no container')
602
+ ) ); ?>
603
+ </div>
604
+ <div>
605
+ <?php $this->cmw_formfield_textbox( $instance, 'container_id',
606
+ array(
607
+ 'label' => __('Unique ID:'),
608
+ 'desc' => __('An optional ID for the container')
609
+ ) ); ?>
610
+ </div>
611
+ <div>
612
+ <?php $this->cmw_formfield_textbox( $instance, 'container_class',
613
+ array(
614
+ 'label' => __('Class:'),
615
+ 'desc' => __('Extra class for the container')
616
+ ) ); ?>
617
+ </div>
618
+
619
+ <?php $this->cmw_close_a_field_section(); ?>
620
 
621
  <?php
622
+ /**
623
+ * start collapsible section : 'Classes'
624
+ */
625
+ $this->cmw_open_a_field_section( $instance, __('Classes'), 'fs_classes' ); ?>
626
+
627
+ <div>
628
+ <?php $this->cmw_formfield_textbox( $instance, 'menu_class',
629
+ array(
630
+ 'label' => __('Menu Class:'),
631
+ 'desc' => __('Class for the list element forming the menu')
632
+ ) ); ?>
633
+ </div>
634
+ <div>
635
+ <?php $this->cmw_formfield_textbox( $instance, 'widget_class',
636
+ array(
637
+ 'label' => __('Widget Class:'),
638
+ 'desc' => __('Extra class for the widget itself')
639
+ ) ); ?>
640
+ </div>
641
+
642
+ <?php $this->cmw_close_a_field_section(); ?>
643
 
644
  <?php
645
+ /**
646
+ * start collapsible section : 'Links'
647
+ */
648
+ $this->cmw_open_a_field_section( $instance, __('Links'), 'fs_links' ); ?>
649
+
650
+ <div>
651
+ <?php $this->cmw_formfield_textbox( $instance, 'before',
652
+ array(
653
+ 'label' => __('Before the Link:'),
654
+ 'desc' =>__( htmlspecialchars('Text/HTML to go before the </a> of the link') ),
655
+ 'fclass' => 'widefat'
656
+ ) ); ?>
657
+ </div>
658
+ <div>
659
+ <?php $this->cmw_formfield_textbox( $instance, 'after',
660
+ array(
661
+ 'label' => __('After the Link:'),
662
+ 'desc' => __( htmlspecialchars('Text/HTML to go after the </a> of the link') ),
663
+ 'fclass' => 'widefat'
664
+ ) ); ?>
665
+ </div>
666
+ <div>
667
+ <?php $this->cmw_formfield_textbox( $instance, 'link_before',
668
+ array(
669
+ 'label' => __('Before the Link Text:'),
670
+ 'desc' => __('Text/HTML to go before the link text'),
671
+ 'fclass' => 'widefat'
672
+ ) ); ?>
673
+ </div>
674
+ <div>
675
+ <?php $this->cmw_formfield_textbox( $instance, 'link_after',
676
+ array(
677
+ 'label' => __('After the Link Text:'),
678
+ 'desc' => __('Text/HTML to go after the link text'),
679
+ 'fclass' => 'widefat'
680
+ ) ); ?>
681
+ </div>
682
+
683
+ <?php $this->cmw_close_a_field_section(); ?>
684
+
685
  <?php
686
+ /**
687
+ * v3.1.0 start collapsible section : 'Alternative'
688
+ */
689
+ $this->cmw_open_a_field_section( $instance, __('Alternative'), 'fs_alternative' ); ?>
690
+
691
+ <div>
692
+ <?php $this->cmw_assist_link(); ?>
693
+
694
+ <label for="<?php echo $this->get_field_id('switch_if'); ?>" class="cmw-followed-by"><?php _e('On condition:'); ?></label>
695
+ <br /><select id="<?php echo $this->get_field_id('switch_if'); ?>" class="cmw-switchable cmw-listen"
696
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('switch_if'); ?>">
697
+ <option value="" <?php selected( $instance['switch_if'], '' ); ?>>&nbsp;</option>
698
+ <option value="current" <?php selected( $instance['switch_if'], 'current' ); ?>><?php _e('Current Item is in...'); ?></option>
699
+ <option value="no-current" <?php selected( $instance['switch_if'], 'no-current' ); ?>><?php _e('Current Item is NOT in...'); ?></option>
700
+ <option value="no-output" <?php selected( $instance['switch_if'], 'no-output' ); ?>><?php _e('No Output from...'); ?></option>
701
+ </select>
702
+
703
+ <select id="<?php echo $this->get_field_id('switch_at'); ?>" class="cmw-switchable cmw-listen"
704
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('switch_at'); ?>">
705
+ <option value="" <?php selected( $instance['switch_at'], '' ); ?>>&nbsp;</option>
706
+ <option value="menu" <?php selected( $instance['switch_at'], 'menu' ); ?>><?php echo _e('Menu'); ?></option>
707
+ <option value="primary" <?php selected( $instance['switch_at'], 'primary' ); ?>><?php echo _e('Primary Filter'); ?></option>
708
+ <option value="secondary" <?php selected( $instance['switch_at'], 'secondary' ); ?>><?php echo _e('Secondary Filter'); ?></option>
709
+ <option value="inclusions" <?php selected( $instance['switch_at'], 'inclusions' ); ?>><?php echo _e('Inclusions'); ?></option>
710
+ <option value="output" <?php selected( $instance['switch_at'], 'output' ); ?>><?php echo _e('Final Output'); ?></option>
711
+ </select>
712
+
713
+ <br />
714
+ <label class="cmw-disableifnot-sw<?php $this->cmw_disableif( 'push', $isNotSwitchable ); ?>"><?php _e('Then switch settings to:'); ?>
715
+ <br /><textarea rows="3" cols="20" <?php $this->cmw_disableif(); ?> id="<?php echo $this->get_field_id('switch_to'); ?>"
716
+ name="<?php echo $this->get_field_name('switch_to'); ?>"
717
+ class="widefat"><?php echo $instance['switch_to']; ?></textarea>
718
+ </label><!-- end .cmw-disableifnot-sw --><?php $this->cmw_disableif( 'pop' ); ?>
719
+ <span class="cmw-small-block cmw-indented"><em class="cmw-colour-grey">Enter/Paste a [cmwizard.../] shortcode</em></span>
720
+
721
+ </div>
722
+
723
+ <?php $this->cmw_close_a_field_section(); ?>
724
+
725
+ <div class="cmw-mock-fieldset"><?php _e('Shortcodes'); ?></div>
726
+ <div class="cmw-the-shortcodes">
727
+ <div class="cmw-small-block"><em class="cmw-colour-grey"><?php _e('The equivalent shortcode for this configuration...'); ?></em></div>
728
+ <div class="cmw-shortcode-nojs cmw-small-block"><?php _e('With Javascript disabled, this is only guaranteed to be accurate when you <em>initially enter</em> Edit mode!'); ?></div>
729
+ <div class="cmw-shortcode-wrap"><code class="widget-<?php echo $this->id_base; ?>-shortcode ui-corner-all"
730
+ title="<?php _e('stand-alone shortcode'); ?>"><?php echo self::cmw_shortcode( array_merge( $instance, array( 'menu' => $menus['selectedMenu'] ) ) ); ?></code></div>
731
+ <?php if( is_numeric( $this->number ) && $this->number > 0 ){ ?>
732
+ <div class="cmw-small-block"><em class="cmw-colour-grey"><?php _e('This <u>specific widget</u> can also be included using...'); ?></em></div>
733
+ <div class="cmw-shortcode-wrap"><code class="widget-<?php echo $this->id_base; ?>-shortcode cmw-instance-shortcode ui-corner-all"
734
+ title="<?php _e('dependent shortcode'); ?>">[cmwizard widget=<?php echo $this->number; ?>/]</code></div>
735
+ <?php } ?>
736
+ </div>
737
 
738
  </div>
739
  <?php
740
 
741
+ if( $this->_cmw_accessibility ){
742
+ wp_localize_script( Custom_Menu_Wizard_Plugin::$script_handle, __CLASS__, array( 'trigger' => '#' . $this->get_field_id('menu') ) );
743
+ }
744
+
745
+ } //end form()
746
+
747
+ /**
748
+ * sanitizes/updates the widget settings sent from the backend admin
749
+ *
750
+ * @filters : custom_menu_wizard_wipe_on_update false
751
+ *
752
+ * @param array $new_instance New widget settings
753
+ * @param array $old_instance Old widget settings
754
+ * @return array Sanitized widget settings
755
+ */
756
+ public function update( $new_instance, $old_instance ) {
757
+
758
+ //call the legacy update method for updates to existing widgets that don't have a version number (old format)...
759
+ if( empty( $new_instance['cmwv'] ) ){
760
+ return $this->cmw_legacy_update( $new_instance, $old_instance );
761
+ }
762
+
763
+ return self::cmw_settings(
764
+ $new_instance,
765
+ //allow a filter to return true, whereby any previous settings (now possibly unused) will be wiped instead of being allowed to remain...
766
+ //eg. add_filter( 'custom_menu_wizard_wipe_on_update', [filter_function], 10, 1 ) => true
767
+ apply_filters( 'custom_menu_wizard_wipe_on_update', false ) ? array() : $old_instance,
768
+ __FUNCTION__ );
769
+
770
+ } //end update()
771
+
772
+ /**
773
+ * produces the widget HTML at the front end
774
+ *
775
+ * @filters : custom_menu_wizard_nav_params array of params that will be sent to wp_nav_menu(), array of instance settings, id base
776
+ * custom_menu_wizard_settings_pre_widget array of instance settings, id base
777
+ * custom_menu_wizard_title_link_atts array of link attributes, title text, array of instance settings, id base
778
+ * custom_menu_wizard_widget_output HTML output string, array of instance settings, id base, $args
779
+ *
780
+ * @param object $args Widget arguments
781
+ * @param array $instance Configuration for this widget instance
782
+ */
783
+ public function widget( $args, $instance ) {
784
+
785
+ //call the legacy widget method for producing existing widgets that don't have a version number (old format)...
786
+ if( empty( $instance['cmwv'] ) ){
787
+ $this->cmw_legacy_widget( $args, $instance );
788
+ return;
789
+ }
790
+
791
+ //sanitize $instance...
792
+ $instance = self::cmw_settings( $instance, array(), __FUNCTION__ );
793
+ //holds information determined by the walker...
794
+ $this->_cmw_walker = array();
795
+
796
+ //v1.1.0 As of WP v3.6, wp_nav_menu() automatically prevents any HTML output if there are no items...
797
+ $instance['hide_empty'] = $instance['hide_empty'] && $this->cmw_wp_version('3.6');
798
+
799
+ //allow a filter to amend the instance settings prior to producing the widget output...
800
+ //eg. add_filter( 'custom_menu_wizard_settings_pre_widget', [filter_function], 10, 2 ) => $instance (array)
801
+ $instance = apply_filters( 'custom_menu_wizard_settings_pre_widget', $instance, $this->id_base );
802
+
803
+ //fetch menu...
804
+ if( !empty( $instance['menu'] ) ){
805
+ $menu = wp_get_nav_menu_object( $instance['menu'] );
806
+
807
+ //no menu, no output...
808
+ if ( !empty( $menu ) ){
809
+
810
+ //unless told not to, put the shortcode equiv. into a data item...
811
+ //NB: to turn this off (example):
812
+ // add_filter( 'custom_menu_wizard_settings_pre_widget', 'cmw_no_cmws', 10, 2 );
813
+ // function cmw_no_cmws( $instance, $id_base ){ $instance['cmws_off'] = true; return $instance; }
814
+ $dataCMWS = empty( $instance['cmws_off'] ) ? " data-cmws='" . esc_attr( self::cmw_shortcode( $instance, true ) ) . "'" : '';
815
+
816
+ if( !empty( $instance['container_class'] ) ){
817
+ //the menu-[menu->slug]-container class gets applied by WP UNLESS an alternative
818
+ //container class is supplied in the params - I'm going to set the param such that
819
+ //this instance's container class (if specified) gets applied IN ADDITION TO the
820
+ //default one...
821
+ $instance['container_class'] = "menu-{$menu->slug}-container {$instance['container_class']}";
822
+ }
823
+
824
+ $instance['menu_class'] = preg_split( '/\s+/', $instance['menu_class'], -1, PREG_SPLIT_NO_EMPTY );
825
+ //add cmw-alternate-maybe & cmw-fellback-maybe classes to the menu and we'll remove or replace later...
826
+ $instance['menu_class'][] = 'cmw-alternate-maybe';
827
+ $instance['menu_class'][] = 'cmw-fellback-maybe';
828
+ $instance['menu_class'] = implode( ' ', $instance['menu_class'] );
829
+
830
+ $walker = new Custom_Menu_Wizard_Walker;
831
+ $params = array(
832
+ 'menu' => $menu,
833
+ 'container' => empty( $instance['container'] ) ? false : $instance['container'],
834
+ 'container_id' => $instance['container_id'],
835
+ 'menu_class' => $instance['menu_class'],
836
+ 'echo' => false,
837
+ 'fallback_cb' => false,
838
+ 'before' => $instance['before'],
839
+ 'after' => $instance['after'],
840
+ 'link_before' => $instance['link_before'],
841
+ 'link_after' => $instance['link_after'],
842
+ 'depth' => $instance['flat_output'] ? -1 : $instance['depth'],
843
+ 'walker' =>$walker,
844
+ //widget specific stuff...
845
+ '_custom_menu_wizard' => $instance
846
+ );
847
+ //for the walker's use...
848
+ $params['_custom_menu_wizard']['_walker'] = array();
849
+ //set wrapper to UL or OL...
850
+ if( $instance['ol_root'] ){
851
+ $params['items_wrap'] = '<ol id="%1$s" class="%2$s" data-cmwv="' . $instance['cmwv'] . '"' . $dataCMWS . '>%3$s</ol>';
852
+ }else{
853
+ $params['items_wrap'] = '<ul id="%1$s" class="%2$s" data-cmwv="' . $instance['cmwv'] . '"' . $dataCMWS . '>%3$s</ul>';
854
+ }
855
+ //add a container class...
856
+ if( !empty( $instance['container_class'] ) ){
857
+ $params['container_class'] = $instance['container_class'];
858
+ }
859
+
860
+ //add my filters...
861
+ add_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
862
+ if( $instance['hide_empty'] ){
863
+ add_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
864
+ }
865
+
866
+ //allow a filter to amend the wp_nav_menu() params prior to calling it...
867
+ //eg. add_filter( 'custom_menu_wizard_nav_params', [filter_function], 10, 3 ) => $params (array)
868
+ //NB: wp_nav_menu() is in wp-includes/nav-menu-template.php
869
+ $out = wp_nav_menu( apply_filters( 'custom_menu_wizard_nav_params', $params, $instance, $this->id_base ) );
870
+
871
+ //remove my filters...
872
+ remove_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
873
+ if( $instance['hide_empty'] ){
874
+ remove_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
875
+ }
876
+
877
+ //only put something out if there is something to put out...
878
+ if( !empty( $out ) ){
879
+
880
+ //check to see if the settings have been changed, either as a result of invoking an alternative
881
+ //configuration, or due to the application of a custom_menu_wizard_walker_change_settings filter...
882
+ if( !empty( $this->_cmw_walker['instances'] ) ){
883
+ $instance = $this->_cmw_walker['instances']['new'];
884
+ }
885
+
886
+ //title from : priority is current -> current root -> branch -> branch root...
887
+ //note that none actually have to be present in the results
888
+ //v3.1.4 : used to get just the title string passed back, now get the menu item element!
889
+ //v3.1.5 : now just gets a single element, not all the possibilities
890
+ if( !empty( $this->_cmw_walker['get_title_from'] )
891
+ && !empty( $this->_cmw_walker['get_title_from']->title ) ){
892
+ //allow the widget_title filter to override...
893
+ $title = apply_filters( 'widget_title', $this->_cmw_walker['get_title_from']->title, $instance, $this->id_base );
894
+ //if we've been asked for a linked title...
895
+ if( $instance['title_linked'] ){
896
+ $n = array(
897
+ 'title' => empty( $this->_cmw_walker['get_title_from']->attr_title ) ? '' : $this->_cmw_walker['get_title_from']->attr_title,
898
+ 'target' => empty( $this->_cmw_walker['get_title_from']->target ) ? '' : $this->_cmw_walker['get_title_from']->target,
899
+ 'rel' => empty( $this->_cmw_walker['get_title_from']->xfn ) ? '' : $this->_cmw_walker['get_title_from']->xfn,
900
+ 'href' => empty( $this->_cmw_walker['get_title_from']->url ) ? '' : $this->_cmw_walker['get_title_from']->url,
901
+ 'class' => 'cmw-linked-widget-title'
902
+ );
903
+ $n = apply_filters( 'custom_menu_wizard_title_link_atts', $n, $this->_cmw_walker['get_title_from'], $instance, $this->id_base );
904
+ $atts = '';
905
+ foreach ( (array)$n as $i => $j ) {
906
+ if ( !empty( $j ) ) {
907
+ $j = ( $i === 'href' ) ? esc_url( $j ) : esc_attr( $j );
908
+ $atts .= ' ' . $i . '="' . $j . '"';
909
+ }
910
+ }
911
+ if( !empty( $atts ) ){
912
+ $title = '<a' . $atts . '>' . $title . '</a>';
913
+ }
914
+ }
915
+ }
916
+ if( empty( $title ) ){
917
+ $title = $instance['hide_title'] ? '' : apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
918
+ }
919
+
920
+ //remove/replace the cmw-fellback-maybe class...
921
+ $out = str_replace(
922
+ array(
923
+ 'cmw-fellback-maybe',
924
+ 'cmw-alternate-maybe'
925
+ ),
926
+ array(
927
+ empty( $this->_cmw_walker['fellback'] ) ? '' : 'cmw-fellback-' . $this->_cmw_walker['fellback'],
928
+ empty( $this->_cmw_walker['alternative'] ) ? '' : 'cmw-invoked-alternative'
929
+ ),
930
+ $out );
931
+
932
+ //try to add widget_class (if specified) to before_widget...
933
+ if( !empty( $instance['widget_class'] ) && !empty( $args['before_widget'] ) ){
934
+ //$args['before_widget'] is usually just a DIV start-tag, with an id and a class; if it
935
+ //gets more complicated than that then this may not work as expected...
936
+ if( preg_match( '/^<[^>]+?class=["\']/', $args['before_widget'] ) > 0 ){
937
+ //...already has a class attribute : prepend mine...
938
+ $args['before_widget'] = preg_replace( '/(class=["\'])/', '$1' . $instance['widget_class'] . ' ', $args['before_widget'], 1 );
939
+ }else{
940
+ //...doesn't currently have a class : add class attribute...
941
+ $args['before_widget'] = preg_replace( '/^(<\w+)(\s|>)/', '$1 class="' . $instance['widget_class'] . '"$2', $args['before_widget'] );
942
+ }
943
+ }
944
+
945
+ if( !empty( $title ) ){
946
+ $out = $args['before_title'] . $title . $args['after_title'] . $out;
947
+ }
948
+ $out = $args['before_widget'] . $out . $args['after_widget'];
949
+ //allow a filter to modify the entire output...
950
+ //eg. add_filter( 'custom_menu_wizard_widget_output', [filter_function], 10, 4 ) => $output (HTML string)
951
+ //NB 4th parameter ($args) added at v3.0.3
952
+ echo apply_filters( 'custom_menu_wizard_widget_output', $out, $instance, $this->id_base, $args );
953
+ }
954
+ }
955
+ }
956
+
957
+ } //end widget()
958
+
959
+ /**
960
+ * outputs an assist anchor
961
+ */
962
+ public function cmw_assist_link(){
963
+
964
+ //don't really need to worry about the id for non-javascript enabled usage because the css hides the
965
+ //button, but it doesn't hurt so I've left it in...
966
+ $hashid = $this->get_field_id( 'cmw' . ++$this->_cmw_hash_ct );
 
 
 
967
  ?>
968
  <a class="widget-<?php echo $this->id_base; ?>-assist button" id="<?php echo $hashid; ?>" href="#<?php echo $hashid; ?>"><?php _e('assist'); ?></a>
969
  <?php
970
 
971
+ }
972
+
973
+ /**
974
+ * outputs the HTML to close off a collapsible/expandable group of settings
975
+ */
976
+ public function cmw_close_a_field_section(){
977
+
978
+ ?></div><?php
979
+
980
+ } //end cmw_close_a_field_section()
981
+
982
+ /**
983
+ * either pushes, pops, or echoes last of, the disabled attributes array
984
+ * note that if accessibility mode is on, nothing should get disabled!
985
+ * as of 3.1.0, nothing gets disabled, just coloured grey (incl. legacy versions)!
986
+ *
987
+ * @param string $action 'pop' or 'push'
988
+ * @param boolean $test What to push
989
+ */
990
+ public function cmw_disableif( $action = 'echo', $test = false ){
991
+
992
+ if( !isset( $this->_cmw_disableif ) ){
993
+ $this->_cmw_disableif = array( '' );
994
+ }
995
+ if( $action == 'push' ){
996
+ if( $test && !$this->_cmw_accessibility ){
997
+ //append disabled attribute...
998
+ //v3.1.0 : nothing gets disabled, including any legacy stuff!
999
+ // $this->_cmw_disableif[] = 'disabled="disabled"';
1000
+ $this->_cmw_disableif[] = '';
1001
+ //and echo disabled class...
1002
+ echo ' cmw-colour-grey';
1003
+ }else{
1004
+ //append a copy of current last element (maintaining status quo)...
1005
+ $e = array_slice( $this->_cmw_disableif, -1 );
1006
+ $this->_cmw_disableif[] = $e[0];
1007
+ }
1008
+ }elseif( $action == 'pop' ){
1009
+ //remove last element (if count is greater than 1, so it is never left totally empty by mistake)...
1010
+ if( count( $this->_cmw_disableif ) > 1 ){
1011
+ array_pop( $this->_cmw_disableif );
1012
+ }
1013
+ }else{
1014
+ //echo last element...
1015
+ $e = array_slice( $this->_cmw_disableif, -1 );
1016
+ echo $e[0];
1017
+ }
1018
+
1019
+ }
1020
+
1021
+ /**
1022
+ * this gets run (filter: wp_nav_menu_{$menu->slug}_items) if hide_empty is set
1023
+ * if $items is empty then add a wp_nav_menu filter to do the actual return of an empty string
1024
+ * it gets run before the wp_nav_menu filter, but it gets the $items array whereas the wp_nav_menu filter does not
1025
+ * it gets added by $this->widget() before wp_nav_menu() is called, and removed immediately after wp_nav_menu() returns
1026
+ *
1027
+ * v1.1.0 As of WP v3.6 this method becomes superfluous because wp_nav_menu() has had code added to immediately
1028
+ * cop out (return false) if the output from wp_nav_menu_{$menu->slug}_items filter(s) is empty.
1029
+ * However, it stays in so as to cope with versions < 3.6
1030
+ *
1031
+ * @param array $items Menu items
1032
+ * @param object $args
1033
+ * @return array Menu items
1034
+ */
1035
+ public function cmw_filter_check_for_no_items($items, $args){
1036
+
1037
+ if( !empty( $args->_custom_menu_wizard ) && empty( $items ) ){
1038
+ add_filter( 'wp_nav_menu', array( $this, 'cmw_filter_no_output_when_empty' ), 65532, 2 );
1039
+ }
1040
+ return $items;
1041
+
1042
+ } //end cmw_filter_check_for_no_items()
1043
+
1044
+ /**
1045
+ * this (filter: wp_nav_menu) merely removes itself from the filters and returns an empty string
1046
+ * it gets added by the cmw_filter_check_for_no_items method below, and only
1047
+ * ever gets run when hide_empty is set on the widget instance
1048
+ *
1049
+ * v1.1.0 As of WP v3.6 this method becomes superfluous because wp_nav_menu() has had code added to immediately
1050
+ * cop out (return false) if the output from wp_nav_menu_{$menu->slug}_items filter(s) is empty.
1051
+ * However, it stays in so as to cope with versions < 3.6
1052
+ *
1053
+ * @param string $nav_menu HTML for the menu
1054
+ * @param object $args
1055
+ * @return string HTML for the menu
1056
+ */
1057
+ public function cmw_filter_no_output_when_empty($nav_menu, $args){
1058
+
1059
+ remove_filter( 'wp_nav_menu', array( $this, __FUNCTION__ ), 65532, 2 );
1060
+ return empty( $args->_custom_menu_wizard ) ? $nav_menu : '';
1061
+
1062
+ } //end cmw_filter_no_output_when_empty()
1063
+
1064
+ /**
1065
+ * v1.2.1 stores any walker-determined information back into the widget instance
1066
+ * gets run by the walker, on the filtered array of menu items, just before running parent::walk()
1067
+ * only gets run *if* there are menu items found
1068
+ *
1069
+ * @param array $items Filtered menu items
1070
+ * @param object $args
1071
+ * @return array Menu items
1072
+ */
1073
+ public function cmw_filter_walker_items( $items, $args ){
1074
+
1075
+ if( !empty( $args->_custom_menu_wizard['_walker'] ) ){
1076
+ $this->_cmw_walker = $args->_custom_menu_wizard['_walker'];
1077
+ }
1078
+ return $items;
1079
+
1080
+ } //end cmw_filter_walker_items()
1081
+
1082
+ /**
1083
+ * output a checkbox field
1084
+ *
1085
+ * @param array $instance Contains current field value
1086
+ * @param string $field Field name
1087
+ * @param array $params Attribute values
1088
+ */
1089
+ public function cmw_formfield_checkbox( &$instance, $field, $params ){
1090
+
1091
+ $labelClass = empty( $params['lclass'] ) ? '' : $params['lclass'];
1092
+ $fieldClass = empty( $params['fclass'] ) ? '' : $params['fclass'];
1093
+ $disabling = !empty( $labelClass ) && isset( $params['disableif'] );
1094
  ?>
1095
+ <label class="<?php echo $labelClass; if( $disabling ){ $this->cmw_disableif( 'push', $params['disableif'] ); } ?>">
1096
+ <input id="<?php echo $this->get_field_id( $field ); ?>" class="<?php echo $fieldClass; ?>"
1097
+ name="<?php echo $this->get_field_name( $field ); ?>" <?php $this->cmw_disableif(); ?>
1098
+ <?php checked($instance[ $field ]); ?> type="checkbox" value="1"
1099
+ /><?php echo $params['label']; ?></label><?php if( $disabling ){ $this->cmw_disableif( 'pop' ); } ?>
1100
  <?php
1101
+ if( !empty( $params['desc'] ) ){
1102
  ?>
1103
+ <span class="cmw-small-block"><em class="cmw-colour-grey"><?php echo $params['desc']; ?></em></span>
1104
  <?php
1105
+ }
1106
 
1107
+ } // end cmw_formfield_checkbox()
1108
 
1109
+ /**
1110
+ * output a text input field
1111
+ *
1112
+ * @param array $instance Contains current field value
1113
+ * @param string $field Field name
1114
+ * @param array $params Attribute values
1115
+ */
1116
+ public function cmw_formfield_textbox( &$instance, $field, $params ){
1117
 
1118
+ $fieldClass = empty( $params['fclass'] ) ? '' : $params['fclass'];
1119
 
1120
+ if( !empty( $params['label'] ) ){
1121
  ?>
1122
+ <label for="<?php echo $this->get_field_id( $field ); ?>"><?php echo $params['label']; ?></label>
1123
  <?php
1124
+ }
1125
  ?>
1126
+ <input id="<?php echo $this->get_field_id( $field ); ?>" class="<?php echo $fieldClass; ?>"
1127
+ name="<?php echo $this->get_field_name( $field ); ?>" <?php $this->cmw_disableif(); ?>
1128
+ type="text" value="<?php echo $instance[ $field ]; ?>" />
1129
  <?php
1130
+ if( !empty( $params['desc'] ) ){
1131
  ?>
1132
+ <span class="cmw-small-block"><em class="cmw-colour-grey"><?php echo $params['desc']; ?></em></span>
1133
  <?php
1134
+ }
1135
+
1136
+ } // end cmw_formfield_textbox()
1137
+
1138
+ /**
1139
+ * gets menus (in name order) and their items, returning empty array if there are none (or if none have items)
1140
+ *
1141
+ * @param integer $selectedMenu (by reference) The instance setting to check against for a menu to be "selected"
1142
+ * @return array
1143
+ */
1144
+ public function cmw_get_custom_menus( &$selectedMenu ){
1145
+
1146
+ $findSM = $selectedMenu > 0;
1147
+ $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
1148
+ if( !empty( $menus ) ){
1149
+ foreach( $menus as $i => $menu ){
1150
+ //find the menu's items, then remove any menus that have no items...
1151
+ $menus[ $i ]->_items = wp_get_nav_menu_items( $menu->term_id );
1152
+ if( empty( $menus[ $i ]->_items ) ){
1153
+ unset( $menus[ $i ] );
1154
+ }else{
1155
+ //if the items are all orphans, then remove the menu...
1156
+ $rootItem = false;
1157
+ foreach( $menus[ $i ]->_items as $item ){
1158
+ $rootItem = $rootItem || $item->menu_item_parent == 0;
1159
+ }
1160
+ if( !$rootItem ){
1161
+ unset( $menus[ $i ] );
1162
+ }elseif( $findSM && $selectedMenu == $menu->term_id ){
1163
+ $findSM = false;
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+ //if findSM is TRUE then we were looking for a specific menu and failed to find it (or it had no eligible items)...
1169
+ if( $findSM ){
1170
+ //clear selectedMenu...
1171
+ $selectedMenu = 0;
1172
+ //this would be the place to flag a warning!
1173
+ }
1174
+
1175
+ return empty( $menus ) ? array() : array_values( $menus );
1176
+
1177
+ } // end cmw_get_custom_menus()
1178
+
1179
+ /**
1180
+ * gets the various option, optgroups, max levels, etc, from the available custom menus (if any)
1181
+ *
1182
+ * @param integer $selectedMenu The instance setting to check against for a menu to be "selected"
1183
+ * @param integer $selectedItem The instance setting to check against for an menu item to be "selected"
1184
+ * @return array|boolean
1185
+ */
1186
+ public function cmw_scan_menus( $selectedMenu, $selectedItem ){
1187
+
1188
+ //create the options for the menu select & branch select...
1189
+ // IE is a pita when it comes to SELECTs because it ignores any styling on OPTGROUPs and OPTIONs, so I'm using
1190
+ // a copy from which the javascript can pick the relevant OPTGROUP
1191
+ $rtn = array(
1192
+ 'maxlevel' => 1, //maximum number of levels (across all menus)
1193
+ 'names' => array(), //HTML of OPTIONs, for selecting a menu (returned as a string)
1194
+ 'optgroups' => array(), //HTML of OPTGROUPs & contained OPTIONs, for selecting an item (returned as a string)
1195
+ 'selectedOptgroup' => array(''), //HTML of currently selected menu's OPTGROUP and its OPTIONs (returned as string)
1196
+ 'selectedBranchName' => __('the Current Item'), //title of currently selected menu item
1197
+ 'selectedLevels' => 1 //number of levels in the currently selected menu
1198
+ );
1199
+
1200
+ //couple of points:
1201
+ // - if there's no currently selected menu (eg. it's a new, unsaved form) then use the first menu found that has eligible items
1202
+ // - if there is a currently selected menu, but that menu is no longer available (no longer exists, or now has no eligible items)
1203
+ // then, again, use the first menu found that does have items. PROBLEM : this means that the widget's instance settings
1204
+ // won't match what the widget is currently displaying! this situation is not unique to this function because it can
1205
+ // also occur for things like depth, but it does raise the question of whether the user should be informed that what
1206
+ // is being presented does not match the current saved settings?
1207
+ // Note that also applies to selected item (ie. the menu still exists but the currently selected item within that menu does not).
1208
+
1209
+ $ct = 0;
1210
+ $sogCt = 0;
1211
+ $itemindents = $menu = $item = NULL;
1212
+ //note that fetching the menus can clear selectedMenu!
1213
+ foreach( $this->cmw_get_custom_menus( $selectedMenu ) as $i => $menu ){
1214
+ $maxgrplevel = 1;
1215
+ $itemindents = array( '0' => 0 );
1216
+ $menuGrpOpts = '';
1217
+ //don't need to check for existence of items because if there were none then the menu wouldn't be here!
1218
+ foreach( $menu->_items as $item ){
1219
+ //exclude orphans!
1220
+ if( isset($itemindents[ $item->menu_item_parent ])){
1221
+ $title = $item->title;
1222
+ $level = $itemindents[ $item->menu_item_parent ] + 1;
1223
+
1224
+ $itemindents[ $item->ID ] = $level;
1225
+ $rtn['maxlevel'] = max( $rtn['maxlevel'], $level );
1226
+ $maxgrplevel = max( $maxgrplevel, $level );
1227
+
1228
+ //if there is no currently selected menu AND this is the first found item for this menu then
1229
+ //set this menu as the currently selected menu (being the first one found with an eligible item)...
1230
+ if( empty( $selectedMenu ) && empty( $menuGrpOpts ) ){
1231
+ $selectedMenu = $menu->term_id;
1232
+ }
1233
+ //only if THIS is the currently selected menu do we determine "selected" for each menu item...
1234
+ if( $selectedMenu == $menu->term_id ){
1235
+ $selected = selected( $selectedItem, $item->ID, false );
1236
+ if( !empty( $selected ) ){
1237
+ $rtn['selectedBranchName'] = $title;
1238
+ }
1239
+ $rtn['selectedOptgroup'][ $sogCt ] .= '<option value="' . $item->ID . '" ' . $selected . ' data-cmw-level="' . $level . '">';
1240
+ $rtn['selectedOptgroup'][ $sogCt ] .= str_repeat( '&nbsp;', ($level - 1) * 3 ) . esc_attr( $title ) . '</option>';
1241
+ }
1242
+ //don't set "selected" on the big list...
1243
+ $menuGrpOpts .= '<option value="' . $item->ID . '" data-cmw-level="' . $level . '">';
1244
+ $menuGrpOpts .= str_repeat( '&nbsp;', ($level - 1) * 3 ) . esc_attr( $title ) . '</option>';
1245
+ }
1246
+ }
1247
+
1248
+ //should never be empty, but check nevertheless...
1249
+ if( !empty( $menuGrpOpts ) ){
1250
+ $rtn['names'][ $ct ] = '<option ' . selected( $selectedMenu, $menu->term_id, false ) . ' value="' . $menu->term_id . '">' . $menu->name . '</option>';
1251
+ $rtn['optgroups'][ $ct ] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $ct . '" data-cmw-max-level="' . $maxgrplevel . '">';
1252
+ $rtn['optgroups'][ $ct ] .= $menuGrpOpts;
1253
+ $rtn['optgroups'][ $ct ] .= '</optgroup>';
1254
+ //if this menu is selected, then store this optgroup as the selected optgroup, and the number of levels it has...
1255
+ if( $selectedMenu == $menu->term_id ){
1256
+ $rtn['selectedOptgroup'][ $sogCt ] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $ct . '" data-cmw-max-level="' . $maxgrplevel . '">' . $rtn['selectedOptgroup'][ $sogCt ] . '</optgroup>';
1257
+ $rtn['selectedOptgroup'][ ++$sogCt ] = '';
1258
+ $rtn['selectedLevels'] = $maxgrplevel;
1259
+ }elseif( $this->_cmw_accessibility ){
1260
+ //if accessibility is on then the selected groups need to contain *all* the groups otherwise, with javascript disabled, the
1261
+ //user will not be able to select menu items from a switched menu without saving first. if javascript is not disabled, then
1262
+ //the script should initially remove any optgroups not immediately required...
1263
+ $rtn['selectedOptgroup'][ $sogCt ] = $rtn['optgroups'][ $ct ];
1264
+ $rtn['selectedOptgroup'][ ++$sogCt ] = '';
1265
+ }
1266
+ $ct++;
1267
+ }
1268
+ }
1269
+ unset( $itemindents, $menu, $item );
1270
+
1271
+ if( empty( $rtn['names'] ) ){
1272
+ $rtn = false;
1273
+ }else{
1274
+ $rtn['names'] = implode( '', $rtn['names'] );
1275
+ $rtn['optgroups'] = implode( '', $rtn['optgroups'] );
1276
+ $rtn['selectedOptgroup'] = implode( '', $rtn['selectedOptgroup'] );
1277
+ if( $this->_cmw_accessibility ){
1278
+ //reset levels of selected optgroup to be the max levels of any group...
1279
+ $rtn['selectedLevels'] = $rtn['maxlevel'];
1280
+ }
1281
+ //send the currently selected menu id back (may be different from the value when passed
1282
+ //in, if this is a new instance or if a menu set for an existing instance has been deleted)
1283
+ $rtn['selectedMenu'] = $selectedMenu;
1284
+ }
1285
+ return $rtn;
1286
+
1287
+ }
1288
+
1289
+ /**
1290
+ * outputs the HTML to begin a collapsible/expandable group of settings
1291
+ *
1292
+ * @param array $instance
1293
+ * @param string $text Label
1294
+ * @param string $fname Field name
1295
+ */
1296
+ public function cmw_open_a_field_section( &$instance, $text, $fname ){
1297
+
1298
+ $hashid = $this->get_field_id( 'cmw' . ++$this->_cmw_hash_ct );
1299
  ?>
1300
  <a class="widget-<?php echo $this->id_base; ?>-fieldset<?php echo $instance[$fname] ? ' cmw-collapsed-fieldset' : ''; ?>"
1301
+ id="<?php echo $hashid; ?>" href="#<?php echo $hashid; ?>"><?php echo $text; ?></a>
1302
+ <input id="<?php echo $this->get_field_id($fname); ?>" class="cmw-display-none cmw-fieldset-state"
1303
+ name="<?php echo $this->get_field_name($fname); ?>"
1304
+ type="checkbox" value="1" <?php checked( $instance[$fname] ); ?> />
1305
  <div class="cmw-fieldset<?php echo $instance[$fname] ? ' cmw-start-fieldset-collapsed' : ''; ?>">
1306
  <?php
1307
 
1308
+ } //end cmw_open_a_field_section()
1309
+
1310
+ /**
1311
+ * returns true if the version of WP is lower-than or greater-than-or-equal to the one provided
1312
+ *
1313
+ * @param string $v Version to test for lower than
1314
+ * @return boolean
1315
+ */
1316
+ public function cmw_wp_version( $v, $gte=false ){
1317
+ global $wp_version;
1318
+
1319
+ $rtn = version_compare( strtolower( $wp_version ), $v . 'a', '<' );
1320
+ return $gte ? !$rtn : $rtn;
1321
+
1322
+ } //end cmw_wp_version()
1323
+
1324
+ /**
1325
+ * sanitizes the widget settings for update(), widget() and form()
1326
+ *
1327
+ * @param array $from_instance Widget settings
1328
+ * @param array $base_instance Old widget settings or an empty array
1329
+ * @param string Name of the calling method
1330
+ * @return array Sanitized widget settings
1331
+ */
1332
+ public static function cmw_settings( $from_instance, $base_instance = false, $method = 'widget' ){
1333
+
1334
  /* old (pre v3) settings...
1335
+ //switches...
1336
+ 'include_ancestors' => 0 : replaced by ancestors
1337
+ 'include_parent' => 0 : replaced by ancestors
1338
+ 'include_parent_siblings' => 0 : replaced by ancestor_siblings
1339
+ 'title_from_parent' => 0 : replaced by title_from_branch
1340
+ 'fallback_no_ancestor' => 0 : no longer applicable
1341
+ 'fallback_include_parent' => 0 : no longer applicable
1342
+ 'fallback_include_parent_siblings' => 0 : no longer applicable (but "sort of" replicated by fallback_siblings)
1343
+ 'fallback_no_children' => 0 : replaced by fallback
1344
+ 'fallback_nc_include_parent' => 0 : no longer applicable
1345
+ 'fallback_nc_include_parent_siblings' => 0 : no longer applicable
1346
+ //integers...
1347
+ 'filter_item' => -2 : replaced by branch
1348
+ 'start_level' => 1 : replaced by level & branch_start
1349
  */
1350
 
1351
+ $instance = is_array( $base_instance ) ? $base_instance : array();
1352
+
1353
+ //switches : values are defaults...
1354
+ foreach( array(
1355
+ 'allow_all_root' => 0, //v3.0.0
1356
+ 'depth_rel_current' => 0,
1357
+ 'fallback_ci_lifo' => 0, //v3.1.5 changes determination of current item from first-found to last-found
1358
+ 'fallback_ci_parent' => 0, //v3.1.0 enables fallback determination of current item to item having current_item_parent set
1359
+ 'fallback_siblings' => 0, //v3.0.0 sort of replaces fallback_include_parent_siblings
1360
+ 'flat_output' => 0,
1361
+ 'hide_title' => 0,
1362
+ 'siblings' => 0, //v3.0.0 replaces include_parent_siblings
1363
+ 'include_root' => 0, //v3.0.0 ; v3.0.4 replaced/expanded by include_level DEPRECATED
1364
+ //v3.1.5 : the title_from_* fields are now DEPRECATED and have been replace with string fields title_branch and title_current
1365
+ 'title_from_branch' => 0, //v3.0.0 replaces title_from_parent DEPRECATED
1366
+ 'title_from_branch_root' => 0, //v3.0.0 added DEPRECATED
1367
+ 'title_from_current' => 0, // DEPRECATED
1368
+ 'title_from_current_root' => 0, //v3.0.0 added DEPRECATED
1369
+ 'title_linked' => 0, //v3.1.4 added
1370
+ 'ol_root' => 0,
1371
+ 'ol_sub' => 0,
1372
+ 'hide_empty' => 0, //this only has relevance prior to WP v3.6
1373
+ //field section collapsed toggles...
1374
+ 'fs_filters' => 1, //v3.0.0 replaces fs_filter and now starts out collapsed
1375
+ 'fs_fallbacks' => 1,
1376
+ 'fs_output' => 1,
1377
+ 'fs_container' => 1,
1378
+ 'fs_classes' => 1,
1379
+ 'fs_links' => 1,
1380
+ 'fs_alternative' => 1 //v3.1.0
1381
+ ) as $k => $v ){
1382
+
1383
+ if( $method == 'update' ){
1384
+ //store as 0 or 1...
1385
+ $instance[ $k ] = empty( $from_instance[ $k ] ) ? 0 : 1;
1386
+ }else{
1387
+ //use internally as boolean...
1388
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? !empty( $from_instance[ $k ] ) : !empty( $v );
1389
+ }
1390
+ }
1391
+
1392
+ //integers : values are minimums (defaults are the values maxed against 0)...
1393
+ foreach( array(
1394
+ 'ancestors' => -9999, //v3.0.0 replaces include_ancestors, but with levels (relative & absolute)
1395
+ 'ancestor_siblings' => -9999, //v3.0.0 also has levels (relative & absolute)
1396
+ 'depth' => 0,
1397
+ 'branch' => 0, //v3.0.0 replaces filter_item, but without current parent|root item
1398
+ 'menu' => 0,
1399
+ 'level' => 1, //v3.0.0 replace start_level (for a level filter)
1400
+ 'fallback_depth' => 0 //v3.0.0 added
1401
+ ) as $k => $v ){
1402
+
1403
+ $instance[ $k ] = isset( $from_instance[ $k ]) ? max( $v, intval( $from_instance[ $k ] ) ) : max( $v, 0 );
1404
+ }
1405
+
1406
+ //strings : values are defaults...
1407
+ foreach( array(
1408
+ 'title' => '',
1409
+ 'filter' => '', //v3.0.0 changed from integer ('', 'branch', 'items'), where empty is equiv. to 'level' (was level=0, branch=1, items=-1)
1410
+ 'branch_start' => '', //v3.0.0 replace start_level (for a branch filter)
1411
+ 'start_mode' => '', //v3.0.0 forces branch_start to use entire level ('', 'level')
1412
+ 'title_branch' => '', //v3.1.5 replace title_from_branch/branch_root ('', 0 or a signed? number)
1413
+ 'title_current' => '', //v3.1.5 replace title_from_current/current_root ('', 0 or a signed? number)
1414
+ 'contains_current' => '', //v3.0.0 changed from switch ('', 'menu', 'primary', 'secondary', 'inclusions' or 'output')
1415
+ 'container' => 'div',
1416
+ 'container_id' => '',
1417
+ 'container_class' => '',
1418
+ 'exclude_level' => '', //v3.0.0 (1 or more digits, possibly with an appended '-' or '+')
1419
+ 'fallback' => '', //v3.0.0 replace fallback_no_children ('', 'parent', 'current', 'quit')
1420
+ 'include_level' => '', //v3.0.4 (1 or more digits, possibly with an appended '-' or '+')
1421
+ 'switch_if' => '', //v3.1.0 ('', 'current', 'no-current', 'no-output')
1422
+ 'switch_at' => '', //v3.1.0 (same as for contains_current)
1423
+ 'switch_to' => '', //v3.1.0 (a [cmwizard .../] shortcode)
1424
+ 'menu_class' => 'menu-widget',
1425
+ 'widget_class' => '',
1426
+ 'cmwv' => ''
1427
+ ) as $k => $v ){
1428
+
1429
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? strip_tags( trim( (string)$from_instance[ $k ] ) ) : $v;
1430
+ if( $method == 'form' ){
1431
+ //escape strings...
1432
+ $instance[ $k ] = esc_attr( trim( $instance[ $k ] ) );
1433
+ }
1434
+ }
1435
+ if( $method == 'widget' && !empty( $instance['switch_to'] ) ){
1436
+ $instance['switch_to'] = apply_filters( 'custom_menu_wizard_sanitize_alternative', $instance['switch_to'] );
1437
+ }
1438
+
1439
+ //html strings : values are defaults...
1440
+ foreach( array(
1441
+ 'before' => '',
1442
+ 'after' => '',
1443
+ 'link_before' => '',
1444
+ 'link_after' => ''
1445
+ ) as $k => $v ){
1446
+
1447
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( (string)$from_instance[ $k ] ) : $v;
1448
+ if( $method == 'form' ){
1449
+ //escape html strings...
1450
+ $instance[ $k ] = esc_html( trim( $instance[ $k ] ) );
1451
+ }
1452
+ }
1453
+
1454
+ //csv strings : values are defaults...
1455
+ foreach( array(
1456
+ 'exclude' => '', //v3.0.0 added
1457
+ 'items' => ''
1458
+ ) as $k => $v ){
1459
+
1460
+ $inherits = array();
1461
+ $instance[ "_$k" ] = array();
1462
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( (string)$from_instance[ $k ] ) : $v;
1463
+ foreach( preg_split('/[,\s]+/', $instance[ $k ], -1, PREG_SPLIT_NO_EMPTY ) as $i ){
1464
+ //values can be just digits, or digits followed by a '+' (for inheritance)...
1465
+ if( preg_match( '/^(\d+)(\+?)$/', $i, $m ) > 0 ){
1466
+ $i = intval( $m[1] );
1467
+ if( $i > 0 ){
1468
+ if( !empty( $m[2] ) ){
1469
+ $inherits[] = $i;
1470
+ $i = $i . '+';
1471
+ }
1472
+ if( !in_array( "$i", $instance[ "_$k" ] ) ){
1473
+ $instance[ "_$k" ][] = "$i";
1474
+ }
1475
+ }
1476
+ }
1477
+ }
1478
+ if( !empty( $inherits ) ){
1479
+ $instance[ "_$k" ] = array_diff( $instance[ "_$k" ], $inherits );
1480
+ }
1481
+ unset( $inherits );
1482
+ //just store as comma-separated...
1483
+ $instance[ $k ] = implode( ',', $instance[ "_$k" ] );
1484
+ //can dump the underbar versions if called from update()...
1485
+ if( $method == 'update' ){
1486
+ unset( $instance[ "_$k" ] );
1487
+ }
1488
+ }
1489
+
1490
+ //v3.0.4 : v3.0.* back compat...
1491
+ //include_root was a boolean, but has been replaced with include_level, and the equiv. of include_root On is include_level=1...
1492
+ if( $instance['include_root'] && empty( $instance['include_level'] ) ){
1493
+ $instance['include_level'] = '1';
1494
+ }
1495
+ unset( $instance['include_root'] );
1496
+
1497
+ //v3.1.5 : back compat...
1498
+ //the 4 title_from_branch/current[_root] fields were booleans, but they're now DEPRECATED
1499
+ //and have been replaced by 2 string fields, title_branch & title_current
1500
+ if( $instance['title_branch'] === '' ){
1501
+ if( !empty( $instance['title_from_branch_root'] ) ){
1502
+ $instance['title_branch'] = 1;
1503
+ }
1504
+ if( !empty( $instance['title_from_branch'] ) ){
1505
+ $instance['title_branch'] = 0;
1506
+ }
1507
+ }
1508
+ if( $instance['title_current'] === '' ){
1509
+ if( !empty( $instance['title_from_current_root'] ) ){
1510
+ $instance['title_current'] = 1;
1511
+ }
1512
+ if( !empty( $instance['title_from_current'] ) ){
1513
+ $instance['title_current'] = 0;
1514
+ }
1515
+ }
1516
+ unset( $instance['title_from_branch'],
1517
+ $instance['title_from_branch_root'],
1518
+ $instance['title_from_current'],
1519
+ $instance['title_from_current_root'] );
1520
+
1521
+ return $instance;
1522
+
1523
+ } //end cmw_settings()
1524
+
1525
+ /**
1526
+ * returns the shortcode equivalent of the current settings (not called by legacy code!)
1527
+ *
1528
+ * @param array $instance
1529
+ * @param boolean $asJSON Requests response in JSON format, for the data-cmws attribute
1530
+ * @return string
1531
+ */
1532
+ public static function cmw_shortcode( $instance, $asJSON=false ){
1533
+
1534
+ $args = array(
1535
+ 'menu' => $instance['menu']
1536
+ );
1537
+ $byBranch = $instance['filter'] == 'branch';
1538
+ $byItems = $instance['filter'] == 'items';
1539
+ $byLevel = !$byBranch && !$byItems;
1540
+
1541
+ //take notice of the widget's hide_title flag...
1542
+ if( !empty( $instance['title'] ) && !$instance['hide_title'] ){
1543
+ $args['title'] = array( $instance['title'] );
1544
+ }
1545
+ //byLevel is the default (no branch & no items), as is level=1, so we only *have* to specify level if it's greater than 1...
1546
+ if( $byLevel && $instance['level'] > 1 ){
1547
+ $args['level'] = $instance['level'];
1548
+ }
1549
+ //specifying branch sets byBranch, overriding byLevel...
1550
+ if( $byBranch ){
1551
+ //use the alternative for 0 ("current") because it's more self-explanatory...
1552
+ $args['branch'] = $instance['branch'] > 0 ? $instance['branch'] : 'current';
1553
+ //start_at only *has* to be specified if not empty...
1554
+ if( !empty( $instance['branch_start'] ) ){
1555
+ $args['start_at'] = array( $instance['branch_start'] );
1556
+ }
1557
+ //start_mode may be brought into play by a fallback so always specify it...
1558
+ if( $instance['start_mode'] == 'level' ){
1559
+ $args['start_mode'] = 'level';
1560
+ }
1561
+ //allow_all_root is only applicable to byBranch...
1562
+ //NB this could be refined further, in that it only comes into play if
1563
+ // (a) start_mode is set to level, or
1564
+ // (b) there is a no-kids fallback set that produces output AND asks for siblings
1565
+ //but that gets a bit fussy so I'm leaving it as-is.
1566
+ if( $instance['allow_all_root'] ){
1567
+ $args['allow_all_root'] = 1;
1568
+ }
1569
+ }
1570
+ //specifying items set byItems, overriding byLevel & byBranch...
1571
+ if( $byItems ){
1572
+ $args['items'] = $instance['_items'];
1573
+ }
1574
+ //depth is not relevant to byItems...
1575
+ else{
1576
+ //depth if greater than 0...
1577
+ if( $instance['depth'] > 0 ){
1578
+ $args['depth'] = $instance['depth'];
1579
+ }
1580
+ //depth relative to current item is only applicable if depth is not unlimited...
1581
+ if( $instance['depth_rel_current'] && $instance['depth'] > 0 ){
1582
+ $args['depth_rel_current'] = 1;
1583
+ }
1584
+ }
1585
+ //fallbacks...
1586
+ //no children : branch = current item...
1587
+ if( $byBranch && $instance['branch'] == 0 ){
1588
+ if( !empty( $instance['fallback'] ) ){
1589
+ $args['fallback'] = array( $instance['fallback'] );
1590
+ if( $args['fallback'] != 'quit' ){
1591
+ if( $instance['fallback_siblings'] ){
1592
+ $args['fallback'][] = '+siblings';
1593
+ }
1594
+ if( $instance['fallback_depth'] > 0 ){
1595
+ $args['fallback'][] = $instance['fallback_depth'];
1596
+ }
1597
+ }
1598
+ }
1599
+ }
1600
+ //branch ancestor inclusions...
1601
+ if( $byBranch && !empty( $instance['ancestors'] ) ){
1602
+ $args['ancestors'] = $instance['ancestors'];
1603
+ //only ancestor-siblings if ancestors...
1604
+ if( !empty( $instance['ancestor_siblings'] ) ){
1605
+ $args['ancestor_siblings'] = $instance['ancestor_siblings'];
1606
+ }
1607
+ }
1608
+ //inclusions by level...
1609
+ if( !empty( $instance['include_level'] ) ){
1610
+ $args['include_level'] = array( $instance['include_level'] );
1611
+ }
1612
+ //exclusions by id...
1613
+ if( !empty( $instance['_exclude'] ) ){
1614
+ $args['exclude'] = $instance['_exclude'];
1615
+ }
1616
+ //...and by level...
1617
+ if( !empty( $instance['exclude_level'] ) ){
1618
+ $args['exclude_level'] = array( $instance['exclude_level'] );
1619
+ }
1620
+ //title from...
1621
+ $n = array();
1622
+ if( $instance['title_current'] !== '' ){
1623
+ if( $instance['title_current'] == '0' ){
1624
+ $n[] = 'current';
1625
+ }else{
1626
+ $n[] = 'current' . $instance['title_current'];
1627
+ }
1628
+ }
1629
+ if( $byBranch && $instance['title_branch'] !== '' ){
1630
+ if( $instance['title_branch'] == '0' ){
1631
+ $n[] = 'branch';
1632
+ }else{
1633
+ $n[] = 'branch' . $instance['title_branch'];
1634
+ }
1635
+ }
1636
+ if( !empty( $n ) ){
1637
+ $args['title_from'] = $n;
1638
+ //...title_linked is only relevant if title_from is set...
1639
+ if( $instance['title_linked'] ){
1640
+ $args['title_linked'] = 1;
1641
+ }
1642
+ }
1643
+ //switches...
1644
+ foreach( array('siblings', 'flat_output', 'ol_root', 'ol_sub', 'fallback_ci_parent', 'fallback_ci_lifo') as $n ){
1645
+ if( $instance[ $n ] ){
1646
+ $args[ $n ] = 1;
1647
+ }
1648
+ }
1649
+ //strings...
1650
+ foreach( array(
1651
+ 'contains_current' => '',
1652
+ 'container' => 'div',
1653
+ 'container_id' => '',
1654
+ 'container_class' => '',
1655
+ 'menu_class' => 'menu-widget',
1656
+ 'widget_class' => ''
1657
+ ) as $n => $v ){
1658
+ if( $instance[ $n ] != $v ){
1659
+ $args[ $n ] = array( $instance[ $n ] );
1660
+ }
1661
+ }
1662
+ foreach( array(
1663
+ 'wrap_link' => 'before',
1664
+ 'wrap_link_text' => 'link_before'
1665
+ ) as $n => $v ){
1666
+ if( preg_match( '/^<(\w+)/', $instance[ $v ], $m ) > 0 ){
1667
+ $args[ $n ] = array( $m[1] );
1668
+ }
1669
+ }
1670
+ //alternative...
1671
+ if( !empty( $instance['switch_if'] ) && !empty( $instance['switch_at'] ) ){
1672
+ $args['alternative'] = array( $instance['switch_if'], $instance['switch_at'] );
1673
+ $content = apply_filters( 'custom_menu_wizard_sanitize_alternative', $instance['switch_to'] );
1674
+ }
1675
+ //build the shortcode...
1676
+ $m = array();
1677
+ foreach( $args as $n => $v ){
1678
+ //array indicates join (with comma sep) & surround it in double quotes, otherwise leave 'as-is'...
1679
+ if( $asJSON ){
1680
+ $m[ $n ] = is_array( $v ) ? implode( ',', $v ) : $v;
1681
+ }else{
1682
+ $m[] = is_array( $v ) ? $n . '="' . implode( ',', $v ) . '"' : $n . '=' . $v;
1683
+ }
1684
+ }
1685
+ unset( $args );
1686
+
1687
+ //NB at v3.0.0, the shortcode changed from custom_menu_wizard to cmwizard (the previous version is still supported)
1688
+ //for JSON, don't output content...
1689
+ return $asJSON ? json_encode( $m ) : '[cmwizard ' . implode( ' ', $m ) . ( empty( $content ) ? '/]' : ']' . $content . '[/cmwizard]' );
1690
+
1691
+ } //end cmw_shortcode()
1692
+
1693
+
1694
+
1695
+ /*======================
1696
+ * LEGACY CODE (v2.1.0)
1697
+ *======================*/
1698
+
1699
+ /**
1700
+ * produces the legacy version of the backend admin form(s)
1701
+ *
1702
+ * @filters : custom_menu_wizard_prevent_legacy_updates false
1703
+ *
1704
+ * @param array $instance Widget settings
1705
+ */
1706
+ public function cmw_legacy_form( $instance ) {
1707
+
1708
+ //sanitize $instance...
1709
+ $instance = $this->cmw_legacy_settings( $instance, array(), 'form' );
1710
+
1711
+ //if no populated menus exist, suggest the user go create one...
1712
+ if( ( $menus = $this->cmw_scan_menus( $instance['menu'], $instance['filter_item'] ) ) === false ){
1713
  ?>
1714
  <p class="widget-<?php echo $this->id_base; ?>-no-menus">
1715
+ <?php printf( __('No populated menus have been created yet. <a href="%s">Create one</a>.'), admin_url('nav-menus.php') ); ?>
1716
  </p>
1717
  <?php
1718
+ return;
1719
+ }
1720
 
1721
+ //set up some simple booleans for use at the disableif___ classes...
1722
+ $isShowSpecific = $instance['filter'] < 0; // disableif-ss (IS show specific items)
1723
+ $isNotChildrenOf = $instance['filter'] < 1; // disableif (is NOT Children-of)
1724
+ $isNotCurrentRootParent = $isNotChildrenOf || $instance['filter_item'] >= 0; // disableifnot-rp (is NOT Children-of Current Root/Parent)
1725
+ $isNotCurrentItem = $isNotChildrenOf || $instance['filter_item'] != 0; // disableifnot-ci (is NOT Children-of Current Item)
1726
 
1727
  ?>
1728
  <div id="<?php echo $this->get_field_id('onchange'); ?>"
1729
+ class="widget-<?php echo $this->id_base; ?>-onchange<?php echo $this->cmw_wp_version('3.8') ? ' cmw-pre-wp-v38' : ''; ?>"
1730
+ data-cmw-v36plus='<?php echo $this->cmw_wp_version('3.6', true) ? 'true' : 'false'; ?>'
1731
+ data-cmw-dialog-prompt='<?php _e('Click an item to toggle &quot;Current Menu Item&quot;'); ?>'
1732
+ data-cmw-dialog-output='<?php _e('Basic Output'); ?>'
1733
+ data-cmw-dialog-fallback='<?php _e('Fallback invoked'); ?>'
1734
+ data-cmw-dialog-set-current='<?php _e('Set Current Item?'); ?>'
1735
+ data-cmw-dialog-shortcodes='<?php _e('Find posts/pages containing a CMW shortcode'); ?>'
1736
+ data-cmw-dialog-untitled='<?php _e('untitled'); ?>'
1737
+ data-cmw-dialog-fixed='<?php _e('fixed'); ?>'
1738
+ data-cmw-dialog-nonce='<?php echo wp_create_nonce( 'cmw-find-shortcodes' ); ?>'
1739
+ data-cmw-dialog-version='2.1.0'
1740
+ data-cmw-dialog-id='<?php echo $this->get_field_id('dialog'); ?>'>
1741
  <?php
1742
+ /**
1743
+ * Legacy warning...
1744
+ */
1745
  ?>
1746
+ <p class="cmw-legacy-warn">
1747
+ <a class="widget-<?php echo $this->id_base; ?>-legacy-close cmw-legacy-close" title="<?php _e('Dismiss'); ?>" href="#">X</a>
1748
+ <em><?php _e('This is an old version of the widget!'); ?>
1749
  <?php
1750
+ //allow a filter to return true, whereby updates to legacy widgets are disallowed...
1751
+ //eg. apply_filter( 'custom_menu_wizard_prevent_legacy_updates', [filter function], 10, 1 ) => true
1752
+ if( apply_filters( 'custom_menu_wizard_prevent_legacy_updates', false ) ){
1753
  ?>
1754
+ <br /><?php _e('Any changes you make will NOT be Saved!'); ?>
1755
  <?php
1756
+ }
1757
  ?>
1758
+ <br /><?php _e('Please consider creating a new instance of the widget to replace this one.'); ?>
1759
+ <a href="<?php echo $this->_cmw_legacy_warnreadmore; ?>" target="_blank"><?php _e('read more'); ?></a></em>
1760
+ </p>
1761
  <?php
1762
 
1763
+ /**
1764
+ * permanently visible section : Title (with Hide) and Menu
1765
+ */
1766
  ?>
1767
+ <p>
1768
+ <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:') ?></label>
1769
+ <?php $this->cmw_formfield_checkbox( $instance, 'hide_title',
1770
+ array(
1771
+ 'label' => __('Hide'),
1772
+ 'lclass' => 'alignright'
1773
+ ) ); ?>
1774
+ <?php $this->cmw_formfield_textbox( $instance, 'title',
1775
+ array(
1776
+ 'desc' => __('Title can be set, but need not be displayed'),
1777
+ 'fclass' => 'widefat cmw-widget-title'
1778
+ ) ); ?>
1779
+ </p>
1780
+
1781
+ <p>
1782
+ <?php $this->cmw_assist_link(); ?>
1783
+ <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
1784
+ <select id="<?php echo $this->get_field_id('menu'); ?>" <?php $this->cmw_disableif(); ?>
1785
+ class="cmw-select-menu cmw-listen" name="<?php echo $this->get_field_name('menu'); ?>">
1786
+ <?php echo $menus['names']; ?>
1787
+ </select>
1788
+ </p>
1789
 
1790
  <?php
1791
+ /**
1792
+ * start collapsible section : 'Filter'
1793
+ */
1794
+ $this->cmw_open_a_field_section( $instance, __('Filter'), 'fs_filter' );
1795
  ?>
1796
+ <p>
1797
+ <?php $this->cmw_assist_link(); ?>
1798
+ <label>
1799
+ <input id="<?php echo $this->get_field_id('filter'); ?>_0" class="cmw-showall cmw-listen" <?php $this->cmw_disableif(); ?>
1800
+ name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="0" <?php checked( $instance['filter'], 0 ); ?> />
1801
+ <?php _e('Show all'); ?></label>
1802
+ <br /><label>
1803
+ <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="cmw-listen" <?php $this->cmw_disableif(); ?>
1804
+ name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="1" <?php checked( $instance['filter'], 1 ); ?> />
1805
+ <?php _e('Children of:'); ?></label>
1806
+ <select id="<?php echo $this->get_field_id('filter_item'); ?>" class="cmw-childrenof cmw-assist-items cmw-listen"
1807
+ name="<?php echo $this->get_field_name('filter_item'); ?>" <?php $this->cmw_disableif(); ?>>
1808
+ <option value="0" <?php selected( $instance['filter_item'], 0 ); ?>><?php _e('Current Item'); ?></option>
1809
+ <option value="-2" <?php selected( $instance['filter_item'], -2 ); ?>><?php _e('Current Root Item'); ?></option>
1810
+ <option value="-1" <?php selected( $instance['filter_item'], -1 ); ?>><?php _e('Current Parent Item'); ?></option>
1811
+ <?php echo $menus['selectedOptgroup']; ?>
1812
+ </select>
1813
+ <br /><label>
1814
+ <input id="<?php echo $this->get_field_id('filter'); ?>_2" class="cmw-showspecific cmw-listen" <?php $this->cmw_disableif(); ?>
1815
+ name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="-1" <?php checked( $instance['filter'], -1 ); ?> />
1816
+ <?php _e('Items:'); ?></label>
1817
+ <?php $this->cmw_formfield_textbox( $instance, 'items',
1818
+ array(
1819
+ 'fclass' => 'cmw-setitems'
1820
+ ) ); ?>
1821
+
1822
+ <select id="<?php echo $this->get_field_id('filter_item_ignore'); ?>" disabled="disabled"
1823
+ class='cmw-off-the-page' name="<?php echo $this->get_field_name('filter_item_ignore'); ?>">
1824
+ <?php echo $menus['optgroups']; ?>
1825
+ </select>
1826
+ </p>
1827
+
1828
+ <p class="cmw-disableif-ss<?php $this->cmw_disableif( 'push', $isShowSpecific ); ?>">
1829
+ <label for="<?php echo $this->get_field_id('start_level'); ?>"><?php _e('Starting Level:'); ?></label>
1830
+ <select id="<?php echo $this->get_field_id('start_level'); ?>" <?php $this->cmw_disableif(); ?>
1831
+ class="cmw-start-level" name="<?php echo $this->get_field_name('start_level'); ?>">
1832
  <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
1833
+ <option value="<?php echo $i; ?>" <?php selected( $instance['start_level'] > $menus['selectedLevels'] ? 1 : $instance['start_level'], $i ); ?>><?php echo $i; ?></option>
1834
  <?php } ?>
1835
+ </select>
1836
+ <span class="cmw-small-block"><em><?php _e('Level to start testing items for inclusion'); ?></em></span>
1837
+ </p><!-- end .cmw-disableif-ss --><?php $this->cmw_disableif( 'pop' ); ?>
1838
+
1839
+ <p class="cmw-disableif-ss<?php $this->cmw_disableif( 'push', $isShowSpecific ); ?>">
1840
+ <label for="<?php echo $this->get_field_id('depth'); ?>"><?php _e('For Depth:'); ?></label>
1841
+ <select id="<?php echo $this->get_field_id('depth'); ?>" class="cmw-depth" data-cmw-text-levels="<?php _e(' levels'); ?>"
1842
+ name="<?php echo $this->get_field_name('depth'); ?>" <?php $this->cmw_disableif(); ?>>
1843
+ <option value="0" <?php selected( $instance['depth'] > $menus['selectedLevels'] ? 0 : $instance['depth'], 0 ); ?>><?php _e('unlimited'); ?></option>
1844
  <?php
1845
+ for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){
1846
  ?>
1847
+ <option value="<?php echo $i; ?>" <?php selected( $instance['depth'], $i ); ?>><?php echo $i; ?> <?php _e($i > 1 ? 'levels' : 'level'); ?></option>
1848
  <?php
1849
+ }
1850
  ?>
1851
+ </select>
1852
+ <span class="cmw-small-block"><em><?php _e('Relative to first Filter item found, <strong>unless</strong>&hellip;'); ?></em></span>
1853
+ <?php $this->cmw_formfield_checkbox( $instance, 'depth_rel_current',
1854
+ array(
1855
+ 'label' => sprintf( __('Relative to &quot;Current&quot; Item %1$s(if found)%2$s'), '<small><em>', '</em></small>' )
1856
+ ) ); ?>
1857
+ </p><!-- end .cmw-disableif-ss --><?php $this->cmw_disableif( 'pop' ); ?>
1858
 
1859
+ <?php $this->cmw_close_a_field_section(); ?>
1860
 
1861
  <?php
1862
+ /**
1863
+ * v1.2.0 start collapsible section : 'Fallbacks'
1864
+ */
1865
+ $this->cmw_open_a_field_section( $instance, __('Fallbacks'), 'fs_fallbacks' );
1866
  ?>
1867
+ <p class="cmw-disableifnot-rp<?php $this->cmw_disableif( 'push', $isNotCurrentRootParent ); ?>">
1868
+ <?php $this->cmw_assist_link(); ?>
1869
+ <span class="cmw-small-block"><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Root / Parent Item</em>, and no ancestor exists' ); ?> :</strong></span>
1870
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_no_ancestor',
1871
+ array(
1872
+ 'label' => __('Switch to Current Item, and')
1873
+ ) ); ?>
1874
+ <br />
1875
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_include_parent',
1876
+ array(
1877
+ 'label' => __('Include Parent...')
1878
+ ) ); ?>
1879
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_include_parent_siblings',
1880
+ array(
1881
+ 'label' => __('with Siblings'),
1882
+ 'lclass' => 'cmw-whitespace-nowrap'
1883
+ ) ); ?>
1884
+ </p><!-- end .cmw-disableifnot-rp --><?php $this->cmw_disableif( 'pop' ); ?>
1885
+
1886
+ <p class="cmw-disableifnot-ci<?php $this->cmw_disableif( 'push', $isNotCurrentItem ); ?>">
1887
+ <span class="cmw-small-block"><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Item</em>, and current item has no children' ); ?> :</strong></span>
1888
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_no_children',
1889
+ array(
1890
+ 'label' => __('Switch to Current Parent Item, and')
1891
+ ) ); ?>
1892
+ <br />
1893
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_nc_include_parent',
1894
+ array(
1895
+ 'label' => __('Include Parent...')
1896
+ ) ); ?>
1897
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_nc_include_parent_siblings',
1898
+ array(
1899
+ 'label' => __('with Siblings'),
1900
+ 'lclass' => 'cmw-whitespace-nowrap'
1901
+ ) ); ?>
1902
+ </p><!-- end .cmw-disableifnot-ci --><?php $this->cmw_disableif( 'pop' ); ?>
1903
+
1904
+ <?php $this->cmw_close_a_field_section(); ?>
1905
 
1906
  <?php
1907
+ /**
1908
+ * start collapsible section : 'Output'
1909
+ */
1910
+ $this->cmw_open_a_field_section( $instance, __('Output'), 'fs_output' );
1911
  ?>
1912
+ <p>
1913
+ <?php $this->cmw_assist_link(); ?>
1914
+ <label>
1915
+ <input id="<?php echo $this->get_field_id('flat_output'); ?>_0" name="<?php echo $this->get_field_name('flat_output'); ?>"
1916
+ type="radio" value="0" <?php checked(!$instance['flat_output']); ?> <?php $this->cmw_disableif(); ?> />
1917
+ <?php _e('Hierarchical'); ?></label>
1918
+ &nbsp;<label class="cmw-whitespace-nowrap">
1919
+ <input id="<?php echo $this->get_field_id('flat_output'); ?>_1" name="<?php echo $this->get_field_name('flat_output'); ?>"
1920
+ type="radio" value="1" <?php checked($instance['flat_output']); ?> <?php $this->cmw_disableif(); ?> />
1921
+ <?php _e('Flat'); ?></label>
1922
+ </p>
1923
+
1924
+ <p>
1925
+ <?php $this->cmw_formfield_checkbox( $instance, 'contains_current',
1926
+ array(
1927
+ 'label' => __('Must Contain &quot;Current&quot; Item'),
1928
+ 'desc' => __('Checks both Filtered and Included items')
1929
+ ) ); ?>
1930
+ </p>
1931
+
1932
+ <p class="cmw-disableif<?php $this->cmw_disableif( 'push', $isNotChildrenOf ); ?>">
1933
+ <?php $this->cmw_formfield_checkbox( $instance, 'include_parent',
1934
+ array(
1935
+ 'label' => __('Include Parent...')
1936
+ ) ); ?>
1937
+ <?php $this->cmw_formfield_checkbox( $instance, 'include_parent_siblings',
1938
+ array(
1939
+ 'label' => __('with Siblings'),
1940
+ 'lclass' => 'cmw-whitespace-nowrap'
1941
+ ) ); ?>
1942
+ <br />
1943
+ <?php $this->cmw_formfield_checkbox( $instance, 'include_ancestors',
1944
+ array(
1945
+ 'label' => __('Include Ancestors')
1946
+ ) ); ?>
1947
+ <br />
1948
+ <?php $this->cmw_formfield_checkbox( $instance, 'title_from_parent',
1949
+ array(
1950
+ 'label' => __('Title from Parent'),
1951
+ 'desc' => __('Only if the &quot;Children of&quot; Filter returns items')
1952
+ ) ); ?>
1953
+ </p><!-- end .cmw-disableif --><?php $this->cmw_disableif( 'pop' ); ?>
1954
+
1955
+ <p>
1956
+ <?php $this->cmw_formfield_checkbox( $instance, 'title_from_current',
1957
+ array(
1958
+ 'label' => __('Title from &quot;Current&quot; Item'),
1959
+ 'desc' => __('Lower priority than &quot;Title from Parent&quot;')
1960
+ ) ); ?>
1961
+ </p>
1962
+
1963
+ <p>
1964
+ <?php _e('Change UL to OL:'); ?>
1965
+ <br />
1966
+ <?php $this->cmw_formfield_checkbox( $instance, 'ol_root',
1967
+ array(
1968
+ 'label' => __('Top Level')
1969
+ ) ); ?>
1970
+ &nbsp;
1971
+ <?php $this->cmw_formfield_checkbox( $instance, 'ol_sub',
1972
+ array(
1973
+ 'label' => __('Sub-Levels')
1974
+ ) ); ?>
1975
+ </p>
1976
 
1977
  <?php
1978
+ //v1.1.0 As of WP v3.6, wp_nav_menu() automatically cops out (without outputting any HTML) if there are no items,
1979
+ // so the hide_empty option becomes superfluous; however, I'll keep the previous setting (if there was one)
1980
+ // in case of reversion to an earlier version of WP...
1981
+ if( $this->cmw_wp_version('3.6') ){
1982
  ?>
1983
+ <p>
1984
+ <?php $this->cmw_formfield_checkbox( $instance, 'hide_empty',
1985
+ array(
1986
+ 'label' => __('Hide Widget if Empty'),
1987
+ 'desc' => __('Prevents any output when no items are found')
1988
+ ) ); ?>
1989
+ </p>
1990
  <?php }else{ ?>
1991
+ <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
1992
+ type="hidden" value="<?php echo $instance['hide_empty'] ? '1' : ''; ?>" />
1993
  <?php } ?>
1994
 
1995
+ <?php $this->cmw_close_a_field_section(); ?>
1996
 
1997
  <?php
1998
+ /**
1999
+ * start collapsible section : 'Container'
2000
+ */
2001
+ $this->cmw_open_a_field_section( $instance, __('Container'), 'fs_container' );
2002
  ?>
2003
+ <p>
2004
+ <?php $this->cmw_formfield_textbox( $instance, 'container',
2005
+ array(
2006
+ 'label' => __('Element:'),
2007
+ 'desc' => __('Eg. div or nav; leave empty for no container')
2008
+ ) ); ?>
2009
+ </p>
2010
+ <p>
2011
+ <?php $this->cmw_formfield_textbox( $instance, 'container_id',
2012
+ array(
2013
+ 'label' => __('Unique ID:'),
2014
+ 'desc' => __('An optional ID for the container')
2015
+ ) ); ?>
2016
+ </p>
2017
+ <p>
2018
+ <?php $this->cmw_formfield_textbox( $instance, 'container_class',
2019
+ array(
2020
+ 'label' => __('Class:'),
2021
+ 'desc' => __('Extra class for the container')
2022
+ ) ); ?>
2023
+ </p>
2024
+
2025
+ <?php $this->cmw_close_a_field_section(); ?>
2026
 
2027
  <?php
2028
+ /**
2029
+ * start collapsible section : 'Classes'
2030
+ */
2031
+ $this->cmw_open_a_field_section( $instance, __('Classes'), 'fs_classes' );
2032
  ?>
2033
+ <p>
2034
+ <?php $this->cmw_formfield_textbox( $instance, 'menu_class',
2035
+ array(
2036
+ 'label' => __('Menu Class:'),
2037
+ 'desc' => __('Class for the list element forming the menu')
2038
+ ) ); ?>
2039
+ </p>
2040
+ <p>
2041
+ <?php $this->cmw_formfield_textbox( $instance, 'widget_class',
2042
+ array(
2043
+ 'label' => __('Widget Class:'),
2044
+ 'desc' => __('Extra class for the widget itself')
2045
+ ) ); ?>
2046
+ </p>
2047
+
2048
+ <?php $this->cmw_close_a_field_section(); ?>
2049
 
2050
  <?php
2051
+ /**
2052
+ * start collapsible section : 'Links'
2053
+ */
2054
+ $this->cmw_open_a_field_section( $instance, __('Links'), 'fs_links' );
2055
  ?>
2056
+ <p>
2057
+ <?php $this->cmw_formfield_textbox( $instance, 'before',
2058
+ array(
2059
+ 'label' => __('Before the Link:'),
2060
+ 'desc' => __( htmlspecialchars('Text/HTML to go before the <a> of the link') ),
2061
+ 'fclass' => 'widefat'
2062
+ ) ); ?>
2063
+ </p>
2064
+ <p>
2065
+ <?php $this->cmw_formfield_textbox( $instance, 'after',
2066
+ array(
2067
+ 'label' => __('After the Link:'),
2068
+ 'desc' => __( htmlspecialchars('Text/HTML to go after the <a> of the link') ),
2069
+ 'fclass' => 'widefat'
2070
+ ) ); ?>
2071
+ </p>
2072
+ <p>
2073
+ <?php $this->cmw_formfield_textbox( $instance, 'link_before',
2074
+ array(
2075
+ 'label' => __('Before the Link Text:'),
2076
+ 'desc' => __('Text/HTML to go before the link text'),
2077
+ 'fclass' => 'widefat'
2078
+ ) ); ?>
2079
+ </p>
2080
+ <p>
2081
+ <?php $this->cmw_formfield_textbox( $instance, 'link_after',
2082
+ array(
2083
+ 'label' => __('After the Link Text:'),
2084
+ 'desc' => __('Text/HTML to go after the link text'),
2085
+ 'fclass' => 'widefat'
2086
+ ) ); ?>
2087
+ </p>
2088
+
2089
+ <?php $this->cmw_close_a_field_section(); ?>
2090
 
2091
  </div>
2092
  <?php
2093
 
2094
+ } //end cmw_legacy_form()
2095
+
2096
+ /**
2097
+ * sanitizes the widget settings for cmw_legacy_update(), cmw_legacy_widget() and cmw_legacy_form()
2098
+ *
2099
+ * @param array $from_instance Widget settings
2100
+ * @param array $base_instance Old widget settings or an empty array
2101
+ * @param string $method Name suffix of the calling method
2102
+ * @return array Sanitized widget settings
2103
+ */
2104
+ public function cmw_legacy_settings( $from_instance, $base_instance, $method = 'update' ){
2105
+
2106
+ $instance = is_array( $base_instance ) ? $base_instance : array();
2107
+
2108
+ //switches...
2109
+ foreach( array(
2110
+ 'hide_title' => 0,
2111
+ 'contains_current' => 0, //v2.0.0 added
2112
+ 'depth_rel_current' => 0, //v2.0.0 added
2113
+ 'fallback_no_ancestor' => 0, //v1.1.0 added
2114
+ 'fallback_include_parent' => 0, //v1.1.0 added
2115
+ 'fallback_include_parent_siblings' => 0, //v1.1.0 added
2116
+ 'fallback_no_children' => 0, //v1.2.0 added
2117
+ 'fallback_nc_include_parent' => 0, //v1.2.0 added
2118
+ 'fallback_nc_include_parent_siblings' => 0, //v1.2.0 added
2119
+ 'flat_output' => 0,
2120
+ 'include_parent' => 0,
2121
+ 'include_parent_siblings' => 0, //v1.1.0 added
2122
+ 'include_ancestors' => 0,
2123
+ 'hide_empty' => 0, //v1.1.0: this now only has relevance prior to WP v3.6
2124
+ 'title_from_parent' => 0,
2125
+ 'title_from_current' => 0, //v1.2.0 added
2126
+ 'ol_root' => 0,
2127
+ 'ol_sub' => 0,
2128
+ //field section toggles...
2129
+ 'fs_filter' => 0,
2130
+ 'fs_fallbacks' => 1, //v1.2.0 added
2131
+ 'fs_output' => 1,
2132
+ 'fs_container' => 1,
2133
+ 'fs_classes' => 1,
2134
+ 'fs_links' => 1
2135
+ ) as $k => $v ){
2136
+
2137
+ if( $method == 'form' ){
2138
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? !empty( $from_instance[ $k ] ) : !empty( $v );
2139
+ }elseif( $method == 'widget' ){
2140
+ $instance[ $k ] = !empty( $from_instance[ $k ] );
2141
+ }else{
2142
+ $instance[ $k ] = empty( $from_instance[ $k ] ) ? 0 : 1;
2143
+ }
2144
+
2145
+ }
2146
+
2147
+ //strings...
2148
+ foreach( array(
2149
+ 'title' => '',
2150
+ 'items' => '', //v2.0.0 added
2151
+ 'container' => 'div',
2152
+ 'container_id' => '',
2153
+ 'container_class' => '',
2154
+ 'menu_class' => 'menu-widget',
2155
+ 'widget_class' => ''
2156
+ ) as $k => $v ){
2157
+
2158
+ if( $method == 'form' ){
2159
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? esc_attr( trim( $from_instance[ $k ] ) ) : $v;
2160
+ }elseif( $method == 'widget' ){
2161
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( $from_instance[ $k ] ) : $v; //bug in 2.0.2 fixed!
2162
+ }else{
2163
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? strip_tags( trim( $from_instance[ $k ] ) ) : $v;
2164
+ }
2165
+
2166
+ }
2167
+
2168
+ //html strings...
2169
+ foreach( array(
2170
+ 'before' => '',
2171
+ 'after' => '',
2172
+ 'link_before' => '',
2173
+ 'link_after' => ''
2174
+ ) as $k => $v ){
2175
+
2176
+ if( $method == 'form' ){
2177
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? esc_html( trim( $from_instance[ $k ] ) ) : $v;
2178
+ }elseif( $method == 'widget' ){
2179
+ $instance[ $k ] = empty( $from_instance[ $k ] ) ? $v : trim( $from_instance[ $k ] );
2180
+ }else{
2181
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( $from_instance[ $k ] ) : $v;
2182
+ }
2183
+
2184
+ }
2185
+
2186
+ //integers...
2187
+ foreach( array(
2188
+ 'depth' => 0,
2189
+ 'filter' => -1, //v2.0.0 changed from switch
2190
+ 'filter_item' => -2, //v1.1.0 changed from 0
2191
+ 'menu' => 0,
2192
+ 'start_level' => 1
2193
+ ) as $k => $v ){
2194
+
2195
+ if( $method == 'form' ){
2196
+ $instance[ $k ] = isset( $from_instance[ $k ]) ? max( $v, intval( $from_instance[ $k ] ) ) : max($v, 0);
2197
+ }elseif( $method == 'widget' ){
2198
+ $instance[ $k ] = max( $v, intval( $from_instance[ $k ] ) );
2199
+ }else{
2200
+ $instance[ $k ] = isset( $from_instance[ $k ]) ? max( $v, intval( $from_instance[ $k ] ) ) : $v;
2201
+ }
2202
+
2203
+ }
2204
+
2205
+ //items special case...
2206
+ if( $method == 'update' && !empty( $instance['items'] ) ){
2207
+ $sep = preg_match( '/(^\d+$|,)/', $instance['items'] ) > 0 ? ',' : ' ';
2208
+ $a = array();
2209
+ foreach( preg_split('/[,\s]+/', $instance['items'], -1, PREG_SPLIT_NO_EMPTY ) as $v ){
2210
+ $i = intval( $v );
2211
+ if( $i > 0 ){
2212
+ $a[] = $i;
2213
+ }
2214
+ }
2215
+ $instance['items'] = implode( $sep, $a );
2216
+ }
2217
+
2218
+ //v1.2.1 holds information determined by the walker...
2219
+ $this->_cmw_walker = array();
2220
+
2221
+ return $instance;
2222
+
2223
+ } //end cmw_legacy_settings()
2224
+
2225
+ /**
2226
+ * updates the widget settings sent from the legacy backend admin
2227
+ *
2228
+ * @filters : custom_menu_wizard_prevent_legacy_updates false
2229
+ * @filters : custom_menu_wizard_wipe_on_update false
2230
+ *
2231
+ * @param array $new_instance New widget settings
2232
+ * @param array $old_instance Old widget settings
2233
+ * @return array Sanitized widget settings
2234
+ */
2235
+ public function cmw_legacy_update( $new_instance, $old_instance ){
2236
+
2237
+ //allow a filter to return true, whereby updates to legacy widgets are disallowed...
2238
+ //eg. apply_filter( 'custom_menu_wizard_prevent_legacy_updates', [filter function], 10, 1 ) => true
2239
+ if( !apply_filters( 'custom_menu_wizard_prevent_legacy_updates', false ) ){
2240
+ return $this->cmw_legacy_settings(
2241
+ $new_instance,
2242
+ //allow a filter to return true, whereby any previous settings (now possibly unused) will be wiped instead of being allowed to remain...
2243
+ //eg. add_filter( 'custom_menu_wizard_wipe_on_update', [filter_function], 10, 1 ) => true
2244
+ apply_filters( 'custom_menu_wizard_wipe_on_update', false ) ? array() : $old_instance,
2245
+ 'update' );
2246
+ }else{
2247
+ //prevent the save!...
2248
+ return false;
2249
+ }
2250
+
2251
+ } //end cmw_legacy_update()
2252
+
2253
+ /**
2254
+ * produces the legacy widget HTML at the front end
2255
+ *
2256
+ * @filters : custom_menu_wizard_nav_params array of params that will be sent to wp_nav_menu(), array of instance settings, id base
2257
+ * custom_menu_wizard_settings_pre_widget array of instance settings, id base
2258
+ * custom_menu_wizard_widget_output HTML output string, array of instance settings, id base, $args
2259
+ *
2260
+ * @param object $args Widget arguments
2261
+ * @param array $instance Configuration for this widget instance
2262
+ */
2263
+ public function cmw_legacy_widget( $args, $instance ) {
2264
+
2265
+ //sanitize $instance...
2266
+ $instance = $this->cmw_legacy_settings( $instance, array(), 'widget' );
2267
+
2268
+ //v1.1.0 As of WP v3.6, wp_nav_menu() automatically prevents any HTML output if there are no items...
2269
+ $instance['hide_empty'] = $instance['hide_empty'] && $this->cmw_wp_version('3.6');
2270
+
2271
+ //allow a filter to amend the instance settings prior to producing the widget output...
2272
+ //eg. add_filter( 'custom_menu_wizard_settings_pre_widget', [filter_function], 10, 2 ) => $instance (array)
2273
+ $instance = apply_filters( 'custom_menu_wizard_settings_pre_widget', $instance, $this->id_base );
2274
+
2275
+ //fetch menu...
2276
+ if( !empty($instance['menu'] ) ){
2277
+ $menu = wp_get_nav_menu_object( $instance['menu'] );
2278
+
2279
+ //no menu, no output...
2280
+ if ( !empty( $menu ) ){
2281
+
2282
+ if( !empty( $instance['widget_class'] ) ){
2283
+ //$args['before_widget'] is usually just a DIV start-tag, with an id and a class; if it
2284
+ //gets more complicated than that then this may not work as expected...
2285
+ if( preg_match( '/^<[^>]+?class=["\']/', $args['before_widget'] ) > 0 ){
2286
+ $args['before_widget'] = preg_replace( '/(class=["\'])/', '$1' . $instance['widget_class'] . ' ', $args['before_widget'], 1 );
2287
+ }else{
2288
+ $args['before_widget'] = preg_replace( '/^(<\w+)(\s|>)/', '$1 class="' . $instance['widget_class'] . '"$2', $args['before_widget'] );
2289
+ }
2290
+ }
2291
+
2292
+ if( !empty( $instance['container_class'] ) ){
2293
+ $instance['container_class'] = "menu-{$menu->slug}-container {$instance['container_class']}";
2294
+ }
2295
+
2296
+ $instance['menu_class'] = preg_split( '/\s+/', $instance['menu_class'], -1, PREG_SPLIT_NO_EMPTY );
2297
+ if( $instance['fallback_no_ancestor'] || $instance['fallback_no_children'] ){
2298
+ //v1.2.1 add a cmw-fellback-maybe class to the menu and we'll remove or replace it later...
2299
+ $instance['menu_class'][] = 'cmw-fellback-maybe';
2300
+ }
2301
+ $instance['menu_class'] = implode( ' ', $instance['menu_class'] );
2302
+
2303
+ $walker = new Custom_Menu_Wizard_Walker;
2304
+ $params = array(
2305
+ 'menu' => $menu,
2306
+ 'container' => empty( $instance['container'] ) ? false : $instance['container'], //bug in 2.0.2 fixed!
2307
+ 'container_id' => $instance['container_id'],
2308
+ 'menu_class' => $instance['menu_class'],
2309
+ 'echo' => false,
2310
+ 'fallback_cb' => false,
2311
+ 'before' => $instance['before'],
2312
+ 'after' => $instance['after'],
2313
+ 'link_before' => $instance['link_before'],
2314
+ 'link_after' => $instance['link_after'],
2315
+ 'depth' => empty( $instance['flat_output'] ) ? $instance['depth'] : -1,
2316
+ 'walker' =>$walker,
2317
+ //widget specific stuff...
2318
+ '_custom_menu_wizard' => $instance
2319
+ );
2320
+ //for the walker's use...
2321
+ $params['_custom_menu_wizard']['_walker'] = array();
2322
+
2323
+ if( $instance['ol_root'] ){
2324
+ $params['items_wrap'] = '<ol id="%1$s" class="%2$s">%3$s</ol>';
2325
+ }
2326
+ if( !empty( $instance['container_class'] ) ){
2327
+ $params['container_class'] = $instance['container_class'];
2328
+ }
2329
+
2330
+ add_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
2331
+ if( $instance['hide_empty'] ){
2332
+ add_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
2333
+ }
2334
+
2335
+ //allow a filter to amend the wp_nav_menu() params prior to calling it...
2336
+ //eg. add_filter( 'custom_menu_wizard_nav_params', [filter_function], 10, 3 ) => $params (array)
2337
+ //NB: wp_nav_menu() is in wp-includes/nav-menu-template.php
2338
+ $out = wp_nav_menu( apply_filters( 'custom_menu_wizard_nav_params', $params, $instance, $this->id_base ) );
2339
+
2340
+ remove_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
2341
+ if( $instance['hide_empty'] ){
2342
+ remove_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
2343
+ }
2344
+
2345
+ //only put something out if there is something to put out...
2346
+ if( !empty( $out ) ){
2347
+
2348
+ //title from : 'from parent' has priority over 'from current'...
2349
+ //note that 'parent' is whatever you are getting the children of and therefore doesn't apply to a ShowAll, whereas
2350
+ //'current' is the current menu item (as determined by WP); also note that neither parent nor current actually has
2351
+ //to be present in the results
2352
+ if( $instance['title_from_parent'] && !empty( $this->_cmw_walker['parent_title'] ) ){
2353
+ $title = $this->_cmw_walker['parent_title'];
2354
+ }
2355
+ if( empty( $title ) && $instance['title_from_current'] && !empty( $this->_cmw_walker['current_title'] ) ){
2356
+ $title = $this->_cmw_walker['current_title'];
2357
+ }
2358
+ if( empty( $title ) ){
2359
+ $title = $instance['hide_title'] ? '' : $instance['title'];
2360
+ }
2361
+
2362
+ //remove/replace the cmw-fellback-maybe class...
2363
+ $out = str_replace(
2364
+ 'cmw-fellback-maybe',
2365
+ empty( $this->_cmw_walker['fellback'] ) ? '' : 'cmw-fellback-' . $this->_cmw_walker['fellback'],
2366
+ $out );
2367
+
2368
+ if ( !empty($title) ){
2369
+ $out = $args['before_title'] . apply_filters('widget_title', $title, $instance, $this->id_base) . $args['after_title'] . $out;
2370
+ }
2371
+ $out = $args['before_widget'] . $out . $args['after_widget'];
2372
+ //allow a filter to modify the entire output...
2373
+ //eg. add_filter( 'custom_menu_wizard_widget_output', [filter_function], 10, 4 ) => $output (HTML string)
2374
+ echo apply_filters( 'custom_menu_wizard_widget_output', $out, $instance, $this->id_base, $args );
2375
+ }
2376
+ }
2377
+ }
2378
+
2379
+ } //end cmw_legacy_widget()
2380
 
2381
  } //end of class
2382
  ?>
readme.txt CHANGED
@@ -3,15 +3,15 @@ Contributors: wizzud
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KP2LVCBXNCEB4
4
  Tags: menu,widget,navigation,custom menu,partial menu,current item,current page,menu level,menu branch,menu shortcode,menu widget,advanced,enhanced
5
  Requires at least: 3.6
6
- Tested up to: 4.1
7
- Stable tag: 3.1.4
8
  License: GPLv2 or Later
9
 
10
  Show branches or levels of your menu in a widget, or in content using a shortcode, with full customisation.
11
 
12
  == Description ==
13
 
14
- This plugin is a boosted version of the WordPress "Custom Menu" widget.
15
  It provides full control over most of the parameters available when calling WP's [wp_nav_menu()](http://codex.wordpress.org/Function_Reference/wp_nav_menu) function, as well as providing pre-filtering of the menu items in order to be able to select a specific portion of the custom menu. It also automatically adds a couple of custom classes. And there's a shortcode that enables you to include the widget's output in your content.
16
 
17
  Features include:
@@ -25,27 +25,36 @@ Features include:
25
  * Automatically add cmw-level-N and cmw-has-submenu classes to output menu items
26
  * Allow the widget title to be entered but not output, or to be set from the current menu item or selected branch item
27
  * Select hierarchical or flat output, both options still abiding by the specified number of levels to output
28
- * Add/specify custom class(es) for the widget block, the menu container, and the menu itself
29
  * Modify the link's output with additional HTML around the link's text and/or the link element itself
30
  * Use Ordered Lists (OL) for the top and/or sub levels instead of Unordered Lists (UL)
31
  * Shortcode, `[cmwizard]`, available to run the widget from within content
 
32
  * Interactive "assist" to help with the widget settings and/or shortcode definition
33
  * Utility to find posts containing this plugin's shortcode
34
- * **NEW** Specify an alternative configuration to use under certain conditions (dual-scenario capability)
35
 
36
- Current documentation for the **Widget Options** can be found
37
- under [Other Notes](http://wordpress.org/plugins/custom-menu-wizard/other_notes/).
38
- The associated **Shortcode Parameters** are documented
39
- under [Installation](http://wordpress.org/plugins/custom-menu-wizard/installation/) (too much text to fit all on one page!).
40
 
41
- **Please, do not be put off by the number of options available!** I suspect (and I admit that I'm guessing!) that for the majority of users
 
 
 
 
 
 
42
  there are probably a couple of very common scenarios:
43
 
44
  1. Show an entire menu...
45
  * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
46
  * Select the menu you wish to use (if it's not already selected)
47
  * Save the widget!
48
- * *Equivalent shortcode resembles `[cmwizard menu=N title="Your Title"/]`*
 
 
49
 
50
  2. Show the current menu item, plus any descendants...
51
  * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
@@ -53,7 +62,9 @@ there are probably a couple of very common scenarios:
53
  * Open the FILTERS section :
54
  * under Primary Filter, click on the *Branch* radio
55
  * Save the widget!
56
- * *Equivalent shortcode resembles `[cmwizard menu=N title="Your Title" branch=current/]`*
 
 
57
 
58
  3. Show just the descendants of the current menu item (if there are any)...
59
  * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
@@ -62,7 +73,9 @@ there are probably a couple of very common scenarios:
62
  * under Primary Filter, click on the *Branch* radio
63
  * under Secondary Filter, set *Starting at* to "+1 (children)"
64
  * Save the widget!
65
- * *Equivalent shortcode resembles `[cmwizard menu=N title="Your Title" branch=current start_at="+1"/]`*
 
 
66
 
67
  4. Always show the top level items, but when the menu contains the current item then also show that current item, with its ancestors and immediate children...
68
  * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
@@ -75,40 +88,40 @@ there are probably a couple of very common scenarios:
75
  * set *On condition* to "Current Item is NOT in..." and "Menu" (the 2nd dropdown)
76
  * in the *Then switch settings to* textarea, type in "[cmwizard depth=1/]" (without the quotes!)
77
  * Save the widget!
78
- * *Equivalent shortcode resembles `[cmwizard menu=N branch=current depth=2 ancestors=1 include_level="1" alternative="no-current,menu"]depth=1[/cmwizard]`*
79
 
 
80
 
81
- If you like this widget (or if you don't?), please consider taking a moment or two to give it a
82
- [Review](http://wordpress.org/support/view/plugin-reviews/custom-menu-wizard) : it helps others, and gives me valuable feedback.
83
 
84
- *Documentation for version 2 of the widget
 
 
 
85
  can be found [here](http://plugins.svn.wordpress.org/custom-menu-wizard/tags/2.0.6/readme.txt)
86
  or [here](http://www.wizzud.com/v210-readme.html).*
87
 
88
  == WIDGET OPTIONS ==
89
 
90
- *NB. Version 2 documentation is [here](http://plugins.svn.wordpress.org/custom-menu-wizard/tags/3.0.0/v210-readme.html#Widget-Options).*
91
-
92
  There are quite a few options, which makes the widget settings box very long. I have therefore grouped most of the options into
93
  collapsible logical sections (with remembered state once saved).
94
 
95
- The associated **SHORTCODE PARAMETERS** are documented
96
- under [Installation](http://wordpress.org/plugins/custom-menu-wizard/installation/).
97
 
98
- ***Always Visible***
99
 
100
- * **Title** *textbox*
101
 
102
  Set the title for your widget.
103
 
104
- * **Hide** *checkbox*
105
 
106
  Prevents the entered `Title` being displayed in the front-end widget output.
107
 
108
  In the Widgets admin page, I find it useful to still be able to see the `Title` in the sidebar when the widget is closed, but I
109
  don't necessarily want that `Title` to actually be output when the widget is displayed at the front-end. Hence this checkbox.
110
 
111
- * **Select Menu** *select*
112
 
113
  Choose the appropriate menu from the dropdown list of Custom Menus currently defined in your WordPress application. The
114
  first one available (alphabetically) is already selected for you by default.
@@ -119,38 +132,38 @@ Filters are applied in the order they are presented.
119
 
120
  ***Primary Filter***
121
 
122
- * **Level** *radio (default On) & select*
123
 
124
  Filters by level within the selected menu, starting at the level selected here. This is the default setting
125
  for a new widget instance, which, if left alone and with all other options at their default, will show the entire selected menu.
126
 
127
  Example : If you wanted to show all the options that were at level 3 or below, you could check this radio and set the select to "3".
128
 
129
- * **Branch** *radio & select*
130
 
131
  Filters by branch, with the head item of the branch being selected from the dropdown. The dropdown presents all the
132
  items from the selected menu, plus a "Current Item" option (the default). Selecting "Current Item" means that the head item of the
133
  branch is the current menu item (as indicated by WordPress), provided, of course, that the current menu item actually corresponds to
134
  a menu item from the currently selected menu!
135
 
136
- * **Items** *radio & textbox*
137
 
138
  Filters by the menu items that you specifically pick (by menu item id, as a comma-separated list). The simplest way
139
  to get your list of ids is to use the "assist", and [un]check the green tick box at the right hand side of each depicted menu item that
140
  you want. Alternatively, just type your list of ids into the box.
141
-
142
  If the id is appended with a '+', eg. '23+', then all the item's descendants will also be included.
143
 
144
  Example : If you only wanted to show, say, 5 of your many available menu items, and those 5 items are not in one handy branch of the menu,
145
  then you might want to use this option.
146
-
147
- Example : If your menu has 6 root branches - "A" thru to "F" - and you were only interested in branches "B" (id of, say, 11) and
148
  "E" (id of, say, 19), you could set `Items` to be "11+,19+", which would include "B" with all its descendants, and "E" with all its
149
  descendants.
150
 
151
  ***Secondary Filter*** *(not applicable to an `Items` filter)*
152
 
153
- * **Starting at** *select*
154
 
155
  This is only applicable to a `Branch` filter and it allows you to shift the starting point of your output within the confines
156
  of the selected branch. By default it is set to the selected branch item itself, but it can be changed to a relative of the branch item (eg.
@@ -160,43 +173,43 @@ Filters are applied in the order they are presented.
160
  Example : If you wanted the entire "current" branch then, with `Branch` set to "Current Item", you might set `Starting at` to "1 (root)".
161
  Alternatively, if you wanted the children of the current menu item then `Starting at` could be set to "+1 (children)".
162
 
163
- * **Item, if possible** *radio (default On)*
164
 
165
  This is the default filter mechanism whereby, if `Starting at` can only result in a single item (ie. it is the branch item itself, or
166
  an ancestor thereof) then only that item and its descendants are considered for filtering.
167
 
168
- * **Level** *radio*
169
 
170
  Changes the default filter mechanism such that if `Starting at` results in the selection of the branch item or one of its ancestors,
171
  then all siblings of that resultant item are also included in the secondary filtering process.
172
-
173
  Example : If Joe and Fred are siblings (ie. they have a common parent) and Joe is the selected branch item - with `Starting at` set
174
  to Joe - then the secondary filter would normally only consider Joe and its descendants. However, if `Level` was enabled instead of
175
  `Item`, then both Joe and Fred, *and all their descendants*, would be considered for filtering.
176
-
177
  Note that there is one exception, and that is that if `Starting at` results in a root-level item, then `Allow all Root Items` must
178
  be enabled in order to allow the other sibling root items to be added into the filter process.
179
 
180
- * **Allow all Root Items** *checkbox*
181
 
182
  In the right conditions - see `Level` above - this allows sibling root items to be considered for secondary filtering.
183
 
184
- * **For Depth** *select*
185
 
186
  This the number of levels of the menu structure that will be considered for inclusion in the final output (in complete
187
  ignorance of any subsequent Inclusions or Exclusions).
188
-
189
  The first level of output is the starting level, regardless of
190
  how that starting level is determined (see `Starting at` and `Relative to Current Item` options). So if you ask
191
  for a Depth of 1 level, you get just the starting level; if you ask for a Depth of 2, you get the starting level and
192
  the one below it.
193
 
194
- * **Relative to Current Item** *checkbox*
195
 
196
  By default, `For Depth` (above) is relative to the first item found, but this may be overridden to be relative to the
197
  current menu item ***if*** `For Depth` is not unlimited **and** the current menu item can found within the selected menu.
198
  If the current menu item is not within the selected menu then it falls back to being relative to the first item found.
199
-
200
  Please note that the current item must also be within the constraints set by the `Starting at` option. In other words, if
201
  current item is *above* the `Starting at` level in the menu structure then it will **not** be used to alter the determination of
202
  Depth.
@@ -205,55 +218,55 @@ Filters are applied in the order they are presented.
205
 
206
  These allow certain other items to be added to the output from the secondary filters.
207
 
208
- The first 3 are only applicable to a `Branch` filter. Please note that they only come into effect when the `Branch` filter item is at
209
  or below the `Starting at` level, and do not include any items that would break the depth limit set in the Secondary Filter options.
210
 
211
- * **Branch Ancestors** *select*
212
 
213
  Include any ancestors (parent, grandparent, etc) of the items selected as the `Branch` filter.
214
  Ancestors can be set to go up to an absolute level, or to go up a certain number of levels relative to the `Branch` filter item.
215
 
216
- * **... with Siblings** *select*
217
 
218
  In conjunction with `Branch Ancestors`, also include all siblings of those ancestors.
219
- As with Ancestors, their siblings can be set to go up to an absolute level, or to go up a certain number of levels relative
220
  to the `Branch` filter item. Note that while it is possibe to set a larger range for siblings than ancestors, the final output
221
  is limited by `Branch Ancestors` setting.
222
-
223
- * **Branch Siblings** *checkbox*
224
 
225
  Include any siblings of the item selected as the `Branch` filter (ie. any items at the same level and within
226
  the same branch as the `Branch` item).
227
 
228
- * **Level** *select*
229
 
230
  This allows an entire level of items to be included, optionally also including all levels either above or below it.
231
  This replaces the `All Root Items` checkbox (pre v3.0.4), which only allowed for the inclusion of the root level items.
232
 
233
  ***Exclusions***
234
 
235
- * **Item Ids** *textbox*
236
 
237
- This is a comma-separated list of the ids of menu items that you do *not* want to appear in the final output.
238
- The simplest way to get your list of ids is to use the "assist", and [un]check
239
  the red cross box at the left hand side of each depicted menu item. Alternatively, just type your list of ids into the box.
240
 
241
  If the id is appended with a '+', eg. '23+', then all the item's descendants will also be excluded.
242
 
243
  Example : If you wanted to show the entire "A" branch, with the sole exception of one grandchild of "A", say "ABC", then you could
244
  set `Branch` to "A", and `Exclusions` to the id of the "ABC" item.
245
-
246
  Example : If you have a menu with 4 root items - "A", "B", "C" & "D" - and you wanted to show all items, with descendants, for all bar
247
  the "C" branch, then you could set `Level` to "1 (root)" and `Exclusions` to, say, "12+", where "12" is the menu item id for "C" and
248
  the "+" indicates that all the descendants of "C" should also be excluded.
249
 
250
- * **Level** *select*
251
 
252
  This allows an entire level of items to be excluded, optionally also excluding all levels either above or below it.
253
 
254
  ***Qualifier***
255
 
256
- * **Current Item is in** *select*
257
 
258
  This allows you to specify that there only be any output shown when/if the current menu item is one of the menu items selected
259
  for output at a particular stage in the filter processing.
@@ -261,173 +274,212 @@ or below the `Starting at` level, and do not include any items that would break
261
  * *"Menu"* : the current menu item has to be somewhere within the selected menu.
262
  * *"Primary Filter"* : the current menu item has to be within the scope of the selected primary filter. So if you selected, say, a child
263
  of "A" as the `Branch` item, then if "A" was the current menu item there would be no output with this qualifier.
264
- * *"Secondary Filter"* : the current menu item has to be within the items as restricted by the secondary filters. So if you
265
  selected `Branch` as "A", with `Starting at` set to "+1 (children)", then if "A" was the current menu item there would be no output with this qualifier.
266
  * *"Inclusions"* : the current menu item has to be in within the items as set by the primary and secondary filters, and the inclusions.
267
  * *"Final Output"* : the current menu item has to be in the final output.
268
 
269
  == Fallbacks Section ==
270
 
271
- The **"If Current Item has no children"** fallback gets applied at the Secondary Filter stage, and its eligibility and
 
 
272
  application are therefore determined and governed by the other Secondary Filter settings.
273
 
274
- It only comes into play (possibly) when a `Branch` filter
275
- is set as "Current Item", and the `Starting at` and `For Depth` settings are such that the output should start at or below the current item,
276
- and would normally include some of the current item's descendants (eg. `Starting at` "the Branch", `For Depth` "1 level" does *not* invoke
277
- the fallback).
278
- The fallback allows for the occasion when the current menu item
279
- *does not have* any immediate children, and provides the ability to then switch the following options:
 
280
 
281
- * **Starting at** *select*
282
 
283
- Enable the fallback, and change the `Starting at` from "+1 (children)" to either
 
 
284
 
285
- * *"-1 (parent)"* : the immediate parent of the current menu item.
286
- * *"the Current Item"* : the current menu item itself.
287
 
288
- * **...and Include its Siblings** *checkbox*
289
 
290
- This will add in the siblings of the item selected above.
291
-
292
  Note : This *only* adds the siblings, not the siblings' descendants! However, if the `Level` radio (in Secondary Filter stage above) is
293
  set, then all the item's siblings *and their descendants* will automatically be included, and [un]setting this option will have no effect.
294
  Also note that if the fallback results in a root-level item being selected as the new `Starting at` item, then the inclusion of siblings
295
  outside the current branch depends on the setting of the `Allow all Root Items` checkbox.
296
 
297
- * **For Depth** *select*
298
 
299
  Override the current `For Depth` setting. Note that any depth value set here will be relative to the current item, regardless
300
  of the current setting of `...Relative to`!
301
-
302
  As an example, this option may be useful in the following scenario : item A has 2 children, B and C; B is the current menu item but has
303
  no children, whereas C has loads of children/grandchildren. If you fallback to B's parent - A - with Unlimited depth set, then you will
304
  get A, B, C, and *all* C's dependents! You may well want to override depth to limit the output to, say, just A, B and C, by setting this
305
  fallback option to "1"? Or maybe A, B, C, and C's immediate children, by setting "2"?
306
 
307
- The **"If no Current Item can be found"** fallback is new to v3.1.0. It gets applied right at the start of processing, when determining
308
- which of the menu items (if any) should be regarded as the unique "Current Item" by this widget. Under certain conditions, WordPress
309
- will mark an item as being the parent of a current item ... but there won't actually be a current item marked! This occurs, for example,
310
- when displaying a full post for which there is no specific related menu item, yet there *is* a menu item for a Category that the
311
- displayed post belongs to : WordPress will then mark the related Category as being the parent of the current item (the post) even though
312
- it can't mark the post as being the current item (because there's no specific item for it within the menu).
 
 
 
 
 
313
 
314
- Enabling this fallback will make the widget look for these situations - only as a last resort! - and set (one of) the found "parent" item(s)
315
- as the Current Item.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
 
317
  == Output Section ==
318
 
319
- * **Hierarchical** *radio (default On)*
320
 
321
  Output in the standard nested list format. The alternative is `Flat`.
322
 
323
- * **Flat** *radio*
324
 
325
  Output in a single list format, ignoring any parent-child relationship other than to maintain the same physical order as would be
326
  presented in a `Hierarchical` output (which is the alternative and default).
327
 
328
- * **Set Title from** *checkboxes*
329
 
330
- These allow you to set the `Title` option from a menu item, and, if brought into play, the `Hide` flag is ignored.
331
- Note that the item providing the `Title` only has to be within the selected menu; it does not have to be present in the final output!
332
- Note also that priority is the order presented (first found, first used).
333
 
334
- * **Current Item** : sets `Title` from the current menu item (if current menu item is in the selected menu).
335
- * **...or its Root** : sets `Title` from the root menu item of the branch containing the current menu item (if current menu item is in the selected menu).
336
- * **Branch** : only applicable to a `Branch` filter, and sets `Title` from the `Branch` item.
337
- * **...or its Root** : only applicable to a `Branch` filter, and sets `Title` from the branch's root menu item.
338
 
339
- * **Make it a Link** *checkbox*
340
 
341
- If the widget `Title` does actually get set using one of the `Set Title from` options, then this will put an anchor around the
342
- title, using the information from the menu item that supplies the title.
343
 
344
- * **Change UL to OL** *checkboxes*
345
 
346
- The standard for menus is to use UL (unordered list) elements to display the output. These settings give you the option to
347
- swap the ULs out for OLs (ordered lists).
348
 
349
- * **Top Level** : swap the outermost UL for an OL.
350
- * **Sub-Levels** : swap any nested (ie. not the outermost) ULs for an OLs.
351
 
352
- * **Hide Widget if Empty** *checkbox*
 
353
 
354
- If checked, the widget will not output *any* HTML unless it finds at least one menu item that matches the Filter settings.
355
 
356
- Please note that as of WordPress v3.6, this option becomes superfluous and will **not** be presented (the wp_nav_menu() function
357
- has been modified to automatically suppress all HTML output if there are no items to be displayed). The widget will retain
358
- the setting used on earlier WP versions (in case reversion from WP v3.6 might be required) but *will not present the option
359
- for WP v3.6+*.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
 
361
  == Container Section ==
362
 
363
- * **Element** *textbox (default "div")*
364
 
365
- The menu list is usually wrapped in a "container" element, and this is the tag for that element.
366
- You may change it for another tag, or you may clear it out and the container will be completely removed. Please note that
367
  WordPress is set by default to only accept "div" or "nav", but that could be changed or extended by any theme or plugin.
368
 
369
- * **Unique ID** *textbox*
370
 
371
  This allows you to specify your own id (which should be unique) for the container.
372
 
373
- * **Class** *textbox*
374
 
375
  This allows you to add your own class to the container element.
376
 
377
  == Classes Section ==
378
 
379
- * **Menu Class** *textbox (default "menu-widget")*
380
 
381
  This is the class that will be applied to the list element that holds the entire menu.
382
 
383
- * **Widget Class** *textbox*
384
 
385
  This allows you to add your own class to the outermost element of the widget, the one that wraps the entire widget output.
386
 
387
  == Links Section ==
388
 
389
- * **Before the Link** *textbox*
390
 
391
  Text or HTML that will be placed immediately before each menu item's link.
392
 
393
- * **After the Link** *textbox*
394
 
395
  Text or HTML that will be placed immediately after each menu item's link.
396
 
397
- * **Before the Link Text** *textbox*
398
 
399
  Text or HTML that will be placed immediately before each menu item's link text.
400
 
401
- * **After the Link Text** *textbox*
402
 
403
  Text or HTML that will be placed immediately after each menu item's link text.
404
 
405
  == Alternative Section ==
406
 
407
- This is new at v3.1.0 and provides a limited dual-scenario capability, based on a couple of conditions. For example, let's say you
408
- want to show the Current Item and its immediate children, *but* if there isn't a Current Item then you want to show the top 2 levels
409
  of the menu : previously this was not possible solely with CMW, but now you can configure the main widget settings for the "current item"
410
  scenario, and add an Alternative setting for when no Current Item can be determined.
411
 
412
- * **On condition** *2 selects*
413
 
414
- Select the appropriate condition for when your Alternative configuration should be used, and also the stage within the
415
- Filter processing when this condition should be tested for (similar to the Qualifier, `Current Item is in`). You need
416
  values in both selects for the Alternative to be considered.
417
 
418
- * **Then switch settings to** *textarea*
419
 
420
- This should contain a CMW-generated shortcode equivalent of the configuration that you want to switch to. Please note that leaving
421
- this empty will **not** prevent the Alternative kicking in if the conditions are set and met! An empty `switch to` will merely default
422
  to the CMW's base settings (Level 1, unlimited Depth). Also note that Alternatives cannot be nested : a primary configuration is
423
  allowed one chance to switch and that's it, so providing an Alternative-that-has-an-Alternative will not work.
424
-
425
- The Assist *will work* with an Alternative - in that it displays the appropriate output - but it can get confusing as to which
426
- configuration set is being used. There is a message displayed whenever the Alternative kicks in (green if successful, red if it
427
- should have kicked in but couldn't due to an error in the alternative settings) so please take note of it, because the Assist
428
- *cannot* be used to modify the Alternative settings, only the primary ones.
429
 
430
- A bit more information about the **Alternative** is available
 
 
 
 
431
  in [this article](http://www.wizzud.com/2014/10/03/custom-menu-wizard-wordpress-plugin-version-3-1/).
432
 
433
  == Installation ==
@@ -438,21 +490,18 @@ in [this article](http://www.wizzud.com/2014/10/03/custom-menu-wizard-wordpress-
438
 
439
  1. Activate the plugin through the 'Plugins' menu in your WP Admin
440
 
441
- The widget will now be available in the 'Widgets' admin page.
442
- As long as you already have at least one Custom Menu defined, you can add the new widget to a sidebar and configure it however you want.
443
  Alternatively, you can use the shortcode in your content.
444
 
445
- Current documentation for the **Widget Options** can be found
446
- under [Other Notes](http://wordpress.org/plugins/custom-menu-wizard/other_notes/).
447
 
448
- = SHORTCODE PARAMETERS =
449
 
450
- *NB. Version 2 documentation is [here](http://plugins.svn.wordpress.org/custom-menu-wizard/tags/3.0.0/v210-readme.html#Shortcode-Parameters).*
451
-
452
- The shortcode is **`[cmwizard]`** (prior to v3, shortcode was *`[custom_menu_wizard]`*, and it is still supported but with a slightly
453
- different parameter set).
454
  Most of the attributes reflect the options available to the widget, but some have been simplified for easier use in the shortcode format.
455
- 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
456
  found then there will be no output from the shortcode.
457
 
458
  The simplest way to build a shortcode is to use a widget : as you set options, the equivalent shortcode is displayed at the base of
@@ -461,11 +510,27 @@ shortcode using a widget in the Inactive Widgets area if you have no need for an
461
  with while constructing your shortcode ***do not have to be Saved*** to the widget itself! Just copy-paste the shortcode when
462
  you are happy with it.
463
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  = title =
465
  *string* : The output's `Title`, which may be overridden by **title_from**. Note that there is no shortcode equivalent of the widget's `Hide` option for the title.
466
 
467
  = menu =
468
- *string or integer* : Accepts a menu name or id. If not provided, the shortcode will attempt to find the first menu (alphabetically)
469
  that has menu items attached to it, and use that.
470
 
471
  = level =
@@ -482,156 +547,172 @@ that has menu items attached to it, and use that.
482
  *string* : Comma-separated list of meu item ids, where an id can optionally be followed by a '+' to include all its descendants (eg. "23+"). Takes priority over **branch**.
483
 
484
  = start_at =
485
- *string* : This is only relevant to a `Branch` filter, and consists of a signed or unsigned integer that indicates either a relative
486
- (to the selected branch item) or absolute level to start your output at (ref. the widget's `Starting at` option under *Secondary Filter*,
487
- [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section)).
488
- By default the starting level for output is the branch item's level. A relative level is indicated by a signed (ie. preceded by
489
  a "+" or "-") integer, eg. `start_at="+1"`, while an absolute level is unsigned, eg. `start_at="1"`. Some examples :
490
 
491
  * `start_at="+1"` : (relative) start at the branch item's level + 1 (also accepts `start_at="children"`)
492
  * `start_at="-1"` : (relative) start at the branch item's level - 1 (also accepts `start_at="parent"`)
493
- * `start_at="-2"` : (relative) would be the "grandparent" level
494
  * `start_at="1"` : (absolute) start at the root item of the selected branch (also accepts `start_at="root"`)
495
  * `start_at="2"` : (absolute) start at one level below root (still within the selected branch)
496
 
497
  = start_mode =
498
  *string* : This has only one accepted value - "level" - and is only applicable for a `Branch` filter whose **start_at** setting returns
499
- in an item that is at or above the selected branch item (relatively or absolutely).
500
- Setting `start_mode="level"` forces the widget to use not only the resultant starting item
501
- and its relevant descendants, but also all that item's siblings *and their descendants*
502
- (ref. the widget's `Level` radio option under *Secondary Filter*,
503
- [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section)).
504
 
505
  = allow_all_root =
506
  *switch, off by default, 1 to enable* : See widget's `Allow all Root Items` option, under *Secondary Filter*,
507
- [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section).
508
 
509
  = depth =
510
  *integer, default 0 (unlimited)* : See widget's `For Depth` option, under *Secondary Filter*,
511
- [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section).
512
 
513
  = depth_rel_current =
514
  *switch, off by default, 1 to enable* : See widget's `Relative to Current Item` option, under *Secondary Filter*,
515
- [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section).
516
-
517
  = ancestors =
518
  *integer, default 0 (off)* : Sets an absolute level (positive integer), or a relative number of levels (negative integer), for which
519
- the ancestors of the `Branch` filter item should be included. See widget's `Branch Ancestors` option, under *Inclusions*,
520
- [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section). (only relevant to a `Branch` filter)
521
 
522
  = ancestor_siblings =
523
  *integer, default 0 (off)* : Sets an absolute level (positive integer), or a relative number of levels (negative integer), for which
524
- the siblings of ancestors of the `Branch` filter item should be included. See widget's `... with Siblings` option, under *Inclusions*,
525
- [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section). (only relevant to a `Branch` filter)
526
 
527
  = siblings =
528
- *switch, off by default, 1 to enable* : See widget's `Branch Siblings` option, under *Inclusions*,
529
- [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section). (only relevant to a `Branch` filter)
530
 
531
  = include_level =
532
- *string* : A level (1, 2, 3, etc), optionally followed by a "+" or "-" to include all subsequent (lower) or prior (higher)
533
- levels respectively. For example, "2" will include all items at level 2, whereas "2-" would include all level 1 **and** level 2 items,
534
- and "2+" would include all items at level 2 or greater.
535
 
536
- Note that prior to v3.0.4, this was `include_root` (a switch), which only included the root level : `include_root` is still accepted, even
537
- though now deprecated, and is equivalent to setting `include_level` to "1". However, if `include_level` is specified then it takes precedence.
 
 
 
 
538
 
539
  = exclude =
540
  *string* : Comma-separated list of meu item ids, where an id can optionally be followed by a '+' to include all its descendants (eg. "23+").
541
 
542
  = exclude_level =
543
- *string* : A level (1, 2, 3, etc), optionally followed by a "+" or "-" to include all subsequent (lower) or prior (higher)
544
- levels respectively. For example, "2" will exclude all items at level 2, whereas "2-" would exclude all level 1 **and** level 2 items,
545
- and "2+" would exclude all items at level 2 or greater.
546
 
547
  = contains_current =
548
  *string* : Accepted values : "menu", "primary", "secondary", "inclusions", or "output". See widget's *Qualifier* options,
549
- under [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section),
550
  for an explanation of the respective settings.
551
 
552
  = fallback =
553
- *string* : There are 2 main options for the *"If Current Item has no children"* fallback (ref. [Fallbacks Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Fallbacks-Section))...
554
 
555
- * *"parent"* : Sets the widget's `Starting at` Fallback option to "-1 (parent)"
556
- * *"current"* : Sets the widget's `Starting at` Fallback option to "the Current Item"
 
557
 
558
- Either of these 2 values can be further qualified by appending a comma and a digit, eg. `fallback="current,1"` or `fallback="parent,2"`, which
559
- will also set the widget's `For Depth` fallback option to the value of the digit(s).
 
560
 
561
- Optionally, "+siblings" can also be used (comma-separated, with or without a depth digit) to indicate that siblings of the "parent" or
562
- "current" fallback item should also be included. The order of the comma-separated values is not important, so "current,+siblings,1" is the
563
- same as "current,1,+siblings", and "2,parent" is the same as "parent,2", etc.
 
564
 
565
  = fallback_ci_parent =
566
- *switch, off by default, 1 to enable* : See widget's *"If no Current Item can be found"* fallback in the
567
- [Fallbacks Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Fallbacks-Section).
 
 
 
 
568
 
569
  = flat_output =
570
- *switch, off by default, 1 to enable* : See widget's `Flat` option, under [Output Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Output-Section).
571
 
572
  = title_from =
573
- *string* : Supply one or more (by separating them with a comma, eg. `title_from="branch,current"`) of the following (ref. the widget's `Set Title from` options, under [Output Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Output-Section))...
 
 
 
 
 
 
 
574
 
575
- * *"current"* : enables the widget's *Current Item* `Set Title from` option
576
- * *"current-root"* : enables the widget's *...or its Root* option that relates to the `Current Item` `Set Title from` option
577
- * *"branch"* : enables the widget's *Branch* `Set Title from` option
578
- * *"branch-root"* : enables the widget's *...or its Root* option that relates to the `Branch` `Set Title from` option
579
 
580
  = title_linked =
581
  *switch, off by default, 1 to enable* : Makes the title into a link if the title comes from one of the `title_from` options.
582
 
583
  = ol_root =
584
- *switch, off by default, 1 to enable* : See widget's `Top Level` option, under *Change UL to OL* in the [Output Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Output-Section).
585
 
586
  = ol_sub =
587
- *switch, off by default, 1 to enable* : See widget's `Sub-Levels` option, under *Change UL to OL* in the [Output Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Output-Section).
588
 
589
  = container =
590
- *string* : See widget's `Element` option, under [Container Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Container-Section).
591
 
592
  = container_id =
593
- *string* : See widget's `Unique ID` option, under [Container Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Container-Section).
594
 
595
  = container_class =
596
- *string* : See widget's `Class` option, under [Container Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Container-Section).
597
 
598
  = menu_class =
599
- *string* : See widget's `Menu Class` option, under [Classes Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Classes-Section).
600
 
601
  = widget_class =
602
- *string* : See widget's `Widget Class` option, under [Classes Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Classes-Section).
603
 
604
  = wrap_link =
605
  *string* : This is an optional tag name (eg. "div", "p", "span") that, if provided, will be made into HTML start/end tags
606
- and sent through to the widget as its `Before the Link` and `After the Link` options (ref. [Links Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Links-Section)).
607
  Please note that the shortcode usage - a simple tag name - is much more restrictive than the widget's options, which allow HTML.
608
 
609
  = wrap_link_text =
610
  *string* : This is an optional tag name (eg. "span", "em", "strong") that, if provided, will be made into HTML start/end tags
611
- and sent through to the widget as its `Before the Link Text` and `After the Link Text` options (ref. [Links Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Links-Section)).
612
  Please note that the shortcode usage - a simple tag name - is much more restrictive than the widget's options, which allow HTML.
613
 
614
  = alternative =
615
- *string* : This is 2 settings separated by a comma, reflecting the `On condition` options under the
616
- [Alternative Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Alternative-Section).
617
  Possible values are:
618
 
619
  * One of "current", "no-current" or "no-output" : the condition to test for
620
  * One of "menu", "primary", "secondary", "inclusions", or "output" : the stage at which to test the condition
621
 
622
- Eg. `alternative="no-current,inclusions"` would test for the absence of a Current Item in the filtered menu items, having completed
623
  the Inclusions stage, and attempt to switch to the Alternative settings.
624
 
625
- The actual Alternative settings - a cut-down shortcode - are placed as content between the shortcodes start and end tags, and this is
626
- the only time that the use of a self-terminating shortcode is not sufficient. When specifiying the Alternative settings, *do not*
627
  include the square brackets, otherwise WordPress will interpret it as a nested shortcode!
628
 
629
- For example, to set a primary configuration of "show Current Branch plus any kids", with an Alternative of "show top 2 levels" if no
630
  current item can be found anywhere in the menu...
631
 
632
  `[cmwizard menu=NN branch=current alternative="no-current,menu"]depth=2[/cmwizard]`
633
 
634
- Alternatively, you could switch it around and say the primary configuration is "show top 2 levels", with an Alternative of
635
  "show Current Branch plus kids" if a current item *can* be found within the menu...
636
 
637
  `[cmwizard menu=NN depth=2 alternative="current,menu"]branch=current[/cmwizard]`
@@ -640,82 +721,75 @@ Note that Alternative (eg. "branch=current") does not require a `menu` option, b
640
  configuration's setting is always used.
641
 
642
  As ever, the best way to construct a full shortcode, including an alternative, is to use the Assist : Use one instance of the CMW
643
- widget to build your Alternative settings, copy the equivalent shortcode into the Alternative option of a second instance of the CMW
644
  widget, and then continue configuring that second instance to be your primary configuration; your final shortcode can simply be lifted
645
  from the second instance!
646
 
647
- A bit more information about the Alternative option is available
648
  in [this article](http://www.wizzud.com/2014/10/03/custom-menu-wizard-wordpress-plugin-version-3-1/).
649
 
650
  = title_tag =
651
  *string* : An optional tag name (eg. "h1", "h3", etc) to replace the default "h2" used to enclose the widget title.
652
- Please note that this option has no equivalent in the widget options, because it *only* applies when a widget is instantiated via a shortcode.
653
 
654
  = findme =
655
- *switch, off by default, 1 to enable* : This is a utility intended for editors only, and output is restricted to those with edit_pages capability.
656
- If enabled it will return a list of posts that contain a CMW shortcode. If `findme` is set, the only other option that is taken any
657
- notice of is `title`, which will be output (if supplied) as an H3 in front of the list. The information provided by this utility is also available
658
- from any widget's "assist".
659
 
660
- Example : `[cmwizard findme=1 title="Posts containing a CMW shortcode..."/]`
 
 
661
 
662
  = SHORTCODE EXAMPLES =
663
 
664
  * Show the entire "main" menu
665
 
666
- `
667
- [cmwizard menu=main/]
668
- `
669
 
670
  * Show the children of the current menu item within the "main" menu, for unlimited depth, setting the widget title from the current menu item
671
 
672
- `
673
- [cmwizard menu=main branch=current start_at=children title_from=current/]
674
- `
675
 
676
  * From the "animals" menu, show all the items *immediately* below "Small Dogs", plus "Small Dogs" and its sibling items, as ordered lists
677
 
678
- `
679
- [cmwizard menu="animals" branch="small dogs" depth=2 include="siblings" ol_root=1 ol_sub=1/]
680
- `
681
 
682
  * From the "animals" menu, show the entire "Small Animals" branch, with the sole exception of the "Small Animals" item itself, whenever "Small Animals" or one of its descendants is the current menu item
683
 
684
- `
685
- [cmwizard menu="animals" branch="small animals" start_at=children contains_current=primary/]
686
- `
687
 
688
  * Show the entire "main" menu entitled "Main Menu" *unless* there's a current menu item, in which case show the current menu item, its siblings and its immediate children, and entitle it "Nearest and Dearest!"
689
 
690
- `
691
- [cmwizard menu=main title="Main Menu" alternative="current,menu"]title="Nearest and Dearest!" branch=current depth=2 siblings=1[/cmwizard]
692
- `
693
 
694
  == Frequently Asked Questions ==
695
- If you have a question or problem that is not covered here, please use the [integrated Support forum](http://wordpress.org/support/plugin/custom-menu-wizard).
696
 
697
  = Are there any known problems/restrictions? =
698
  Yep, 'fraid so :
699
 
700
- 1. The widget will only recognise one "current" item (as of v2.0.2, that's the first one encountered; prior to that it was the last one found). It is perfectly possible to have more than one menu item marked as "current", but if CMW has been configured to filter on anything related to a "current menu item" it can only choose one. The simplest example of multiple "current" items is if you add the same page to a menu more than once, but any other plugin that adds and/or manipulates menu items could potentially cause problems for CMW.
701
  2. The widget's "assist" uses jQuery UI's Dialog, which unfortunately (in versions 1.10.3/4) has a *really* annoying bug in its handling of a draggable (ie. when you drag the Dialog's title bar to reposition it on the page) when the page has been scrolled. It is due to be fixed in UI v1.11.0, but meantime I have defaulted the Dialog to fixed position, with an option to toggle back to absolute : it's not perfect but it's the best compromise I can come up with to maintain some sort of useability.
702
 
703
  = Why isn't it working? Why is there no output? =
704
- 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
705
- the option settings on the widget/shortcode. The quickest way to resolve any such issues is to use the widget's interactive "assist", and
706
- 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
707
- infallible (and it's been proven a fair few times!), so if you still have problems then please let me have as much information as possible
708
- (the shortcode for your settings is a good start?) and I will endeavour to help. Please note that simply reporting "It doesn't work" is not
 
 
709
  the most useful of feedbacks, and is unlikely to get a response other than, possibly, a request for more details.
710
 
711
  = How do I use the "assist"? =
712
- The widget's interactive "assist" is specific to each widget instance. It is a javascript-driven *emulator* that uses the widget instance's
713
- option settings - including the menu selected - to build a pictorial representation of the menu and show you, in blue, which menu items will
714
- 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
715
  some of the more advanced HTML-modifying options such as can be found under the Container, Classes or Links sections.
716
- 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
717
- item to change). The "current menu item" is shaded red, with its parent shaded orange and ancestors shaded yellow. All changes in the
718
- "current menu item" and the widget options are immediately reflected by the "assist" (text fields in the widget options simply need to lose
719
  focus).
720
 
721
  The red cross to the left of each menu item toggles the Exclusions setting for the item and/or its descendants. The button has 3 settings :
@@ -724,39 +798,41 @@ The red cross to the left of each menu item toggles the Exclusions setting for t
724
  * Just this item (white on red)
725
  * This item *plus* all its descendants (white on red, with a small yellow plus sign)
726
 
727
- Just click through the toggle states. When the Primary Filter is set to "Items", the green tick buttons to the right of each menu item
728
  work in the same way.
729
 
730
  Note that if a green "Alternate settings" message is showing then the ticks and crosses buttons will show the approriate Alternative
731
  settings but they will be slightly opaque and they will *not* be clickable!
732
 
733
- Once you are happy with the results, having tested all possible settings of "current menu item" (if it applies), then simply Save the widget.
734
- Alternatively, copy-paste the shortcode text - at the base of either the "assist" or the widget form - straight into your post (you do **not** need to Save the widget!).
735
- The widget does not have to Saved to *test* any of the options.
736
 
737
  = Is there an easy way to construct the shortcode? =
738
  Yes, use a widget form. The shortcode for all the selected/specified options is show at the base of the widget (v3+) and the base of the
739
- "assist". The widget does not have to be placed within a widget area, it can also be used from the Inactive Widgets area. And
740
- you do **not** need to Save the widget just to get a shortcode!
 
 
 
741
 
742
  = How do I get the menu item ids for the 'Items' option? =
743
- Use the widget's interactive "assist" (see above). Within the representative menu structure, each menu item's id is set in its title
744
- 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
745
- then show a green tick "checkbox" to the right of each menu item and you simply [un]check the items as required. Each selection will be reflected back into the
746
  widget's `Items` settings, and also in the shortcode texts.
747
 
748
- The more painstaking way is to go to Appearance, Menus and select the relevant menu; hover over the *edit*, *Remove*, or *Cancel* link for an item and look in
749
  the URL (the link's href) for `menu-item=NNN` ... the NNN is the menu item id.
750
 
751
  = How do I get the menu item ids for the 'Exclude Ids' option? =
752
- The "assist" shows a red cross "checkbox" to the left of each menu item, and [un]checking the items will refelect back into the options and
753
  shortcode texts. Otherwise, it's the same principle as outlined above for `Items` ids.
754
 
755
  = What's the difference between including Branch Siblings (or Branch Ancestors + Siblings), and switching to 'Level' instead of 'Item' in the Secondary Filter section? =
756
- If you elect to include Branch [Ancestor] Siblings, you will *only* get the siblings, **not** their descendants (assuming they have any).
757
  On the other hand, if you make `Starting at` use 'Level' instead of 'Item' then siblings *and their descendants* will be added to the filter.
758
 
759
- For example, let's say that Bravo and Charlie are sibling items immediately below Alpha, and that Bravo is the selected Branch Item,
760
  with `Starting at` set to "the Branch" (ie. Bravo). If you switch from "Item" to "Level" then both Bravo, Charlie, *and all their descendants*,
761
  will become eligible for filtering. If you left "Item" enabled, and switched on the inclusion of Branch Siblings, then Bravo and Charlie
762
  would both still be eligible, but only *Bravo's descendants* would be; not Charlie's!
@@ -767,21 +843,25 @@ widget (sidebar, footer, header, ad-hoc, etc?) or the shortcode (page content, p
767
  requirements for styling are likely to be different ... possibly even within the same web page's output. So all styling is down to your theme,
768
  and if you wish to modify it you will need to add to your theme's stylesheet.
769
 
770
- The safest way to do this is via a child theme, so that any changes you make will not be lost if/when the main theme gets updated. The best
771
  way to test your changes is by utilising the developer capabilities that are available in most modern browsers (personally, I could not
772
- do without Firefox and the Firebug extension!) and dynamically applying/modifying styles, possibly utilising the custom classes that the
773
  widget applies to its output, or the Container options for a user-defined id or class.
774
 
775
  = How can I find all my posts/pages that have a CMW shortcode so that I can upgrade them? =
776
- There is a button on the widget's "assist" - `[...]` - that will provide a list of posts/pages whose content, or meta data (custom fields),
777
- contains any CMW shortcode. Each entry is a link that opens the item in a new tab/window. The link's title gives a bit more information :
778
  post type, id, whether the shortcode(s) are in content and/or meta data, and the shortcode(s) concerned.
779
  This utility does not check things like text widgets, plugin-specific tables, theme-provided textareas, etc.
780
 
781
- There is also an extension to the shortcode - `[cmwizard findme=1/]` - that will output the same information, should you not be able to use
782
- the "assist" (for some unknown reason). You may optionally provide a title attribute; any other attributes are ignored.
783
  Note that output from this shortcode extension is restricted to users with edit_pages capability.
784
 
 
 
 
 
785
 
786
  == Screenshots ==
787
  1. Widget (all sections collapsed)
@@ -796,6 +876,12 @@ Note that output from this shortcode extension is restricted to users with edit_
796
 
797
  == Changelog ==
798
 
 
 
 
 
 
 
799
  = 3.1.4 =
800
  * bugfix : in shortcode processing, any supplied Alternative settings weren't being used. thanks corrideat
801
  * bugfix : prevent texturization of shortcode's content, for when it is being used with an Alternative setting
@@ -805,7 +891,7 @@ Note that output from this shortcode extension is restricted to users with edit_
805
  * tweak : minor change to css for the assist when running under the Customizer (WordPress 4.1)
806
 
807
  = 3.1.2 =
808
- * modified the readme : documentation for the Shortcode Parameters has been moved to the Installation page (to avoid being truncated)
809
 
810
  = 3.1.1 =
811
  * bugfix : only show the allow_all_root setting in the shortcode equivalent if the primary filter is by branch
@@ -839,9 +925,9 @@ Note that output from this shortcode extension is restricted to users with edit_
839
  * addition : added a couple of filters
840
 
841
  = 3.0.0 =
842
- * **! Rewrite, and Change of Approach !** The widget has had a major rewrite! The `Children of` filter has been replaced with a `Branch` filter, with a subsequent shift in focus for the secondary filter parameters, from the children's level (0, 1 or more items) up to the branch level (a single item!). This should provide a more intuitive interface, and is definitely easier to code for. **However**, it only affects *new instances* of the widget; v2 instances are still ***fully supported***.
843
 
844
- Please also note that the shortcode tag for v3 has changed to **`[cmwizard]`**, with a revised set of parameters. The old shortcode tag is still supported, but only with the v2 parameter set, and only providing v2 functionality, ie. it is the shortcode tag that determines which widget version to use, and the appropriate parameter set for that version.
845
 
846
  There is no automatic upgrade of widget settings from v2 to v3! I suggest bringing up the "assist" for the existing v2 widget, running it side-by-side with the "assist" of a new instance of the widget, and using them to the compare the desired outputs. I would also strongly recommend that you put your old widgets into the inactive area until you are completely happy with their new replacements. If you are upgrading from version 2, and you would like a bit more information, [this article](http://www.wizzud.com/2014/06/16/custom-menu-wizard-wordpress-plugin-version-3/) might help.
847
  * change : **the minmum requirement for WordPress is v3.6**
@@ -937,6 +1023,12 @@ Note that output from this shortcode extension is restricted to users with edit_
937
 
938
  == Upgrade Notice ==
939
 
 
 
 
 
 
 
940
  = 3.1.4 =
941
  Fixed a couple of bugs in the shortcode processing, for when an Alternative is being used.
942
  Added the ability to make a title into a link when the title has been set from a menu item.
@@ -945,7 +1037,7 @@ Added the ability to make a title into a link when the title has been set from a
945
  Tweaked the assist's css for when running the Customizer in WordPress 4.1.
946
 
947
  = 3.1.2 =
948
- No code changes, just moved the readme's Shortcode Parameters documentation onto the Installation page (to avoid being truncated).
949
 
950
  = 3.1.1 =
951
  Added a work-around for when a theme inadvertently(!) de-registers the widget, which then prevents the shortcode working.
@@ -973,7 +1065,7 @@ Fixed a bug that created new widget instances as legacy version rather than late
973
  Also added a couple of filters.
974
 
975
  = 3.0.0 =
976
- **Rewrite, and change of approach** : __! Important !__ : existing (version 2) widgets and shortcodes *__are fully supported__*. Please [read the Changelog](http://wordpress.org/plugins/custom-menu-wizard/changelog/) *before* upgrading!
977
  Version 3 swaps the *Children-Of* filter for a *Branch* filter, with secondary filters to then refine the branch items. It has better filter capabilities - relative and absolute start level, presence of a current menu item at different stages - and adds exclusion of items by id and/or level. A new shortcode - *[cmwizard]* - has been added to support the v3 functionality.
978
  Changes that also apply to version 2 widgets include : **Minimum requirement for WordPress now v3.6!**; handling of duplicate menu ids, improved compatibility with Widget Customizer (required due to its incorporation into WordPress v3.9 core), and tweaks to the "assist".
979
 
@@ -1000,10 +1092,10 @@ Changed determination of the "current" item such that only the first one encount
1000
  Fixed a bug whereby a test for a specific-items filter prevented show-all from producing any output.
1001
 
1002
  = 2.0.0 =
1003
- **! 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 :
1004
  + if you have set a `Children of` filter, **and** you have changed the `Start Level` to a level greater than 2, or
1005
  + if you have set the `Children of` filter to Current Parent/Root Item, and you have **not** set the "no ancestor" fallback.
1006
- *__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.__*
1007
 
1008
  New options :
1009
  + `Items` allows specific menu item ids to be listed, as an alternative to the other filters
@@ -1015,8 +1107,8 @@ Rebuilt the "demo" facility as an "assist" wizard for the widget It is now fully
1015
  Bugfix : The fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
1016
 
1017
  = 1.2.1 =
1018
- Added a few extra custom classes, and changed the defaults for new widgets such that only the Filter section is open by default.
1019
- Fixed Show All processing so that custom classes always get applied, and 'Title from "Current" Item' works regardless of filter settings.
1020
  Fixed a couple of the shortcode examples in the readme.txt, and added display of the applicable shortcode settings to the demo.html.
1021
 
1022
  = 1.2.0 =
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KP2LVCBXNCEB4
4
  Tags: menu,widget,navigation,custom menu,partial menu,current item,current page,menu level,menu branch,menu shortcode,menu widget,advanced,enhanced
5
  Requires at least: 3.6
6
+ Tested up to: 4.3
7
+ Stable tag: 3.1.5
8
  License: GPLv2 or Later
9
 
10
  Show branches or levels of your menu in a widget, or in content using a shortcode, with full customisation.
11
 
12
  == Description ==
13
 
14
+ This plugin is a boosted version of the WordPress "Custom Menu" widget.
15
  It provides full control over most of the parameters available when calling WP's [wp_nav_menu()](http://codex.wordpress.org/Function_Reference/wp_nav_menu) function, as well as providing pre-filtering of the menu items in order to be able to select a specific portion of the custom menu. It also automatically adds a couple of custom classes. And there's a shortcode that enables you to include the widget's output in your content.
16
 
17
  Features include:
25
  * Automatically add cmw-level-N and cmw-has-submenu classes to output menu items
26
  * Allow the widget title to be entered but not output, or to be set from the current menu item or selected branch item
27
  * Select hierarchical or flat output, both options still abiding by the specified number of levels to output
28
+ * Specify custom class(es) for the widget block, the menu container, and the menu itself
29
  * Modify the link's output with additional HTML around the link's text and/or the link element itself
30
  * Use Ordered Lists (OL) for the top and/or sub levels instead of Unordered Lists (UL)
31
  * Shortcode, `[cmwizard]`, available to run the widget from within content
32
+ * Shortcode can reference a widget instance, making maintenance of multiple occurences of the same (or very similar) shortcode a lot easier
33
  * Interactive "assist" to help with the widget settings and/or shortcode definition
34
  * Utility to find posts containing this plugin's shortcode
35
+ * Specify an alternative configuration to use under certain conditions (dual-scenario capability)
36
 
37
+ Current documentation for the **Widget Options** can be found
38
+ under [Other Notes](https://wordpress.org/plugins/custom-menu-wizard/other_notes/).
39
+ The associated **Shortcode Attributes** are documented
40
+ under [Installation](https://wordpress.org/plugins/custom-menu-wizard/installation/).
41
 
42
+ > You may find that the documentation here is truncated, so I have reproduced the readme.txt
43
+ (no screenshots) as [cmw-doc.html](http://www.wizzud.com/cmw-doc.html).
44
+ This file is also now included in the plugin download, and is linked to from the Custom Menu Wizard entry
45
+ on the admin Plugins page.
46
+ My apologies if this causes - or has caused - any confusion.
47
+
48
+ **Please, do not be put off by the number of options available!** I suspect (and I admit that I'm guessing!) that for the majority of users
49
  there are probably a couple of very common scenarios:
50
 
51
  1. Show an entire menu...
52
  * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
53
  * Select the menu you wish to use (if it's not already selected)
54
  * Save the widget!
55
+ * *Equivalent shortcode resembles...*
56
+
57
+ `[cmwizard menu=N title="Your Title"/]`
58
 
59
  2. Show the current menu item, plus any descendants...
60
  * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
62
  * Open the FILTERS section :
63
  * under Primary Filter, click on the *Branch* radio
64
  * Save the widget!
65
+ * *Equivalent shortcode resembles...*
66
+
67
+ `[cmwizard menu=N title="Your Title" branch=current/]`
68
 
69
  3. Show just the descendants of the current menu item (if there are any)...
70
  * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
73
  * under Primary Filter, click on the *Branch* radio
74
  * under Secondary Filter, set *Starting at* to "+1 (children)"
75
  * Save the widget!
76
+ * *Equivalent shortcode resembles...*
77
+
78
+ `[cmwizard menu=N title="Your Title" branch=current start_at="+1"/]`
79
 
80
  4. Always show the top level items, but when the menu contains the current item then also show that current item, with its ancestors and immediate children...
81
  * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
88
  * set *On condition* to "Current Item is NOT in..." and "Menu" (the 2nd dropdown)
89
  * in the *Then switch settings to* textarea, type in "[cmwizard depth=1/]" (without the quotes!)
90
  * Save the widget!
91
+ * *Equivalent shortcode resembles...*
92
 
93
+ `[cmwizard menu=N branch=current depth=2 ancestors=1 include_level="1" alternative="no-current,menu"]depth=1[/cmwizard]`
94
 
 
 
95
 
96
+ If you like this widget (or if you don't?), please consider taking a moment or two to give it a
97
+ [Review](https://wordpress.org/support/view/plugin-reviews/custom-menu-wizard) : it helps others, and gives me valuable feedback.
98
+
99
+ *Documentation for version 2 of the widget
100
  can be found [here](http://plugins.svn.wordpress.org/custom-menu-wizard/tags/2.0.6/readme.txt)
101
  or [here](http://www.wizzud.com/v210-readme.html).*
102
 
103
  == WIDGET OPTIONS ==
104
 
 
 
105
  There are quite a few options, which makes the widget settings box very long. I have therefore grouped most of the options into
106
  collapsible logical sections (with remembered state once saved).
107
 
108
+ The associated **SHORTCODE ATTRIBUTES** are documented
109
+ under [Installation](https://wordpress.org/plugins/custom-menu-wizard/installation/).
110
 
111
+ _**Always Visible**_
112
 
113
+ * **Title** *(textbox)*
114
 
115
  Set the title for your widget.
116
 
117
+ * **Hide** *(checkbox)*
118
 
119
  Prevents the entered `Title` being displayed in the front-end widget output.
120
 
121
  In the Widgets admin page, I find it useful to still be able to see the `Title` in the sidebar when the widget is closed, but I
122
  don't necessarily want that `Title` to actually be output when the widget is displayed at the front-end. Hence this checkbox.
123
 
124
+ * **Select Menu** *(select)*
125
 
126
  Choose the appropriate menu from the dropdown list of Custom Menus currently defined in your WordPress application. The
127
  first one available (alphabetically) is already selected for you by default.
132
 
133
  ***Primary Filter***
134
 
135
+ * **Level** *(radio, default On, & select)*
136
 
137
  Filters by level within the selected menu, starting at the level selected here. This is the default setting
138
  for a new widget instance, which, if left alone and with all other options at their default, will show the entire selected menu.
139
 
140
  Example : If you wanted to show all the options that were at level 3 or below, you could check this radio and set the select to "3".
141
 
142
+ * **Branch** *(radio & select)*
143
 
144
  Filters by branch, with the head item of the branch being selected from the dropdown. The dropdown presents all the
145
  items from the selected menu, plus a "Current Item" option (the default). Selecting "Current Item" means that the head item of the
146
  branch is the current menu item (as indicated by WordPress), provided, of course, that the current menu item actually corresponds to
147
  a menu item from the currently selected menu!
148
 
149
+ * **Items** *(radio & textbox)*
150
 
151
  Filters by the menu items that you specifically pick (by menu item id, as a comma-separated list). The simplest way
152
  to get your list of ids is to use the "assist", and [un]check the green tick box at the right hand side of each depicted menu item that
153
  you want. Alternatively, just type your list of ids into the box.
154
+
155
  If the id is appended with a '+', eg. '23+', then all the item's descendants will also be included.
156
 
157
  Example : If you only wanted to show, say, 5 of your many available menu items, and those 5 items are not in one handy branch of the menu,
158
  then you might want to use this option.
159
+
160
+ Example : If your menu has 6 root branches - "A" thru to "F" - and you were only interested in branches "B" (id of, say, 11) and
161
  "E" (id of, say, 19), you could set `Items` to be "11+,19+", which would include "B" with all its descendants, and "E" with all its
162
  descendants.
163
 
164
  ***Secondary Filter*** *(not applicable to an `Items` filter)*
165
 
166
+ * **Starting at** *(select)*
167
 
168
  This is only applicable to a `Branch` filter and it allows you to shift the starting point of your output within the confines
169
  of the selected branch. By default it is set to the selected branch item itself, but it can be changed to a relative of the branch item (eg.
173
  Example : If you wanted the entire "current" branch then, with `Branch` set to "Current Item", you might set `Starting at` to "1 (root)".
174
  Alternatively, if you wanted the children of the current menu item then `Starting at` could be set to "+1 (children)".
175
 
176
+ * **Item, if possible** *(radio, default On)*
177
 
178
  This is the default filter mechanism whereby, if `Starting at` can only result in a single item (ie. it is the branch item itself, or
179
  an ancestor thereof) then only that item and its descendants are considered for filtering.
180
 
181
+ * **Level** *(radio)*
182
 
183
  Changes the default filter mechanism such that if `Starting at` results in the selection of the branch item or one of its ancestors,
184
  then all siblings of that resultant item are also included in the secondary filtering process.
185
+
186
  Example : If Joe and Fred are siblings (ie. they have a common parent) and Joe is the selected branch item - with `Starting at` set
187
  to Joe - then the secondary filter would normally only consider Joe and its descendants. However, if `Level` was enabled instead of
188
  `Item`, then both Joe and Fred, *and all their descendants*, would be considered for filtering.
189
+
190
  Note that there is one exception, and that is that if `Starting at` results in a root-level item, then `Allow all Root Items` must
191
  be enabled in order to allow the other sibling root items to be added into the filter process.
192
 
193
+ * **Allow all Root Items** *(checkbox)*
194
 
195
  In the right conditions - see `Level` above - this allows sibling root items to be considered for secondary filtering.
196
 
197
+ * **For Depth** *(select)*
198
 
199
  This the number of levels of the menu structure that will be considered for inclusion in the final output (in complete
200
  ignorance of any subsequent Inclusions or Exclusions).
201
+
202
  The first level of output is the starting level, regardless of
203
  how that starting level is determined (see `Starting at` and `Relative to Current Item` options). So if you ask
204
  for a Depth of 1 level, you get just the starting level; if you ask for a Depth of 2, you get the starting level and
205
  the one below it.
206
 
207
+ * **Relative to Current Item** *(checkbox)*
208
 
209
  By default, `For Depth` (above) is relative to the first item found, but this may be overridden to be relative to the
210
  current menu item ***if*** `For Depth` is not unlimited **and** the current menu item can found within the selected menu.
211
  If the current menu item is not within the selected menu then it falls back to being relative to the first item found.
212
+
213
  Please note that the current item must also be within the constraints set by the `Starting at` option. In other words, if
214
  current item is *above* the `Starting at` level in the menu structure then it will **not** be used to alter the determination of
215
  Depth.
218
 
219
  These allow certain other items to be added to the output from the secondary filters.
220
 
221
+ The first 3 are only applicable to a `Branch` filter. Please note that they only come into effect when the `Branch` filter item is at
222
  or below the `Starting at` level, and do not include any items that would break the depth limit set in the Secondary Filter options.
223
 
224
+ * **Branch Ancestors** *(select)*
225
 
226
  Include any ancestors (parent, grandparent, etc) of the items selected as the `Branch` filter.
227
  Ancestors can be set to go up to an absolute level, or to go up a certain number of levels relative to the `Branch` filter item.
228
 
229
+ * **... with Siblings** *(select)*
230
 
231
  In conjunction with `Branch Ancestors`, also include all siblings of those ancestors.
232
+ As with Ancestors, their siblings can be set to go up to an absolute level, or to go up a certain number of levels relative
233
  to the `Branch` filter item. Note that while it is possibe to set a larger range for siblings than ancestors, the final output
234
  is limited by `Branch Ancestors` setting.
235
+
236
+ * **Branch Siblings** *(checkbox)*
237
 
238
  Include any siblings of the item selected as the `Branch` filter (ie. any items at the same level and within
239
  the same branch as the `Branch` item).
240
 
241
+ * **Level** *(select)*
242
 
243
  This allows an entire level of items to be included, optionally also including all levels either above or below it.
244
  This replaces the `All Root Items` checkbox (pre v3.0.4), which only allowed for the inclusion of the root level items.
245
 
246
  ***Exclusions***
247
 
248
+ * **Item Ids** *(textbox)*
249
 
250
+ This is a comma-separated list of the ids of menu items that you do *not* want to appear in the final output.
251
+ The simplest way to get your list of ids is to use the "assist", and [un]check
252
  the red cross box at the left hand side of each depicted menu item. Alternatively, just type your list of ids into the box.
253
 
254
  If the id is appended with a '+', eg. '23+', then all the item's descendants will also be excluded.
255
 
256
  Example : If you wanted to show the entire "A" branch, with the sole exception of one grandchild of "A", say "ABC", then you could
257
  set `Branch` to "A", and `Exclusions` to the id of the "ABC" item.
258
+
259
  Example : If you have a menu with 4 root items - "A", "B", "C" & "D" - and you wanted to show all items, with descendants, for all bar
260
  the "C" branch, then you could set `Level` to "1 (root)" and `Exclusions` to, say, "12+", where "12" is the menu item id for "C" and
261
  the "+" indicates that all the descendants of "C" should also be excluded.
262
 
263
+ * **Level** *(select)*
264
 
265
  This allows an entire level of items to be excluded, optionally also excluding all levels either above or below it.
266
 
267
  ***Qualifier***
268
 
269
+ * **Current Item is in** *(select)*
270
 
271
  This allows you to specify that there only be any output shown when/if the current menu item is one of the menu items selected
272
  for output at a particular stage in the filter processing.
274
  * *"Menu"* : the current menu item has to be somewhere within the selected menu.
275
  * *"Primary Filter"* : the current menu item has to be within the scope of the selected primary filter. So if you selected, say, a child
276
  of "A" as the `Branch` item, then if "A" was the current menu item there would be no output with this qualifier.
277
+ * *"Secondary Filter"* : the current menu item has to be within the items as restricted by the secondary filters. So if you
278
  selected `Branch` as "A", with `Starting at` set to "+1 (children)", then if "A" was the current menu item there would be no output with this qualifier.
279
  * *"Inclusions"* : the current menu item has to be in within the items as set by the primary and secondary filters, and the inclusions.
280
  * *"Final Output"* : the current menu item has to be in the final output.
281
 
282
  == Fallbacks Section ==
283
 
284
+ ***If Current Item has no children***
285
+
286
+ This gets applied at the Secondary Filter stage, and its eligibility and
287
  application are therefore determined and governed by the other Secondary Filter settings.
288
 
289
+ It only comes into play (possibly) when a `Branch` filter is set as "Current Item", and the `Starting at`
290
+ and `For Depth` settings are such that the output should start at or below the current item,
291
+ and would normally include some of the current item's descendants (eg. `Starting at` "the Branch",
292
+ `For Depth` "1 level" does *not* invoke the fallback).
293
+ The fallback allows for the occasion when the current menu item *does not have* any immediate children.
294
+
295
+ * **Unlabelled Select** *(select)*
296
 
297
+ Enable the fallback by selecting one of
298
 
299
+ * *Start at : -1 (parent)* : overrides the `Starting at` option to be the immediate parent of the Current Item
300
+ * *Start at : the Current Item* : overrides the `Starting at` option to be the Current Item
301
+ * *No output!* : self-explanatory
302
 
303
+ * **...and Include its Siblings** *(checkbox)*
 
304
 
305
+ This will add in the siblings of the item selected above (excluding the "No output!" setting!).
306
 
 
 
307
  Note : This *only* adds the siblings, not the siblings' descendants! However, if the `Level` radio (in Secondary Filter stage above) is
308
  set, then all the item's siblings *and their descendants* will automatically be included, and [un]setting this option will have no effect.
309
  Also note that if the fallback results in a root-level item being selected as the new `Starting at` item, then the inclusion of siblings
310
  outside the current branch depends on the setting of the `Allow all Root Items` checkbox.
311
 
312
+ * **For Depth** *(select)*
313
 
314
  Override the current `For Depth` setting. Note that any depth value set here will be relative to the current item, regardless
315
  of the current setting of `...Relative to`!
316
+
317
  As an example, this option may be useful in the following scenario : item A has 2 children, B and C; B is the current menu item but has
318
  no children, whereas C has loads of children/grandchildren. If you fallback to B's parent - A - with Unlimited depth set, then you will
319
  get A, B, C, and *all* C's dependents! You may well want to override depth to limit the output to, say, just A, B and C, by setting this
320
  fallback option to "1"? Or maybe A, B, C, and C's immediate children, by setting "2"?
321
 
322
+ ***If no Current Item can be found***
323
+
324
+ * **Try items marked Parent of Current** *(checkbox)*
325
+
326
+ This gets applied right at the start of processing, when determining
327
+ which of the menu items (if any) should be regarded as the unique "Current Item" by this widget.
328
+ Under certain conditions, WordPress will mark an item as being the parent of a current item ...
329
+ but there won't actually be a current item marked! This occurs, for example, when displaying a full post for which there is
330
+ no specific related menu item, yet there *is* a menu item for a Category that the displayed post belongs to :
331
+ WordPress will then mark the related Category as being the parent of the current item (the post) even though
332
+ it can't mark the post as being the current item (because there's no specific item for it within the menu).
333
 
334
+ Enabling this fallback will make the widget look for these situations - only as a last resort! -
335
+ and set (one of) the found "parent" item(s) as the Current Item.
336
+
337
+ ***If more than 1 possible Current Item***
338
+
339
+ * **Use the last one found** *(checkbox)*
340
+
341
+ Occasionally it is possible for CMW to have more than one possible candidate for Current Item. Since there can only be one
342
+ Current Item, CMW picks the *first one* encountered. However, this may cause a problem where, for example, a root level item **and**
343
+ one of its sub-menu items are *both* set to list items from Category A, and the page being displayed is a full post that belongs
344
+ to category A : CMW will more than likely determine that the root level item is the Current Item, whereas you really need the
345
+ sub-menu item to be the Current Item (probably to maintain consistency with what is produced when other sub-menu items are "current").
346
+
347
+ Enabling this fallback will make CMW use the last-found (instead of first-found) candidate for Current item, ie. when
348
+ the choice is between a submenu item or its parent item, the submenu item will be used.
349
+
350
+ Note that this option is most likely to only have any effect when the `If no Current Item can be found` fallback (above) is
351
+ enabled, but given that any other plugin/theme could affect the menu item structure that gets passed thru to CMW it is not
352
+ impossible for other configurations to also be affected.
353
 
354
  == Output Section ==
355
 
356
+ * **Hierarchical** *(radio, default On)*
357
 
358
  Output in the standard nested list format. The alternative is `Flat`.
359
 
360
+ * **Flat** *(radio)*
361
 
362
  Output in a single list format, ignoring any parent-child relationship other than to maintain the same physical order as would be
363
  presented in a `Hierarchical` output (which is the alternative and default).
364
 
365
+ ***Set Title from***
366
 
367
+ These allow you to set the `Title` option from a menu item, and, if brought into play, the `Hide` flag is ignored.
368
+ Note that the item providing the `Title` only has to be within the selected menu; it does not have to be present in the final output!
369
+ Note also that a `Current Item` setting will be used in preference to a `Branch Item` setting.
370
 
371
+ A relative setting - such as `Currrent Item` "-2 levels" - will top out at the root-level ancestor (which
372
+ could be the Current/Branch Item!) if there aren't enough ancestors available.
373
+ Also, an absolute setting - such as `Branch Item` "level 4" - will bottom out at the Current/Branch Item
374
+ if it's at/above the absolute level specified.
375
 
376
+ * **Current Item** *(select)*
377
 
378
+ Sets `Title` from the current menu item (if current menu item is in the selected menu), or an ancestor
379
+ of that item, either at an absolute or relative level.
380
 
381
+ * **Branch Item** *(select)
382
 
383
+ Only applicable to a `Branch` filter, and sets `Title` from the `Branch` item, or an ancestor
384
+ of that item, either at an absolute or relative level.
385
 
386
+ * **Make it a Link** *(checkbox)*
 
387
 
388
+ If the widget `Title` does actually get set using one of the options above, then this will
389
+ put an anchor around the title, using the information from the menu item that supplies the title.
390
 
391
+ ***Change UL to OL***
392
 
393
+ The standard for menus is to use UL (unordered list) elements to display the output. These settings give you the option to
394
+ swap the ULs out for OLs (ordered lists).
395
+
396
+ * **Top Level** *(checkbox)*
397
+
398
+ Swap the outermost UL for an OL.
399
+
400
+ * **Sub-Levels** *(checkbox)*
401
+
402
+ Swap any nested (ie. not the outermost) ULs for an OLs.
403
+
404
+ ***Pre WordPress 3.6***
405
+
406
+ Please note that as of WordPress v3.6, the `Hide Widget if Empty` option becomes superfluous and will **not**
407
+ be presented (the wp_nav_menu() function has been modified to automatically suppress all HTML output if there
408
+ are no items to be displayed).
409
+
410
+ * **Hide Widget if Empty** *(checkbox)*
411
+
412
+ If checked, the widget will not output *any* HTML unless it finds at least one menu item that matches the Filter settings.
413
 
414
  == Container Section ==
415
 
416
+ * **Element** *(textbox, default "div")*
417
 
418
+ The menu list is usually wrapped in a "container" element, and this is the tag for that element.
419
+ You may change it for another tag, or you may clear it out and the container will be completely removed. Please note that
420
  WordPress is set by default to only accept "div" or "nav", but that could be changed or extended by any theme or plugin.
421
 
422
+ * **Unique ID** *(textbox)*
423
 
424
  This allows you to specify your own id (which should be unique) for the container.
425
 
426
+ * **Class** *(textbox)*
427
 
428
  This allows you to add your own class to the container element.
429
 
430
  == Classes Section ==
431
 
432
+ * **Menu Class** *(textbox, default "menu-widget")*
433
 
434
  This is the class that will be applied to the list element that holds the entire menu.
435
 
436
+ * **Widget Class** *(textbox)*
437
 
438
  This allows you to add your own class to the outermost element of the widget, the one that wraps the entire widget output.
439
 
440
  == Links Section ==
441
 
442
+ * **Before the Link** *(textbox)*
443
 
444
  Text or HTML that will be placed immediately before each menu item's link.
445
 
446
+ * **After the Link** *(textbox)*
447
 
448
  Text or HTML that will be placed immediately after each menu item's link.
449
 
450
+ * **Before the Link Text** *(textbox)*
451
 
452
  Text or HTML that will be placed immediately before each menu item's link text.
453
 
454
+ * **After the Link Text** *(textbox)*
455
 
456
  Text or HTML that will be placed immediately after each menu item's link text.
457
 
458
  == Alternative Section ==
459
 
460
+ This is new at v3.1.0 and provides a limited dual-scenario capability, based on a couple of conditions. For example, let's say you
461
+ want to show the Current Item and its immediate children, *but* if there isn't a Current Item then you want to show the top 2 levels
462
  of the menu : previously this was not possible solely with CMW, but now you can configure the main widget settings for the "current item"
463
  scenario, and add an Alternative setting for when no Current Item can be determined.
464
 
465
+ * **On condition** *(2 selects)*
466
 
467
+ Select the appropriate condition for when your Alternative configuration should be used, and also the stage within the
468
+ Filter processing when this condition should be tested for (similar to the Qualifier, `Current Item is in`). You need
469
  values in both selects for the Alternative to be considered.
470
 
471
+ * **Then switch settings to** *(textarea)*
472
 
473
+ This should contain a CMW-generated shortcode equivalent of the configuration that you want to switch to. Please note that leaving
474
+ this empty will **not** prevent the Alternative kicking in if the conditions are set and met! An empty `switch to` will merely default
475
  to the CMW's base settings (Level 1, unlimited Depth). Also note that Alternatives cannot be nested : a primary configuration is
476
  allowed one chance to switch and that's it, so providing an Alternative-that-has-an-Alternative will not work.
 
 
 
 
 
477
 
478
+ The Assist *will work* with an Alternative - in that it displays the appropriate output - but it can get confusing as to which
479
+ configuration set is being used. There is a message displayed whenever the Alternative kicks in (green if successful, red if it
480
+ should have kicked in but couldn't due to an error in the alternative settings) so please take note of it.
481
+
482
+ A bit more information about the **Alternative** is available
483
  in [this article](http://www.wizzud.com/2014/10/03/custom-menu-wizard-wordpress-plugin-version-3-1/).
484
 
485
  == Installation ==
490
 
491
  1. Activate the plugin through the 'Plugins' menu in your WP Admin
492
 
493
+ The widget will now be available in the 'Widgets' admin page.
494
+ As long as you already have at least one Custom Menu defined, you can add the new widget to a sidebar and configure it however you want.
495
  Alternatively, you can use the shortcode in your content.
496
 
497
+ Current documentation for the **Widget Options** can be found
498
+ under [Other Notes](https://wordpress.org/plugins/custom-menu-wizard/other_notes/).
499
 
500
+ = SHORTCODE ATTRIBUTES =
501
 
502
+ The shortcode is **`[cmwizard]`**.
 
 
 
503
  Most of the attributes reflect the options available to the widget, but some have been simplified for easier use in the shortcode format.
504
+ 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
505
  found then there will be no output from the shortcode.
506
 
507
  The simplest way to build a shortcode is to use a widget : as you set options, the equivalent shortcode is displayed at the base of
510
  with while constructing your shortcode ***do not have to be Saved*** to the widget itself! Just copy-paste the shortcode when
511
  you are happy with it.
512
 
513
+ = widget =
514
+ *integer* : **!NEW!** *from v3.1.5*, the shortcode will accept a `widget=N` attribute which will load an
515
+ existing widget instance.
516
+
517
+ The shortcode - resembling **`[cmwizard widget=N/]`**, where **N** is an integer - is provided at the base
518
+ of each widget, below the widget's "equivalent" shortcode.
519
+
520
+ It will look for the instance in all active sidebars, *and* the Inactive Widgets area.
521
+ You can prevent inspection of the Inactive Widgets area by adding an `inactive=0` attribute.
522
+ It will ***not*** look in any other *Inactive Sidebar...* area unless you specifically tell it to do so by
523
+ adding an `orphaned=1` attribute.
524
+
525
+ Using this attribute reduces the shortcode length, and may cut down on maintenance where
526
+ you have the same shortcode in a number of places ... as long as you are prepared to keep the widget instance (even if it's in the
527
+ Inactive Widgets area). You can override the widget instance's settings by supplying any of the standard shortcode attributes.
528
+
529
  = title =
530
  *string* : The output's `Title`, which may be overridden by **title_from**. Note that there is no shortcode equivalent of the widget's `Hide` option for the title.
531
 
532
  = menu =
533
+ *string or integer* : Accepts a menu name or id. If not provided, the shortcode will attempt to find the first menu (alphabetically)
534
  that has menu items attached to it, and use that.
535
 
536
  = level =
547
  *string* : Comma-separated list of meu item ids, where an id can optionally be followed by a '+' to include all its descendants (eg. "23+"). Takes priority over **branch**.
548
 
549
  = start_at =
550
+ *string* : This is only relevant to a `Branch` filter, and consists of a signed or unsigned integer that indicates either a relative
551
+ (to the selected branch item) or absolute level to start your output at (ref. the widget's `Starting at` option under *Secondary Filter*,
552
+ [Filters Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section)).
553
+ By default the starting level for output is the branch item's level. A relative level is indicated by a signed (ie. preceded by
554
  a "+" or "-") integer, eg. `start_at="+1"`, while an absolute level is unsigned, eg. `start_at="1"`. Some examples :
555
 
556
  * `start_at="+1"` : (relative) start at the branch item's level + 1 (also accepts `start_at="children"`)
557
  * `start_at="-1"` : (relative) start at the branch item's level - 1 (also accepts `start_at="parent"`)
558
+ * `start_at="-2"` : (relative) would be the "grandparent" level
559
  * `start_at="1"` : (absolute) start at the root item of the selected branch (also accepts `start_at="root"`)
560
  * `start_at="2"` : (absolute) start at one level below root (still within the selected branch)
561
 
562
  = start_mode =
563
  *string* : This has only one accepted value - "level" - and is only applicable for a `Branch` filter whose **start_at** setting returns
564
+ in an item that is at or above the selected branch item (relatively or absolutely).
565
+ Setting `start_mode="level"` forces the widget to use not only the resultant starting item
566
+ and its relevant descendants, but also all that item's siblings *and their descendants*
567
+ (ref. the widget's `Level` radio option under *Secondary Filter*,
568
+ [Filters Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section)).
569
 
570
  = allow_all_root =
571
  *switch, off by default, 1 to enable* : See widget's `Allow all Root Items` option, under *Secondary Filter*,
572
+ [Filters Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section).
573
 
574
  = depth =
575
  *integer, default 0 (unlimited)* : See widget's `For Depth` option, under *Secondary Filter*,
576
+ [Filters Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section).
577
 
578
  = depth_rel_current =
579
  *switch, off by default, 1 to enable* : See widget's `Relative to Current Item` option, under *Secondary Filter*,
580
+ [Filters Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section).
581
+
582
  = ancestors =
583
  *integer, default 0 (off)* : Sets an absolute level (positive integer), or a relative number of levels (negative integer), for which
584
+ the ancestors of the `Branch` filter item should be included. See widget's `Branch Ancestors` option, under *Inclusions*,
585
+ [Filters Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section). (only relevant to a `Branch` filter)
586
 
587
  = ancestor_siblings =
588
  *integer, default 0 (off)* : Sets an absolute level (positive integer), or a relative number of levels (negative integer), for which
589
+ the siblings of ancestors of the `Branch` filter item should be included. See widget's `... with Siblings` option, under *Inclusions*,
590
+ [Filters Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section). (only relevant to a `Branch` filter)
591
 
592
  = siblings =
593
+ *switch, off by default, 1 to enable* : See widget's `Branch Siblings` option, under *Inclusions*,
594
+ [Filters Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section). (only relevant to a `Branch` filter)
595
 
596
  = include_level =
597
+ *string* : A level (1, 2, 3, etc), optionally followed by a "+" or "-" to include all subsequent (lower) or prior (higher)
598
+ levels respectively. For example :
 
599
 
600
+ * `include_level="2"` : include all items at level 2
601
+ * `include_level="2-"` : include all level 1 **and** level 2 items
602
+ * `include_level="2+"` : include all items at level 2 or greater.
603
+
604
+ Note that prior to v3.0.4, this was **include_root** (a switch), which only included the root level : `include_root=1` is still accepted, even
605
+ though now deprecated, and is equivalent to setting `include_level="1"`. However, if **include_level** is specified then it takes precedence.
606
 
607
  = exclude =
608
  *string* : Comma-separated list of meu item ids, where an id can optionally be followed by a '+' to include all its descendants (eg. "23+").
609
 
610
  = exclude_level =
611
+ *string* : A level (1, 2, 3, etc), optionally followed by a "+" or "-" to exclude all subsequent (lower) or prior (higher)
612
+ levels respectively. See the examples for **include_level** above.
 
613
 
614
  = contains_current =
615
  *string* : Accepted values : "menu", "primary", "secondary", "inclusions", or "output". See widget's *Qualifier* options,
616
+ under [Filters Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section),
617
  for an explanation of the respective settings.
618
 
619
  = fallback =
620
+ *string* : This enables the widget's *If Current Item has no children* fallback (ref. [Fallbacks Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Fallbacks-Section))...
621
 
622
+ * *"parent"* : Sets the widget's Fallback option to "Start at : -1 (parent)"
623
+ * *"current"* : Sets the widget's Fallback option to "Start at : the Current Item"
624
+ * *"quit"* : Sets the widget's Fallback option to "No output!"
625
 
626
+ The first two values can be further qualified by appending a comma and a digit, eg. `fallback="current,1"`
627
+ or `fallback="parent,2"`, which will also set the widget's `For Depth` fallback option to the value of the
628
+ digit(s).
629
 
630
+ Optionally, "+siblings" can also be used (comma-separated, with or without a depth digit) to indicate that
631
+ siblings of the "parent" or "current" fallback item should also be included. The order of the comma-separated
632
+ values is not important, so `fallback="current,+siblings,1"` is the same as `fallback="current,1,+siblings"`,
633
+ and `fallback="2,parent"` is the same as `fallback="parent,2"`, etc.
634
 
635
  = fallback_ci_parent =
636
+ *switch, off by default, 1 to enable* : See widget's *If no Current Item can be found* entry in the
637
+ [Fallbacks Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Fallbacks-Section).
638
+
639
+ = fallback_ci_lifo =
640
+ *switch, off by default, 1 to enable* : See widget's *If more than 1 possible Current Item* entry in the
641
+ [Fallbacks Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Fallbacks-Section).
642
 
643
  = flat_output =
644
+ *switch, off by default, 1 to enable* : See widget's `Flat` option, under [Output Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Output-Section).
645
 
646
  = title_from =
647
+ *string* : Supply a "current" and/or a "branch" item (comma-separated), corresponding to the 2 selects in the widget's `Set Title from` options,
648
+ under [Output Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Output-Section).
649
+
650
+ * *"current"* : take the title from the Current Item
651
+ * *"currentN"* : take the title from an ancestor of the Current Item, where **N** is the literal level of the ancestor, eg. "current2" would be the Current Item's ancestor that sits at level 2
652
+ * *"current-N"* : take the title from an ancestor of the Current Item, where **N** is the number of levels above the current item, eg. "current-2" would be the Current Item's grand-parent
653
+ * *"current-root"* : equivalent to *"current1"*; takes the title from the Current Item's root-level ancestor
654
+ * *"current-parent"* : equivalent to *"current-1"*; takes the title from the Current Item's parent
655
 
656
+ All the above are also available for the Branch Item, eg. *"branch"*, *"branch1"*, *"branch-2"*, etc.
657
+ As an example, `title_from="current-1,branch"` will take the title from either the Current Item's parent - if
658
+ there is a Current Item found in the menu - or the Primary Filter's Branch setting if there isn't a Current
659
+ Item available.
660
 
661
  = title_linked =
662
  *switch, off by default, 1 to enable* : Makes the title into a link if the title comes from one of the `title_from` options.
663
 
664
  = ol_root =
665
+ *switch, off by default, 1 to enable* : See widget's `Top Level` option, under *Change UL to OL* in the [Output Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Output-Section).
666
 
667
  = ol_sub =
668
+ *switch, off by default, 1 to enable* : See widget's `Sub-Levels` option, under *Change UL to OL* in the [Output Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Output-Section).
669
 
670
  = container =
671
+ *string* : See widget's `Element` option, under [Container Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Container-Section).
672
 
673
  = container_id =
674
+ *string* : See widget's `Unique ID` option, under [Container Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Container-Section).
675
 
676
  = container_class =
677
+ *string* : See widget's `Class` option, under [Container Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Container-Section).
678
 
679
  = menu_class =
680
+ *string* : See widget's `Menu Class` option, under [Classes Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Classes-Section).
681
 
682
  = widget_class =
683
+ *string* : See widget's `Widget Class` option, under [Classes Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Classes-Section).
684
 
685
  = wrap_link =
686
  *string* : This is an optional tag name (eg. "div", "p", "span") that, if provided, will be made into HTML start/end tags
687
+ and sent through to the widget as its `Before the Link` and `After the Link` options (ref. [Links Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Links-Section)).
688
  Please note that the shortcode usage - a simple tag name - is much more restrictive than the widget's options, which allow HTML.
689
 
690
  = wrap_link_text =
691
  *string* : This is an optional tag name (eg. "span", "em", "strong") that, if provided, will be made into HTML start/end tags
692
+ and sent through to the widget as its `Before the Link Text` and `After the Link Text` options (ref. [Links Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Links-Section)).
693
  Please note that the shortcode usage - a simple tag name - is much more restrictive than the widget's options, which allow HTML.
694
 
695
  = alternative =
696
+ *string* : This is 2 settings separated by a comma, reflecting the `On condition` options under the
697
+ [Alternative Section](https://wordpress.org/plugins/custom-menu-wizard/other_notes/#Alternative-Section).
698
  Possible values are:
699
 
700
  * One of "current", "no-current" or "no-output" : the condition to test for
701
  * One of "menu", "primary", "secondary", "inclusions", or "output" : the stage at which to test the condition
702
 
703
+ Eg. `alternative="no-current,inclusions"` would test for the absence of a Current Item in the filtered menu items, having completed
704
  the Inclusions stage, and attempt to switch to the Alternative settings.
705
 
706
+ The actual Alternative settings - a cut-down shortcode - are placed as content between the shortcodes start and end tags, and this is
707
+ the only time that the use of a self-terminating shortcode is not sufficient. When specifiying the Alternative settings, *do not*
708
  include the square brackets, otherwise WordPress will interpret it as a nested shortcode!
709
 
710
+ For example, to set a primary configuration of "show Current Branch plus any kids", with an Alternative of "show top 2 levels" if no
711
  current item can be found anywhere in the menu...
712
 
713
  `[cmwizard menu=NN branch=current alternative="no-current,menu"]depth=2[/cmwizard]`
714
 
715
+ Alternatively, you could switch it around and say the primary configuration is "show top 2 levels", with an Alternative of
716
  "show Current Branch plus kids" if a current item *can* be found within the menu...
717
 
718
  `[cmwizard menu=NN depth=2 alternative="current,menu"]branch=current[/cmwizard]`
721
  configuration's setting is always used.
722
 
723
  As ever, the best way to construct a full shortcode, including an alternative, is to use the Assist : Use one instance of the CMW
724
+ widget to build your Alternative settings, copy the equivalent shortcode into the Alternative option of a second instance of the CMW
725
  widget, and then continue configuring that second instance to be your primary configuration; your final shortcode can simply be lifted
726
  from the second instance!
727
 
728
+ A bit more information about the Alternative option is available
729
  in [this article](http://www.wizzud.com/2014/10/03/custom-menu-wizard-wordpress-plugin-version-3-1/).
730
 
731
  = title_tag =
732
  *string* : An optional tag name (eg. "h1", "h3", etc) to replace the default "h2" used to enclose the widget title.
733
+ Please note that this attribute has no equivalent in the widget options, because it *only* applies when a widget is instantiated via a shortcode.
734
 
735
  = findme =
736
+ *switch, off by default, 1 to enable* : This is a utility intended for editors only, and output is restricted to those with edit_pages capability.
737
+ If enabled it will return a list of posts that contain a CMW shortcode. If `findme` is set, the only other attribute that is taken any
738
+ notice of is `title`, which will be output (if supplied) as an H3 in front of the list. Example :
 
739
 
740
+ `[cmwizard findme=1 title="Posts containing a CMW shortcode..."/]`
741
+
742
+ Note that the information provided by this utility is also available from any widget's "assist".
743
 
744
  = SHORTCODE EXAMPLES =
745
 
746
  * Show the entire "main" menu
747
 
748
+ `[cmwizard menu=main/]`
 
 
749
 
750
  * Show the children of the current menu item within the "main" menu, for unlimited depth, setting the widget title from the current menu item
751
 
752
+ `[cmwizard menu=main branch=current start_at=children title_from=current/]`
 
 
753
 
754
  * From the "animals" menu, show all the items *immediately* below "Small Dogs", plus "Small Dogs" and its sibling items, as ordered lists
755
 
756
+ `[cmwizard menu="animals" branch="small dogs" depth=2 include="siblings" ol_root=1 ol_sub=1/]`
 
 
757
 
758
  * From the "animals" menu, show the entire "Small Animals" branch, with the sole exception of the "Small Animals" item itself, whenever "Small Animals" or one of its descendants is the current menu item
759
 
760
+ `[cmwizard menu="animals" branch="small animals" start_at=children contains_current=primary/]`
 
 
761
 
762
  * Show the entire "main" menu entitled "Main Menu" *unless* there's a current menu item, in which case show the current menu item, its siblings and its immediate children, and entitle it "Nearest and Dearest!"
763
 
764
+ `[cmwizard menu=main title="Main Menu" alternative="current,menu"]title="Nearest and Dearest!" branch=current depth=2 siblings=1[/cmwizard]`
 
 
765
 
766
  == Frequently Asked Questions ==
767
+ If you have a question or problem that is not covered here, please use the [Support forum](https://wordpress.org/support/plugin/custom-menu-wizard).
768
 
769
  = Are there any known problems/restrictions? =
770
  Yep, 'fraid so :
771
 
772
+ 1. The widget will only recognise one "current" item (prior to v2.0.2 it was the last one found; as of v2.0.2, it's the first one encountered, but v3.1.5 add a switch that lets you opt for the last one found). It is perfectly possible to have more than one menu item marked as "current", but if CMW has been configured to filter on anything related to a "current menu item" it can only choose one. The simplest example of multiple "current" items is if you add the same page to a menu more than once, but any other plugin that adds and/or manipulates menu items could potentially cause problems for CMW.
773
  2. The widget's "assist" uses jQuery UI's Dialog, which unfortunately (in versions 1.10.3/4) has a *really* annoying bug in its handling of a draggable (ie. when you drag the Dialog's title bar to reposition it on the page) when the page has been scrolled. It is due to be fixed in UI v1.11.0, but meantime I have defaulted the Dialog to fixed position, with an option to toggle back to absolute : it's not perfect but it's the best compromise I can come up with to maintain some sort of useability.
774
 
775
  = Why isn't it working? Why is there no output? =
776
+ 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
777
+ the option settings on the widget/shortcode. The quickest way to resolve any such issues is to use the widget's interactive "assist", and
778
+ 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
779
+ infallible (and it's been proven a fair few times!), so if you still have problems then please let me have as much information as possible
780
+ (the shortcode equivalent of your settings is a good start?) and I will endeavour to help.
781
+
782
+ Please note that simply reporting "It doesn't work" is not
783
  the most useful of feedbacks, and is unlikely to get a response other than, possibly, a request for more details.
784
 
785
  = How do I use the "assist"? =
786
+ The widget's interactive "assist" is specific to each widget instance. It is a javascript-driven *emulator* that uses the widget instance's
787
+ option settings - including the menu selected - to build a pictorial representation of the menu and show you, in blue, which menu items will
788
+ 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
789
  some of the more advanced HTML-modifying options such as can be found under the Container, Classes or Links sections.
790
+ 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
791
+ item to change). The "current menu item" is shaded red, with its parent shaded orange and ancestors shaded yellow. All changes in the
792
+ "current menu item" and the widget options are immediately reflected by the "assist" (text fields in the widget options simply need to lose
793
  focus).
794
 
795
  The red cross to the left of each menu item toggles the Exclusions setting for the item and/or its descendants. The button has 3 settings :
798
  * Just this item (white on red)
799
  * This item *plus* all its descendants (white on red, with a small yellow plus sign)
800
 
801
+ Just click through the toggle states. When the Primary Filter is set to "Items", the green tick buttons to the right of each menu item
802
  work in the same way.
803
 
804
  Note that if a green "Alternate settings" message is showing then the ticks and crosses buttons will show the approriate Alternative
805
  settings but they will be slightly opaque and they will *not* be clickable!
806
 
807
+ Once you are happy with the results, having tested all possible settings of "current menu item" (if it applies), then simply Save the widget.
808
+ If you are using a shortcode implementation, then copy-paste the shortcode text - at the base of either the "assist" or the widget form - straight into your post.
 
809
 
810
  = Is there an easy way to construct the shortcode? =
811
  Yes, use a widget form. The shortcode for all the selected/specified options is show at the base of the widget (v3+) and the base of the
812
+ "assist". The widget does not have to be placed within a widget area, it can also be used from the Inactive Widgets area.
813
+
814
+ = Do I have to Save the widget if I am using a shortcode? =
815
+ Only if (as of v3.1.5) you are using the `widget=N` attribute, which refers back to an existing widget instance
816
+ for its settings.
817
 
818
  = How do I get the menu item ids for the 'Items' option? =
819
+ Use the widget's interactive "assist" (see above). Within the representative menu structure, each menu item's id is set in its title
820
+ 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
821
+ then show a green tick "checkbox" to the right of each menu item and you simply [un]check the items as required. Each selection will be reflected back into the
822
  widget's `Items` settings, and also in the shortcode texts.
823
 
824
+ The more painstaking way is to go to Appearance, Menus and select the relevant menu; hover over the *edit*, *Remove*, or *Cancel* link for an item and look in
825
  the URL (the link's href) for `menu-item=NNN` ... the NNN is the menu item id.
826
 
827
  = How do I get the menu item ids for the 'Exclude Ids' option? =
828
+ The "assist" shows a red cross "checkbox" to the left of each menu item, and [un]checking the items will reflect back into the options and
829
  shortcode texts. Otherwise, it's the same principle as outlined above for `Items` ids.
830
 
831
  = What's the difference between including Branch Siblings (or Branch Ancestors + Siblings), and switching to 'Level' instead of 'Item' in the Secondary Filter section? =
832
+ If you elect to include Branch [Ancestor] Siblings, you will *only* get the siblings, **not** their descendants (assuming they have any).
833
  On the other hand, if you make `Starting at` use 'Level' instead of 'Item' then siblings *and their descendants* will be added to the filter.
834
 
835
+ For example, let's say that Bravo and Charlie are sibling items immediately below Alpha, and that Bravo is the selected Branch Item,
836
  with `Starting at` set to "the Branch" (ie. Bravo). If you switch from "Item" to "Level" then both Bravo, Charlie, *and all their descendants*,
837
  will become eligible for filtering. If you left "Item" enabled, and switched on the inclusion of Branch Siblings, then Bravo and Charlie
838
  would both still be eligible, but only *Bravo's descendants* would be; not Charlie's!
843
  requirements for styling are likely to be different ... possibly even within the same web page's output. So all styling is down to your theme,
844
  and if you wish to modify it you will need to add to your theme's stylesheet.
845
 
846
+ The safest way to do this is via a child theme, so that any changes you make will not be lost if/when the main theme gets updated. The best
847
  way to test your changes is by utilising the developer capabilities that are available in most modern browsers (personally, I could not
848
+ do without Firefox and the Firebug extension!) and dynamically applying/modifying styles, possibly utilising the custom classes that the
849
  widget applies to its output, or the Container options for a user-defined id or class.
850
 
851
  = How can I find all my posts/pages that have a CMW shortcode so that I can upgrade them? =
852
+ There is a button on the widget's "assist" - `[...]` - that will provide a list of posts/pages whose content, or meta data (custom fields),
853
+ contains any CMW shortcode. Each entry is a link that opens the item in a new tab/window. The link's title gives a bit more information :
854
  post type, id, whether the shortcode(s) are in content and/or meta data, and the shortcode(s) concerned.
855
  This utility does not check things like text widgets, plugin-specific tables, theme-provided textareas, etc.
856
 
857
+ There is also an extension to the shortcode - `[cmwizard findme=1/]` - that will output the same information, should you not be able to use
858
+ the "assist" (for some unknown reason). You may optionally provide a title attribute; any other attributes are ignored.
859
  Note that output from this shortcode extension is restricted to users with edit_pages capability.
860
 
861
+ = Is Version 2 of the widget, including the old [custom_menu_wizard/] shortcode, still supported? =
862
+ In Version 3, Yes. However, I highly recommend that you upgrade your widgets & shortcodes to the latest versions,
863
+ because Version 2 will **not** be supported beyond Version 3.
864
+
865
 
866
  == Screenshots ==
867
  1. Widget (all sections collapsed)
876
 
877
  == Changelog ==
878
 
879
+ = 3.1.5 =
880
+ * addition : expanded Title From to allow absolute ancestor levels (besides root) and relative ancestor levels
881
+ * addition : added a fallback option to switch determination of Current Item from first-found to last-found
882
+ * addition : added a shortcode attribute that loads an existing widget instance : `[cmwizard widget=N/]`
883
+ * documentation : updated, and provided an html version in the plugin download
884
+
885
  = 3.1.4 =
886
  * bugfix : in shortcode processing, any supplied Alternative settings weren't being used. thanks corrideat
887
  * bugfix : prevent texturization of shortcode's content, for when it is being used with an Alternative setting
891
  * tweak : minor change to css for the assist when running under the Customizer (WordPress 4.1)
892
 
893
  = 3.1.2 =
894
+ * modified the readme : documentation for the Shortcode Attributes has been moved to the Installation page (to avoid being truncated)
895
 
896
  = 3.1.1 =
897
  * bugfix : only show the allow_all_root setting in the shortcode equivalent if the primary filter is by branch
925
  * addition : added a couple of filters
926
 
927
  = 3.0.0 =
928
+ * **! Rewrite, and Change of Approach !** The widget has had a major rewrite! The `Children of` filter has been replaced with a `Branch` filter, with a subsequent shift in focus for the secondary filter options, from the children's level (0, 1 or more items) up to the branch level (a single item!). This should provide a more intuitive interface, and is definitely easier to code for. **However**, it only affects *new instances* of the widget; v2 instances are still ***fully supported***.
929
 
930
+ Please also note that the shortcode tag for v3 has changed to **`[cmwizard]`**, with a revised set of attributes. The old shortcode tag is still supported, but only with the v2 attribute set, and only providing v2 functionality, ie. it is the shortcode tag that determines which widget version to use, and the appropriate attribute set for that version.
931
 
932
  There is no automatic upgrade of widget settings from v2 to v3! I suggest bringing up the "assist" for the existing v2 widget, running it side-by-side with the "assist" of a new instance of the widget, and using them to the compare the desired outputs. I would also strongly recommend that you put your old widgets into the inactive area until you are completely happy with their new replacements. If you are upgrading from version 2, and you would like a bit more information, [this article](http://www.wizzud.com/2014/06/16/custom-menu-wizard-wordpress-plugin-version-3/) might help.
933
  * change : **the minmum requirement for WordPress is v3.6**
1023
 
1024
  == Upgrade Notice ==
1025
 
1026
+ = 3.1.5 =
1027
+ Expanded Title From to allow absolute ancestor levels (besides root) and relative ancestor levels.
1028
+ Added a fallback option to switch determination of Current Item from first-found to last-found.
1029
+ Added the ability for the shortcode version to load an existing widget instance.
1030
+ Updated the documentation and provided an html version in the download.
1031
+
1032
  = 3.1.4 =
1033
  Fixed a couple of bugs in the shortcode processing, for when an Alternative is being used.
1034
  Added the ability to make a title into a link when the title has been set from a menu item.
1037
  Tweaked the assist's css for when running the Customizer in WordPress 4.1.
1038
 
1039
  = 3.1.2 =
1040
+ No code changes, just moved the readme's Shortcode Attributes documentation onto the Installation page (to avoid being truncated).
1041
 
1042
  = 3.1.1 =
1043
  Added a work-around for when a theme inadvertently(!) de-registers the widget, which then prevents the shortcode working.
1065
  Also added a couple of filters.
1066
 
1067
  = 3.0.0 =
1068
+ **Rewrite, and change of approach** : __! Important !__ : existing (version 2) widgets and shortcodes *__are fully supported__*. Please [read the Changelog](https://wordpress.org/plugins/custom-menu-wizard/changelog/) *before* upgrading!
1069
  Version 3 swaps the *Children-Of* filter for a *Branch* filter, with secondary filters to then refine the branch items. It has better filter capabilities - relative and absolute start level, presence of a current menu item at different stages - and adds exclusion of items by id and/or level. A new shortcode - *[cmwizard]* - has been added to support the v3 functionality.
1070
  Changes that also apply to version 2 widgets include : **Minimum requirement for WordPress now v3.6!**; handling of duplicate menu ids, improved compatibility with Widget Customizer (required due to its incorporation into WordPress v3.9 core), and tweaks to the "assist".
1071
 
1092
  Fixed a bug whereby a test for a specific-items filter prevented show-all from producing any output.
1093
 
1094
  = 2.0.0 =
1095
+ **! 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 :
1096
  + if you have set a `Children of` filter, **and** you have changed the `Start Level` to a level greater than 2, or
1097
  + if you have set the `Children of` filter to Current Parent/Root Item, and you have **not** set the "no ancestor" fallback.
1098
+ *__If you think you may be impacted, please check the [Changelog](https://wordpress.org/plugins/custom-menu-wizard/changelog/) for a fuller explanation of what has changed.__*
1099
 
1100
  New options :
1101
  + `Items` allows specific menu item ids to be listed, as an alternative to the other filters
1107
  Bugfix : The fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
1108
 
1109
  = 1.2.1 =
1110
+ Added a few extra custom classes, and changed the defaults for new widgets such that only the Filter section is open by default.
1111
+ Fixed Show All processing so that custom classes always get applied, and 'Title from "Current" Item' works regardless of filter settings.
1112
  Fixed a couple of the shortcode examples in the readme.txt, and added display of the applicable shortcode settings to the demo.html.
1113
 
1114
  = 1.2.0 =