Custom Menu Wizard Widget - Version 3.0.0

Version Description

  • ! 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.

    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.

    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 might help.

  • change : the minmum requirement for WordPress is v3.6

  • addition : more options for requiring that the "current" menu item be present at some point in the filter process

  • addition : Branch filter levels can be either relative (to the selected Branch item) or absolute (within the menu structure)

  • addition : menu items can now be excluded from the final output, either explicitly by id (optionally including descendants), or by level

  • addition : the ids of Items can be set to include all descendants

  • addition : the inclusion of branch ancestors, and optionally their siblings, can be set by absolute level or relative number of levels

  • addition : the widget title can now be automatically set from the root level item of the Branch item or current menu item

  • 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")

  • 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

  • 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])

  • This release includes an upgrade to v2.1.0 for all existing version 2 widgets/shortcodes - please read the v2.1.0 changes below.

Download this release

Release Info

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

Code changes from version 2.0.6 to 3.0.0

custom-menu-wizard.css CHANGED
@@ -1,47 +1,127 @@
1
  /**
2
  * custom-menu-wizard.css
3
- * Version: 2.0.5
4
  */
5
  /*widget admin style...*/
6
- /*NB .widget-content class is not present in accessibility mode*/
7
- .js .widget-content .widget-custom-menu-wizard-onchange .cmw-start-fieldset-collapsed {display:none;}
8
- .widget-custom-menu-wizard-onchange {margin:0 0 0.5em;}
9
- .widget-custom-menu-wizard-onchange .cmw-colour-grey {color:#999999;}
10
- .widget-custom-menu-wizard-onchange .cmw-pad-left-1 {padding-left:1em;}
 
 
 
 
 
 
 
 
 
 
 
 
11
  .widget-custom-menu-wizard-onchange .cmw-off-the-page {display:none; position:absolute; left:-5000px; top:-5000px;}
12
- .widget-custom-menu-wizard-onchange .cmw-toggle-assist {float:right; line-height:1; margin-left:1em;}
13
- .widget-custom-menu-wizard-collapsible-fieldset .hidden-field {display:none;}
14
- .widget-custom-menu-wizard-collapsible-fieldset {margin:0.5em 0 0; cursor:pointer; border-left-width:0; border-right-width:0; border-radius:0; text-align:center; background-color:#ffffff;}
15
- .widget-custom-menu-wizard-collapsible-fieldset h3 {font-size:1em; margin:0; padding:2px 0.5em;}
16
- .widget-custom-menu-wizard-collapsible-fieldset h4 {font-size:1em; margin:0.75em 0;}
17
- .widget-custom-menu-wizard-collapsible-fieldset div {background-color:transparent; background-repeat:no-repeat; background-position:0 -36px; height:16px; width:16px; float:right; margin-top:0.75em; outline:0 none;}
18
- .js .widget-content .widget-custom-menu-wizard-collapsible-fieldset div.cmw-collapsed-fieldset {background-position:0 0;}
19
- .widget-custom-menu-wizard-disableifnot-rp, .widget-custom-menu-wizard-disableifnot-ci {line-height:1.3333;}
20
- .widget-custom-menu-wizard-childrenof optgroup option {padding-left:0.75em;}
21
- select.widget-custom-menu-wizard-listen {max-width:100%;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  /*dialog style...*/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  /*...menu...*/
24
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu {margin:0; font-size:0.8333em;}
25
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu ul {font-size:10px; list-style-type:none; margin:0.5em 0; padding:0;}
26
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu ul ul {margin:0 0 0 5em;}
27
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu li {display:block; margin:0; padding:0;}
28
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu a {display:inline-block; text-align:center; padding:3px 4px; margin:2px; color:#333333; text-decoration:none;}
29
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu a:focus {outline:0 none;}
30
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu input {vertical-align:top; visibility:hidden}
31
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu ul span {display:block; width:10em; padding:0 2px; height:1.75em; line-height:1.75; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; border:1px solid #999999; box-shadow:2px 2px 2px 0px #666666;}
32
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu .current-menu-ancestor {border-color:#cccc00 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 153, 1)), to(rgba(255, 255, 153, 0.1))); background-image:-webkit-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:-moz-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:-ms-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:-o-linear-gradient(top, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); background-image:linear-gradient(to bottom, rgba(255, 255, 153, 1), rgba(255, 255, 153, 0.1)); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff99, endColorstr=#1affff99); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffff99, endColorstr=#1affff99)";}
33
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu .current-menu-item {border-color:#cc0000 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 204, 204, 1)), to(rgba(255, 204, 204, 0.1))); background-image:-webkit-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:-moz-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:-ms-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:-o-linear-gradient(top, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); background-image:linear-gradient(to bottom, rgba(255, 204, 204, 1), rgba(255, 204, 204, 0.1)); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcccc, endColorstr=#1affcccc); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcccc, endColorstr=#1affcccc)";}
34
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu .current-menu-parent {border-color:#ff9900 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 204, 153, 1)), to(rgba(255, 204, 153, 0.1))); background-image:-webkit-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:-moz-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:-ms-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:-o-linear-gradient(top, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); background-image:linear-gradient(to bottom, rgba(255, 204, 153, 1), rgba(255, 204, 153, 0.1)); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcc99, endColorstr=#1affcc99); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffffcc99, endColorstr=#1affcc99)";}
35
- .widget-custom-menu-wizard-dialog .cmw-demo-themenu .picked > a {background-color:#333366; color:#ffffff; font-weight:bold;}
36
- .widget-custom-menu-wizard-dialog .cmw-demo-filteritems input {visibility:visible}
37
- /*...shortcode...*/
38
- .widget-custom-menu-wizard-dialog .cmw-demo-theshortcode {clear:both; width:100%; margin:1em 0 0.5em;}
39
- .widget-custom-menu-wizard-dialog .cmw-demo-theshortcode code {border:1px solid #CCCCCC; display:inline-block; line-height:1.5; padding:0.25em 0.5em;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  /*...output...*/
41
- .widget-custom-menu-wizard-dialog .cmw-demo-theoutput {float:right; margin:0 0 1em 2em; font-size:0.8333em;}
42
- .widget-custom-menu-wizard-dialog .cmw-demo-theoutput h3 {border-bottom:1px solid #CCCCCC; margin:0 0 0.25em; padding: 0.5em 0 0.25em;}
43
- .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap {margin:0.5em 0; padding:0 1em; border:1px solid #cccccc; min-height:2em; min-width:9em;}
44
- .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ul, .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol {margin:0.5em 0 0.5em 1.5em; padding:0;}
 
 
45
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap li {margin:0;}
46
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap li ul {margin:0 0 0 1em;}
47
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap li ol {margin:0 0 0 1.5em;}
@@ -55,5 +135,36 @@ select.widget-custom-menu-wizard-listen {max-width:100%;}
55
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol ol {list-style-type:lower-alpha;}
56
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol ol ol {list-style-type:lower-roman;}
57
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol ol ol ol {list-style-type:decimal;}
58
- .widget-custom-menu-wizard-dialog .cmw-demo-fallback {display:none; text-align:right; color:#006600;}
59
- .widget-custom-menu-wizard-dialog .cmw-demo-fallback.cmw-demo-fellback {display:block;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  /**
2
  * custom-menu-wizard.css
3
+ * Version: 3.0.0
4
  */
5
  /*widget admin style...*/
6
+ .widget-custom-menu-wizard-onchange {line-height:1.5; margin:0 0 0.5em;}
7
+ .widget-custom-menu-wizard-onchange a,
8
+ .widget-custom-menu-wizard-onchange a.button {color:#0074a2;}
9
+ .widget-custom-menu-wizard-onchange a:hover,
10
+ .widget-custom-menu-wizard-onchange a.button:hover,
11
+ .widget-custom-menu-wizard-onchange a:active,
12
+ .widget-custom-menu-wizard-onchange a.button:active {border-color:#2ea2cc; color:#2ea2cc;}
13
+ .widget-custom-menu-wizard-onchange select,
14
+ .widget-custom-menu-wizard-onchange input[type="text"] {max-width:100%;}
15
+ .widget-custom-menu-wizard-onchange optgroup option {padding-left:0.75em;}
16
+ .widget-custom-menu-wizard-onchange .cmw-colour-grey,
17
+ .widget-custom-menu-wizard-onchange .cmw-colour-grey input {color:#7F7F7F}
18
+ .widget-custom-menu-wizard-onchange small.cmw-colour-grey {vertical-align:top;}
19
+ .widget-custom-menu-wizard-onchange .cmw-display-none {display:none;}
20
+ .widget-custom-menu-wizard-onchange .cmw-followed-by {margin-right:1em;}
21
+ .widget-custom-menu-wizard-onchange .cmw-indented {margin-left:1em;}
22
+ .widget-custom-menu-wizard-onchange .cmw-maxwidth-twothirds {max-width:66% !important;}
23
  .widget-custom-menu-wizard-onchange .cmw-off-the-page {display:none; position:absolute; left:-5000px; top:-5000px;}
24
+ .widget-custom-menu-wizard-onchange .cmw-pseudo-para {margin:1em 0;}
25
+ .widget-custom-menu-wizard-onchange .cmw-small-block {display:block; font-size:0.769em;}
26
+ .widget-custom-menu-wizard-onchange .cmw-textalign-right {text-align:right;}
27
+ .widget-custom-menu-wizard-onchange .cmw-verticalalign-baseline {vertical-align:baseline;}
28
+ .widget-custom-menu-wizard-onchange .cmw-whitespace-nowrap {white-space:nowrap;}
29
+ .widget-custom-menu-wizard-onchange .widget-custom-menu-wizard-assist {float:right; font-size:0.77em; height:auto; line-height:1.5; margin:-0.4em 0 0 1em; padding:0 1em;}
30
+ .widget-custom-menu-wizard-onchange .cmw-fieldset > div {margin:1em 0;}
31
+ a.widget-custom-menu-wizard-fieldset {background-image:url("custom-menu-wizard.png"); background-repeat:no-repeat; background-position:100% -32px; border-bottom:1px solid #E5E5E5; border-top:1px solid #E5E5E5; display:block; font-weight:bold; line-height:2.5; margin:0.5em 0 0; text-decoration:none; text-transform:uppercase; -webkit-transition-duration:0s; transition-duration:0s; width:100%;}
32
+ a.widget-custom-menu-wizard-fieldset:hover,
33
+ a.widget-custom-menu-wizard-fieldset:active {background-position:100% -112px;}
34
+ a.widget-custom-menu-wizard-fieldset.cmw-collapsed-fieldset {background-position:100% 8px;}
35
+ a.widget-custom-menu-wizard-fieldset.cmw-collapsed-fieldset:hover,
36
+ a.widget-custom-menu-wizard-fieldset.cmw-collapsed-fieldset:active {background-position:100% -72px;}
37
+ .widget-custom-menu-wizard-onchange .cmw-start-fieldset-collapsed {display:none;}
38
+ /*...pre WP3.8...*/
39
+ .widget-custom-menu-wizard-onchange.cmw-pre-wp-v38 input[type="checkbox"],
40
+ .widget-custom-menu-wizard-onchange.cmw-pre-wp-v38 input[type="radio"] {margin-right:4px;}
41
+ .widget-custom-menu-wizard-onchange.cmw-pre-wp-v38 .cmw-fieldset {padding-top:1em;}
42
+ /*...accessibility...*/
43
+ .no-js a.widget-custom-menu-wizard-fieldset.cmw-collapsed-fieldset {background-position:100% -32px;}
44
+ .no-js a.widget-custom-menu-wizard-fieldset.cmw-collapsed-fieldset:hover,
45
+ .no-js a.widget-custom-menu-wizard-fieldset.cmw-collapsed-fieldset:active {background-position:100% -112px;}
46
+ .no-js .widget-custom-menu-wizard-onchange .cmw-start-fieldset-collapsed {display:block;}
47
+ .no-js a.widget-custom-menu-wizard-fieldset {background-image:none;}
48
+ .no-js .widget-custom-menu-wizard-assist {display:none;}
49
+ /*...customizer tweaks...*/
50
+ .wp-customizer .widget-custom-menu-wizard-onchange input[type="text"] {margin:1px 0;}
51
+ /*...warnings...*/
52
+ .widget-custom-menu-wizard-no-menus,
53
+ .widget-custom-menu-wizard-onchange .cmw-legacy-warn {background-color:#ffeecc; border-left:4px solid #DD3D36; -webkit-box-shadow:0 1px 1px 0 rgba(0, 0, 0, 0.1); box-shadow:0 1px 1px 0 rgba(0, 0, 0, 0.1); font-size:0.846em; margin:5px 0 15px; padding:1px 1em;}
54
+ .widget-custom-menu-wizard-onchange a.cmw-legacy-close {background-color:transparent; border-radius:1.3em; color:#cc0000; display:block; float:right; font-size:1.5em; font-weight:bold; line-height:1.3; margin:0 -0.6em 0 0.5em; text-align:center; text-decoration:none; width:1.3em;}
55
+ .widget-custom-menu-wizard-onchange a.cmw-legacy-close:hover {background-color:#ffffff; -webkit-box-shadow:1px 1px 1px 0 rgba(0, 0, 0, 0.2); box-shadow:1px 1px 1px 0 rgba(0, 0, 0, 0.2);}
56
+ .widget-custom-menu-wizard-no-menus {background-color:#ffffff; font-size:1em; padding:0.5em 1em;}
57
+ /*shortcodes...*/
58
+ .widget-custom-menu-wizard-dialog .cmw-demo-theshortcode {clear:both; margin:1em 0 0.5em; width:100%;}
59
+ .widget-custom-menu-wizard-onchange .cmw-shortcode-wrap {clear:both; margin:0.75em 0 1em; width:100%;}
60
+ .widget-custom-menu-wizard-dialog code,
61
+ .widget-custom-menu-wizard-onchange code {border:1px solid #CCCCCC; display:inline-block; line-height:1.5; padding:0.25em 0.5em;}
62
+ .widget-custom-menu-wizard-onchange code {padding:0.1em 0.2em; font-size:0.846em;}
63
  /*dialog style...*/
64
+ .widget-custom-menu-wizard-dialog {font-size:0.8392em; line-height:1.5;}
65
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu,
66
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu *,
67
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput,
68
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput *,
69
+ .widget-custom-menu-wizard-dialog .cmw-demo-theshortcode,
70
+ .widget-custom-menu-wizard-dialog .cmw-demo-theshortcode * {position:relative;}
71
+ /*...fix for jquery UI's draggable problem in v1.10.3/4...*/
72
+ .cmw-assistance-dialog {position:absolute !important;}
73
+ .cmw-assistance-dialog.cmw-assistance-dialog-fixed {position:fixed !important;}
74
+ .cmw-assistance-dialog .cmw-dialog-fixed-absolute {height:20px; margin:-10px 0 0; position:absolute; right:2em; top:50%;}
75
+ .cmw-assistance-dialog .cmw-dialog-fixed-absolute .ui-button-text {line-height:1; padding:1px 0.35em 1px 1.3em;}
76
+ .cmw-assistance-dialog .cmw-dialog-fixed-absolute .ui-button-icon-primary {left:0.1em;}
77
+ .cmw-assistance-dialog .ui-dialog-title {width:65% !important;}
78
+ /*...for customizer, make sure the assist goes above .wp-full-overlay...*/
79
+ .wp-customizer .cmw-assistance-dialog {z-index:600000;}
80
  /*...menu...*/
81
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu {float:left; margin:0 0.5em 0.5em 0;}
82
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul {font-size:0.8333em; line-height:1.75;}
83
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu ul {list-style-type:none; margin:0.5em 0 0.5em 1.5em; padding:0;}
84
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul ul {margin:0 0 0 2.4em;}
85
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul li {display:block; margin:0; padding:1px 0; white-space:nowrap;}
86
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul a {display:inline-block; text-align:center; text-decoration:none; vertical-align:top;}
87
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul a:hover {text-decoration:none;}
88
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul a:focus {outline:0 none;}
89
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul span {display:block; width:10em; padding:0 2px; height:1.75em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; border:1px solid #999999; -webkit-box-shadow:2px 2px 2px 0px #666666; box-shadow:2px 2px 2px 0px #666666;}
90
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul .current-menu-ancestor {border-color:#cccc00 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 153, 0.7)), to(rgba(255, 255, 153, 0))); background-image:-webkit-linear-gradient(top, rgba(255, 255, 153, 0.7), rgba(255, 255, 153, 0)); background-image:-moz-linear-gradient(top, rgba(255, 255, 153, 0.7), rgba(255, 255, 153, 0)); background-image:-ms-linear-gradient(top, rgba(255, 255, 153, 0.7), rgba(255, 255, 153, 0)); background-image:-o-linear-gradient(top, rgba(255, 255, 153, 0.7), rgba(255, 255, 153, 0)); background-image:linear-gradient(to bottom, rgba(255, 255, 153, 0.7), rgba(255, 255, 153, 0)); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#b2ffff99, endColorstr=#01ffff99); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#b2ffff99, endColorstr=#01ffff99)";}
91
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul .current-menu-item {border-color:#cc0000 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 204, 204, 0.7)), to(rgba(255, 204, 204, 0))); background-image:-webkit-linear-gradient(top, rgba(255, 204, 204, 0.7), rgba(255, 204, 204, 0)); background-image:-moz-linear-gradient(top, rgba(255, 204, 204, 0.7), rgba(255, 204, 204, 0)); background-image:-ms-linear-gradient(top, rgba(255, 204, 204, 0.7), rgba(255, 204, 204, 0)); background-image:-o-linear-gradient(top, rgba(255, 204, 204, 0.7), rgba(255, 204, 204, 0)); background-image:linear-gradient(to bottom, rgba(255, 204, 204, 0.7), rgba(255, 204, 204, 0)); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#b2ffcccc, endColorstr=#01ffcccc); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#b2ffcccc, endColorstr=#01ffcccc)";}
92
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul .current-menu-parent {border-color:#ff9900 !important; background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255, 204, 153, 0.7)), to(rgba(255, 204, 153, 0))); background-image:-webkit-linear-gradient(top, rgba(255, 204, 153, 0.7), rgba(255, 204, 153, 0)); background-image:-moz-linear-gradient(top, rgba(255, 204, 153, 0.7), rgba(255, 204, 153, 0)); background-image:-ms-linear-gradient(top, rgba(255, 204, 153, 0.7), rgba(255, 204, 153, 0)); background-image:-o-linear-gradient(top, rgba(255, 204, 153, 0.7), rgba(255, 204, 153, 0)); background-image:linear-gradient(to bottom, rgba(255, 204, 153, 0.7), rgba(255, 204, 153, 0)); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#b2ffcc99, endColorstr=#01ffcc99); -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#b2ffcc99, endColorstr=#01ffcc99)";}
93
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul a.cmw-item {color:#000000; margin:1px; padding:2px;}
94
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul .picked > a.cmw-item {background-color:#333366; color:#ffffff;}
95
+ /*...ticks/crosses...*/
96
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul a.cmw-tick,
97
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul a.cmw-cross {background:transparent url("custom-menu-wizard.png") no-repeat 0 -160px; height:20px; margin:3px 2px 0px 0px; position:relative; -webkit-transition-duration:0s; transition-duration:0s; width:20px;}
98
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul a.cmw-tick {background-position:0 -240px; margin-left:2px; margin-right:0; visibility:hidden;}
99
+ .widget-custom-menu-wizard-dialog .cmw-demo-filteritems a.cmw-tick {visibility:visible;}
100
+ .widget-custom-menu-wizard-dialog .cmw-version-210 a.cmw-cross {display:none;}
101
+ .widget-custom-menu-wizard-dialog .cmw-has-cross > a.cmw-cross,
102
+ .widget-custom-menu-wizard-dialog .cmw-has-cross.cmw-inherit-cross a.cmw-cross {background-position:0 -200px;}
103
+ .widget-custom-menu-wizard-dialog .cmw-has-cross.cmw-inherit-cross > a.cmw-cross {background-position:0 -320px;}
104
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul a.cmw-cross:hover {background-position:0 -180px;}
105
+ .widget-custom-menu-wizard-dialog .cmw-has-cross > a.cmw-cross:hover,
106
+ .widget-custom-menu-wizard-dialog .cmw-has-cross.cmw-inherit-cross a.cmw-cross:hover {background-position:0 -220px;}
107
+ .widget-custom-menu-wizard-dialog .cmw-has-cross.cmw-inherit-cross > a.cmw-cross:hover {background-position:0 -360px;}
108
+ .widget-custom-menu-wizard-dialog .cmw-has-tick > a.cmw-tick,
109
+ .widget-custom-menu-wizard-dialog .cmw-has-tick.cmw-inherit-tick a.cmw-tick {background-position:0 -280px;}
110
+ .widget-custom-menu-wizard-dialog .cmw-has-tick.cmw-inherit-tick > a.cmw-tick {background-position:0 -340px;}
111
+ .widget-custom-menu-wizard-dialog .cmw-demo-themenu-ul a.cmw-tick:hover {background-position:0 -260px;}
112
+ .widget-custom-menu-wizard-dialog .cmw-has-tick > a.cmw-tick:hover,
113
+ .widget-custom-menu-wizard-dialog .cmw-has-tick.cmw-inherit-tick a.cmw-tick:hover {background-position:0 -300px;}
114
+ .widget-custom-menu-wizard-dialog .cmw-has-tick.cmw-inherit-tick > a.cmw-tick:hover {background-position:0 -380px;}
115
+ /*...expanders...*/
116
+ .widget-custom-menu-wizard-dialog .widget-custom-menu-wizard-colexp {position:absolute; top:0.5em; -webkit-transition-duration:0s; transition-duration:0s;}
117
+ .widget-custom-menu-wizard-dialog .widget-custom-menu-wizard-colexp.ui-icon-triangle-1-e {opacity:0.2;}
118
  /*...output...*/
119
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput {float:right; margin:0 0 3.5em 0.5em;}
120
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput h3 {border-bottom:1px solid #CCCCCC; font-size:1.1667em; margin:0.25em 0; padding: 0 0.857em 0.25em 0.643em;}
121
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap {border:1px solid #cccccc; margin:0.5em 0; min-height:3em; min-width:10em; padding:0 0 1px;}
122
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap {border-left:4px solid #2ea2cc;}
123
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ul,
124
+ .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol {margin:0.5em 1em 0.5em 2em; padding:0;}
125
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap li {margin:0;}
126
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap li ul {margin:0 0 0 1em;}
127
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap li ol {margin:0 0 0 1.5em;}
135
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol ol {list-style-type:lower-alpha;}
136
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol ol ol {list-style-type:lower-roman;}
137
  .widget-custom-menu-wizard-dialog .cmw-demo-theoutput-wrap ol ol ol ol ol {list-style-type:decimal;}
138
+ .widget-custom-menu-wizard-dialog .cmw-demo-plugin-version {float:right; line-height:1;}
139
+ .widget-custom-menu-wizard-dialog .cmw-demo-small {font-size:0.8333em;}
140
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-new,
141
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-old,
142
+ .widget-custom-menu-wizard-dialog .cmw-demo-inclusions,
143
+ .widget-custom-menu-wizard-dialog .cmw-demo-exclusions,
144
+ .widget-custom-menu-wizard-dialog .cmw-demo-setcurrent,
145
+ .widget-custom-menu-wizard-dialog .cmw-demo-fallback {border-color:#7AD03A; border-left:4px solid #7AD03A; -webkit-box-shadow:0 1px 1px 0 rgba(0, 0, 0, 0.1); box-shadow:0 1px 1px 0 rgba(0, 0, 0, 0.1); display:none; font-style:italic; margin:0 0 2px 0; padding:1px 1em;}
146
+ .widget-custom-menu-wizard-dialog .cmw-demo-setcurrent {border-color:#DD3D36; border-left:4px solid #DD3D36; margin:2px 0 0;}
147
+ .widget-custom-menu-wizard-dialog .cmw-demo-setcurrent.error,
148
+ .widget-custom-menu-wizard-dialog .cmw-demo-inclusions.updated,
149
+ .widget-custom-menu-wizard-dialog .cmw-demo-exclusions.updated,
150
+ .widget-custom-menu-wizard-dialog .cmw-demo-fallback.updated {display:block;}
151
+ /*...find shortcodes...*/
152
+ .widget-custom-menu-wizard-find-shortcodes {overflow:hidden; text-align:center; width:50px;}
153
+ .widget-custom-menu-wizard-find-shortcodes span {display:block;}
154
+ .widget-custom-menu-wizard-find-shortcodes span.spinner {display:none; float:none;}
155
+ .cmw-ajax-showing .widget-custom-menu-wizard-find-shortcodes:after {background-color:#CC0000; content:""; display:block; height:2px; left:10%; position:absolute; top:50%; -webkit-transform:skew(0deg,-20deg); -ms-transform:skew(0deg,-20deg); -o-transform:skew(0deg,-20deg); transform:skew(0deg,-20deg); width:80%;}
156
+ .widget-custom-menu-wizard-dialog .cmw-find-shortcodes {position:absolute; right:0; top:-3em; z-index:10;}
157
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-shortcodes {background-color:#FFFFFF; border:1px solid #666666; box-shadow:2px 2px 2px #666666; margin:0; max-width:70%; min-width:10em; opacity:0; padding:0 0 1px; position:absolute; right:5000px; top:-1000px;}
158
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-shortcodes {border-right:4px solid #2EA2CC; -webkit-transition:opacity 1s linear 0s, right 0s linear 1s, top 0s linear 1s; transition:opacity 1s linear 0s, right 0s linear 1s, top 0s linear 1s;}
159
+ .widget-custom-menu-wizard-dialog .cmw-ajax-showing .cmw-demo-found-shortcodes {opacity:1;}
160
+ .widget-custom-menu-wizard-dialog .cmw-ajax-showing .cmw-demo-found-shortcodes,
161
+ .widget-custom-menu-wizard-dialog .cmw-ajax-fetching .cmw-demo-found-shortcodes {right:60px; top:-4em;}
162
+ .widget-custom-menu-wizard-dialog .cmw-ajax-fetching span.spinner {display:block !important;}
163
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-shortcodes p {font-size:inherit; font-style:italic; margin:1em;}
164
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-shortcodes dl {border:0 none; margin:0.5em 0; padding:0;}
165
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-shortcodes a {display:block; overflow:hidden; padding:0 1em; text-overflow:ellipsis; white-space:nowrap;}
166
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-shortcodes a:hover {background-color:#F0F0F0;}
167
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-new,
168
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-old {display:block; font-style:normal; padding:0; margin:2px 0;}
169
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-new {border-color:#ffffff; border-left:4px solid #ffffff;}
170
+ .widget-custom-menu-wizard-dialog .cmw-demo-found-old {border-color:#DD3D36; border-left:4px solid #DD3D36;}
custom-menu-wizard.js CHANGED
@@ -1,600 +1,1524 @@
1
  /* Plugin Name: Custom Menu Wizard
2
- * Version: 2.0.5
3
- * Author: Roger Barrett
4
- *
5
  * Script for controlling this widget's options (in Admin -> Widgets)
6
  */
7
- /*global jQuery, window, document */
8
- /*jslint forin: true, nomen: true, plusplus: true, regexp: true, unparam: true, sloppy: true, white: true */
 
9
  jQuery(function($){
10
- var dotPrefix = '.widget-custom-menu-wizard',
11
- assist = {
12
- /**
13
- * gets a widget form's dialog element as a jQuery object
14
- * @param {Object} fm jQuery of the widget form
15
- * @return (Object) jQuery of the dialog element
16
- */
17
- getDialog : function(fm){
18
- return $( '#' + fm.find(dotPrefix + '-onchange').data().cmwDialogId );
19
- },
20
- /**
21
- * gets the widget form's values
22
- * @param {Object} fm jQuery of the widget form
23
- * @return {Object} key=>value pairs of the form element values
24
- */
25
- getSettings : function(fm){
26
- var settings = {};
27
- $.each(fm.serializeArray(), function(i, v){
28
- var name = v.name.replace(/.*\[([^\]]+)\]$/, '$1'),
29
- val = name !== 'items' && /^-?\d+$/.test(v.value) ? parseInt(v.value, 10) : v.value;
30
- settings[name] = val;
31
- if(name === 'items'){
32
- settings._items_sep = !val || /(^\d+$|,)/.test($.trim(val)) ? ',' : ' ';
33
- val = $.map(val.split(/[,\s]+/), function(x){
34
- x = x ? parseInt(x, 10) : 0;
35
- return isNaN(x) || x < 1 ? null : x;
36
- });
37
- settings._items = val.join(settings._items_sep);
38
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  });
40
- return settings;
41
- },
42
- /**
43
- * recursively build LI items for the menu structure (kicked off by createMenu())
44
- * @param {Object} items Set of menu items with same parent
45
- * @param {integer} level Level of parent within the structure (1-based)
46
- * @return {string} HTML
47
- */
48
- buildRecurse : function(items, level, trace){
49
- var rtn = '', n, i;
50
- level = (level || 0) + 1;
51
- trace = trace || '';
52
- for(n in items){
53
- i = n.split('|')[0];
54
- rtn += '<li class="level-' + level + '" data-itemid="' + i + '" data-level="' + level + '" data-trace="' + trace + '">';
55
- rtn += '<a class="ui-corner-all" href="#"><span class="ui-corner-all" title="#' + i + '">' + n.replace(/^\d+\|/, '');
56
- rtn += '</span></a><input type="checkbox" value="' + i + '" />';
57
- if(items[n]){
58
- rtn += '<ul>' + assist.buildRecurse(items[n], level, i + (trace ? ',' : '') + trace) + '</ul>';
59
- }
60
- rtn += '</li>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
- return rtn;
63
- },
64
- /**
65
- * change handler for an item's checkbox in the menu structure
66
- * @this {Element} Input checkbox element
67
- * @param {Object} e Event object
68
- */
69
- changeMenu : function(e){
70
- var self = $(this),
71
- itemsField = $(self.closest('.ui-dialog-content').data().cmwTriggerChange).closest('form').find(dotPrefix + '-setitems'),
72
- currVal = $.trim(itemsField.val()),
73
- sep = !currVal || /(^\d+$|,)/.test(currVal) ? ',' : ' ';
74
- itemsField.val(
75
- self.closest('.cmw-demo-themenu').find('input').map(function(){
76
- return this.checked ? this.value : null;
77
- }).get().join(sep)
78
- ).trigger('change');
79
- },
80
- /**
81
- * click handler for an item in the menu structure : sets or clears current menu item and its ancestors
82
- * @this {Element} Anchor element clicked on
83
- * @param {Object} e Event object
84
- * @return {boolean} false
85
- */
86
- clickMenu : function(e){
87
- var self = $(this),
88
- cls = ['current-menu-item', 'current-menu-parent', 'current-menu-ancestor'],
89
- dialog = self.closest('.ui-dialog-content'),
90
- themenu = dialog.find('.cmw-demo-themenu'),
91
- inPath = self.find('span').not('.' + cls[0]).parentsUntil(themenu, 'li'),
92
- i, n,
93
- appendCls = function(){
94
- this.title = this.title + ' ' + n.replace(' ', ' & ').replace(/-/g, ' ');
95
- };
96
- themenu.find('.' + cls.join(',.')).removeClass(cls.join(' ')).each(function(){
97
- this.title = this.title.replace(/\s.*$/, '');
98
- });
99
- for(i = 0; i < inPath.length; i++){
100
- n = i === 1 ? cls.join(' ') : cls[0];
101
- inPath.eq(i).children('a').find('span').addClass(n).each(appendCls);
102
- if(cls.length > 1){
103
- cls.shift();
104
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  }
106
- //trigger a change in the widget form, causing update() to be run...
107
- $( dialog.data().cmwTriggerChange ).trigger('change');
108
- return false;
109
- },
110
- /**
111
- * click handler for an item in the Basic Output list : triggers a click on the respective menu structure item
112
- * @this {Element} Anchor element clicked on
113
- * @param {Object} e Event object
114
- * @return {boolean} false
115
- */
116
- clickOutput : function(e){
117
- var indx = this.href.split('#')[1];
118
- $(this).closest('.ui-dialog-content').find('.cmw-demo-themenu a').eq(indx).not(':has(.current-menu-item)').trigger('click');
119
- this.blur();
120
- return false;
121
- },
122
- /**
123
- * click handler for the shortcode : selects the text contained in the shortcode's CODE element
124
- * @this {Element} The CODE element
125
- * @param {Object} e Event object
126
- */
127
- clickShortcode : function(e){
128
- var doc = document,
129
- range, selection;
130
- if (doc.body.createTextRange){ //ms
131
- range = doc.body.createTextRange();
132
- range.moveToElementText(this);
133
- range.select();
134
- }else if(window.getSelection){ //all others
135
- selection = window.getSelection();
136
- range = doc.createRange();
137
- range.selectNodeContents(this);
138
- selection.removeAllRanges();
139
- selection.addRange(range);
140
  }
141
- },
142
- /**
143
- * creates a new list of menu items and inserts it into the dialog content in place of any previous one
144
- * @param {Object} dialog jQuery object of the dialog
145
- * @param {Object} fm jQuery object of the widget form
146
- */
147
- createMenu : function(dialog, fm){
148
- var data = dialog.data(),
149
- themenu = dialog.find('.cmw-demo-themenu'),
150
- selectmenu = fm.find(dotPrefix + '-selectmenu'),
151
- menuid = parseInt(selectmenu.val(), 10),
152
- currentmenu = themenu.find('ul').eq(0),
153
- mdata = themenu.data(),
154
- menu;
155
- if(!currentmenu.length || currentmenu.data('menuid') !== menuid){
156
- menu = $('<ul>' + assist.buildRecurse( fm.find(dotPrefix + '-childrenof optgroup').data().cmwItems || {} ) + '</ul>');
157
- currentmenu.remove();
158
- dialog.dialog('option', 'title', data.cmwTitlePrefix + selectmenu.find('option:selected').text() );
159
- mdata.maxLevel = 0;
160
- themenu.append( menu.data('menuid', menuid) )
161
- .find('a').each(function(i){
162
- var level = $(this).parent('li').data().level;
163
- $(this).data('indx', i);
164
- if(level && level > mdata.maxLevel){
165
- mdata.maxLevel = level;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  }
167
- });
 
 
 
 
168
  }
169
- },
170
- /**
171
- * toggles the assist dialog open/closed, creating it if necessary
172
- * @this {Element} A -toggle-assist anchor
173
- * @param {Object} e Event object
174
- * @return {boolean} false
175
- */
176
- init : function(e){
177
- var self = $(this),
178
- data = self.closest(dotPrefix + '-onchange').data(),
179
- dialog = $( '#' + data.cmwDialogId ),
180
- fm = self.closest('form');
181
- if(!dialog.length){
182
- //create it...
183
- dialog = $('<div/>', {id:data.cmwDialogId}).addClass(dotPrefix.substr(1) + '-dialog')
184
- .data({cmwTriggerChange:data.cmwDialogTrigger, cmwTitlePrefix:data.cmwDialogTitle})
185
- .append( $('<div/>').addClass('cmw-demo-theoutput').html('<strong>' + data.cmwDialogOutput + '</strong> &hellip;<div class="cmw-demo-theoutput-wrap ui-corner-all"></div><div class="cmw-demo-fallback"><small>' + data.cmwDialogFallback + '</small></div>') )
186
- .append( $('<div/>').addClass('cmw-demo-themenu').html('<small><em>' + data.cmwDialogPrompt + '</em></small>') )
187
- .append( $('<div/>').addClass('cmw-demo-theshortcode').html('<code class="ui-corner-all">[custom_menu_wizard]</code>') );
188
- dialog.find('.cmw-demo-themenu').on('click', 'a', assist.clickMenu);
189
- dialog.find('.cmw-demo-themenu').on('change', 'input', assist.changeMenu);
190
- dialog.find('.cmw-demo-theshortcode').on('click', 'code', assist.clickShortcode);
191
- dialog.find('.cmw-demo-theoutput').on('click', 'a', assist.clickOutput);
192
- dialog.dialog({autoOpen:false, width:Math.min($(window).width() * 0.8, 600), modal:false});
193
- }
194
- if(dialog.dialog('isOpen')){
195
- dialog.dialog('close');
196
- }else{
197
- assist.createMenu(dialog, fm);
198
- dialog.dialog('open');
199
- $(data.cmwDialogTrigger).trigger('change');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  }
201
- this.blur();
202
- return false;
203
- },
204
- /**
205
- * create and show the shortcode equivalent
206
- * @param {Object} fm jQuery object of the widget form
207
- * @param {Object} settings Form element values
208
- */
209
- shortcode : function(fm, settings){
210
- var args = {
211
- 'menu' : [settings.menu]
212
- },
213
- v, m, n;
214
- if(settings.title){
215
- args.title = settings.title;
216
- }
217
- if(settings.filter > 0){
218
- switch(settings.filter_item){
219
- case 0: args.children_of = 'current'; break;
220
- case -1: args.children_of = 'parent'; break;
221
- case -2: args.children_of = 'root'; break;
222
- default:
223
- args.children_of = [settings.filter_item];
224
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  }
226
- if(settings.filter < 0){
227
- args.items = settings._items;
 
 
228
  }
229
- if(settings.filter > 0 && settings.filter_item < 0 && settings.fallback_no_ancestor){
230
- if(settings.fallback_include_parent_siblings){
231
- args.fallback_parent = 'siblings';
232
- }else if(settings.fallback_include_parent){
233
- args.fallback_parent = 'parent';
234
- }else{
235
- args.fallback_parent = [1];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  }
238
- if(settings.filter > 0 && !settings.filter_item && settings.fallback_no_children){
239
- if(settings.fallback_nc_include_parent_siblings){
240
- args.fallback_current = 'siblings';
241
- }else if(settings.fallback_nc_include_parent){
242
- args.fallback_current = 'parent';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  }else{
244
- args.fallback_current = [1];
 
 
 
 
 
 
 
 
 
 
 
245
  }
 
 
 
 
246
  }
247
- if(settings.start_level > 1){
248
- args.start_level = [settings.start_level];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  }
250
- if(settings.depth > 0){
251
- args.depth = [settings.depth];
 
 
 
 
 
 
 
 
 
 
 
252
  }
253
- if(settings.depth_rel_current && settings.depth > 0){
254
- args.depth_rel_current = [1];
 
 
255
  }
256
- n = [];
257
- if(settings.filter > 0){
258
- if(settings.include_parent_siblings){
259
- n.push('siblings');
260
- }else if(settings.include_parent){
261
- n.push('parent');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
- if(settings.include_ancestors){
264
- n.push('ancestors');
265
  }
266
- if(n.length){
267
- args.include = n.join(' ');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  }
269
  }
270
- n = [];
271
- if(settings.filter > 0 && settings.title_from_parent){
272
- n.push('parent');
273
- }
274
- if(settings.title_from_current){
275
- n.push('current');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  }
277
- if(n.length){
278
- args.title_from = n.join(' ');
 
 
 
 
279
  }
280
- for(n in {flat_output:1, contains_current:1, ol_root:1, ol_sub:1}){
281
- if(settings[n]){
282
- args[n] = [1];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
 
 
284
  }
285
- v = {container:'div', container_id:'', container_class:'', menu_class:'menu-widget', widget_class:''};
286
- for(n in v){
287
- if(settings[n] !== v[n]){
288
- args[n] = settings[n];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  }
290
  }
291
- v = {wrap_link:'before', wrap_link_text:'link_before'};
292
- for(n in v){
293
- m = settings[v[n]].toString().match(/^<(\w+)/);
294
- if(m && m[1]){
295
- args[n] = m[1];
 
 
 
 
296
  }
297
- }
298
- v = [];
299
- for(n in args){
300
- //array indicates 'as is', otherwise surround it in double quotes...
301
- v.push( $.isArray(args[n]) ? n + '=' + args[n][0] : n + '="' + args[n] + '"' );
302
- }
303
- assist.getDialog(fm).find('code').text('[custom_menu_wizard ' + v.join(' ') + ']');
304
- },
305
- /**
306
- *
307
- * @this {Element}
308
- * @param {Object} e Event object
309
- * @param {Object} fm jQuery of widget form
310
- * @param {Object} settings Form element values
311
- */
312
- show : function(e, fm, settings){ //scope is a widget form element
313
- //hide_empty is assumed to be On (WP < v3.6) or will automatically be On (WP v3.6+)
314
- fm = fm || $(this).closest('form');
315
- settings = settings || assist.getSettings(fm);
316
- var dialog = assist.getDialog(fm),
317
- themenu = dialog.find('.cmw-demo-themenu'),
318
- items = themenu.find('.picked'),
319
- html = '',
320
- title = '',
321
- currLevel = 0,
322
- output = dialog.find('.cmw-demo-theoutput-wrap').empty(),
323
- listClass = ['menu-widget'],
324
- itemList = {};
325
- if(items.length && output.length){
326
- if(settings.filter > 0 && settings.title_from_parent){
327
- title = themenu.find('.the-parent').children('a').text() || '';
328
  }
329
- if(!title && settings.title_from_current){
330
- title = themenu.find('.current-menu-item').text() || '';
 
 
331
  }
332
- if(!title && !settings.hide_title){
333
- title = settings.title || '';
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
335
- items.each(function(i){
336
- var self = $(this),
337
- data = self.data(),
338
- trace = data.trace ? data.trace.toString().split(',') : [],
339
- iid = data.itemid.toString(),
340
- level = 1,
341
- anchor = self.children('a');
342
- if(!settings.flat_output){
343
- itemList[iid] = 1;
344
- for(i = 0; i < trace.length; i++){
345
- if(itemList[trace[i]]){
346
- level++;
347
- }
348
- }
349
- }
350
- if(currLevel){
351
- if(level > currLevel){
352
- html += settings.ol_sub ? '<ol>' : '<ul>';
353
- }else{
354
- while(currLevel > level){
355
- --currLevel;
356
- html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
357
- }
358
- html += '</li>';
359
- }
360
  }
361
- html += '<li class="cmw-level-' + level + (data.included || '') + '"><a href="#' + anchor.data('indx') + '">' + anchor.text() + '</a>';
362
- currLevel = level;
363
- });
364
- while(currLevel > 1){
365
- --currLevel;
366
- html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
367
- }
368
- html += '</li>';
369
- listClass.push( dialog.find('.cmw-demo-fallback').data('fellback') );
370
- html = (settings.ol_root ? '<ol' : '<ul') + ' class="' + $.trim(listClass.join(' ')) + '">' + html + (settings.ol_root ? '</ol>' : '</ul>');
371
- output.html(html);
372
- if(title){
373
- output.prepend('<h3>' + title + '</h3>');
374
- }
375
- output.find('li').filter(function(){
376
- return !!$(this).children('ul, ol').length;
377
- }).addClass('cmw-has-submenu');
378
- }
379
- assist.shortcode(fm, settings);
380
- },
381
- /**
382
- * updates the graphic menu structure from the widget form data
383
- * @this {Element} An input (radio or checkbox) or select element from the widget form
384
- * @param {Object} e Event object
385
- */
386
- update : function(e){
387
- var fm = $(this).closest('form'),
388
- dialog = assist.getDialog(fm),
389
- maxLevel, settings, includeParent, includeParentSiblings, themenu, items,
390
- currentItemLI, currentItemLevel, fallback, parent, i, j;
391
- if(!dialog.length || !dialog.dialog('isOpen')){
392
- return;
393
- }
394
-
395
- if($(e.target).hasClass(dotPrefix.substr(1) + '-selectmenu')){
396
- assist.createMenu(dialog, fm);
397
- }
398
- settings = assist.getSettings(fm);
399
- includeParent = settings.include_parent;
400
- includeParentSiblings = settings.include_parent_siblings;
401
- themenu = dialog.find('.cmw-demo-themenu');
402
- maxLevel = themenu.data().maxLevel;
403
- currentItemLI = themenu.find('.current-menu-item').closest('li');
404
- currentItemLevel = currentItemLI.length ? currentItemLI.data().level : -1;
405
- items = themenu.find('li').removeData('included').removeClass('the-parent');
406
-
407
- if(settings.filter < 0){
408
- items = items.filter(function(){
409
- var checkbox = $(this).children('input'),
410
- checked = (settings._items_sep + settings._items + settings._items_sep).indexOf(settings._items_sep + checkbox[0].value + settings._items_sep) > -1;
411
- checkbox.prop('checked', checked);
412
- return checked;
413
- });
414
- if(!settings._items){
415
- items = $([]);
416
  }
 
 
417
  }
 
418
 
419
- if(items.length && !currentItemLI.length && (settings.contains_current || (settings.filter > 0 && settings.filter_item < 1))){
420
- items = $([]);
421
- }
 
422
 
423
- if(items.length && settings.filter > 0){
424
- //kids of...
425
- if(settings.filter_item > 0){
426
- //specific item...
427
- parent = items.filter('[data-itemid=' + settings.filter_item + ']');
428
- }else if(!settings.filter_item){
429
- //current...
430
- if(currentItemLI.find('li').length){
431
- parent = currentItemLI;
432
- }else if(settings.fallback_no_children){
433
- //fall back to current parent...
434
- parent = themenu.find('.current-menu-parent').closest('li');
435
- if(!parent.length){
436
- parent = themenu; //beware!
437
- }
438
- includeParent = includeParent || settings.fallback_nc_include_parent;
439
- includeParentSiblings = includeParentSiblings || settings.fallback_nc_include_parent_siblings;
440
- fallback = 'cmw-fellback-to-parent';
441
- }
442
- }else{
443
- //parent or root...
444
- if(currentItemLevel === 1 && settings.fallback_no_ancestor){
445
- parent = currentItemLI;
446
- includeParent = includeParent || settings.fallback_include_parent;
447
- includeParentSiblings = includeParentSiblings || settings.fallback_include_parent_siblings;
448
- fallback = 'cmw-fellback-to-current';
449
- }else if(currentItemLevel === 1){
450
- parent = themenu; //beware!
451
- }else if(settings.filter_item < -1){
452
- parent = themenu.find('.current-menu-ancestor').eq(0).closest('li');
453
- }else{
454
- parent = themenu.find('.current-menu-parent').closest('li');
455
- }
456
  }
457
- }
458
-
459
- if(items.length){
460
- if(!settings.filter){
461
- //showall : use the levels...
462
- if(settings.depth_rel_current && settings.depth && currentItemLI.length && currentItemLevel >= settings.start_level){
463
- j = currentItemLevel + settings.depth - 1;
464
- }else{
465
- j = settings.depth ? settings.start_level + settings.depth - 1 : 9999;
466
- }
467
- for(i = 1; i <= maxLevel; i++){
468
- if(i < settings.start_level || i > j){
469
- items = items.not('.level-' + i);
 
470
  }
471
  }
472
- }else if(parent && parent.length){
473
- //kids of...
474
- if(settings.depth_rel_current && settings.depth && currentItemLI.length && parent.has(currentItemLI[0]).length){
475
- j = currentItemLevel - 1 + settings.depth;
476
- }else{
477
- j = settings.depth ? Math.max( (parent.data().level || 0) + settings.depth, settings.start_level + settings.depth - 1 ) : 9999;
478
- }
479
- items = parent.find('li').filter(function(){
480
- var level = $(this).data().level;
481
- return level >= settings.start_level && level <= j;
482
- });
483
- }else if(settings.filter > 0){
484
- //kids-of, but no parent found...
485
- items = $([]);
486
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
  }
 
 
 
 
 
 
 
488
 
489
- if(items.length){
490
- if(settings.filter > 0 && parent && parent.is('li')){
491
- //kids of an item...
492
- if(includeParentSiblings){
493
- items = items.add( parent.siblings('li').data('included', ' cmw-an-included-parent-sibling') );
494
- includeParent = true;
495
- }
496
- if(settings.include_ancestors){
497
- items = items.add( parent.parentsUntil(themenu, 'li').data('included', ' cmw-an-included-ancestor') );
498
- includeParent = true;
499
- }
500
- if(includeParent){
501
- items = items.add( parent.data('included', ' cmw-the-included-parent') );
 
 
 
 
 
502
  }
503
  }
 
 
 
 
 
 
504
  }
 
505
 
506
- //must contain current item?...
507
- if(items.length && settings.contains_current && (!currentItemLI.length || !items.filter(currentItemLI).length)){
508
- items = $([]);
509
- }
510
 
511
- if(items.length && parent && parent.is('li')){
512
- parent.addClass('the-parent');
 
 
 
 
 
 
 
 
 
 
513
  }
514
- fallback = items.length ? fallback : '';
515
- dialog.find('.cmw-demo-fallback').data('fellback', fallback).toggleClass('cmw-demo-fellback', !!fallback);
516
- themenu.toggleClass('cmw-demo-filteritems', settings.filter < 0)
517
- .find('.picked').not( items.addClass('picked') ).removeClass('picked');
518
- assist.show.call(this, e, fm, settings);
519
  }
520
- };
521
 
522
- $(document)
523
- //fieldsets...
524
- .on('click', dotPrefix + '-collapsible-fieldset', function(){
525
- var self = $(this),
526
- chkbox = self.find('input').eq(0),
527
- collapse = !chkbox.prop('checked');
528
- if(chkbox.length){
529
- chkbox.prop('checked', collapse);
530
- self.find('div').toggleClass('cmw-collapsed-fieldset', collapse);
531
- self.next('div')[collapse?'slideUp':'slideDown']();
532
- }
533
- this.blur();
534
- return false;
535
- })
536
- //change of menu, and enableif / disableif...
537
- .on('change', dotPrefix + '-listen', function(){
538
- var fm = $(this.form),
539
- selectMenu = fm.find(dotPrefix + '-selectmenu'),
540
- showAll = fm.find(dotPrefix + '-showall').prop('checked'),
541
- showSpecific = fm.find(dotPrefix + '-showspecific').prop('checked'),
542
- filterItem = fm.find(dotPrefix + '-childrenof'),
543
- fiVal = parseInt(filterItem.val(), 10),
544
- groupClone;
545
- if(selectMenu.is(this)){
546
- //change of menu : swap out the childrenof's optgroup for the new one...
547
- selectMenu = this.selectedIndex;
548
- if(!filterItem.find('optgroup').filter(function(){
549
- var keep = $(this).data('cmwOptgroupIndex') === selectMenu;
550
- if(!keep){
551
- $(this).remove();
552
- }
553
- return keep;
554
- }).length){
555
- groupClone = $('#' + filterItem.attr('id') + '_ignore').find('optgroup').eq(selectMenu).clone();
556
- if(groupClone.length){
557
- if(fiVal > 0){
558
- fiVal = 0;
559
- filterItem.val(fiVal);
560
- }
561
- groupClone.find('option[selected]').removeAttr('selected').prop('selected', false);
562
- filterItem.append(groupClone);
563
- }
564
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
  }
566
  $.each(
567
  { '' : showAll || showSpecific,
568
  '-ss' : showSpecific,
569
- 'not-rp' : showAll || showSpecific || fiVal >= 0,
570
- 'not-ci' : showAll || showSpecific || !!fiVal
571
  },
572
  function(k, v){
573
- $(dotPrefix + '-disableif' + k, this.form).toggleClass('cmw-colour-grey', v).find('input,select').prop('disabled', v);
574
  });
575
- })
576
- //any change event on inputs or selects...
577
- .on('change', dotPrefix + '-onchange', assist.update)
578
- //assist dialog...
579
- .on('click', dotPrefix + '-toggle-assist', assist.init)
580
- //when a widget is closed, close its open dialog...
581
- .on('click', '.widget-action, .widget-control-close', function(){
582
- $(this).closest('div.widget').find('.widget-custom-menu-wizard-onchange').each(function(){
583
- var dialog = $('#' + $(this).data().cmwDialogId);
584
- if(dialog.length && dialog.dialog('isOpen')){
585
- dialog.dialog('close');
 
 
 
 
 
 
 
 
 
 
 
 
 
586
  }
587
- });
588
- })
589
- //when a widget is deleted, remove its dialog...
590
- .on('click', '.widget-control-remove', function(e){
591
- $(this).closest('div.widget').find('.widget-custom-menu-wizard-onchange').each(function(){
592
- var dialog = $('#' + $(this).data().cmwDialogId);
593
- if(dialog.length){
594
- dialog.dialog('destroy');
595
- dialog.remove();
 
 
596
  }
597
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598
  });
599
 
600
  });
1
  /* Plugin Name: Custom Menu Wizard
 
 
 
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
+ widgetCustomMenuWizardClass = function(suffix, dot){
11
+ return (!dot ? '' : '.') + 'widget-custom-menu-wizard-' + suffix;
12
+ },
13
+ /**
14
+ * updates the graphic menu structure from the widget settings
15
+ * @this {element} div.widget-custom-menu-wizard-onchange
16
+ * @param {object} e Event object
17
+ */
18
+ assistance = function(e){
19
+ var v = $(this).data().cmwDialogVersion.replace(/\./g, '');
20
+ v = /^\d+$/.test(v) ? 'v' + v : v;
21
+ //run the update() method of the relevant assist object, based on a version number...
22
+ if(cmwAssist[v]){
23
+ cmwAssist[v].update(e ? e.target : this);
24
+ }else{
25
+ cmwAssist.update(e ? e.target : this);
26
+ }
27
+ }, //end assistance()
28
+ /**
29
+ * sets the tick or cross classes and returns a filtered set of the items that *are* ticked/crossed
30
+ * @param {object} items jQuery of elements to filter
31
+ * @param {array} settings Widget settings
32
+ * @param {string} tickOrCross Either 'tick' or 'cross'
33
+ * @return {object} jQuery of filtered items
34
+ */
35
+ filterTickCross = function( items, settings, tickOrCross ){
36
+ var sep = tickOrCross === 'tick' ? settings._items_sep : settings._exclude_sep,
37
+ inheritance = [],
38
+ haystack = tickOrCross === 'tick' ? '_items' : '_exclude';
39
+ haystack = settings[haystack]
40
+ ? //extract those with inheritance...
41
+ $.grep(settings[haystack].split(sep), function(v){
42
+ if(/\+$/.test(v)){
43
+ inheritance.push(parseInt(v, 10));
44
+ return !v;
45
+ }
46
+ return !!v;
47
+ })
48
+ : [];
49
+ haystack = sep + haystack.join(sep) + sep;
50
+ inheritance = sep + inheritance.join(sep) + sep;
51
+ //need to remember that we're turning off as well as on, because there's no generic clear-down...
52
+ items = items.each(function(){
53
+ var item = $(this),
54
+ data = item.data(),
55
+ inherit = inheritance.indexOf(sep + data.itemid + sep) > -1,
56
+ matched = inherit || haystack.indexOf(sep + data.itemid + sep) > -1;
57
+ item.toggleClass('cmw-has-' + tickOrCross, matched)
58
+ .toggleClass('cmw-inherit-' + tickOrCross, inherit);
59
  });
60
+ //returning items that *are* ticked/crossed, so get the inheritance items...
61
+ return items.filter('.cmw-inherit-' + tickOrCross)
62
+ //...get their descendants and clear any tick/cross classes that may have been set...
63
+ .find('li').removeClass('cmw-has-' + tickOrCross + ' cmw-inherit-' + tickOrCross)
64
+ //...add back in (to the inheritance descendants) any (other) items that still have tick/cross
65
+ //set, which will include the uppermost of any inheritance items...
66
+ .add( items.filter('.cmw-has-' + tickOrCross) );
67
+ }, //end filterTickCross
68
+ /**
69
+ * gets the -onchange wrapper
70
+ * @param {object} below jQuery of element to search below (exclusive, ie. find())
71
+ * @param {object} above jQuery of element to search above (inclusive, ie. closest())
72
+ * @return {object} jQuery of the -onchange wrapper
73
+ */
74
+ findOnchange = function(below, above){
75
+ return (!below ? above : below)[!below ? 'closest' : 'find'](widgetCustomMenuWizardClass('onchange', 1));
76
+ },
77
+ /**
78
+ * gets the widget's field values
79
+ * @param {object} oc jQuery of the widget's onchange wrapper
80
+ * @return {object} key=>value pairs of the field element values
81
+ */
82
+ getSettings = function(oc){
83
+ var settings = {},
84
+ legacyVersion = oc.data('cmwDialogVersion') === '2.1.0',
85
+ csv = {items:1, exclude:1},
86
+ keepAsString = $.extend({branch_start:1, exclude_level:1}, csv);
87
+ $.each(oc.find(':input').serializeArray(), function(i, v){
88
+ var name = v.name.replace(/.*\[([^\]]+)\]$/, '$1'),
89
+ val = !keepAsString[name] && /^-?\d+$/.test(v.value) ? parseInt(v.value, 10) : v.value;
90
+ settings[name] = val;
91
+ if(name === 'hide_empty'){
92
+ settings[name] = !!oc.data().cmwV36plus || val;
93
+ }else if(csv[name]){
94
+ settings['_' + name + '_sep'] = !val || /(^\d+\+?$|,)/.test($.trim(val)) ? ',' : ' ';
95
+ val = $.map(val.split(/[,\s]+/), function(x){
96
+ var inherit = !legacyVersion && /\+$/.test(x);
97
+ x = x ? parseInt(x, 10) : 0;
98
+ return isNaN(x) || x < 1 ? null : (inherit ? x + '+' : x);
99
+ });
100
+ settings['_' + name] = val.join(settings['_' + name + '_sep']);
101
  }
102
+ });
103
+ return settings;
104
+ }, //end getSettings()
105
+ /**
106
+ * produces the final output
107
+ * @param {object} dialog jQuery of target dialog
108
+ * @param {object} settings Field element values
109
+ */
110
+ showOutput = function(dialog, settings){
111
+ var topOfMenu = dialog.find('.cmw-demo-themenu-ul'),
112
+ items = topOfMenu.find('.picked'),
113
+ html = '',
114
+ title = settings.hide_title ? '' : settings.title,
115
+ currLevel = 0,
116
+ output = dialog.find('.cmw-demo-theoutput-wrap').empty(),
117
+ listClass = ['menu-widget'],
118
+ itemList = {};
119
+ if(items.length && output.length){
120
+ //determine title: update() might have set a class for it...
121
+ title = topOfMenu.find('.title-from-item').children('.cmw-item').text() || '';
122
+ //...otherwise, check the actual widget title...
123
+ if(!title && !settings.hide_title){
124
+ title = settings.title || '';
125
+ }
126
+
127
+ items.each(function(i){
128
+ var self = $(this),
129
+ data = self.data(),
130
+ trace = data.trace ? data.trace.toString().split(',') : [],
131
+ iid = data.itemid.toString(),
132
+ level = 1,
133
+ anchor = self.children('.cmw-item');
134
+ if(!settings.flat_output){
135
+ itemList[iid] = 1;
136
+ for(i = 0; i < trace.length; i++){
137
+ if(itemList[trace[i]]){
138
+ level++;
139
+ }
140
+ }
 
 
 
141
  }
142
+ if(currLevel){
143
+ if(level > currLevel){
144
+ html += settings.ol_sub ? '<ol>' : '<ul>';
145
+ }else{
146
+ while(currLevel > level){
147
+ --currLevel;
148
+ html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
149
+ }
150
+ html += '</li>';
151
+ }
152
+ }
153
+ html += '<li class="cmw-level-' + level + (data.included || '') + '"><a href="#' + anchor.data('indx') + '">' + anchor.text() + '</a>';
154
+ currLevel = level;
155
+ });
156
+ while(currLevel > 1){
157
+ --currLevel;
158
+ html += '</li>' + (settings.ol_sub ? '</ol>' : '</ul>');
159
  }
160
+ html += '</li>';
161
+ listClass.push( dialog.find('.cmw-demo-fallback').data('fellback') );
162
+ html = (settings.ol_root ? '<ol' : '<ul') + ' class="' + $.trim(listClass.join(' ')) + '">' + html + (settings.ol_root ? '</ol>' : '</ul>');
163
+ output.html(html);
164
+ output.find('li').filter(function(){
165
+ return !!$(this).children('ul, ol').length;
166
+ }).addClass('cmw-has-submenu');
167
+ }
168
+ if(output.length && title && (items.length || !settings.hide_empty)){
169
+ output.prepend('<h3>' + title + '</h3>');
170
+ }
171
+ }, //end showOutput()
172
+ /**
173
+ * swap in the apropriate optgroup of items according to the selected menu
174
+ * @param {object} menuItems jQuery of the SELECT being modified
175
+ * @param {integer} selectedItem Currently selected item in menuItems
176
+ * @param {integer} selectedMenu Index of selected menu
177
+ * @return {integer} selectedItem
178
+ */
179
+ swapItems = function(menuItems, selectedItem, selectedMenu){
180
+ var groupClone;
181
+ if(!menuItems.find('optgroup').filter(function(){
182
+ var keepit = $(this).data().cmwOptgroupIndex === selectedMenu;
183
+ if(!keepit){
184
+ $(this).remove();
185
+ }
186
+ return keepit;
187
+ }).length){
188
+ groupClone = $('#' + menuItems.attr('id') + '_ignore').find('optgroup').eq(selectedMenu).clone();
189
+ if(groupClone.length){
190
+ if(selectedItem > 0){
191
+ selectedItem = 0;
192
+ }
193
+ menuItems.append(groupClone).val(selectedItem);
194
  }
195
+ }
196
+ return selectedItem;
197
+ }, //end swapItems()
198
+ /**
199
+ * toggles a set of widgets options open or closed
200
+ * @this {element} Header of the set
201
+ * @param {object} e Event object
202
+ * @return {boolean} false
203
+ */
204
+ clickFieldset = function(e){
205
+ var self = $(this),
206
+ chkbox = self.next('.cmw-fieldset-state'),
207
+ collapse = !chkbox.prop('checked');
208
+ if(chkbox.length){
209
+ chkbox.prop('checked', collapse);
210
+ self.toggleClass('cmw-collapsed-fieldset', collapse);
211
+ chkbox.next('div')[collapse ? 'slideUp' : 'slideDown']();
212
+ }
213
+ this.blur();
214
+ return false;
215
+ }, //end clickFieldSet()
216
+ /**
217
+ * fetches and displays a list of posts that contain old/new shortcodes
218
+ * @this {element} The link/button
219
+ * @param {object} e Event object
220
+ * @return {boolean} false
221
+ */
222
+ clickFindShortcodes = function(e){
223
+ var self = $(this),
224
+ grandad = self.parent().parent();
225
+ //if currently fetching, do nothing...
226
+ if(ajaxurl && !grandad.hasClass('cmw-ajax-fetching')){
227
+ //if currently showing previous results, remove the results from view...
228
+ if(grandad.hasClass('cmw-ajax-showing')){
229
+ grandad.removeClass('cmw-ajax-showing');
230
+ }else{
231
+ //fetch results via ajax, showing spinner while doing so...
232
+ grandad.addClass('cmw-ajax-fetching');
233
+ $.post(
234
+ ajaxurl,
235
+ { 'action': 'cmw-find-shortcodes', '_wpnonce': self.data().nonce }
236
+ ).done(function(response){
237
+ if(!!response && response !== '0'){
238
+ grandad.find('.cmw-demo-found-shortcodes').html($(response).find('response_data').text());
239
+ grandad.addClass('cmw-ajax-showing');
240
  }
241
+ }
242
+ ).always(function(response){
243
+ grandad.removeClass('cmw-ajax-fetching');
244
+ }
245
+ );
246
  }
247
+ }
248
+ this.blur();
249
+ return false;
250
+ },
251
+ /**
252
+ * toggles fixed/absolute positioning for the dialog (work around for draggable problems in jQuery UI v1.10.3/4)
253
+ * @this {element} The "fixed" button
254
+ * @param {object} e Event object
255
+ * @return {boolean} false
256
+ */
257
+ clickFixedAbsolute = function(e){
258
+ var self = $(this),
259
+ data = self.data(),
260
+ toAbsolute = !data.cmwAbsolute,
261
+ dialogBox = self.closest('.ui-dialog'),
262
+ dialog = dialogBox.find('.ui-dialog-content'),
263
+ //if fixed -> absolute, add scrollTop to [css]top; if absolute -> fixed, substract scrollTop from [css]top...
264
+ newTop = parseInt(dialogBox.css('top'), 10) + ( (toAbsolute ? 1 : -1) * $(document).scrollTop() );
265
+ data.cmwAbsolute = toAbsolute;
266
+ if(!data.cmwMaxHeight){
267
+ //store the initial maxHeight setting...
268
+ data.cmwMaxHeight = dialog.dialog('option', 'maxHeight');
269
+ }
270
+ //swap the icon...
271
+ self.button('option', 'icons', {primary:toAbsolute ? 'ui-icon-circle-close' : 'ui-icon-circle-check'});
272
+ //toggle the class to force either fixed (add class) or absolute (remove class)...
273
+ dialogBox.toggleClass('cmw-assistance-dialog-fixed', !toAbsolute);
274
+ //have to reset dialog's maxHeight here, *before* re-positioning, because UI will screw up the position when we set it!...
275
+ dialog.dialog('option', {maxHeight: toAbsolute ? !toAbsolute : data.cmwMaxHeight});
276
+ //re-position the dialog (have to use CSS because UI dialog's position option doesn't hack it!)...
277
+ dialogBox.css('top', newTop);
278
+ return false;
279
+ },
280
+ /**
281
+ * click handler for an item in the menu structure : sets or clears current menu item and its ancestors
282
+ * @this {element} Anchor element clicked on
283
+ * @param {object} e Event object
284
+ * @return {boolean} false
285
+ */
286
+ clickMenu = function(e){
287
+ var self = $(this),
288
+ cls = ['current-menu-item', 'current-menu-parent', 'current-menu-ancestor'],
289
+ dialog = self.closest('.ui-dialog-content'),
290
+ topOfMenu = dialog.find('.cmw-demo-themenu-ul'),
291
+ inPath = self.find('span').not('.' + cls[0]).parentsUntil(topOfMenu, 'li'),
292
+ i, n,
293
+ appendCls = function(){
294
+ this.title = this.title + ' ' + n.replace(' ', ' & ').replace(/-/g, ' ');
295
+ };
296
+ topOfMenu.find('.' + cls.join(',.')).removeClass(cls.join(' ')).each(function(){
297
+ this.title = this.title.replace(/\s.*$/, '');
298
+ });
299
+ for(i = 0; i < inPath.length; i++){
300
+ n = i === 1 ? cls.join(' ') : cls[0];
301
+ inPath.eq(i).children('.cmw-item').find('span').addClass(n).each(appendCls);
302
+ if(cls.length > 1){
303
+ cls.shift();
304
  }
305
+ }
306
+ //run update() via assistance()...
307
+ assistance.call( $(dialog.data().cmwOnchange).get(0) );
308
+ return false;
309
+ }, //end clickMenu()
310
+ /**
311
+ * click handler for an item in the Basic Output list : triggers a click on the respective menu structure item
312
+ * @this {element} Anchor element clicked on
313
+ * @param {object} e Event object
314
+ * @return {boolean} false
315
+ */
316
+ clickOutput = function(e){
317
+ $(this).closest('.ui-dialog-content').find('.cmw-item')
318
+ .eq( this.href.split('#')[1] )
319
+ .not(':has(.current-menu-item)').trigger('click');
320
+ this.blur();
321
+ return false;
322
+ }, //end clickOutput()
323
+ /**
324
+ * click handler for assist's menu tree expander/collapser
325
+ *
326
+ * @this {element} Element clicked on
327
+ * @param {object} e Event object
328
+ * @return {boolean} false
329
+ */
330
+ clickTreeExpander = function(e){
331
+ var direction = /1-e/.test(this.className) ? 'slideUp' : 'slideDown';
332
+ $(this).toggleClass('ui-icon-triangle-1-s ui-icon-triangle-1-e').prev('ul')[direction]();
333
+ return false;
334
+ },
335
+ /**
336
+ * click handler for a tick or cross against any item in the menu structure (does not modify any classes)
337
+ * @this {element} Tick or Cross clicked on
338
+ * @param {object} e Event object
339
+ * @return {boolean} false
340
+ */
341
+ clickTickCross = function(e){
342
+ var self = $(this),
343
+ tickOrCross = self.hasClass('cmw-tick') ? 'tick' : 'cross',
344
+ item = self.parent(),
345
+ topOfMenu = item.closest('.cmw-demo-themenu-ul'),
346
+ hasInheritTickCross = item.hasClass('cmw-inherit-' + tickOrCross),
347
+ hasTickCross = hasInheritTickCross || item.hasClass('cmw-has-' + tickOrCross),
348
+ inheritsTickCrossFrom = hasTickCross ? $([]) : item.parentsUntil(topOfMenu, '.cmw-inherit-' + tickOrCross),
349
+ widgetField = $( item.closest('.ui-dialog-content').data().cmwOnchange )
350
+ .find(tickOrCross === 'tick' ? '.cmw-setitems' : '.cmw-exclusions'),
351
+ sampleSet;
352
+ if(widgetField.length){ //should never not find it!
353
+ //A. if this item hasInheritTickCross then this click will remove tickCross entirely and all inheritance will be lost
354
+ //B. else* if this item hasTickCross then this click will either
355
+ // B1. if the item has no descendants, or we're running legacy version, remove the tickCross
356
+ // B2. else add inheritance to it
357
+ //C. else* if this item inheritsTickCross then this click will
358
+ // - disinherit the relevant ancestor (keeping tickCross!)
359
+ // - and set all its descendants - bar this* item! - to tickCross
360
+ //D. else* this click will add tickCross (without inheritance) to this item
361
+
362
+ //find everything that currently has tickCross, and for (A) remove this item, or for (D) add this item...
363
+ //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
364
+ sampleSet = topOfMenu
365
+ .find('.cmw-has-' + tickOrCross)[ hasInheritTickCross || inheritsTickCrossFrom.length ? 'not' : 'add' ](item);
366
+ //(A) & (D) are now sorted and can be forgotten about
367
+ //for (B1), remove this item...
368
+ if(hasTickCross && (!item.children('ul').length || topOfMenu.parent().hasClass('cmw-version-210'))){
369
+ sampleSet = sampleSet.not(item);
370
  }
371
+ else
372
+ //for (B2), because we're adding inheritance, any descendant of this item that is currently tickCross now needs to be excluded...
373
+ if(hasTickCross && !hasInheritTickCross){
374
+ sampleSet = sampleSet.not( item.find('.cmw-has-' + tickOrCross) );
375
  }
376
+ //(B) is now also sorted and can be forgotten about
377
+ //for (C) we need to find the ancestor providing the inheritance and add in all its descendants bar this item...
378
+ sampleSet = sampleSet.add( inheritsTickCrossFrom.find('li').not(item) );
379
+ //map what's left to the associated item id, appending a plus sign for inheritance...
380
+ sampleSet = sampleSet.map(function(){
381
+ var plus = this === item[0] || this === inheritsTickCrossFrom.get(0) ? hasTickCross : $(this).hasClass('cmw-inherit-' + tickOrCross);
382
+ return $(this).data().itemid + (plus ? '+' : '');
383
+ })
384
+ .get().join( /(,|^\d+\+?$)/.test( $.trim(widgetField.val()) || ',' ) ? ',' : ' ' );
385
+ widgetField.val(sampleSet).trigger('change');
386
+ }
387
+ this.blur();
388
+ return false;
389
+ }, //end clickTickCross()
390
+ /**
391
+ * closes an open dialog when the widget is closed
392
+ * @this {element} The .widget-action or .widget-control-close clicked on
393
+ * @param {object} e Event object
394
+ */
395
+ closeDialog = function(e){
396
+ var widgetContainer = $(this).closest('div.widget'),
397
+ containerParent = widgetContainer.parent();
398
+ //in widget customizer, opening one widget closes any other open widget without triggering anything that
399
+ //would make CMW close the assist ... which is good! however, I also don't want the assist to close when
400
+ //[re]opening a CMW widget, so for the customizer I need to check that the widget is "expanded" before
401
+ //making the assist close...
402
+ if(!containerParent.hasClass('customize-control-widget_form') || containerParent.hasClass('expanded')){
403
+ findOnchange(widgetContainer).each(function(){
404
+ var dialog = $('#' + $(this).data().cmwDialogId);
405
+ if(dialog.length && dialog.dialog('isOpen')){
406
+ dialog.dialog('close');
407
  }
408
+ });
409
+ }
410
+ }, //end closeDialog()
411
+ /**
412
+ * creates the UI Dialog for the widget's assist
413
+ * @param {object} data Information required to build the dialog
414
+ * @return {object} jQuery object of the created Dialog
415
+ */
416
+ createDialog = function(data){
417
+ var dialogOpts = {
418
+ autoOpen: false,
419
+ //initial width is the lowest of 600px and 90% of window width...
420
+ width: Math.min($(window).width() * 0.9, 600),
421
+ //starting out at fixed, so max the resizable height at 40px less than window height so that entire dialog box is visible...
422
+ maxHeight: $(window).height() - 40,
423
+ modal: false,
424
+ containment: 'window',
425
+ create: function(){
426
+ //add a "fixed" button to the titlebar, enabling switching between fixed and absolute positioning for the dialog...
427
+ var dialogBox = $(this).closest('.ui-dialog');
428
+ if(dialogBox.hasClass('cmw-assistance-dialog-fixed')){
429
+ $('<button/>').addClass('cmw-dialog-fixed-absolute')
430
+ .button({label:data.cmwDialogFixed, icons:{primary:'ui-icon-circle-check'}})
431
+ .appendTo( dialogBox.find('.ui-dialog-titlebar') )
432
+ .on('click', clickFixedAbsolute);
433
+ }
434
+ },
435
+ dialogClass: 'cmw-assistance-dialog cmw-assistance-dialog-fixed'
436
+ },
437
+ msgs = $.map(['SetCurrent', 'Inclusions', 'Exclusions', 'Fallback'], function(v){
438
+ return '<div class="cmw-demo-' + v.toLowerCase() + ' cmw-demo-small">' + (data['cmwDialog' + v] || '') + '</div>';
439
+ }),
440
+ dialog = $('<div/>', {id:data.cmwDialogId}).addClass(widgetCustomMenuWizardClass('dialog'))
441
+ .append(
442
+ $('<div/>').addClass('cmw-demo-themenu cmw-version-' + data.cmwDialogVersion.replace(/\./g, ''))
443
+ .html('<em class="cmw-demo-small">' + data.cmwDialogPrompt + '</em>')
444
+ )
445
+ .append(
446
+ $('<div/>').addClass('cmw-demo-theoutput')
447
+ .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(''))
448
+ )
449
+ .append(
450
+ $('<div/>').addClass('cmw-demo-theshortcode')
451
+ .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>')
452
+ );
453
+ dialog.dialog(dialogOpts);
454
+ dialog.find('.cmw-demo-themenu')
455
+ .on('click', '.cmw-tick,.cmw-cross', clickTickCross)
456
+ .on('click', '.cmw-item', clickMenu);
457
+ dialog.find('.cmw-demo-theoutput').on('click', 'a', clickOutput);
458
+ return dialog;
459
+ }, //end createDialog()
460
+ /**
461
+ * removes associated dialog when the widget is deleted
462
+ * @this {element} The .widget-control-remove clicked on
463
+ * @param {object} e Event object
464
+ */
465
+ removeDialog = function(e){
466
+ findOnchange($(this).closest('div.widget')).each(function(){
467
+ var dialog = $('#' + $(this).data().cmwDialogId);
468
+ if(dialog.length){
469
+ dialog.dialog('destroy');
470
+ dialog.remove();
471
  }
472
+ });
473
+ }, //end removeDialog()
474
+ /**
475
+ * (re)sets the dialog's title
476
+ * @param {object} dialog jQuery object of the dialog
477
+ * @param {object} oc jQuery object of the onchange wrapper
478
+ */
479
+ setDialogTitle = function(dialog, oc){
480
+ dialog.dialog('option', 'title', 'CMW : ' + ( oc.find('.cmw-widget-title').val() || dialog.data().cmwUntitled ) + ' [' + oc.find('.cmw-select-menu').find('option:selected').text() + ']' );
481
+ },
482
+ /**
483
+ * creates a new list of menu items and inserts it into the dialog content in place of any previous one
484
+ * @param {object} dialog jQuery object of the dialog
485
+ */
486
+ createMenu = function(dialog){
487
+ var oc = $(dialog.data().cmwOnchange),
488
+ menuid = parseInt(oc.find('.cmw-select-menu').val(), 10),
489
+ currentmenu = dialog.find('.cmw-demo-themenu-ul'),
490
+ trace = [],
491
+ maxLevel = 0,
492
+ menu = '',
493
+ outdentedExpander = function(x){
494
+ //x is the level of the *previous* item; so if previous item was at level 4, then its parent was at level 3, and
495
+ //was therefore indented twice (given that a level 1 item is not indented), so number of outdents is x - 2!
496
+ //each level is indented by 2.4em; subtract 2 from x and multiply by 2.4em, then add another 2em...
497
+ 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>' : '';
498
+ };
499
+ if(!currentmenu.length || currentmenu.data('menuid') !== menuid){
500
+ oc.find('.cmw-assist-items optgroup').find('option').each(function(i){
501
+ var self = $(this),
502
+ level = self.data().cmwLevel;
503
+ while(level < trace.length){
504
+ menu += '</li></ul>' + outdentedExpander(trace.length);
505
+ trace.pop();
506
+ }
507
+ if(level > trace.length){
508
+ menu += '<ul>';
509
  }else{
510
+ menu += '</li>';
511
+ trace.pop();
512
+ }
513
+ //data-level is 1-based, with 1 being root
514
+ //data-trace is the menu item ids of this item's ancestors, from root down to parent (inclusive)
515
+ menu += '<li class="level-' + level + '" data-itemid="' + this.value + '" data-level="' + level + '" data-trace="' + trace.join(',') + '">';
516
+ menu += '<a href="#" class="cmw-cross ui-corner-all"></a>';
517
+ menu += '<a class="cmw-item ui-corner-all" href="#" data-indx="' + i + '"><span class="ui-corner-all" title="#' + this.value + '">' + $.trim(self.text());
518
+ menu += '</span></a><a href="#" class="cmw-tick ui-corner-all"></a>';
519
+ trace.push(this.value);
520
+ if(level > maxLevel){
521
+ maxLevel = level;
522
  }
523
+ });
524
+ while(trace.length){
525
+ menu += '</li></ul>' + outdentedExpander(trace.length);
526
+ trace.pop();
527
  }
528
+ currentmenu.remove();
529
+ dialog.find('.cmw-demo-themenu')
530
+ .append( $(menu).addClass('cmw-demo-themenu-ul').data({maxLevel:maxLevel, menuid:menuid}) );
531
+ }
532
+ }, //end createMenu()
533
+ /**
534
+ * toggles the assist dialog open/closed, creating it if necessary
535
+ * @this {element} A The assist anchor
536
+ * @param {object} e Event object
537
+ * @return {boolean} false
538
+ */
539
+ clickAssist = function(e){
540
+ var self = $(this),
541
+ oc = findOnchange(0, self),
542
+ data = oc.data(),
543
+ dialog = $( '#' + data.cmwDialogId );
544
+ if(!dialog.length){
545
+ dialog = createDialog(data).data({
546
+ cmwOnchange: '#' + oc.attr('id'),
547
+ cmwUntitled: '[' + data.cmwDialogUntitled + ']'
548
+ });
549
+ }
550
+ if(dialog.dialog('isOpen')){
551
+ dialog.dialog('close');
552
+ }else{
553
+ createMenu(dialog);
554
+ setDialogTitle(dialog, oc);
555
+ dialog.dialog('open');
556
+ assistance.call(oc[0]);
557
+ }
558
+ this.blur();
559
+ return false;
560
+ }; //end clickAssist()
561
+
562
+ /**
563
+ * The assist object, containing functions that do most of the version-specific work.
564
+ * There is a default set of functions, and any different legacy versions that are still
565
+ * supported should be under their own 'vNNN' object (eg. 'v210'). Which set of functions
566
+ * is called is determined by assistance(), which is the only entry point (into update()),
567
+ * and is governed by the widget form (specifically, the version property of the data
568
+ * attached to the -onchange wrapper).
569
+ */
570
+ cmwAssist = {
571
+ /**
572
+ * sets any fields that are dependent on the selected menu
573
+ * @param {object} oc jQuery of the widget's onchange wrapper
574
+ * @param {integer} maxLevel Maximum level of the selected menu
575
+ */
576
+ setLevels : function(oc, maxLevel){
577
+ if(!maxLevel){
578
+ return;
579
+ }
580
+ var theSelect = oc.find('.cmw-branch-start'),
581
+ level = theSelect.val();
582
+ theSelect.find('optgroup').each(function(i){
583
+ var self = $(this),
584
+ opts = self.find('option'),
585
+ ct = opts.length,
586
+ texts = self.data();
587
+ if(i){
588
+ //absolute...
589
+ opts.slice(maxLevel).remove();
590
+ while(ct < maxLevel){
591
+ ++ct;
592
+ self.append( $('<option/>', {value:ct}).text(ct) );
593
  }
594
+ }else{
595
+ //relative...
596
+ //if maxLevel is 1 : the item (1 option)
597
+ //if maxLevel is 2 : -1, the item, +1 (3 options)
598
+ //if maxLevel is 3 : -2, -1, the item, +1, +2 (5 options)
599
+ //etc, etc
600
+ ct = (ct + 1) / 2;
601
+ if(ct > maxLevel){
602
+ opts.each(function(i, el){
603
+ if(Math.abs(el.value) >= maxLevel){
604
+ $(el).remove();
605
+ }
606
+ });
607
  }
608
+ while(ct < maxLevel){
609
+ self.prepend( $('<option/>', {value:-ct}).text(-ct + (ct > 1 ? '' : ' (' + texts.cmwTextParent + ')')) )
610
+ .append( $('<option/>', {value:'+' + ct}).text('+' + ct + (ct > 1 ? '' : ' (' + texts.cmwTextChildren + ')')) );
611
+ ++ct;
612
  }
613
+ }
614
+ });
615
+ //if level is absolute and > maxLevel, set to 1...
616
+ if(/^\d+$/.test(level)){
617
+ if(level > maxLevel){
618
+ theSelect.val('1');
619
+ }
620
+ //if level is relative, not 'the item', and absolutely >= maxLevel, set to '' (the item)...
621
+ }else if(level !== '' && Math.abs(level) >= maxLevel){
622
+ theSelect.val('');
623
+ }
624
+
625
+ oc.find('.cmw-ancestors,.cmw-ancestor-siblings').each(function(){
626
+ var self = $(this),
627
+ ct = (self.find('option').length + 1) / 2, //should never be below 2
628
+ v = self.val(),
629
+ minLevel = Math.max(2, maxLevel);
630
+ if(Math.abs(v) >= maxLevel){
631
+ self.val( v < 0 ? 1 - maxLevel : maxLevel - 1 );
632
+ }
633
+ if(ct !== minLevel){
634
+ self.find('optgroup').each(function(i, el){
635
+ var optgroup = $(el),
636
+ text = optgroup.data().cmwTextForOption,
637
+ j;
638
+ if(ct > minLevel){
639
+ optgroup.find('option').slice( minLevel - ct ).remove();
640
  }
641
+ for(j = ct; j < minLevel; j++){
642
+ optgroup.append( $('<option/>', {value:i ? j : -j}).text( text.replace('%d', i ? j : -j) ) );
643
  }
644
+ });
645
+ }
646
+ });
647
+
648
+ oc.find('.cmw-exclude-level').each(function(){
649
+ var self = $(this),
650
+ options = self.find('option'),
651
+ ct = (options.length - 1) / 3,
652
+ v = self.val(),
653
+ above = options.eq(2).text(),
654
+ below = options.eq(3).text();
655
+ options.slice( (maxLevel * 3) + 1 ).remove();
656
+ while(ct < maxLevel){
657
+ ++ct;
658
+ self.append( $('<option/>', {value:ct}).text(ct) )
659
+ .append( $('<option/>', {value:ct + '-'}).text(above.replace(/\d+/, ct)) )
660
+ .append( $('<option/>', {value:ct + '+'}).text(below.replace(/\d+/, ct)) );
661
+ }
662
+ if(parseInt(v, 10) > maxLevel){
663
+ self.val('');
664
+ }
665
+ });
666
+
667
+ //do the easy levels...
668
+ oc.find('.cmw-level').each(function(){
669
+ var self = $(this),
670
+ ct = self.find('option').length;
671
+ if(self.val() > maxLevel){
672
+ self.val(1);
673
+ }
674
+ self.find('option').slice(maxLevel).remove();
675
+ while(ct < maxLevel){
676
+ ++ct;
677
+ self.append( $('<option/>', {value:ct}).text(ct) );
678
+ }
679
+ });
680
+ oc.find('.cmw-depth,.cmw-fallback-depth').each(function(){
681
+ var self = $(this),
682
+ ct = self.find('option').length,
683
+ txt = ' ' + self.data().cmwTextLevels;
684
+ if(self.val() > maxLevel){
685
+ self.val(0);
686
+ }
687
+ self.find('option').slice(maxLevel + 1).remove();
688
+ while(ct <= maxLevel){
689
+ self.append( $('<option/>', {value:ct}).text(ct + txt) );
690
+ ++ct;
691
+ }
692
+ });
693
+ }, //end cmwAssist.setLevels()
694
+ /**
695
+ * enables/disables fields and swaps selected menus
696
+ * @param {object} target jQuery of element responsible for 'change' event
697
+ * @param {object} oc jQuery of the widget's onchange wrapper
698
+ */
699
+ setFields : function(target, oc){
700
+ var byBranchCheckbox = oc.find('.cmw-bybranch'),
701
+ byItems = oc.find('.cmw-byitems').prop('checked'),
702
+ notByBranch = byItems || !byBranchCheckbox.prop('checked'),
703
+ menuItems = oc.find('.cmw-assist-items'),
704
+ selectedItem = parseInt(menuItems.val(), 10);
705
+ //change of menu? : make sure the correct optgroup of menu items is used...
706
+ if(target.hasClass('cmw-select-menu')){
707
+ selectedItem = swapItems(menuItems, selectedItem, target[0].selectedIndex);
708
+ this.setLevels(oc, (menuItems.find('optgroup').data() || {}).cmwMaxLevel);
709
+ //if level is changed, switch to by-level filtering...
710
+ }else if(target.hasClass('cmw-level')){
711
+ notByBranch = true;
712
+ byItems = !notByBranch;
713
+ oc.find('.cmw-bylevel').prop('checked', notByBranch);
714
+ //if by-branch's branch is changed, switch to by-branch filtering...
715
+ }else if(target.is(menuItems)){
716
+ notByBranch = byItems = false;
717
+ byBranchCheckbox.prop('checked', !notByBranch);
718
+ //change of items?, switch to by-items filtering...
719
+ }else if(target.hasClass('cmw-setitems')){
720
+ byItems = true;
721
+ notByBranch = byItems;
722
+ oc.find('.cmw-byitems').prop('checked', byItems);
723
+ //if ancestors is cleared, clear ancestor siblings...
724
+ }else if(target.hasClass('cmw-ancestors') && target.val() === '0'){
725
+ oc.find('.cmw-ancestor-siblings').val('0');
726
+ //if include ancestor's siblings is changed to a value, and ancestors is empty, set ancestors from ancestor siblings...
727
+ }else if(target.hasClass('cmw-ancestor-siblings') && target.val() !== '0' && oc.find('.cmw-ancestors').val() === '0'){
728
+ oc.find('.cmw-ancestors').val( target.val() );
729
+ }
730
+
731
+ $.each( //disable if...
732
+ { '-ss' : byItems, //...is Items
733
+ '-ud' : oc.find('.cmw-depth').val() < 1, //...is Unlimited Depth
734
+ 'not-br' : notByBranch, //...is NOT Branch
735
+ 'not-br-ci' : notByBranch || !!selectedItem, //...is NOT Branch:Current Item
736
+ 'not-fb-pc' : !{'parent':1, 'current':1}[oc.find('.cmw-fallback').val()] //...is NOT fallback to parent or current
737
+ },
738
+ function(k, v){
739
+ oc.find('.cmw-disableif' + k).toggleClass('cmw-colour-grey', v).find('input,select').prop('disabled', v);
740
+ });
741
+ }, //end cmwAssist.setFields()
742
+ /**
743
+ * create and return the shortcode equivalent
744
+ * @param {object} settings Widget settings
745
+ * @return {string} Shortcode
746
+ */
747
+ shortcode : function(settings){
748
+ var args = {
749
+ 'menu' : settings.menu
750
+ },
751
+ byBranch = settings.filter === 'branch',
752
+ byItems = settings.filter === 'items',
753
+ byLevel = !byBranch && !byItems,
754
+ v, m, n;
755
+ //take notice of the widget's hide_title flag...
756
+ if(settings.title && !settings.hide_title){
757
+ args.title = [settings.title];
758
+ }
759
+ //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...
760
+ if(byLevel && settings.level > 1){
761
+ args.level = settings.level;
762
+ }
763
+ //specifying branch sets byBranch, overriding byLevel...
764
+ if(byBranch){
765
+ args.branch = settings.branch || 'current';
766
+ //start_at only *has* to be specified if not empty...
767
+ if(settings.branch_start){
768
+ args.start_at = [settings.branch_start];
769
+ }
770
+ //start_mode may be brought into play by a fallback so always specify it...
771
+ if(settings.start_mode === 'level'){
772
+ args.start_mode = 'level';
773
+ }
774
+ }
775
+ //specifying items set byItems, overriding byLevel & byBranch...
776
+ if(byItems){
777
+ args.items = [settings._items];
778
+ }
779
+ //depth if greater than 0...
780
+ if(settings.depth > 0){
781
+ args.depth = settings.depth;
782
+ }
783
+ //depth relative to current item is only applicable if depth is not unlimited...
784
+ if(settings.depth_rel_current && settings.depth > 0){
785
+ args.depth_rel_current = 1;
786
+ }
787
+ //fallbacks...
788
+ //no children : branch = current item...
789
+ if(byBranch && !settings.branch){
790
+ //format = quit|parent|current [,+siblings] [,depth] eg. "parent,+siblings,1" or "current,2" or "current,+siblings" or "parent" or "quit"
791
+ if(settings.fallback){
792
+ args.fallback = [settings.fallback];
793
+ if(settings.fallback !== 'quit'){
794
+ if(settings.fallback_siblings){
795
+ args.fallback.push('+siblings');
796
+ }
797
+ if(settings.fallback_depth){
798
+ args.fallback.push( settings.fallback_depth );
799
  }
800
  }
801
+ }
802
+ }
803
+ //branch ancestor inclusions...
804
+ if(byBranch && settings.ancestors){
805
+ args.ancestors = settings.ancestors;
806
+ if(settings.ancestor_siblings){
807
+ args.ancestor_siblings = settings.ancestor_siblings;
808
+ }
809
+ }
810
+ //exclusions by id...
811
+ if(settings._exclude){
812
+ args.exclude = [settings._exclude];
813
+ }
814
+ //...and by level...
815
+ if(settings.exclude_level){
816
+ args.exclude_level = [settings.exclude_level];
817
+ }
818
+ //title from...
819
+ n = [];
820
+ //...current and current root (in that order of priority) are exclusive...
821
+ if(settings.title_from_current){
822
+ n.push('current');
823
+ }else if(settings.title_from_current_root){
824
+ n.push('current-root');
825
+ }
826
+ //...branch and branch root (in that order of priority) are exclusive...
827
+ if(byBranch && settings.title_from_branch){
828
+ n.push('branch');
829
+ }else if(byBranch && settings.title_from_branch_root){
830
+ n.push('branch-root');
831
+ }
832
+ if(n.length){
833
+ args.title_from = n;
834
+ }
835
+ //switches...
836
+ for(n in {allow_all_root:1, siblings:1, include_root:1, flat_output:1, ol_root:1, ol_sub:1}){
837
+ if(settings[n]){
838
+ args[n] = 1;
839
+ }
840
+ }
841
+ //strings...
842
+ v = {contains_current:'', container:'div', container_id:'', container_class:'', menu_class:'menu-widget', widget_class:''};
843
+ for(n in v){
844
+ if(settings[n] !== v[n]){
845
+ args[n] = [settings[n]];
846
+ }
847
+ }
848
+ v = {wrap_link:'before', wrap_link_text:'link_before'};
849
+ for(n in v){
850
+ m = settings[v[n]].toString().match(/^<(\w+)/);
851
+ if(m && m[1]){
852
+ args[n] = [m[1]];
853
+ }
854
+ }
855
+ //build the shortcode...
856
+ v = [];
857
+ for(n in args){
858
+ //array indicates join (with comma sep) & surround it in double quotes, otherwise leave 'as-is'...
859
+ v.push( $.isArray(args[n]) ? n + '="' + args[n].join(',') + '"' : n + '=' + args[n] );
860
+ }
861
+ //NB at v3.0.0, the shortcode changed from custom_menu_wizard to cmwizard (the previous version is still supported)
862
+ return '[cmwizard ' + v.join(' ') + ']';
863
+ }, //end cmwAssist.shortcode()
864
+ /**
865
+ * updates the graphic menu structure from the widget data
866
+ * @param {element} el Element responsible for being here
867
+ */
868
+ update : function(el){
869
+ var target = $(el),
870
+ oc = findOnchange(0, target),
871
+ dialog = $('#' + oc.data().cmwDialogId),
872
+ tobLevel = -1,
873
+ lastVisibleLevel = 9999,
874
+ hasIncl = 0,
875
+ hasExcl = 0,
876
+ fallback = '',
877
+ byLevel, byBranch, byItems, theBranchItem,
878
+ ciBranch, maxLevel, settings,
879
+ topOfMenu, items, exclusions,
880
+ local_depth, local_depth_rel_current,
881
+ currentItemLI, currentItemLevel, topOfBranch, i, j, k, x, y;
882
+
883
+ if(target.hasClass('cmw-listen')){
884
+ //the widget field that changed is likely to have an effect on other widget fields...
885
+ this.setFields(target, oc);
886
+ }
887
+ settings = getSettings(oc);
888
+
889
+ //dialog specific...
890
+ if(dialog.length && dialog.dialog('isOpen')){
891
+
892
+ dialog.dialog('moveToTop');
893
+
894
+ //if selected menu has changed, modify assist's structure...
895
+ if(target.hasClass('cmw-select-menu')){
896
+ createMenu(dialog);
897
+ }
898
+ byBranch = settings.filter === 'branch';
899
+ byItems = settings.filter === 'items';
900
+ byLevel = !byBranch && !byItems;
901
+ ciBranch = byBranch && !settings.branch;
902
+ topOfMenu = dialog.find('.cmw-demo-themenu-ul');
903
+ maxLevel = topOfMenu.data().maxLevel;
904
+ currentItemLI = topOfMenu.find('.current-menu-item').closest('li');
905
+ currentItemLevel = currentItemLI.length ? currentItemLI.data().level : -1;
906
+ items = topOfMenu.find('li').removeData('included').removeClass('title-from-item');
907
+
908
+ //don't want to alter settings at all (affects shortcode output), so create some local copies for use within this function...
909
+ local_depth = settings.depth;
910
+ local_depth_rel_current = settings.depth_rel_current;
911
+
912
+ //ticks and crosses (need to be run against the full set of items)...
913
+ exclusions = filterTickCross(items, settings, 'cross');
914
+ if(byItems){
915
+ //primary filter : items...
916
+ items = filterTickCross(items, settings, 'tick');
917
+ }
918
+
919
+ //if current item is definitely needed and is not present, cop out...
920
+ if(items.length && !currentItemLI.length && (!!settings.contains_current || ciBranch)){
921
+ items = $([]);
922
+ }
923
+
924
+ //primary filter : branch...
925
+ if(byBranch && items.length){
926
+ topOfBranch = ciBranch ? currentItemLI : items.filter('[data-itemid=' + settings.branch + ']');
927
+ if(topOfBranch.length){
928
+ tobLevel = topOfBranch.data().level || 0;
929
+ items = topOfBranch.add( topOfBranch.find('li') );
930
+ //since topOfBranch can change later on...
931
+ theBranchItem = topOfBranch;
932
+ }else{
933
+ items = $([]);
934
  }
935
+ }
936
+
937
+ //primary filter : level...
938
+ if(byLevel && items.length && settings.level > 1){
939
+ for(i = 1, j = []; i < settings.level; i++){
940
+ j.push('.level-' + i);
941
  }
942
+ items = items.not( j.join(',') );
943
+ }
944
+
945
+ //check for current item after primary...
946
+ if(settings.contains_current === 'primary' && items.length && !currentItemLI.is(items)){
947
+ items = $([]);
948
+ }
949
+
950
+ //secondary filter : level...
951
+ if(byLevel && items.length && local_depth){
952
+ i = local_depth_rel_current && currentItemLevel >= settings.level
953
+ //if the limited depth is relative to current item, and current item can be found at or below the start level...
954
+ ? currentItemLevel
955
+ //set relative to start level...
956
+ : settings.level;
957
+ i += local_depth;
958
+ //note that i has been set to the first level *not* wanted!
959
+ if(i <= maxLevel){
960
+ for(j = []; i <= maxLevel; i++){
961
+ j.push('.level-' + i);
962
  }
963
+ //filter to remove...
964
+ items = items.not( j.join(',') );
965
  }
966
+ }
967
+
968
+ //secondary filter : branch...
969
+ if(byBranch && items.length){
970
+ //convert start level to integer...
971
+ j = parseInt(settings.branch_start, 10);
972
+ //convert relative to absolute (max'd against 1)...
973
+ j = isNaN(j) || !j ? tobLevel : (settings.branch_start.match(/^(\+|-)/) ? Math.max(1, tobLevel + j) : j);
974
+
975
+ //in order to be eligible for a no-kids fallback:
976
+ // - branch must be current item
977
+ // - fallback must be set
978
+ // - current item has no kids
979
+ if(ciBranch && settings.fallback && !currentItemLI.find('li').length){
980
+ //yes, we have a fallback situation...
981
+ fallback = 'cmw-fellback-to-' + settings.fallback;
982
+ if(settings.fallback === 'quit'){
983
+ //copout : just set secondary start level beyond maxLevel...
984
+ j = maxLevel + 1;
985
+ }else{
986
+ //for current, fall back to tob; for parent, fall back to tob - 1, ensuring that we don't fall back further than root...
987
+ j = settings.fallback === 'current' || tobLevel < 2 ? tobLevel : tobLevel - 1;
988
+ //if fallback depth is specified, override depth and set to relative-to-current...
989
+ if(settings.fallback_depth){
990
+ local_depth = settings.fallback_depth;
991
+ local_depth_rel_current = 1;
992
+ }
993
  }
994
  }
995
+
996
+ //j is the secondary start level, tobLevel is the primary level
997
+ //easy result : if j > maxLevel then there are no matches...
998
+ if(j > maxLevel){
999
+ items = $([]);
1000
+ }else{
1001
+ //if secondary start is higher up the structure than primary start, reset the tob...
1002
+ if(j < tobLevel){
1003
+ topOfBranch = topOfBranch.parentsUntil(topOfMenu, 'li.level-' + j);
1004
  }
1005
+ //do we want (and need) to force starting with the entire level...
1006
+ // - only relevant if secondary start is at or above primary start
1007
+ // - and if secondary level is root then allow_all_root must be set
1008
+ if(settings.start_mode === 'level' && j <= tobLevel && (j > 1 || settings.allow_all_root)){
1009
+ //...reset items to eveything at tob's level, plus all their descendants...
1010
+ items = topOfBranch.parent().find('li');
1011
+ }else if(j < tobLevel){
1012
+ //tob has changed so reset items (to just tob and descendants)...
1013
+ items = topOfBranch.add( topOfBranch.find('li') );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1014
  }
1015
+ //if falling back and siblings are required, add them in...
1016
+ //note that root level sibling inclusion is still governed by allow_all_root!
1017
+ if(!!fallback && settings.fallback_siblings && items.length && (j > 1 || settings.allow_all_root)){
1018
+ items = items.add( topOfBranch.siblings('li.level-' + j) );
1019
  }
1020
+ }
1021
+ //may have a tob but might not have any items!...
1022
+ if(items.length){
1023
+ //reset tob level (regardless of whether tob has changed)...
1024
+ tobLevel = j;
1025
+ //is depth unlimited?...
1026
+ k = 9999;
1027
+ if(local_depth){
1028
+ //is (limited) depth relative to current item, and is there an eligible current item to measure against?...
1029
+ k = local_depth_rel_current && currentItemLevel >= tobLevel && items.filter(currentItemLI).length
1030
+ ? currentItemLevel
1031
+ : tobLevel;
1032
+ k += local_depth;
1033
+ lastVisibleLevel = k - 1;
1034
  }
1035
+ //note that k has been set to the first level (after those wanted) that is *not* wanted!
1036
+ for(i = 1, j = []; i <= maxLevel; i++){
1037
+ if(i >= tobLevel && i < k){
1038
+ j.push('.level-' + i);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1039
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1040
  }
1041
+ //filter to keep...
1042
+ items = items.filter( j.join(',') );
1043
  }
1044
+ }
1045
 
1046
+ //check for current item after secondary...
1047
+ if(settings.contains_current === 'secondary' && items.length && !currentItemLI.is(items)){
1048
+ items = $([]);
1049
+ }
1050
 
1051
+ //branch inclusions...
1052
+ if(byBranch && items.length){
1053
+ //branch ancestors, possibly with their siblings : but only if the original branch item is either
1054
+ //in items or is below lastVisibleLevel; ALSO, do not show ancestors below lastVisibleLevel!
1055
+ j = theBranchItem.data().level;
1056
+ if(settings.ancestors && (theBranchItem.is(items) || j > lastVisibleLevel)){
1057
+ x = settings.ancestors;
1058
+ //convert a relative level to an absolute one...
1059
+ if(x < 0){
1060
+ x = Math.max(1, j + x);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1061
  }
1062
+ //ancestor siblings?...
1063
+ y = settings.ancestor_siblings;
1064
+ //convert a relative level to an absolute one...
1065
+ if(y < 0){
1066
+ y = Math.max(1, j + y);
1067
+ }
1068
+ //get the level classes for ancestors and siblings that need to be included...
1069
+ for(i = x, j = [], k = []; i <= maxLevel; i++){
1070
+ if(i <= lastVisibleLevel){
1071
+ //ancestors...
1072
+ j.push('.level-' + i);
1073
+ if(y > 0 && i >= y){
1074
+ //siblings...
1075
+ k.push('.level-' + i);
1076
  }
1077
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1078
  }
1079
+ //store current length of items...
1080
+ x = items.length;
1081
+ //find the ancestors...
1082
+ j = theBranchItem.parentsUntil(topOfMenu, j.join(','));
1083
+ //add new ones into items...
1084
+ items = items.add( j.not(items).data('included', ' cmw-an-included-ancestor') );
1085
+ //got ancestors, now what about their siblings?...
1086
+ if(k.length){
1087
+ //filter ancestors for those we want siblings of, and add new siblings into items...
1088
+ items = items.add( j.filter( k.join(',') ).siblings('li').not(items).data('included', ' cmw-an-included-ancestor-sibling') );
1089
+ }
1090
+ //note how many have been added to items as a result of the includes...
1091
+ hasIncl += items.length - x;
1092
+ }
1093
+ //branch siblings : only if the original branch item is currently in items...
1094
+ if(settings.siblings && theBranchItem.is(items)){
1095
+ j = items.length;
1096
+ items = items.add( theBranchItem.siblings('li').data('included', ' cmw-an-included-sibling') );
1097
+ hasIncl += items.length - j;
1098
  }
1099
+ }
1100
+ //other inclusions...
1101
+ if(items.length && settings.include_root){
1102
+ j = items.length;
1103
+ items = items.add( topOfMenu.find('.level-1') );
1104
+ hasIncl += items.length - j;
1105
+ }
1106
 
1107
+ //check for current item after inclusions...
1108
+ if(settings.contains_current === 'inclusions' && items.length && !currentItemLI.is(items)){
1109
+ items = $([]);
1110
+ }
1111
+
1112
+ //exclusions...
1113
+ if(items.length && exclusions.length){
1114
+ j = items.length;
1115
+ items = items.not(exclusions);
1116
+ hasExcl += j - items.length;
1117
+ }
1118
+ if(items.length && !!settings.exclude_level){
1119
+ k = settings.exclude_level.match(/^(\d+)(\+|-)?$/);
1120
+ k = k ? [parseInt(k[1], 10), k[2] || ''] : [];
1121
+ if(k[0] > 0){
1122
+ for(j = [], i = 1; i <= maxLevel; i++){
1123
+ if( i === k[0] || (k[1] === '-' && i < k[0]) || (k[1] === '+' && i > k[0]) ){
1124
+ j.push('.level-' + i);
1125
  }
1126
  }
1127
+ if(j.length){
1128
+ //filter to remove...
1129
+ k = items.length;
1130
+ items = items.not( j.join(',') );
1131
+ hasExcl += k - items.length;
1132
+ }
1133
  }
1134
+ }
1135
 
1136
+ //final check for current item in output...
1137
+ if(settings.contains_current === 'output' && items.length && !currentItemLI.is(items)){
1138
+ items = $([]);
1139
+ }
1140
 
1141
+ //title from...
1142
+ //...check current then current root...
1143
+ if(settings.title_from_current && currentItemLI.length){
1144
+ currentItemLI.addClass('title-from-item');
1145
+ }else if(settings.title_from_current_root && currentItemLI.length){
1146
+ currentItemLI.closest('.level-1').addClass('title-from-item');
1147
+ }else if(byBranch && theBranchItem){
1148
+ //...then branch, then branch root...
1149
+ if(settings.title_from_branch){
1150
+ theBranchItem.addClass('title-from-item');
1151
+ }else if(settings.title_from_branch_root){
1152
+ theBranchItem.closest('.level-1').addClass('title-from-item');
1153
  }
 
 
 
 
 
1154
  }
 
1155
 
1156
+ //show/hide the fall back message...
1157
+ dialog.find('.cmw-demo-fallback').data('fellback', fallback).toggleClass('updated', !!fallback);
1158
+ //show/hide the 'select current item' prompt...
1159
+ dialog.find('.cmw-demo-setcurrent').toggleClass('error', !currentItemLI.length && (settings.contains_current || ciBranch));
1160
+ //...and the inclusions/exclusions messages...
1161
+ i = {inclusions:hasIncl, exclusions:hasExcl};
1162
+ for(j in i){
1163
+ k = dialog.find('.cmw-demo-' + j);
1164
+ k.text( k.text().replace(/\d+$/, i[j]) ).toggleClass('updated', i[j] > 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1165
  }
1166
+
1167
+ //toggle ticks and 'pick' the remaining items...
1168
+ topOfMenu.toggleClass('cmw-demo-filteritems', byItems)
1169
+ .find('.picked').not( items.addClass('picked') ).removeClass('picked');
1170
+ //produce output...
1171
+ setDialogTitle(dialog, oc);
1172
+ showOutput(dialog, settings);
1173
+
1174
+ } //end dialog specific
1175
+
1176
+ oc.add(dialog).find('code').text( this.shortcode( settings ) );
1177
+
1178
+ } //end cmwAssist.update()
1179
+ }; //end cmwAssist
1180
+
1181
+ /**
1182
+ * pre-v3.0.0 version
1183
+ */
1184
+ cmwAssist.v210 = {
1185
+ /**
1186
+ * sets any fields that are dependent on the selected menu
1187
+ * @param {object} oc jQuery of the widget's onchange wrapper
1188
+ * @param {integer} maxLevel Maximum level of the selected menu
1189
+ */
1190
+ setLevels : function(oc, maxLevel){
1191
+ var theSelect = oc.find('.cmw-start-level'),
1192
+ level = theSelect.val(),
1193
+ ct = theSelect.find('option').length,
1194
+ txt;
1195
+ if(level > maxLevel){
1196
+ theSelect.val(1);
1197
+ }
1198
+ theSelect.find('option').slice(maxLevel).remove();
1199
+ while(ct < maxLevel){
1200
+ ++ct;
1201
+ theSelect.append( $('<option/>', {value:ct}).text(ct) );
1202
+ }
1203
+ theSelect = oc.find('.cmw-depth');
1204
+ level = theSelect.val();
1205
+ ct = theSelect.find('option').length;
1206
+ txt = ' ' + theSelect.data().cmwTextLevels;
1207
+ if(level > maxLevel){
1208
+ theSelect.val(0); //=unlimited
1209
+ }
1210
+ theSelect.find('option').slice(maxLevel + 1).remove();
1211
+ while(ct <= maxLevel){
1212
+ theSelect.append( $('<option/>', {value:ct}).text(ct + txt) );
1213
+ ++ct;
1214
+ }
1215
+ }, //end cmwAssist.v210.setLevels()
1216
+ /**
1217
+ * enables/disables fields and swaps selected menus
1218
+ * @param {object} target jQuery of element responsible for 'change' event
1219
+ * @param {object} oc jQuery of the widget's onchange wrapper
1220
+ */
1221
+ setFields : function(target, oc){
1222
+ var showAll = oc.find('.cmw-showall').prop('checked'),
1223
+ showSpecific = oc.find('.cmw-showspecific').prop('checked'),
1224
+ menuItems = oc.find('.cmw-assist-items'),
1225
+ selectedItem = parseInt(menuItems.val(), 10);
1226
+ //change of menu? : make sure the correct optgroup of menu items is used...
1227
+ if(target.hasClass('cmw-select-menu')){
1228
+ selectedItem = swapItems(menuItems, selectedItem, target[0].selectedIndex);
1229
+ this.setLevels(oc, (menuItems.find('optgroup').data() || {}).cmwMaxLevel);
1230
  }
1231
  $.each(
1232
  { '' : showAll || showSpecific,
1233
  '-ss' : showSpecific,
1234
+ 'not-rp' : showAll || showSpecific || selectedItem >= 0,
1235
+ 'not-ci' : showAll || showSpecific || !!selectedItem
1236
  },
1237
  function(k, v){
1238
+ oc.find('.cmw-disableif' + k).toggleClass('cmw-colour-grey', v).find('input,select').prop('disabled', v);
1239
  });
1240
+ }, //end cmwAssist.v210.setFields()
1241
+ /**
1242
+ * create and return the shortcode equivalent
1243
+ * @param {object} settings Widget settings
1244
+ * @return {string} Shortcode
1245
+ */
1246
+ shortcode : function(settings){
1247
+ var args = {
1248
+ 'menu' : settings.menu
1249
+ },
1250
+ byLevel = !settings.filter,
1251
+ byBranch = settings.filter > 0,
1252
+ byItems = !byLevel && !byBranch,
1253
+ v, m, n;
1254
+ if(settings.title){
1255
+ args.title = [settings.title];
1256
+ }
1257
+ if(byBranch){
1258
+ switch(settings.filter_item){
1259
+ case 0: args.children_of = ['current']; break;
1260
+ case -1: args.children_of = ['parent']; break;
1261
+ case -2: args.children_of = ['root']; break;
1262
+ default:
1263
+ args.children_of = settings.filter_item;
1264
  }
1265
+ }
1266
+ if(byItems){
1267
+ args.items = [settings._items];
1268
+ }
1269
+ if(byBranch && settings.filter_item < 0 && settings.fallback_no_ancestor){
1270
+ if(settings.fallback_include_parent_siblings){
1271
+ args.fallback_parent = ['siblings'];
1272
+ }else if(settings.fallback_include_parent){
1273
+ args.fallback_parent = ['parent'];
1274
+ }else{
1275
+ args.fallback_parent = 1;
1276
  }
1277
+ }
1278
+ if(byBranch && !settings.filter_item && settings.fallback_no_children){
1279
+ if(settings.fallback_nc_include_parent_siblings){
1280
+ args.fallback_current = ['siblings'];
1281
+ }else if(settings.fallback_nc_include_parent){
1282
+ args.fallback_current = ['parent'];
1283
+ }else{
1284
+ args.fallback_current = 1;
1285
+ }
1286
+ }
1287
+ if(settings.start_level > 1){
1288
+ args.start_level = settings.start_level;
1289
+ }
1290
+ if(settings.depth > 0){
1291
+ args.depth = settings.depth;
1292
+ }
1293
+ //depth relative to current item is only applicable if depth is not unlimited...
1294
+ if(settings.depth_rel_current && settings.depth > 0){
1295
+ args.depth_rel_current = 1;
1296
+ }
1297
+ n = [];
1298
+ if(byBranch){
1299
+ if(settings.include_parent_siblings){
1300
+ n.push('siblings');
1301
+ }else if(settings.include_parent){
1302
+ n.push('parent');
1303
+ }
1304
+ if(settings.include_ancestors){
1305
+ n.push('ancestors');
1306
+ }
1307
+ if(n.length){
1308
+ args.include = n;
1309
+ }
1310
+ }
1311
+ n = [];
1312
+ if(byBranch && settings.title_from_parent){
1313
+ n.push('parent');
1314
+ }
1315
+ if(settings.title_from_current){
1316
+ n.push('current');
1317
+ }
1318
+ if(n.length){
1319
+ args.title_from = n;
1320
+ }
1321
+ for(n in {flat_output:1, contains_current:1, ol_root:1, ol_sub:1}){
1322
+ if(settings[n]){
1323
+ args[n] = 1;
1324
+ }
1325
+ }
1326
+ v = {container:'div', container_id:'', container_class:'', menu_class:'menu-widget', widget_class:''};
1327
+ for(n in v){
1328
+ if(settings[n] !== v[n]){
1329
+ args[n] = [settings[n]];
1330
+ }
1331
+ }
1332
+ v = {wrap_link:'before', wrap_link_text:'link_before'};
1333
+ for(n in v){
1334
+ m = settings[v[n]].toString().match(/^<(\w+)/);
1335
+ if(m && m[1]){
1336
+ args[n] = [m[1]];
1337
+ }
1338
+ }
1339
+ v = [];
1340
+ for(n in args){
1341
+ //array indicates join (with space sep) & surround it in double quotes, otherwise leave 'as-is'...
1342
+ v.push( $.isArray(args[n]) ? n + '="' + args[n].join(' ') + '"' : n + '=' + args[n] );
1343
+ }
1344
+ return '[custom_menu_wizard ' + v.join(' ') + ']';
1345
+ }, //end cmwAssist.v210.shortcode()
1346
+ /**
1347
+ * updates the graphic menu structure from the widget data
1348
+ * @param {element} el Element responsible for being here
1349
+ */
1350
+ update : function(el){
1351
+ var target = $(el),
1352
+ oc = findOnchange(0, target),
1353
+ dialog = $('#' + oc.data().cmwDialogId),
1354
+ byLevel, byBranch, byItems,
1355
+ maxLevel, settings, includeParent, includeParentSiblings, topOfMenu, items,
1356
+ currentItemLI, currentItemLevel, fallback, parent, i, j;
1357
+
1358
+ if(target.hasClass('cmw-listen')){
1359
+ //the widget field that changed is likely to have an effect on other widget fields...
1360
+ this.setFields(target, oc);
1361
+ }
1362
+
1363
+ //everything below this point is dialogue-related...
1364
+ if(!dialog.length || !dialog.dialog('isOpen')){
1365
+ return;
1366
+ }
1367
+ dialog.dialog('moveToTop');
1368
+
1369
+ //if selected menu has changed, modify assist's structure...
1370
+ if(target.hasClass('cmw-select-menu')){
1371
+ createMenu(dialog);
1372
+ }
1373
+ settings = getSettings(oc);
1374
+ byLevel = !settings.filter;
1375
+ byBranch = settings.filter > 0;
1376
+ byItems = !byLevel && !byBranch;
1377
+ includeParent = settings.include_parent;
1378
+ includeParentSiblings = settings.include_parent_siblings;
1379
+ topOfMenu = dialog.find('.cmw-demo-themenu-ul');
1380
+ maxLevel = topOfMenu.data().maxLevel;
1381
+ currentItemLI = topOfMenu.find('.current-menu-item').closest('li');
1382
+ currentItemLevel = currentItemLI.length ? currentItemLI.data().level : -1;
1383
+ items = topOfMenu.find('li').removeData('included').removeClass('title-from-item');
1384
+
1385
+ if(byItems){
1386
+ items = filterTickCross(items, settings, 'tick');
1387
+ }
1388
+
1389
+ if(items.length && !currentItemLI.length && (settings.contains_current || (byBranch && settings.filter_item < 1))){
1390
+ items = $([]);
1391
+ }
1392
+
1393
+ if(items.length && byBranch){
1394
+ //kids of...
1395
+ if(settings.filter_item > 0){
1396
+ //specific item...
1397
+ parent = items.filter('[data-itemid=' + settings.filter_item + ']');
1398
+ }else if(!settings.filter_item){
1399
+ //current...
1400
+ if(currentItemLI.find('li').length){
1401
+ parent = currentItemLI;
1402
+ }else if(settings.fallback_no_children){
1403
+ //fall back to current parent...
1404
+ parent = topOfMenu.find('.current-menu-parent').closest('li');
1405
+ if(!parent.length){
1406
+ parent = topOfMenu; //beware!
1407
+ }
1408
+ includeParent = includeParent || settings.fallback_nc_include_parent;
1409
+ includeParentSiblings = includeParentSiblings || settings.fallback_nc_include_parent_siblings;
1410
+ fallback = 'cmw-fellback-to-parent';
1411
+ }
1412
+ }else{
1413
+ //parent or root...
1414
+ if(currentItemLevel === 1 && settings.fallback_no_ancestor){
1415
+ parent = currentItemLI;
1416
+ includeParent = includeParent || settings.fallback_include_parent;
1417
+ includeParentSiblings = includeParentSiblings || settings.fallback_include_parent_siblings;
1418
+ fallback = 'cmw-fellback-to-current';
1419
+ }else if(currentItemLevel === 1){
1420
+ parent = topOfMenu; //beware!
1421
+ }else if(settings.filter_item < -1){
1422
+ parent = topOfMenu.find('.current-menu-ancestor').eq(0).closest('li');
1423
+ }else{
1424
+ parent = topOfMenu.find('.current-menu-parent').closest('li');
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ if(items.length){
1430
+ if(byLevel){
1431
+ //showall : use the levels...
1432
+ if(settings.depth_rel_current && settings.depth && currentItemLI.length && currentItemLevel >= settings.start_level){
1433
+ j = currentItemLevel + settings.depth - 1;
1434
+ }else{
1435
+ j = settings.depth ? settings.start_level + settings.depth - 1 : 9999;
1436
+ }
1437
+ for(i = 1; i <= maxLevel; i++){
1438
+ if(i < settings.start_level || i > j){
1439
+ items = items.not('.level-' + i);
1440
+ }
1441
+ }
1442
+ }else if(parent && parent.length){
1443
+ //kids of...
1444
+ if(settings.depth_rel_current && settings.depth && currentItemLI.length && parent.has(currentItemLI[0]).length){
1445
+ j = currentItemLevel - 1 + settings.depth;
1446
+ }else{
1447
+ j = settings.depth ? Math.max( (parent.data().level || 0) + settings.depth, settings.start_level + settings.depth - 1 ) : 9999;
1448
+ }
1449
+ items = parent.find('li').filter(function(){
1450
+ var level = $(this).data().level;
1451
+ return level >= settings.start_level && level <= j;
1452
+ });
1453
+ }else if(byBranch){
1454
+ //kids-of, but no parent found...
1455
+ items = $([]);
1456
+ }
1457
+ }
1458
+
1459
+ if(items.length){
1460
+ if(byBranch && parent && parent.is('li')){
1461
+ //kids of an item...
1462
+ if(includeParentSiblings){
1463
+ items = items.add( parent.siblings('li').data('included', ' cmw-an-included-parent-sibling') );
1464
+ includeParent = true;
1465
+ }
1466
+ if(settings.include_ancestors){
1467
+ items = items.add( parent.parentsUntil(topOfMenu, 'li').data('included', ' cmw-an-included-ancestor') );
1468
+ includeParent = true;
1469
+ }
1470
+ if(includeParent){
1471
+ items = items.add( parent.data('included', ' cmw-the-included-parent') );
1472
+ }
1473
+ }
1474
+ }
1475
+
1476
+ //must contain current item?...
1477
+ if(items.length && settings.contains_current && (!currentItemLI.length || !items.filter(currentItemLI).length)){
1478
+ items = $([]);
1479
+ }
1480
+
1481
+ //title from parent has higher priority than title from current...
1482
+ if(settings.title_from_parent && items.length && parent && parent.is('li')){
1483
+ parent.addClass('title-from-item');
1484
+ }else if(settings.title_from_current && items.length){
1485
+ currentItemLI.addClass('title-from-item');
1486
+ }
1487
+ //fallback?...
1488
+ fallback = items.length ? fallback : '';
1489
+ dialog.find('.cmw-demo-fallback').data('fellback', fallback).toggleClass('updated', !!fallback);
1490
+ //show/hide the 'select current item' prompt...
1491
+ dialog.find('.cmw-demo-setcurrent').toggleClass('error', !currentItemLI.length && (settings.contains_current || (byBranch && settings.filter_item < 1)));
1492
+
1493
+ //toggle ticks and 'pick' the remaining items...
1494
+ topOfMenu.toggleClass('cmw-demo-filteritems', byItems)
1495
+ .find('.picked').not( items.addClass('picked') ).removeClass('picked');
1496
+ //produce output...
1497
+ setDialogTitle(dialog, oc);
1498
+ showOutput(dialog, settings);
1499
+ dialog.find('code').text( this.shortcode(settings) );
1500
+ } //end cmwAssist.v210.update()
1501
+ }; //end cmwAssist.v210
1502
+
1503
+ $(document)
1504
+ //any change event on widget's inputs or selects...
1505
+ .on('change', widgetCustomMenuWizardClass('onchange', 1), assistance)
1506
+ //open/close assist dialog...
1507
+ .on('click', widgetCustomMenuWizardClass('assist', 1), clickAssist)
1508
+ //expand/collapse fieldsets...
1509
+ .on('click', widgetCustomMenuWizardClass('fieldset', 1), clickFieldset)
1510
+ //when a widget is closed, close its open dialog...
1511
+ .on('click', '.widget-action,.widget-control-close', closeDialog)
1512
+ //when a widget is deleted, remove its dialog...
1513
+ .on('click', '.widget-control-remove', removeDialog)
1514
+ //collapse/expand assist menu tree items...
1515
+ .on('click', widgetCustomMenuWizardClass('colexp', 1), clickTreeExpander)
1516
+ //find posts containing shortcodes by ajax...
1517
+ .on('click', widgetCustomMenuWizardClass('find-shortcodes', 1), clickFindShortcodes)
1518
+ //remove the legacy warning...
1519
+ .on('click', widgetCustomMenuWizardClass('legacy-close', 1), function(){
1520
+ $(this).parent().remove();
1521
+ return false;
1522
  });
1523
 
1524
  });
custom-menu-wizard.min.js CHANGED
@@ -1,28 +1,52 @@
1
- /* Plugin Name: Custom Menu Wizard
2
- * Version: 2.0.5
3
- * Author: Roger Barrett
4
- *
5
- * Script for controlling this widget's options (in Admin -> Widgets)
6
- */
7
- jQuery(function(f){var n={getDialog:function(d){return f("#"+d.find(".widget-custom-menu-wizard-onchange").data().cmwDialogId)},getSettings:function(d){var a={};f.each(d.serializeArray(),function(b,d){var c=d.name.replace(/.*\[([^\]]+)\]$/,"$1"),e="items"!==c&&/^-?\d+$/.test(d.value)?parseInt(d.value,10):d.value;a[c]=e;"items"===c&&(a._items_sep=!e||/(^\d+$|,)/.test(f.trim(e))?",":" ",e=f.map(e.split(/[,\s]+/),function(a){a=a?parseInt(a,10):0;return isNaN(a)||1>a?null:a}),a._items=e.join(a._items_sep))});
8
- return a},buildRecurse:function(d,a,b){var f="",c,e;a=(a||0)+1;b=b||"";for(c in d)e=c.split("|")[0],f+='<li class="level-'+a+'" data-itemid="'+e+'" data-level="'+a+'" data-trace="'+b+'">',f+='<a class="ui-corner-all" href="#"><span class="ui-corner-all" title="#'+e+'">'+c.replace(/^\d+\|/,""),f+='</span></a><input type="checkbox" value="'+e+'" />',d[c]&&(f+="<ul>"+n.buildRecurse(d[c],a,e+(b?",":"")+b)+"</ul>"),f+="</li>";return f},changeMenu:function(d){d=f(this);var a=f(d.closest(".ui-dialog-content").data().cmwTriggerChange).closest("form").find(".widget-custom-menu-wizard-setitems"),
9
- b=f.trim(a.val()),b=!b||/(^\d+$|,)/.test(b)?",":" ";a.val(d.closest(".cmw-demo-themenu").find("input").map(function(){return this.checked?this.value:null}).get().join(b)).trigger("change")},clickMenu:function(d){var a=f(this);d=["current-menu-item","current-menu-parent","current-menu-ancestor"];var b=a.closest(".ui-dialog-content"),g=b.find(".cmw-demo-themenu"),a=a.find("span").not("."+d[0]).parentsUntil(g,"li"),c,e=function(){this.title=this.title+" "+c.replace(" "," & ").replace(/-/g," ")};g.find("."+
10
- d.join(",.")).removeClass(d.join(" ")).each(function(){this.title=this.title.replace(/\s.*$/,"")});for(g=0;g<a.length;g++)c=1===g?d.join(" "):d[0],a.eq(g).children("a").find("span").addClass(c).each(e),1<d.length&&d.shift();f(b.data().cmwTriggerChange).trigger("change");return!1},clickOutput:function(d){d=this.href.split("#")[1];f(this).closest(".ui-dialog-content").find(".cmw-demo-themenu a").eq(d).not(":has(.current-menu-item)").trigger("click");this.blur();return!1},clickShortcode:function(d){var a=
11
- document;a.body.createTextRange?(a=a.body.createTextRange(),a.moveToElementText(this),a.select()):window.getSelection&&(d=window.getSelection(),a=a.createRange(),a.selectNodeContents(this),d.removeAllRanges(),d.addRange(a))},createMenu:function(d,a){var b=d.data(),g=d.find(".cmw-demo-themenu"),c=a.find(".widget-custom-menu-wizard-selectmenu"),e=parseInt(c.val(),10),m=g.find("ul").eq(0),k=g.data(),h;m.length&&m.data("menuid")===e||(h=f("<ul>"+n.buildRecurse(a.find(".widget-custom-menu-wizard-childrenof optgroup").data().cmwItems||
12
- {})+"</ul>"),m.remove(),d.dialog("option","title",b.cmwTitlePrefix+c.find("option:selected").text()),k.maxLevel=0,g.append(h.data("menuid",e)).find("a").each(function(a){var b=f(this).parent("li").data().level;f(this).data("indx",a);b&&b>k.maxLevel&&(k.maxLevel=b)}))},init:function(d){var a=f(this);d=a.closest(".widget-custom-menu-wizard-onchange").data();var b=f("#"+d.cmwDialogId),a=a.closest("form");b.length||(b=f("<div/>",{id:d.cmwDialogId}).addClass("widget-custom-menu-wizard-dialog").data({cmwTriggerChange:d.cmwDialogTrigger,
13
- cmwTitlePrefix:d.cmwDialogTitle}).append(f("<div/>").addClass("cmw-demo-theoutput").html("<strong>"+d.cmwDialogOutput+'</strong> &hellip;<div class="cmw-demo-theoutput-wrap ui-corner-all"></div><div class="cmw-demo-fallback"><small>'+d.cmwDialogFallback+"</small></div>")).append(f("<div/>").addClass("cmw-demo-themenu").html("<small><em>"+d.cmwDialogPrompt+"</em></small>")).append(f("<div/>").addClass("cmw-demo-theshortcode").html('<code class="ui-corner-all">[custom_menu_wizard]</code>')),b.find(".cmw-demo-themenu").on("click",
14
- "a",n.clickMenu),b.find(".cmw-demo-themenu").on("change","input",n.changeMenu),b.find(".cmw-demo-theshortcode").on("click","code",n.clickShortcode),b.find(".cmw-demo-theoutput").on("click","a",n.clickOutput),b.dialog({autoOpen:!1,width:Math.min(0.8*f(window).width(),600),modal:!1}));b.dialog("isOpen")?b.dialog("close"):(n.createMenu(b,a),b.dialog("open"),f(d.cmwDialogTrigger).trigger("change"));this.blur();return!1},shortcode:function(d,a){var b={menu:[a.menu]},g,c,e;a.title&&(b.title=a.title);if(0<
15
- a.filter)switch(a.filter_item){case 0:b.children_of="current";break;case -1:b.children_of="parent";break;case -2:b.children_of="root";break;default:b.children_of=[a.filter_item]}0>a.filter&&(b.items=a._items);0<a.filter&&0>a.filter_item&&a.fallback_no_ancestor&&(b.fallback_parent=a.fallback_include_parent_siblings?"siblings":a.fallback_include_parent?"parent":[1]);0<a.filter&&!a.filter_item&&a.fallback_no_children&&(b.fallback_current=a.fallback_nc_include_parent_siblings?"siblings":a.fallback_nc_include_parent?
16
- "parent":[1]);1<a.start_level&&(b.start_level=[a.start_level]);0<a.depth&&(b.depth=[a.depth]);a.depth_rel_current&&0<a.depth&&(b.depth_rel_current=[1]);e=[];0<a.filter&&(a.include_parent_siblings?e.push("siblings"):a.include_parent&&e.push("parent"),a.include_ancestors&&e.push("ancestors"),e.length&&(b.include=e.join(" ")));e=[];0<a.filter&&a.title_from_parent&&e.push("parent");a.title_from_current&&e.push("current");e.length&&(b.title_from=e.join(" "));for(e in{flat_output:1,contains_current:1,ol_root:1,
17
- ol_sub:1})a[e]&&(b[e]=[1]);g={container:"div",container_id:"",container_class:"",menu_class:"menu-widget",widget_class:""};for(e in g)a[e]!==g[e]&&(b[e]=a[e]);g={wrap_link:"before",wrap_link_text:"link_before"};for(e in g)(c=a[g[e]].toString().match(/^<(\w+)/))&&c[1]&&(b[e]=c[1]);g=[];for(e in b)g.push(f.isArray(b[e])?e+"="+b[e][0]:e+'="'+b[e]+'"');n.getDialog(d).find("code").text("[custom_menu_wizard "+g.join(" ")+"]")},show:function(d,a,b){a=a||f(this).closest("form");b=b||n.getSettings(a);d=n.getDialog(a);
18
- var g=d.find(".cmw-demo-themenu"),c=g.find(".picked"),e="",m="",k=0,h=d.find(".cmw-demo-theoutput-wrap").empty(),p=["menu-widget"],q={};if(c.length&&h.length){0<b.filter&&b.title_from_parent&&(m=g.find(".the-parent").children("a").text()||"");!m&&b.title_from_current&&(m=g.find(".current-menu-item").text()||"");m||b.hide_title||(m=b.title||"");for(c.each(function(a){var c=f(this),d=c.data(),h=d.trace?d.trace.toString().split(","):[];a=d.itemid.toString();var g=1,c=c.children("a");if(!b.flat_output)for(q[a]=
19
- 1,a=0;a<h.length;a++)q[h[a]]&&g++;if(k)if(g>k)e+=b.ol_sub?"<ol>":"<ul>";else{for(;k>g;)--k,e+="</li>"+(b.ol_sub?"</ol>":"</ul>");e+="</li>"}e+='<li class="cmw-level-'+g+(d.included||"")+'"><a href="#'+c.data("indx")+'">'+c.text()+"</a>";k=g});1<k;)--k,e+="</li>"+(b.ol_sub?"</ol>":"</ul>");e+="</li>";p.push(d.find(".cmw-demo-fallback").data("fellback"));e=(b.ol_root?"<ol":"<ul")+' class="'+f.trim(p.join(" "))+'">'+e+(b.ol_root?"</ol>":"</ul>");h.html(e);m&&h.prepend("<h3>"+m+"</h3>");h.find("li").filter(function(){return!!f(this).children("ul, ol").length}).addClass("cmw-has-submenu")}n.shortcode(a,
20
- b)},update:function(d){var a=f(this).closest("form"),b=n.getDialog(a),g,c,e,m,k,h,p,q,r,l,s;if(b.length&&b.dialog("isOpen")){f(d.target).hasClass("widget-custom-menu-wizard-selectmenu")&&n.createMenu(b,a);c=n.getSettings(a);e=c.include_parent;m=c.include_parent_siblings;k=b.find(".cmw-demo-themenu");g=k.data().maxLevel;p=k.find(".current-menu-item").closest("li");q=p.length?p.data().level:-1;h=k.find("li").removeData("included").removeClass("the-parent");0>c.filter&&(h=h.filter(function(){var a=f(this).children("input"),
21
- b=-1<(c._items_sep+c._items+c._items_sep).indexOf(c._items_sep+a[0].value+c._items_sep);a.prop("checked",b);return b}),c._items||(h=f([])));h.length&&!p.length&&(c.contains_current||0<c.filter&&1>c.filter_item)&&(h=f([]));h.length&&0<c.filter&&(0<c.filter_item?l=h.filter("[data-itemid="+c.filter_item+"]"):c.filter_item?1===q&&c.fallback_no_ancestor?(l=p,e=e||c.fallback_include_parent,m=m||c.fallback_include_parent_siblings,r="cmw-fellback-to-current"):l=1===q?k:-1>c.filter_item?k.find(".current-menu-ancestor").eq(0).closest("li"):
22
- k.find(".current-menu-parent").closest("li"):p.find("li").length?l=p:c.fallback_no_children&&(l=k.find(".current-menu-parent").closest("li"),l.length||(l=k),e=e||c.fallback_nc_include_parent,m=m||c.fallback_nc_include_parent_siblings,r="cmw-fellback-to-parent"));if(h.length)if(c.filter)l&&l.length?(s=c.depth_rel_current&&c.depth&&p.length&&l.has(p[0]).length?q-1+c.depth:c.depth?Math.max((l.data().level||0)+c.depth,c.start_level+c.depth-1):9999,h=l.find("li").filter(function(){var a=f(this).data().level;
23
- return a>=c.start_level&&a<=s})):0<c.filter&&(h=f([]));else for(s=c.depth_rel_current&&c.depth&&p.length&&q>=c.start_level?q+c.depth-1:c.depth?c.start_level+c.depth-1:9999,q=1;q<=g;q++)if(q<c.start_level||q>s)h=h.not(".level-"+q);h.length&&0<c.filter&&l&&l.is("li")&&(m&&(h=h.add(l.siblings("li").data("included"," cmw-an-included-parent-sibling")),e=!0),c.include_ancestors&&(h=h.add(l.parentsUntil(k,"li").data("included"," cmw-an-included-ancestor")),e=!0),e&&(h=h.add(l.data("included"," cmw-the-included-parent"))));
24
- !h.length||!c.contains_current||p.length&&h.filter(p).length||(h=f([]));h.length&&l&&l.is("li")&&l.addClass("the-parent");r=h.length?r:"";b.find(".cmw-demo-fallback").data("fellback",r).toggleClass("cmw-demo-fellback",!!r);k.toggleClass("cmw-demo-filteritems",0>c.filter).find(".picked").not(h.addClass("picked")).removeClass("picked");n.show.call(this,d,a,c)}}};f(document).on("click",".widget-custom-menu-wizard-collapsible-fieldset",function(){var d=f(this),a=d.find("input").eq(0),b=!a.prop("checked");
25
- a.length&&(a.prop("checked",b),d.find("div").toggleClass("cmw-collapsed-fieldset",b),d.next("div")[b?"slideUp":"slideDown"]());this.blur();return!1}).on("change",".widget-custom-menu-wizard-listen",function(){var d=f(this.form),a=d.find(".widget-custom-menu-wizard-selectmenu"),b=d.find(".widget-custom-menu-wizard-showall").prop("checked"),g=d.find(".widget-custom-menu-wizard-showspecific").prop("checked"),d=d.find(".widget-custom-menu-wizard-childrenof"),c=parseInt(d.val(),10),e;a.is(this)&&(a=this.selectedIndex,
26
- d.find("optgroup").filter(function(){var b=f(this).data("cmwOptgroupIndex")===a;b||f(this).remove();return b}).length||(e=f("#"+d.attr("id")+"_ignore").find("optgroup").eq(a).clone(),e.length&&(0<c&&(c=0,d.val(c)),e.find("option[selected]").removeAttr("selected").prop("selected",!1),d.append(e))));f.each({"":b||g,"-ss":g,"not-rp":b||g||0<=c,"not-ci":b||g||!!c},function(a,b){f(".widget-custom-menu-wizard-disableif"+a,this.form).toggleClass("cmw-colour-grey",b).find("input,select").prop("disabled",
27
- b)})}).on("change",".widget-custom-menu-wizard-onchange",n.update).on("click",".widget-custom-menu-wizard-toggle-assist",n.init).on("click",".widget-action, .widget-control-close",function(){f(this).closest("div.widget").find(".widget-custom-menu-wizard-onchange").each(function(){var d=f("#"+f(this).data().cmwDialogId);d.length&&d.dialog("isOpen")&&d.dialog("close")})}).on("click",".widget-control-remove",function(d){f(this).closest("div.widget").find(".widget-custom-menu-wizard-onchange").each(function(){var a=
28
- f("#"+f(this).data().cmwDialogId);a.length&&(a.dialog("destroy"),a.remove())})})});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*Source: custom-menu-wizard.js
2
+ *Compiled: 2014-06-13, Google Closure Compiler...
3
+ *STATISTICS
4
+ * - originalSize: 61388
5
+ * - originalGzipSize: 15232
6
+ * - compressedSize: 22367
7
+ * - compressedGzipSize: 7096
8
+ */
9
+ jQuery(function(f){var z,y=function(a,b){return(b?".":"")+"widget-custom-menu-wizard-"+a},D=function(a){var b=f(this).data().cmwDialogVersion.replace(/\./g,""),b=/^\d+$/.test(b)?"v"+b:b;z[b]?z[b].update(a?a.target:this):z.update(a?a.target:this)},E=function(a,b,d){var c="tick"===d?b._items_sep:b._exclude_sep,g=[],l="tick"===d?"_items":"_exclude",l=b[l]?f.grep(b[l].split(c),function(b){return/\+$/.test(b)?(g.push(parseInt(b,10)),!b):!!b}):[],l=c+l.join(c)+c,g=c+g.join(c)+c;a=a.each(function(){var b=
10
+ f(this),a=b.data(),s=-1<g.indexOf(c+a.itemid+c),a=s||-1<l.indexOf(c+a.itemid+c);b.toggleClass("cmw-has-"+d,a).toggleClass("cmw-inherit-"+d,s)});return a.filter(".cmw-inherit-"+d).find("li").removeClass("cmw-has-"+d+" cmw-inherit-"+d).add(a.filter(".cmw-has-"+d))},B=function(a,b){return(a?a:b)[a?"find":"closest"](y("onchange",1))},H=function(a){var b={},d="2.1.0"===a.data("cmwDialogVersion"),c={items:1,exclude:1},g=f.extend({branch_start:1,exclude_level:1},c);f.each(a.find(":input").serializeArray(),
11
+ function(l,e){var k=e.name.replace(/.*\[([^\]]+)\]$/,"$1"),s=!g[k]&&/^-?\d+$/.test(e.value)?parseInt(e.value,10):e.value;b[k]=s;"hide_empty"===k?b[k]=!!a.data().cmwV36plus||s:c[k]&&(b["_"+k+"_sep"]=!s||/(^\d+\+?$|,)/.test(f.trim(s))?",":" ",s=f.map(s.split(/[,\s]+/),function(b){var a=!d&&/\+$/.test(b);b=b?parseInt(b,10):0;return isNaN(b)||1>b?null:a?b+"+":b}),b["_"+k]=s.join(b["_"+k+"_sep"]))});return b},I=function(a,b){var d=a.find(".cmw-demo-themenu-ul"),c=d.find(".picked"),g="",l=b.hide_title?
12
+ "":b.title,e=0,k=a.find(".cmw-demo-theoutput-wrap").empty(),s=["menu-widget"],u={};if(c.length&&k.length){(l=d.find(".title-from-item").children(".cmw-item").text()||"")||b.hide_title||(l=b.title||"");for(c.each(function(a){var c=f(this),d=c.data(),l=d.trace?d.trace.toString().split(","):[];a=d.itemid.toString();var k=1,c=c.children(".cmw-item");if(!b.flat_output)for(u[a]=1,a=0;a<l.length;a++)u[l[a]]&&k++;if(e)if(k>e)g+=b.ol_sub?"<ol>":"<ul>";else{for(;e>k;)--e,g+="</li>"+(b.ol_sub?"</ol>":"</ul>");
13
+ g+="</li>"}g+='<li class="cmw-level-'+k+(d.included||"")+'"><a href="#'+c.data("indx")+'">'+c.text()+"</a>";e=k});1<e;)--e,g+="</li>"+(b.ol_sub?"</ol>":"</ul>");g+="</li>";s.push(a.find(".cmw-demo-fallback").data("fellback"));g=(b.ol_root?"<ol":"<ul")+' class="'+f.trim(s.join(" "))+'">'+g+(b.ol_root?"</ol>":"</ul>");k.html(g);k.find("li").filter(function(){return!!f(this).children("ul, ol").length}).addClass("cmw-has-submenu")}k.length&&l&&(c.length||!b.hide_empty)&&k.prepend("<h3>"+l+"</h3>")},J=
14
+ function(a,b,d){var c;a.find("optgroup").filter(function(){var b=f(this).data().cmwOptgroupIndex===d;b||f(this).remove();return b}).length||(c=f("#"+a.attr("id")+"_ignore").find("optgroup").eq(d).clone(),c.length&&(0<b&&(b=0),a.append(c).val(b)));return b},K=function(a){a=f(this);var b=a.data(),d=!b.cmwAbsolute,c=a.closest(".ui-dialog"),g=c.find(".ui-dialog-content"),l=parseInt(c.css("top"),10)+(d?1:-1)*f(document).scrollTop();b.cmwAbsolute=d;b.cmwMaxHeight||(b.cmwMaxHeight=g.dialog("option","maxHeight"));
15
+ a.button("option","icons",{primary:d?"ui-icon-circle-close":"ui-icon-circle-check"});c.toggleClass("cmw-assistance-dialog-fixed",!d);g.dialog("option",{maxHeight:d?!d:b.cmwMaxHeight});c.css("top",l);return!1},L=function(a){var b=f(this);a=["current-menu-item","current-menu-parent","current-menu-ancestor"];var d=b.closest(".ui-dialog-content"),c=d.find(".cmw-demo-themenu-ul"),b=b.find("span").not("."+a[0]).parentsUntil(c,"li"),g,l=function(){this.title=this.title+" "+g.replace(" "," & ").replace(/-/g,
16
+ " ")};c.find("."+a.join(",.")).removeClass(a.join(" ")).each(function(){this.title=this.title.replace(/\s.*$/,"")});for(c=0;c<b.length;c++)g=1===c?a.join(" "):a[0],b.eq(c).children(".cmw-item").find("span").addClass(g).each(l),1<a.length&&a.shift();D.call(f(d.data().cmwOnchange).get(0));return!1},M=function(a){f(this).closest(".ui-dialog-content").find(".cmw-item").eq(this.href.split("#")[1]).not(":has(.current-menu-item)").trigger("click");this.blur();return!1},N=function(a){a=f(this);var b=a.hasClass("cmw-tick")?
17
+ "tick":"cross",d=a.parent();a=d.closest(".cmw-demo-themenu-ul");var c=d.hasClass("cmw-inherit-"+b),g=c||d.hasClass("cmw-has-"+b),l=g?f([]):d.parentsUntil(a,".cmw-inherit-"+b),e=f(d.closest(".ui-dialog-content").data().cmwOnchange).find("tick"===b?".cmw-setitems":".cmw-exclusions"),k;e.length&&(k=a.find(".cmw-has-"+b)[c||l.length?"not":"add"](d),!g||d.children("ul").length&&!a.parent().hasClass("cmw-version-210")?g&&!c&&(k=k.not(d.find(".cmw-has-"+b))):k=k.not(d),k=k.add(l.find("li").not(d)),k=k.map(function(){var a=
18
+ this===d[0]||this===l.get(0)?g:f(this).hasClass("cmw-inherit-"+b);return f(this).data().itemid+(a?"+":"")}).get().join(/(,|^\d+\+?$)/.test(f.trim(e.val())||",")?",":" "),e.val(k).trigger("change"));this.blur();return!1},O=function(a){var b={autoOpen:!1,width:Math.min(.9*f(window).width(),600),maxHeight:f(window).height()-40,modal:!1,containment:"window",create:function(){var b=f(this).closest(".ui-dialog");if(b.hasClass("cmw-assistance-dialog-fixed"))f("<button/>").addClass("cmw-dialog-fixed-absolute").button({label:a.cmwDialogFixed,
19
+ icons:{primary:"ui-icon-circle-check"}}).appendTo(b.find(".ui-dialog-titlebar")).on("click",K)},dialogClass:"cmw-assistance-dialog cmw-assistance-dialog-fixed"},d=f.map(["SetCurrent","Inclusions","Exclusions","Fallback"],function(b){return'<div class="cmw-demo-'+b.toLowerCase()+' cmw-demo-small">'+(a["cmwDialog"+b]||"")+"</div>"}),d=f("<div/>",{id:a.cmwDialogId}).addClass(y("dialog")).append(f("<div/>").addClass("cmw-demo-themenu cmw-version-"+a.cmwDialogVersion.replace(/\./g,"")).html('<em class="cmw-demo-small">'+
20
+ a.cmwDialogPrompt+"</em>")).append(f("<div/>").addClass("cmw-demo-theoutput").html('<em class="cmw-demo-small">'+a.cmwDialogOutput+'</em><em class="cmw-demo-plugin-version cmw-demo-small">v'+a.cmwDialogVersion+"</em>"+d.shift()+'<div class="cmw-demo-theoutput-wrap ui-corner-all"></div>'+d.join(""))).append(f("<div/>").addClass("cmw-demo-theshortcode").html('<code class="ui-corner-all"></code><div class="cmw-find-shortcodes"><a href="#" class="button-secondary '+y("find-shortcodes")+'" data-nonce="'+
21
+ (a.cmwDialogNonce||"")+'" title="'+a.cmwDialogShortcodes+'"><span class="spinner"></span><span>[&hellip;]</span></a></div><div class="cmw-demo-found-shortcodes cmw-demo-small ui-corner-all"></div>'));d.dialog(b);d.find(".cmw-demo-themenu").on("click",".cmw-tick,.cmw-cross",N).on("click",".cmw-item",L);d.find(".cmw-demo-theoutput").on("click","a",M);return d},F=function(a,b){a.dialog("option","title","CMW : "+(b.find(".cmw-widget-title").val()||a.data().cmwUntitled)+" ["+b.find(".cmw-select-menu").find("option:selected").text()+
22
+ "]")},G=function(a){var b=f(a.data().cmwOnchange),d=parseInt(b.find(".cmw-select-menu").val(),10),c=a.find(".cmw-demo-themenu-ul"),g=[],l=0,e="",k=function(b){return 1<b?'<a href="#" class="'+y("colexp")+' ui-icon ui-icon-triangle-1-e" style="left:-'+(2.4*(b-2)+2)+'em;">&nbsp;</a>':""};if(!c.length||c.data("menuid")!==d){for(b.find(".cmw-assist-items optgroup").find("option").each(function(b){for(var a=f(this),c=a.data().cmwLevel;c<g.length;)e+="</li></ul>"+k(g.length),g.pop();c>g.length?e+="<ul>":
23
+ (e+="</li>",g.pop());e+='<li class="level-'+c+'" data-itemid="'+this.value+'" data-level="'+c+'" data-trace="'+g.join(",")+'">';e+='<a href="#" class="cmw-cross ui-corner-all"></a>';e+='<a class="cmw-item ui-corner-all" href="#" data-indx="'+b+'"><span class="ui-corner-all" title="#'+this.value+'">'+f.trim(a.text());e+='</span></a><a href="#" class="cmw-tick ui-corner-all"></a>';g.push(this.value);c>l&&(l=c)});g.length;)e+="</li></ul>"+k(g.length),g.pop();c.remove();a.find(".cmw-demo-themenu").append(f(e).addClass("cmw-demo-themenu-ul").data({maxLevel:l,
24
+ menuid:d}))}};z={setLevels:function(a,b){if(b){var d=a.find(".cmw-branch-start"),c=d.val();d.find("optgroup").each(function(a){var c=f(this),d=c.find("option"),k=d.length,s=c.data();if(a)for(d.slice(b).remove();k<b;)++k,c.append(f("<option/>",{value:k}).text(k));else for(k=(k+1)/2,k>b&&d.each(function(a,c){Math.abs(c.value)>=b&&f(c).remove()});k<b;)c.prepend(f("<option/>",{value:-k}).text(-k+(1<k?"":" ("+s.cmwTextParent+")"))).append(f("<option/>",{value:"+"+k}).text("+"+k+(1<k?"":" ("+s.cmwTextChildren+
25
+ ")"))),++k});/^\d+$/.test(c)?c>b&&d.val("1"):""!==c&&Math.abs(c)>=b&&d.val("");a.find(".cmw-ancestors,.cmw-ancestor-siblings").each(function(){var a=f(this),c=(a.find("option").length+1)/2,d=a.val(),k=Math.max(2,b);Math.abs(d)>=b&&a.val(0>d?1-b:b-1);c!==k&&a.find("optgroup").each(function(b,a){var d=f(a),g=d.data().cmwTextForOption,e;c>k&&d.find("option").slice(k-c).remove();for(e=c;e<k;e++)d.append(f("<option/>",{value:b?e:-e}).text(g.replace("%d",b?e:-e)))})});a.find(".cmw-exclude-level").each(function(){var a=
26
+ f(this),c=a.find("option"),d=(c.length-1)/3,k=a.val(),s=c.eq(2).text(),u=c.eq(3).text();for(c.slice(3*b+1).remove();d<b;)++d,a.append(f("<option/>",{value:d}).text(d)).append(f("<option/>",{value:d+"-"}).text(s.replace(/\d+/,d))).append(f("<option/>",{value:d+"+"}).text(u.replace(/\d+/,d)));parseInt(k,10)>b&&a.val("")});a.find(".cmw-level").each(function(){var a=f(this),c=a.find("option").length;a.val()>b&&a.val(1);for(a.find("option").slice(b).remove();c<b;)++c,a.append(f("<option/>",{value:c}).text(c))});
27
+ a.find(".cmw-depth,.cmw-fallback-depth").each(function(){var a=f(this),c=a.find("option").length,d=" "+a.data().cmwTextLevels;a.val()>b&&a.val(0);for(a.find("option").slice(b+1).remove();c<=b;)a.append(f("<option/>",{value:c}).text(c+d)),++c})}},setFields:function(a,b){var d=b.find(".cmw-bybranch"),c=b.find(".cmw-byitems").prop("checked"),g=c||!d.prop("checked"),l=b.find(".cmw-assist-items"),e=parseInt(l.val(),10);a.hasClass("cmw-select-menu")?(e=J(l,e,a[0].selectedIndex),this.setLevels(b,(l.find("optgroup").data()||
28
+ {}).cmwMaxLevel)):a.hasClass("cmw-level")?(g=!0,c=!g,b.find(".cmw-bylevel").prop("checked",g)):a.is(l)?(g=c=!1,d.prop("checked",!g)):a.hasClass("cmw-setitems")?(g=c=!0,b.find(".cmw-byitems").prop("checked",c)):a.hasClass("cmw-ancestors")&&"0"===a.val()?b.find(".cmw-ancestor-siblings").val("0"):a.hasClass("cmw-ancestor-siblings")&&"0"!==a.val()&&"0"===b.find(".cmw-ancestors").val()&&b.find(".cmw-ancestors").val(a.val());f.each({"-ss":c,"-ud":1>b.find(".cmw-depth").val(),"not-br":g,"not-br-ci":g||!!e,
29
+ "not-fb-pc":!{parent:1,current:1}[b.find(".cmw-fallback").val()]},function(a,c){b.find(".cmw-disableif"+a).toggleClass("cmw-colour-grey",c).find("input,select").prop("disabled",c)})},shortcode:function(a){var b={menu:a.menu},d="branch"===a.filter,c="items"===a.filter,g;a.title&&!a.hide_title&&(b.title=[a.title]);!d&&!c&&1<a.level&&(b.level=a.level);d&&(b.branch=a.branch||"current",a.branch_start&&(b.start_at=[a.branch_start]),"level"===a.start_mode&&(b.start_mode="level"));c&&(b.items=[a._items]);
30
+ 0<a.depth&&(b.depth=a.depth);a.depth_rel_current&&0<a.depth&&(b.depth_rel_current=1);d&&!a.branch&&a.fallback&&(b.fallback=[a.fallback],"quit"!==a.fallback&&(a.fallback_siblings&&b.fallback.push("+siblings"),a.fallback_depth&&b.fallback.push(a.fallback_depth)));d&&a.ancestors&&(b.ancestors=a.ancestors,a.ancestor_siblings&&(b.ancestor_siblings=a.ancestor_siblings));a._exclude&&(b.exclude=[a._exclude]);a.exclude_level&&(b.exclude_level=[a.exclude_level]);c=[];a.title_from_current?c.push("current"):
31
+ a.title_from_current_root&&c.push("current-root");d&&a.title_from_branch?c.push("branch"):d&&a.title_from_branch_root&&c.push("branch-root");c.length&&(b.title_from=c);for(c in{allow_all_root:1,siblings:1,include_root:1,flat_output:1,ol_root:1,ol_sub:1})a[c]&&(b[c]=1);d={contains_current:"",container:"div",container_id:"",container_class:"",menu_class:"menu-widget",widget_class:""};for(c in d)a[c]!==d[c]&&(b[c]=[a[c]]);d={wrap_link:"before",wrap_link_text:"link_before"};for(c in d)(g=a[d[c]].toString().match(/^<(\w+)/))&&
32
+ g[1]&&(b[c]=[g[1]]);d=[];for(c in b)d.push(f.isArray(b[c])?c+'="'+b[c].join(",")+'"':c+"="+b[c]);return"[cmwizard "+d.join(" ")+"]"},update:function(a){var b=f(a);a=B(0,b);var d=f("#"+a.data().cmwDialogId),c=-1,g=9999,l=0,e=0,k="",s,u,r,v,x,m,t,h,y,A,z,w,C,q,p,n;b.hasClass("cmw-listen")&&this.setFields(b,a);m=H(a);if(d.length&&d.dialog("isOpen")){d.dialog("moveToTop");b.hasClass("cmw-select-menu")&&G(d);b="branch"===m.filter;u="items"===m.filter;s=!b&&!u;v=b&&!m.branch;t=d.find(".cmw-demo-themenu-ul");
33
+ x=t.data().maxLevel;w=t.find(".current-menu-item").closest("li");C=w.length?w.data().level:-1;h=t.find("li").removeData("included").removeClass("title-from-item");A=m.depth;z=m.depth_rel_current;y=E(h,m,"cross");u&&(h=E(h,m,"tick"));h.length&&!w.length&&(m.contains_current||v)&&(h=f([]));b&&h.length&&(q=v?w:h.filter("[data-itemid="+m.branch+"]"),q.length?(c=q.data().level||0,h=q.add(q.find("li")),r=q):h=f([]));if(s&&h.length&&1<m.level){p=1;for(n=[];p<m.level;p++)n.push(".level-"+p);h=h.not(n.join(","))}"primary"===
34
+ m.contains_current&&h.length&&!w.is(h)&&(h=f([]));if(s&&h.length&&A&&(p=z&&C>=m.level?C:m.level,p+=A,p<=x)){for(n=[];p<=x;p++)n.push(".level-"+p);h=h.not(n.join(","))}if(b&&h.length&&(n=parseInt(m.branch_start,10),n=isNaN(n)||!n?c:m.branch_start.match(/^(\+|-)/)?Math.max(1,c+n):n,v&&m.fallback&&!w.find("li").length&&(k="cmw-fellback-to-"+m.fallback,"quit"===m.fallback?n=x+1:(n="current"===m.fallback||2>c?c:c-1,m.fallback_depth&&(A=m.fallback_depth,z=1))),n>x?h=f([]):(n<c&&(q=q.parentsUntil(t,"li.level-"+
35
+ n)),"level"===m.start_mode&&n<=c&&(1<n||m.allow_all_root)?h=q.parent().find("li"):n<c&&(h=q.add(q.find("li"))),k&&m.fallback_siblings&&h.length&&(1<n||m.allow_all_root)&&(h=h.add(q.siblings("li.level-"+n)))),h.length)){c=n;q=9999;A&&(q=z&&C>=c&&h.filter(w).length?C:c,q+=A,g=q-1);p=1;for(n=[];p<=x;p++)p>=c&&p<q&&n.push(".level-"+p);h=h.filter(n.join(","))}"secondary"===m.contains_current&&h.length&&!w.is(h)&&(h=f([]));if(b&&h.length){n=r.data().level;if(m.ancestors&&(r.is(h)||n>g)){p=m.ancestors;0>
36
+ p&&(p=Math.max(1,n+p));c=m.ancestor_siblings;0>c&&(c=Math.max(1,n+c));n=[];for(q=[];p<=x;p++)p<=g&&(n.push(".level-"+p),0<c&&p>=c&&q.push(".level-"+p));p=h.length;n=r.parentsUntil(t,n.join(","));h=h.add(n.not(h).data("included"," cmw-an-included-ancestor"));q.length&&(h=h.add(n.filter(q.join(",")).siblings("li").not(h).data("included"," cmw-an-included-ancestor-sibling")));l+=h.length-p}m.siblings&&r.is(h)&&(n=h.length,h=h.add(r.siblings("li").data("included"," cmw-an-included-sibling")),l+=h.length-
37
+ n)}h.length&&m.include_root&&(n=h.length,h=h.add(t.find(".level-1")),l+=h.length-n);"inclusions"===m.contains_current&&h.length&&!w.is(h)&&(h=f([]));h.length&&y.length&&(n=h.length,h=h.not(y),e+=n-h.length);if(h.length&&m.exclude_level&&(q=(q=m.exclude_level.match(/^(\d+)(\+|-)?$/))?[parseInt(q[1],10),q[2]||""]:[],0<q[0])){n=[];for(p=1;p<=x;p++)(p===q[0]||"-"===q[1]&&p<q[0]||"+"===q[1]&&p>q[0])&&n.push(".level-"+p);n.length&&(q=h.length,h=h.not(n.join(",")),e+=q-h.length)}"output"===m.contains_current&&
38
+ h.length&&!w.is(h)&&(h=f([]));m.title_from_current&&w.length?w.addClass("title-from-item"):m.title_from_current_root&&w.length?w.closest(".level-1").addClass("title-from-item"):b&&r&&(m.title_from_branch?r.addClass("title-from-item"):m.title_from_branch_root&&r.closest(".level-1").addClass("title-from-item"));d.find(".cmw-demo-fallback").data("fellback",k).toggleClass("updated",!!k);d.find(".cmw-demo-setcurrent").toggleClass("error",!w.length&&(m.contains_current||v));p={inclusions:l,exclusions:e};
39
+ for(n in p)q=d.find(".cmw-demo-"+n),q.text(q.text().replace(/\d+$/,p[n])).toggleClass("updated",0<p[n]);t.toggleClass("cmw-demo-filteritems",u).find(".picked").not(h.addClass("picked")).removeClass("picked");F(d,a);I(d,m)}a.add(d).find("code").text(this.shortcode(m))},v210:{setLevels:function(a,b){var d=a.find(".cmw-start-level"),c=d.val(),g=d.find("option").length,l;c>b&&d.val(1);for(d.find("option").slice(b).remove();g<b;)++g,d.append(f("<option/>",{value:g}).text(g));d=a.find(".cmw-depth");c=d.val();
40
+ g=d.find("option").length;l=" "+d.data().cmwTextLevels;c>b&&d.val(0);for(d.find("option").slice(b+1).remove();g<=b;)d.append(f("<option/>",{value:g}).text(g+l)),++g},setFields:function(a,b){var d=b.find(".cmw-showall").prop("checked"),c=b.find(".cmw-showspecific").prop("checked"),g=b.find(".cmw-assist-items"),l=parseInt(g.val(),10);a.hasClass("cmw-select-menu")&&(l=J(g,l,a[0].selectedIndex),this.setLevels(b,(g.find("optgroup").data()||{}).cmwMaxLevel));f.each({"":d||c,"-ss":c,"not-rp":d||c||0<=l,
41
+ "not-ci":d||c||!!l},function(a,c){b.find(".cmw-disableif"+a).toggleClass("cmw-colour-grey",c).find("input,select").prop("disabled",c)})},shortcode:function(a){var b={menu:a.menu},d=0<a.filter,c=!!a.filter&&!d,g;a.title&&(b.title=[a.title]);if(d)switch(a.filter_item){case 0:b.children_of=["current"];break;case -1:b.children_of=["parent"];break;case -2:b.children_of=["root"];break;default:b.children_of=a.filter_item}c&&(b.items=[a._items]);d&&0>a.filter_item&&a.fallback_no_ancestor&&(b.fallback_parent=
42
+ a.fallback_include_parent_siblings?["siblings"]:a.fallback_include_parent?["parent"]:1);d&&!a.filter_item&&a.fallback_no_children&&(b.fallback_current=a.fallback_nc_include_parent_siblings?["siblings"]:a.fallback_nc_include_parent?["parent"]:1);1<a.start_level&&(b.start_level=a.start_level);0<a.depth&&(b.depth=a.depth);a.depth_rel_current&&0<a.depth&&(b.depth_rel_current=1);c=[];d&&(a.include_parent_siblings?c.push("siblings"):a.include_parent&&c.push("parent"),a.include_ancestors&&c.push("ancestors"),
43
+ c.length&&(b.include=c));c=[];d&&a.title_from_parent&&c.push("parent");a.title_from_current&&c.push("current");c.length&&(b.title_from=c);for(c in{flat_output:1,contains_current:1,ol_root:1,ol_sub:1})a[c]&&(b[c]=1);d={container:"div",container_id:"",container_class:"",menu_class:"menu-widget",widget_class:""};for(c in d)a[c]!==d[c]&&(b[c]=[a[c]]);d={wrap_link:"before",wrap_link_text:"link_before"};for(c in d)(g=a[d[c]].toString().match(/^<(\w+)/))&&g[1]&&(b[c]=[g[1]]);d=[];for(c in b)d.push(f.isArray(b[c])?
44
+ c+'="'+b[c].join(" ")+'"':c+"="+b[c]);return"[custom_menu_wizard "+d.join(" ")+"]"},update:function(a){var b=f(a);a=B(0,b);var d=f("#"+a.data().cmwDialogId),c,g,l,e,k,s,u,r,v,x,m,t,h;b.hasClass("cmw-listen")&&this.setFields(b,a);if(d.length&&d.dialog("isOpen")){d.dialog("moveToTop");b.hasClass("cmw-select-menu")&&G(d);e=H(a);c=!e.filter;b=0<e.filter;g=!c&&!b;k=e.include_parent;s=e.include_parent_siblings;u=d.find(".cmw-demo-themenu-ul");l=u.data().maxLevel;v=u.find(".current-menu-item").closest("li");
45
+ x=v.length?v.data().level:-1;r=u.find("li").removeData("included").removeClass("title-from-item");g&&(r=E(r,e,"tick"));r.length&&!v.length&&(e.contains_current||b&&1>e.filter_item)&&(r=f([]));r.length&&b&&(0<e.filter_item?t=r.filter("[data-itemid="+e.filter_item+"]"):e.filter_item?1===x&&e.fallback_no_ancestor?(t=v,k=k||e.fallback_include_parent,s=s||e.fallback_include_parent_siblings,m="cmw-fellback-to-current"):t=1===x?u:-1>e.filter_item?u.find(".current-menu-ancestor").eq(0).closest("li"):u.find(".current-menu-parent").closest("li"):
46
+ v.find("li").length?t=v:e.fallback_no_children&&(t=u.find(".current-menu-parent").closest("li"),t.length||(t=u),k=k||e.fallback_nc_include_parent,s=s||e.fallback_nc_include_parent_siblings,m="cmw-fellback-to-parent"));if(r.length)if(c)for(h=e.depth_rel_current&&e.depth&&v.length&&x>=e.start_level?x+e.depth-1:e.depth?e.start_level+e.depth-1:9999,c=1;c<=l;c++){if(c<e.start_level||c>h)r=r.not(".level-"+c)}else t&&t.length?(h=e.depth_rel_current&&e.depth&&v.length&&t.has(v[0]).length?x-1+e.depth:e.depth?
47
+ Math.max((t.data().level||0)+e.depth,e.start_level+e.depth-1):9999,r=t.find("li").filter(function(){var a=f(this).data().level;return a>=e.start_level&&a<=h})):b&&(r=f([]));r.length&&b&&t&&t.is("li")&&(s&&(r=r.add(t.siblings("li").data("included"," cmw-an-included-parent-sibling")),k=!0),e.include_ancestors&&(r=r.add(t.parentsUntil(u,"li").data("included"," cmw-an-included-ancestor")),k=!0),k&&(r=r.add(t.data("included"," cmw-the-included-parent"))));!r.length||!e.contains_current||v.length&&r.filter(v).length||
48
+ (r=f([]));e.title_from_parent&&r.length&&t&&t.is("li")?t.addClass("title-from-item"):e.title_from_current&&r.length&&v.addClass("title-from-item");m=r.length?m:"";d.find(".cmw-demo-fallback").data("fellback",m).toggleClass("updated",!!m);d.find(".cmw-demo-setcurrent").toggleClass("error",!v.length&&(e.contains_current||b&&1>e.filter_item));u.toggleClass("cmw-demo-filteritems",g).find(".picked").not(r.addClass("picked")).removeClass("picked");F(d,a);I(d,e);d.find("code").text(this.shortcode(e))}}}};
49
+ f(document).on("change",y("onchange",1),D).on("click",y("assist",1),function(a){a=f(this);a=B(0,a);var b=a.data(),d=f("#"+b.cmwDialogId);d.length||(d=O(b).data({cmwOnchange:"#"+a.attr("id"),cmwUntitled:"["+b.cmwDialogUntitled+"]"}));d.dialog("isOpen")?d.dialog("close"):(G(d),F(d,a),d.dialog("open"),D.call(a[0]));this.blur();return!1}).on("click",y("fieldset",1),function(a){a=f(this);var b=a.next(".cmw-fieldset-state"),d=!b.prop("checked");b.length&&(b.prop("checked",d),a.toggleClass("cmw-collapsed-fieldset",
50
+ d),b.next("div")[d?"slideUp":"slideDown"]());this.blur();return!1}).on("click",".widget-action,.widget-control-close",function(a){a=f(this).closest("div.widget");var b=a.parent();b.hasClass("customize-control-widget_form")&&!b.hasClass("expanded")||B(a).each(function(){var a=f("#"+f(this).data().cmwDialogId);a.length&&a.dialog("isOpen")&&a.dialog("close")})}).on("click",".widget-control-remove",function(a){B(f(this).closest("div.widget")).each(function(){var a=f("#"+f(this).data().cmwDialogId);a.length&&
51
+ (a.dialog("destroy"),a.remove())})}).on("click",y("colexp",1),function(a){a=/1-e/.test(this.className)?"slideUp":"slideDown";f(this).toggleClass("ui-icon-triangle-1-s ui-icon-triangle-1-e").prev("ul")[a]();return!1}).on("click",y("find-shortcodes",1),function(a){a=f(this);var b=a.parent().parent();ajaxurl&&!b.hasClass("cmw-ajax-fetching")&&(b.hasClass("cmw-ajax-showing")?b.removeClass("cmw-ajax-showing"):(b.addClass("cmw-ajax-fetching"),f.post(ajaxurl,{action:"cmw-find-shortcodes",_wpnonce:a.data().nonce}).done(function(a){a&&
52
+ "0"!==a&&(b.find(".cmw-demo-found-shortcodes").html(f(a).find("response_data").text()),b.addClass("cmw-ajax-showing"))}).always(function(a){b.removeClass("cmw-ajax-fetching")})));this.blur();return!1}).on("click",y("legacy-close",1),function(){f(this).parent().remove();return!1})});
custom-menu-wizard.php CHANGED
@@ -3,13 +3,41 @@
3
  * Plugin Name: Custom Menu Wizard
4
  * Plugin URI: http://wordpress.org/plugins/custom-menu-wizard/
5
  * Description: Show any part of a custom menu in a Widget, or in content using a Shortcode. Customise the output with extra classes or html; filter by current menu item or a specific item; set a depth, show the parent(s), change the list style, etc. Use the included emulator to assist with the filter settings.
6
- * Version: 2.0.6
7
  * Author: Roger Barrett
8
  * Author URI: http://www.wizzud.com/
9
  * License: GPL2+
10
  */
11
-
12
  /*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  * v2.0.6 change log:
14
  * - modified determination of current item to cope better with multiple occurences (still first-found, but within prioritised groups)
15
  * - 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)
@@ -72,387 +100,448 @@
72
  * - moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript
73
  */
74
 
75
- $Custom_Menu_Wizard_Widget_Version = '2.0.6';
76
-
77
- /**
78
- * registers the widget and adds the shortcode
79
- */
80
- function custom_menu_wizard_register_widget() {
81
- register_widget('Custom_Menu_Wizard_Widget');
82
- add_shortcode('custom_menu_wizard', 'custom_menu_wizard_widget_shortcode');
83
- }
84
- add_action('widgets_init', 'custom_menu_wizard_register_widget');
85
 
86
- /**
87
- * enqueues script file for the widget admin
88
- */
89
- function custom_menu_wizard_widget_admin_script(){
90
- global $wp_scripts, $Custom_Menu_Wizard_Widget_Version;
91
- wp_enqueue_style('custom-menu-wizard-plugin-styles', plugins_url('/custom-menu-wizard.css', __FILE__), array(), $Custom_Menu_Wizard_Widget_Version);
92
- wp_enqueue_script('custom-menu-wizard-plugin-script', plugins_url('/custom-menu-wizard.min.js', __FILE__), array('jquery-ui-dialog'), $Custom_Menu_Wizard_Widget_Version);
93
- if( !wp_style_is( 'jquery-ui', 'registered' ) ) {
94
- $jquery_ui_version = isset( $wp_scripts->registered['jquery-ui-core']->ver ) ? $wp_scripts->registered['jquery-ui-core']->ver : '1.9.2';
95
- wp_register_style( 'jquery-ui', 'http://ajax.googleapis.com/ajax/libs/jqueryui/' . $jquery_ui_version . '/themes/smoothness/jquery-ui.css' );
96
- }
97
- wp_enqueue_style( 'jquery-ui' );
98
- }
99
- add_action('admin_print_scripts-widgets.php', 'custom_menu_wizard_widget_admin_script');
100
 
101
- /**
102
- * request read changelog before updating
103
- */
104
- function custom_menu_wizard_update_message($plugin_data, $r){
105
 
106
- $url = 'http://wordpress.org/plugins/' . $r->slug. '/changelog/';
107
- $style = implode( ';', array(
108
- '-webkit-box-sizing:border-box',
109
- '-moz-box-sizing:border-box',
110
- 'box-sizing:border-box',
111
- 'background-color:#D54E21',
112
- 'border-radius:2px',
113
- 'color:#FFFFFF',
114
- 'display:inline-block',
115
- 'margin:0',
116
- 'max-width:100%',
117
- 'overflow:hidden',
118
- 'padding:0 0.5em',
119
- 'text-overflow:ellipsis',
120
- 'text-shadow:0 1px 0 rgba(0, 0, 0, 0.5)',
121
- 'vertical-align:text-bottom',
122
- 'white-space:nowrap'
123
- ) ) . ';';
 
 
 
124
 
125
- ?>
126
- <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>
127
- <?php
 
 
 
 
128
 
129
- }
130
- /**
131
- * if the plugin has an update...
132
- */
133
- function custom_menu_wizard_admin_menu(){
134
- add_action('in_plugin_update_message-' . plugin_basename(__FILE__), 'custom_menu_wizard_update_message', 10, 2);
135
- }
136
- add_action('admin_menu', 'custom_menu_wizard_admin_menu');
137
 
 
 
 
 
138
 
139
- /*
140
- * Custom Menu Wizard Walker class
141
- * NB: Walker_Nav_Menu class is in wp-includes/nav-menu-template.php, and is itself an
142
- * extension of the Walker class (wp-includes/class-wp-walker.php)
143
- */
144
- class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
 
 
145
 
146
- /**
147
- * opens a sub-level with a UL or OL start-tag
148
- *
149
- * @param string $output Passed by reference. Used to append additional content.
150
- * @param int $depth Depth of page. Used for padding.
151
- */
152
- function start_lvl( &$output, $depth = 0, $args = array() ) {
153
- $indent = str_repeat("\t", $depth);
154
- $listtag = empty( $args->_custom_menu_wizard['ol_sub'] ) ? 'ul' : 'ol';
155
- $output .= "\n$indent<$listtag class=\"sub-menu\">\n";
156
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
- /**
159
- * closes a sub-level with a UL or OL end-tag
160
- *
161
- * @param string $output Passed by reference. Used to append additional content.
162
- * @param int $depth Depth of page. Used for padding.
163
- */
164
- function end_lvl( &$output, $depth = 0, $args = array() ) {
165
- $indent = str_repeat("\t", $depth);
166
- $listtag = empty( $args->_custom_menu_wizard['ol_sub'] ) ? 'ul' : 'ol';
167
- $output .= "$indent</$listtag>\n";
168
- }
169
 
170
- /**
171
- * pre-filters elements then calls parent::walk()
172
- *
173
- * @filters : custom_menu_wizard_walker_items array of filtered menu elements; array of args
174
- *
175
- * @param array $elements Menu items
176
- * @param integer $max_depth
177
- * @return string
178
- */
179
- function walk($elements, $max_depth){
180
 
181
- $args = array_slice(func_get_args(), 2);
182
- $args = $args[0];
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
- if( $max_depth >= -1 && !empty( $elements ) && isset($args->_custom_menu_wizard) ){
 
 
 
 
 
 
 
185
 
186
- $cmw =& $args->_custom_menu_wizard;
187
- //in $cmw (array) :
188
- // filter : 0 = show all; 1 = kids of (current [root|parent] item or specific item); -1 = specific items (v2.0.0)
189
- // filter_item : 0 = current item, -1 = parent of current (v1.1.0), -2 = root ancestor of current (v1.1.0); else a menu item id
190
- // flat_output : true = equivalent of $max_depth == -1
191
- // include_parent : true = include the filter_item menu item
192
- // include_parent_siblings : true = include the siblings (& parent) of the filter_item menu item
193
- // include_ancestors : true = include the filter_item menu item plus all it's ancestors
194
- // title_from_parent : true = widget wants parent's title
195
- // title_from_current : true = widget wants current item's title (v1.2.0)
196
- // start_level : integer, 1+
197
- // depth : integer, replacement for max_depth and also applied to 'flat' output
198
- // depth_rel_current : true = changes depth calc from "relative to first filtered item found" to "relative to current item's level" (if current item is found below level/branch) (v2.0.0)
199
- // fallback_no_ancestor : true = if looking for an ancestor (root or parent) of a top-level current item, fallback to current item (v1.1.0)
200
- // fallback_include_parent : true = if fallback_no_ancestor comes into play then force include_parent to true (v1.1.0)
201
- // fallback_include_parent_siblings : true = if fallback_no_ancestor comes into play then force include_parent_siblings to true (v1.1.0)
202
- // fallback_no_children : true = if looking for a current item, and that item turns out to have no children, fallback to current parent (v1.2.0)
203
- // fallback_nc_include_parent : true = if fallback_no_children comes into play then force include_parent to true (v1.2.0)
204
- // fallback_nc_include_parent_siblings : true = if fallback_no_children comes into play then force include_parent_siblings to true (v1.2.0)
205
- // contains_current : true = the output - both Filtered and any Included items - must contain the current menu item (v2.0.0)
206
- // items : comma-or-space delimited list of item ids
207
- //
208
- // _walker (array) : for anything that only the walker can determine and that needs to be communicated back to the widget instance
209
- //
210
- //$elements is an array of objects, indexed by position within the menu (menu_order),
211
- //starting at 1 and incrementing sequentially regardless of parentage (ie. first item is [1],
212
- //second item is [2] whether it's at root or subordinate to first item)
213
- $cmw['_walker']['fellback'] = false;
214
 
215
- $find_kids_of = $cmw['filter'] > 0;
216
- $find_specific_items = $cmw['filter'] < 0; //v2.0.0 //v2.0.1:bug fixed (changed < 1 to < 0)
217
- $find_current_item = $find_kids_of && empty( $cmw['filter_item'] );
218
- $find_current_parent = $find_kids_of && $cmw['filter_item'] == -1; //v1.1.0
219
- $find_current_root = $find_kids_of && $cmw['filter_item'] == -2; //v1.1.0
220
- $depth_rel_current = $cmw['depth_rel_current'] && $cmw['depth'] > 0; //v2.0.0
221
- //these could change depending on whether a fallback comes into play (v1.1.0)
222
- $include_parent = $cmw['include_parent'] || $cmw['include_ancestors'];
223
- $include_parent_siblings = $cmw['include_parent_siblings'];
224
 
225
- $id_field = $this->db_fields['id']; //eg. = 'db_id'
226
- $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
 
 
227
 
228
- $structure = array(0 => array(
229
- 'level' => 0,
230
- 'ancestors' => array(),
231
- 'kids' => array(),
232
- 'element' => -1,
233
- 'keepCount' => 0
234
- ));
235
- $levels = array(
236
- array() //for the artificial level-0
237
- );
238
- $allLevels = 9999;
239
- $startWithKidsOf = -1;
240
- $currentItem = array();
241
 
242
- foreach( $elements as $i=>$item ){
243
- $itemID = $item->$id_field;
244
- $parentID = empty( $item->$parent_field ) ? 0 : $item->$parent_field;
 
 
 
 
 
245
 
 
246
 
247
- //if $structure[] hasn't been set then it's an orphan; in order to keep orphans, max_depth must be 0 (ie. unlimited)
248
- //note that if a child is an orphan then all descendants of that child are also considered to be orphans!
249
- //also note that orphans (in the original menu) are ignored by this widget!
250
- if( isset( $structure[ $parentID ] ) ){
251
- //keep track of current item (as a structure key)...
252
- //v2.0.6 change...
253
- if( $item->current ){
254
- //should(!) never get either parent and/or ancestor on an item marked as "current", but unfortunately it does occur (grrr!).
255
- //so this has to cope, not only with more than 1 "current" item, but also with "current" items that are incorrectly marked
256
- //as (their own?!) parent and/or ancestor.
257
- //we're going to look for correctly (solely) marked "current" items and take the first one found;
258
- //failing that, look for a "current" item that is also marked as parent, and, again, use the first one found;
259
- //failing that, look for a "current" item that is also marked as an ancestor, and, again, use the first one found.
260
- //
261
- //array keys, priority order : just current -> parent, not ancestor -> parent and ancestor -> ancestor
262
- // first found...
263
- // - a001 : just current
264
- // - b001 : current & parent (not ancestor)
265
- // - c001 : current & parent & ancestor
266
- // - d001 : current & ancestor (not parent)
267
- // next found...
268
- // - a002 : just current
269
- // - b002 : current & parent (not ancestor)
270
- // - c002 : current & parent & ancestor
271
- // - d002 : current & ancestor (not parent)
272
- // etc
273
- //example :
274
- // - 1st found : current & ancestor = d001
275
- // - 2nd found : current & parent & ancestor = c002
276
- // - 3rd found : just current = a003
277
- // - 4th found : just current & parent = b004
278
- // - 5th found : just current = a005
279
- //reverse sort keys alphabetically and a003 comes out on bottom, so third found gets used! (copes with 999 "current" items; should be enough!)
280
- $j = $item->current_item_ancestor ? ( $item->current_item_parent ? 'c' : 'd') : ( $item->current_item_parent ? 'b' : 'a' );
281
- $currentItem[ $j . sprintf( '%03d' , count( $currentItem ) + 1 ) ] = $itemID;
282
- }
283
 
284
- //this level...
285
- $thisLevel = $structure[ $parentID ]['level'] + 1;
286
- if( empty( $levels[ $thisLevel ] ) ){
287
- $levels[ $thisLevel ] = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  }
289
- $levels[ $thisLevel ][] = $itemID;
290
-
291
- $structure[ $itemID ] = array(
292
- //level within structure...
293
- 'level' => $thisLevel,
294
- //ancestors (from the artificial level-0, right down to parent, inclusive) within structure...
295
- 'ancestors' => $structure[ $parentID ]['ancestors'],
296
- //kids within structure, ie array of itemID's...
297
- 'kids' => array(),
298
- //item within elements...
299
- 'element' => $i,
300
- //assume no matches...
301
- 'keep' => false
302
- );
303
- $structure[ $itemID ]['ancestors'][] = $parentID;
304
- $structure[ $parentID ]['kids'][] = $itemID;
305
  }
306
- } //end foreach
307
 
308
- if( empty( $currentItem ) ){
309
- $currentItem = false;
310
- }else{
311
- krsort( $currentItem );
312
- $currentItem = array_pop( $currentItem );
313
  }
314
 
315
- //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...
316
- $continue = true;
317
- if( empty( $currentItem ) && ( $find_current_item || $find_current_parent || $find_current_root || $cmw['contains_current'] ) ){
318
- $continue = false;
319
- }elseif( $find_specific_items && empty( $cmw['items'] ) ){
320
- $continue = false;
321
  }
322
 
323
- // IMPORTANT : as of v2.0.0, start level has been rationalised so that it acts the same across all filters (except for specific items!).
324
- // Previously ...
325
- // start level for a show-all filter literally started at the specified level and reported all levels until depth was reached.
326
- // however, start level for a kids-of filter specified the level that the *immediate* kids of the selected filter had to be at
327
- // or below. That was consistent for a specific item, current-item and current-parent filter, but for a current-root filter what
328
- // it actually did was test the current item against the start level, not the current item's root ancestor! Inconsistent!
329
- // But regardless of the current-root filter's use of start level, there was still the inconsistency between show-all and
330
- // kids-of usage.
331
- // Now (as of v2.0.0) ...
332
- // start level and depth have been changed to definitively be secondary filters to the show-all & kids-of primary filter.
333
- // The primary filter - show-all, or a kids-of - will provide the initial set of items, and the secondary - start level & depth -
334
- // will further refine that set, with start level being an absolute, and depth still being relative to the first item found.
335
- // The sole exception to this is when Depth Relative to Current Menu Item is set, which modifies the calculation of depth (only)
336
- // 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
337
- // below start level).
338
- // 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,
339
- // would fail to return any items because the immediate kids (at level 3) were outside the start level. Now, the returned items
340
- // will begin with the grand-kids (ie. those at level 4).
341
- // Note that neither start level nor depth are applicable to a specific items filter (also new at v2.0.0)!
342
 
343
- //the kids-of filters...
344
- if( $continue && $find_kids_of ){
345
- //specific item...
346
- if( $cmw['filter_item'] > 0 && isset( $structure[ $cmw['filter_item'] ] ) && !empty( $structure[ $cmw['filter_item'] ]['kids'] ) ){
347
- $startWithKidsOf = $cmw['filter_item'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  }
349
- if( $find_current_item ){
350
- if( !empty( $structure[ $currentItem ]['kids'] ) ){
351
- $startWithKidsOf = $currentItem;
352
- }elseif( $cmw['fallback_no_children'] ){
353
- //no kids, and fallback to current parent is set...
354
- //note that there is no "double fallback", so current parent "can" be the artifical zero element (level-0) *if*
355
- // the current item is a singleton( ie. no kids & no ancestors)!
356
- $ancestor = array_slice( $structure[ $currentItem ]['ancestors'], -1, 1 );
357
- $startWithKidsOf = $ancestor[0]; //can be zero!
358
- $include_parent = $include_parent || $cmw['fallback_nc_include_parent'];
359
- $include_parent_siblings = $include_parent_siblings || $cmw['fallback_nc_include_parent_siblings'];
360
- $cmw['_walker']['fellback'] = 'to-parent';
361
- }
362
- }elseif( $find_current_parent || $find_current_root ){
363
- //as of v2.0.0 the fallback to current item - for current menu items at the top level - is deprecated, but
364
- //retained for a while to maintain backward compatibility
365
- //if no parent : fall back to current item (if set)...
366
- if( $structure[ $currentItem ]['level'] == 1 && $cmw['fallback_no_ancestor'] ){
367
- $startWithKidsOf = $currentItem;
368
- $include_parent = $include_parent || $cmw['fallback_include_parent'];
369
- $include_parent_siblings = $include_parent_siblings || $cmw['fallback_include_parent_siblings'];
370
- $cmw['_walker']['fellback'] = 'to-current';
371
- }else{
372
- //as of v2.0.0, the artificial level-0 counts as parent of a top-level current menu item...
373
- if( $find_current_parent ){
374
- $ancestor = -1;
375
- }elseif( $structure[ $currentItem ]['level'] > 1 ){
376
- $ancestor = 1;
377
- }else{
378
- $ancestor = 0;
379
- }
380
- $ancestor = array_slice( $structure[ $currentItem ]['ancestors'], $ancestor, 1 );
381
- if( !empty( $ancestor ) ){
382
- $startWithKidsOf = $ancestor[0]; //as of v2.0.0, this can now be zero!
383
- }
384
  }
385
  }
386
  }
 
387
 
388
- if( $continue ){
389
- //right, let's set the keep flags
390
- //for specific items, go straight in on the item id (start level and depth do not apply here)...
391
- if( $find_specific_items ){
392
- foreach( preg_split('/[,\s]+/', $cmw['items'] ) as $itemID ){
393
- if( isset( $structure[ $itemID ] ) ){
394
- $structure[ $itemID ]['keep'] = true;
395
- $structure[0]['keepCount']++;
396
- }
397
- }
398
- //for show-all filter, just use the levels...
399
- }elseif( !$find_kids_of ){
400
- //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
401
- if( $depth_rel_current && !empty( $currentItem ) && $structure[ $currentItem ]['level'] >= $cmw['start_level'] ){
402
- $bottomLevel = $structure[ $currentItem ]['level'] + $cmw['depth'] - 1;
403
- }else{
404
- $bottomLevel = $cmw['depth'] > 0 ? $cmw['start_level'] + $cmw['depth'] - 1 : $allLevels;
405
  }
406
- for( $i = $cmw['start_level']; isset( $levels[ $i ] ) && $i <= $bottomLevel; $i++ ){
407
- foreach( $levels[ $i ] as $itemID ){
408
- $structure[ $itemID ]['keep'] = true;
409
- $structure[0]['keepCount']++;
 
 
 
 
 
 
 
410
  }
411
  }
412
- //for kids-of filters, run a recursive through the structure's kids...
413
- }elseif( $startWithKidsOf > -1 ){
414
- //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
415
- //NB the in_array() of ancestors prevents depth_rel_current when startWithKidsOf == currentItem
416
- if( $depth_rel_current && !empty( $currentItem ) && $structure[ $currentItem ]['level'] >= $cmw['start_level']
417
- && in_array( $startWithKidsOf, $structure[ $currentItem ]['ancestors'] ) ){
418
- $bottomLevel = $structure[ $currentItem ]['level'] - 1 + $cmw['depth'];
419
- }else{
420
- $bottomLevel = $cmw['depth'] > 0
421
- ? max( $structure[ $startWithKidsOf ]['level'] + $cmw['depth'], $cmw['start_level'] + $cmw['depth'] - 1 )
422
- : $allLevels;
423
- }
424
- //$structure[0]['keepCount'] gets incremented in this recursive method...
425
- $this->_cmw_set_keep_kids( $structure, $startWithKidsOf, $cmw['start_level'], $bottomLevel );
426
  }
427
-
428
- if( $structure[0]['keepCount'] > 0 ){
429
- //we have some items! we now may need to set some more keep flags, depending on the include settings...
 
 
 
430
 
431
- //do we need to include parent, parent siblings, and/or ancestors?...
432
- //NB these are not restricted by start_level!
433
- if( $find_kids_of && $startWithKidsOf > 0 ){
434
- if( $include_parent ){
435
- $structure[ $startWithKidsOf ]['keep'] = true;
436
- //add the class directly to the elements item...
437
- $elements[ $structure[ $startWithKidsOf ]['element'] ]->classes[] = 'cmw-the-included-parent';
438
- }
439
- if( $include_parent_siblings ){
440
- $ancestor = array_slice( $structure[ $startWithKidsOf ]['ancestors'], -1, 1);
441
- foreach($structure[ $ancestor[0] ]['kids'] as $itemID ){
442
- //may have already been kept by include_parent...
443
- if( !$structure[ $itemID ]['keep'] ){
444
- $structure[ $itemID ]['keep'] = true;
445
- //add the class directly to the elements item...
446
- $elements[ $structure[ $itemID ]['element'] ]->classes[] = 'cmw-an-included-parent-sibling';
447
- }
448
- }
449
  }
450
- if( $cmw['include_ancestors'] ){
451
- foreach( $structure[ $startWithKidsOf ]['ancestors'] as $itemID ){
452
- if( $itemID > 0 && !$structure[ $itemID ]['keep'] ){
453
- $structure[ $itemID ]['keep'] = true;
454
- //add the class directly to the elements item...
455
- $elements[ $structure[ $itemID ]['element'] ]->classes[] = 'cmw-an-included-parent-ancestor';
 
456
  }
457
  }
458
  }
@@ -460,1140 +549,325 @@ class Custom_Menu_Wizard_Walker extends Walker_Nav_Menu {
460
  }
461
  }
462
 
463
- $substructure = array();
464
- //check that (a) we have items, and (b) if we must have current menu item, we've got it...
465
- if( $structure[0]['keepCount'] > 0 && ( !$cmw['contains_current'] || $structure[ $currentItem ]['keep'] ) ){
466
-
467
- //might we want the parent's title as the widget title?...
468
- if( $find_kids_of && $cmw['title_from_parent'] && $startWithKidsOf > 0 ){
469
- $cmw['_walker']['parent_title'] = apply_filters(
470
- 'the_title',
471
- $elements[ $structure[ $startWithKidsOf ]['element'] ]->title,
472
- $elements[ $structure[ $startWithKidsOf ]['element'] ]->ID
473
- );
474
- }
475
- //might we want the current item's title as the widget title?...
476
- if( !empty( $currentItem ) && $cmw['title_from_current'] ){
477
- $cmw['_walker']['current_title'] = apply_filters(
478
- 'the_title',
479
- $elements[ $structure[ $currentItem ]['element'] ]->title,
480
- $elements[ $structure[ $currentItem ]['element'] ]->ID
481
- );
482
- }
483
-
484
- //now we need to gather together all the 'keep' items from structure;
485
- //while doing so, we need to set up levels and kids, ready for adding classes...
486
- foreach( $structure as $k=>$v ){
487
- if( $k > 0 && $v['keep'] ){
488
- $substructure[ $k ] = $v;
489
- //take a copy of the elements item...
490
- $substructure[ $k ]['element'] = $elements[ $v['element'] ];
491
- //use kids as a has-submenu flag...
492
- $substructure[ $k ]['kids'] = 0;
493
- //any surviving parent (except the artificial level-0) should have submenu class set on it...
494
- array_shift( $v['ancestors'] ); //remove the level-0
495
- for( $i = count( $v['ancestors'] ) - 1; $i >= 0; $i-- ){
496
- if( isset( $substructure[ $v['ancestors'][ $i ] ] ) ){
497
- $substructure[ $v['ancestors'][ $i ] ]['kids']++;
498
- }else{
499
- //not a 'kept' ancestor so remove it...
500
- array_splice( $v['ancestors'], $i, 1 );
501
- }
502
- }
503
- //ancestors now only has 'kept' ancestors...
504
- $substructure[ $k ]['level'] = count( $v['ancestors'] ) + 1;
505
- //need to ensure that the parent_field of all the new top-level (ie. root) items is set to
506
- //zero, otherwise the parent::walk() will assume they're orphans and ignore them.
507
- //however, we also need to check - especially for a specific-items filter (v2.0.0) - that parent_field of a
508
- //child actually points to the closest 'kept' ancestor; otherwise, given A (kept) > B (not kept) > C (kept)
509
- //the parent_field of C would point to a non-existent B and would subsequently be considered an orphan!
510
- if( $substructure[ $k ]['level'] == 1){
511
- $substructure[ $k ]['element']->$parent_field = 0;
512
- }else{
513
- //NB even though this really only needs to be done for $find_specific_items, I'm doing it regardless.
514
- //set to the closest ancestor, ie. the new(?) parent...
515
- $ancestor = array_slice( $v['ancestors'], -1, 1 );
516
- $substructure[ $k ]['element']->$parent_field = $ancestor[0];
517
  }
518
  }
519
  }
520
- }
521
 
522
- //put substructure's elements back into $elements (remember that it's a 1-based array!)...
523
- $elements = array();
524
- $i = 1;
525
- foreach( $substructure as $k=>$v ){
526
- $elements[ $i ] = $v['element'];
527
- //add the submenu class?...
528
- if( $v['kids'] > 0 ){
529
- $elements[ $i ]->classes[] = 'cmw-has-submenu';
530
- }else{
531
- //3.7 adds a menu-item-has-children class to (original) menu items that have kids : remove it as the item is now childless...
532
- $elements[ $i ]->classes = array_diff( $elements[ $i ]->classes, array('menu-item-has-children') );
533
  }
534
- //add the level class...
535
- $elements[ $i ]->classes[] = 'cmw-level-' . $v['level'];
536
- $i++;
537
- }
538
- unset( $structure, $substructure );
539
-
540
- //since we've done all the depth filtering, set max_depth to unlimited (unless 'flat' was requested!)...
541
- if( !$cmw['flat_output'] ){
542
- $max_depth = 0;
543
- }
544
- } //ends the check for bad max depth, empty elements, or empty cmw args
545
-
546
- return empty( $elements ) ? '' : parent::walk( apply_filters( 'custom_menu_wizard_walker_items', $elements, $args ), $max_depth, $args );
547
- }
548
 
549
- /**
550
- * recursively set the keep flag if within specified level/depth
551
- */
552
- function _cmw_set_keep_kids( &$structure, $itemId, $topLevel, $bottomLevel ){
553
- $ct = count( $structure[ $itemId ]['kids'] );
554
- for( $i = 0; $i < $ct; $i++ ){
555
- $j = $structure[ $itemId ]['kids'][ $i ];
556
- if( $structure[ $j ]['level'] <= $bottomLevel ){
557
- $structure[ $j ]['keep'] = $structure[ $j ]['level'] >= $topLevel;
558
- if( $structure[ $j ]['keep'] ){
559
- $structure[0]['keepCount']++;
560
  }
561
- }
562
- if( $structure[ $j ]['level'] < $bottomLevel ){
563
- $this->_cmw_set_keep_kids( $structure, $j, $topLevel, $bottomLevel );
564
- }
565
- }
566
- }
567
-
568
- } //end Custom_Menu_Wizard_Walker class
569
-
570
- /**
571
- * Custom Menu Wizard Widget class
572
- */
573
- class Custom_Menu_Wizard_Widget extends WP_Widget {
574
-
575
- var $_cmw_switches = array(
576
- 'hide_title' => 0,
577
- 'contains_current' => 0, //v2.0.0 added
578
- 'depth_rel_current' => 0, //v2.0.0 added
579
- 'fallback_no_ancestor' => 0, //v1.1.0 added
580
- 'fallback_include_parent' => 0, //v1.1.0 added
581
- 'fallback_include_parent_siblings' => 0, //v1.1.0 added
582
- 'fallback_no_children' => 0, //v1.2.0 added
583
- 'fallback_nc_include_parent' => 0, //v1.2.0 added
584
- 'fallback_nc_include_parent_siblings' => 0, //v1.2.0 added
585
- 'flat_output' => 0,
586
- 'include_parent' => 0,
587
- 'include_parent_siblings' => 0, //v1.1.0 added
588
- 'include_ancestors' => 0,
589
- 'hide_empty' => 0, //v1.1.0: this now only has relevance prior to WP v3.6
590
- 'title_from_parent' => 0,
591
- 'title_from_current' => 0, //v1.2.0 added
592
- 'ol_root' => 0,
593
- 'ol_sub' => 0,
594
- //field section toggles...
595
- 'fs_filter' => 0,
596
- 'fs_fallbacks' => 1, //v1.2.0 added
597
- 'fs_output' => 1,
598
- 'fs_container' => 1,
599
- 'fs_classes' => 1,
600
- 'fs_links' => 1
601
- );
602
- var $_cmw_strings = array(
603
- 'title' => '',
604
- 'items' => '', //v2.0.0 added
605
- 'container' => 'div',
606
- 'container_id' => '',
607
- 'container_class' => '',
608
- 'menu_class' => 'menu-widget',
609
- 'widget_class' => ''
610
- );
611
- var $_cmw_html = array(
612
- 'before' => '',
613
- 'after' => '',
614
- 'link_before' => '',
615
- 'link_after' => ''
616
- );
617
- var $_cmw_integers = array(
618
- 'depth' => 0,
619
- 'filter' => -1, //v2.0.0 changed from switch
620
- 'filter_item' => -2, //v1.1.0 changed from 0
621
- 'menu' => 0,
622
- 'start_level' => 1
623
- );
624
-
625
- //v1.2.1 holds information determined by the walker...
626
- var $_cmw_walker = array();
627
-
628
- /**
629
- * class constructor
630
- */
631
- function __construct() {
632
- parent::__construct(
633
- 'custom-menu-wizard',
634
- 'Custom Menu Wizard',
635
- array(
636
- 'classname' => 'widget_custom_menu_wizard',
637
- 'description' => __('Add a custom menu, or part of one, as a widget')
638
- )
639
- );
640
- }
641
-
642
- /**
643
- * v1.2.1 stores any walker-determined information back into the widget instance
644
- * gets run by the walker, on the filtered array of menu items, just before running parent::walk()
645
- * only gets run *if* there are menu items found
646
- *
647
- * @param array $items Filtered menu items
648
- * @param object $args
649
- * @return array Menu items
650
- */
651
- function cmw_filter_walker_items($items, $args){
652
- if( !empty( $args->_custom_menu_wizard['_walker'] ) ){
653
- $this->_cmw_walker = $args->_custom_menu_wizard['_walker'];
654
- }
655
- return $items;
656
- }
657
-
658
- /**
659
- * this (filter: wp_nav_menu) merely removes itself from the filters and returns an empty string
660
- * it gets added by the cmw_filter_check_for_no_items method below, and only
661
- * ever gets run when hide_empty is set on the widget instance
662
- *
663
- * v1.1.0 As of WP v3.6 this method becomes superfluous because wp_nav_menu() has had code added to immediately
664
- * cop out (return false) if the output from wp_nav_menu_{$menu->slug}_items filter(s) is empty.
665
- * However, it stays in so as to cope with versions < 3.6
666
- *
667
- * @param string $nav_menu HTML for the menu
668
- * @param object $args
669
- * @return string HTML for the menu
670
- */
671
- function cmw_filter_no_output_when_empty($nav_menu, $args){
672
- remove_filter( 'wp_nav_menu', array( $this, 'cmw_filter_no_output_when_empty' ), 65532, 2 );
673
- return empty( $args->_custom_menu_wizard ) ? $nav_menu : '';
674
- }
675
-
676
- /**
677
- * this gets run (filter: wp_nav_menu_{$menu->slug}_items) if hide_empty is set
678
- * if $items is empty then add a wp_nav_menu filter to do the actual return of an empty string
679
- * it gets run before the wp_nav_menu filter, but it gets the $items array whereas the wp_nav_menu filter does not
680
- * it gets added by $this->widget() before wp_nav_menu() is called, and removed immediately after wp_nav_menu() returns
681
- *
682
- * v1.1.0 As of WP v3.6 this method becomes superfluous because wp_nav_menu() has had code added to immediately
683
- * cop out (return false) if the output from wp_nav_menu_{$menu->slug}_items filter(s) is empty.
684
- * However, it stays in so as to cope with versions < 3.6
685
- *
686
- * @param array $items Menu items
687
- * @param object $args
688
- * @return array Menu items
689
- */
690
- function cmw_filter_check_for_no_items($items, $args){
691
- if( !empty( $args->_custom_menu_wizard ) && empty( $items ) ){
692
- add_filter( 'wp_nav_menu', array( $this, 'cmw_filter_no_output_when_empty' ), 65532, 2 );
693
- }
694
- return $items;
695
- }
696
-
697
- /**
698
- * produces the widget HTML at the front end
699
- *
700
- * @filters : custom_menu_wizard_nav_params array of params that will be sent to wp_nav_menu()
701
- *
702
- * @param object $args Widget arguments
703
- * @param array $instance Configuration for this widget instance
704
- */
705
- function widget($args, $instance) {
706
- extract( $args, EXTR_SKIP );
707
-
708
- //switches...
709
- foreach( $this->_cmw_switches as $k=>$v ){
710
- $instance[ $k ] = !empty( $instance[ $k ] );
711
- }
712
- //integers...
713
- foreach( $this->_cmw_integers as $k=>$v ){
714
- $instance[ $k ] = max( $v, intval( $instance[ $k ] ) );
715
- }
716
- //strings...
717
- foreach( $this->_cmw_strings as $k=>$v ){
718
- $instance[ $k ] = isset( $instance[ $k ] ) ? trim( $instance[ $k ] ) : $v; //bug in 2.0.2 fixed!
719
- }
720
- //html strings...
721
- foreach( $this->_cmw_html as $k=>$v ){
722
- $instance[ $k ] = empty( $instance[ $k ] ) ? $v : trim( $instance[ $k ] );
723
- }
724
-
725
- //v1.1.0 As of WP v3.6, wp_nav_menu() automatically prevents any HTML output if there are no items...
726
- $instance['hide_empty'] = $instance['hide_empty'] && $this->_pre_3point6();
727
-
728
- //fetch menu...
729
- if( !empty($instance['menu'] ) ){
730
- $menu = wp_get_nav_menu_object( $instance['menu'] );
731
-
732
- //no menu, no output...
733
- if ( !empty( $menu ) ){
734
 
 
 
 
 
 
 
735
  if( !empty( $instance['widget_class'] ) ){
736
- //$before_widget is usually just a DIV start-tag, with an id and a class; if it
737
- //gets more complicated than that then this may not work as expected...
738
- if( preg_match( '/^<[^>]+?class=["\']/', $before_widget ) > 0 ){
739
- $before_widget = preg_replace( '/(class=["\'])/', '$1' . $instance['widget_class'] . ' ', $before_widget, 1 );
740
- }else{
741
- $before_widget = preg_replace( '/^(<\w+)(\s|>)/', '$1 class="' . $instance['widget_class'] . '"$2', $before_widget );
742
- }
743
- }
744
-
745
- if( !empty( $instance['container_class'] ) ){
746
- $instance['container_class'] = "menu-{$menu->slug}-container {$instance['container_class']}";
747
- }
748
-
749
- $instance['menu_class'] = preg_split( '/\s+/', $instance['menu_class'], -1, PREG_SPLIT_NO_EMPTY );
750
- if( $instance['fallback_no_ancestor'] || $instance['fallback_no_children'] ){
751
- //v1.2.1 add a cmw-fellback-maybe class to the menu and we'll remove or replace it later...
752
- $instance['menu_class'][] = 'cmw-fellback-maybe';
753
  }
754
- $instance['menu_class'] = implode( ' ', $instance['menu_class'] );
755
 
756
- $walker = new Custom_Menu_Wizard_Walker;
757
- $params = array(
758
- 'menu' => $menu,
759
- 'container' => $instance['container'] == 'none' ? false : $instance['container'],
760
- 'container_id' => $instance['container_id'],
761
- 'menu_class' => $instance['menu_class'],
762
- 'echo' => false,
763
- 'fallback_cb' => false,
764
- 'before' => $instance['before'],
765
- 'after' => $instance['after'],
766
- 'link_before' => $instance['link_before'],
767
- 'link_after' => $instance['link_after'],
768
- 'depth' => empty( $instance['flat_output'] ) ? $instance['depth'] : -1,
769
- 'walker' =>$walker,
770
- //widget specific stuff...
771
- '_custom_menu_wizard' => array(
772
- 'filter' => $instance['filter'],
773
- 'filter_item' => $instance['filter_item'],
774
- 'fallback_no_ancestor' => $instance['fallback_no_ancestor'], //v1.1.0
775
- 'fallback_include_parent' => $instance['fallback_include_parent'], //v1.1.0
776
- 'fallback_include_parent_siblings' => $instance['fallback_include_parent_siblings'], //v1.1.0
777
- 'fallback_no_children' => $instance['fallback_no_children'], //v1.2.0
778
- 'fallback_nc_include_parent' => $instance['fallback_nc_include_parent'], //v1.2.0
779
- 'fallback_nc_include_parent_siblings' => $instance['fallback_nc_include_parent_siblings'], //v1.2.0
780
- 'include_parent' => $instance['include_parent'],
781
- 'include_parent_siblings' => $instance['include_parent_siblings'], //v1.1.0
782
- 'include_ancestors' => $instance['include_ancestors'],
783
- 'title_from_parent' => $instance['title_from_parent'],
784
- 'title_from_current' => $instance['title_from_current'], //v1.2.0
785
- 'ol_root' => $instance['ol_root'],
786
- 'ol_sub' => $instance['ol_sub'],
787
- 'flat_output' => $instance['flat_output'],
788
- 'start_level' => $instance['start_level'],
789
- 'depth' => $instance['depth'],
790
- 'depth_rel_current' => $instance['depth_rel_current'], //v2.0.0
791
- 'contains_current' => $instance['contains_current'], //v2.0.0
792
- 'items' => $instance['items'], //v2.0.0
793
- //v1.2.1 this is for the walker's use...
794
- '_walker' => array()
795
- )
796
- );
797
- if( $instance['ol_root'] ){
798
- $params['items_wrap'] = '<ol id="%1$s" class="%2$s">%3$s</ol>';
799
- }
800
- if( !empty( $instance['container_class'] ) ){
801
- $params['container_class'] = $instance['container_class'];
802
- }
803
 
804
- add_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
805
- if( $instance['hide_empty'] ){
806
- add_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
 
 
 
 
 
 
 
 
 
 
807
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
808
 
809
- //NB: wp_nav_menu() is in wp-includes/nav-menu-template.php
810
- $out = wp_nav_menu( apply_filters( 'custom_menu_wizard_nav_params', $params ) );
811
 
812
- remove_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
813
- if( $instance['hide_empty'] ){
814
- remove_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
 
 
 
 
 
815
  }
816
-
817
- //only put something out if there is something to put out...
818
- if( !empty( $out ) ){
819
-
820
- //title from : 'from parent' has priority over 'from current'...
821
- //note that 'parent' is whatever you are getting the children of and therefore doesn't apply to a ShowAll, whereas
822
- //'current' is the current menu item (as determined by WP); also note that neither parent nor current actually has
823
- //to be present in the results
824
- if( $instance['title_from_parent'] && !empty( $this->_cmw_walker['parent_title'] ) ){
825
- $title = $this->_cmw_walker['parent_title'];
826
- }
827
- if( empty( $title ) && $instance['title_from_current'] && !empty( $this->_cmw_walker['current_title'] ) ){
828
- $title = $this->_cmw_walker['current_title'];
829
- }
830
- if( empty( $title ) ){
831
- $title = $instance['hide_title'] ? '' : $instance['title'];
832
- }
833
-
834
- //remove/replace the cmw-fellback-maybe class...
835
- $out = str_replace(
836
- 'cmw-fellback-maybe',
837
- empty( $this->_cmw_walker['fellback'] ) ? '' : 'cmw-fellback-' . $this->_cmw_walker['fellback'],
838
- $out );
839
-
840
- echo $before_widget;
841
- if ( !empty($title) ){
842
- echo $before_title . apply_filters('widget_title', $title, $instance, $this->id_base) . $after_title;
843
  }
844
- echo $out . $after_widget;
845
  }
846
  }
847
- }
848
- }
849
-
850
- /**
851
- * updates the widget settings sent from the backend admin
852
- */
853
- function update( $new_instance, $old_instance ) {
854
- $instance = $old_instance;
855
 
856
- //switches...
857
- foreach( $this->_cmw_switches as $k=>$v ){
858
- $instance[ $k ] = empty( $new_instance[ $k ] ) ? 0 : 1;
859
- }
860
- //integers...
861
- foreach( $this->_cmw_integers as $k=>$v ){
862
- $instance[ $k ] = isset( $new_instance[ $k ]) ? max( $v, intval( $new_instance[ $k ] ) ) : $v;
863
- }
864
- //strings...
865
- foreach( $this->_cmw_strings as $k=>$v ){
866
- $instance[ $k ] = isset( $new_instance[ $k ] ) ? strip_tags( trim( $new_instance[ $k ] ) ) : $v;
867
- }
868
- //html strings...
869
- foreach( $this->_cmw_html as $k=>$v ){
870
- $instance[ $k ] = isset( $new_instance[ $k ] ) ? trim( $new_instance[ $k ] ) : $v;
871
- }
872
- //items special case...
873
- if( !empty( $instance['items'] ) ){
874
- $sep = preg_match( '/(^\d+$|,)/', $instance['items'] ) > 0 ? ',' : ' ';
875
- $a = array();
876
- foreach( preg_split('/[,\s]+/', $instance['items'], -1, PREG_SPLIT_NO_EMPTY ) as $v ){
877
- $i = intval( $v );
878
- if( $i > 0 ){
879
- $a[] = $i;
 
 
 
 
 
 
 
 
 
880
  }
 
881
  }
882
- $instance['items'] = implode( $sep, $a );
883
- }
884
-
885
- return $instance;
886
- }
887
-
888
- /**
889
- * produces the backend admin form(s)
890
- */
891
- function form( $instance ) {
892
-
893
- //switches...
894
- foreach( $this->_cmw_switches as $k=>$v ){
895
- $instance[ $k ] = isset( $instance[ $k ] ) ? !empty( $instance[ $k ] ) : !empty( $v );
896
- }
897
- //integers...
898
- foreach( $this->_cmw_integers as $k=>$v ){
899
- $instance[ $k ] = isset( $instance[ $k ]) ? max( $v, intval( $instance[ $k ] ) ) : max($v, 0);
900
- }
901
- //strings...
902
- foreach( $this->_cmw_strings as $k=>$v ){
903
- $instance[ $k ] = isset( $instance[ $k ] ) ? esc_attr( trim( $instance[ $k ] ) ) : $v;
904
- }
905
- //html strings...
906
- foreach( $this->_cmw_html as $k=>$v ){
907
- $instance[ $k ] = isset( $instance[ $k ] ) ? esc_html( trim( $instance[ $k ] ) ) : $v;
908
- }
909
 
910
- //get menus...
911
- $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
912
- $noitems = true;
913
- if( !empty( $menus ) ){
914
- foreach( $menus as $i=>$menu ){
915
- $menus[ $i ]->_items = wp_get_nav_menu_items( $menu->term_id );
916
- if( !empty( $menus[ $i ]->_items ) ){
917
- $noitems = false;
 
 
 
 
 
918
  }
919
- }
920
- }
921
-
922
- //if no populated menus exist, suggest the user go create one...
923
- if( $noitems ){
924
- echo '<p>'. sprintf( __('No populated menus have been created yet. <a href="%s">Create one</a>.'), admin_url('nav-menus.php') ) .'</p>';
925
- return;
926
- }
927
-
928
- ?>
929
- <div class="widget-<?php echo $this->id_base; ?>-onchange"
930
- data-cmw-dialog-title='<?php _e('Selected Menu : '); ?>'
931
- data-cmw-dialog-prompt='<?php _e('Click an item to toggle &quot;Current Menu Item&quot;'); ?>'
932
- data-cmw-dialog-output='<?php _e('Basic Output'); ?>'
933
- data-cmw-dialog-fallback='<?php _e('Fallback invoked'); ?>'
934
- data-cmw-dialog-trigger='#<?php echo $this->get_field_id('filter_item'); ?>'
935
- data-cmw-dialog-id='<?php echo $this->get_field_id('dialog'); ?>'>
936
- <?php
937
-
938
- /**
939
- * permanently visible section : Title (with Hide) and Menu
940
- */
941
- ?>
942
- <p>
943
- <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:') ?></label>
944
- <label class="alignright">
945
- <input id="<?php echo $this->get_field_id('hide_title'); ?>" name="<?php echo $this->get_field_name('hide_title'); ?>"
946
- type="checkbox" value="1" <?php checked( $instance['hide_title'] ); ?> />
947
- <?php _e('Hide'); ?></label>
948
- <input id="<?php echo $this->get_field_id('title'); ?>" class="widefat" name="<?php echo $this->get_field_name('title'); ?>"
949
- type="text" value="<?php echo $instance['title']; ?>" />
950
- <small><em><?php _e('Title can be set, but need not be displayed'); ?></em></small>
951
- </p>
952
-
953
- <p>
954
- <small class="cmw-toggle-assist">
955
- <a class="widget-<?php echo $this->id_base; ?>-toggle-assist" href="#"><?php _e('assist'); ?></a>
956
- </small>
957
- <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
958
- <select id="<?php echo $this->get_field_id('menu'); ?>"
959
- class="widget-<?php echo $this->id_base; ?>-selectmenu widget-<?php echo $this->id_base; ?>-listen"
960
- name="<?php echo $this->get_field_name('menu'); ?>">
961
- <?php
962
- foreach( $menus as $i=>$menu ){
963
- if( !empty( $menu->_items ) ){
964
- ?>
965
- <option <?php selected($instance['menu'], $menu->term_id); ?> value="<?php echo $menu->term_id; ?>"><?php echo $menu->name; ?></option>
966
- <?php
967
- }
968
- }
969
- ?>
970
- </select>
971
- </p>
972
-
973
- <?php
974
- /**
975
- * start collapsible section : 'Filter'
976
- */
977
- $this->_open_a_field_section($instance, 'Filter', 'fs_filter');
978
- ?>
979
- <p>
980
- <small class="cmw-toggle-assist">
981
- <a class="widget-<?php echo $this->id_base; ?>-toggle-assist" href="#"><?php _e('assist'); ?></a>
982
- </small>
983
- <label>
984
- <input id="<?php echo $this->get_field_id('filter'); ?>_0"
985
- class="widget-<?php echo $this->id_base; ?>-showall widget-<?php echo $this->id_base; ?>-listen"
986
- name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="0" <?php checked($instance['filter'], 0); ?> />
987
- <?php _e('Show all'); ?></label>
988
- <br /><label>
989
- <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="widget-<?php echo $this->id_base; ?>-listen"
990
- name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="1" <?php checked($instance['filter'], 1); ?> />
991
- <?php _e('Children of:'); ?></label>
992
- <select id="<?php echo $this->get_field_id('filter_item'); ?>"
993
- class="widget-<?php echo $this->id_base; ?>-childrenof widget-<?php echo $this->id_base; ?>-listen"
994
- name="<?php echo $this->get_field_name('filter_item'); ?>">
995
- <option value="0" <?php selected( $instance['filter_item'], 0 ); ?>><?php _e('Current Item'); ?></option>
996
- <option value="-2" <?php selected( $instance['filter_item'], -2 ); ?>><?php _e('Current Root Item'); ?></option>
997
- <option value="-1" <?php selected( $instance['filter_item'], -1 ); ?>><?php _e('Current Parent Item'); ?></option>
998
- <?php
999
- //v1.1.0
1000
- // IE is a pita when it comes to SELECTs because it ignores any styling on OPTGROUPs and OPTIONs, so I'm changing the way
1001
- // that this SELECT works by introducing a copy from which the javascript can pick the relevant OPTGROUP
1002
- $menuOptions = array();
1003
-
1004
- $maxlevel = 1;
1005
- foreach( $menus as $i=>$menu ){
1006
- //as of v1.2.0 : no items, no optgroup!
1007
- if( !empty( $menu->_items ) ){
1008
- $grpdata = array();
1009
- $itemindents = array( '0' => array( 'level'=>0, 'grpkey'=>'' ) );
1010
- $menuGrpOpts = array();
1011
- foreach( $menu->_items as $item ){
1012
- //exclude orpans!
1013
- if( isset($itemindents[ $item->menu_item_parent ])){
1014
- $title = apply_filters( 'the_title', $item->title, $item->ID );
1015
- $level = $itemindents[ $item->menu_item_parent ]['level'] + 1;
1016
- $grpkey = $item->ID . '|' . $title;
1017
- $grpdata[ $grpkey ] = array();
1018
- if( !empty( $itemindents[ $item->menu_item_parent ]['grpkey'] )){
1019
- $grpdata[ $itemindents[ $item->menu_item_parent ]['grpkey'] ][ $grpkey ] = array();
1020
  }
1021
-
1022
- $itemindents[ $item->ID ] = array( 'level'=>$level, 'grpkey'=>$grpkey );
1023
- $maxlevel = max( $maxlevel, $level );
1024
- //v2.0.0 indents changed to non-breaking spaces...
1025
- $menuGrpOpts[] = '<option value="' . $item->ID . '" ' .
1026
- selected( $instance['filter_item'], $item->ID, false ) . '>' .
1027
- str_repeat( '&nbsp;', ($level - 1) * 3 ) . $title . '</option>';
1028
  }
1029
  }
1030
-
1031
- //the menu had items, but they might all have been orphans?...
1032
- if( !empty( $menuGrpOpts ) ){
1033
- foreach( array_reverse( $grpdata ) as $k=>$v ){
1034
- if( empty( $v ) ){
1035
- $grpdata[ $k ] = false;
1036
- }else{
1037
- foreach( $v as $n=>$j ){
1038
- $grpdata[ $k ][ $n ] = $grpdata[ $n ];
1039
- unset( $grpdata[ $n ] );
1040
- }
 
1041
  }
1042
  }
1043
- $grpdata = json_encode( $grpdata );
1044
- $menuOptions[] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $i . '" data-cmw-items="' . esc_attr($grpdata) . '">';
1045
- $menuOptions[] = implode("\n", $menuGrpOpts);
1046
- $menuOptions[] = '</optgroup>';
1047
  }
1048
- unset( $menuGrpOpts, $grpdata, $itemindents );
1049
- }
1050
- }
1051
- $menuOptions = implode("\n", $menuOptions);
1052
- echo $menuOptions;
1053
- ?>
1054
- </select>
1055
- <br /><label>
1056
- <input id="<?php echo $this->get_field_id('filter'); ?>_2"
1057
- class="widget-<?php echo $this->id_base; ?>-showspecific widget-<?php echo $this->id_base; ?>-listen"
1058
- name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="-1" <?php checked($instance['filter'], -1); ?> />
1059
- <?php _e('Items:'); ?></label>
1060
- <input id="<?php echo $this->get_field_id('items'); ?>" class="widget-<?php echo $this->id_base; ?>-setitems"
1061
- name="<?php echo $this->get_field_name('items'); ?>" type="text" value="<?php echo $instance['items']; ?>" />
1062
-
1063
- <select id="<?php echo $this->get_field_id('filter_item_ignore'); ?>" disabled="disabled"
1064
- class='cmw-off-the-page' name="<?php echo $this->get_field_name('filter_item_ignore'); ?>">
1065
- <?php echo $menuOptions; ?>
1066
- </select>
1067
- </p>
1068
-
1069
- <p class="widget-<?php echo $this->id_base; ?>-disableif-ss">
1070
- <label for="<?php echo $this->get_field_id('start_level'); ?>"><?php _e('Starting Level:'); ?></label>
1071
- <select id="<?php echo $this->get_field_id('start_level'); ?>" name="<?php echo $this->get_field_name('start_level'); ?>">
1072
- <?php
1073
- $j = max( $maxlevel, $instance['start_level'] );
1074
- for( $i = 1; $i <= $j; $i++ ){
1075
- ?>
1076
- <option value="<?php echo $i; ?>" <?php selected($instance['start_level'], $i); ?>><?php echo $i; ?></option>
1077
- <?php
1078
- }
1079
- ?>
1080
- </select>
1081
- <br /><small><em><?php _e('Level to start testing items for inclusion'); ?></em></small>
1082
- </p>
1083
-
1084
- <p class="widget-<?php echo $this->id_base; ?>-disableif-ss">
1085
- <label for="<?php echo $this->get_field_id('depth'); ?>"><?php _e('For Depth:'); ?></label>
1086
- <select id="<?php echo $this->get_field_id('depth'); ?>" name="<?php echo $this->get_field_name('depth'); ?>">
1087
- <option value="0" <?php selected( $instance['depth'], 0 ); ?>><?php _e('unlimited'); ?></option>
1088
- <?php
1089
- $j = max( $j, $instance['depth'] );
1090
- for( $i = 1; $i <= $j; $i++ ){
1091
- ?>
1092
- <option value="<?php echo $i; ?>" <?php selected( $instance['depth'], $i ); ?>><?php echo $i; ?> <?php _e($i > 1 ? 'levels' : 'level'); ?></option>
1093
- <?php
1094
- }
1095
- ?>
1096
- </select>
1097
- <br /><small><em><?php _e('Relative to first Filter item found, <strong>unless</strong>&hellip;'); ?></em></small>
1098
- <br /><label>
1099
- <input id="<?php echo $this->get_field_id('depth_rel_current'); ?>"
1100
- name="<?php echo $this->get_field_name('depth_rel_current'); ?>" type="checkbox" value="1"
1101
- <?php checked($instance['depth_rel_current']); ?> />
1102
- <?php _e('Relative to &quot;Current&quot; Item <small><em>(if found)</em></small>'); ?></label>
1103
- </p>
1104
- <?php $this->_close_a_field_section(); ?>
1105
-
1106
- <?php
1107
- /**
1108
- * v1.2.0 start collapsible section : 'Fallbacks'
1109
- */
1110
- $this->_open_a_field_section($instance, 'Fallbacks', 'fs_fallbacks');
1111
- ?>
1112
- <p class="clear widget-<?php echo $this->id_base; ?>-disableifnot-rp">
1113
- <small class="cmw-toggle-assist">
1114
- <a class="widget-<?php echo $this->id_base; ?>-toggle-assist" href="#"><?php _e('assist'); ?></a>
1115
- </small>
1116
- <small><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Root / Parent Item</em>, and no ancestor exists' ); ?> :</strong></small>
1117
- <br /><label>
1118
- <input id="<?php echo $this->get_field_id('fallback_no_ancestor'); ?>"
1119
- name="<?php echo $this->get_field_name('fallback_no_ancestor'); ?>" type="checkbox" value="1"
1120
- <?php checked($instance['fallback_no_ancestor']); ?> />
1121
- <?php _e('Switch to Current Item, and'); ?></label>
1122
- <br /><label class="cmw-pad-left-1">
1123
- <input id="<?php echo $this->get_field_id('fallback_include_parent'); ?>"
1124
- name="<?php echo $this->get_field_name('fallback_include_parent'); ?>" type="checkbox" value="1"
1125
- <?php checked($instance['fallback_include_parent']); ?> />
1126
- <?php _e('Include Parent...'); ?> </label>
1127
- <label>
1128
- <input id="<?php echo $this->get_field_id('fallback_include_parent_siblings'); ?>"
1129
- name="<?php echo $this->get_field_name('fallback_include_parent_siblings'); ?>" type="checkbox" value="1"
1130
- <?php checked($instance['fallback_include_parent_siblings']); ?> />
1131
- <?php _e('with Siblings'); ?></label>
1132
- </p>
1133
-
1134
- <p class="widget-<?php echo $this->id_base; ?>-disableifnot-ci">
1135
- <small><strong><?php _e( 'If &quot;Children of&quot; is <em>Current Item</em>, and current item has no children' ); ?> :</strong></small>
1136
- <br /><label>
1137
- <input id="<?php echo $this->get_field_id('fallback_no_children'); ?>"
1138
- name="<?php echo $this->get_field_name('fallback_no_children'); ?>" type="checkbox" value="1"
1139
- <?php checked($instance['fallback_no_children']); ?> />
1140
- <?php _e('Switch to Current Parent Item, and'); ?></label>
1141
- <br /><label class="cmw-pad-left-1">
1142
- <input id="<?php echo $this->get_field_id('fallback_nc_include_parent'); ?>"
1143
- name="<?php echo $this->get_field_name('fallback_nc_include_parent'); ?>" type="checkbox" value="1"
1144
- <?php checked($instance['fallback_nc_include_parent']); ?> />
1145
- <?php _e('Include Parent...'); ?> </label>
1146
- <label>
1147
- <input id="<?php echo $this->get_field_id('fallback_nc_include_parent_siblings'); ?>"
1148
- name="<?php echo $this->get_field_name('fallback_nc_include_parent_siblings'); ?>" type="checkbox" value="1"
1149
- <?php checked($instance['fallback_nc_include_parent_siblings']); ?> />
1150
- <?php _e('with Siblings'); ?></label>
1151
- </p>
1152
-
1153
-
1154
- <?php $this->_close_a_field_section(); ?>
1155
-
1156
- <?php
1157
- /**
1158
- * start collapsible section : 'Output'
1159
- */
1160
- $this->_open_a_field_section($instance, 'Output', 'fs_output');
1161
- ?>
1162
- <p>
1163
- <small class="cmw-toggle-assist">
1164
- <a class="widget-<?php echo $this->id_base; ?>-toggle-assist" href="#"><?php _e('assist'); ?></a>
1165
- </small>
1166
- <label>
1167
- <input id="<?php echo $this->get_field_id('flat_output'); ?>_0" name="<?php echo $this->get_field_name('flat_output'); ?>"
1168
- type="radio" value="0" <?php checked(!$instance['flat_output']); ?> />
1169
- <?php _e('Hierarchical'); ?></label>
1170
- &nbsp;<label>
1171
- <input id="<?php echo $this->get_field_id('flat_output'); ?>_1" name="<?php echo $this->get_field_name('flat_output'); ?>"
1172
- type="radio" value="1" <?php checked($instance['flat_output']); ?> />
1173
- <?php _e('Flat'); ?></label>
1174
- </p>
1175
-
1176
- <p>
1177
- <label>
1178
- <input id="<?php echo $this->get_field_id('contains_current'); ?>"
1179
- name="<?php echo $this->get_field_name('contains_current'); ?>" type="checkbox"
1180
- value="1" <?php checked($instance['contains_current']); ?> />
1181
- <?php _e('Must Contain &quot;Current&quot; Item'); ?></label>
1182
- <br /><small><em><?php _e('Checks both Filtered and Included items'); ?></em></small>
1183
- </p>
1184
-
1185
- <p class="widget-<?php echo $this->id_base; ?>-disableif">
1186
- <label>
1187
- <input id="<?php echo $this->get_field_id('include_parent'); ?>"
1188
- name="<?php echo $this->get_field_name('include_parent'); ?>" type="checkbox"
1189
- value="1" <?php checked($instance['include_parent']); ?> />
1190
- <?php _e('Include Parent...'); ?> </label>
1191
- <label>
1192
- <input id="<?php echo $this->get_field_id('include_parent_siblings'); ?>"
1193
- name="<?php echo $this->get_field_name('include_parent_siblings'); ?>" type="checkbox"
1194
- value="1" <?php checked($instance['include_parent_siblings']); ?> />
1195
- <?php _e('with Siblings'); ?></label>
1196
- <br /><label>
1197
- <input id="<?php echo $this->get_field_id('include_ancestors'); ?>"
1198
- name="<?php echo $this->get_field_name('include_ancestors'); ?>" type="checkbox"
1199
- value="1" <?php checked($instance['include_ancestors']); ?> />
1200
- <?php _e('Include Ancestors'); ?></label>
1201
- <br /><label>
1202
- <input id="<?php echo $this->get_field_id('title_from_parent'); ?>"
1203
- name="<?php echo $this->get_field_name('title_from_parent'); ?>" type="checkbox"
1204
- value="1" <?php checked($instance['title_from_parent']); ?> />
1205
- <?php _e('Title from Parent'); ?></label>
1206
- <br /><small><em><?php _e('Only if the &quot;Children of&quot; Filter returns items'); ?></em></small>
1207
- </p>
1208
-
1209
- <p>
1210
- <label>
1211
- <input id="<?php echo $this->get_field_id('title_from_current'); ?>"
1212
- name="<?php echo $this->get_field_name('title_from_current'); ?>" type="checkbox"
1213
- value="1" <?php checked($instance['title_from_current']); ?> />
1214
- <?php _e('Title from &quot;Current&quot; Item'); ?></label>
1215
- <br /><small><em><?php _e('Lower priority than &quot;Title from Parent&quot;'); ?></em></small>
1216
- </p>
1217
-
1218
- <p>
1219
- <?php _e('Change UL to OL:'); ?>
1220
- <br /><label>
1221
- <input id="<?php echo $this->get_field_id('ol_root'); ?>" name="<?php echo $this->get_field_name('ol_root'); ?>"
1222
- type="checkbox" value="1" <?php checked($instance['ol_root']); ?> />
1223
- <?php _e('Top Level'); ?></label>
1224
- &nbsp;<label>
1225
- <input id="<?php echo $this->get_field_id('ol_sub'); ?>" name="<?php echo $this->get_field_name('ol_sub'); ?>"
1226
- type="checkbox" value="1" <?php checked($instance['ol_sub']); ?> />
1227
- <?php _e('Sub-Levels'); ?></label>
1228
- </p>
1229
-
1230
- <?php
1231
- //v1.1.0 As of WP v3.6, wp_nav_menu() automatically cops out (without outputting any HTML) if there are no items,
1232
- // so the hide_empty option becomes superfluous; however, I'll keep the previous setting (if there was one)
1233
- // in case of reversion to an earlier version of WP...
1234
- if( $this->_pre_3point6() ){
1235
- ?>
1236
- <p>
1237
- <label>
1238
- <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
1239
- type="checkbox" value="1" <?php checked($instance['hide_empty']); ?> />
1240
- <?php _e('Hide Widget if Empty'); ?></label>
1241
- <br /><small><em><?php _e('Prevents any output when no items are found'); ?></em></small>
1242
- </p>
1243
- <?php }else{ ?>
1244
- <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
1245
- type="hidden" value="<?php echo $instance['hide_empty'] ? '1' : ''; ?>" />
1246
- <?php } ?>
1247
-
1248
- <?php $this->_close_a_field_section(); ?>
1249
-
1250
- <?php
1251
- /**
1252
- * start collapsible section : 'Container'
1253
- */
1254
- $this->_open_a_field_section($instance, 'Container', 'fs_container');
1255
- ?>
1256
- <p>
1257
- <label for="<?php echo $this->get_field_id('container'); ?>"><?php _e('Element:') ?></label>
1258
- <input id="<?php echo $this->get_field_id('container'); ?>" name="<?php echo $this->get_field_name('container'); ?>"
1259
- type="text" value="<?php echo $instance['container']; ?>" />
1260
- <br /><small><em><?php _e( 'Eg. div or nav; leave empty for no container' ); ?></em></small>
1261
- </p>
1262
- <p>
1263
- <label for="<?php echo $this->get_field_id('container_id'); ?>"><?php _e('Unique ID:') ?></label>
1264
- <input id="<?php echo $this->get_field_id('container_id'); ?>" name="<?php echo $this->get_field_name('container_id'); ?>"
1265
- type="text" value="<?php echo $instance['container_id']; ?>" />
1266
- <br /><small><em><?php _e( 'An optional ID for the container' ); ?></em></small>
1267
- </p>
1268
- <p>
1269
- <label for="<?php echo $this->get_field_id('container_class'); ?>"><?php _e('Class:') ?></label>
1270
- <input id="<?php echo $this->get_field_id('container_class'); ?>" name="<?php echo $this->get_field_name('container_class'); ?>"
1271
- type="text" value="<?php echo $instance['container_class']; ?>" />
1272
- <br /><small><em><?php _e( 'Extra class for the container' ); ?></em></small>
1273
- </p>
1274
- <?php $this->_close_a_field_section(); ?>
1275
-
1276
- <?php
1277
- /**
1278
- * start collapsible section : 'Classes'
1279
- */
1280
- $this->_open_a_field_section($instance, 'Classes', 'fs_classes');
1281
- ?>
1282
- <p>
1283
- <label for="<?php echo $this->get_field_id('menu_class'); ?>"><?php _e('Menu Class:') ?></label>
1284
- <input id="<?php echo $this->get_field_id('menu_class'); ?>" name="<?php echo $this->get_field_name('menu_class'); ?>"
1285
- type="text" value="<?php echo $instance['menu_class']; ?>" />
1286
- <br /><small><em><?php _e( 'Class for the list element forming the menu' ); ?></em></small>
1287
- </p>
1288
- <p>
1289
- <label for="<?php echo $this->get_field_id('widget_class'); ?>"><?php _e('Widget Class:') ?></label>
1290
- <input id="<?php echo $this->get_field_id('widget_class'); ?>" name="<?php echo $this->get_field_name('widget_class'); ?>"
1291
- type="text" value="<?php echo $instance['widget_class']; ?>" />
1292
- <br /><small><em><?php _e( 'Extra class for the widget itself' ); ?></em></small>
1293
- </p>
1294
- <?php $this->_close_a_field_section(); ?>
1295
-
1296
- <?php
1297
- /**
1298
- * start collapsible section : 'Links'
1299
- */
1300
- $this->_open_a_field_section($instance, 'Links', 'fs_links');
1301
- ?>
1302
- <p>
1303
- <label for="<?php echo $this->get_field_id('before'); ?>"><?php _e('Before the Link:') ?></label>
1304
- <input id="<?php echo $this->get_field_id('before'); ?>" class="widefat" name="<?php echo $this->get_field_name('before'); ?>"
1305
- type="text" value="<?php echo $instance['before']; ?>" />
1306
- <small><em><?php _e( htmlspecialchars('Text/HTML to go before the <a> of the link') ); ?></em></small>
1307
- </p>
1308
- <p>
1309
- <label for="<?php echo $this->get_field_id('after'); ?>"><?php _e('After the Link:') ?></label>
1310
- <input id="<?php echo $this->get_field_id('after'); ?>" class="widefat" name="<?php echo $this->get_field_name('after'); ?>"
1311
- type="text" value="<?php echo $instance['after']; ?>" />
1312
- <small><em><?php _e( htmlspecialchars('Text/HTML to go after the </a> of the link') ); ?></em></small>
1313
- </p>
1314
- <p>
1315
- <label for="<?php echo $this->get_field_id('link_before'); ?>"><?php _e('Before the Link Text:') ?></label>
1316
- <input id="<?php echo $this->get_field_id('link_before'); ?>" class="widefat" name="<?php echo $this->get_field_name('link_before'); ?>"
1317
- type="text" value="<?php echo $instance['link_before']; ?>" />
1318
- <small><em><?php _e( 'Text/HTML to go before the link text' ); ?></em></small>
1319
- </p>
1320
- <p>
1321
- <label for="<?php echo $this->get_field_id('link_after'); ?>"><?php _e('After the Link Text:') ?></label>
1322
- <input id="<?php echo $this->get_field_id('link_after'); ?>" class="widefat" name="<?php echo $this->get_field_name('link_after'); ?>"
1323
- type="text" value="<?php echo $instance['link_after']; ?>" />
1324
- <small><em><?php _e( 'Text/HTML to go after the link text' ); ?></em></small>
1325
- </p>
1326
- <?php $this->_close_a_field_section(); ?>
1327
-
1328
- <script type="text/javascript">jQuery(function($){$('#<?php echo $this->get_field_id('menu'); ?>').trigger('change');})</script>
1329
-
1330
- </div>
1331
-
1332
- <?php
1333
- } //end form()
1334
-
1335
- /**
1336
- * outputs the HTML to begin a collapsible/expandable group of settings
1337
- *
1338
- * @param array $instance
1339
- * @param string $text Label
1340
- * @param string $fname Field name
1341
- */
1342
- function _open_a_field_section( &$instance, $text, $fname ){
1343
- ?>
1344
- <div class="stuffbox widget-<?php echo $this->id_base; ?>-collapsible-fieldset" title="<?php _e( 'Click to show/hide' ); ?>">
1345
- <input id="<?php echo $this->get_field_id($fname); ?>" class="hidden-field" name="<?php echo $this->get_field_name($fname); ?>"
1346
- type="checkbox" value="1" <?php checked( $instance[$fname] ); ?> />
1347
- <div style="background-image:url(images/arrows.png);" class="<?php echo $instance[$fname] ? 'cmw-collapsed-fieldset' : ''; ?>"></div>
1348
- <h4><?php _e( $text ); ?></h4>
1349
- </div>
1350
- <div class="<?php echo $instance[$fname] ? 'cmw-start-fieldset-collapsed' : ''; ?>">
1351
- <?php
1352
- } //end _open_a_field_section()
1353
-
1354
- /**
1355
- * outputs the HTML to close off a collapsible/expandable group of settings
1356
- */
1357
- function _close_a_field_section(){
1358
- ?>
1359
- </div>
1360
- <?php
1361
- } //end _close_a_field_section()
1362
-
1363
- /**
1364
- * returns true if the version of WP is lower than 3.6 (ie. 3.5* or below)
1365
- */
1366
- function _pre_3point6(){
1367
- global $wp_version;
1368
-
1369
- return version_compare( strtolower( $wp_version ), '3.6a', '<' );
1370
- } //end _pre_3point6()
1371
-
1372
- } //end of class
1373
-
1374
- /**
1375
- * as of v1.2.0
1376
- * shortcode processing for [custom_menu_wizard option="" option="" ...]
1377
- * see wp-includes/widgets.php for the_widget() code
1378
- * Note that hide_empty is set to ON and can not be overridden!
1379
- *
1380
- * default (ie. no options) is:
1381
- * - show all
1382
- * - of first populated menu found (alphabetically)
1383
- * - from root, for unlimited depth
1384
- * - as hierarchical nested ULs inside a DIV.widget_custom_menu_wizard.shortcode_custom_menu_wizard
1385
- *
1386
- * @filters : custom_menu_wizard_shortcode_attributes array of attributes supplied to the shortcode
1387
- * custom_menu_wizard_shortcode_settings array of widget settings derived from the attributes
1388
- * custom_menu_wizard_shortcode_widget_args array of the sidebar args used to wrap widgets and their titles (before|after_widget, before|after_title)
1389
- *
1390
- * @param array $atts options supplied to the shortcode
1391
- * @param string $content Within start-end shortcode tags
1392
- * @param string $tag Shortcode tag
1393
- * @return string HTML
1394
- */
1395
- function custom_menu_wizard_widget_shortcode($atts, $content, $tag){
1396
- $html = '';
1397
- $ok = false;
1398
- $instance = shortcode_atts( array(
1399
- 'title' => '',
1400
- 'menu' => 0, // menu id, slug or name
1401
- //determines filter & filter_item ('items' takes precedence over 'children_of' because it's more specific)...
1402
- 'children_of' => '', // empty = show all (dep. on 'items'); menu item id or title (caseless), or current|current-item|parent|current-parent|root|current-ancestor
1403
- '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)
1404
- 'start_level' => 1,
1405
- 'depth' => 0, // 0 = unlimited
1406
- //only if children_of is (parent|current-parent|root|current-ancestor); determines fallback_no_ancestor, fallback_include_parent & fallback_include_parent_siblings...
1407
- 'fallback_parent' => 0, // 1 = use current-item; 'parent' = *and* include parent, 'siblings' = *and* include both parent and its siblings
1408
- //only if children_of is (current|current-item); determines fallback_no_children, fallback_nc_include_parent & fallback_nc_include_parent_siblings...
1409
- 'fallback_current' => 0, // 1 = use current-parent; 'parent' = *and* include parent (if available), 'siblings' = *and* include both parent (if available) and its siblings
1410
- //switches...
1411
- 'flat_output' => 0,
1412
- 'contains_current' => 0, // v2.0.0
1413
- //determines include_parent, include_parent_siblings & include_ancestors...
1414
- 'include' =>'', //comma|space|hyphen separated list of 'parent', 'siblings', 'ancestors'
1415
- 'ol_root' => 0,
1416
- 'ol_sub' => 0,
1417
- //determines title_from_parent & title_from_current...
1418
- 'title_from' => '', //comma|space|hyphen separated list of 'parent', 'current'
1419
- 'depth_rel_current' => 0, // v2.0.0
1420
- //strings...
1421
- 'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
1422
- 'container_id' => '',
1423
- 'container_class' => '',
1424
- 'menu_class' => 'menu-widget',
1425
- 'widget_class' => '',
1426
- //determines before & after...
1427
- 'wrap_link' => '', // a tag name (eg. div, p, span, etc)
1428
- //determines link_before & link_after...
1429
- 'wrap_link_text' => '' // a tag name (eg. span, em, strong)
1430
- ), $atts );
1431
-
1432
- $instance = apply_filters( 'custom_menu_wizard_shortcode_attributes', $instance );
1433
-
1434
- if( empty( $instance['menu'] ) ){
1435
- //gonna find the first menu (alphabetically) that has items...
1436
- $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
1437
- }else{
1438
- //allow for menu being something other than an id (eg. slug or name), but we need the id for the widget...
1439
- $menus = wp_get_nav_menu_object( $instance['menu'] );
1440
- if( !empty( $menus) ){
1441
- $menus = array( $menus );
1442
- }
1443
- }
1444
- if( !empty( $menus ) ){
1445
- foreach( $menus as $i=>$menu ){
1446
- $items = wp_get_nav_menu_items( $menu->term_id );
1447
- $ok = !empty( $items );
1448
- if( $ok ){
1449
- $instance['menu'] = $menu->term_id;
1450
- break;
1451
- }
1452
- }
1453
- }
1454
- unset( $menus );
1455
-
1456
- if( $ok ){
1457
- $instance['filter'] = $instance['filter_item'] = 0;
1458
- if( empty( $instance['items'] ) ){
1459
- //children_of => filter & filter_item...
1460
- if( empty( $instance['children_of'] ) ){
1461
- $instance['children_of'] = '';
1462
- }
1463
- switch( $instance['children_of'] ){
1464
- case '':
1465
- break;
1466
- case 'root': case 'current-ancestor':
1467
- --$instance['filter_item']; //ends up as -2
1468
- case 'parent': case 'current-parent':
1469
- --$instance['filter_item']; //ends up as -1
1470
- case 'current': case 'current-item':
1471
- $instance['filter'] = 1;
1472
- break;
1473
- default:
1474
- $instance['filter'] = 1;
1475
- $instance['filter_item'] = strtolower( $instance['children_of'] );
1476
- }
1477
- //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...
1478
- if( !is_numeric( $instance['filter_item'] ) ){
1479
- foreach( $items as $item ){
1480
- $ok = strtolower( $item->title ) == $instance['filter_item'];
1481
- if( $ok ){
1482
- $instance['filter_item'] = $item->ID;
1483
- break;
1484
  }
1485
  }
1486
- }
1487
- }else{
1488
- $instance['filter'] = -1;
1489
- }
1490
- unset( $instance['children_of'] );
1491
- }
1492
-
1493
- if( $ok ){
1494
- //fallback_parent => fallback_no_ancestor switch (and extension switches)...
1495
- $instance['fallback_no_ancestor'] = $instance['fallback_include_parent'] = $instance['fallback_include_parent_siblings'] = 0;
1496
- if( $instance['filter_item'] < 0 && !empty( $instance['fallback_parent'] ) ){
1497
- $instance['fallback_no_ancestor'] = 1;
1498
- $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_parent'] ), -1, PREG_SPLIT_NO_EMPTY );
1499
- foreach( $i as $j ){
1500
- if( $j == 'parent' ){
1501
- $instance['fallback_include_parent'] = 1;
1502
- }elseif( $j == 'siblings' ){
1503
- $instance['fallback_include_parent_siblings'] = 1;
1504
  }
1505
- }
1506
- }
1507
- //fallback_current => fallback_no_children switch (and extension switches)...
1508
- $instance['fallback_no_children'] = $instance['fallback_nc_include_parent'] = $instance['fallback_nc_include_parent_siblings'] = 0;
1509
- if( $instance['filter'] == 1 && $instance['filter_item'] == 0 && !empty( $instance['fallback_current'] ) ){
1510
- $instance['fallback_no_children'] = 1;
1511
- $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_current'] ), -1, PREG_SPLIT_NO_EMPTY );
1512
- foreach( $i as $j ){
1513
- if( $j == 'parent' ){
1514
- $instance['fallback_nc_include_parent'] = 1;
1515
- }elseif( $j == 'siblings' ){
1516
- $instance['fallback_nc_include_parent_siblings'] = 1;
1517
  }
1518
- }
1519
- }
1520
- unset( $instance['fallback_parent'], $instance['fallback_current'] );
1521
- //include => include_* ...
1522
- $instance['include_parent'] = $instance['include_parent_siblings'] = $instance['include_ancestors'] = 0;
1523
- if( $instance['filter'] == 1 && !empty( $instance['include'] ) ){
1524
- $i = preg_split( '/[\s,-]+/', strtolower( $instance['include'] ), -1, PREG_SPLIT_NO_EMPTY );
1525
- foreach( $i as $j ){
1526
- if( $j == 'parent' ){
1527
- $instance['include_parent'] = 1;
1528
- }elseif( $j == 'siblings' ){
1529
- $instance['include_parent_siblings'] = 1;
1530
- }elseif( $j == 'ancestors' ){
1531
- $instance['include_ancestors'] = 1;
1532
  }
 
 
 
1533
  }
1534
- }
1535
- unset( $instance['include'] );
1536
- //title_from => title_from_parent, title_from_current ...
1537
- $instance['title_from_parent'] = $instance['title_from_current'] = 0;
1538
- if( !empty( $instance['title_from'] ) ){
1539
- $i = preg_split( '/[\s,-]+/', strtolower( $instance['title_from'] ), -1, PREG_SPLIT_NO_EMPTY );
1540
- foreach( $i as $j ){
1541
- if( $j == 'parent' ){
1542
- $instance['title_from_parent'] = 1;
1543
- }elseif( $j == 'current' ){
1544
- $instance['title_from_current'] = 1;
1545
- }
 
 
 
 
1546
  }
1547
- }
1548
- unset( $instance['title_from'] );
1549
- //wrap_link => before & after...
1550
- $instance['before'] = $instance['after'] = '';
1551
- $instance['wrap_link'] = esc_attr( trim( $instance['wrap_link'] ) );
1552
- if( !empty( $instance['wrap_link'] ) ){
1553
- $instance['before'] = '<' . $instance['wrap_link'] . '>';
1554
- $instance['after'] = '</' . $instance['wrap_link'] . '>';
1555
- }
1556
- //wrap_link_text => link_before & link_after...
1557
- $instance['link_before'] = $instance['link_after'] = '';
1558
- $instance['wrap_link_text'] = esc_attr( trim( $instance['wrap_link_text'] ) );
1559
- if( !empty( $instance['wrap_link_text'] ) ){
1560
- $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
1561
- $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
1562
- }
1563
 
1564
- //handle widget_class here because we have full control over $before_widget...
1565
- $before_widget_class = array(
1566
- 'widget_custom_menu_wizard',
1567
- 'shortcode_custom_menu_wizard'
1568
- );
1569
- $instance['widget_class'] = empty( $instance['widget_class'] ) ? '' : esc_attr( trim ( $instance['widget_class'] ) );
1570
- if( !empty( $instance['widget_class'] ) ){
1571
- foreach( explode(' ', $instance['widget_class'] ) as $i ){
1572
- if( !empty( $i ) && !in_array( $i, $before_widget_class ) ){
1573
- $before_widget_class[] = $i;
1574
- }
1575
- }
1576
- }
1577
- $instance['widget_class'] = '';
1578
- //turn on hide_empty...
1579
- $instance['hide_empty'] = 1;
1580
- }
1581
 
1582
- if( $ok ){
1583
- //apart from before_title, these are lifted from the_widget()...
1584
- $sidebar_args = array(
1585
- 'before_widget' => '<div class="' . implode( ' ', $before_widget_class ) . '">',
1586
- 'after_widget' => '</div>',
1587
- 'before_title' => '<h2 class="widgettitle">',
1588
- 'after_title' => '</h2>'
1589
- );
1590
- ob_start();
1591
- the_widget(
1592
- 'Custom_Menu_Wizard_Widget',
1593
- apply_filters('custom_menu_wizard_shortcode_settings', $instance ),
1594
- apply_filters('custom_menu_wizard_shortcode_widget_args', $sidebar_args )
1595
- );
1596
- $html = ob_get_clean();
1597
- }
1598
- return empty($html) ? '' : $html;
1599
  }
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.0.0
7
  * Author: Roger Barrett
8
  * Author URI: http://www.wizzud.com/
9
  * License: GPL2+
10
  */
11
+ defined( 'ABSPATH' ) or exit();
12
  /*
13
+ * v3.0.0 change log
14
+ * - Major rethink/rewrite : the Children Of filter is now a Branch filter, and the selected menu item becomes the key focus point rather
15
+ * than its children. The Levels available for a Branch filter now include relative levels as well as absolute levels, and there are more
16
+ * options available for requiring that the menu contains the current menu item. With the exception of some anomalies (edge cases) the
17
+ * output achievable with v2 of the widget is still available with v3. Although there is no automatice upgrade available, v2 is still
18
+ * fully supported; however, any new instances of the widget will be created as v3 only. Note that the shortcode for v3 has changed to
19
+ * [cmwizard ... ], but, again, v2's [custom_menu_wizard] is still supported! NB: There is no separate 2.1.0 release - it is incorporated
20
+ * into this release.
21
+ * Simplest examples : "Children Of = Current Item" becomes "Branch = Current Item, Starting at +1 (children)"
22
+ * "Children Of = Current Item, with Include Parent" becomes "Branch = Current Item, Starting at the Current Item"
23
+ * - menu items can now be specifically excluded from the final output
24
+ * - ids for Items and Exclusions can be set to include all descendants
25
+ * - widget title can be set from the root of Branch or current item
26
+ * - the inclusion of branch ancestors, and optionally their siblings, can be set by absolute or relative level
27
+ * - compatibile with Widget Customizer plugin, and its implementation in WP 3.9 core
28
+ * - added title_tag to shortcode options
29
+ * - added findme to shortcode options, [cmwizard findme=1], output restricted to edit_pages capability
30
+ *
31
+ * v2.1.0 change log
32
+ * - fixed bug where duplicate menu item ids were causing elements to be ignored
33
+ * - fixed IE8 bug with levels indentation in assist
34
+ * - removed automatic selection of shortcode text (inconsistent cross-browser, so just triple click as usual; paste-as-text if possible!)
35
+ * - swapped the Items' checkboxes for clickable Ticks
36
+ * - tweaked dialog styling, and make more responsive to re-sizing
37
+ * - added collapsible menu structures to dialog, and set fixed position (with toggle back to absolute)
38
+ * - added utility to "assist" to locate posts containing a CMW shortcode
39
+ * - minimum requirement for WP raised to v3.6
40
+ *
41
  * v2.0.6 change log:
42
  * - modified determination of current item to cope better with multiple occurences (still first-found, but within prioritised groups)
43
  * - 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)
100
  * - moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript
101
  */
102
 
103
+ if( !class_exists( 'Custom_Menu_Wizard_Plugin' ) ){
 
 
 
 
 
 
 
 
 
104
 
105
+ //include the widget class and its walker...
106
+ include( plugin_dir_path( __FILE__ ) . 'include/class.widget.php' );
107
+ include( plugin_dir_path( __FILE__ ) . 'include/class.walker.php' );
 
 
 
 
 
 
 
 
 
 
 
108
 
109
+ //instantiate...
110
+ add_action( 'plugins_loaded', array( 'Custom_Menu_Wizard_Plugin', 'init' ) );
 
 
111
 
112
+ //declare the main plugin class...
113
+ class Custom_Menu_Wizard_Plugin {
114
+
115
+ public static $version = '3.0.0';
116
+ protected static $instance;
117
+
118
+ /**
119
+ * constructor : adds actions
120
+ */
121
+ public function __construct(){
122
+
123
+ add_action( 'widgets_init', array( &$this, 'widget_and_shortcode' ) );
124
+ add_action( 'wp_ajax_cmw-find-shortcodes', array( &$this, 'ajax_find_shortcodes' ) );
125
+ add_action( 'admin_print_styles-widgets.php', array( &$this, 'enqueue_styles' ) );
126
+ add_action( 'admin_print_scripts-widgets.php', array( &$this, 'enqueue_scripts' ) );
127
+ add_action( 'admin_menu', array( &$this, 'admin_menu' ) );
128
+ //add customizer support...
129
+ add_action( 'customize_controls_enqueue_scripts', array( &$this, 'enqueue_styles' ) );
130
+ add_action( 'customize_controls_enqueue_scripts', array( &$this, 'enqueue_scripts' ) );
131
+
132
+ } //end __construct()
133
 
134
+ /**
135
+ * hooked into plugins_loaded action : creates the plugin instance
136
+ */
137
+ public static function init(){
138
+
139
+ is_null( self::$instance ) && self::$instance = new self;
140
+ return self::$instance;
141
 
142
+ } //end init()
 
 
 
 
 
 
 
143
 
144
+ /**
145
+ * hooked into admin_menu action : add action for when an update to this plugin is available
146
+ */
147
+ public function admin_menu(){
148
 
149
+ add_action( 'in_plugin_update_message-' . plugin_basename( __FILE__ ), array( &$this, 'update_message' ), 10, 2 );
150
+
151
+ } //end admin_menu()
152
+
153
+ /**
154
+ * hooked into admin_print_scripts-widgets.php & customize_controls_enqueue_scripts actions : queues scripts needed by the plugin
155
+ */
156
+ public function enqueue_scripts(){
157
 
158
+ $min = defined( 'WP_DEBUG' ) && WP_DEBUG ? '' : '.min';
159
+ wp_enqueue_script( 'custom-menu-wizard-plugin-script', plugins_url( "/custom-menu-wizard$min.js", __FILE__ ), array('jquery-ui-dialog'), self::$version );
160
+
161
+ } //end enqueue_scripts()
162
+
163
+ /**
164
+ * hooked into admin_print_styles-widgets.php & customize_controls_enqueue_scripts actions : queues styles needed by the plugin
165
+ */
166
+ public function enqueue_styles(){
167
+ global $wp_scripts;
168
+
169
+ wp_enqueue_style( 'custom-menu-wizard-plugin-styles', plugins_url( '/custom-menu-wizard.css', __FILE__ ), array(), self::$version );
170
+ //if there's no jquery-ui style already registered, register Smoothness...
171
+ if( !wp_style_is( 'jquery-ui', 'registered' ) ) {
172
+ //get the jquery ui core version (default to 1.9.2 if not found)...
173
+ $jquery_ui_version = isset( $wp_scripts->registered['jquery-ui-core']->ver ) ? $wp_scripts->registered['jquery-ui-core']->ver : '1.9.2';
174
+ //register Smoothness theme for the determined ui version...
175
+ // wp_register_style( 'jquery-ui', '//ajax.googleapis.com/ajax/libs/jqueryui/' . $jquery_ui_version . '/themes/smoothness/jquery-ui.css' );
176
+ wp_register_style( 'jquery-ui', '//code.jquery.com/ui/' . $jquery_ui_version . '/themes/smoothness/jquery-ui.css' );
177
+ }
178
+ wp_enqueue_style( 'jquery-ui' );
179
+
180
+ } //end enqueue_styles()
181
+
182
+ /**
183
+ * hooked into in_plugin_update_message-custom-menu-wizard action : request read changelog before updating
184
+ * @param array $plugin_data Plugin metadata
185
+ * @param array $r Metadata about the available plugin update
186
+ */
187
+ public function update_message( $plugin_data, $r ){
188
+
189
+ $url = 'http://wordpress.org/plugins/' . $r->slug. '/changelog/';
190
+ $style = implode( ';', array(
191
+ '-webkit-box-sizing:border-box',
192
+ '-moz-box-sizing:border-box',
193
+ 'box-sizing:border-box',
194
+ 'background-color:#D54E21',
195
+ 'border-radius:2px',
196
+ 'color:#FFFFFF',
197
+ 'display:inline-block',
198
+ 'margin:0',
199
+ 'max-width:100%',
200
+ 'overflow:hidden',
201
+ 'padding:0 0.5em',
202
+ 'text-overflow:ellipsis',
203
+ 'text-shadow:0 1px 0 rgba(0, 0, 0, 0.5)',
204
+ 'vertical-align:text-bottom',
205
+ 'white-space:nowrap'
206
+ ) ) . ';';
207
 
208
+ ?>
209
+ <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>
210
+ <?php
 
 
 
 
 
 
 
 
211
 
212
+ } //end update_message()
 
 
 
 
 
 
 
 
 
213
 
214
+ /**
215
+ * hooked into wp_ajax_cmw-find-shortcodes action : handle ajax request to find posts containing CMW shortcodes
216
+ */
217
+ public function ajax_find_shortcodes(){
218
+
219
+ check_admin_referer( 'cmw-find-shortcodes' );
220
+ $response = array(
221
+ 'what' => 'cmw_find_shortcodes',
222
+ 'action' => 'list_posts',
223
+ 'id' => '1',
224
+ 'data' => $this->find_shortcodes()
225
+ );
226
+ $xmlResponse = new WP_Ajax_Response($response);
227
+ $xmlResponse->send();
228
 
229
+ }
230
+
231
+ /**
232
+ * list any post that contains a CMW shortcode; can be called from a shortcode or via an ajax call
233
+ *
234
+ * @param array $shortcodeInst Array of shortcode attributes
235
+ * @return string HTML
236
+ */
237
 
238
+ public function find_shortcodes( $shortcodeInst = false ){
239
+ global $wpdb;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
+ $html = '';
 
 
 
 
 
 
 
 
242
 
243
+ //from a shortcode, the user must have edit_pages capability (implies Editor or above)...
244
+ if( $shortcodeInst !== false && !current_user_can( 'edit_pages' ) ){
245
+ return $html;
246
+ }
247
 
248
+ $codes = array(
249
+ 'cmw-demo-found-old' => '[custom_menu_wizard',
250
+ 'cmw-demo-found-new' => '[cmwizard'
251
+ );
252
+ foreach( $codes as $k => $v ){
253
+ $j = str_replace( '-', '_', $k );
254
+ $$j = '%' . like_escape( esc_sql ( $v ) ) . '%';
255
+ }
 
 
 
 
 
256
 
257
+ //search in all custom fields...
258
+ $sql = "SELECT DISTINCT post_id FROM {$wpdb->postmeta}";
259
+ $sql .= " WHERE meta_value LIKE '%s' OR meta_value LIKE '%s'";
260
+ $post_ids_meta = $wpdb->get_col( $wpdb->prepare( $sql, $cmw_demo_found_old, $cmw_demo_found_new ) );
261
+ //search in post_content...
262
+ $sql = "SELECT DISTINCT ID FROM {$wpdb->posts}";
263
+ $sql .= " WHERE post_content LIKE '%s' OR post_content LIKE '%s'";
264
+ $post_ids_post = $wpdb->get_col( $wpdb->prepare( $sql, $cmw_demo_found_old, $cmw_demo_found_new ) );
265
 
266
+ $post_ids = array_merge( $post_ids_meta, $post_ids_post );
267
 
268
+ if( empty( $post_ids ) ){
269
+ $html .= '<p>' . __('No CMW shortcodes found.') . '</p>';
270
+ }else{
271
+ $args = array(
272
+ 'ignore_sticky_posts' => true,
273
+ 'nopaging' => true,
274
+ 'orderby' => 'date',
275
+ 'post_type' => 'any',
276
+ 'post_status' => array( 'publish', 'draft', 'future', 'pending', 'private' ),
277
+ 'post__in' => $post_ids
278
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
+ $the_query = new WP_Query( $args );
281
+ if( $the_query->have_posts() ){
282
+ $html .= '<dl>';
283
+ while( $the_query->have_posts() ){
284
+ $the_query->the_post();
285
+ $id = get_the_ID();
286
+ $inPost = in_array( $id, $post_ids_post );
287
+ $inMeta = in_array( $id, $post_ids_meta );
288
+ $dtClass = array();
289
+ $anchorTitle = array();
290
+ if( $inPost ){
291
+ $content = get_the_content();
292
+ foreach( $codes as $k => $v ){
293
+ if( strpos( $content, $v ) !== false ){
294
+ $dtClass[ $k ] = 1;
295
+ $anchorTitle[ $v . ']' ] = 1;
296
+ }
297
+ }
298
+ }
299
+ if( $inMeta ){
300
+ $content = get_post_meta( $id );
301
+ foreach( $content as $k => $v ){
302
+ $content[ $k ] = implode(' ', $v );
303
+ }
304
+ $content = implode( ' ', $content );
305
+ foreach( $codes as $k => $v ){
306
+ if( strpos( $content, $v ) !== false ){
307
+ $dtClass[ $k ] = 1;
308
+ $anchorTitle[ $v . ']' ] = 1;
309
+ }
310
+ }
311
+ }
312
+ $anchorTarget = get_post_type( $id );
313
+ if( empty( $anchorTarget ) ){
314
+ $anchorTarget = __('unknown type');
315
+ }else{
316
+ $anchorTarget = (string)$anchorTarget;
317
+ }
318
+ $content = $inPost ? ( $inMeta ? __( 'content+meta' ) : __( 'content' ) ) : __( 'meta' );
319
+ $anchorTitle = $anchorTarget . ' #' . $id . ', ' . $content . ', ' . implode( __(' and '), array_keys( $anchorTitle ) );
320
+ $anchorTarget = '';
321
+ if( $shortcodeInst === false ){
322
+ //is from assistant via ajax...
323
+ $anchorTarget = 'target="_blank"';
324
+ $anchorTitle .= ' ... ' . __('opens new tab/window');
325
+ }
326
+ $html .= '<dt class="' . implode( ' ', array_keys( $dtClass ) ) . '"><a href="' . get_permalink() . '" ' . $anchorTarget . ' title="' . $anchorTitle . '">' . get_the_title() . '</a></dt>';
327
  }
328
+ $html .= '</dl>';
329
+ }else{
330
+ $html .= '<p>' . __('No CMW shortcodes found.') . '</p>';
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  }
 
332
 
333
+ wp_reset_postdata();
 
 
 
 
334
  }
335
 
336
+ //if originator is shortcode, put a simple wrapper (no styling!) around the results, and optionally an H3 title...
337
+ if( $shortcodeInst !== false ){
338
+ $anchorTitle = is_array( $shortcodeInst ) && !empty( $shortcodeInst['title'] ) ? esc_attr( strip_tags( trim( $shortcodeInst['title'] ) ) ) : '';
339
+ $anchorTitle = empty( $anchorTitle ) ? '' : '<h3>' . $anchorTitle . '</h3>';
340
+ $html = '<div class="cmw-list-posts-with-shortcodes">' . $anchorTitle . $html . '</div>';
 
341
  }
342
 
343
+ return $html;
344
+
345
+ } //end find_shortcodes()
346
+
347
+ /**
348
+ * hooked into widgets_init action : registers widget and adds shortcode(s)
349
+ */
350
+ public function widget_and_shortcode(){
351
+
352
+ //register the widget class...
353
+ register_widget( 'Custom_Menu_Wizard_Widget' );
354
+ //add shortcode...
355
+ add_shortcode( 'cmwizard', array( &$this, 'shortcode' ) );
356
+ //add shortcode, v2.1.0 version (deprecated!)...
357
+ add_shortcode( 'custom_menu_wizard', array( &$this, 'shortcode_legacy' ) );
 
 
 
 
358
 
359
+ } //end widget_and_shortcode()
360
+
361
+ /**
362
+ * shortcode processing for [cmwizard option="" option="" ...] (as of v3.0.0)
363
+ *
364
+ * see wp-includes/widgets.php for the_widget() code
365
+ * Note that hide_empty is set to ON and can not be overridden!
366
+ *
367
+ * differences from [custom_menu_wizard] shortcode (ie. v2.1.0)
368
+ * deprecated:
369
+ * - children_of : now branch, and limited to current[-item] or digits; parent|current-parent|root|current-ancestor all require conversion
370
+ * - start_level : now level (integer) for a by-level filter, or start_at (string) for a by-branch filter (determining branch_start)
371
+ * changed:
372
+ * - contains_current : was a switch, now a string (empty or menu|primary|secondary|output); switch ON = 'output'
373
+ * - include : now accepts siblings, ancestors and/or ancestor-siblings (csv); parent is gone, and hyphen separator no longer allowed
374
+ * - title_from : should now be csv, hyphen separator no longer allowed
375
+ * added:
376
+ * - title_tag & findme
377
+ *
378
+ * default (ie. no options) is:
379
+ * - show all
380
+ * - of first populated menu found (alphabetically)
381
+ * - from root, for unlimited depth
382
+ * - as hierarchical nested ULs inside a DIV.widget_custom_menu_wizard.shortcode_custom_menu_wizard
383
+ *
384
+ * @filters : custom_menu_wizard_shortcode_attributes array of attributes (unfiltered!) supplied to the shortcode
385
+ * shortcode_atts_cmwizard array of output attributes, array of supported attributes, array of supplied attributes
386
+ * custom_menu_wizard_shortcode_settings array of widget settings derived from the attributes
387
+ * custom_menu_wizard_shortcode_widget_args array of the sidebar args used to wrap widgets and their titles (before|after_widget, before|after_title)
388
+ * 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'
389
+ *
390
+ * @param array $atts options supplied to the shortcode
391
+ * @param string $content Within start-end shortcode tags
392
+ * @param string $tag Shortcode tag
393
+ * @return string HTML that comes from running the_widget()
394
+ */
395
+ public function shortcode($atts, $content, $tag){
396
+
397
+ $html = '';
398
+ $ok = false;
399
+
400
+ // NB csv = comma or space separated list...
401
+ $instance = shortcode_atts( array(
402
+ 'title' => '',
403
+ 'menu' => 0, // menu id, slug or name
404
+ 'level' => 0,
405
+ //determines filter (in conjunction with items)...
406
+ 'branch' => 0, // a menu item id, or current|current-item
407
+ //determines filter (in conjunction with branch)...
408
+ 'items' => '', // csv of menu item ids (an id may have a '+' appended, for inheritance, eg. '23+')
409
+ 'depth' => 0, // 0 = unlimited
410
+ 'depth_rel_current' => 0,
411
+ //determines branch_start...
412
+ 'start_at' => '',
413
+ 'start_mode' => '', // 'level' or empty
414
+ 'allow_all_root' => 0,
415
+ //inclusions...
416
+ 'ancestors' => 0, //integer (negative = relative)
417
+ 'ancestor_siblings' => 0, //integer (negative = relative)
418
+ 'include_root' => 0, //switch (means *all* root items!)
419
+ 'siblings' => 0, //switch
420
+ //exclusions...
421
+ 'exclude' => '', // csv of menu item ids (an id may have a '+' appended, for inheritance, eg. '23+')
422
+ 'exclude_level' => '', // digit, possibly appended with a '+' or '-', eg. '2', '2+', or '2-'
423
+ 'contains_current' => '', // menu|primary|secondary|output
424
+ //determines fallback (current|parent|quit) and, optionally, fallback_siblings and/or fallback_depth...
425
+ 'fallback' => '', //eg. 'quit', or 'current' or 'current+siblings' or 'parent+siblings,2' or 'parent,1'
426
+ //switches...
427
+ 'flat_output' => 0,
428
+ //determines title_from_[branch|current|branch-root|current-root]...
429
+ 'title_from' => '', // csv of branch|current|branch-root|current-root
430
+ 'ol_root' => 0,
431
+ 'ol_sub' => 0,
432
+ //strings...
433
+ 'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
434
+ 'container_id' => '',
435
+ 'container_class' => '',
436
+ 'menu_class' => 'menu-widget',
437
+ 'widget_class' => '',
438
+ //determines before & after...
439
+ 'wrap_link' => '', // a tag name (eg. div, p, span, etc)
440
+ //determines link_before & link_after...
441
+ 'wrap_link_text' => '', // a tag name (eg. span, em, strong)
442
+ //modifies the before/after_title $sidebar_args, changing the default H2 tag to something else(?)...
443
+ 'title_tag' => '', // a tag name (eg. h1, h3, etc)
444
+ //utility : doesn't run widget! instead, lists all posts/pages that contain a CMW shortcode...
445
+ 'findme' => 0
446
+ ),
447
+ apply_filters(
448
+ 'custom_menu_wizard_shortcode_attributes',
449
+ array_merge( (array)$atts, array('cmwv' => self::$version) )
450
+ ),
451
+ $tag // since WP3.6 this allows use of shortcode_atts_cmwizard filter, applied by shortcode_atts()
452
+ );
453
+
454
+ if( !empty( $instance['findme'] ) ){
455
+ return $this->find_shortcodes( $instance );
456
+ }
457
+
458
+ //in order of priority...
459
+ $byItems = !empty( $instance['items'] );
460
+ $byBranch = !$byItems && !empty( $instance['branch'] );
461
+ $byLevel = !$byItems && !$byBranch;
462
+
463
+ if( empty( $instance['menu'] ) ){
464
+ //gonna find the first menu (alphabetically) that has items...
465
+ $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
466
+ }else{
467
+ //allow for menu being something other than an id (eg. slug or name), but we need the id for the widget...
468
+ $menus = wp_get_nav_menu_object( $instance['menu'] );
469
+ if( !empty( $menus) ){
470
+ $menus = array( $menus );
471
  }
472
+ }
473
+ if( !empty( $menus ) ){
474
+ foreach( $menus as $i=>$menu ){
475
+ $items = wp_get_nav_menu_items( $menu->term_id );
476
+ $ok = !empty( $items );
477
+ if( $ok ){
478
+ $instance['menu'] = $menu->term_id;
479
+ break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
  }
481
  }
482
  }
483
+ unset( $menus );
484
 
485
+ if( $ok ){
486
+ if( $byItems ){
487
+ $instance['filter'] = 'items';
488
+ }
489
+ if( $byBranch ){
490
+ $instance['filter'] = 'branch';
491
+ switch( "{$instance['start_at']}" ){
492
+ case '0':
493
+ case 'branch': $instance['branch_start'] = ''; break;
494
+ case 'root' : $instance['branch_start'] = '1'; break;
495
+ case 'children': $instance['branch_start'] = '+1'; break;
496
+ case 'parent': $instance['branch_start'] = '-1'; break;
497
+ default: $instance['branch_start'] = "{$instance['start_at']}";
 
 
 
 
498
  }
499
+ if( $instance['branch'] == 'current' || $instance['branch'] == 'current-item' ){
500
+ $instance['branch'] = 0;
501
+ }elseif( !is_numeric( $instance['branch'] ) ){
502
+ //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...
503
+ $instance['branch'] = strtolower( $instance['branch'] );
504
+ foreach( $items as $item ){
505
+ $ok = strtolower( $item->title ) == $instance['branch'];
506
+ if( $ok ){
507
+ $instance['branch'] = $item->ID;
508
+ break;
509
+ }
510
  }
511
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  }
513
+ if( $byLevel ){
514
+ $instance['filter'] = '';
515
+ $instance['level'] = max(1, intval( $instance['level'] ) );
516
+ }
517
+ unset( $instance['start_at'] );
518
+ }
519
 
520
+ if( $ok ){
521
+ //fallback => fallback and fallback_siblings and fallback_depth...
522
+ //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)
523
+ //but "quit", "current" or "parent" must be present (others are optional)
524
+ if( $byBranch && empty( $instance['branch'] ) && !empty( $instance['fallback'] ) ){
525
+ $i = preg_split( '/[\s,]+/', strtolower( $instance['fallback'] ), -1, PREG_SPLIT_NO_EMPTY );
526
+ $instance['fallback'] = '';
527
+ if( in_array( 'quit', $i ) ){
528
+ $instance['fallback'] = 'quit';
529
+ }elseif( in_array( 'parent', $i ) ){
530
+ $instance['fallback'] = 'parent';
531
+ }elseif( in_array( 'current', $i ) ){
532
+ $instance['fallback'] = 'current';
533
+ }
534
+ if( !empty( $instance['fallback'] ) && $instance['fallback'] != 'quit' ){
535
+ if( in_array( '+siblings', $i ) ){
536
+ $instance['fallback_siblings'] = 1;
 
537
  }
538
+ $i = array_diff( $i, array( 'quit', 'parent', 'current', '+siblings' ) );
539
+ if( !empty( $i ) ){
540
+ foreach( $i as $v ){
541
+ $v = trim( $v );
542
+ if( preg_match( '/^\d+$/', $v ) > 0 && intval( $v ) > 0 ){
543
+ $instance['fallback_depth'] = intval( $v );
544
+ break;
545
  }
546
  }
547
  }
549
  }
550
  }
551
 
552
+ if( $ok ){
553
+ //title_from => title_from_...
554
+ if( !empty( $instance['title_from'] ) ){
555
+ $i = preg_split( '/[\s,]+/', strtolower( $instance['title_from'] ), -1, PREG_SPLIT_NO_EMPTY );
556
+ foreach( $i as $j ){
557
+ if( $j == 'branch' || $j == 'current' ){
558
+ $instance[ 'title_from_' . $j ] = 1;
559
+ }elseif( $j == 'branch-root' || $j == 'current-root' ){
560
+ $instance[ 'title_from_' . str_replace( '-', '_', $j ) ] = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
561
  }
562
  }
563
  }
564
+ unset( $instance['title_from'] );
565
 
566
+ //wrap_link => before & after...
567
+ $instance['wrap_link'] = esc_attr( trim( $instance['wrap_link'] ) );
568
+ if( !empty( $instance['wrap_link'] ) ){
569
+ $instance['before'] = '<' . $instance['wrap_link'] . '>';
570
+ $instance['after'] = '</' . $instance['wrap_link'] . '>';
 
 
 
 
 
 
571
  }
572
+ unset( $instance['wrap_link'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
573
 
574
+ //wrap_link_text => link_before & link_after...
575
+ $instance['wrap_link_text'] = esc_attr( trim( $instance['wrap_link_text'] ) );
576
+ if( !empty( $instance['wrap_link_text'] ) ){
577
+ $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
578
+ $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
 
 
 
 
 
 
579
  }
580
+ unset( $instance['wrap_link_text'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
 
582
+ //handle widget_class here because we have full control over $before_widget...
583
+ $before_widget_class = array(
584
+ 'widget_custom_menu_wizard',
585
+ 'shortcode_custom_menu_wizard'
586
+ );
587
+ $instance['widget_class'] = empty( $instance['widget_class'] ) ? '' : esc_attr( trim ( $instance['widget_class'] ) );
588
  if( !empty( $instance['widget_class'] ) ){
589
+ foreach( explode(' ', $instance['widget_class'] ) as $i ){
590
+ if( !empty( $i ) && !in_array( $i, $before_widget_class ) ){
591
+ $before_widget_class[] = $i;
592
+ }
593
+ }
 
 
 
 
 
 
 
 
 
 
 
 
594
  }
595
+ unset( $instance['widget_class'] );
596
 
597
+ //turn on hide_empty...
598
+ $instance['hide_empty'] = 1;
599
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
 
601
+ if( $ok ){
602
+ //not used by the plugin, but could be used in the widget code to tell whether it was being
603
+ //run as a result of a widget or a shortcode?...
604
+ $instance['shortcode'] = true;
605
+ //allow the element that wraps the widget title to be changed from an h2 (the WP default) to another tag...
606
+ //note : does not allow for changing the class, or for removing the wrapping element
607
+ // for a class override, add CSS rule for
608
+ // .shortcode_custom_menu_wizard .widgettitle { ..... }
609
+ // can also be overriden using the 'custom_menu_wizard_shortcode_widget_args' filter (applied below)
610
+ $instance['title_tag'] = esc_attr( trim( $instance['title_tag'] ) );
611
+ if( empty( $instance['title_tag'] ) ){
612
+ //default to H2...
613
+ $instance['title_tag'] = 'h2';
614
  }
615
+ //apart from before_widget, these are lifted from the_widget() (wp-includes/widgets.php)...
616
+ $sidebar_args = array(
617
+ 'before_widget' => '<div class="' . implode( ' ', $before_widget_class ) . '">',
618
+ 'after_widget' => '</div>',
619
+ 'before_title' => '<' . $instance['title_tag'] . ' class="widgettitle">',
620
+ 'after_title' => '</' . $instance['title_tag'] . '>'
621
+ );
622
+ unset( $instance['title_tag'] );
623
+
624
+ ob_start();
625
+ the_widget(
626
+ 'Custom_Menu_Wizard_Widget',
627
+ apply_filters(
628
+ 'custom_menu_wizard_shortcode_settings',
629
+ array_merge( $instance, array('cmwv' => self::$version) )
630
+ ),
631
+ apply_filters(
632
+ 'custom_menu_wizard_shortcode_widget_args',
633
+ array_merge( $sidebar_args, array('cmwv' => self::$version) )
634
+ ) );
635
+ $html = ob_get_clean();
636
+ }
637
+ return empty($html) ? '' : $html;
638
+
639
+ } //end shortcode()
640
+
641
+ /**
642
+ * shortcode processing for [custom_menu_wizard option="" option="" ...] (as of v2.1.0)
643
+ * see wp-includes/widgets.php for the_widget() code
644
+ * Note that hide_empty is set to ON and can not be overridden!
645
+ *
646
+ * default (ie. no options) is:
647
+ * - show all
648
+ * - of first populated menu found (alphabetically)
649
+ * - from root, for unlimited depth
650
+ * - as hierarchical nested ULs inside a DIV.widget_custom_menu_wizard.shortcode_custom_menu_wizard
651
+ *
652
+ * @filters : custom_menu_wizard_shortcode_attributes array of attributes supplied to the shortcode
653
+ * custom_menu_wizard_shortcode_settings array of widget settings derived from the attributes
654
+ * custom_menu_wizard_shortcode_widget_args array of the sidebar args used to wrap widgets and their titles (before|after_widget, before|after_title)
655
+ *
656
+ * @param array $atts options supplied to the shortcode
657
+ * @param string $content Within start-end shortcode tags
658
+ * @param string $tag Shortcode tag
659
+ * @return string HTML that comes from running the_widget()
660
+ */
661
+ function shortcode_legacy($atts, $content, $tag){
662
+ $html = '';
663
+ $ok = false;
664
+ $instance = shortcode_atts( array(
665
+ 'title' => '',
666
+ 'menu' => 0, // menu id, slug or name
667
+ //determines filter & filter_item ('items' takes precedence over 'children_of' because it's more specific)...
668
+ 'children_of' => '', // empty = show all (dep. on 'items'); menu item id or title (caseless), or current|current-item|parent|current-parent|root|current-ancestor
669
+ '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)
670
+ 'start_level' => 1,
671
+ 'depth' => 0, // 0 = unlimited
672
+ //only if children_of is (parent|current-parent|root|current-ancestor); determines fallback_no_ancestor, fallback_include_parent & fallback_include_parent_siblings...
673
+ 'fallback_parent' => 0, // 1 = use current-item; 'parent' = *and* include parent, 'siblings' = *and* include both parent and its siblings
674
+ //only if children_of is (current|current-item); determines fallback_no_children, fallback_nc_include_parent & fallback_nc_include_parent_siblings...
675
+ 'fallback_current' => 0, // 1 = use current-parent; 'parent' = *and* include parent (if available), 'siblings' = *and* include both parent (if available) and its siblings
676
+ //switches...
677
+ 'flat_output' => 0,
678
+ 'contains_current' => 0, // v2.0.0
679
+ //determines include_parent, include_parent_siblings & include_ancestors...
680
+ 'include' =>'', //comma|space|hyphen separated list of 'parent', 'siblings', 'ancestors'
681
+ 'ol_root' => 0,
682
+ 'ol_sub' => 0,
683
+ //determines title_from_parent & title_from_current...
684
+ 'title_from' => '', //comma|space|hyphen separated list of 'parent', 'current'
685
+ 'depth_rel_current' => 0, // v2.0.0
686
+ //strings...
687
+ 'container' => 'div', // a tag : div|nav are WP restrictions, not the widget's; '' = no container
688
+ 'container_id' => '',
689
+ 'container_class' => '',
690
+ 'menu_class' => 'menu-widget',
691
+ 'widget_class' => '',
692
+ //determines before & after...
693
+ 'wrap_link' => '', // a tag name (eg. div, p, span, etc)
694
+ //determines link_before & link_after...
695
+ 'wrap_link_text' => '' // a tag name (eg. span, em, strong)
696
+ ),
697
+ $atts,
698
+ $tag // since WP3.6 this allows use of shortcode_atts_custom_menu_wizard filter, applied by shortcode_atts()
699
+ );
700
 
701
+ $instance = apply_filters( 'custom_menu_wizard_shortcode_attributes', $instance );
 
702
 
703
+ if( empty( $instance['menu'] ) ){
704
+ //gonna find the first menu (alphabetically) that has items...
705
+ $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
706
+ }else{
707
+ //allow for menu being something other than an id (eg. slug or name), but we need the id for the widget...
708
+ $menus = wp_get_nav_menu_object( $instance['menu'] );
709
+ if( !empty( $menus) ){
710
+ $menus = array( $menus );
711
  }
712
+ }
713
+ if( !empty( $menus ) ){
714
+ foreach( $menus as $i=>$menu ){
715
+ $items = wp_get_nav_menu_items( $menu->term_id );
716
+ $ok = !empty( $items );
717
+ if( $ok ){
718
+ $instance['menu'] = $menu->term_id;
719
+ break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
720
  }
 
721
  }
722
  }
723
+ unset( $menus );
 
 
 
 
 
 
 
724
 
725
+ if( $ok ){
726
+ $instance['filter'] = $instance['filter_item'] = 0;
727
+ if( empty( $instance['items'] ) ){
728
+ //children_of => filter & filter_item...
729
+ if( empty( $instance['children_of'] ) ){
730
+ $instance['children_of'] = '';
731
+ }
732
+ switch( $instance['children_of'] ){
733
+ case '':
734
+ break;
735
+ case 'root': case 'current-ancestor':
736
+ --$instance['filter_item']; //ends up as -2
737
+ case 'parent': case 'current-parent':
738
+ --$instance['filter_item']; //ends up as -1
739
+ case 'current': case 'current-item':
740
+ $instance['filter'] = 1;
741
+ break;
742
+ default:
743
+ $instance['filter'] = 1;
744
+ $instance['filter_item'] = strtolower( $instance['children_of'] );
745
+ }
746
+ //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...
747
+ if( !is_numeric( $instance['filter_item'] ) ){
748
+ foreach( $items as $item ){
749
+ $ok = strtolower( $item->title ) == $instance['filter_item'];
750
+ if( $ok ){
751
+ $instance['filter_item'] = $item->ID;
752
+ break;
753
+ }
754
+ }
755
+ }
756
+ }else{
757
+ $instance['filter'] = -1;
758
  }
759
+ unset( $instance['children_of'] );
760
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
761
 
762
+ if( $ok ){
763
+ //fallback_parent => fallback_no_ancestor switch (and extension switches)...
764
+ $instance['fallback_no_ancestor'] = $instance['fallback_include_parent'] = $instance['fallback_include_parent_siblings'] = 0;
765
+ if( $instance['filter_item'] < 0 && !empty( $instance['fallback_parent'] ) ){
766
+ $instance['fallback_no_ancestor'] = 1;
767
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_parent'] ), -1, PREG_SPLIT_NO_EMPTY );
768
+ foreach( $i as $j ){
769
+ if( $j == 'parent' ){
770
+ $instance['fallback_include_parent'] = 1;
771
+ }elseif( $j == 'siblings' ){
772
+ $instance['fallback_include_parent_siblings'] = 1;
773
+ }
774
+ }
775
  }
776
+ //fallback_current => fallback_no_children switch (and extension switches)...
777
+ $instance['fallback_no_children'] = $instance['fallback_nc_include_parent'] = $instance['fallback_nc_include_parent_siblings'] = 0;
778
+ if( $instance['filter'] == 1 && $instance['filter_item'] == 0 && !empty( $instance['fallback_current'] ) ){
779
+ $instance['fallback_no_children'] = 1;
780
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['fallback_current'] ), -1, PREG_SPLIT_NO_EMPTY );
781
+ foreach( $i as $j ){
782
+ if( $j == 'parent' ){
783
+ $instance['fallback_nc_include_parent'] = 1;
784
+ }elseif( $j == 'siblings' ){
785
+ $instance['fallback_nc_include_parent_siblings'] = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
786
  }
 
 
 
 
 
 
 
787
  }
788
  }
789
+ unset( $instance['fallback_parent'], $instance['fallback_current'] );
790
+ //include => include_* ...
791
+ $instance['include_parent'] = $instance['include_parent_siblings'] = $instance['include_ancestors'] = 0;
792
+ if( $instance['filter'] == 1 && !empty( $instance['include'] ) ){
793
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['include'] ), -1, PREG_SPLIT_NO_EMPTY );
794
+ foreach( $i as $j ){
795
+ if( $j == 'parent' ){
796
+ $instance['include_parent'] = 1;
797
+ }elseif( $j == 'siblings' ){
798
+ $instance['include_parent_siblings'] = 1;
799
+ }elseif( $j == 'ancestors' ){
800
+ $instance['include_ancestors'] = 1;
801
  }
802
  }
 
 
 
 
803
  }
804
+ unset( $instance['include'] );
805
+ //title_from => title_from_parent, title_from_current ...
806
+ $instance['title_from_parent'] = $instance['title_from_current'] = 0;
807
+ if( !empty( $instance['title_from'] ) ){
808
+ $i = preg_split( '/[\s,-]+/', strtolower( $instance['title_from'] ), -1, PREG_SPLIT_NO_EMPTY );
809
+ foreach( $i as $j ){
810
+ if( $j == 'parent' ){
811
+ $instance['title_from_parent'] = 1;
812
+ }elseif( $j == 'current' ){
813
+ $instance['title_from_current'] = 1;
814
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
815
  }
816
  }
817
+ unset( $instance['title_from'] );
818
+ //wrap_link => before & after...
819
+ $instance['before'] = $instance['after'] = '';
820
+ $instance['wrap_link'] = esc_attr( trim( $instance['wrap_link'] ) );
821
+ if( !empty( $instance['wrap_link'] ) ){
822
+ $instance['before'] = '<' . $instance['wrap_link'] . '>';
823
+ $instance['after'] = '</' . $instance['wrap_link'] . '>';
 
 
 
 
 
 
 
 
 
 
 
824
  }
825
+ //wrap_link_text => link_before & link_after...
826
+ $instance['link_before'] = $instance['link_after'] = '';
827
+ $instance['wrap_link_text'] = esc_attr( trim( $instance['wrap_link_text'] ) );
828
+ if( !empty( $instance['wrap_link_text'] ) ){
829
+ $instance['link_before'] = '<' . $instance['wrap_link_text'] . '>';
830
+ $instance['link_after'] = '</' . $instance['wrap_link_text'] . '>';
 
 
 
 
 
 
831
  }
832
+
833
+ //handle widget_class here because we have full control over $before_widget...
834
+ $before_widget_class = array(
835
+ 'widget_custom_menu_wizard',
836
+ 'shortcode_custom_menu_wizard'
837
+ );
838
+ $instance['widget_class'] = empty( $instance['widget_class'] ) ? '' : esc_attr( trim ( $instance['widget_class'] ) );
839
+ if( !empty( $instance['widget_class'] ) ){
840
+ foreach( explode(' ', $instance['widget_class'] ) as $i ){
841
+ if( !empty( $i ) && !in_array( $i, $before_widget_class ) ){
842
+ $before_widget_class[] = $i;
843
+ }
844
+ }
 
845
  }
846
+ $instance['widget_class'] = '';
847
+ //turn on hide_empty...
848
+ $instance['hide_empty'] = 1;
849
  }
850
+
851
+ if( $ok ){
852
+ //apart from before_title, these are lifted from the_widget()...
853
+ $sidebar_args = array(
854
+ 'before_widget' => '<div class="' . implode( ' ', $before_widget_class ) . '">',
855
+ 'after_widget' => '</div>',
856
+ 'before_title' => '<h2 class="widgettitle">',
857
+ 'after_title' => '</h2>'
858
+ );
859
+ ob_start();
860
+ the_widget(
861
+ 'Custom_Menu_Wizard_Widget',
862
+ apply_filters('custom_menu_wizard_shortcode_settings', $instance ),
863
+ apply_filters('custom_menu_wizard_shortcode_widget_args', $sidebar_args )
864
+ );
865
+ $html = ob_get_clean();
866
  }
867
+ return empty($html) ? '' : $html;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
 
869
+ } //end shortcode_legacy()
870
+
871
+ } //end class Custom_Menu_Wizard_Plugin
 
 
 
 
 
 
 
 
 
 
 
 
 
 
872
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  }
custom-menu-wizard.png ADDED
Binary file
include/class.walker.php ADDED
@@ -0,0 +1,1036 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Plugin Name: Custom Menu Wizard
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
+ var $_cmw_tree;
15
+ var $_cmw_lowest;
16
+ var $_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
+ 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
+ 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
+ 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
+ }
72
+
73
+ } //ends the check for bad max depth, empty elements, or empty cmw args
74
+
75
+ return empty( $elements ) ? '' : parent::walk( apply_filters( 'custom_menu_wizard_walker_items', $elements, $args ), $max_depth, $args );
76
+
77
+ } //end walk()
78
+
79
+ /**
80
+ * current & legacy : finds and returns ID of current menu item while creating the tree and levels arrays
81
+ *
82
+ * @param array $elements Array of menu items
83
+ * @param array $cmw Array of widget instance settings
84
+ * @return integer ID of current menu item (false if not found)
85
+ */
86
+ function _cmw_find_current_item( &$elements, &$cmw ){
87
+ //$elements is an array of objects, indexed by position within the menu (menu_order),
88
+ //starting at 1 and incrementing sequentially regardless of parentage (ie. first item is [1],
89
+ //second item is [2] whether it's at root or subordinate to first item)
90
+
91
+ $id_field = $this->db_fields['id']; //eg. = 'db_id'
92
+ $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
93
+ $currentItem = array();
94
+
95
+ $this->_cmw_tree = array(
96
+ 0 => array(
97
+ 'level' => 0,
98
+ 'ancestors' => array(),
99
+ 'kids' => array(),
100
+ 'element' => array(),
101
+ 'keepCount' => 0,
102
+ 'keep' => false
103
+ )
104
+ );
105
+ $this->_cmw_levels = array(
106
+ array() //for the artificial level-0
107
+ );
108
+
109
+ if( isset( $cmw['_exclude'] ) ){ //only came in at v3.0.0!
110
+ //v3+ allows for inheritance on the $cmw items and exclude csv fields, which means that the _items and _exclude
111
+ //arrays do not necessarly contain all the relevant items ids, eg. an element such as '23+' in the _exclude array means
112
+ //that menu item id = 23 is excluded ... AND so are all its descendants!
113
+ $cmw['__items'] = array();
114
+ $cmw['__exclude'] = array();
115
+ $inheritItems = array();
116
+ $inheritExclude = array();
117
+ foreach( $cmw['_items'] as $i ){
118
+ if( substr( $i, -1 ) == '+' ){
119
+ $i = substr( $i, 0, -1 );
120
+ $inheritItems[] = $i;
121
+ }
122
+ $cmw['__items'][] = $i;
123
+ }
124
+ foreach( $cmw['_exclude'] as $i ){
125
+ if( substr( $i, -1 ) == '+' ){
126
+ $i = substr( $i, 0, -1 );
127
+ $inheritExclude[] = $i;
128
+ }
129
+ $cmw['__exclude'][] = $i;
130
+ }
131
+ }
132
+
133
+ foreach( $elements as $i => $item ){
134
+ $itemID = $item->$id_field;
135
+ $parentID = empty( $item->$parent_field ) ? 0 : $item->$parent_field;
136
+
137
+ //if $this->_cmw_tree[] for the parent hasn't been set then it's an orphan...
138
+ //NOTE The parent Walker will only handle orphans in a "hierarchical Show All" situation (ie. non-flat output, unlimited depth)
139
+ // 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!).
140
+ // However, CMW excludes all orphans (which includes descendants of orphans) so they never reach the parent Walker.
141
+ // If orphans are required, use WordPress's own Custom Menu widget.
142
+ if( isset( $this->_cmw_tree[ $parentID ] ) ){
143
+ //check for current item (as a menu item ID, ie. a key into the tree)...
144
+ if( $item->current ){
145
+ //should(!) never get either parent and/or ancestor on an item marked as "current", but unfortunately it does occur (grrr!).
146
+ //so this has to cope, not only with more than 1 "current" item, but also with "current" items that are incorrectly marked
147
+ //as (their own?!) parent and/or ancestor.
148
+ //we're going to look for correctly (solely) marked "current" items and take the first one found;
149
+ //failing that, look for a "current" item that is also marked as parent, and, again, use the first one found;
150
+ //failing that, look for a "current" item that is also marked as an ancestor, and, again, use the first one found.
151
+ //
152
+ //array keys, priority order : just current -> parent, not ancestor -> parent and ancestor -> ancestor
153
+ // first found...
154
+ // - a001 : just current
155
+ // - b001 : current & parent (not ancestor)
156
+ // - c001 : current & parent & ancestor
157
+ // - d001 : current & ancestor (not parent)
158
+ // next found...
159
+ // - a002 : just current
160
+ // - b002 : current & parent (not ancestor)
161
+ // - c002 : current & parent & ancestor
162
+ // - d002 : current & ancestor (not parent)
163
+ // etc
164
+ //example :
165
+ // - 1st found : current & ancestor = d001
166
+ // - 2nd found : current & parent & ancestor = c002
167
+ // - 3rd found : just current = a003
168
+ // - 4th found : just current & parent = b004
169
+ // - 5th found : just current = a005
170
+ //reverse sort keys alphabetically and a003 comes out on bottom, so third found gets used! (copes with 999 "current" items; should be enough!)
171
+ $j = $item->current_item_ancestor ? ( $item->current_item_parent ? 'c' : 'd') : ( $item->current_item_parent ? 'b' : 'a' );
172
+ $currentItem[ $j . sprintf( '%03d' , count( $currentItem ) + 1 ) ] = $itemID;
173
+ }
174
+
175
+ //because it is possible for $itemID not to be unique, we have to play canny :
176
+ //we need to be able to store multiple references back into $elements for each $itemID, and we therefore have to
177
+ //check whether we already have a $this->_cmw_tree entry for the $itemID, and if we do then simply store the pointer
178
+ //back to $elements in the existing $this->_cmw_tree entry for the item ID.
179
+ //This has consequences :
180
+ // - the individual menu items (for non-unique item IDs) will be in or out as a group, not as individual items
181
+ // - if we happen to need, say, the branch title from the branch being filtered, we have to take it from the
182
+ // first of the group and there need not be any relation between that item's title and the original menu item's
183
+ // title (as set on the WP Menu Admin page). Can't help that.
184
+ // - any child will be a child of the group, which actually means it *should* become (at some point?!) the child
185
+ // of the last member of the group(?!). BUT what if the child is actually intended to be the child of the item it follows,
186
+ // eg. itemID N, child, itemID N, child, itemID N, itemID N, child, child, itemID N, etc?
187
+ // 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
188
+ // attempt to rectify the few (rare) occasions where these situations may arise. (It's possible, but I have chosen not to).
189
+ // NB The parent Walker assigns children of a non-unique-id menu item to the 1st occurence of the item ID (1st in group)
190
+ if( isset( $this->_cmw_tree[ $itemID ] ) ){
191
+ //it's a non-unique item ID! just add the $elements pointer to the element array...
192
+ $this->_cmw_tree[ $itemID ]['element'][] = $i;
193
+ }else{
194
+ //this level...
195
+ $thisLevel = $this->_cmw_tree[ $parentID ]['level'] + 1;
196
+ if( empty( $this->_cmw_levels[ $thisLevel ] ) ){
197
+ $this->_cmw_levels[ $thisLevel ] = array();
198
+ }
199
+ $this->_cmw_levels[ $thisLevel ][] = $itemID;
200
+
201
+ $this->_cmw_tree[ $itemID ] = array(
202
+ //level within structure...
203
+ 'level' => $thisLevel,
204
+ //ancestors (from the artificial level-0, right down to parent, inclusive) within structure...
205
+ 'ancestors' => $this->_cmw_tree[ $parentID ]['ancestors'],
206
+ //kids within structure, ie array of itemID's...
207
+ 'kids' => array(),
208
+ //index of item within elements...
209
+ 'element' => array( $i ),
210
+ //classes added by widget...
211
+ 'classes' => array(),
212
+ //assume no match...
213
+ 'keep' => false
214
+ );
215
+ //append this item's parent onto its ancestors...
216
+ $this->_cmw_tree[ $itemID ]['ancestors'][] = $parentID;
217
+ //add this item to its parent's kids...
218
+ $this->_cmw_tree[ $parentID ]['kids'][] = $itemID;
219
+
220
+ //check for inheritance on items and exclude...
221
+ if( isset( $inheritItems ) ){
222
+ if( in_array( $parentID, $inheritItems ) ){
223
+ $inheritItems[] = $itemID;
224
+ $cmw['__items'][] = $itemID;
225
+ }
226
+ if( in_array( $parentID, $inheritExclude ) ){
227
+ $inheritExclude[] = $itemID;
228
+ $cmw['__exclude'][] = $itemID;
229
+ }
230
+ }
231
+ }
232
+ }
233
+ } //end foreach
234
+
235
+ if( isset( $inheritItems ) ){
236
+ unset( $inheritItems, $inheritExclude );
237
+ }
238
+
239
+ if( empty( $currentItem ) ){
240
+ $currentItem = false;
241
+ }else{
242
+ krsort( $currentItem );
243
+ $currentItem = array_pop( $currentItem );
244
+ }
245
+
246
+ return $currentItem;
247
+
248
+ } //end _cmw_find_current_item()
249
+
250
+ /**
251
+ * current: test for one item being below another item and/or its siblings
252
+ *
253
+ * @param integer $lookBelow Menu item ID that needs checking, possibly its siblings as well
254
+ * @param integer $searchFor Menu item ID that is being looked for
255
+ * @param boolean $andSiblings Whether to check in $lookBelow's siblings as well
256
+ * @return boolean Found (or not)
257
+ */
258
+ function _cmw_check_contains_item( $lookBelow, $searchFor, $andSiblings ){
259
+
260
+ $rtn = in_array( $lookBelow, $this->_cmw_tree[ $searchFor ]['ancestors'] );
261
+ if( !$rtn && $andSiblings ){
262
+ foreach( $this->_cmw_tree[ $this->_cmw_get_parent( $lookBelow ) ]['kids'] as $kid ){
263
+ //check whether one of $searchFor's ancestors is $kid...
264
+ if( $kid != $lookBelow && in_array( $kid, $this->_cmw_tree[ $searchFor ]['ancestors'] ) ){
265
+ $rtn = true;
266
+ break;
267
+ }
268
+ }
269
+ }
270
+ return $rtn;
271
+
272
+ }
273
+
274
+ /**
275
+ * returns the menu item id if an item's parent
276
+ *
277
+ * @param integer $kid Menu item ID
278
+ * @return integer Menu item ID of kid's parent
279
+ */
280
+ function _cmw_get_parent( $kid ){
281
+
282
+ $immediateParent = array_slice( $this->_cmw_tree[ $kid ]['ancestors'], -1, 1);
283
+ return $immediateParent[0];
284
+
285
+ }
286
+
287
+ /**
288
+ * current: set keep flag of an item's siblings
289
+ *
290
+ * @param integer $itemID Menu item ID
291
+ * @param string $classSuffix Suffix of class to be added to the kept items
292
+ */
293
+ function _cmw_include_siblings_of( $itemID, $classSuffix='sibling' ){
294
+
295
+ foreach( $this->_cmw_tree[ $this->_cmw_get_parent( $itemID ) ]['kids'] as $i ){
296
+ if( !$this->_cmw_tree[ $i ]['keep'] ){
297
+ $this->_cmw_tree[ $i ]['keep'] = true;
298
+ $this->_cmw_tree[0]['keepCount']++;
299
+ if( !empty( $classSuffix ) ){
300
+ $this->_cmw_tree[ $i ]['classes'][] = 'cmw-an-included-' . $classSuffix;
301
+ }
302
+ }
303
+ }
304
+
305
+ } //end _cmw_include_siblings_of()
306
+
307
+ /**
308
+ * current : recursively set the keep flag if within specified level/depth
309
+ * if item passed in is eligible, sets that item as kept and runs through its kids recursively
310
+ *
311
+ * @param integer $itemID Menu item ID
312
+ */
313
+ function _cmw_set_keep_recursive( $itemID ){
314
+
315
+ //at or above lowest?...
316
+ if( $this->_cmw_tree[ $itemID ]['level'] <= $this->_cmw_lowest ){
317
+ //at or below highest?...
318
+ if( $this->_cmw_tree[ $itemID ]['level'] >= $this->_cmw_highest ){
319
+ //keep (if not already)...
320
+ if( !$this->_cmw_tree[ $itemID ]['keep'] ){
321
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
322
+ $this->_cmw_tree[0]['keepCount']++;
323
+ }
324
+ }
325
+ //unless this item is above the lowest level there's no point checking its kids...
326
+ if( $this->_cmw_tree[ $itemID ]['level'] < $this->_cmw_lowest ){
327
+ for( $i = 0, $ct = count( $this->_cmw_tree[ $itemID ]['kids'] ); $i < $ct; $i++ ){
328
+ $this->_cmw_set_keep_recursive( $this->_cmw_tree[ $itemID ]['kids'][ $i ] );
329
+ }
330
+ }
331
+ }
332
+
333
+ } //end _cmw_set_keep_recursive()
334
+
335
+ /**
336
+ * legacy : recursively set the keep flag if within specified level/depth
337
+ * runs through kids of item passed in : if kid is eligible, sets kid to kept; if grandkids might be eligible, recurse with kid
338
+ *
339
+ * @param integer $itemID Menu item ID
340
+ * @param integer $topLevel Uppermost level that can be kept
341
+ * @param integer $bottomLevel Lowermost level that can be kept
342
+ */
343
+ function _cmw_legacy_set_keep_kids( $itemId, $topLevel, $bottomLevel ){
344
+
345
+ for( $i = 0, $ct = count( $this->_cmw_tree[ $itemId ]['kids'] ); $i < $ct; $i++ ){
346
+ $j = $this->_cmw_tree[ $itemId ]['kids'][ $i ];
347
+ if( $this->_cmw_tree[ $j ]['level'] <= $bottomLevel ){
348
+ if( ( $this->_cmw_tree[ $j ]['keep'] = $this->_cmw_tree[ $j ]['level'] >= $topLevel ) !== false){
349
+ $this->_cmw_tree[0]['keepCount']++;
350
+ }
351
+ }
352
+ if( $this->_cmw_tree[ $j ]['level'] < $bottomLevel ){
353
+ $this->_cmw_legacy_set_keep_kids( $j, $topLevel, $bottomLevel );
354
+ }
355
+ }
356
+
357
+ } //end _cmw_legacy_set_keep_kids()
358
+
359
+ /**
360
+ * pre-filters elements
361
+ *
362
+ * @param {object} $args Params supplied to wp_nav_menu()
363
+ * @param {array} $elements Menu items
364
+ * @return {array} Modified menu items
365
+ */
366
+ function _cmw_walk( &$args, $elements ){
367
+
368
+ $cmw =& $args->_custom_menu_wizard;
369
+
370
+ $cmw['_walker']['fellback'] = false;
371
+
372
+ $find_items = $cmw['filter'] == 'items';
373
+ $find_branch = $cmw['filter'] == 'branch';
374
+ $find_level = !$find_items && !$find_branch;
375
+ $find_current = $find_branch && empty( $cmw['branch'] );
376
+
377
+ //find the current menu item while creating the tree and levels arrays...
378
+ $currentItem = $this->_cmw_find_current_item( $elements, $cmw );
379
+
380
+ //measuring depth relative to the current item only applies if depth is *not* unlimited...
381
+ $depth = intval( $cmw['depth'] );
382
+ $depth_rel_current = $cmw['depth_rel_current'] && $depth > 0 && !empty( $currentItem ); //v2.0.0
383
+ //no-kids fallback?...
384
+ $canFallback = $find_current && in_array( $cmw['fallback'], array('current', 'parent', 'quit') );
385
+
386
+ $id_field = $this->db_fields['id']; //eg. = 'db_id'
387
+ $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
388
+
389
+ $unlimited = 65532;
390
+ $topOfBranch = -1;
391
+
392
+ $continue = true;
393
+ //no point doing much more if we need the current item and we haven't found it...
394
+ //handles contains_current == 'menu'...
395
+ if( empty( $currentItem ) && ( $find_current || !empty( $cmw['contains_current'] ) ) ){
396
+ $continue = false;
397
+ }
398
+
399
+ //PRIMARY FILTERS...
400
+ if( $continue ){
401
+ //levels...
402
+ if( $find_level ){
403
+ $continue = $cmw['level'] < count( $this->_cmw_levels );
404
+ }
405
+ //items...
406
+ if( $find_items ){
407
+ $continue = !empty( $cmw['__items'] );
408
+ }
409
+ //branch...
410
+ if( $find_branch ){
411
+ $topOfBranch = $find_current
412
+ ? $currentItem
413
+ : ( isset( $this->_cmw_tree[ $cmw['branch'] ] )
414
+ ? $cmw['branch']
415
+ : -1
416
+ );
417
+ $theBranchItem = $topOfBranch;
418
+ $continue = $topOfBranch > 0;
419
+ }
420
+ } //end PRIMARIES
421
+
422
+ //check for current item...
423
+ if( $continue && ( $cmw['contains_current'] == 'primary' ) ){
424
+ if( $find_level ){
425
+ $continue = $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['level'];
426
+ }
427
+ if( $find_items ){
428
+ $continue = in_array( $currentItem, $cmw['__items'] );
429
+ }
430
+ if( $find_branch ){
431
+ $continue = $topOfBranch == $currentItem || in_array( $topOfBranch, $this->_cmw_tree[ $currentItem ]['ancestors'] );
432
+ }
433
+ }
434
+
435
+ //SECONDARY FILTERS...
436
+ if( $continue ){
437
+ //right, let's set some keep flags
438
+ //for specific items, go straight in on the item id (levels and depth don't apply here)...
439
+ if( $find_items ){
440
+ foreach( $cmw['__items'] as $itemID ){
441
+ $itemID = intval( $itemID );
442
+ //avoid double counting of duplicates...
443
+ if( !empty( $itemID ) && isset( $this->_cmw_tree[ $itemID ] ) && !$this->_cmw_tree[ $itemID ]['keep'] ){
444
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
445
+ $this->_cmw_tree[0]['keepCount']++;
446
+ }
447
+ }
448
+ }
449
+ //for by-level filter, use the levels...
450
+ if( $find_level ){
451
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
452
+ if( $depth_rel_current && $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['level'] ){
453
+ $bottomLevel = $this->_cmw_tree[ $currentItem ]['level'] + $depth - 1;
454
+ }else{
455
+ $bottomLevel = $depth > 0 ? $cmw['level'] + $depth - 1 : $unlimited;
456
+ }
457
+ for( $i = $cmw['level']; isset( $this->_cmw_levels[ $i ] ) && $i <= $bottomLevel; $i++ ){
458
+ foreach( $this->_cmw_levels[ $i ] as $itemID ){
459
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
460
+ $this->_cmw_tree[0]['keepCount']++;
461
+ }
462
+ }
463
+ }
464
+ //for branch filters, run a recursive through the structure...
465
+ if( $find_branch ){
466
+ $i = $this->_cmw_tree[ $topOfBranch ]['level'];
467
+ //convert branch_start to an actual level...
468
+ $j = intval( $cmw['branch_start'] );
469
+ //convert relative to absolute (max'd against 1)...
470
+ $j = empty( $j ) ? $i : ( preg_match( '/^(\+|-)/', $cmw['branch_start'] ) > 0 ? max( 1, $i + $j ) : $j );
471
+
472
+ //do we have a current-item-no-kids fallback?...
473
+ if( $canFallback && empty( $this->_cmw_tree[ $currentItem ]['kids'] ) ){
474
+ //yes, we do...
475
+ $cmw['_walker']['fellback'] = 'to-' . $cmw['fallback'];
476
+ //is it a copout?...
477
+ if( $cmw['fallback'] == 'quit' ){
478
+ //just set the secondary start level beyond the maximum level available...
479
+ $j = count( $this->_cmw_levels );
480
+ }else{
481
+ //for current, fall back to primary start level; for parent, fall back to primary start level - 1, ensuring
482
+ //that we don't fall back further than root...
483
+ $j = $cmw['fallback'] == 'current' || $i < 2 ? $i : $i - 1;
484
+ //if fallback_depth is specified, override depth and set to depth-rel-current...
485
+ if( !empty( $cmw['fallback_depth'] ) ){
486
+ $depth = intval( $cmw['fallback_depth'] );
487
+ $depth_rel_current = true;
488
+ }
489
+ }
490
+ }
491
+
492
+ //$i is the primary level, and $j is the secondary start level
493
+ //easy result : if secondary start level > max level then there are no matches...
494
+ if( $j < count( $this->_cmw_levels ) ){
495
+ //if secondary start level is higher up the tree than the primary level, then
496
+ //reset the tob to be the current tob's ancestor at the level of $j...
497
+ if( $j < $i ){
498
+ $topOfBranch = array_slice( $this->_cmw_tree[ $topOfBranch ]['ancestors'], $j, 1 );
499
+ $topOfBranch = $topOfBranch[0];
500
+ //NB $theBranchItem is still set to the original branch item!
501
+ }
502
+
503
+ $this->_cmw_lowest = $unlimited;
504
+ $this->_cmw_highest = $j;
505
+
506
+ //$topOfBranch is a menu item id.
507
+ //if secondary start is at or above primary, and start_mode is set to "level" then it effectively means that
508
+ //the top of branch becomes the current topOfBranch *plus* all its siblings. the one qualifier for
509
+ //this is that if topOfBranch's current level is 1 (root) then the allow_all_root switch must be
510
+ //enabled in order to expand to all root items
511
+ $forceLevel = $j <= $i && $cmw['start_mode'] == 'level' && ( $j > 1 || $cmw['allow_all_root'] );
512
+
513
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
514
+ //NB for depth_rel_current to be applicable we need a current item that is :
515
+ // (a) at or below the secondary start level, and
516
+ // (b) within the (modified?) branch (ie. has topOfBranch - or possibly one of its siblings! - as an ancestor)
517
+ if( $depth_rel_current
518
+ //...this is the (a) part...
519
+ && $this->_cmw_tree[ $currentItem ]['level'] >= $this->_cmw_highest
520
+ //...this is the (b) part, and it might be complicated by the setting of $forceLevel, which would
521
+ // require the testing, not only of topOfBranch, but also topOfBranch's siblings...
522
+ && $this->_cmw_check_contains_item( $topOfBranch, $currentItem, $forceLevel ) ){
523
+ $this->_cmw_lowest = $this->_cmw_tree[ $currentItem ]['level'];
524
+ }elseif( $depth > 0 ){
525
+ $this->_cmw_lowest = $this->_cmw_highest;
526
+ }
527
+ $this->_cmw_lowest += $depth - 1;
528
+ //$this->_cmw_tree[0]['keepCount'] gets incremented during this recursive...
529
+ if( $forceLevel ){
530
+ foreach( $this->_cmw_tree[ $this->_cmw_get_parent( $topOfBranch ) ]['kids'] as $k ){
531
+ $this->_cmw_set_keep_recursive( $k );
532
+ }
533
+ }else{
534
+ $this->_cmw_set_keep_recursive( $topOfBranch );
535
+ }
536
+ //if falling back and siblings are required, add them in...
537
+ //note that root level sibling inclusion is still governed by allow_all_root!
538
+ if( !empty( $cmw['_walker']['fellback'] ) && $cmw['fallback_siblings'] && $this->_cmw_tree[0]['keepCount'] > 0
539
+ && ( $j > 1 || $cmw['allow_all_root'] ) ){
540
+ $this->_cmw_include_siblings_of( $topOfBranch );
541
+ }
542
+ }
543
+ }
544
+ $continue = $this->_cmw_tree[0]['keepCount'] > 0;
545
+ } //end SECONDARIES
546
+
547
+ //check for current item...
548
+ if( $continue && $cmw['contains_current'] == 'secondary' ){
549
+ $continue = $this->_cmw_tree[ $currentItem ]['keep'];
550
+ }
551
+
552
+ //INCLUSIONS...
553
+ if( $continue ){
554
+ if( $find_branch ){
555
+ //branch ancestors, possibly with their siblings : but only if the original branch item is either being kept or
556
+ //is below the lowest visible level; ALSO, do not keep any ancestors below the lowest visible level...
557
+ if( $cmw['ancestors'] != 0 && ( $this->_cmw_tree[ $theBranchItem ]['keep'] || $this->_cmw_tree[ $theBranchItem ]['level'] > $this->_cmw_lowest ) ){
558
+ //convert relative to absolute...
559
+ $absAncestors = $cmw['ancestors'] < 0
560
+ ? max( 1, $this->_cmw_tree[ $theBranchItem ]['level'] + $cmw['ancestors'] )
561
+ : $cmw['ancestors'];
562
+ //convert relative to absolute...
563
+ $absSiblings = $cmw['ancestor_siblings'] < 0
564
+ ? max( 1, $this->_cmw_tree[ $theBranchItem ]['level'] + $cmw['ancestor_siblings'] )
565
+ : $cmw['ancestor_siblings'];
566
+ foreach( $this->_cmw_tree[ $theBranchItem ]['ancestors'] as $itemID ){
567
+ if( $itemID > 0
568
+ && $this->_cmw_tree[ $itemID ]['level'] >= $absAncestors
569
+ && $this->_cmw_tree[ $itemID ]['level'] <= $this->_cmw_lowest ){
570
+ if( !$this->_cmw_tree[ $itemID ]['keep'] ){
571
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
572
+ $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-ancestor';
573
+ $this->_cmw_tree[0]['keepCount']++;
574
+ }
575
+ //only keep ancestor siblings if the ancestor itself is being kept
576
+ if( $absSiblings > 0
577
+ && $this->_cmw_tree[ $itemID ]['level'] >= $absSiblings
578
+ && $this->_cmw_tree[ $itemID ]['keep'] ){
579
+ $this->_cmw_include_siblings_of( $itemID, 'ancestor-sibling' );
580
+ }
581
+ }
582
+ }
583
+ }
584
+ //branch siblings : only if the original branch item is being kept...
585
+ if( $cmw['siblings'] && $this->_cmw_tree[ $theBranchItem ]['keep'] ){
586
+ $this->_cmw_include_siblings_of( $theBranchItem );
587
+ }
588
+ }
589
+ if( $cmw['include_root'] ){
590
+ //all root items...
591
+ foreach( $this->_cmw_levels[1] as $itemID ){
592
+ if( !$this->_cmw_tree[ $itemID ]['keep'] ){
593
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
594
+ $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-root-item';
595
+ $this->_cmw_tree[0]['keepCount']++;
596
+ }
597
+ }
598
+ }
599
+ } //end INCLUSIONS
600
+
601
+ //check for current item...
602
+ if( $continue && $cmw['contains_current'] == 'inclusions' ){
603
+ $continue = $this->_cmw_tree[ $currentItem ]['keep'];
604
+ }
605
+
606
+ //EXCLUSIONS...
607
+ if( $continue){
608
+ if( !empty( $cmw['__exclude'] )){
609
+ foreach( $cmw['__exclude'] as $itemID ){
610
+ if( $itemID > 0 && isset( $this->_cmw_tree[ $itemID ] ) && $this->_cmw_tree[ $itemID ]['keep'] ){
611
+ $this->_cmw_tree[ $itemID ]['keep'] = false;
612
+ $this->_cmw_tree[0]['keepCount']--;
613
+ }
614
+ }
615
+ $continue = $this->_cmw_tree[0]['keepCount'] > 0;
616
+ }
617
+ if( $continue && !empty( $cmw['exclude_level'] ) && preg_match( '/^(\d+)(\+|-)?$/', $cmw['exclude_level'], $i ) > 0 ){
618
+ if( empty( $i[2] ) ){
619
+ $i = $i[1];
620
+ $j = $i + 1;
621
+ }elseif( $i[2] == '+' ){
622
+ $i = $i[1];
623
+ $j = count( $this->_cmw_levels );
624
+ }else{
625
+ $j = $i[1] + 1;
626
+ $i = 1;
627
+ }
628
+ while( $i > 0 && isset( $this->_cmw_levels[ $i ] ) && $i < $j ){
629
+ foreach( $this->_cmw_levels[ $i ] as $itemID ){
630
+ if( $this->_cmw_tree[ $itemID ]['keep'] ){
631
+ $this->_cmw_tree[ $itemID ]['keep'] = false;
632
+ $this->_cmw_tree[0]['keepCount']--;
633
+ }
634
+ }
635
+ $i++;
636
+ }
637
+ $continue = $this->_cmw_tree[0]['keepCount'] > 0;
638
+ }
639
+ } //end EXCLUSIONS
640
+
641
+ //check for current item...
642
+ if( $continue && $cmw['contains_current'] == 'output' ){
643
+ $continue = $this->_cmw_tree[ $currentItem ]['keep'];
644
+ }
645
+
646
+ //check for title_from...
647
+ if( $continue ){
648
+ //might we want the (original) branch item's, or root item's, title as the widget title?...
649
+ if( $find_branch && $theBranchItem > 0 ){
650
+ $cmw['_walker']['branch_title'] = apply_filters(
651
+ 'the_title',
652
+ $elements[ $this->_cmw_tree[ $theBranchItem ]['element'][0] ]->title,
653
+ $elements[ $this->_cmw_tree[ $theBranchItem ]['element'][0] ]->ID
654
+ );
655
+ if( $this->_cmw_tree[ $theBranchItem ]['level'] > 1 ){
656
+ $topOfBranch = array_slice( $this->_cmw_tree[ $theBranchItem ]['ancestors'], 1, 1 );
657
+ $topOfBranch = $topOfBranch[0];
658
+ $cmw['_walker']['branch_root_title'] = apply_filters(
659
+ 'the_title',
660
+ $elements[ $this->_cmw_tree[ $topOfBranch ]['element'][0] ]->title,
661
+ $elements[ $this->_cmw_tree[ $topOfBranch ]['element'][0] ]->ID
662
+ );
663
+ }else{
664
+ $cmw['_walker']['branch_root_title'] = $cmw['_walker']['branch_title'];
665
+ }
666
+ }
667
+ //might we want the current item's, or root item's, title as the widget title?...
668
+ if( !empty( $currentItem ) ){
669
+ $cmw['_walker']['current_title'] = apply_filters(
670
+ 'the_title',
671
+ $elements[ $this->_cmw_tree[ $currentItem ]['element'][0] ]->title,
672
+ $elements[ $this->_cmw_tree[ $currentItem ]['element'][0] ]->ID
673
+ );
674
+ if( $this->_cmw_tree[ $currentItem ]['level'] > 1 ){
675
+ $topOfBranch = array_slice( $this->_cmw_tree[ $currentItem ]['ancestors'], 1, 1 );
676
+ $topOfBranch = $topOfBranch[0];
677
+ $cmw['_walker']['current_root_title'] = apply_filters(
678
+ 'the_title',
679
+ $elements[ $this->_cmw_tree[ $topOfBranch ]['element'][0] ]->title,
680
+ $elements[ $this->_cmw_tree[ $topOfBranch ]['element'][0] ]->ID
681
+ );
682
+ }else{
683
+ $cmw['_walker']['current_root_title'] = $cmw['_walker']['current_title'];
684
+ }
685
+ }
686
+ }
687
+
688
+ $this->_cmw_levels = null;
689
+ $substructure = array();
690
+ if( $continue ){
691
+ //now we need to gather together all the 'keep' items from the tree;
692
+ //while doing so, we need to set up levels and kids, ready for adding classes...
693
+ foreach( $this->_cmw_tree as $k => $v ){
694
+ if( $v['keep'] ){
695
+ $substructure[ $k ] = $v;
696
+ //use kids as a has-submenu flag...
697
+ $substructure[ $k ]['kids'] = 0;
698
+ //any surviving parent (except the artificial level-0) should have submenu class set on it...
699
+ array_shift( $v['ancestors'] ); //remove the level-0
700
+ for( $i = count( $v['ancestors'] ) - 1; $i >= 0; $i-- ){
701
+ if( isset( $substructure[ $v['ancestors'][ $i ] ] ) ){
702
+ $substructure[ $v['ancestors'][ $i ] ]['kids']++;
703
+ }else{
704
+ //not a 'kept' ancestor so remove it...
705
+ array_splice( $v['ancestors'], $i, 1 );
706
+ }
707
+ }
708
+ //ancestors now only has 'kept' ancestors...
709
+ $substructure[ $k ]['level'] = count( $v['ancestors'] ) + 1;
710
+ //need to ensure that the parent_field of all the new top-level (ie. root) items is set to
711
+ //zero, otherwise the parent::walk() will assume they're orphans.
712
+ //however, we also need to check that parent_field of a child actually points to the closest
713
+ //'kept' ancestor; otherwise, given A (kept) > B (not kept) > C (kept) the parent_field of C
714
+ //would point to a non-existent B and would subsequently be considered an orphan!
715
+ if( $substructure[ $k ]['level'] == 1){
716
+ $ancestor = 0;
717
+ }else{
718
+ //set to the closest ancestor, ie. the new(?) parent...
719
+ $ancestor = array_slice( $v['ancestors'], -1, 1 );
720
+ $ancestor = $ancestor[0];
721
+ }
722
+ //take a copy of the elements item(s)...
723
+ $substructure[ $k ]['element'] = array();
724
+ foreach( $v['element'] as $i => $j ){
725
+ $elements[ $j ]->$parent_field = $ancestor;
726
+ $substructure[ $k ]['element'][] = $elements[ $j ];
727
+ }
728
+ }
729
+ }
730
+ }
731
+ $this->_cmw_tree = null;
732
+
733
+ //put substructure's elements back into $elements (remember that $elements is a 1-based array!)...
734
+ $elements = array();
735
+ $n = 1;
736
+ foreach( $substructure as $k => $v ){
737
+ $ct = count( $v['element'] ) - 1;
738
+ foreach( $v['element'] as $i => $j ){
739
+ $elements[ $n ] = $j;
740
+ //add the level class...
741
+ $elements[ $n ]->classes[] = 'cmw-level-' . $v['level'];
742
+ //add the submenu class? (only to last in group!)...
743
+ if( $v['kids'] > 0 && $i == $ct ){
744
+ $elements[ $n ]->classes[] = 'cmw-has-submenu';
745
+ }else{
746
+ //3.7 adds a menu-item-has-children class to (original) menu items that have kids : remove it as the item is now childless...
747
+ $elements[ $n ]->classes = array_diff( $elements[ $n ]->classes, array('menu-item-has-children') );
748
+ }
749
+ //add any other CMW classes...
750
+ $elements[ $n ]->classes = array_merge( $elements[ $n ]->classes, $v['classes'] );
751
+ $n++;
752
+ }
753
+ }
754
+ unset( $substructure );
755
+
756
+ return $elements;
757
+
758
+ } //end _cmw_walk()
759
+
760
+ /**
761
+ * pre-filters elements (v2.1.0)
762
+ *
763
+ * @param {object} $args Params supplied to wp_nav_menu()
764
+ * @param {array} $elements Menu items
765
+ * @return {array} Modified menu items
766
+ */
767
+ function _cmw_walk_legacy( &$args, $elements ){
768
+
769
+ //NB : $elements is an array of objects, indexed by position within the menu (menu_order),
770
+ //starting at 1 and incrementing sequentially regardless of parentage (ie. first item is [1],
771
+ //second item is [2] whether it's at root or subordinate to first item)
772
+
773
+ $cmw =& $args->_custom_menu_wizard;
774
+
775
+ $cmw['_walker']['fellback'] = false;
776
+
777
+ $find_kids_of = $cmw['filter'] > 0;
778
+ $find_specific_items = $cmw['filter'] < 0; //v2.0.0 //v2.0.1:bug fixed (changed < 1 to < 0)
779
+ $find_current_item = $find_kids_of && empty( $cmw['filter_item'] );
780
+ $find_current_parent = $find_kids_of && $cmw['filter_item'] == -1; //v1.1.0
781
+ $find_current_root = $find_kids_of && $cmw['filter_item'] == -2; //v1.1.0
782
+ $depth_rel_current = $cmw['depth_rel_current'] && $cmw['depth'] > 0; //v2.0.0
783
+ //these could change depending on whether a fallback comes into play (v1.1.0)
784
+ $include_parent = $cmw['include_parent'] || $cmw['include_ancestors'];
785
+ $include_parent_siblings = $cmw['include_parent_siblings'];
786
+
787
+ $id_field = $this->db_fields['id']; //eg. = 'db_id'
788
+ $parent_field = $this->db_fields['parent']; //eg. = 'menu_item_parent'
789
+
790
+ //find the current menu item while creating the tree and levels arrays...
791
+ $currentItem = $this->_cmw_find_current_item( $elements, $cmw );
792
+
793
+ $allLevels = 9999;
794
+ $startWithKidsOf = -1;
795
+
796
+ //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...
797
+ $continue = true;
798
+ if( empty( $currentItem ) && ( $find_current_item || $find_current_parent || $find_current_root || $cmw['contains_current'] ) ){
799
+ $continue = false;
800
+ }elseif( $find_specific_items && empty( $cmw['items'] ) ){
801
+ $continue = false;
802
+ }
803
+
804
+ // IMPORTANT : as of v2.0.0, start level has been rationalised so that it acts the same across all filters (except for specific items!).
805
+ // Previously ...
806
+ // start level for a show-all filter literally started at the specified level and reported all levels until depth was reached.
807
+ // however, start level for a kids-of filter specified the level that the *immediate* kids of the selected filter had to be at
808
+ // or below. That was consistent for a specific item, current-item and current-parent filter, but for a current-root filter what
809
+ // it actually did was test the current item against the start level, not the current item's root ancestor! Inconsistent!
810
+ // But regardless of the current-root filter's use of start level, there was still the inconsistency between show-all and
811
+ // kids-of usage.
812
+ // Now (as of v2.0.0) ...
813
+ // start level and depth have been changed to definitively be secondary filters to the show-all & kids-of primary filter.
814
+ // The primary filter - show-all, or a kids-of - will provide the initial set of items, and the secondary - start level & depth -
815
+ // will further refine that set, with start level being an absolute, and depth still being relative to the first item found.
816
+ // The sole exception to this is when Depth Relative to Current Menu Item is set, which modifies the calculation of depth (only)
817
+ // 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
818
+ // below start level).
819
+ // 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,
820
+ // would fail to return any items because the immediate kids (at level 3) were outside the start level. Now, the returned items
821
+ // will begin with the grand-kids (ie. those at level 4).
822
+ // Note that neither start level nor depth are applicable to a specific items filter (also new at v2.0.0)!
823
+
824
+ //the kids-of filters...
825
+ if( $continue && $find_kids_of ){
826
+ //specific item...
827
+ if( $cmw['filter_item'] > 0 && isset( $this->_cmw_tree[ $cmw['filter_item'] ] ) && !empty( $this->_cmw_tree[ $cmw['filter_item'] ]['kids'] ) ){
828
+ $startWithKidsOf = $cmw['filter_item'];
829
+ }
830
+ if( $find_current_item ){
831
+ if( !empty( $this->_cmw_tree[ $currentItem ]['kids'] ) ){
832
+ $startWithKidsOf = $currentItem;
833
+ }elseif( $cmw['fallback_no_children'] ){
834
+ //no kids, and fallback to current parent is set...
835
+ //note that there is no "double fallback", so current parent "can" be the artifical zero element (level-0) *if*
836
+ // the current item is a singleton( ie. no kids & no ancestors)!
837
+ $ancestor = array_slice( $this->_cmw_tree[ $currentItem ]['ancestors'], -1, 1 );
838
+ $startWithKidsOf = $ancestor[0]; //can be zero!
839
+ $include_parent = $include_parent || $cmw['fallback_nc_include_parent'];
840
+ $include_parent_siblings = $include_parent_siblings || $cmw['fallback_nc_include_parent_siblings'];
841
+ $cmw['_walker']['fellback'] = 'to-parent';
842
+ }
843
+ }elseif( $find_current_parent || $find_current_root ){
844
+ //as of v2.0.0 the fallback to current item - for current menu items at the top level - is deprecated, but
845
+ //retained for a while to maintain backward compatibility
846
+ //if no parent : fall back to current item (if set)...
847
+ if( $this->_cmw_tree[ $currentItem ]['level'] == 1 && $cmw['fallback_no_ancestor'] ){
848
+ $startWithKidsOf = $currentItem;
849
+ $include_parent = $include_parent || $cmw['fallback_include_parent'];
850
+ $include_parent_siblings = $include_parent_siblings || $cmw['fallback_include_parent_siblings'];
851
+ $cmw['_walker']['fellback'] = 'to-current';
852
+ }else{
853
+ //as of v2.0.0, the artificial level-0 counts as parent of a top-level current menu item...
854
+ if( $find_current_parent ){
855
+ $ancestor = -1;
856
+ }elseif( $this->_cmw_tree[ $currentItem ]['level'] > 1 ){
857
+ $ancestor = 1;
858
+ }else{
859
+ $ancestor = 0;
860
+ }
861
+ $ancestor = array_slice( $this->_cmw_tree[ $currentItem ]['ancestors'], $ancestor, 1 );
862
+ if( !empty( $ancestor ) ){
863
+ $startWithKidsOf = $ancestor[0]; //as of v2.0.0, this can now be zero!
864
+ }
865
+ }
866
+ }
867
+ }
868
+
869
+ if( $continue ){
870
+ //right, let's set the keep flags
871
+ //for specific items, go straight in on the item id (start level and depth do not apply here)...
872
+ if( $find_specific_items ){
873
+ foreach( preg_split('/[,\s]+/', $cmw['items'] ) as $itemID ){
874
+ if( isset( $this->_cmw_tree[ $itemID ] ) ){
875
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
876
+ $this->_cmw_tree[0]['keepCount']++;
877
+ }
878
+ }
879
+ //for show-all filter, just use the levels...
880
+ }elseif( !$find_kids_of ){
881
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
882
+ if( $depth_rel_current && !empty( $currentItem ) && $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['start_level'] ){
883
+ $bottomLevel = $this->_cmw_tree[ $currentItem ]['level'] + $cmw['depth'] - 1;
884
+ }else{
885
+ $bottomLevel = $cmw['depth'] > 0 ? $cmw['start_level'] + $cmw['depth'] - 1 : $allLevels;
886
+ }
887
+ for( $i = $cmw['start_level']; isset( $this->_cmw_levels[ $i ] ) && $i <= $bottomLevel; $i++ ){
888
+ foreach( $this->_cmw_levels[ $i ] as $itemID ){
889
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
890
+ $this->_cmw_tree[0]['keepCount']++;
891
+ }
892
+ }
893
+ //for kids-of filters, run a recursive through the structure's kids...
894
+ }elseif( $startWithKidsOf > -1 ){
895
+ //prior to v2.0.0, depth was always related to the first item found, and still is *unless* depth_rel_current is set
896
+ //NB the in_array() of ancestors prevents depth_rel_current when startWithKidsOf == currentItem
897
+ if( $depth_rel_current && !empty( $currentItem ) && $this->_cmw_tree[ $currentItem ]['level'] >= $cmw['start_level']
898
+ && in_array( $startWithKidsOf, $this->_cmw_tree[ $currentItem ]['ancestors'] ) ){
899
+ $bottomLevel = $this->_cmw_tree[ $currentItem ]['level'] - 1 + $cmw['depth'];
900
+ }else{
901
+ $bottomLevel = $cmw['depth'] > 0
902
+ ? max( $this->_cmw_tree[ $startWithKidsOf ]['level'] + $cmw['depth'], $cmw['start_level'] + $cmw['depth'] - 1 )
903
+ : $allLevels;
904
+ }
905
+ //$this->_cmw_tree[0]['keepCount'] gets incremented in this recursive method...
906
+ $this->_cmw_legacy_set_keep_kids( $startWithKidsOf, $cmw['start_level'], $bottomLevel );
907
+ }
908
+
909
+ if( $this->_cmw_tree[0]['keepCount'] > 0 ){
910
+ //we have some items! we now may need to set some more keep flags, depending on the include settings...
911
+
912
+ //do we need to include parent, parent siblings, and/or ancestors?...
913
+ //NB these are not restricted by start_level!
914
+ if( $find_kids_of && $startWithKidsOf > 0 ){
915
+ if( $include_parent ){
916
+ $this->_cmw_tree[ $startWithKidsOf ]['keep'] = true;
917
+ $this->_cmw_tree[ $startWithKidsOf ]['classes'][] = 'cmw-the-included-parent';
918
+ }
919
+ if( $include_parent_siblings ){
920
+ $ancestor = array_slice( $this->_cmw_tree[ $startWithKidsOf ]['ancestors'], -1, 1);
921
+ foreach($this->_cmw_tree[ $ancestor[0] ]['kids'] as $itemID ){
922
+ //may have already been kept by include_parent...
923
+ if( !$this->_cmw_tree[ $itemID ]['keep'] ){
924
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
925
+ $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-parent-sibling';
926
+ }
927
+ }
928
+ }
929
+ if( $cmw['include_ancestors'] ){
930
+ foreach( $this->_cmw_tree[ $startWithKidsOf ]['ancestors'] as $itemID ){
931
+ if( $itemID > 0 && !$this->_cmw_tree[ $itemID ]['keep'] ){
932
+ $this->_cmw_tree[ $itemID ]['keep'] = true;
933
+ $this->_cmw_tree[ $itemID ]['classes'][] = 'cmw-an-included-parent-ancestor';
934
+ }
935
+ }
936
+ }
937
+ }
938
+ }
939
+ }
940
+
941
+ //check that (a) we have items, and (b) if we must have current menu item, we've got it...
942
+ $continue = $this->_cmw_tree[0]['keepCount'] > 0 && ( !$cmw['contains_current'] || $this->_cmw_tree[ $currentItem ]['keep'] );
943
+ //check for title_from...
944
+ if( $continue ){
945
+ //might we want the parent's title as the widget title?...
946
+ if( $find_kids_of && $cmw['title_from_parent'] && $startWithKidsOf > 0 ){
947
+ $cmw['_walker']['parent_title'] = apply_filters(
948
+ 'the_title',
949
+ $elements[ $this->_cmw_tree[ $startWithKidsOf ]['element'][0] ]->title,
950
+ $elements[ $this->_cmw_tree[ $startWithKidsOf ]['element'][0] ]->ID
951
+ );
952
+ }
953
+ //might we want the current item's title as the widget title?...
954
+ if( !empty( $currentItem ) && $cmw['title_from_current'] ){
955
+ $cmw['_walker']['current_title'] = apply_filters(
956
+ 'the_title',
957
+ $elements[ $this->_cmw_tree[ $currentItem ]['element'][0] ]->title,
958
+ $elements[ $this->_cmw_tree[ $currentItem ]['element'][0] ]->ID
959
+ );
960
+ }
961
+ }
962
+
963
+ $this->_cmw_levels = null;
964
+ $substructure = array();
965
+ if( $continue ){
966
+ //now we need to gather together all the 'keep' items from the tree;
967
+ //while doing so, we need to set up levels and kids, ready for adding classes...
968
+ foreach( $this->_cmw_tree as $k => $v ){
969
+ if( $k > 0 && $v['keep'] ){
970
+ $substructure[ $k ] = $v;
971
+ //use kids as a has-submenu flag...
972
+ $substructure[ $k ]['kids'] = 0;
973
+ //any surviving parent (except the artificial level-0) should have submenu class set on it...
974
+ array_shift( $v['ancestors'] ); //remove the level-0
975
+ for( $i = count( $v['ancestors'] ) - 1; $i >= 0; $i-- ){
976
+ if( isset( $substructure[ $v['ancestors'][ $i ] ] ) ){
977
+ $substructure[ $v['ancestors'][ $i ] ]['kids']++;
978
+ }else{
979
+ //not a 'kept' ancestor so remove it...
980
+ array_splice( $v['ancestors'], $i, 1 );
981
+ }
982
+ }
983
+ //ancestors now only has 'kept' ancestors...
984
+ $substructure[ $k ]['level'] = count( $v['ancestors'] ) + 1;
985
+ //need to ensure that the parent_field of all the new top-level (ie. root) items is set to
986
+ //zero, otherwise the parent::walk() will assume they're orphans.
987
+ //however, we also need to check that parent_field of a child actually points to the closest
988
+ //'kept' ancestor; otherwise, given A (kept) > B (not kept) > C (kept) the parent_field of C
989
+ //would point to a non-existent B and would subsequently be considered an orphan!
990
+ if( $substructure[ $k ]['level'] == 1){
991
+ $ancestor = 0;
992
+ }else{
993
+ //set to the closest ancestor, ie. the new(?) parent...
994
+ $ancestor = array_slice( $v['ancestors'], -1, 1 );
995
+ $ancestor = $ancestor[0];
996
+ }
997
+ //take a copy of the elements item(s)...
998
+ $substructure[ $k ]['element'] = array();
999
+ foreach( $v['element'] as $i => $j ){
1000
+ $elements[ $j ]->$parent_field = $ancestor;
1001
+ $substructure[ $k ]['element'][] = $elements[ $j ];
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+ $this->_cmw_tree = null;
1007
+
1008
+ //put substructure's elements back into $elements (remember that $elements is a 1-based array!)...
1009
+ $elements = array();
1010
+ $n = 1;
1011
+ foreach( $substructure as $k => $v ){
1012
+ $ct = count( $v['element'] ) - 1;
1013
+ foreach( $v['element'] as $i => $j ){
1014
+ $elements[ $n ] = $j;
1015
+ //add the level class...
1016
+ $elements[ $n ]->classes[] = 'cmw-level-' . $v['level'];
1017
+ //add the submenu class? (only to last in group!)...
1018
+ if( $v['kids'] > 0 && $i == $ct ){
1019
+ $elements[ $n ]->classes[] = 'cmw-has-submenu';
1020
+ }elseif( $v['kids'] == 0 ){
1021
+ //3.7 adds a menu-item-has-children class to (original) menu items that have kids : remove it as the item is now childless...
1022
+ $elements[ $n ]->classes = array_diff( $elements[ $n ]->classes, array('menu-item-has-children') );
1023
+ }
1024
+ //add any other CMW classes...
1025
+ $elements[ $n ]->classes = array_merge( $elements[ $n ]->classes, $v['classes'] );
1026
+ $n++;
1027
+ }
1028
+ }
1029
+ unset( $substructure );
1030
+
1031
+ return $elements;
1032
+
1033
+ } //end _cmw_walk_legacy()
1034
+
1035
+ } //end Custom_Menu_Wizard_Walker class
1036
+ ?>
include/class.widget.php ADDED
@@ -0,0 +1,2101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Plugin Name: Custom Menu Wizard
4
+ *
5
+ * Custom Menu Wizard Widget class
6
+ */
7
+ class Custom_Menu_Wizard_Widget extends WP_Widget {
8
+
9
+ /**
10
+ * class constructor
11
+ */
12
+ 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_allow_legacy_update = true;
24
+ $this->_cmw_legacy_warnreadmore = 'http://wordpress.org/plugins/' . $this->id_base . '/changelog/';
25
+ //accessibility mode doesn't necessarily mean that javascript is disabled, but if javascript *is* disabled
26
+ //then accessibility mode *will* be on...
27
+ $this->_cmw_accessibility = isset( $_GET['editwidget'] ) && $_GET['editwidget'];
28
+ $this->_cmw_hash_ct = 0;
29
+
30
+ } //end __construct()
31
+
32
+ /**
33
+ * produces the backend admin form(s)
34
+ *
35
+ * @param array $instance Widget settings
36
+ */
37
+ function form( $instance ) {
38
+
39
+ //only call the legacy form method if the widget already exists and doesn't have a version number (old format)...
40
+ if( !empty( $instance ) && empty( $instance['cmwv'] ) ){
41
+ $this->cmw_legacy_form( $instance );
42
+ return;
43
+ }
44
+
45
+ //sanitize $instance...
46
+ $instance = $this->cmw_settings( $instance, array(), __FUNCTION__ );
47
+
48
+ //if no populated menus exist, suggest the user go create one...
49
+ if( ( $menus = $this->cmw_scan_menus( $instance['menu'], $instance['branch'] ) ) === false ){
50
+ ?>
51
+ <p class="widget-<?php echo $this->id_base; ?>-no-menus">
52
+ <em><?php printf( __('No populated menus have been created yet! <a href="%s">Create one...</a>'), admin_url('nav-menus.php') ); ?></em>
53
+ <input id="<?php echo $this->get_field_id('cmwv'); ?>" name="<?php echo $this->get_field_name('cmwv'); ?>"
54
+ type="hidden" value="<?php echo Custom_Menu_Wizard_Plugin::$version; ?>" />
55
+ <?php foreach( array('filters', 'fallbacks', 'output', 'container', 'classes', 'links') as $v ){ ?>
56
+ <input id="<?php echo $this->get_field_id("fs_$v"); ?>" name="<?php echo $this->get_field_name("fs_$v"); ?>"
57
+ type="hidden" value="<?php echo $instance["fs_$v"] ? '1' : '0' ?>" />
58
+ <?php } ?>
59
+ </p>
60
+ <?php
61
+ //all done : quit...
62
+ return;
63
+ }
64
+
65
+ //create the OPTIONs for the relative & absolute optgroups in the branch level select...
66
+ $absGroup = array();
67
+ $relGroup = array();
68
+ if( empty( $instance['branch_start'] ) ){
69
+ $branchLevel = '';
70
+ }else{
71
+ $branchLevel = $instance['branch_start'];
72
+ $i = substr( $branchLevel, 0, 1 );
73
+ //is it currently set relative?...
74
+ if( $i == '+' || $i == '-' ){
75
+ //if we only have 1 level then set to branch item...
76
+ if( $menus['selectedLevels'] < 2 ){
77
+ $branchLevel = '';
78
+ //otherwise, limit the 'relativeness' to 1 less than the number of levels
79
+ //available (ie. 5 levels available gives a range -4 thru to +4)...
80
+ }elseif( abs( intval( $branchLevel ) ) > $menus['selectedLevels'] - 1 ){
81
+ $branchLevel = $i . ($menus['selectedLevels'] - 1);
82
+ }
83
+ //max an absolute against the number of levels available...
84
+ }elseif( intval( $branchLevel ) > $menus['selectedLevels'] ){
85
+ $branchLevel = $menus['selectedLevels'];
86
+ }
87
+ }
88
+ //start with the middle option of the relatives (the level of the branch item)...
89
+ $relGroup[] = '<option value="" ' . selected( $branchLevel, '', false ) . '>' . __('the Branch') . '</option>';
90
+ //now do the absolutes and relatives...
91
+ for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){
92
+ //topmost of the absolutes gets ' (root)' appended to its descriptor...
93
+ $t = $i == 1 ? "$i (" . __('root') . ')' : $i;
94
+ //append to the absolutes...
95
+ $absGroup[] = '<option value="' . $i . '" ' . selected( $branchLevel, "$i", false ) . '>' . $t . '</option>';
96
+ //for anything LESS THAN the number of levels...
97
+ if( $i < $menus['selectedLevels'] ){
98
+ //immediately above the branch item gets ' (parent)' appended to its descriptor...
99
+ $t = $i == 1 ? "-$i (" . __('parent') . ')' : "-$i";
100
+ //prepend to the relatives...
101
+ array_unshift( $relGroup, '<option value="-' . $i . '" ' . selected( $branchLevel, "-$i", false ) . '>' . $t . '</option>' );
102
+ //immediately below the branch item gets ' (children)' appended to its descriptor...
103
+ $t = $i == 1 ? "+$i (" . __('children') . ')' : "+$i";
104
+ //append to the relatives...
105
+ array_push( $relGroup, '<option value="+' . $i . '" ' . selected( $branchLevel, "+$i", false ) . '>' . $t . '</option>' );
106
+ }
107
+ }
108
+
109
+ //set up some simple booleans for use at the disableif___ classes...
110
+ $isByItems = $instance['filter'] == 'items'; // disableif-ss (IS Items filter)
111
+ $isUnlimitedDepth = empty( $instance['depth'] ); // disableif-ud (IS unlimited depth)
112
+ $isNotByBranch = $instance['filter'] != 'branch'; // disableifnot-br (is NOT Branch filter)
113
+ $isNotBranchCurrentItem = $isNotByBranch || !empty( $instance['branch'] ); // disableifnot-br-ci (is NOT "Branch:Current Item")
114
+ $isNotFallbackParentCurrent = !in_array( $instance['fallback'], array('parent', 'current') ); //disableifnot-fb-pc (is NOT set to fall back to parent or current)
115
+
116
+ //NB the 'onchange' wrapper holds any text required by the "assist"
117
+ ?>
118
+ <div id="<?php echo $this->get_field_id('onchange'); ?>"
119
+ class="widget-<?php echo $this->id_base; ?>-onchange<?php echo $this->cmw_wp_version('3.8') ? ' cmw-pre-wp-v38' : ''; ?>"
120
+ data-cmw-v36plus='<?php echo $this->cmw_wp_version('3.6', true) ? 'true' : 'false'; ?>'
121
+ data-cmw-dialog-prompt='<?php _e('Click an item to toggle &quot;Current Menu Item&quot;'); ?>'
122
+ data-cmw-dialog-output='<?php _e('Basic Output'); ?>'
123
+ data-cmw-dialog-fallback='<?php _e('Fallback invoked'); ?>'
124
+ data-cmw-dialog-inclusions='<?php _e('Inclusions : 0'); ?>'
125
+ data-cmw-dialog-exclusions='<?php _e('Exclusions : 0'); ?>'
126
+ data-cmw-dialog-set-current='<?php _e('No Current Item!'); ?>'
127
+ data-cmw-dialog-shortcodes='<?php _e('Find posts/pages containing a CMW shortcode'); ?>'
128
+ data-cmw-dialog-untitled='<?php _e('untitled'); ?>'
129
+ data-cmw-dialog-fixed='<?php _e('fixed'); ?>'
130
+ data-cmw-dialog-nonce='<?php echo wp_create_nonce( 'cmw-find-shortcodes' ); ?>'
131
+ data-cmw-dialog-version='<?php echo Custom_Menu_Wizard_Plugin::$version; ?>'
132
+ data-cmw-dialog-id='<?php echo $this->get_field_id('dialog'); ?>'>
133
+
134
+ <?php
135
+ /**
136
+ * permanently visible section : Title (with Hide) and Menu
137
+ */
138
+ ?>
139
+ <p>
140
+ <input id="<?php echo $this->get_field_id('cmwv'); ?>" name="<?php echo $this->get_field_name('cmwv'); ?>"
141
+ type="hidden" value="<?php echo Custom_Menu_Wizard_Plugin::$version; ?>" />
142
+ <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:') ?></label>
143
+ <?php $this->cmw_formfield_checkbox( $instance, 'hide_title',
144
+ array(
145
+ 'label' => __('Hide'),
146
+ 'lclass' => 'alignright'
147
+ ) ); ?>
148
+ <?php $this->cmw_formfield_textbox( $instance, 'title',
149
+ array(
150
+ 'fclass' => 'widefat cmw-widget-title'
151
+ ) ); ?>
152
+ </p>
153
+
154
+ <p>
155
+ <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
156
+ <select id="<?php echo $this->get_field_id('menu'); ?>"
157
+ class="cmw-select-menu cmw-listen" name="<?php echo $this->get_field_name('menu'); ?>">
158
+ <?php echo $menus['names']; ?>
159
+ </select>
160
+ </p>
161
+
162
+ <?php
163
+ /**
164
+ * start collapsible section : 'Filter'
165
+ */
166
+ $this->cmw_open_a_field_section( $instance, __('Filters'), 'fs_filters' ); ?>
167
+
168
+ <div>
169
+ <?php $this->cmw_assist_link(); ?>
170
+ <strong><?php _e('Primary Filter'); ?></strong>
171
+
172
+ <br />
173
+ <label class="cmw-verticalalign-baseline">
174
+ <input id="<?php echo $this->get_field_id('filter'); ?>_0" class="cmw-bylevel cmw-listen"
175
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('filter'); ?>"
176
+ type="radio" value="" <?php checked( $instance['filter'], '' ); ?>
177
+ /><?php _e('Level:'); ?></label>
178
+ <select id="<?php echo $this->get_field_id('level'); ?>" class="cmw-level cmw-listen"
179
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('level'); ?>">
180
+ <?php for( $i = 1, $j = $instance['level'] > $menus['selectedLevels'] ? 1 : $instance['level']; $i <= $menus['selectedLevels']; $i++ ){ ?>
181
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php echo $i > 1 ? $i : $i . __(' (root)'); ?></option>
182
+ <?php } ?>
183
+ </select>
184
+
185
+ <br />
186
+ <label class="cmw-verticalalign-baseline">
187
+ <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="cmw-bybranch cmw-listen"
188
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('filter'); ?>"
189
+ type="radio" value="branch" <?php checked( $instance['filter'], 'branch' ); ?>
190
+ /><?php _e('Branch:'); ?></label>
191
+ <select id="<?php echo $this->get_field_id('branch'); ?>" class="cmw-branches cmw-assist-items cmw-listen"
192
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('branch'); ?>">
193
+ <option value="0" <?php selected( $instance['branch'], 0 ); ?>><?php _e('Current Item'); ?></option>
194
+ <?php echo $menus['selectedOptgroup']; ?>
195
+ </select>
196
+ <select id="<?php echo $this->get_field_id('branch_ignore'); ?>" class='cmw-off-the-page' disabled="disabled"
197
+ name="<?php echo $this->get_field_name('branch_ignore'); ?>">
198
+ <?php echo $menus['optgroups']; ?>
199
+ </select>
200
+
201
+ <br />
202
+ <label class="cmw-verticalalign-baseline">
203
+ <input id="<?php echo $this->get_field_id('filter'); ?>_2" class="cmw-byitems cmw-listen"
204
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('filter'); ?>"
205
+ type="radio" value="items" <?php checked( $instance['filter'], 'items' ); ?>
206
+ /><?php _e('Items:'); ?></label>
207
+ <?php $this->cmw_formfield_textbox( $instance, 'items',
208
+ array(
209
+ 'fclass' => 'cmw-maxwidth-twothirds cmw-setitems cmw-listen'
210
+ ) ); ?>
211
+ </div>
212
+
213
+ <div class="cmw-disableif-ss<?php $this->cmw_disableif( 'push', $isByItems ); ?>">
214
+ <?php $this->cmw_assist_link(); ?>
215
+ <strong><?php _e('Secondary Filter'); ?></strong>
216
+
217
+ <br />
218
+ <label class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>"><?php _e('Starting at:'); ?>
219
+ <select id="<?php echo $this->get_field_id('branch_start'); ?>" class="cmw-branch-start cmw-listen"
220
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('branch_start'); ?>">
221
+ <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-children="<?php _e('children'); ?>"
222
+ data-cmw-text-parent="<?php _e('parent'); ?>">
223
+ <?php echo implode( '', $relGroup ); ?>
224
+ </optgroup>
225
+ <optgroup label="<?php _e('absolute...'); ?>">
226
+ <?php echo implode( '', $absGroup ); ?>
227
+ </optgroup>
228
+ </select></label><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
229
+
230
+ <div class="cmw-indented">
231
+ <label class="cmw-followed-by">
232
+ <input id="<?php echo $this->get_field_id('start_mode'); ?>_0"
233
+ name="<?php echo $this->get_field_name('start_mode'); ?>"
234
+ <?php $this->cmw_disableif(); ?> type="radio" value="" <?php checked( $instance['start_mode'] !== 'level' ); ?>
235
+ /><?php printf( __('Item %1$s(if possible)%2$s'), '<small>', '</small>' ); ?></label>
236
+
237
+ <label class="cmw-whitespace-nowrap">
238
+ <input id="<?php echo $this->get_field_id('start_mode'); ?>_1" name="<?php echo $this->get_field_name('start_mode'); ?>"
239
+ <?php $this->cmw_disableif(); ?> type="radio" value="level" <?php checked( $instance['start_mode'] === 'level' ); ?>
240
+ /><?php _e('Level'); ?></label>
241
+
242
+ <br />
243
+ <?php $this->cmw_formfield_checkbox( $instance, 'allow_all_root',
244
+ array(
245
+ 'label' => __('Allow all Root Items'),
246
+ 'lclass' => 'cmw-disableifnot-br',
247
+ 'disableif' => $isNotByBranch
248
+ ) ); ?>
249
+ </div>
250
+
251
+ <label><?php _e('For Depth:'); ?>
252
+ <select id="<?php echo $this->get_field_id('depth'); ?>" data-cmw-text-levels="<?php _e('levels'); ?>"
253
+ <?php $this->cmw_disableif(); ?> class="cmw-depth cmw-listen" name="<?php echo $this->get_field_name('depth'); ?>">
254
+ <option value="0" <?php selected( $instance['depth'] > $menus['selectedLevels'] ? 0 : $instance['depth'], 0 ); ?>><?php _e('unlimited'); ?></option>
255
+ <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
256
+ <option value="<?php echo $i; ?>" <?php selected( $instance['depth'], $i ); ?>><?php printf( _n('%d level', '%d levels', $i), $i ); ?></option>
257
+ <?php } ?>
258
+ </select></label>
259
+
260
+ <div class="cmw-indented">
261
+ <?php $this->cmw_formfield_checkbox( $instance, 'depth_rel_current',
262
+ array(
263
+ 'label' => __('Relative to Current Item'),
264
+ 'lclass' => 'cmw-disableif-ud',
265
+ 'disableif' => $isUnlimitedDepth
266
+ ) ); ?>
267
+ </div>
268
+ </div><!-- end .cmw-disableif-ss --><?php $this->cmw_disableif( 'pop' ); ?>
269
+
270
+ <div>
271
+ <?php $this->cmw_assist_link(); ?>
272
+ <strong><?php _e('Inclusions'); ?></strong>
273
+ <br />
274
+ <label class="cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>"><?php _e('Branch Ancestors:'); ?>
275
+ <select id="<?php echo $this->get_field_id('ancestors'); ?>" class="cmw-ancestors cmw-listen"
276
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('ancestors'); ?>"
277
+ data-cmw-text-tolevel="<?php _e('to level'); ?>">
278
+ <option value="0" <?php selected( $j, 0 ); ?>>&nbsp;</option>
279
+ <?php
280
+ $j = $instance['ancestors'];
281
+ $j = max( min( $j, $menus['selectedLevels'] - 1 ), 1 - $menus['selectedLevels'] ); ?>
282
+ <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-for-option=" <?php _e('%d levels'); ?>">
283
+ <option value="-1" <?php selected( $j, -1 ); ?>><?php printf( __('%d level (parent)'), -1 ); ?></option>
284
+ <?php for( $i = -2; $i > 0 - $menus['selectedLevels']; $i-- ){ ?>
285
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('%d levels'), $i ); ?></option>
286
+ <?php } ?>
287
+ </optgroup>
288
+ <optgroup label="<?php _e('absolute...'); ?>" data-cmw-text-for-option="<?php _e('to level %d'); ?> ">
289
+ <option value="1" <?php selected( $j, 1 ); ?>><?php printf( __('to level %d (root)'), 1 ); ?></option>
290
+ <?php for( $i = 2; $i < $menus['selectedLevels']; $i++ ){ ?>
291
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('to level %d'), $i ); ?></option>
292
+ <?php } ?>
293
+ </optgroup>
294
+ </select></label><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
295
+
296
+ <div class="cmw-indented cmw-disableifnot-br<?php $this->cmw_disableif( 'push', $isNotByBranch ); ?>">
297
+ <label><?php _e('... with Siblings:'); ?>
298
+ <select id="<?php echo $this->get_field_id('ancestor_siblings'); ?>" class="cmw-ancestor-siblings cmw-listen"
299
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('ancestor_siblings'); ?>">
300
+ <option value="0" <?php selected( $j, 0 ); ?>>&nbsp;</option>
301
+ <?php
302
+ $j = $instance['ancestor_siblings'];
303
+ $j = max( min( $j, $menus['selectedLevels'] - 1 ), 1 - $menus['selectedLevels'] ); ?>
304
+ <optgroup label="<?php _e('relative...'); ?>" data-cmw-text-for-option=" <?php _e('%d levels'); ?>">
305
+ <option value="-1" <?php selected( $j, -1 ); ?>><?php printf( __('%d level (parent)'), -1 ); ?></option>
306
+ <?php for( $i = -2; $i > 0 - $menus['selectedLevels']; $i-- ){ ?>
307
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('%d levels'), $i ); ?></option>
308
+ <?php } ?>
309
+ </optgroup>
310
+ <optgroup label="<?php _e('absolute...'); ?>" data-cmw-text-for-option="<?php _e('to level %d'); ?> ">
311
+ <option value="1" <?php selected( $j, 1 ); ?>><?php printf( __('to level %d (root)'), 1 ); ?></option>
312
+ <?php for( $i = 2; $i < $menus['selectedLevels']; $i++ ){ ?>
313
+ <option value="<?php echo $i; ?>" <?php selected( $j, $i ); ?>><?php printf( __('to level %d'), $i ); ?></option>
314
+ <?php } ?>
315
+ </optgroup>
316
+ </select></label>
317
+ </div><!-- end .cmw-disableifnot-br --><?php $this->cmw_disableif( 'pop' ); ?>
318
+
319
+ <?php $this->cmw_formfield_checkbox( $instance, 'siblings',
320
+ array(
321
+ 'label' => __('Branch Siblings'),
322
+ 'lclass' => 'cmw-disableifnot-br',
323
+ 'disableif' => $isNotByBranch
324
+ ) ); ?>
325
+
326
+ <br />
327
+ <?php $this->cmw_formfield_checkbox( $instance, 'include_root',
328
+ array(
329
+ 'label' => __('All Root Items')
330
+ ) ); ?>
331
+ </div>
332
+
333
+ <div>
334
+ <?php $this->cmw_assist_link(); ?>
335
+ <strong><?php _e('Exclusions'); ?></strong>
336
+ <br />
337
+ <?php $this->cmw_formfield_textbox( $instance, 'exclude',
338
+ array(
339
+ 'label' => __('Item Ids:'),
340
+ 'fclass' => 'cmw-maxwidth-twothirds cmw-exclusions'
341
+ ) ); ?>
342
+ <br />
343
+ <label><?php _e('By Level:'); ?>
344
+ <select id="<?php echo $this->get_field_id('exclude_level'); ?>" class="cmw-exclude-level"
345
+ name="<?php echo $this->get_field_name('exclude_level'); ?>">
346
+ <?php $j = intval($instance['exclude_level']) > $menus['selectedLevels'] ? '' : $instance['exclude_level']; ?>
347
+ <option value="" <?php selected( $j, '' ); ?>>&nbsp;</option>
348
+ <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
349
+ <option value="<?php echo $i; ?>" <?php selected( $j, "$i" ); ?>><?php echo $i; ?></option>
350
+ <option value="<?php echo $i . '-'; ?>" <?php selected( $j, $i . '-' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and above'); ?></option>
351
+ <option value="<?php echo $i . '+'; ?>" <?php selected( $j, $i . '+' ); ?>>&nbsp;&nbsp;&nbsp;<?php echo $i . __(' and below'); ?></option>
352
+ <?php } ?>
353
+ </select></label>
354
+ </div>
355
+
356
+ <div>
357
+ <?php $this->cmw_assist_link(); ?>
358
+ <strong><?php _e('Qualifier'); ?></strong>
359
+ <br /><label for="<?php echo $this->get_field_id('contains_current'); ?>"><?php _e('Current Item is in:'); ?></label>
360
+ <select id="<?php echo $this->get_field_id('contains_current'); ?>"
361
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('contains_current'); ?>">
362
+ <option value="" <?php selected( $instance['contains_current'], '' ); ?>>&nbsp;</option>
363
+ <option value="menu" <?php selected( $instance['contains_current'], 'menu' ); ?>><?php echo _e('Menu'); ?></option>
364
+ <option value="primary" <?php selected( $instance['contains_current'], 'primary' ); ?>><?php echo _e('Primary Filter'); ?></option>
365
+ <option value="secondary" <?php selected( $instance['contains_current'], 'secondary' ); ?>><?php echo _e('Secondary Filter'); ?></option>
366
+ <option value="inclusions" <?php selected( $instance['contains_current'], 'inclusions' ); ?>><?php echo _e('Inclusions'); ?></option>
367
+ <option value="output" <?php selected( $instance['contains_current'], 'output' ); ?>><?php echo _e('Final Output'); ?></option>
368
+ </select>
369
+ </div>
370
+
371
+ <?php $this->cmw_close_a_field_section(); ?>
372
+
373
+ <?php
374
+ /**
375
+ * v1.2.0 start collapsible section : 'Fallbacks'
376
+ */
377
+ $this->cmw_open_a_field_section( $instance, __('Fallbacks'), 'fs_fallbacks' ); ?>
378
+
379
+ <div class="cmw-disableifnot-br-ci<?php $this->cmw_disableif( 'push', $isNotBranchCurrentItem ); ?>">
380
+ <?php $this->cmw_assist_link(); ?>
381
+
382
+ <label for="<?php echo $this->get_field_id('fallback'); ?>"><?php _e('If Current Item has no children:'); ?></label>
383
+ <select id="<?php echo $this->get_field_id('fallback'); ?>" class="cmw-fallback cmw-listen"
384
+ <?php $this->cmw_disableif(); ?> name="<?php echo $this->get_field_name('fallback'); ?>">
385
+ <option value="" <?php selected( $instance['fallback'], '' ); ?>>&nbsp;</option>
386
+ <option value="parent" <?php selected( $instance['fallback'], 'parent' ); ?>><?php _e('Start at : -1 (parent)'); ?></option>
387
+ <option value="current" <?php selected( $instance['fallback'], 'current' ); ?>><?php _e('Start at : the Current Item'); ?></option>
388
+ <option value="quit" <?php selected( $instance['fallback'], 'quit' ); ?>><?php _e('No output!'); ?></option>
389
+ </select>
390
+
391
+ <div class="cmw-indented cmw-disableifnot-fb-pc<?php $this->cmw_disableif( 'push', $isNotFallbackParentCurrent ); ?>">
392
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_siblings',
393
+ array(
394
+ 'label' => '&hellip;' . __('and Include its Siblings')
395
+ ) ); ?>
396
+
397
+ <br />
398
+ <label><?php _e('For Depth:'); ?>
399
+ <select id="<?php echo $this->get_field_id('fallback_depth'); ?>" data-cmw-text-levels="<?php _e('levels'); ?>"
400
+ <?php $this->cmw_disableif(); ?> class="cmw-fallback-depth" name="<?php echo $this->get_field_name('fallback_depth'); ?>">
401
+ <option value="0" <?php selected( $instance['fallback_depth'] > $menus['selectedLevels'] ? 0 : $instance['fallback_depth'], 0 ); ?>>&nbsp;</option>
402
+ <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
403
+ <option value="<?php echo $i; ?>" <?php selected( $instance['fallback_depth'], $i ); ?>><?php printf( _n('%d level', '%d levels', $i), $i ); ?></option>
404
+ <?php } ?>
405
+ </select></label>
406
+ <span class="cmw-small-block"><em class="cmw-colour-grey"><?php _e('Fallback Depth is Relative to Current Item!'); ?></em></span>
407
+ </div><!-- end .cmw-disableifnot-fb-pc --><?php $this->cmw_disableif( 'pop' ); ?>
408
+
409
+ </div><!-- end .cmw-disableifnot-br-ci --><?php $this->cmw_disableif( 'pop' ); ?>
410
+
411
+ <?php $this->cmw_close_a_field_section(); ?>
412
+
413
+ <?php
414
+ /**
415
+ * start collapsible section : 'Output'
416
+ */
417
+ $this->cmw_open_a_field_section( $instance, __('Output'), 'fs_output' ); ?>
418
+
419
+ <div>
420
+ <?php $this->cmw_assist_link(); ?>
421
+ <label class="cmw-followed-by">
422
+ <input id="<?php echo $this->get_field_id('flat_output'); ?>_0"
423
+ name="<?php echo $this->get_field_name('flat_output'); ?>"
424
+ <?php $this->cmw_disableif(); ?> type="radio" value="0" <?php checked( !$instance['flat_output'] ); ?>
425
+ /><?php _e('Hierarchical'); ?></label>
426
+ <label class="cmw-whitespace-nowrap">
427
+ <input id="<?php echo $this->get_field_id('flat_output'); ?>_1"
428
+ name="<?php echo $this->get_field_name('flat_output'); ?>"
429
+ <?php $this->cmw_disableif(); ?> type="radio" value="1" <?php checked( $instance['flat_output'] ); ?>
430
+ /><?php _e('Flat'); ?></label>
431
+ </div>
432
+
433
+ <div>
434
+ Set Title from:
435
+ <br>
436
+ <?php $this->cmw_formfield_checkbox( $instance, 'title_from_current',
437
+ array(
438
+ 'label' => __('Current Item'),
439
+ 'lclass' => 'cmw-followed-by'
440
+ ) ); ?>
441
+ <?php $this->cmw_formfield_checkbox( $instance, 'title_from_current_root',
442
+ array(
443
+ 'label' => '&hellip;' . __('or its Root'),
444
+ 'lclass' => 'cmw-whitespace-nowrap'
445
+ ) ); ?>
446
+ <br />
447
+ <?php $this->cmw_formfield_checkbox( $instance, 'title_from_branch',
448
+ array(
449
+ 'label' => __('Branch'),
450
+ 'lclass' => 'cmw-followed-by cmw-disableifnot-br',
451
+ 'disableif' => $isNotByBranch
452
+ ) ); ?>
453
+ <?php $this->cmw_formfield_checkbox( $instance, 'title_from_branch_root',
454
+ array(
455
+ 'label' => '&hellip;' . __('or its Root'),
456
+ 'lclass' => 'cmw-whitespace-nowrap cmw-disableifnot-br',
457
+ 'disableif' => $isNotByBranch
458
+ ) ); ?>
459
+ </div>
460
+
461
+ <div>
462
+ Change UL to OL:
463
+ <br />
464
+ <?php $this->cmw_formfield_checkbox( $instance, 'ol_root',
465
+ array(
466
+ 'label' => __('Top Level'),
467
+ 'lclass' => 'cmw-followed-by'
468
+ ) ); ?>
469
+ <?php $this->cmw_formfield_checkbox( $instance, 'ol_sub',
470
+ array(
471
+ 'label' => __('Sub-Levels'),
472
+ 'lclass' => 'cmw-whitespace-nowrap'
473
+ ) ); ?>
474
+ </div>
475
+
476
+ <?php
477
+ //v1.1.0 As of WP v3.6, wp_nav_menu() automatically cops out (without outputting any HTML) if there are no items,
478
+ // so the hide_empty option becomes superfluous; however, I'll keep the previous setting (if there was one)
479
+ // in case of reversion to an earlier version of WP...
480
+ if( $this->cmw_wp_version('3.6') ){
481
+ ?>
482
+ <div>
483
+ <?php $this->cmw_formfield_checkbox( $instance, 'hide_empty',
484
+ array(
485
+ 'label' => __('Hide Widget if Empty'),
486
+ 'desc' => __('Prevents any output when no items are found')
487
+ ) ); ?>
488
+ </div>
489
+ <?php }else{ ?>
490
+ <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
491
+ type="hidden" value="<?php echo $instance['hide_empty'] ? '1' : ''; ?>" />
492
+ <?php } ?>
493
+
494
+ <?php $this->cmw_close_a_field_section(); ?>
495
+
496
+ <?php
497
+ /**
498
+ * start collapsible section : 'Container'
499
+ */
500
+ $this->cmw_open_a_field_section( $instance, __('Container'), 'fs_container' ); ?>
501
+
502
+ <div>
503
+ <?php $this->cmw_formfield_textbox( $instance, 'container',
504
+ array(
505
+ 'label' => __('Element:'),
506
+ 'desc' => __('Eg. div or nav; leave empty for no container')
507
+ ) ); ?>
508
+ </div>
509
+ <div>
510
+ <?php $this->cmw_formfield_textbox( $instance, 'container_id',
511
+ array(
512
+ 'label' => __('Unique ID:'),
513
+ 'desc' => __('An optional ID for the container')
514
+ ) ); ?>
515
+ </div>
516
+ <div>
517
+ <?php $this->cmw_formfield_textbox( $instance, 'container_class',
518
+ array(
519
+ 'label' => __('Class:'),
520
+ 'desc' => __('Extra class for the container')
521
+ ) ); ?>
522
+ </div>
523
+
524
+ <?php $this->cmw_close_a_field_section(); ?>
525
+
526
+ <?php
527
+ /**
528
+ * start collapsible section : 'Classes'
529
+ */
530
+ $this->cmw_open_a_field_section( $instance, __('Classes'), 'fs_classes' ); ?>
531
+
532
+ <div>
533
+ <?php $this->cmw_formfield_textbox( $instance, 'menu_class',
534
+ array(
535
+ 'label' => __('Menu Class:'),
536
+ 'desc' => __('Class for the list element forming the menu')
537
+ ) ); ?>
538
+ </div>
539
+ <div>
540
+ <?php $this->cmw_formfield_textbox( $instance, 'widget_class',
541
+ array(
542
+ 'label' => __('Widget Class:'),
543
+ 'desc' => __('Extra class for the widget itself')
544
+ ) ); ?>
545
+ </div>
546
+
547
+ <?php $this->cmw_close_a_field_section(); ?>
548
+
549
+ <?php
550
+ /**
551
+ * start collapsible section : 'Links'
552
+ */
553
+ $this->cmw_open_a_field_section( $instance, __('Links'), 'fs_links' ); ?>
554
+
555
+ <div>
556
+ <?php $this->cmw_formfield_textbox( $instance, 'before',
557
+ array(
558
+ 'label' => __('Before the Link:'),
559
+ 'desc' =>__( htmlspecialchars('Text/HTML to go before the </a> of the link') ),
560
+ 'fclass' => 'widefat'
561
+ ) ); ?>
562
+ </div>
563
+ <div>
564
+ <?php $this->cmw_formfield_textbox( $instance, 'after',
565
+ array(
566
+ 'label' => __('After the Link:'),
567
+ 'desc' => __( htmlspecialchars('Text/HTML to go after the </a> of the link') ),
568
+ 'fclass' => 'widefat'
569
+ ) ); ?>
570
+ </div>
571
+ <div>
572
+ <?php $this->cmw_formfield_textbox( $instance, 'link_before',
573
+ array(
574
+ 'label' => __('Before the Link Text:'),
575
+ 'desc' => __('Text/HTML to go before the link text'),
576
+ 'fclass' => 'widefat'
577
+ ) ); ?>
578
+ </div>
579
+ <div>
580
+ <?php $this->cmw_formfield_textbox( $instance, 'link_after',
581
+ array(
582
+ 'label' => __('After the Link Text:'),
583
+ 'desc' => __('Text/HTML to go after the link text'),
584
+ 'fclass' => 'widefat'
585
+ ) ); ?>
586
+ </div>
587
+
588
+ <?php $this->cmw_close_a_field_section(); ?>
589
+
590
+ <div class="cmw-shortcode-wrap"><code class="widget-<?php echo $this->id_base; ?>-shortcode ui-corner-all" title="<?php _e('shortcode'); ?>"><?php echo $this->cmw_shortcode( $instance ); ?></code></div>
591
+
592
+ </div>
593
+ <?php
594
+
595
+ if( $this->_cmw_accessibility ){
596
+ ?>
597
+ <script type="text/javascript">jQuery(function($){ $('#<?php echo $this->get_field_id('menu'); ?>').trigger('change'); });</script>
598
+ <?php
599
+ }
600
+
601
+ } //end form()
602
+
603
+ /**
604
+ * sanitizes/updates the widget settings sent from the backend admin
605
+ *
606
+ * @param array $new_instance New widget settings
607
+ * @param array $old_instance Old widget settings
608
+ * @return array Sanitized widget settings
609
+ */
610
+ function update( $new_instance, $old_instance ) {
611
+
612
+ //call the legacy update method for updates to existing widgets that don't have a version number (old format)...
613
+ if( empty( $new_instance['cmwv'] ) ){
614
+ return $this->cmw_legacy_update( $new_instance, $old_instance );
615
+ }
616
+
617
+ return $this->cmw_settings( $new_instance, $old_instance, __FUNCTION__ );
618
+
619
+ } //end update()
620
+
621
+ /**
622
+ * produces the widget HTML at the front end
623
+ *
624
+ * @filters : custom_menu_wizard_nav_params array of params that will be sent to wp_nav_menu(), array of instance settings, id base
625
+ * custom_menu_wizard_settings_pre_widget array of instance settings, id base
626
+ * custom_menu_wizard_widget_output HTML output string, array of instance settings, id base
627
+ *
628
+ * @param object $args Widget arguments
629
+ * @param array $instance Configuration for this widget instance
630
+ */
631
+ function widget( $args, $instance ) {
632
+
633
+ //call the legacy widget method for producing existing widgets that don't have a version number (old format)...
634
+ if( empty( $instance['cmwv'] ) ){
635
+ $this->cmw_legacy_widget( $args, $instance );
636
+ return;
637
+ }
638
+
639
+ //sanitize $instance...
640
+ $instance = $this->cmw_settings( $instance, array(), __FUNCTION__ );
641
+
642
+ extract( $args, EXTR_SKIP );
643
+
644
+ //v1.1.0 As of WP v3.6, wp_nav_menu() automatically prevents any HTML output if there are no items...
645
+ $instance['hide_empty'] = $instance['hide_empty'] && $this->cmw_wp_version('3.6');
646
+
647
+ //allow a filter to amend the instance settings prior to producing the widget output...
648
+ //eg. add_filter( 'custom_menu_wizard_settings_pre_widget', [filter_function], 10, 2 ) => $instance (array)
649
+ $instance = apply_filters( 'custom_menu_wizard_settings_pre_widget', $instance, $this->id_base );
650
+
651
+ //fetch menu...
652
+ if( !empty( $instance['menu'] ) ){
653
+ $menu = wp_get_nav_menu_object( $instance['menu'] );
654
+
655
+ //no menu, no output...
656
+ if ( !empty( $menu ) ){
657
+
658
+ if( !empty( $instance['container_class'] ) ){
659
+ //the menu-[menu->slug]-container class gets applied by WP UNLESS an alternative
660
+ //container class is supplied in the params - I'm going to set the param such that
661
+ //this instance's container class (if specified) gets applied IN ADDITION TO the
662
+ //default one...
663
+ $instance['container_class'] = "menu-{$menu->slug}-container {$instance['container_class']}";
664
+ }
665
+
666
+ $instance['menu_class'] = preg_split( '/\s+/', $instance['menu_class'], -1, PREG_SPLIT_NO_EMPTY );
667
+ if( $instance['fallback'] ){
668
+ //add a cmw-fellback-maybe class to the menu and we'll remove or replace it later...
669
+ $instance['menu_class'][] = 'cmw-fellback-maybe';
670
+ }
671
+ $instance['menu_class'] = implode( ' ', $instance['menu_class'] );
672
+
673
+ $walker = new Custom_Menu_Wizard_Walker;
674
+ $params = array(
675
+ 'menu' => $menu,
676
+ 'container' => empty( $instance['container'] ) ? false : $instance['container'],
677
+ 'container_id' => $instance['container_id'],
678
+ 'menu_class' => $instance['menu_class'],
679
+ 'echo' => false,
680
+ 'fallback_cb' => false,
681
+ 'before' => $instance['before'],
682
+ 'after' => $instance['after'],
683
+ 'link_before' => $instance['link_before'],
684
+ 'link_after' => $instance['link_after'],
685
+ 'depth' => $instance['flat_output'] ? -1 : $instance['depth'],
686
+ 'walker' =>$walker,
687
+ //widget specific stuff...
688
+ '_custom_menu_wizard' => $instance
689
+ );
690
+ //for the walker's use...
691
+ $params['_custom_menu_wizard']['_walker'] = array();
692
+ //set wrapper to UL or OL...
693
+ $dataCMWS = empty( $instance['cmws_off'] ) ? " data-cmws='" . esc_attr( $this->cmw_shortcode( $instance, true ) ) . "'" : '';
694
+ if( $instance['ol_root'] ){
695
+ $params['items_wrap'] = '<ol id="%1$s" class="%2$s" data-cmwv="' . $instance['cmwv'] . '"' . $dataCMWS . '>%3$s</ol>';
696
+ }else{
697
+ $params['items_wrap'] = '<ul id="%1$s" class="%2$s" data-cmwv="' . $instance['cmwv'] . '"' . $dataCMWS . '>%3$s</ul>';
698
+ }
699
+ //add a container class...
700
+ if( !empty( $instance['container_class'] ) ){
701
+ $params['container_class'] = $instance['container_class'];
702
+ }
703
+
704
+ //add my filters...
705
+ add_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
706
+ if( $instance['hide_empty'] ){
707
+ add_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
708
+ }
709
+
710
+ //allow a filter to amend the wp_nav_menu() params prior to calling it...
711
+ //eg. add_filter( 'custom_menu_wizard_nav_params', [filter_function], 10, 3 ) => $params (array)
712
+ //NB: wp_nav_menu() is in wp-includes/nav-menu-template.php
713
+ $out = wp_nav_menu( apply_filters( 'custom_menu_wizard_nav_params', $params, $instance, $this->id_base ) );
714
+
715
+ //remove my filters...
716
+ remove_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
717
+ if( $instance['hide_empty'] ){
718
+ remove_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
719
+ }
720
+
721
+ //only put something out if there is something to put out...
722
+ if( !empty( $out ) ){
723
+
724
+ //title from : priority is current -> current root -> branch -> branch root...
725
+ //note that none actually have to be present in the results
726
+ foreach( array('current', 'current_root', 'branch', 'branch_root') as $v){
727
+ if( $instance[ 'title_from_' . $v ] && !empty( $this->_cmw_walker[ $v . '_title' ] ) ){
728
+ $title = $this->_cmw_walker[ $v . '_title' ];
729
+ break;
730
+ }
731
+ }
732
+ if( empty( $title ) ){
733
+ $title = $instance['hide_title'] ? '' : $instance['title'];
734
+ }
735
+ //allow the widget_title filter to override anything we've set up...
736
+ $title = apply_filters('widget_title', $title, $instance, $this->id_base);
737
+
738
+ //remove/replace the cmw-fellback-maybe class...
739
+ $out = str_replace(
740
+ 'cmw-fellback-maybe',
741
+ empty( $this->_cmw_walker['fellback'] ) ? '' : 'cmw-fellback-' . $this->_cmw_walker['fellback'],
742
+ $out );
743
+
744
+ //try to add widget_class (if specified) to before_widget...
745
+ if( !empty( $instance['widget_class'] ) && !empty( $before_widget ) ){
746
+ //$before_widget is usually just a DIV start-tag, with an id and a class; if it
747
+ //gets more complicated than that then this may not work as expected...
748
+ if( preg_match( '/^<[^>]+?class=["\']/', $before_widget ) > 0 ){
749
+ //...already has a class attribute : prepend mine...
750
+ $before_widget = preg_replace( '/(class=["\'])/', '$1' . $instance['widget_class'] . ' ', $before_widget, 1 );
751
+ }else{
752
+ //...doesn't currently have a class : add class attribute...
753
+ $before_widget = preg_replace( '/^(<\w+)(\s|>)/', '$1 class="' . $instance['widget_class'] . '"$2', $before_widget );
754
+ }
755
+ }
756
+
757
+ if( !empty( $title ) ){
758
+ $out = $before_title . $title . $after_title . $out;
759
+ }
760
+ $out = $before_widget . $out . $after_widget;
761
+ //allow a filter to modify the entire output...
762
+ //eg. add_filter( 'custom_menu_wizard_widget_output', [filter_function], 10, 3 ) => $output (HTML string)
763
+ echo apply_filters( 'custom_menu_wizard_widget_output', $out, $instance, $this->id_base );
764
+ }
765
+ }
766
+ }
767
+
768
+ } //end widget()
769
+
770
+ /**
771
+ * outputs an assist anchor
772
+ */
773
+ function cmw_assist_link(){
774
+
775
+ //don't really need to worry about the id for non-javascript enabled usage because the css hides the
776
+ //button, but it doesn't hurt so I've left it in...
777
+ $hashid = $this->get_field_id( 'cmw' . ++$this->_cmw_hash_ct );
778
+ ?>
779
+ <a class="widget-<?php echo $this->id_base; ?>-assist button" id="<?php echo $hashid; ?>" href="#<?php echo $hashid; ?>"><?php _e('assist'); ?></a>
780
+ <?php
781
+
782
+ }
783
+
784
+ /**
785
+ * outputs the HTML to close off a collapsible/expandable group of settings
786
+ */
787
+ function cmw_close_a_field_section(){
788
+
789
+ ?></div><?php
790
+
791
+ } //end cmw_close_a_field_section()
792
+
793
+ /**
794
+ * either pushes, pops, or echoes last of, the disabled attributes array
795
+ * note that if accessibility mode is on, nothing should get disabled!
796
+ *
797
+ * @param string $action 'pop' or 'push'
798
+ * @param boolean $test What to push
799
+ */
800
+ function cmw_disableif( $action = 'echo', $test = false ){
801
+ if( !isset( $this->_cmw_disableif ) ){
802
+ $this->_cmw_disableif = array( '' );
803
+ }
804
+ if( $action == 'push' ){
805
+ if( $test && !$this->_cmw_accessibility ){
806
+ //append disabled attribute...
807
+ $this->_cmw_disableif[] = 'disabled="disabled"';
808
+ //and echo disabled class...
809
+ echo ' cmw-colour-grey';
810
+ }else{
811
+ //append a copy of current last element (maintaining status quo)...
812
+ $e = array_slice( $this->_cmw_disableif, -1 );
813
+ $this->_cmw_disableif[] = $e[0];
814
+ }
815
+ }elseif( $action == 'pop' ){
816
+ //remove last element (if count is greater than 1, so it is never left totally empty by mistake)...
817
+ if( count( $this->_cmw_disableif ) > 1 ){
818
+ array_pop( $this->_cmw_disableif );
819
+ }
820
+ }else{
821
+ //echo last element...
822
+ $e = array_slice( $this->_cmw_disableif, -1 );
823
+ echo $e[0];
824
+ }
825
+ }
826
+
827
+ /**
828
+ * this gets run (filter: wp_nav_menu_{$menu->slug}_items) if hide_empty is set
829
+ * if $items is empty then add a wp_nav_menu filter to do the actual return of an empty string
830
+ * it gets run before the wp_nav_menu filter, but it gets the $items array whereas the wp_nav_menu filter does not
831
+ * it gets added by $this->widget() before wp_nav_menu() is called, and removed immediately after wp_nav_menu() returns
832
+ *
833
+ * v1.1.0 As of WP v3.6 this method becomes superfluous because wp_nav_menu() has had code added to immediately
834
+ * cop out (return false) if the output from wp_nav_menu_{$menu->slug}_items filter(s) is empty.
835
+ * However, it stays in so as to cope with versions < 3.6
836
+ *
837
+ * @param array $items Menu items
838
+ * @param object $args
839
+ * @return array Menu items
840
+ */
841
+ function cmw_filter_check_for_no_items($items, $args){
842
+
843
+ if( !empty( $args->_custom_menu_wizard ) && empty( $items ) ){
844
+ add_filter( 'wp_nav_menu', array( $this, 'cmw_filter_no_output_when_empty' ), 65532, 2 );
845
+ }
846
+ return $items;
847
+
848
+ } //end cmw_filter_check_for_no_items()
849
+
850
+ /**
851
+ * this (filter: wp_nav_menu) merely removes itself from the filters and returns an empty string
852
+ * it gets added by the cmw_filter_check_for_no_items method below, and only
853
+ * ever gets run when hide_empty is set on the widget instance
854
+ *
855
+ * v1.1.0 As of WP v3.6 this method becomes superfluous because wp_nav_menu() has had code added to immediately
856
+ * cop out (return false) if the output from wp_nav_menu_{$menu->slug}_items filter(s) is empty.
857
+ * However, it stays in so as to cope with versions < 3.6
858
+ *
859
+ * @param string $nav_menu HTML for the menu
860
+ * @param object $args
861
+ * @return string HTML for the menu
862
+ */
863
+ function cmw_filter_no_output_when_empty($nav_menu, $args){
864
+
865
+ remove_filter( 'wp_nav_menu', array( $this, __FUNCTION__ ), 65532, 2 );
866
+ return empty( $args->_custom_menu_wizard ) ? $nav_menu : '';
867
+
868
+ } //end cmw_filter_no_output_when_empty()
869
+
870
+ /**
871
+ * v1.2.1 stores any walker-determined information back into the widget instance
872
+ * gets run by the walker, on the filtered array of menu items, just before running parent::walk()
873
+ * only gets run *if* there are menu items found
874
+ *
875
+ * @param array $items Filtered menu items
876
+ * @param object $args
877
+ * @return array Menu items
878
+ */
879
+ function cmw_filter_walker_items( $items, $args ){
880
+
881
+ if( !empty( $args->_custom_menu_wizard['_walker'] ) ){
882
+ $this->_cmw_walker = $args->_custom_menu_wizard['_walker'];
883
+ }
884
+ return $items;
885
+
886
+ } //end cmw_filter_walker_items()
887
+
888
+ /**
889
+ * output a checkbox field
890
+ *
891
+ * @param array $instance Contains current field value
892
+ * @param string $field Field name
893
+ * @param array $params Attribute values
894
+ */
895
+ function cmw_formfield_checkbox( &$instance, $field, $params ){
896
+
897
+ $labelClass = empty( $params['lclass'] ) ? '' : $params['lclass'];
898
+ $fieldClass = empty( $params['fclass'] ) ? '' : $params['fclass'];
899
+ $disabling = !empty( $labelClass ) && isset( $params['disableif'] );
900
+ ?>
901
+ <label class="<?php echo $labelClass; if( $disabling ){ $this->cmw_disableif( 'push', $params['disableif'] ); } ?>">
902
+ <input id="<?php echo $this->get_field_id( $field ); ?>" class="<?php echo $fieldClass; ?>"
903
+ name="<?php echo $this->get_field_name( $field ); ?>" <?php $this->cmw_disableif(); ?>
904
+ <?php checked($instance[ $field ]); ?> type="checkbox" value="1"
905
+ /><?php echo $params['label']; ?></label><?php if( $disabling ){ $this->cmw_disableif( 'pop' ); } ?>
906
+ <?php
907
+ if( !empty( $params['desc'] ) ){
908
+ ?>
909
+ <span class="cmw-small-block"><em class="cmw-colour-grey"><?php echo $params['desc']; ?></em></span>
910
+ <?php
911
+ }
912
+
913
+ } // end cmw_formfield_checkbox()
914
+
915
+ /**
916
+ * output a text input field
917
+ *
918
+ * @param array $instance Contains current field value
919
+ * @param string $field Field name
920
+ * @param array $params Attribute values
921
+ */
922
+ function cmw_formfield_textbox( &$instance, $field, $params ){
923
+
924
+ $fieldClass = empty( $params['fclass'] ) ? '' : $params['fclass'];
925
+
926
+ if( !empty( $params['label'] ) ){
927
+ ?>
928
+ <label for="<?php echo $this->get_field_id( $field ); ?>"><?php echo $params['label']; ?></label>
929
+ <?php
930
+ }
931
+ ?>
932
+ <input id="<?php echo $this->get_field_id( $field ); ?>" class="<?php echo $fieldClass; ?>"
933
+ name="<?php echo $this->get_field_name( $field ); ?>" <?php $this->cmw_disableif(); ?>
934
+ type="text" value="<?php echo $instance[ $field ]; ?>" />
935
+ <?php
936
+ if( !empty( $params['desc'] ) ){
937
+ ?>
938
+ <span class="cmw-small-block"><em class="cmw-colour-grey"><?php echo $params['desc']; ?></em></span>
939
+ <?php
940
+ }
941
+
942
+ } // end cmw_formfield_textbox()
943
+
944
+ /**
945
+ * gets menus (in name order) and their items, returning empty array if there are none (or if none have items)
946
+ *
947
+ * @param integer $selectedMenu (by reference) The instance setting to check against for a menu to be "selected"
948
+ * @return array
949
+ */
950
+ function cmw_get_custom_menus( &$selectedMenu ){
951
+
952
+ $findSM = $selectedMenu > 0;
953
+ $menus = wp_get_nav_menus( array( 'orderby' => 'name' ) );
954
+ if( !empty( $menus ) ){
955
+ foreach( $menus as $i => $menu ){
956
+ //find the menu's items, then remove any menus that have no items...
957
+ $menus[ $i ]->_items = wp_get_nav_menu_items( $menu->term_id );
958
+ if( empty( $menus[ $i ]->_items ) ){
959
+ unset( $menus[ $i ] );
960
+ }else{
961
+ //if the items are all orphans, then remove the menu...
962
+ $rootItem = false;
963
+ foreach( $menus[ $i ]->_items as $item ){
964
+ $rootItem = $rootItem || $item->menu_item_parent == 0;
965
+ }
966
+ if( !$rootItem ){
967
+ unset( $menus[ $i ] );
968
+ }elseif( $findSM && $selectedMenu == $menu->term_id ){
969
+ $findSM = false;
970
+ }
971
+ }
972
+ }
973
+ }
974
+ //if findSM is TRUE then we were looking for a specific menu and failed to find it (or it had no eligible items)...
975
+ if( $findSM ){
976
+ //clear selectedMenu...
977
+ $selectedMenu = 0;
978
+ //this would be the place to flag a warning!
979
+ }
980
+
981
+ return empty( $menus ) ? array() : array_values( $menus );
982
+
983
+ } // end cmw_get_custom_menus()
984
+
985
+ /**
986
+ * gets the various option, optgroups, max levels, etc, from the available custom menus (if any)
987
+ *
988
+ * @param integer $selectedMenu The instance setting to check against for a menu to be "selected"
989
+ * @param integer $selectedItem The instance setting to check against for an menu item to be "selected"
990
+ * @return array|boolean
991
+ */
992
+ function cmw_scan_menus( $selectedMenu, $selectedItem ){
993
+
994
+ //create the options for the menu select & branch select...
995
+ // IE is a pita when it comes to SELECTs because it ignores any styling on OPTGROUPs and OPTIONs, so I'm using
996
+ // a copy from which the javascript can pick the relevant OPTGROUP
997
+ $rtn = array(
998
+ 'maxlevel' => 1, //maximum number of levels (across all menus)
999
+ 'names' => array(), //HTML of OPTIONs, for selecting a menu (returned as a string)
1000
+ 'optgroups' => array(), //HTML of OPTGROUPs & contained OPTIONs, for selecting an item (returned as a string)
1001
+ 'selectedOptgroup' => array(''), //HTML of currently selected menu's OPTGROUP and its OPTIONs (returned as string)
1002
+ 'selectedBranchName' => __('the Current Item'), //title of currently selected menu item
1003
+ 'selectedLevels' => 1 //number of levels in the currently selected menu
1004
+ );
1005
+
1006
+ //couple of points:
1007
+ // - if there's no currently selected menu (eg. it's a new, unsaved form) then use the first menu found that has eligible items
1008
+ // - if there is a currently selected menu, but that menu is no longer available (no longer exists, or now has no eligible items)
1009
+ // then, again, use the first menu found that does have items. PROBLEM : this means that the widget's instance settings
1010
+ // won't match what the widget is currently displaying! this situation is not unique to this function because it can
1011
+ // also occur for things like depth, but it does raise the question of whether the user should be informed that what
1012
+ // is being presented does not match the current saved settings?
1013
+ // Note that also applies to selected item (ie. the menu still exists but the currently selected item within that menu does not).
1014
+
1015
+ $ct = 0;
1016
+ $sogCt = 0;
1017
+ $itemindents = $menu = $item = NULL;
1018
+ //note that fetching the menus can clear selectedMenu!
1019
+ foreach( $this->cmw_get_custom_menus( $selectedMenu ) as $i => $menu ){
1020
+ $maxgrplevel = 1;
1021
+ $itemindents = array( '0' => 0 );
1022
+ $menuGrpOpts = '';
1023
+ //don't need to check for existence of items because if there were none then the menu wouldn't be here!
1024
+ foreach( $menu->_items as $item ){
1025
+ //exclude orphans!
1026
+ if( isset($itemindents[ $item->menu_item_parent ])){
1027
+ $title = apply_filters( 'the_title', $item->title, $item->ID );
1028
+ $level = $itemindents[ $item->menu_item_parent ] + 1;
1029
+
1030
+ $itemindents[ $item->ID ] = $level;
1031
+ $rtn['maxlevel'] = max( $rtn['maxlevel'], $level );
1032
+ $maxgrplevel = max( $maxgrplevel, $level );
1033
+
1034
+ //if there is no currently selected menu AND this is the first found item for this menu then
1035
+ //set this menu as the currently selected menu (being the first one found with an eligible item)...
1036
+ if( empty( $selectedMenu ) && empty( $menuGrpOpts ) ){
1037
+ $selectedMenu = $menu->term_id;
1038
+ }
1039
+ //only if THIS is the currently selected menu do we determine "selected" for each menu item...
1040
+ if( $selectedMenu == $menu->term_id ){
1041
+ $selected = selected( $selectedItem, $item->ID, false );
1042
+ if( !empty( $selected ) ){
1043
+ $rtn['selectedBranchName'] = $title;
1044
+ }
1045
+ $rtn['selectedOptgroup'][ $sogCt ] .= '<option value="' . $item->ID . '" ' . $selected . ' data-cmw-level="' . $level . '">';
1046
+ $rtn['selectedOptgroup'][ $sogCt ] .= str_repeat( '&nbsp;', ($level - 1) * 3 ) . $title . '</option>';
1047
+ }
1048
+ //don't set "selected" on the big list...
1049
+ $menuGrpOpts .= '<option value="' . $item->ID . '" data-cmw-level="' . $level . '">';
1050
+ $menuGrpOpts .= str_repeat( '&nbsp;', ($level - 1) * 3 ) . $title . '</option>';
1051
+ }
1052
+ }
1053
+
1054
+ //should never be empty, but check nevertheless...
1055
+ if( !empty( $menuGrpOpts ) ){
1056
+ $rtn['names'][ $ct ] = '<option ' . selected( $selectedMenu, $menu->term_id, false ) . ' value="' . $menu->term_id . '">' . $menu->name . '</option>';
1057
+ $rtn['optgroups'][ $ct ] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $ct . '" data-cmw-max-level="' . $maxgrplevel . '">';
1058
+ $rtn['optgroups'][ $ct ] .= $menuGrpOpts;
1059
+ $rtn['optgroups'][ $ct ] .= '</optgroup>';
1060
+ //if this menu is selected, then store this optgroup as the selected optgroup, and the number of levels it has...
1061
+ if( $selectedMenu == $menu->term_id ){
1062
+ $rtn['selectedOptgroup'][ $sogCt ] = '<optgroup label="' . $menu->name . '" data-cmw-optgroup-index="' . $ct . '" data-cmw-max-level="' . $maxgrplevel . '">' . $rtn['selectedOptgroup'][ $sogCt ] . '</optgroup>';
1063
+ $rtn['selectedOptgroup'][ ++$sogCt ] = '';
1064
+ $rtn['selectedLevels'] = $maxgrplevel;
1065
+ }elseif( $this->_cmw_accessibility ){
1066
+ //if accessibility is on then the selected groups need to contain *all* the groups otherwise, with javascript disabled, the
1067
+ //user will not be able to select menu items from a switched menu without saving first. if javascript is not disabled, then
1068
+ //the script should initially remove any optgroups not immediately required...
1069
+ $rtn['selectedOptgroup'][ $sogCt ] = $rtn['optgroups'][ $ct ];
1070
+ $rtn['selectedOptgroup'][ ++$sogCt ] = '';
1071
+ }
1072
+ $ct++;
1073
+ }
1074
+ }
1075
+ unset( $itemindents, $menu, $item );
1076
+
1077
+ if( empty( $rtn['names'] ) ){
1078
+ $rtn = false;
1079
+ }else{
1080
+ $rtn['names'] = implode( '', $rtn['names'] );
1081
+ $rtn['optgroups'] = implode( '', $rtn['optgroups'] );
1082
+ $rtn['selectedOptgroup'] = implode( '', $rtn['selectedOptgroup'] );
1083
+ if( $this->_cmw_accessibility ){
1084
+ //reset levels of selected optgroup to be the max levels of any group...
1085
+ $rtn['selectedLevels'] = $rtn['maxlevel'];
1086
+ }
1087
+ }
1088
+ return $rtn;
1089
+
1090
+ }
1091
+
1092
+ /**
1093
+ * outputs the HTML to begin a collapsible/expandable group of settings
1094
+ *
1095
+ * @param array $instance
1096
+ * @param string $text Label
1097
+ * @param string $fname Field name
1098
+ */
1099
+ function cmw_open_a_field_section( &$instance, $text, $fname ){
1100
+
1101
+ $hashid = $this->get_field_id( 'cmw' . ++$this->_cmw_hash_ct );
1102
+ ?>
1103
+ <a class="widget-<?php echo $this->id_base; ?>-fieldset<?php echo $instance[$fname] ? ' cmw-collapsed-fieldset' : ''; ?>"
1104
+ id="<?php echo $hashid; ?>" href="#<?php echo $hashid; ?>"><?php echo $text; ?></a>
1105
+ <input id="<?php echo $this->get_field_id($fname); ?>" class="cmw-display-none cmw-fieldset-state"
1106
+ name="<?php echo $this->get_field_name($fname); ?>"
1107
+ type="checkbox" value="1" <?php checked( $instance[$fname] ); ?> />
1108
+ <div class="cmw-fieldset<?php echo $instance[$fname] ? ' cmw-start-fieldset-collapsed' : ''; ?>">
1109
+ <?php
1110
+
1111
+ } //end cmw_open_a_field_section()
1112
+
1113
+ /**
1114
+ * returns true if the version of WP is lower-than or greater-than-or-equal to the one provided
1115
+ *
1116
+ * @param string $v Version to test for lower than
1117
+ * @return boolean
1118
+ */
1119
+ function cmw_wp_version( $v, $gte=false ){
1120
+ global $wp_version;
1121
+
1122
+ $rtn = version_compare( strtolower( $wp_version ), $v . 'a', '<' );
1123
+ return $gte ? !$rtn : $rtn;
1124
+
1125
+ } //end cmw_wp_version()
1126
+
1127
+ /**
1128
+ * sanitizes the widget settings for update(), widget() and form()
1129
+ *
1130
+ * @param array $from_instance Widget settings
1131
+ * @param array $base_instance Old widget settings or an empty array
1132
+ * @param string Name of the calling method
1133
+ * @return array Sanitized widget settings
1134
+ */
1135
+ function cmw_settings( $from_instance, $base_instance, $method = 'update' ){
1136
+
1137
+ /* old (pre v3) settings...
1138
+ //switches...
1139
+ 'include_ancestors' => 0 : replaced by ancestors
1140
+ 'include_parent' => 0 : replaced by ancestors
1141
+ 'include_parent_siblings' => 0 : replaced by ancestor_siblings
1142
+ 'title_from_parent' => 0 : replaced by title_from_branch
1143
+ 'fallback_no_ancestor' => 0 : no longer applicable
1144
+ 'fallback_include_parent' => 0 : no longer applicable
1145
+ 'fallback_include_parent_siblings' => 0 : no longer applicable (but "sort of" replicated by fallback_siblings)
1146
+ 'fallback_no_children' => 0 : replaced by fallback
1147
+ 'fallback_nc_include_parent' => 0 : no longer applicable
1148
+ 'fallback_nc_include_parent_siblings' => 0 : no longer applicable
1149
+ //integers...
1150
+ 'filter_item' => -2 : replaced by branch
1151
+ 'start_level' => 1 : replaced by level & branch_start
1152
+ */
1153
+
1154
+ $instance = is_array( $base_instance ) ? $base_instance : array();
1155
+
1156
+ //switches : values are defaults...
1157
+ foreach( array(
1158
+ 'allow_all_root' => 0, //v3.0.0
1159
+ 'depth_rel_current' => 0,
1160
+ 'fallback_siblings' => 0, //v3.0.0 sort of replaces fallback_include_parent_siblings
1161
+ 'flat_output' => 0,
1162
+ 'hide_title' => 0,
1163
+ 'siblings' => 0, //v3.0.0 replaces include_parent_siblings
1164
+ 'include_root' => 0, //v3.0.0
1165
+ 'title_from_branch' => 0, //v3.0.0 replaces title_from_parent
1166
+ 'title_from_branch_root' => 0, //v3.0.0 added
1167
+ 'title_from_current' => 0,
1168
+ 'title_from_current_root' => 0, //v3.0.0 added
1169
+ 'ol_root' => 0,
1170
+ 'ol_sub' => 0,
1171
+ 'hide_empty' => 0, //this only has relevance prior to WP v3.6
1172
+ //field section collapsed toggles...
1173
+ 'fs_filters' => 1, //v3.0.0 replaces fs_filter and now starts out collapsed
1174
+ 'fs_fallbacks' => 1,
1175
+ 'fs_output' => 1,
1176
+ 'fs_container' => 1,
1177
+ 'fs_classes' => 1,
1178
+ 'fs_links' => 1
1179
+ ) as $k => $v ){
1180
+
1181
+ if( $method == 'update' ){
1182
+ //store as 0 or 1...
1183
+ $instance[ $k ] = empty( $from_instance[ $k ] ) ? 0 : 1;
1184
+ }else{
1185
+ //use internally as boolean...
1186
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? !empty( $from_instance[ $k ] ) : !empty( $v );
1187
+ }
1188
+ }
1189
+
1190
+ //integers : values are minimums (defaults are the values maxed against 0)...
1191
+ foreach( array(
1192
+ 'ancestors' => -9999, //v3.0.0 replaces include_ancestors, but with levels (relative & absolute)
1193
+ 'ancestor_siblings' => -9999, //v3.0.0 also has levels (relative & absolute)
1194
+ 'depth' => 0,
1195
+ 'branch' => 0, //v3.0.0 replaces filter_item, but without current parent|root item
1196
+ 'menu' => 0,
1197
+ 'level' => 1, //v3.0.0 replace start_level (for a level filter)
1198
+ 'fallback_depth' => 0 //v3.0.0 added
1199
+ ) as $k => $v ){
1200
+
1201
+ $instance[ $k ] = isset( $from_instance[ $k ]) ? max( $v, intval( $from_instance[ $k ] ) ) : max( $v, 0 );
1202
+ }
1203
+
1204
+ //strings : values are defaults...
1205
+ foreach( array(
1206
+ 'title' => '',
1207
+ 'filter' => '', //v3.0.0 changed from integer ('', 'branch', 'items'), where empty is equiv. to 'level' (was level=0, branch=1, items=-1)
1208
+ 'branch_start' => '', //v3.0.0 replace start_level (for a branch filter)
1209
+ 'start_mode' => '', //v3.0.0 forces branch_start to use entire level ('', 'level')
1210
+ 'contains_current' => '', //v3.0.0 changed from switch ('', 'menu', 'primary', 'secondary', 'inclusions' or 'output')
1211
+ 'container' => 'div',
1212
+ 'container_id' => '',
1213
+ 'container_class' => '',
1214
+ 'exclude_level' => '', //v3.0.0 (1 or more digits, possibly with an appended '-' or '+')
1215
+ 'fallback' => '', //v3.0.0 replace fallback_no_children ('', 'parent', 'current', 'quit')
1216
+ 'menu_class' => 'menu-widget',
1217
+ 'widget_class' => '',
1218
+ 'cmwv' => ''
1219
+ ) as $k => $v ){
1220
+
1221
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? strip_tags( trim( (string)$from_instance[ $k ] ) ) : $v;
1222
+ if( $method == 'form' ){
1223
+ //escape strings...
1224
+ $instance[ $k ] = esc_attr( trim( $instance[ $k ] ) );
1225
+ }
1226
+ }
1227
+
1228
+ //html strings : values are defaults...
1229
+ foreach( array(
1230
+ 'before' => '',
1231
+ 'after' => '',
1232
+ 'link_before' => '',
1233
+ 'link_after' => ''
1234
+ ) as $k => $v ){
1235
+
1236
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( (string)$from_instance[ $k ] ) : $v;
1237
+ if( $method == 'form' ){
1238
+ //escape html strings...
1239
+ $instance[ $k ] = esc_html( trim( $instance[ $k ] ) );
1240
+ }
1241
+ }
1242
+
1243
+ //csv strings : values are defaults...
1244
+ foreach( array(
1245
+ 'exclude' => '', //v3.0.0 added
1246
+ 'items' => ''
1247
+ ) as $k => $v ){
1248
+
1249
+ $inherits = array();
1250
+ $instance[ "_$k" ] = array();
1251
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( (string)$from_instance[ $k ] ) : $v;
1252
+ foreach( preg_split('/[,\s]+/', $instance[ $k ], -1, PREG_SPLIT_NO_EMPTY ) as $i ){
1253
+ //values can be just digits, or digits followed by a '+' (for inheritance)...
1254
+ if( preg_match( '/^(\d+)(\+?)$/', $i, $m ) > 0 ){
1255
+ $i = intval( $m[1] );
1256
+ if( $i > 0 ){
1257
+ if( !empty( $m[2] ) ){
1258
+ $inherits[] = $i;
1259
+ $i = $i . '+';
1260
+ }
1261
+ if( !in_array( "$i", $instance[ "_$k" ] ) ){
1262
+ $instance[ "_$k" ][] = "$i";
1263
+ }
1264
+ }
1265
+ }
1266
+ }
1267
+ if( !empty( $inherits ) ){
1268
+ $instance[ "_$k" ] = array_diff( $instance[ "_$k" ], $inherits );
1269
+ }
1270
+ unset( $inherits );
1271
+ //just store as comma-separated...
1272
+ $instance[ $k ] = implode( ',', $instance[ "_$k" ] );
1273
+ //can dump the underbar versions if called from update()...
1274
+ if( $method == 'update' ){
1275
+ unset( $instance[ "_$k" ] );
1276
+ }
1277
+ }
1278
+
1279
+ //holds information determined by the walker...
1280
+ $this->_cmw_walker = array();
1281
+
1282
+ return $instance;
1283
+
1284
+ } //end cmw_settings()
1285
+
1286
+ /**
1287
+ * returns the shortcode equivalent of the current settings (not called by legacy code!)
1288
+ *
1289
+ * @param array $instance
1290
+ * @return string
1291
+ */
1292
+ function cmw_shortcode( $instance, $asJSON=false ){
1293
+
1294
+ $args = array(
1295
+ 'menu' => $instance['menu']
1296
+ );
1297
+ $byBranch = $instance['filter'] == 'branch';
1298
+ $byItems = $instance['filter'] == 'items';
1299
+ $byLevel = !$byBranch && !$byItems;
1300
+
1301
+ //take notice of the widget's hide_title flag...
1302
+ if( !empty( $instance['title'] ) && !$instance['hide_title'] ){
1303
+ $args['title'] = array( $instance['title'] );
1304
+ }
1305
+ //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...
1306
+ if( $byLevel && $instance['level'] > 1 ){
1307
+ $args['level'] = $instance['level'];
1308
+ }
1309
+ //specifying branch sets byBranch, overriding byLevel...
1310
+ if( $byBranch ){
1311
+ //use the alternative for 0 ("current") because it's more self-explanatory...
1312
+ $args['branch'] = $instance['branch'] > 0 ? $instance['branch'] : 'current';
1313
+ //start_at only *has* to be specified if not empty...
1314
+ if( !empty( $instance['branch_start'] ) ){
1315
+ $args['start_at'] = array( $instance['branch_start'] );
1316
+ }
1317
+ //start_mode may be brought into play by a fallback so always specify it...
1318
+ if( $instance['start_mode'] == 'level' ){
1319
+ $args['start_mode'] = 'level';
1320
+ }
1321
+ }
1322
+ //specifying items set byItems, overriding byLevel & byBranch...
1323
+ if( $byItems ){
1324
+ $args['items'] = $instance['_items'];
1325
+ }
1326
+ //depth if greater than 0...
1327
+ if( $instance['depth'] > 0 ){
1328
+ $args['depth'] = $instance['depth'];
1329
+ }
1330
+ //depth relative to current item is only applicable if depth is not unlimited...
1331
+ if( $instance['depth_rel_current'] && $instance['depth'] > 0 ){
1332
+ $args['depth_rel_current'] = 1;
1333
+ }
1334
+ //fallbacks...
1335
+ //no children : branch = current item...
1336
+ if( $byBranch && $instance['branch'] == 0 ){
1337
+ if( !empty( $instance['fallback'] ) ){
1338
+ $args['fallback'] = array( $instance['fallback'] );
1339
+ if( $args['fallback'] != 'quit' ){
1340
+ if( $instance['fallback_siblings'] ){
1341
+ $args['fallback'][] = '+siblings';
1342
+ }
1343
+ if( $instance['fallback_depth'] > 0 ){
1344
+ $args['fallback'][] = $instance['fallback_depth'];
1345
+ }
1346
+ }
1347
+ }
1348
+ }
1349
+ //branch ancestor inclusions...
1350
+ if( $byBranch && !empty( $instance['ancestors'] ) ){
1351
+ $args['ancestors'] = $instance['ancestors'];
1352
+ //only ancestor-siblings if ancestors...
1353
+ if( !empty( $instance['ancestor_siblings'] ) ){
1354
+ $args['ancestor_siblings'] = $instance['ancestor_siblings'];
1355
+ }
1356
+ }
1357
+ //exclusions by id...
1358
+ if( !empty( $instance['_exclude'] ) ){
1359
+ $args['exclude'] = $instance['_exclude'];
1360
+ }
1361
+ //...and by level...
1362
+ if( !empty( $instance['exclude_level'] ) ){
1363
+ $args['exclude_level'] = array( $instance['exclude_level'] );
1364
+ }
1365
+ //title from...
1366
+ $n = array();
1367
+ if( $instance['title_from_current'] ){
1368
+ $n[] = 'current';
1369
+ }else if( $instance['title_from_current_root'] ){
1370
+ $n[] = 'current-root';
1371
+ }
1372
+ if( $byBranch && $instance['title_from_branch'] ){
1373
+ $n[] = 'branch';
1374
+ }else if( $byBranch && $instance['title_from_branch_root'] ){
1375
+ $n[] = 'branch-root';
1376
+ }
1377
+ if( !empty( $n ) ){
1378
+ $args['title_from'] = $n;
1379
+ }
1380
+ //switches...
1381
+ foreach( array('allow_all_root', 'siblings', 'include_root', 'flat_output', 'ol_root', 'ol_sub') as $n ){
1382
+ if( $instance[ $n ] ){
1383
+ $args[ $n ] = 1;
1384
+ }
1385
+ }
1386
+ //strings...
1387
+ foreach( array(
1388
+ 'contains_current' => '',
1389
+ 'container' => 'div',
1390
+ 'container_id' => '',
1391
+ 'container_class' => '',
1392
+ 'menu_class' => 'menu-widget',
1393
+ 'widget_class' => ''
1394
+ ) as $n => $v ){
1395
+ if( $instance[ $n ] != $v ){
1396
+ $args[ $n ] = array( $instance[ $n ] );
1397
+ }
1398
+ }
1399
+ foreach( array(
1400
+ 'wrap_link' => 'before',
1401
+ 'wrap_link_text' => 'link_before'
1402
+ ) as $n => $v ){
1403
+ if( preg_match( '/^<(\w+)/', $instance[ $v ], $m ) > 0 ){
1404
+ $args[ $n ] = array( $m[1] );
1405
+ }
1406
+ }
1407
+ //build the shortcode...
1408
+ $m = array();
1409
+ foreach( $args as $n => $v ){
1410
+ //array indicates join (with comma sep) & surround it in double quotes, otherwise leave 'as-is'...
1411
+ if( $asJSON ){
1412
+ $m[ $n ] = is_array( $v ) ? implode( ',', $v ) : $v;
1413
+ }else{
1414
+ $m[] = is_array( $v ) ? $n . '="' . implode( ',', $v ) . '"' : $n . '=' . $v;
1415
+ }
1416
+ }
1417
+ unset( $args );
1418
+
1419
+ //NB at v3.0.0, the shortcode changed from custom_menu_wizard to cmwizard (the previous version is still supported)
1420
+ return $asJSON ? json_encode( $m ) : '[cmwizard ' . implode( ' ', $m ) . ']';
1421
+
1422
+ } //end cmw_shortcode()
1423
+
1424
+
1425
+
1426
+ /*======================
1427
+ * LEGACY CODE (v2.1.0)
1428
+ *======================*/
1429
+
1430
+ /**
1431
+ * produces the legacy version of the backend admin form(s)
1432
+ *
1433
+ * @param array $instance Widget settings
1434
+ */
1435
+ function cmw_legacy_form( $instance ) {
1436
+
1437
+ //sanitize $instance...
1438
+ $instance = $this->cmw_legacy_settings( $instance, array(), 'form' );
1439
+
1440
+ //if no populated menus exist, suggest the user go create one...
1441
+ if( ( $menus = $this->cmw_scan_menus( $instance['menu'], $instance['filter_item'] ) ) === false ){
1442
+ ?>
1443
+ <p class="widget-<?php echo $this->id_base; ?>-no-menus">
1444
+ <?php printf( __('No populated menus have been created yet. <a href="%s">Create one</a>.'), admin_url('nav-menus.php') ); ?>
1445
+ </p>
1446
+ <?php
1447
+ return;
1448
+ }
1449
+
1450
+ //set up some simple booleans for use at the disableif___ classes...
1451
+ $isShowSpecific = $instance['filter'] < 0; // disableif-ss (IS show specific items)
1452
+ $isNotChildrenOf = $instance['filter'] < 1; // disableif (is NOT Children-of)
1453
+ $isNotCurrentRootParent = $isNotChildrenOf || $instance['filter_item'] >= 0; // disableifnot-rp (is NOT Children-of Current Root/Parent)
1454
+ $isNotCurrentItem = $isNotChildrenOf || $instance['filter_item'] != 0; // disableifnot-ci (is NOT Children-of Current Item)
1455
+
1456
+ ?>
1457
+ <div id="<?php echo $this->get_field_id('onchange'); ?>"
1458
+ class="widget-<?php echo $this->id_base; ?>-onchange<?php echo $this->cmw_wp_version('3.8') ? ' cmw-pre-wp-v38' : ''; ?>"
1459
+ data-cmw-v36plus='<?php echo $this->cmw_wp_version('3.6', true) ? 'true' : 'false'; ?>'
1460
+ data-cmw-dialog-prompt='<?php _e('Click an item to toggle &quot;Current Menu Item&quot;'); ?>'
1461
+ data-cmw-dialog-output='<?php _e('Basic Output'); ?>'
1462
+ data-cmw-dialog-fallback='<?php _e('Fallback invoked'); ?>'
1463
+ data-cmw-dialog-set-current='<?php _e('Set Current Item?'); ?>'
1464
+ data-cmw-dialog-shortcodes='<?php _e('Find posts/pages containing a CMW shortcode'); ?>'
1465
+ data-cmw-dialog-untitled='<?php _e('untitled'); ?>'
1466
+ data-cmw-dialog-fixed='<?php _e('fixed'); ?>'
1467
+ data-cmw-dialog-nonce='<?php echo wp_create_nonce( 'cmw-find-shortcodes' ); ?>'
1468
+ data-cmw-dialog-version='2.1.0'
1469
+ data-cmw-dialog-id='<?php echo $this->get_field_id('dialog'); ?>'>
1470
+ <?php
1471
+ /**
1472
+ * Legacy warning...
1473
+ */
1474
+ ?>
1475
+ <p class="cmw-legacy-warn">
1476
+ <a class="widget-<?php echo $this->id_base; ?>-legacy-close cmw-legacy-close" title="<?php _e('Dismiss'); ?>" href="#">X</a>
1477
+ <em><?php _e('This is an old version of the widget!'); ?>
1478
+ <?php
1479
+ if( !$this->_cmw_allow_legacy_update ){
1480
+ ?>
1481
+ <br /><?php _e('Any changes you make will NOT be Saved!'); ?>
1482
+ <?php
1483
+ }
1484
+ ?>
1485
+ <br /><?php _e('Please consider creating a new instance of the widget to replace this one.'); ?>
1486
+ <a href="<?php echo $this->_cmw_legacy_warnreadmore; ?>" target="_blank"><?php _e('read more'); ?></a></em>
1487
+ </p>
1488
+ <?php
1489
+
1490
+ /**
1491
+ * permanently visible section : Title (with Hide) and Menu
1492
+ */
1493
+ ?>
1494
+ <p>
1495
+ <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:') ?></label>
1496
+ <?php $this->cmw_formfield_checkbox( $instance, 'hide_title',
1497
+ array(
1498
+ 'label' => __('Hide'),
1499
+ 'lclass' => 'alignright'
1500
+ ) ); ?>
1501
+ <?php $this->cmw_formfield_textbox( $instance, 'title',
1502
+ array(
1503
+ 'desc' => __('Title can be set, but need not be displayed'),
1504
+ 'fclass' => 'widefat cmw-widget-title'
1505
+ ) ); ?>
1506
+ </p>
1507
+
1508
+ <p>
1509
+ <?php $this->cmw_assist_link(); ?>
1510
+ <label for="<?php echo $this->get_field_id('menu'); ?>"><?php _e('Select Menu:'); ?></label>
1511
+ <select id="<?php echo $this->get_field_id('menu'); ?>" <?php $this->cmw_disableif(); ?>
1512
+ class="cmw-select-menu cmw-listen" name="<?php echo $this->get_field_name('menu'); ?>">
1513
+ <?php echo $menus['names']; ?>
1514
+ </select>
1515
+ </p>
1516
+
1517
+ <?php
1518
+ /**
1519
+ * start collapsible section : 'Filter'
1520
+ */
1521
+ $this->cmw_open_a_field_section( $instance, __('Filter'), 'fs_filter' );
1522
+ ?>
1523
+ <p>
1524
+ <?php $this->cmw_assist_link(); ?>
1525
+ <label>
1526
+ <input id="<?php echo $this->get_field_id('filter'); ?>_0" class="cmw-showall cmw-listen" <?php $this->cmw_disableif(); ?>
1527
+ name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="0" <?php checked( $instance['filter'], 0 ); ?> />
1528
+ <?php _e('Show all'); ?></label>
1529
+ <br /><label>
1530
+ <input id="<?php echo $this->get_field_id('filter'); ?>_1" class="cmw-listen" <?php $this->cmw_disableif(); ?>
1531
+ name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="1" <?php checked( $instance['filter'], 1 ); ?> />
1532
+ <?php _e('Children of:'); ?></label>
1533
+ <select id="<?php echo $this->get_field_id('filter_item'); ?>" class="cmw-childrenof cmw-assist-items cmw-listen"
1534
+ name="<?php echo $this->get_field_name('filter_item'); ?>" <?php $this->cmw_disableif(); ?>>
1535
+ <option value="0" <?php selected( $instance['filter_item'], 0 ); ?>><?php _e('Current Item'); ?></option>
1536
+ <option value="-2" <?php selected( $instance['filter_item'], -2 ); ?>><?php _e('Current Root Item'); ?></option>
1537
+ <option value="-1" <?php selected( $instance['filter_item'], -1 ); ?>><?php _e('Current Parent Item'); ?></option>
1538
+ <?php echo $menus['selectedOptgroup']; ?>
1539
+ </select>
1540
+ <br /><label>
1541
+ <input id="<?php echo $this->get_field_id('filter'); ?>_2" class="cmw-showspecific cmw-listen" <?php $this->cmw_disableif(); ?>
1542
+ name="<?php echo $this->get_field_name('filter'); ?>" type="radio" value="-1" <?php checked( $instance['filter'], -1 ); ?> />
1543
+ <?php _e('Items:'); ?></label>
1544
+ <?php $this->cmw_formfield_textbox( $instance, 'items',
1545
+ array(
1546
+ 'fclass' => 'cmw-setitems'
1547
+ ) ); ?>
1548
+
1549
+ <select id="<?php echo $this->get_field_id('filter_item_ignore'); ?>" disabled="disabled"
1550
+ class='cmw-off-the-page' name="<?php echo $this->get_field_name('filter_item_ignore'); ?>">
1551
+ <?php echo $menus['optgroups']; ?>
1552
+ </select>
1553
+ </p>
1554
+
1555
+ <p class="cmw-disableif-ss<?php $this->cmw_disableif( 'push', $isShowSpecific ); ?>">
1556
+ <label for="<?php echo $this->get_field_id('start_level'); ?>"><?php _e('Starting Level:'); ?></label>
1557
+ <select id="<?php echo $this->get_field_id('start_level'); ?>" <?php $this->cmw_disableif(); ?>
1558
+ class="cmw-start-level" name="<?php echo $this->get_field_name('start_level'); ?>">
1559
+ <?php for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){ ?>
1560
+ <option value="<?php echo $i; ?>" <?php selected( $instance['start_level'] > $menus['selectedLevels'] ? 1 : $instance['start_level'], $i ); ?>><?php echo $i; ?></option>
1561
+ <?php } ?>
1562
+ </select>
1563
+ <span class="cmw-small-block"><em><?php _e('Level to start testing items for inclusion'); ?></em></span>
1564
+ </p><!-- end .cmw-disableif-ss --><?php $this->cmw_disableif( 'pop' ); ?>
1565
+
1566
+ <p class="cmw-disableif-ss<?php $this->cmw_disableif( 'push', $isShowSpecific ); ?>">
1567
+ <label for="<?php echo $this->get_field_id('depth'); ?>"><?php _e('For Depth:'); ?></label>
1568
+ <select id="<?php echo $this->get_field_id('depth'); ?>" class="cmw-depth" data-cmw-text-levels="<?php _e('levels'); ?>"
1569
+ name="<?php echo $this->get_field_name('depth'); ?>" <?php $this->cmw_disableif(); ?>>
1570
+ <option value="0" <?php selected( $instance['depth'] > $menus['selectedLevels'] ? 0 : $instance['depth'], 0 ); ?>><?php _e('unlimited'); ?></option>
1571
+ <?php
1572
+ for( $i = 1; $i <= $menus['selectedLevels']; $i++ ){
1573
+ ?>
1574
+ <option value="<?php echo $i; ?>" <?php selected( $instance['depth'], $i ); ?>><?php echo $i; ?> <?php _e($i > 1 ? 'levels' : 'level'); ?></option>
1575
+ <?php
1576
+ }
1577
+ ?>
1578
+ </select>
1579
+ <span class="cmw-small-block"><em><?php _e('Relative to first Filter item found, <strong>unless</strong>&hellip;'); ?></em></span>
1580
+ <?php $this->cmw_formfield_checkbox( $instance, 'depth_rel_current',
1581
+ array(
1582
+ 'label' => sprintf( __('Relative to &quot;Current&quot; Item %1$s(if found)%2$s'), '<small><em>', '</em></small>' )
1583
+ ) ); ?>
1584
+ </p><!-- end .cmw-disableif-ss --><?php $this->cmw_disableif( 'pop' ); ?>
1585
+
1586
+ <?php $this->cmw_close_a_field_section(); ?>
1587
+
1588
+ <?php
1589
+ /**
1590
+ * v1.2.0 start collapsible section : 'Fallbacks'
1591
+ */
1592
+ $this->cmw_open_a_field_section( $instance, __('Fallbacks'), 'fs_fallbacks' );
1593
+ ?>
1594
+ <p class="cmw-disableifnot-rp<?php $this->cmw_disableif( 'push', $isNotCurrentRootParent ); ?>">
1595
+ <?php $this->cmw_assist_link(); ?>
1596
+ <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>
1597
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_no_ancestor',
1598
+ array(
1599
+ 'label' => __('Switch to Current Item, and')
1600
+ ) ); ?>
1601
+ <br />
1602
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_include_parent',
1603
+ array(
1604
+ 'label' => __('Include Parent...')
1605
+ ) ); ?>
1606
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_include_parent_siblings',
1607
+ array(
1608
+ 'label' => __('with Siblings'),
1609
+ 'lclass' => 'cmw-whitespace-nowrap'
1610
+ ) ); ?>
1611
+ </p><!-- end .cmw-disableifnot-rp --><?php $this->cmw_disableif( 'pop' ); ?>
1612
+
1613
+ <p class="cmw-disableifnot-ci<?php $this->cmw_disableif( 'push', $isNotCurrentItem ); ?>">
1614
+ <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>
1615
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_no_children',
1616
+ array(
1617
+ 'label' => __('Switch to Current Parent Item, and')
1618
+ ) ); ?>
1619
+ <br />
1620
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_nc_include_parent',
1621
+ array(
1622
+ 'label' => __('Include Parent...')
1623
+ ) ); ?>
1624
+ <?php $this->cmw_formfield_checkbox( $instance, 'fallback_nc_include_parent_siblings',
1625
+ array(
1626
+ 'label' => __('with Siblings'),
1627
+ 'lclass' => 'cmw-whitespace-nowrap'
1628
+ ) ); ?>
1629
+ </p><!-- end .cmw-disableifnot-ci --><?php $this->cmw_disableif( 'pop' ); ?>
1630
+
1631
+ <?php $this->cmw_close_a_field_section(); ?>
1632
+
1633
+ <?php
1634
+ /**
1635
+ * start collapsible section : 'Output'
1636
+ */
1637
+ $this->cmw_open_a_field_section( $instance, __('Output'), 'fs_output' );
1638
+ ?>
1639
+ <p>
1640
+ <?php $this->cmw_assist_link(); ?>
1641
+ <label>
1642
+ <input id="<?php echo $this->get_field_id('flat_output'); ?>_0" name="<?php echo $this->get_field_name('flat_output'); ?>"
1643
+ type="radio" value="0" <?php checked(!$instance['flat_output']); ?> <?php $this->cmw_disableif(); ?> />
1644
+ <?php _e('Hierarchical'); ?></label>
1645
+ &nbsp;<label class="cmw-whitespace-nowrap">
1646
+ <input id="<?php echo $this->get_field_id('flat_output'); ?>_1" name="<?php echo $this->get_field_name('flat_output'); ?>"
1647
+ type="radio" value="1" <?php checked($instance['flat_output']); ?> <?php $this->cmw_disableif(); ?> />
1648
+ <?php _e('Flat'); ?></label>
1649
+ </p>
1650
+
1651
+ <p>
1652
+ <?php $this->cmw_formfield_checkbox( $instance, 'contains_current',
1653
+ array(
1654
+ 'label' => __('Must Contain &quot;Current&quot; Item'),
1655
+ 'desc' => __('Checks both Filtered and Included items')
1656
+ ) ); ?>
1657
+ </p>
1658
+
1659
+ <p class="cmw-disableif<?php $this->cmw_disableif( 'push', $isNotChildrenOf ); ?>">
1660
+ <?php $this->cmw_formfield_checkbox( $instance, 'include_parent',
1661
+ array(
1662
+ 'label' => __('Include Parent...')
1663
+ ) ); ?>
1664
+ <?php $this->cmw_formfield_checkbox( $instance, 'include_parent_siblings',
1665
+ array(
1666
+ 'label' => __('with Siblings'),
1667
+ 'lclass' => 'cmw-whitespace-nowrap'
1668
+ ) ); ?>
1669
+ <br />
1670
+ <?php $this->cmw_formfield_checkbox( $instance, 'include_ancestors',
1671
+ array(
1672
+ 'label' => __('Include Ancestors')
1673
+ ) ); ?>
1674
+ <br />
1675
+ <?php $this->cmw_formfield_checkbox( $instance, 'title_from_parent',
1676
+ array(
1677
+ 'label' => __('Title from Parent'),
1678
+ 'desc' => __('Only if the &quot;Children of&quot; Filter returns items')
1679
+ ) ); ?>
1680
+ </p><!-- end .cmw-disableif --><?php $this->cmw_disableif( 'pop' ); ?>
1681
+
1682
+ <p>
1683
+ <?php $this->cmw_formfield_checkbox( $instance, 'title_from_current',
1684
+ array(
1685
+ 'label' => __('Title from &quot;Current&quot; Item'),
1686
+ 'desc' => __('Lower priority than &quot;Title from Parent&quot;')
1687
+ ) ); ?>
1688
+ </p>
1689
+
1690
+ <p>
1691
+ <?php _e('Change UL to OL:'); ?>
1692
+ <br />
1693
+ <?php $this->cmw_formfield_checkbox( $instance, 'ol_root',
1694
+ array(
1695
+ 'label' => __('Top Level')
1696
+ ) ); ?>
1697
+ &nbsp;
1698
+ <?php $this->cmw_formfield_checkbox( $instance, 'ol_sub',
1699
+ array(
1700
+ 'label' => __('Sub-Levels')
1701
+ ) ); ?>
1702
+ </p>
1703
+
1704
+ <?php
1705
+ //v1.1.0 As of WP v3.6, wp_nav_menu() automatically cops out (without outputting any HTML) if there are no items,
1706
+ // so the hide_empty option becomes superfluous; however, I'll keep the previous setting (if there was one)
1707
+ // in case of reversion to an earlier version of WP...
1708
+ if( $this->cmw_wp_version('3.6') ){
1709
+ ?>
1710
+ <p>
1711
+ <?php $this->cmw_formfield_checkbox( $instance, 'hide_empty',
1712
+ array(
1713
+ 'label' => __('Hide Widget if Empty'),
1714
+ 'desc' => __('Prevents any output when no items are found')
1715
+ ) ); ?>
1716
+ </p>
1717
+ <?php }else{ ?>
1718
+ <input id="<?php echo $this->get_field_id('hide_empty'); ?>" name="<?php echo $this->get_field_name('hide_empty'); ?>"
1719
+ type="hidden" value="<?php echo $instance['hide_empty'] ? '1' : ''; ?>" />
1720
+ <?php } ?>
1721
+
1722
+ <?php $this->cmw_close_a_field_section(); ?>
1723
+
1724
+ <?php
1725
+ /**
1726
+ * start collapsible section : 'Container'
1727
+ */
1728
+ $this->cmw_open_a_field_section( $instance, __('Container'), 'fs_container' );
1729
+ ?>
1730
+ <p>
1731
+ <?php $this->cmw_formfield_textbox( $instance, 'container',
1732
+ array(
1733
+ 'label' => __('Element:'),
1734
+ 'desc' => __('Eg. div or nav; leave empty for no container')
1735
+ ) ); ?>
1736
+ </p>
1737
+ <p>
1738
+ <?php $this->cmw_formfield_textbox( $instance, 'container_id',
1739
+ array(
1740
+ 'label' => __('Unique ID:'),
1741
+ 'desc' => __('An optional ID for the container')
1742
+ ) ); ?>
1743
+ </p>
1744
+ <p>
1745
+ <?php $this->cmw_formfield_textbox( $instance, 'container_class',
1746
+ array(
1747
+ 'label' => __('Class:'),
1748
+ 'desc' => __('Extra class for the container')
1749
+ ) ); ?>
1750
+ </p>
1751
+
1752
+ <?php $this->cmw_close_a_field_section(); ?>
1753
+
1754
+ <?php
1755
+ /**
1756
+ * start collapsible section : 'Classes'
1757
+ */
1758
+ $this->cmw_open_a_field_section( $instance, __('Classes'), 'fs_classes' );
1759
+ ?>
1760
+ <p>
1761
+ <?php $this->cmw_formfield_textbox( $instance, 'menu_class',
1762
+ array(
1763
+ 'label' => __('Menu Class:'),
1764
+ 'desc' => __('Class for the list element forming the menu')
1765
+ ) ); ?>
1766
+ </p>
1767
+ <p>
1768
+ <?php $this->cmw_formfield_textbox( $instance, 'widget_class',
1769
+ array(
1770
+ 'label' => __('Widget Class:'),
1771
+ 'desc' => __('Extra class for the widget itself')
1772
+ ) ); ?>
1773
+ </p>
1774
+
1775
+ <?php $this->cmw_close_a_field_section(); ?>
1776
+
1777
+ <?php
1778
+ /**
1779
+ * start collapsible section : 'Links'
1780
+ */
1781
+ $this->cmw_open_a_field_section( $instance, __('Links'), 'fs_links' );
1782
+ ?>
1783
+ <p>
1784
+ <?php $this->cmw_formfield_textbox( $instance, 'before',
1785
+ array(
1786
+ 'label' => __('Before the Link:'),
1787
+ 'desc' => __( htmlspecialchars('Text/HTML to go before the <a> of the link') ),
1788
+ 'fclass' => 'widefat'
1789
+ ) ); ?>
1790
+ </p>
1791
+ <p>
1792
+ <?php $this->cmw_formfield_textbox( $instance, 'after',
1793
+ array(
1794
+ 'label' => __('After the Link:'),
1795
+ 'desc' => __( htmlspecialchars('Text/HTML to go after the <a> of the link') ),
1796
+ 'fclass' => 'widefat'
1797
+ ) ); ?>
1798
+ </p>
1799
+ <p>
1800
+ <?php $this->cmw_formfield_textbox( $instance, 'link_before',
1801
+ array(
1802
+ 'label' => __('Before the Link Text:'),
1803
+ 'desc' => __('Text/HTML to go before the link text'),
1804
+ 'fclass' => 'widefat'
1805
+ ) ); ?>
1806
+ </p>
1807
+ <p>
1808
+ <?php $this->cmw_formfield_textbox( $instance, 'link_after',
1809
+ array(
1810
+ 'label' => __('After the Link Text:'),
1811
+ 'desc' => __('Text/HTML to go after the link text'),
1812
+ 'fclass' => 'widefat'
1813
+ ) ); ?>
1814
+ </p>
1815
+
1816
+ <?php $this->cmw_close_a_field_section(); ?>
1817
+
1818
+ </div>
1819
+ <?php
1820
+
1821
+ } //end cmw_legacy_form()
1822
+
1823
+ /**
1824
+ * sanitizes the widget settings for cmw_legacy_update(), cmw_legacy_widget() and cmw_legacy_form()
1825
+ *
1826
+ * @param array $from_instance Widget settings
1827
+ * @param array $base_instance Old widget settings or an empty array
1828
+ * @param string $method Name suffix of the calling method
1829
+ * @return array Sanitized widget settings
1830
+ */
1831
+ function cmw_legacy_settings( $from_instance, $base_instance, $method = 'update' ){
1832
+
1833
+ $instance = is_array( $base_instance ) ? $base_instance : array();
1834
+
1835
+ //switches...
1836
+ foreach( array(
1837
+ 'hide_title' => 0,
1838
+ 'contains_current' => 0, //v2.0.0 added
1839
+ 'depth_rel_current' => 0, //v2.0.0 added
1840
+ 'fallback_no_ancestor' => 0, //v1.1.0 added
1841
+ 'fallback_include_parent' => 0, //v1.1.0 added
1842
+ 'fallback_include_parent_siblings' => 0, //v1.1.0 added
1843
+ 'fallback_no_children' => 0, //v1.2.0 added
1844
+ 'fallback_nc_include_parent' => 0, //v1.2.0 added
1845
+ 'fallback_nc_include_parent_siblings' => 0, //v1.2.0 added
1846
+ 'flat_output' => 0,
1847
+ 'include_parent' => 0,
1848
+ 'include_parent_siblings' => 0, //v1.1.0 added
1849
+ 'include_ancestors' => 0,
1850
+ 'hide_empty' => 0, //v1.1.0: this now only has relevance prior to WP v3.6
1851
+ 'title_from_parent' => 0,
1852
+ 'title_from_current' => 0, //v1.2.0 added
1853
+ 'ol_root' => 0,
1854
+ 'ol_sub' => 0,
1855
+ //field section toggles...
1856
+ 'fs_filter' => 0,
1857
+ 'fs_fallbacks' => 1, //v1.2.0 added
1858
+ 'fs_output' => 1,
1859
+ 'fs_container' => 1,
1860
+ 'fs_classes' => 1,
1861
+ 'fs_links' => 1
1862
+ ) as $k => $v ){
1863
+
1864
+ if( $method == 'form' ){
1865
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? !empty( $from_instance[ $k ] ) : !empty( $v );
1866
+ }elseif( $method == 'widget' ){
1867
+ $instance[ $k ] = !empty( $from_instance[ $k ] );
1868
+ }else{
1869
+ $instance[ $k ] = empty( $from_instance[ $k ] ) ? 0 : 1;
1870
+ }
1871
+
1872
+ }
1873
+
1874
+ //strings...
1875
+ foreach( array(
1876
+ 'title' => '',
1877
+ 'items' => '', //v2.0.0 added
1878
+ 'container' => 'div',
1879
+ 'container_id' => '',
1880
+ 'container_class' => '',
1881
+ 'menu_class' => 'menu-widget',
1882
+ 'widget_class' => ''
1883
+ ) as $k => $v ){
1884
+
1885
+ if( $method == 'form' ){
1886
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? esc_attr( trim( $from_instance[ $k ] ) ) : $v;
1887
+ }elseif( $method == 'widget' ){
1888
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( $from_instance[ $k ] ) : $v; //bug in 2.0.2 fixed!
1889
+ }else{
1890
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? strip_tags( trim( $from_instance[ $k ] ) ) : $v;
1891
+ }
1892
+
1893
+ }
1894
+
1895
+ //html strings...
1896
+ foreach( array(
1897
+ 'before' => '',
1898
+ 'after' => '',
1899
+ 'link_before' => '',
1900
+ 'link_after' => ''
1901
+ ) as $k => $v ){
1902
+
1903
+ if( $method == 'form' ){
1904
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? esc_html( trim( $from_instance[ $k ] ) ) : $v;
1905
+ }elseif( $method == 'widget' ){
1906
+ $instance[ $k ] = empty( $from_instance[ $k ] ) ? $v : trim( $from_instance[ $k ] );
1907
+ }else{
1908
+ $instance[ $k ] = isset( $from_instance[ $k ] ) ? trim( $from_instance[ $k ] ) : $v;
1909
+ }
1910
+
1911
+ }
1912
+
1913
+ //integers...
1914
+ foreach( array(
1915
+ 'depth' => 0,
1916
+ 'filter' => -1, //v2.0.0 changed from switch
1917
+ 'filter_item' => -2, //v1.1.0 changed from 0
1918
+ 'menu' => 0,
1919
+ 'start_level' => 1
1920
+ ) as $k => $v ){
1921
+
1922
+ if( $method == 'form' ){
1923
+ $instance[ $k ] = isset( $from_instance[ $k ]) ? max( $v, intval( $from_instance[ $k ] ) ) : max($v, 0);
1924
+ }elseif( $method == 'widget' ){
1925
+ $instance[ $k ] = max( $v, intval( $from_instance[ $k ] ) );
1926
+ }else{
1927
+ $instance[ $k ] = isset( $from_instance[ $k ]) ? max( $v, intval( $from_instance[ $k ] ) ) : $v;
1928
+ }
1929
+
1930
+ }
1931
+
1932
+ //items special case...
1933
+ if( $method == 'update' && !empty( $instance['items'] ) ){
1934
+ $sep = preg_match( '/(^\d+$|,)/', $instance['items'] ) > 0 ? ',' : ' ';
1935
+ $a = array();
1936
+ foreach( preg_split('/[,\s]+/', $instance['items'], -1, PREG_SPLIT_NO_EMPTY ) as $v ){
1937
+ $i = intval( $v );
1938
+ if( $i > 0 ){
1939
+ $a[] = $i;
1940
+ }
1941
+ }
1942
+ $instance['items'] = implode( $sep, $a );
1943
+ }
1944
+
1945
+ //v1.2.1 holds information determined by the walker...
1946
+ $this->_cmw_walker = array();
1947
+
1948
+ return $instance;
1949
+
1950
+ } //end cmw_legacy_settings()
1951
+
1952
+ /**
1953
+ * updates the widget settings sent from the legacy backend admin
1954
+ *
1955
+ * @param array $new_instance New widget settings
1956
+ * @param array $old_instance Old widget settings
1957
+ * @return array Sanitized widget settings
1958
+ */
1959
+ function cmw_legacy_update( $new_instance, $old_instance ){
1960
+
1961
+ if( $this->_cmw_allow_legacy_update ){
1962
+ return $this->cmw_legacy_settings( $new_instance, $old_instance, 'update' );
1963
+ }else{
1964
+ //prevent the save!...
1965
+ return false;
1966
+ }
1967
+
1968
+ } //end cmw_legacy_update()
1969
+
1970
+ /**
1971
+ * produces the legacy widget HTML at the front end
1972
+ *
1973
+ * @filters : custom_menu_wizard_nav_params array of params that will be sent to wp_nav_menu(), array of instance settings, id base
1974
+ * custom_menu_wizard_settings_pre_widget array of instance settings, id base
1975
+ * custom_menu_wizard_widget_output HTML output string, array of instance settings, id base
1976
+ *
1977
+ * @param object $args Widget arguments
1978
+ * @param array $instance Configuration for this widget instance
1979
+ */
1980
+ function cmw_legacy_widget( $args, $instance ) {
1981
+
1982
+ //sanitize $instance...
1983
+ $instance = $this->cmw_legacy_settings( $instance, array(), 'widget' );
1984
+
1985
+ extract( $args, EXTR_SKIP );
1986
+
1987
+ //v1.1.0 As of WP v3.6, wp_nav_menu() automatically prevents any HTML output if there are no items...
1988
+ $instance['hide_empty'] = $instance['hide_empty'] && $this->cmw_wp_version('3.6');
1989
+
1990
+ //allow a filter to amend the instance settings prior to producing the widget output...
1991
+ //eg. add_filter( 'custom_menu_wizard_settings_pre_widget', [filter_function], 10, 2 ) => $instance (array)
1992
+ $instance = apply_filters( 'custom_menu_wizard_settings_pre_widget', $instance, $this->id_base );
1993
+
1994
+ //fetch menu...
1995
+ if( !empty($instance['menu'] ) ){
1996
+ $menu = wp_get_nav_menu_object( $instance['menu'] );
1997
+
1998
+ //no menu, no output...
1999
+ if ( !empty( $menu ) ){
2000
+
2001
+ if( !empty( $instance['widget_class'] ) ){
2002
+ //$before_widget is usually just a DIV start-tag, with an id and a class; if it
2003
+ //gets more complicated than that then this may not work as expected...
2004
+ if( preg_match( '/^<[^>]+?class=["\']/', $before_widget ) > 0 ){
2005
+ $before_widget = preg_replace( '/(class=["\'])/', '$1' . $instance['widget_class'] . ' ', $before_widget, 1 );
2006
+ }else{
2007
+ $before_widget = preg_replace( '/^(<\w+)(\s|>)/', '$1 class="' . $instance['widget_class'] . '"$2', $before_widget );
2008
+ }
2009
+ }
2010
+
2011
+ if( !empty( $instance['container_class'] ) ){
2012
+ $instance['container_class'] = "menu-{$menu->slug}-container {$instance['container_class']}";
2013
+ }
2014
+
2015
+ $instance['menu_class'] = preg_split( '/\s+/', $instance['menu_class'], -1, PREG_SPLIT_NO_EMPTY );
2016
+ if( $instance['fallback_no_ancestor'] || $instance['fallback_no_children'] ){
2017
+ //v1.2.1 add a cmw-fellback-maybe class to the menu and we'll remove or replace it later...
2018
+ $instance['menu_class'][] = 'cmw-fellback-maybe';
2019
+ }
2020
+ $instance['menu_class'] = implode( ' ', $instance['menu_class'] );
2021
+
2022
+ $walker = new Custom_Menu_Wizard_Walker;
2023
+ $params = array(
2024
+ 'menu' => $menu,
2025
+ 'container' => empty( $instance['container'] ) ? false : $instance['container'], //bug in 2.0.2 fixed!
2026
+ 'container_id' => $instance['container_id'],
2027
+ 'menu_class' => $instance['menu_class'],
2028
+ 'echo' => false,
2029
+ 'fallback_cb' => false,
2030
+ 'before' => $instance['before'],
2031
+ 'after' => $instance['after'],
2032
+ 'link_before' => $instance['link_before'],
2033
+ 'link_after' => $instance['link_after'],
2034
+ 'depth' => empty( $instance['flat_output'] ) ? $instance['depth'] : -1,
2035
+ 'walker' =>$walker,
2036
+ //widget specific stuff...
2037
+ '_custom_menu_wizard' => $instance
2038
+ );
2039
+ //for the walker's use...
2040
+ $params['_custom_menu_wizard']['_walker'] = array();
2041
+
2042
+ if( $instance['ol_root'] ){
2043
+ $params['items_wrap'] = '<ol id="%1$s" class="%2$s">%3$s</ol>';
2044
+ }
2045
+ if( !empty( $instance['container_class'] ) ){
2046
+ $params['container_class'] = $instance['container_class'];
2047
+ }
2048
+
2049
+ add_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
2050
+ if( $instance['hide_empty'] ){
2051
+ add_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
2052
+ }
2053
+
2054
+ //allow a filter to amend the wp_nav_menu() params prior to calling it...
2055
+ //eg. add_filter( 'custom_menu_wizard_nav_params', [filter_function], 10, 3 ) => $params (array)
2056
+ //NB: wp_nav_menu() is in wp-includes/nav-menu-template.php
2057
+ $out = wp_nav_menu( apply_filters( 'custom_menu_wizard_nav_params', $params, $instance, $this->id_base ) );
2058
+
2059
+ remove_filter('custom_menu_wizard_walker_items', array( $this, 'cmw_filter_walker_items' ), 10, 2);
2060
+ if( $instance['hide_empty'] ){
2061
+ remove_filter( "wp_nav_menu_{$menu->slug}_items", array( $this, 'cmw_filter_check_for_no_items' ), 65532, 2 );
2062
+ }
2063
+
2064
+ //only put something out if there is something to put out...
2065
+ if( !empty( $out ) ){
2066
+
2067
+ //title from : 'from parent' has priority over 'from current'...
2068
+ //note that 'parent' is whatever you are getting the children of and therefore doesn't apply to a ShowAll, whereas
2069
+ //'current' is the current menu item (as determined by WP); also note that neither parent nor current actually has
2070
+ //to be present in the results
2071
+ if( $instance['title_from_parent'] && !empty( $this->_cmw_walker['parent_title'] ) ){
2072
+ $title = $this->_cmw_walker['parent_title'];
2073
+ }
2074
+ if( empty( $title ) && $instance['title_from_current'] && !empty( $this->_cmw_walker['current_title'] ) ){
2075
+ $title = $this->_cmw_walker['current_title'];
2076
+ }
2077
+ if( empty( $title ) ){
2078
+ $title = $instance['hide_title'] ? '' : $instance['title'];
2079
+ }
2080
+
2081
+ //remove/replace the cmw-fellback-maybe class...
2082
+ $out = str_replace(
2083
+ 'cmw-fellback-maybe',
2084
+ empty( $this->_cmw_walker['fellback'] ) ? '' : 'cmw-fellback-' . $this->_cmw_walker['fellback'],
2085
+ $out );
2086
+
2087
+ if ( !empty($title) ){
2088
+ $out = $before_title . apply_filters('widget_title', $title, $instance, $this->id_base) . $after_title . $out;
2089
+ }
2090
+ $out = $before_widget . $out . $after_widget;
2091
+ //allow a filter to modify the entire output...
2092
+ //eg. add_filter( 'custom_menu_wizard_widget_output', [filter_function], 10, 3 ) => $output (HTML string)
2093
+ echo apply_filters( 'custom_menu_wizard_widget_output', $out, $instance, $this->id_base );
2094
+ }
2095
+ }
2096
+ }
2097
+
2098
+ } //end cmw_legacy_widget()
2099
+
2100
+ } //end of class
2101
+ ?>
readme.txt CHANGED
@@ -1,9 +1,10 @@
1
  === Custom Menu Wizard Widget ===
2
  Contributors: wizzud
3
- Tags: menu,widget,widgets,navigation,nav,custom menus,custom menu,partial menu,menu level,menu branch
4
- Requires at least: 3.0.1
5
- Tested up to: 3.8
6
- Stable tag: 2.0.6
 
7
  License: GPLv2 or Later
8
 
9
  Show branches or levels of your menu in a widget, or in content using a shortcode, with full customisation.
@@ -11,402 +12,538 @@ Show branches or levels of your menu in a widget, or in content using a shortcod
11
  == Description ==
12
 
13
  This plugin is a boosted version of the WordPress "Custom Menu" widget.
14
- It provides full control over most of the parameters available when calling WP's [wp_nav_menu()](http://codex.wordpress.org/Function_Reference/wp_nav_menu) function, as well as providing pre-filtering of the menu items in order to be able to select a specific portion of the custom menu. It also automatically adds a couple of custom classes. And there is now (v1.2.0) a shortcode that enables you to include the widget's output in your content.
15
 
16
  Features include:
17
 
18
- * Display the entire menu, just a branch of it, or even just a specific level of it
19
- * Select a branch based on a specific menu item, or the "current" item (currently displayed page)
20
- * Specify a level to start at, and the number of levels to output
21
- * Select hierarchicial or flat output, both options still abiding by the specified number of levels to output
22
- * Allow the widget title to be entered but not output, or even to be set from the parent item of a menu's sub-branch
23
- * Optionally disable widget output completely if there are no matching menu items found (WordPress < v3.6)
24
- * Include a sub-branch's parent and/or ancestor item(s) in the output, over and above any start level setting
25
  * Automatically add cmw-level-N and cmw-has-submenu classes to output menu items
 
 
26
  * Add/specify custom class(es) for the widget block, the menu container, and the menu itself
27
  * Modify the link's output with additional HTML around the link's text and/or the link element itself
28
  * Use Ordered Lists (OL) for the top and/or sub levels instead of Unordered Lists (UL)
29
- * Select a branch based on the ultimate ancestor (root level) of the "current" item
30
- * Shortcode, [custom_menu_wizard], available to run the widget from within content
31
- * Make the output conditional upon the "current" item appearing in the selected/included items
32
- * Specify specific menu items
33
- * Use the widget's interactive "assist" to help with the widget settings or shortcode definition
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- Documentation for the Widget Options, and the associated Shortcode Parameters, can be found under
36
- [Other Notes](http://wordpress.org/plugins/custom-menu-wizard/other_notes/).
 
37
 
38
- == Widget Options ==
 
 
39
 
40
  There are quite a few options, which makes the widget settings box very long. I have therefore grouped most of the options into
41
- logical sections and made each section collapsible (with remembered state once saved). As of v1.2.1, only the Filter section is
42
- open by default; all sections below that start off collapsed.
 
43
 
44
- * **Title**
 
 
45
 
46
  Set the title for your widget.
47
 
48
- * **Hide** *(checkbox)*
49
 
50
  Prevents the entered `Title` being displayed in the front-end widget output.
51
-
52
- In the Widgets admin page, I find it useful to still be able to see the `Title` in the sidebar when the widget is collapsed, but I
53
  don't necessarily want that `Title` to actually be output when the widget is displayed at the front-end. Hence this checkbox.
54
 
55
- * **Select Menu** *(select)*
56
 
57
  Choose the appropriate menu from the dropdown list of Custom Menus currently defined in your WordPress application. The
58
  first one available (alphabetically) is already selected for you by default.
59
 
60
- * **Filter**
 
 
 
 
61
 
62
- * **Show all** *(radio, default)*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
- Don't apply the `Children of` filter. If you check this option, the `Children of` select will be disabled, as will the `Include Parent`,
65
- `Include Ancestors` and `Title from Parent Item` checkboxes, as they are only applicable to a `Children of` filter.
 
 
66
 
67
- * **Children of** *(radio & select)*
 
 
68
 
69
- The dropdown list will present a "Current Item" option (default), a "Current Root Item" (v1.1.0)
70
- and "Current Parent Item" (v1.1.0) option, followed by all the available items from the menu chosen in `Select Menu`.
71
- The widget will output the *children* of the selected item, always assuming that they lie within the bounds of any other parameters set.
72
 
73
- * "Current Item" is the menu item that WordPress has marked as being currently on display (current menu item)
74
 
75
- * "Current Parent Item" (v1.1.0) is the *immediate* ancestor (within `Select Menu`) of the current menu item
 
 
 
76
 
77
- * "Current Root Item" (v1.1.0) is the *ultimate* ancestor (within `Select Menu`) of the current menu item.
 
78
 
79
- Obviously, if the current menu item (as determined by WordPress) does not
80
- appear in the `Select Menu` then there is going to be no output for any of these options.
81
-
82
- If you change `Select Menu`, the options presented in this dropdown will change accordingly and the selected option will revert to the default.
83
 
84
- * **Items** (radio & text input)* as of v2.0.0
 
85
 
86
- Takes a comma- or space-delimited list of menu item ids, specifiying the specific menu items that are required. This is intended
87
- for situations that it is not possible to handle using other option settings. The simplest way to determine the menu item ids is
88
- to use the "assist" facility.
89
-
90
- Note that the `Starting Level` and `Depth` options (including `Relative to "Current" Item`) have no effect if `Items` is set.
91
 
92
- * **Starting Level** *(select, default: "1")*
 
 
 
 
 
 
 
 
93
 
94
- This is the level within the chosen menu (from `Select Menu`) that the widget will start looking for items to keep. Obviously, level 1
95
- is the root level (ie. those items that have no parent item); level 2 is all the immediate children of the root level items, and so on.
96
- Note that for a `Children of` filter there is no difference between level 1 and level 2 (because there are no children at level 1).
97
- Also note that this option does not apply if `Items` is set.
98
 
99
- * **For Depth** *(select, default: "unlimited")*
100
 
101
- This is the maximum depth of the eventual output structure after filtering, and in the case of `Flat` output being requested it is
102
- still applied - as if the output were `Hierarchical` and then flattened at the very last moment.
103
-
104
- You need to be aware that, by default, the
105
- `For Depth` setting is applied relative to the level at which the first item to be kept is found. For example, say you were to set
106
- `Children of` to "Current Item", `Starting Level` to "2", and `For Depth` to "2 levels" : if the current item was found at level 3,
107
- then you would get the current item's immediate children (from level 4), plus *their* immediate children (from level 5).
108
- Note that this option does not apply if `Items` is set.
109
 
110
- * **Relative to "Current" Item** *(checkbox)* as of v2.0.0
111
-
112
- This changes the `For Depth` option such that depth is applied relative to the current menu item, instead of relative to the
113
- first item found that is to be kept. It only has any effect when `For Depth` is set to something other than "unlimited", and when
114
- the current menu item is within the filtered items (before taking `For Depth` into account).
115
 
116
- * **Fallbacks**
117
 
118
- Fallback for `Children of` being set to either *Current Root Item* or *Current Parent Item*, and current item not having an ancestor:
 
 
119
 
120
- * **Switch to Current Item** *(checkbox)* as of v1.1.0
121
 
122
- If enabled,
123
- it provides a fallback of effectively switching the filter to "Current Item" if the current menu item
124
- (as determined by WordPress) is at Level 1
125
- (top level) of the selected menu. For example, say you were to set `Children of` to "Current Parent Item", with `Starting Level`
126
- at "1" and `For Depth` at "unlimited" : if the current menu item was found at level 1 (root level of the menu) then ordinarily
127
- there would be no output because the current item has no parent! If you were to enable the `Switch to Current Item` fallback then you
128
- *would* have some output - the entire branch below the current item.
129
 
130
- * **Include Parent...** *(checkbox)* as of v1.1.0
 
131
 
132
- This option extends the `Switch to Current Item` option (above). If the enabled fallback is actually used, this option can
133
- temporarily override the equivalent **Output** option to ON. Note that if the **Output** options are already set to include
134
- the parent item (with or without siblings), this option has absolutely no effect.
135
 
136
- * **& its Siblings** *(checkbox)* as of v1.1.0
 
137
 
138
- This option extends the `Switch to Current Item` option (above). If the enabled fallback is actually used, this option can
139
- temporarily override the equivalent **Output** option to ON. Note that if the equivalent **Output** option is already enabled,
140
- this option has absolutely no effect.
141
 
142
- Fallback for `Children of` being set to *Current Item*, and current item not having any children:
 
 
 
 
 
143
 
144
- * **Switch to Current Parent Item** *(checkbox)* as of v1.2.0
 
145
 
146
- If enabled, it provides a fallback of effectively switching the filter to "Current Parent Item" if looking for children
147
- of Current Item and there aren't any. For example, say you were to set `Children of` to "Current Item", with `Starting Level`
148
- at "1" and `For Depth` at "unlimited" : if the current menu item has no children then ordinarily
149
- there would be no output! If you were to enable the `Switch to Current Item` fallback then you
150
- *would* have some output - the current item and its siblings.
151
-
152
- Please note that there is one difference between this fallback and the normal "Current Parent Item" filter : if the current item
153
- has no ancestor (as well as no children) then you will always get the current item and its siblings, regardless of any other settings!
154
 
155
- * **Include Parent...** *(checkbox)* as of v1.2.0
156
 
157
- This option extends the `Switch to Current Parent Item` option (above). If the enabled fallback is actually used, this option can
158
- temporarily override the equivalent **Output** option to ON. Note that if the **Output** options are already set to include
159
- the parent item (with or without siblings), this option has absolutely no effect.
160
 
161
- * **& its Siblings** *(checkbox)* as of v1.2.0
162
 
163
- This option extends the `Switch to Current Parent Item` option (above). If the enabled fallback is actually used, this option can
164
- temporarily override the equivalent **Output** option to ON. Note that if the equivalent **Output** option is already enabled,
165
- this option has absolutely no effect.
166
 
167
- * **Output**
168
 
169
- * **Hierarchical** *(radio, default)*
 
 
 
 
 
170
 
171
- Output in the standard nested list format.
172
 
173
- * **Flat** *(radio)*
174
 
175
- Output in a single list format, ignoring any parent-child relationship other than to maintain the same physical order as would be
176
- presented in a `Hierarchical` output.
177
 
178
- * **Must Contain "Current" Item** *(checkbox)* as of v2.0.0
179
-
180
- If checked, the widget will not list any menu items unless the current menu item appears somewhere in the list.
 
 
 
 
 
 
 
 
 
181
 
182
- * **Include Parent...** *(checkbox)*
183
 
184
- If checked, include the parent item in the output. Only applies to a successful `Children of` filter.
 
185
 
186
- * **& its Siblings** *(checkbox)* as of v1.1.0
 
 
 
 
 
187
 
188
- If checked, include the parent item **and** its siblings in the output. Only applies to a successful `Children of` filter.
189
 
190
- * **Include Ancestors** *(checkbox)*
191
 
192
- Same as `Include Parent` except that all ancestors, right back to root level, are included. Only applies to a successful
193
- `Children of` filter.
194
 
195
- * **Title from Parent** *(checkbox)*
196
 
197
- Again, this only applies to a successful `Children of` filter. If checked, use the title of the parent item as the widget's
198
- title when displaying the output. This will override (ie. ignore) the `Hide` checkbox setting!
199
-
200
- Please note that this is **not** the same as asking for the title from "the parent of the current menu item"!
 
 
201
 
202
- * **Title from "Current" Item** *(checkbox)*
203
 
204
- If checked, use the title of the current menu item (as determined by WordPress) as the widget's
205
- title when displaying the output. This will override (ie. ignore) the `Hide` checkbox setting!
206
-
207
- Note that the current menu item is not required to be within the resultant output, merely within the `Select Menu`.
208
- Also, `Title from Parent` (if applicable, and if available) takes priority over this option.
 
 
209
 
210
- * **Change UL to OL**
211
 
212
- The standard for menus is to use UL (unordered list) elements to display the output. These settings give you the option to swap
213
- the ULs out for OLs (ordered lists).
214
-
215
- * **Top Level** *(checkbox)*
216
 
217
- If checked, swap the outermost UL for an OL.
218
-
219
- * **Sub-Levels** *(checkbox)*
220
 
221
- If checked, swap any nested (ie. not the outermost) ULs for an OLs.
222
 
223
- * **Hide Widget if Empty** *(checkbox)*
 
224
 
225
- If checked, the widget will not output *any* HTML unless it finds at least one menu item that matches the Filter settings.
226
-
227
- Please note that as of WordPress v3.6, this option becomes superfluous and will **not** be presented (the wp_nav_menu() function
228
- has been modified to automatically suppress all HTML output if there are no items to be displayed). The widget will retain
229
- the setting used on earlier WP versions (in case reversion from WP v3.6 might be required) but *will not present the option
230
- for WP v3.6+*.
231
 
232
- * **Container**
 
 
233
 
234
- * **Element** *(default: "div")*
 
 
 
235
 
236
- This menu list is usually wrapped in a "container" element, and this is the tag for that element. You may change it for another
237
- tag, or you may clear it out and the container will be completely removed. Please note that WordPress is set by default to only
238
- accept "div" or "nav", but that could be changed or extended by any template or plugin.
239
 
240
- * **Unique ID**
 
241
 
242
- This allows you to specify your own id (which should be unique) for the container.
 
243
 
244
- * **Class**
245
 
246
- This allows you to add your own class to the container element.
247
 
248
- * **Classes**
 
 
 
249
 
250
- * **Menu Class** *(default: "menu-widget")*
251
 
252
- This is the class that will be applied to the list element that holds the entire menu.
253
-
254
- * **Widget Class**
255
 
256
- This allows you to add your own class to the outermost element of the widget, the one that wraps the entire widget output.
 
 
257
 
258
- * **Links**
259
 
260
- * **Before the Link**
261
 
262
- Text or HTML that will be placed immediately before each menu item's link.
263
 
264
- * **After the Link**
265
 
266
- Text or HTML that will be placed immediately after each menu item's link.
267
 
268
- * **Before the Link Text**
269
 
270
- Text or HTML that will be placed immediately before each menu item's link text.
271
 
272
- * **After the Link Text**
273
 
274
- Text or HTML that will be placed immediately after each menu item's link text.
275
 
276
- == Shortcode Parameters ==
277
 
278
- The shortcode is **`[custom_menu_wizard]`**. Most of the attributes reflect the options available to the widget, but some have been simplified for easier use in the shortcode format.
279
- Please note that the `Hide Widget if Empty` option is not available to the shortcode : it is set to enabled, and if there are no menu items found then there will be no output from the shortcode.
280
 
281
- The simplest way to build a shortcode is to use the widget's "assist" facility (new in v2.0.0). The facilty is available even when the widget is in
282
- the Inactive Widgets area, so you don't have to add an unwanted instance of the widget to a sidebar.
283
 
284
- * **title** *(string)*
285
 
286
- 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.
287
 
288
- * **menu** *(string | integer)*
289
 
290
- Accepts a menu name (most likely usage) or id. If not provided, the shortcode will attempt to find the first menu (ordered by name)
291
- that has menu items attached to it, and use that.
292
 
293
- * **children_of** *(string | integer)*
294
 
295
- If not empty then it specifies a `Children of` filter. If neither `children_of` nor `items` are supplied (or are empty) then the
296
- filter defaults back to `Show all` (see above). Note that `items`, if supplied, will take precedence over `children_of`.
297
-
298
- * If numeric, it is taken as being the id of a menu item. The widget will look for the `Children of` that menu item (within `menu`).
299
- (Hint : In Menus Admin, hover over the item's **Remove** link and note the number after *menu-item=* in the URL)
300
-
301
- * Certain specific strings have the following meanings:
302
-
303
- * *'current'* or *'current-item'* : a `Children of` "Current Item" filter
304
 
305
- * *'parent'* or *'current-parent'* : a `Children of` "Current Parent Item" filter
306
 
307
- * *'root'* or *'current-ancestor'* : a `Children of` "Current Root Item" filter
308
 
309
- * If any other string, it is taken to be the title of a menu item. The widget will look for the `Children of` that menu item
310
- (within `menu`). Please note that the code looks for a *caseless* title match, so specifying `children_of="my menu item"` will
311
- match against a menu item with the title "My Menu Item". Also note that the first match found (hierarchically) is the one that
312
- gets used (it is quite possible to have same-named items within a menu structure).
 
313
 
314
- * **items** *(string)* See widget's `Items` option, under **Filter** above.
 
 
 
 
315
 
316
- * **fallback_parent** *(string | integer)*
 
317
 
318
- This is the fallback option for when `Children of` is set to either *Current Root Item* or *Current Parent Item*, and
319
- the current item has no ancestors (see `Switch to Current Item` under **Fallbacks** above).
 
320
 
321
- * Any "truthy" value (eg. 1, *'true'*, *'on'*, *'parent'*, *'siblings'*) : Enables widget's `Switch to Current Item` **Fallbacks** option
322
-
323
- * *'parent'* : Enables widget's `Include Parent...` **Fallbacks** extension option (in addition to the above)
324
 
325
- * *'siblings'* : Enables widget's `& its Siblings` **Fallbacks** extension option (in addition to the above)
 
326
 
327
- * **fallback_current** *(string | integer)*
 
 
328
 
329
- This is the fallback option for when `Children of` is set to *Current Item*, and
330
- the current item has no children (see `Switch to Current Parent Item` under **Fallbacks** above).
331
 
332
- * Any "truthy" value (eg. 1, *'true'*, *'on'*, *'parent'*, *'siblings'*) : Enables widget's `Switch to Current Parent Item` **Fallbacks** option
333
-
334
- * *'parent'* : Enables widget's `Include Parent...` **Fallbacks** extension option (in addition to the above)
 
 
 
335
 
336
- * *'siblings'* : Enables widget's `& its Siblings` **Fallbacks** extension option (in addition to the above)
 
 
 
 
337
 
338
- * **start_level** *(integer, default 1)* See widget's `Starting Level` option, under **Filter** above.
 
 
 
 
 
 
339
 
340
- * **depth** *(integer, default 0)* See widget's `For Depth` option, under **Filter** above.
 
 
341
 
342
- * **depth_rel_current** *(switch, off by default, 1 to enable)* See widget's `Relative to "Currrent" Item` option, under **Filter** above.
 
 
 
 
 
 
343
 
344
- * **flat_output** *(switch, off by default, 1 to enable)* See widget's `Flat` option, under **Output** above.
 
 
345
 
346
- * **contains_current** *(switch, off by default, 1 to enable)* See widget's `Must Contain "Current" Item` option, under **Output** above.
 
 
 
347
 
348
- * **include** *(string)*
 
 
 
349
 
350
- * *'parent'* : Enables widget's `Include Parent...` **Output** option
 
 
351
 
352
- * *'siblings'* : Enables widget's `& its Siblings` **Output** option
 
353
 
354
- * *'ancestors'* : Enables widget's `Include Ancestors` **Output** option
355
-
356
- Supply more than one by separating them with a comma, space or hyphen, eg. `include="siblings ancestors"`.
 
 
 
 
 
 
357
 
358
- * **title_from** *(string)*
 
359
 
360
- * *'parent'* : Enables widget's `Title from Parent` **Output** option
 
361
 
362
- * *'current'* : Enables widget's `Title from "Current" Item` **Output** option
 
363
 
364
- Supply more than one by separating them with a comma, space or hyphen, eg. `title_from="parent,current"`.
 
 
365
 
366
- * **ol_root** *(switch, off by default, 1 to enable)* See widget's `Top Level` option, under **Output** above.
 
367
 
368
- * **ol_sub** *(switch, off by default, 1 to enable)* See widget's `Sub-Levels` option, under **Output** above.
 
369
 
370
- * **container** *(string)* See widget's `Element` option, under **Container** above.
 
 
 
371
 
372
- * **container_id** *(string)* See widget's `Unique ID` option, under **Container** above.
 
373
 
374
- * **container_class** *(string)* See widget's `Class` option, under **Container** above.
 
375
 
376
- * **menu_class** *(string)* See widget's `Menu Class` option, under **Classes** above.
 
377
 
378
- * **widget_class** *(string)* See widget's `Widget Class` option, under **Classes** above.
 
379
 
380
- * **wrap_link** *(string)*
 
381
 
382
- This is an optional tag name (eg. *'div'*, *'p'*, *'span'*) that, if provided, will be made into HTML start/end tags
383
- and sent through to the widget as its `Before the Link` and `After the Link` options. Please note that the shortcode usage - a simple
384
- tag name - is much more restrictive than the widget's options, which allow HTML.
385
 
386
- * **wrap_link_text** *(string)*
 
387
 
388
- This is an optional tag name (eg. *'span'*, *'em'*, *'strong'*) that, if provided, will be made into HTML start/end tags
389
- and sent through to the widget as its `Before the Link Text` and `After the Link Text` options. Please note that the shortcode usage - a
390
- simple tag name - is much more restrictive than the widget's options, which allow HTML.
 
391
 
392
- **Shortcode Examples**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
 
394
- * Show the entire "main" menu :
395
 
396
  `
397
- [custom_menu_wizard menu=main]
398
  `
399
 
400
- * Show the children of the Current Item within the "main" menu, for unlimited depth, and include the Current Item's parent :
401
 
402
  `
403
- [custom_menu_wizard menu=main children_of=current include=parent]
404
  `
405
 
406
- * From the "animals" menu, show all the items *immediately* below (depth=1) "Small Dogs", plus "Small Dogs" and its sibling items, as ordered lists :
407
 
408
  `
409
- [custom_menu_wizard menu="animals" children_of="small dogs" depth=1 include="siblings" ol_root=1 ol_sub=1]
410
  `
411
 
412
  == Installation ==
@@ -418,32 +555,27 @@ the Inactive Widgets area, so you don't have to add an unwanted instance of the
418
  1. Activate the plugin through the 'Plugins' menu in your WP Admin
419
 
420
  The widget will now be available in the 'Widgets' admin page.
421
- As long as you already have at least one Menu defined, you can add the new widget to a sidebar and configure it however you want.
422
  Alternatively, you can use the shortcode in your content.
423
 
424
  == Frequently Asked Questions ==
 
425
 
426
- If you have a question or problem that is not covered here, please use the integrated Support forum.
427
-
428
- = Are there any known problems?
429
-
430
  Yep, 'fraid so :
431
 
432
- 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" (adding a page twice to a menu, for example), but if CMW has been configured to filter on anything related to a "current item" it can only choose one. This may also cause problems with some plugins that can affect a menu (PolyLang's language-switcher is one known example).
433
-
434
- 2. The widget does not play well with PolyLang's language-switcher when it has been added to a menu that the widget is filtering.
435
 
436
  = Why isn't it working? Why is there no output? =
437
-
438
  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
439
  the option settings on the widget/shortcode. The quickest way to resolve any such issues is to use the widget's interactive "assist", and
440
  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
441
- infallible, and if you still have problems then please let me have as much information as possible and I will endeavour to help. (Please
442
- note that simply reporting "It doesn't work" is not the most useful of feedbacks, and is unlikely to get a response other than, possibly,
443
- a request for more details).
444
 
445
  = How do I use the "assist"? =
446
-
447
  The widget's interactive "assist" is specific to each widget instance. It is a javascript-driven *emulator* that uses the widget instance's
448
  option settings - including the menu selected - to build a pictorial representation of the menu and show you, in blue, which menu items will
449
  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
@@ -453,183 +585,208 @@ item to change). The "current menu item" is shaded red, with its parent shaded o
453
  "current menu item" and the widget options are immediately reflected by the "assist" (text fields in the widget options simply need to lose
454
  focus).
455
 
456
- Once you are happy with the results, having tested all possible settings of "current menu item" (if it applies), then simply Save the widget.
457
- Alternatively, simply copy-paste the shortcode code produced by the "assist" straight into your post (you do not need to Save the widget!).
458
- The widget does not have to Saved to *test* any of the options.
 
 
459
 
460
- = Is there an easy way to construct the shortcode to get the results that I want? =
 
461
 
462
- Yes. Use the widget's interactive "assist" capability (see above). Note that you do not need to have the widget in a sidebar : the
463
- "assist" also works off a widget that is in the Inactive Widgets area of the widget admin page.
 
464
 
465
- = How do I get the menu item ids for the `Items` option? =
 
 
 
466
 
467
- Use the widget's interactive "assist" (see above). Within the representation menu structure, each menu item's id is set in its title
 
468
  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
469
- then show a checkbox beside each menu item and you simply [un]check the items as required. Each selection will be reflected back into the
470
- widget's `Items` settings, and also in the shortcode code.
471
 
472
- Alternatively, go to Appearance, Menus and select the relevant menu; hover over the edit, Remove, or Cancel link for an item and look in
473
  the URL (the link's href) for `menu-item=NNN` ... the NNN is the menu item id.
474
 
475
- = Why is the `Must Contain Current Item` option in the Output section and not in the Filter section? =
 
 
476
 
477
- It was a close call, but since the Output options can extend the final list - and the check for "current menu item" is made against the
478
- *entire* resultant list - I decided that `Must contain Current Item` was more of a "final output" check than an initial filter.
 
479
 
480
- == Screenshots ==
 
 
 
 
 
 
 
 
 
481
 
482
- 1. Widget options
 
 
 
483
 
484
- 2. More widget options
 
 
 
 
485
 
486
- 3. Even more widget options
 
 
487
 
488
- 4. Widget's "assist"
 
 
 
 
 
 
 
 
 
489
 
490
  == Changelog ==
491
 
492
- = 2.0.6 =
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
 
 
494
  * change : modified determination of current item to cope better with multiple occurences (still first-found, but within prioritised groups)
495
-
496
  * change : display of the upgrade notice in the plugins list has been replaced with a simple request to read the changelog before upgrading
497
 
498
  = 2.0.5 =
499
-
500
  * bugfix : prevent PHP warnings of Undefined index/offset
501
 
502
  = 2.0.4 =
503
-
504
  * bugfix : clearing the container field failed to remove the container from the output
505
-
506
  * addition : in the "assist", added automatic selection of the shortcode text when it is clicked
507
-
508
  * addition : remove WordPress's menu-item-has-children class (introduced in v3.7) when the filtered item no longer has children
509
-
510
  * change : tweaked styles and javascript in admin for WordPress v3.8
511
 
512
  = 2.0.3 =
513
-
514
  * bugfix : missing global when enqueuing scripts and styles for admin
515
 
516
  = 2.0.2 =
517
-
518
  * bugfix : the Include Ancestors option was not automatically including the Parent
519
-
520
  * bugfix : the "assist" was incorrectly calculating Depth Relative to Current Item when the current menu item was outside the scope of the Filtered items
521
-
522
  * behaviour change : only recognise the first "current" item found (used to allow subsequent "current" items to override any already encountered)
523
 
524
  = 2.0.1 =
525
-
526
  * bugfix : an incorrect test for a specific-items filter prevented show-all producing any output
527
 
528
  = 2.0.0 =
529
-
530
  * **! Possible Breaker !** The calculation of `Start Level` has been made consistent across the `Show all` and `Children of` filters : if you previously had a setup where you were filtering for the children of an item at level 2, with start level set to 4, there would have been no output because the immediate children (at level 3) were outside the start level. Now, there *will* be output, starting with the grand-children (at level 4).
531
-
532
  * **! Possible Breaker !** There is now deemed to be an artificial "root" item above the level 1 items, which mean that a `Children of` filter set to "Current Parent Item" or "Current Root Item" will no longer fail for a top-level "current menu item". If you have the "no ancestor" fallback set then this change will have no impact (but you may now want to consider turning the fallback off?); if you *don't* currently use the "no ancestor" fallback, then where there was previously no output there will now be some!
533
-
534
  * added new option : Items, a comma- or space-delimited list of menu item ids, as an alternative Filter
535
-
536
  * added new option : Depth Relative to Current Item to the Filter section (depth_rel_current=1 in the shortcode)
537
-
538
  * added new option : Must Contain Current Item to the Output section (contains_current=1 in the shortcode)
539
-
540
  * changed the widget's "demo" facility to "assist" and brought it into WordPress admin, with full interactivity with the widget
541
-
542
  * refactored code
543
 
544
  = 1.2.2 =
545
-
546
  * bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
547
 
548
  = 1.2.1 =
549
-
550
  * 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)
551
-
552
  * corrected 'show all from start level 1' processing so that custom classes get applied and 'Title from "Current" Item' works (regardless of filter settings)
553
-
554
  * changed the defaults for new widgets such that only the Filter section is open by default; all the others are collapsed
555
-
556
  * in demo.html, added output of the shortcode applicable to the selections made
557
-
558
  * in demo.html, added a link to the documentation page
559
-
560
  * corrected 2 of the shortcode examples in the readme.txt, and made emulator (demo) available from the readme
561
 
562
  = 1.2.0 =
563
-
564
  * added custom_menu_wizard shortcode, to run the widget from within content
565
-
566
  * moved the 'no ancestor' fallback into new Fallback collapsible section, and added a fallback for Current Item with no children
567
-
568
  * added an option allowing setting of title from current menu item's title
569
-
570
  * 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)
571
-
572
  * don't include menus with no items
573
-
574
  * updated demo.html
575
 
576
  = 1.1.0 =
577
-
578
  * added 'Current Root Item' and 'Current Parent Item' to the `Children of` filter
579
-
580
  * added `Fallback to Current Item` option, with subsidiary options for overriding a couple of Output options, as a means to enable Current Root & Current Parent to match a Current Item at root level
581
-
582
  * added an Output option to include both the parent item **and** the parent's siblings (for a successful `Children of` filter)
583
-
584
  * added max-width style (100%) to the `Children of` SELECT in the widget options
585
-
586
  * added widget version to the admin js enqueuer
587
-
588
  * ignore/disable Hide Empty option for WP >= v3.6 (wp_nav_menu() now does it automatically)
589
-
590
  * included a stand-alone helper/demo html page
591
-
592
  * rebuilt the `Children of` SELECT in the widget options to cope with IE's lack of OPTGROUP/OPTION styling
593
-
594
  * moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript
595
 
596
  = 1.0.0 =
597
-
598
- Initial release
599
 
600
  == Upgrade Notice ==
601
 
602
- = 2.0.6 =
 
 
 
603
 
 
604
  Determination of the current menu item has been slightly modified to cope a bit better with occasions where multiple items have been set as "current".
605
  The display of the upgrade notice in the plugins list has been replaced with a simple request to read the changelog before upgrading.
606
 
607
  = 2.0.5 =
608
-
609
  Fixed a bug to prevent PHP warnings of Undefined index/offset being output.
610
 
611
  = 2.0.4 =
612
-
613
  Fixed a bug that prevented the container field being removed, and added removal of the menu-item-has-children class when the filtered item no longer has children.
614
  The admin widget styling and javascript have been tweaked to accommodate WordPress 3.8.
615
 
616
  = 2.0.3 =
617
-
618
  Fixed a minor bug with a missing global when enqueuing script and style for the admin.
619
 
620
  = 2.0.2 =
621
-
622
  Fixed a bug with the Include Ancestors option, where it was not automatically including the Parent.
623
  Fixed a bug in the "assist", where it was incorrectly calculating Depth Relative to Current Item when the current menu item was outside the scope of the Filtered items.
624
  Changed determination of the "current" item such that only the first one encountered is recognised, rather than allowing subsequent "current" items to override previous ones.
625
 
626
  = 2.0.1 =
627
-
628
  Fixed a bug whereby a test for a specific-items filter prevented show-all from producing any output.
629
 
630
  = 2.0.0 =
631
-
632
- **! 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 :
633
  + if you have set a `Children of` filter, **and** you have changed the `Start Level` to a level greater than 2, or
634
  + if you have set the `Children of` filter to Current Parent/Root Item, and you have **not** set the "no ancestor" fallback.
635
  *__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.__*
@@ -641,17 +798,14 @@ New options :
641
  Rebuilt the "demo" facility as an "assist" wizard for the widget It is now fully interactive with the widget instance, and generates the entire shortcode according to the widget instance settings.
642
 
643
  = 1.2.2 =
644
-
645
  Bugfix : The fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
646
 
647
  = 1.2.1 =
648
-
649
  Added a few extra custom classes, and changed the defaults for new widgets such that only the Filter section is open by default.
650
  Fixed Show All processing so that custom classes always get applied, and 'Title from "Current" Item' works regardless of filter settings.
651
  Fixed a couple of the shortcode examples in the readme.txt, and added display of the applicable shortcode settings to the demo.html.
652
 
653
  = 1.2.0 =
654
-
655
  Added custom_menu_wizard shortcode, to run the widget from within content.
656
  Added a new fallback for Current Item having no children, and moved all fallbacks into a collapsible Fallbacks section.
657
  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).
1
  === Custom Menu Wizard Widget ===
2
  Contributors: wizzud
3
+ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KP2LVCBXNCEB4
4
+ Tags: menu,widget,widgets,navigation,nav,custom menus,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: 3.9
7
+ Stable tag: 3.0.0
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.
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:
18
 
19
+ * Display an entire menu, just a branch of it, just certain level(s) of it, or even just specific items from it!
20
+ * Select a branch based on a specific menu item, or the current menu item (currently displayed page)
21
+ * Specify a relative or absolute level to start at, and the number of levels to output
22
+ * Include ancestor item(s) in the output, with or without siblings
23
+ * Exclude certain menu items, or levels of items
24
+ * Make the output conditional upon the current menu item being found in different stages of the filter selection process
 
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 hierarchicial 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
+
35
+ Current documentation for the [Widget Options](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#WIDGET-OPTIONS),
36
+ and the associated [Shortcode Parameters](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#SHORTCODE-PARAMETERS),
37
+ can be found under [Other Notes](http://wordpress.org/plugins/custom-menu-wizard/other_notes/).
38
+
39
+ **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
40
+ there are probably a couple of very common scenarios:
41
+
42
+ 1. Show the current menu item, plus any descendants...
43
+ * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
44
+ * Select the menu you wish to use (if it's not already selected)
45
+ * Open the FILTERS section :
46
+ * under Primary Filter, click on the *Branch* radio
47
+ * Save the widget!
48
+ * *Equivalent shortcode resembles `[cmwizard menu=N title="Your Title" branch=current]`*
49
+
50
+ 2. Show just the descendants of the current menu item (if there are any)...
51
+ * Drag a new Custom Menu Wizard widget into the sidebar, and give it a title (if you want one)
52
+ * Select the menu you wish to use (if it's not already selected)
53
+ * Open the FILTERS section :
54
+ * under Primary Filter, click on the *Branch* radio
55
+ * under Secondary Filter, set *Starting at* to "+1 (children)"
56
+ * Save the widget!
57
+ * *Equivalent shortcode resembles `[cmwizard menu=N title="Your Title" branch=current start_at="+1"]`*
58
+
59
+ If you like this widget (or if you don't?), please consider taking a moment or two to give it a
60
+ [Review](http://wordpress.org/support/view/plugin-reviews/custom-menu-wizard) : it helps others, and gives me valuable feedback.
61
 
62
+ *Documentation for version 2 of the widget
63
+ can be found [here](http://plugins.svn.wordpress.org/custom-menu-wizard/tags/2.0.6/readme.txt)
64
+ or [here](http://www.wizzud.com/v210-readme.html).*
65
 
66
+ == WIDGET OPTIONS ==
67
+
68
+ *NB. Version 2 documentation is [here](http://plugins.svn.wordpress.org/custom-menu-wizard/tags/3.0.0/v210-readme.html#Widget-Options).*
69
 
70
  There are quite a few options, which makes the widget settings box very long. I have therefore grouped most of the options into
71
+ collapsible logical sections (with remembered state once saved).
72
+
73
+ Note that certain options may be enabled/disabled depending on your choice of primary, and possibly secondary, filters.
74
 
75
+ ***Always Visible***
76
+
77
+ * **Title** *textbox*
78
 
79
  Set the title for your widget.
80
 
81
+ * **Hide** *checkbox*
82
 
83
  Prevents the entered `Title` being displayed in the front-end widget output.
84
+
85
+ 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
86
  don't necessarily want that `Title` to actually be output when the widget is displayed at the front-end. Hence this checkbox.
87
 
88
+ * **Select Menu** *select*
89
 
90
  Choose the appropriate menu from the dropdown list of Custom Menus currently defined in your WordPress application. The
91
  first one available (alphabetically) is already selected for you by default.
92
 
93
+ == Filters Section ==
94
+
95
+ Filters are applied in the order they are presented.
96
+
97
+ ***Primary Filter***
98
 
99
+ * **Level** *radio (default On) & select*
100
+
101
+ Filters by level within the selected menu, starting at the level selected here. This is the default setting
102
+ for a new widget instance, which, if left alone and with all other options at their default, will show the entire selected menu.
103
+
104
+ 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".
105
+
106
+ * **Branch** *radio & select*
107
+
108
+ Filters by branch, with the head item of the branch being selected from the dropdown. The dropdown presents all the
109
+ items from the selected menu, plus a "Current Item" option (the default). Selecting "Current Item" means that the head item of the
110
+ branch is the current menu item (as indicated by WordPress), provided, of course, that the current menu item actually corresponds to
111
+ a menu item from the currently selected menu!
112
+
113
+ * **Items** *radio & textbox*
114
+
115
+ Filters by the menu items that you specifically pick (by menu item id, as a comma-separated list). The simplest way
116
+ 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
117
+ you want. Alternatively, just type your list of ids into the box.
118
 
119
+ If the id is appended with a '+', eg. '23+', then all the item's descendants will also be included.
120
+
121
+ 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,
122
+ then you might want to use this option.
123
 
124
+ 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
125
+ "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
126
+ descendants.
127
 
128
+ ***Secondary Filter*** *(not applicable to an `Items` filter)*
 
 
129
 
130
+ * **Starting at** *select*
131
 
132
+ This is only applicable to a `Branch` filter and it allows you to shift the starting point of your output within the confines
133
+ 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.
134
+ parent, grandparent, children, etc) or to an absolute, fixed level within the branch containing the selected branch item (eg. the root
135
+ level item for the branch, or one level below the branch's root item, etc).
136
 
137
+ Example : If you wanted the entire "current" branch then, with `Branch` set to "Current Item", you might set `Starting at` to "1 (root)".
138
+ Alternatively, if you wanted the children of the current menu item then `Starting at` could be set to "+1 (children)".
139
 
140
+ * **Item, if possible** *radio (default On)*
 
 
 
141
 
142
+ 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
143
+ an ancestor thereof) then only that item and its descendants are considered for filtering.
144
 
145
+ * **Level** *radio*
 
 
 
 
146
 
147
+ Changes the default filter mechanism such that if `Starting at` results in the selection of the branch item or one of its ancestors,
148
+ then all siblings of that resultant item are also included in the secondary filtering process.
149
+
150
+ Example : If Joe and Fred are siblings (ie. they have a common parent) and Joe is the selected branch item - with `Starting at` set
151
+ to Joe - then the secondary filter would normally only consider Joe and its descendants. However, if `Level` was enabled instead of
152
+ `Item`, then both Joe and Fred, *and all their descendants*, would be considered for filtering.
153
+
154
+ 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
155
+ be enabled in order to allow the other sibling root items to be added into the filter process.
156
 
157
+ * **Allow all Root Items** *checkbox*
 
 
 
158
 
159
+ In the right conditions - see `Level` above - this allows sibling root items to be considered for secondary filtering.
160
 
161
+ * **For Depth** *select*
 
 
 
 
 
 
 
162
 
163
+ This the number of levels of the menu structure that will be considered for inclusion in the final output.
 
 
 
 
164
 
165
+ * **Relative to Current Item** *checkbox*
166
 
167
+ By default, `For Depth` (above) is relative to the first item found, but this may be overridden to be relative to the
168
+ current menu item ***if*** `For Depth` is not unlimited **and** the current menu item can found within the selected menu.
169
+ If the current menu item is not within the selected menu then it falls back to being relative to the first item found.
170
 
171
+ ***Inclusions***
172
 
173
+ These allow certain other items to be added to the output from the secondary filters.
 
 
 
 
 
 
174
 
175
+ 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
176
+ or below the `Starting at` level, and do not include any items that would break the depth limit set in the Secondary Filter options.
177
 
178
+ * **Branch Ancestors** *select*
 
 
179
 
180
+ Include any ancestors (parent, grandparent, etc) of the items selected as the `Branch` filter.
181
+ 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.
182
 
183
+ * **... with Siblings** *select*
 
 
184
 
185
+ In conjunction with `Branch Ancestors`, also include all siblings of those ancestors.
186
+ 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
187
+ to the `Branch` filter item. Note that while it is possibe to set a larger range for siblings than ancestors, the final output
188
+ is limited by `Branch Ancestors` setting.
189
+
190
+ * **Branch Siblings** *checkbox*
191
 
192
+ Include any siblings of the item selected as the `Branch` filter (ie. any items at the same level and within
193
+ the same branch as the `Branch` item).
194
 
195
+ * **All Root Items** *checkbox*
 
 
 
 
 
 
 
196
 
197
+ This is not restricted by other previous filter settings, and simply adds all the top level menu items into the mix.
198
 
199
+ ***Exclusions***
 
 
200
 
201
+ * **Item Ids** *textbox*
202
 
203
+ This is a comma-separated list of the ids of menu items that you do *not* want to appear in the final output.
204
+ The simplest way to get your list of ids is to use the "assist", and [un]check
205
+ the red cross box at the left hand side of each depicted menu item. Alternatively, just type your list of ids into the box.
206
 
207
+ If the id is appended with a '+', eg. '23+', then all the item's descendants will also be excluded.
208
 
209
+ Example : If you wanted to show the entire "A" branch, with the sole exception of one grandchild of "A", say "ABC", then you could
210
+ set `Branch` to "A", and `Exclusions` to the id of the "ABC" item.
211
+
212
+ 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
213
+ 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
214
+ the "+" indicates that all the descendants of "C" should also be excluded.
215
 
216
+ * **By Level** *select*
217
 
218
+ This allows an entire level of items to be excluded, optionally also excluding all levels either above or below it.
219
 
220
+ ***Qualifier***
 
221
 
222
+ * **Current Item is in** *select*
223
+
224
+ 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
225
+ for output at a particular stage in the filter proccessing.
226
+
227
+ * *"Menu"* : the current menu item has to be somewhere within the selected menu.
228
+ * *"Primary Filter"* : the current menu item has to be within the scope of the selected primary filter. So if you selected, say, a child
229
+ of "A" as the `Branch` item, then if "A" was the current menu item there would be no output with this qualifier.
230
+ * *"Secondary Filter"* : the current menu item has to be within the items as restricted by the secondary filters. So if you
231
+ 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.
232
+ * *"Inclusions"* : the current menu item has to be in within the items as set by the primary and secondary filters, and the inclusions.
233
+ * *"Final Output"* : the current menu item has to be in the final output.
234
 
235
+ == Fallbacks Section ==
236
 
237
+ Fallbacks get applied at the Secondary Filter stage, and their eligibility and application are therefore determined and
238
+ governed by the other Secondary Filter settings.
239
 
240
+ There is one fallback, and it only comes into play (possibly) when a `Branch` filter
241
+ 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,
242
+ and would normally include some of the current item's descendants (eg. `Starting at` "the Branch", `For Depth` "1 level" does *not* invoke
243
+ the fallback).
244
+ The fallback allows for the occasion when the current menu item
245
+ *does not have* any immediate children, and provides the ability to then switch the following options:
246
 
247
+ * **Starting at** *select*
248
 
249
+ Enable the fallback, and change the `Starting at` from "+1 (children)" to either
250
 
251
+ * *"-1 (parent)"* : the immediate parent of the current menu item.
252
+ * *"the Current Item"* : the current menu item itself.
253
 
254
+ * **...and Include its Siblings** *checkbox*
255
 
256
+ This will add in the siblings of the item selected above.
257
+
258
+ Note : This *only* adds the siblings, not the siblings' descendants! However, if the `Level` radio (in Secondary Filter stage above) is
259
+ set, then all the item's siblings *and their descendants* will automatically be included, and [un]setting this option will have no effect.
260
+ 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
261
+ outside the current branch depends on the setting of the `Allow all Root Items` checkbox.
262
 
263
+ * **For Depth** *select*
264
 
265
+ Override the current `For Depth` setting. Note that any depth value set here will be relative to the current item, regardless
266
+ of the current setting of `...Relative to`!
267
+
268
+ 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
269
+ no children, whereas C has loads of children/grandchildren. If you fallback to B's parent - A - with Unlimited depth set, then you will
270
+ 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
271
+ fallback option to "1"? Or maybe A, B, C, and C's immediate children, by setting "2"?
272
 
273
+ == Output Section ==
274
 
275
+ * **Hierarchical** *radio (default On)*
 
 
 
276
 
277
+ Output in the standard nested list format. The alternative is `Flat`.
 
 
278
 
279
+ * **Flat** *radio*
280
 
281
+ Output in a single list format, ignoring any parent-child relationship other than to maintain the same physical order as would be
282
+ presented in a `Hierarchical` output (which is the alternative and default).
283
 
284
+ * **Set Title from** *checkboxes*
 
 
 
 
 
285
 
286
+ These allow you to set the `Title` option from a menu item, and, if brought into play, the `Hide` flag is ignored.
287
+ 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!
288
+ Note also that priority is the order presented (first found, first used).
289
 
290
+ * **Current Item** : sets `Title` from the current menu item (if current menu item is in the selected menu).
291
+ * **...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).
292
+ * **Branch** : only applicable to a `Branch` filter, and sets `Title` from the `Branch` item.
293
+ * **...or its Root** : only applicable to a `Branch` filter, and sets `Title` from the branch's root menu item.
294
 
295
+ * **Change UL to OL** *checkboxes*
 
 
296
 
297
+ The standard for menus is to use UL (unordered list) elements to display the output. These settings give you the option to
298
+ swap the ULs out for OLs (ordered lists).
299
 
300
+ * **Top Level** : swap the outermost UL for an OL.
301
+ * **Sub-Levels** : swap any nested (ie. not the outermost) ULs for an OLs.
302
 
303
+ * **Hide Widget if Empty** *checkbox*
304
 
305
+ If checked, the widget will not output *any* HTML unless it finds at least one menu item that matches the Filter settings.
306
 
307
+ Please note that as of WordPress v3.6, this option becomes superfluous and will **not** be presented (the wp_nav_menu() function
308
+ has been modified to automatically suppress all HTML output if there are no items to be displayed). The widget will retain
309
+ the setting used on earlier WP versions (in case reversion from WP v3.6 might be required) but *will not present the option
310
+ for WP v3.6+*.
311
 
312
+ == Container Section ==
313
 
314
+ * **Element** *textbox (default "div")*
 
 
315
 
316
+ The menu list is usually wrapped in a "container" element, and this is the tag for that element.
317
+ You may change it for another tag, or you may clear it out and the container will be completely removed. Please note that
318
+ WordPress is set by default to only accept "div" or "nav", but that could be changed or extended by any theme or plugin.
319
 
320
+ * **Unique ID** *textbox*
321
 
322
+ This allows you to specify your own id (which should be unique) for the container.
323
 
324
+ * **Class** *textbox*
325
 
326
+ This allows you to add your own class to the container element.
327
 
328
+ == Classes Section ==
329
 
330
+ * **Menu Class** *textbox (default "menu-widget")*
331
 
332
+ This is the class that will be applied to the list element that holds the entire menu.
333
 
334
+ * **Widget Class** *textbox*
335
 
336
+ This allows you to add your own class to the outermost element of the widget, the one that wraps the entire widget output.
337
 
338
+ == Links Section ==
339
 
340
+ * **Before the Link** *textbox*
 
341
 
342
+ Text or HTML that will be placed immediately before each menu item's link.
 
343
 
344
+ * **After the Link** *textbox*
345
 
346
+ Text or HTML that will be placed immediately after each menu item's link.
347
 
348
+ * **Before the Link Text** *textbox*
349
 
350
+ Text or HTML that will be placed immediately before each menu item's link text.
 
351
 
352
+ * **After the Link Text** *textbox*
353
 
354
+ Text or HTML that will be placed immediately after each menu item's link text.
 
 
 
 
 
 
 
 
355
 
356
+ == SHORTCODE PARAMETERS ==
357
 
358
+ *NB. Version 2 documentation is [here](http://plugins.svn.wordpress.org/custom-menu-wizard/tags/3.0.0/v210-readme.html#Shortcode-Parameters).*
359
 
360
+ The shortcode is **`[cmwizard]`** (prior to v3, shortcode was *`[custom_menu_wizard]`*, and it is still supported but with a slightly
361
+ different parameter set).
362
+ Most of the attributes reflect the options available to the widget, but some have been simplified for easier use in the shortcode format.
363
+ 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
364
+ found then there will be no output from the shortcode.
365
 
366
+ 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
367
+ 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
368
+ 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
369
+ with while constructing your shortcode ***do not have to be Saved*** to the widget itself! Just copy-paste the shortcode when
370
+ you are happy with it.
371
 
372
+ = title =
373
+ *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.
374
 
375
+ = menu =
376
+ *string or integer* : Accepts a menu name or id. If not provided, the shortcode will attempt to find the first menu (alphabetically)
377
+ that has menu items attached to it, and use that.
378
 
379
+ = level =
380
+ *integer* : Sets the `Level` filter to the specified (greater than zero) value. Defaults to 1, and is ignored if either **branch** or **items** is specified.
 
381
 
382
+ = branch =
383
+ *string or integer* : If not empty then `Branch` is set as the primary filter, with the branch item being set from the assigned value:
384
 
385
+ * If numeric, it is taken as being the id of a menu item.
386
+ * If set to either *"current"* or *"current-item"* then the `Branch` item is set to "Current Item".
387
+ * 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 *caseless* title match, so specifying `branch="my menu item"` will match against a menu item with the title "My Menu Item".
388
 
389
+ = items =
390
+ *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**.
391
 
392
+ = start_at =
393
+ *string* : This is only relevant to a `Branch` filter, and consists of a signed or unsigned integer that indicates either a relative
394
+ (to the selected branch item) or absolute level to start your output at (ref. the widget's `Starting at` option under *Secondary Filter*,
395
+ [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above).
396
+ By default the starting level for output is the branch item's level. A relative level is indicated by a signed (ie. preceded by
397
+ a "+" or "-") integer, eg. `start_at="+1"`, while an absolute level is unsigned, eg. `start_at="1"`. Some examples :
398
 
399
+ * `start_at="+1"` : (relative) start at the branch item's level + 1 (also accepts `start_at="children"`)
400
+ * `start_at="-1"` : (relative) start at the branch item's level - 1 (also accepts `start_at="parent"`)
401
+ * `start_at="-2"` : (relative) would be the "grandparent" level
402
+ * `start_at="1"` : (absolute) start at the root item of the selected branch (also accepts `start_at="root"`)
403
+ * `start_at="2"` : (absolute) start at one level below root (still within the selected branch)
404
 
405
+ = start_mode =
406
+ *string* : This has only one accepted value - "level" - and is only applicable for a `Branch` filter whose **start_at** setting returns
407
+ in an item is at or above the selected branch item (relatively or absolutely).
408
+ Setting `start_mode="level"` forces the widget to use not only the resultant starting item
409
+ and its relevant descendants, but also all that item's siblings *and their descendants*
410
+ (ref. the widget's `Level` radio option under *Secondary Filter*,
411
+ [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above).
412
 
413
+ = allow_all_root =
414
+ *switch, off by default, 1 to enable* : See widget's `Allow all Root Items` option, under *Secondary Filter*,
415
+ [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above.
416
 
417
+ = depth =
418
+ *integer, default 0 (unlimited)* : See widget's `For Depth` option, under *Secondary Filter*,
419
+ [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above.
420
+
421
+ = depth_rel_current =
422
+ *switch, off by default, 1 to enable* : See widget's `Relative to Current Item` option, under *Secondary Filter*,
423
+ [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above.
424
 
425
+ = include_root =
426
+ *switch, off by default, 1 to enable* : Sets the widget's Include `All Root Items` option. See widget's `All Root Items`
427
+ option, under *Inclusions*, [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above.
428
 
429
+ = ancestors =
430
+ *integer, default 0 (off)* : Sets an absolute level (positive integer), or a relative number of levels (negative integer), for which
431
+ the ancestors of the `Branch` filter item should be included. See widget's `Branch Ancestors` option, under *Inclusions*,
432
+ [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above. (only relevant to a `Branch` filter)
433
 
434
+ = ancestor_siblings =
435
+ *integer, default 0 (off)* : Sets an absolute level (positive integer), or a relative number of levels (negative integer), for which
436
+ the siblings of ancestors of the `Branch` filter item should be included. See widget's `... with Siblings` option, under *Inclusions*,
437
+ [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above. (only relevant to a `Branch` filter)
438
 
439
+ = siblings =
440
+ *switch, off by default, 1 to enable* : See widget's `Branch Siblings` option, under *Inclusions*,
441
+ [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above. (only relevant to a `Branch` filter)
442
 
443
+ = exclude =
444
+ *string* : Comma-separated list of meu item ids, where an id can optionally be followed by a '+' to include all its descendants (eg. "23+").
445
 
446
+ = exclude_level =
447
+ *string* : A level (1, 2, 3, etc), optionally followed by a "+" or "-" to include all subsequent (lower) or prior (higher)
448
+ levels respectively. For example, "2" will exclude all items at level 2, whereas "2-" would exclude all level 1 **and** level 2 items,
449
+ and "2+" would exlude all items at level 2 or greater.
450
+
451
+ = contains_current =
452
+ *string* : Accepted values : "menu", "primary", "secondary", "inclusions", or "output". See widget's *Qualifier* options,
453
+ under [Filters Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Filters-Section) above,
454
+ for an explanation of the respective settings.
455
 
456
+ = fallback =
457
+ *string* : There are 2 main options for fallback (ref. [Fallbacks Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Fallbacks-Section) above)...
458
 
459
+ * *"parent"* : Sets the widget's `Starting at` Fallback option to "-1 (parent)"
460
+ * *"current"* : Sets the widget's `Starting at` Fallback option to "the Current Item"
461
 
462
+ 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
463
+ will also set the widget's `For Depth` fallback option to the value of the digit(s).
464
 
465
+ Optionally, "+siblings" can also be used (comma-separated, with or without a depth digit) to indicate that siblings of the "parent" or
466
+ "current" fallback item should also be included. The order of the comma-separated values is not important, so "current,+siblings,1" is the
467
+ same as "current,1,+siblings", and "2,parent" is the same as "parent,2", etc.
468
 
469
+ = flat_output =
470
+ *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) above.
471
 
472
+ = title_from =
473
+ *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) above)...
474
 
475
+ * *"current"* : enables the widget's *Current Item* `Set Title from` option
476
+ * *"current-root"* : enables the widget's *...or its Root* option that relates to the `Current Item` `Set Title from` option
477
+ * *"branch"* : enables the widget's *Branch* `Set Title from` option
478
+ * *"branch-root"* : enables the widget's *...or its Root* option that relates to the `Branch` `Set Title from` option
479
 
480
+ = ol_root =
481
+ *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) above.
482
 
483
+ = ol_sub =
484
+ *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) above.
485
 
486
+ = container =
487
+ *string* : See widget's `Element` option, under [Container Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Container-Section) above.
488
 
489
+ = container_id =
490
+ *string* : See widget's `Unique ID` option, under [Container Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Container-Section) above.
491
 
492
+ = container_class =
493
+ *string* : See widget's `Class` option, under [Container Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Container-Section) above.
494
 
495
+ = menu_class =
496
+ *string* : See widget's `Menu Class` option, under [Classes Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Classes-Section) above.
 
497
 
498
+ = widget_class =
499
+ *string* : See widget's `Widget Class` option, under [Classes Section](http://wordpress.org/plugins/custom-menu-wizard/other_notes/#Classes-Section) above.
500
 
501
+ = wrap_link =
502
+ *string* : This is an optional tag name (eg. "div", "p", "span") that, if provided, will be made into HTML start/end tags
503
+ 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)).
504
+ Please note that the shortcode usage - a simple tag name - is much more restrictive than the widget's options, which allow HTML.
505
 
506
+ = wrap_link_text =
507
+ *string* : This is an optional tag name (eg. "span", "em", "strong") that, if provided, will be made into HTML start/end tags
508
+ 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)).
509
+ Please note that the shortcode usage - a simple tag name - is much more restrictive than the widget's options, which allow HTML.
510
+
511
+ = title_tag =
512
+ *string* : An optional tag name (eg. "h1", "h3", etc) to replace the default "h2" used to enclose the widget title.
513
+ Please note that this option has no equivalent in the widget options, because it *only* applies when a widget is instantiated via a shortcode.
514
+
515
+ = findme =
516
+ *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.
517
+ 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
518
+ 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
519
+ from any widget's "assist".
520
+
521
+ Example : `[cmwizard findme=1 title="Posts containing a CMW shortcode..."]`
522
+
523
+ == Shortcode Examples ==
524
+
525
+ * Show the entire "main" menu
526
+
527
+ `
528
+ [cmwizard menu=main]
529
+ `
530
 
531
+ * Show the children of the current menu item within the "main" menu, for unlimited depth, setting the widget title from the current menu item
532
 
533
  `
534
+ [cmwizard menu=main branch=current start_at=children title_from=current]
535
  `
536
 
537
+ * From the "animals" menu, show all the items *immediately* below "Small Dogs", plus "Small Dogs" and its sibling items, as ordered lists
538
 
539
  `
540
+ [cmwizard menu="animals" branch="small dogs" depth=2 include="siblings" ol_root=1 ol_sub=1]
541
  `
542
 
543
+ * 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
544
 
545
  `
546
+ [cmwizard menu="animals" branch="small animals" start_at=children contains_current=primary]
547
  `
548
 
549
  == Installation ==
555
  1. Activate the plugin through the 'Plugins' menu in your WP Admin
556
 
557
  The widget will now be available in the 'Widgets' admin page.
558
+ 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.
559
  Alternatively, you can use the shortcode in your content.
560
 
561
  == Frequently Asked Questions ==
562
+ 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).
563
 
564
+ = Are there any known problems/restrictions? =
 
 
 
565
  Yep, 'fraid so :
566
 
567
+ 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.
568
+ 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.
 
569
 
570
  = Why isn't it working? Why is there no output? =
 
571
  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
572
  the option settings on the widget/shortcode. The quickest way to resolve any such issues is to use the widget's interactive "assist", and
573
  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
574
+ 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
575
+ (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
576
+ the most useful of feedbacks, and is unlikely to get a response other than, possibly, a request for more details.
577
 
578
  = How do I use the "assist"? =
 
579
  The widget's interactive "assist" is specific to each widget instance. It is a javascript-driven *emulator* that uses the widget instance's
580
  option settings - including the menu selected - to build a pictorial representation of the menu and show you, in blue, which menu items will
581
  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
585
  "current menu item" and the widget options are immediately reflected by the "assist" (text fields in the widget options simply need to lose
586
  focus).
587
 
588
+ 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 :
589
+
590
+ * Off (dimmed)
591
+ * Just this item (white on red)
592
+ * This item *plus* all its descendants (white on red, with a small yellow plus sign)
593
 
594
+ 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
595
+ work in the same way.
596
 
597
+ Once you are happy with the results, having tested all possible settings of "current menu item" (if it applies), then simply Save the widget.
598
+ 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!).
599
+ The widget does not have to Saved to *test* any of the options.
600
 
601
+ = Is there an easy way to construct the shortcode? =
602
+ 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
603
+ "assist". The widget does not have to be placed within a widget area, it can also be used from the Inactive Widgets area. And
604
+ you do **not** need to Save the widget just to get a shortcode!
605
 
606
+ = How do I get the menu item ids for the 'Items' option? =
607
+ Use the widget's interactive "assist" (see above). Within the representative menu structure, each menu item's id is set in its title
608
  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
609
+ 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
610
+ widget's `Items` settings, and also in the shortcode texts.
611
 
612
+ 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
613
  the URL (the link's href) for `menu-item=NNN` ... the NNN is the menu item id.
614
 
615
+ = How do I get the menu item ids for the 'Exclude Ids' option? =
616
+ 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
617
+ shortcode texts. Otherwise, it's the same principle as outlined above for `Items` ids.
618
 
619
+ = What's the difference between including Branch Siblings (or Branch Ancestors + Siblings), and switching to 'Level' instead of 'Item' in the Secondary Filter section? =
620
+ If you elect to include Branch [Ancestor] Siblings, you will *only* get the siblings, **not** their descendants (assuming they have any).
621
+ 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.
622
 
623
+ For example, let's say that Bravo and Charlie are sibling items immediately below Alpha, and that Bravo is the selected Branch Item,
624
+ with `Starting at` set to "the Branch" (ie. Bravo). If you switch from "Item" to "Level" then both Bravo, Charlie, *and all their descendants*,
625
+ will become eligible for filtering. If you left "Item" enabled, and switched on the inclusion of Branch Siblings, then Bravo and Charlie
626
+ would both still be eligible, but only *Bravo's descendants* would be; not Charlie's!
627
+
628
+ = Where is the styling of the output coming from, and how do I change it? =
629
+ 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
630
+ widget (sidebar, footer, header, ad-hoc, etc?) or the shortcode (page content, post content, widget content, custom field, etc?) and everyone's
631
+ 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,
632
+ and if you wish to modify it you will need to add to your theme's stylesheet.
633
 
634
+ 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
635
+ way to test your changes is by utilising the developer capabilities that are available in most modern browsers (personally, I could not
636
+ do without Firefox and the Firebug extension!) and dynamically applying/modifying styles, possibly utilising the custom classes that the
637
+ widget applies to its output, or the Container options for a user-defined id or class.
638
 
639
+ = How can I find all my posts/pages that have a CMW shortcode so that I can upgrade them? =
640
+ There is a button on the widget's "assist" - `[...]` - that will provide a list of posts/pages whose content, or meta data (custom fields),
641
+ 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 :
642
+ post type, id, whether the shortcode(s) are in content and/or meta data, and the shortcode(s) concerned.
643
+ This utility does not check things like text widgets, plugin-specific tables, theme-provided textareas, etc.
644
 
645
+ There is also an extension to the shortcode - `[cmwizard findme=1]` - that will output the same information, should you not be able to use
646
+ the "assist" (for some unknown reason). You may optionally provide a title attribute; any other attributes are ignored.
647
+ Note that output from this shortcode extension is restricted to users with edit_pages capability.
648
 
649
+
650
+ == Screenshots ==
651
+ 1. Widget (all sections collapsed)
652
+ 2. Filters Section
653
+ 3. Fallbacks Section
654
+ 4. Output Section
655
+ 5. Container Section
656
+ 6. Classes Section
657
+ 7. Links Section
658
+ 8. Widget's "assist"
659
 
660
  == Changelog ==
661
 
662
+ = 3.0.0 =
663
+ * **! 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***.
664
+
665
+ 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.
666
+
667
+ 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.
668
+ * change : **the minmum requirement for WordPress is v3.6**
669
+ * addition : more options for requiring that the "current" menu item be present at some point in the filter process
670
+ * addition : Branch filter levels can be either relative (to the selected Branch item) or absolute (within the menu structure)
671
+ * addition : menu items can now be excluded from the final output, either explicitly by id (optionally including descendants), or by level
672
+ * addition : the ids of Items can be set to include all descendants
673
+ * addition : the inclusion of branch ancestors, and optionally their siblings, can be set by absolute level or relative number of levels
674
+ * addition : the widget title can now be automatically set from the root level item of the Branch item or current menu item
675
+ * 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")
676
+ * 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
677
+ * 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])
678
+ * This release includes an upgrade to v2.1.0 for all existing version 2 widgets/shortcodes - please read the v2.1.0 changes below.
679
+
680
+ = 2.1.0 (incorporated into v3.0.0 release) =
681
+ * change : **the minmum requirement for WordPress is v3.6**
682
+ * bugfix : handle duplicate menu item ids which were causing elements to be ignored
683
+ * bugfix : fix IE8 levels indentation in the "assist"
684
+ * bugfix : the "assist" is now "fixed" position (toggle-able back to absolute), mainly to get around a bug in jQuery UI draggable
685
+ * remove : take out the automatic selection of shortcode text (inconsistent cross-browser, so just triple click as usual; paste-as-text if possible!)
686
+ * addition : in the "assist", provide collapsible options for those larger menus
687
+ * addition : added utility to the "assist" enabling posts containing a CMW shortcode to be located
688
+ * change : in the "assist", swap the menu Items checkboxes for clickable Ticks
689
+ * change : in the "assist", tweak styling and make more responsive to re-sizing
690
+ * change : made compatible with Widget Customizer
691
+ * Note : there is no separate release available for this version!
692
 
693
+ = 2.0.6 =
694
  * change : modified determination of current item to cope better with multiple occurences (still first-found, but within prioritised groups)
 
695
  * change : display of the upgrade notice in the plugins list has been replaced with a simple request to read the changelog before upgrading
696
 
697
  = 2.0.5 =
 
698
  * bugfix : prevent PHP warnings of Undefined index/offset
699
 
700
  = 2.0.4 =
 
701
  * bugfix : clearing the container field failed to remove the container from the output
 
702
  * addition : in the "assist", added automatic selection of the shortcode text when it is clicked
 
703
  * addition : remove WordPress's menu-item-has-children class (introduced in v3.7) when the filtered item no longer has children
 
704
  * change : tweaked styles and javascript in admin for WordPress v3.8
705
 
706
  = 2.0.3 =
 
707
  * bugfix : missing global when enqueuing scripts and styles for admin
708
 
709
  = 2.0.2 =
 
710
  * bugfix : the Include Ancestors option was not automatically including the Parent
 
711
  * bugfix : the "assist" was incorrectly calculating Depth Relative to Current Item when the current menu item was outside the scope of the Filtered items
 
712
  * behaviour change : only recognise the first "current" item found (used to allow subsequent "current" items to override any already encountered)
713
 
714
  = 2.0.1 =
 
715
  * bugfix : an incorrect test for a specific-items filter prevented show-all producing any output
716
 
717
  = 2.0.0 =
 
718
  * **! Possible Breaker !** The calculation of `Start Level` has been made consistent across the `Show all` and `Children of` filters : if you previously had a setup where you were filtering for the children of an item at level 2, with start level set to 4, there would have been no output because the immediate children (at level 3) were outside the start level. Now, there *will* be output, starting with the grand-children (at level 4).
 
719
  * **! Possible Breaker !** There is now deemed to be an artificial "root" item above the level 1 items, which mean that a `Children of` filter set to "Current Parent Item" or "Current Root Item" will no longer fail for a top-level "current menu item". If you have the "no ancestor" fallback set then this change will have no impact (but you may now want to consider turning the fallback off?); if you *don't* currently use the "no ancestor" fallback, then where there was previously no output there will now be some!
 
720
  * added new option : Items, a comma- or space-delimited list of menu item ids, as an alternative Filter
 
721
  * added new option : Depth Relative to Current Item to the Filter section (depth_rel_current=1 in the shortcode)
 
722
  * added new option : Must Contain Current Item to the Output section (contains_current=1 in the shortcode)
 
723
  * changed the widget's "demo" facility to "assist" and brought it into WordPress admin, with full interactivity with the widget
 
724
  * refactored code
725
 
726
  = 1.2.2 =
 
727
  * bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
728
 
729
  = 1.2.1 =
 
730
  * 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)
 
731
  * corrected 'show all from start level 1' processing so that custom classes get applied and 'Title from "Current" Item' works (regardless of filter settings)
 
732
  * changed the defaults for new widgets such that only the Filter section is open by default; all the others are collapsed
 
733
  * in demo.html, added output of the shortcode applicable to the selections made
 
734
  * in demo.html, added a link to the documentation page
 
735
  * corrected 2 of the shortcode examples in the readme.txt, and made emulator (demo) available from the readme
736
 
737
  = 1.2.0 =
 
738
  * added custom_menu_wizard shortcode, to run the widget from within content
 
739
  * moved the 'no ancestor' fallback into new Fallback collapsible section, and added a fallback for Current Item with no children
 
740
  * added an option allowing setting of title from current menu item's title
 
741
  * 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)
 
742
  * don't include menus with no items
 
743
  * updated demo.html
744
 
745
  = 1.1.0 =
 
746
  * added 'Current Root Item' and 'Current Parent Item' to the `Children of` filter
 
747
  * added `Fallback to Current Item` option, with subsidiary options for overriding a couple of Output options, as a means to enable Current Root & Current Parent to match a Current Item at root level
 
748
  * added an Output option to include both the parent item **and** the parent's siblings (for a successful `Children of` filter)
 
749
  * added max-width style (100%) to the `Children of` SELECT in the widget options
 
750
  * added widget version to the admin js enqueuer
 
751
  * ignore/disable Hide Empty option for WP >= v3.6 (wp_nav_menu() now does it automatically)
 
752
  * included a stand-alone helper/demo html page
 
753
  * rebuilt the `Children of` SELECT in the widget options to cope with IE's lack of OPTGROUP/OPTION styling
 
754
  * moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript
755
 
756
  = 1.0.0 =
757
+ * Initial release
 
758
 
759
  == Upgrade Notice ==
760
 
761
+ = 3.0.0 =
762
+ **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!
763
+ 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.
764
+ 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".
765
 
766
+ = 2.0.6 =
767
  Determination of the current menu item has been slightly modified to cope a bit better with occasions where multiple items have been set as "current".
768
  The display of the upgrade notice in the plugins list has been replaced with a simple request to read the changelog before upgrading.
769
 
770
  = 2.0.5 =
 
771
  Fixed a bug to prevent PHP warnings of Undefined index/offset being output.
772
 
773
  = 2.0.4 =
 
774
  Fixed a bug that prevented the container field being removed, and added removal of the menu-item-has-children class when the filtered item no longer has children.
775
  The admin widget styling and javascript have been tweaked to accommodate WordPress 3.8.
776
 
777
  = 2.0.3 =
 
778
  Fixed a minor bug with a missing global when enqueuing script and style for the admin.
779
 
780
  = 2.0.2 =
 
781
  Fixed a bug with the Include Ancestors option, where it was not automatically including the Parent.
782
  Fixed a bug in the "assist", where it was incorrectly calculating Depth Relative to Current Item when the current menu item was outside the scope of the Filtered items.
783
  Changed determination of the "current" item such that only the first one encountered is recognised, rather than allowing subsequent "current" items to override previous ones.
784
 
785
  = 2.0.1 =
 
786
  Fixed a bug whereby a test for a specific-items filter prevented show-all from producing any output.
787
 
788
  = 2.0.0 =
789
+ **! 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 :
 
790
  + if you have set a `Children of` filter, **and** you have changed the `Start Level` to a level greater than 2, or
791
  + if you have set the `Children of` filter to Current Parent/Root Item, and you have **not** set the "no ancestor" fallback.
792
  *__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.__*
798
  Rebuilt the "demo" facility as an "assist" wizard for the widget It is now fully interactive with the widget instance, and generates the entire shortcode according to the widget instance settings.
799
 
800
  = 1.2.2 =
 
801
  Bugfix : The fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly
802
 
803
  = 1.2.1 =
 
804
  Added a few extra custom classes, and changed the defaults for new widgets such that only the Filter section is open by default.
805
  Fixed Show All processing so that custom classes always get applied, and 'Title from "Current" Item' works regardless of filter settings.
806
  Fixed a couple of the shortcode examples in the readme.txt, and added display of the applicable shortcode settings to the demo.html.
807
 
808
  = 1.2.0 =
 
809
  Added custom_menu_wizard shortcode, to run the widget from within content.
810
  Added a new fallback for Current Item having no children, and moved all fallbacks into a collapsible Fallbacks section.
811
  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).
v210-readme.html ADDED
@@ -0,0 +1,626 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Custom Menu Wizard WP Plugin, v2.1.0 Readme</title>
5
+ <style type="text/css">
6
+ body {font-family: "Lucida Grande", Verdana, sans-serif;}
7
+ code {font-size: 1.3em;}
8
+ div.success {background: #0f0; width: 50%; margin: 0 auto; padding: 1px 10px; border: 3px solid #0d0;}
9
+ div.error {padding: 1px 10px; margin: 30px auto;}
10
+ div.error p {font-weight: bold;}
11
+ div.error ul {list-style: square;}
12
+ div.fatal {background: #faa; border: 3px solid #d00;}
13
+ div.warning {background: #f60; border: 3px solid #e40;}
14
+ div.note {background: #5cf; border: 3px solid #3ad;}
15
+ </style>
16
+ </head>
17
+ <body>
18
+ <hr />
19
+
20
+ <h1>Custom Menu Wizard Widget</h1>
21
+
22
+ <p><em>Show branches or levels of your menu in a widget, or in content using a shortcode, with full customisation.</em></p>
23
+
24
+ <hr />
25
+
26
+ <p>
27
+ <strong>Contributors:</strong> wizzud<br />
28
+ <strong>Donate link:</strong> <br />
29
+ <strong>Tags:</strong> menu, widget, widgets, navigation, nav, custom menus, custom menu, partial menu, menu level, menu branch<br />
30
+ <strong>Requires at least:</strong> 3.6<br />
31
+ <strong>Tested up to:</strong> 3.9<br />
32
+ <strong>Stable tag:</strong> 2.1.0<br/>
33
+ <strong>License:</strong> GPLv2 or Later </p>
34
+
35
+ <hr />
36
+
37
+ <h3>Description</h3>
38
+ <p>This plugin is a boosted version of the WordPress "Custom Menu" widget.
39
+ 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">wp_nav_menu()</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 is now (v1.2.0) a shortcode that enables you to include the widget's output in your content.</p>
40
+
41
+ <p>Features include:</p>
42
+
43
+ <ul>
44
+ <li>Display the entire menu, just a branch of it, or even just a specific level of it</li>
45
+ <li>Select a branch based on a specific menu item, or the "current" item (currently displayed page)</li>
46
+ <li>Specify a level to start at, and the number of levels to output</li>
47
+ <li>Select hierarchicial or flat output, both options still abiding by the specified number of levels to output</li>
48
+ <li>Allow the widget title to be entered but not output, or even to be set from the parent item of a menu's sub-branch</li>
49
+ <li>Optionally disable widget output completely if there are no matching menu items found (WordPress &lt; v3.6)</li>
50
+ <li>Include a sub-branch's parent and/or ancestor item(s) in the output, over and above any start level setting</li>
51
+ <li>Automatically add cmw-level-N and cmw-has-submenu classes to output menu items</li>
52
+ <li>Add/specify custom class(es) for the widget block, the menu container, and the menu itself</li>
53
+ <li>Modify the link's output with additional HTML around the link's text and/or the link element itself</li>
54
+ <li>Use Ordered Lists (OL) for the top and/or sub levels instead of Unordered Lists (UL)</li>
55
+ <li>Select a branch based on the ultimate ancestor (root level) of the "current" item</li>
56
+ <li>Shortcode, [custom_menu_wizard], available to run the widget from within content</li>
57
+ <li>Make the output conditional upon the "current" item appearing in the selected/included items</li>
58
+ <li>Specify specific menu items</li>
59
+ <li>Use the widget's interactive "assist" to help with the widget settings or shortcode definition </li>
60
+ </ul>
61
+
62
+ <p>Documentation for the <a href='#Widget-Options'>Widget Options</a>, and the associated
63
+ <a href='#Shortcode-Parameters'>Shortcode Parameters</a>, can be found under Other Notes.</p> <hr />
64
+ <h3>Installation</h3>
65
+ <ol>
66
+ <li><p>EITHER Upload the zip package via 'Plugins &gt; Add New &gt; Upload' in your WP Admin</p>
67
+
68
+ <p>OR Extract the zip package and upload <code>custom-menu-wizard</code> folder to the <code>/wp-content/plugins/</code> directory</p></li>
69
+ <li><p>Activate the plugin through the 'Plugins' menu in your WP Admin</p></li>
70
+ </ol>
71
+
72
+ <p>The widget will now be available in the 'Widgets' admin page.
73
+ As long as you already have at least one Menu defined, you can add the new widget to a sidebar and configure it however you want.
74
+ Alternatively, you can use the shortcode in your content.</p> <hr />
75
+ <h3>Frequently Asked Questions</h3>
76
+ <p>If you have a question or problem that is not covered here, please use the integrated Support forum.</p>
77
+
78
+ <h4>Are there any known problems?</h4>
79
+
80
+ <p>Yep, 'fraid so :</p>
81
+
82
+ <ol>
83
+ <li>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 plugin that adds and/or manipulates menu items, could potentially cause problems for the widget.</li>
84
+ </ol>
85
+
86
+ <h4>Why isn't it working? Why is there no output?</h4>
87
+
88
+ <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
89
+ the option settings on the widget/shortcode. The quickest way to resolve any such issues is to use the widget's interactive "assist", and
90
+ 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
91
+ infallible, and if you still have problems then please let me have as much information as possible and I will endeavour to help. (Please
92
+ note that simply reporting "It doesn't work" is not the most useful of feedbacks, and is unlikely to get a response other than, possibly,
93
+ a request for more details).</p>
94
+
95
+ <h4>How do I use the "assist"?</h4>
96
+
97
+ <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
98
+ option settings - including the menu selected - to build a pictorial representation of the menu and show you, in blue, which menu items will
99
+ 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
100
+ some of the more advanced HTML-modifying options such as can be found under the Container, Classes or Links sections.
101
+ 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
102
+ item to change). The "current menu item" is shaded red, with its parent shaded orange and ancestors shaded yellow. All changes in the
103
+ "current menu item" and the widget options are immediately reflected by the "assist" (text fields in the widget options simply need to lose
104
+ focus).</p>
105
+
106
+ <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.
107
+ Alternatively, simply copy-paste the shortcode code produced by the "assist" straight into your post (you do not need to Save the widget!).
108
+ The widget does not have to Saved to <em>test</em> any of the options.</p>
109
+
110
+ <h4>Is there an easy way to construct the shortcode to get the results that I want?</h4>
111
+
112
+ <p>Yes. Use the widget's interactive "assist" capability (see above). Note that you do not need to have the widget in a sidebar : the
113
+ "assist" also works off a widget that is in the Inactive Widgets area of the widget admin page.</p>
114
+
115
+ <h4>How do I get the menu item ids for the `Items` option?</h4>
116
+
117
+ <p>Use the widget's interactive "assist" (see above). Within the representation menu structure, each menu item's id is set in its title
118
+ 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
119
+ then show a green tick "checkbox" beside each menu item and you simply [un]check the items as required. Each selection will be reflected back into the
120
+ widget's <code>Items</code> settings, and also in the shortcode code.</p>
121
+
122
+ <p>Alternatively, go to Appearance, Menus and select the relevant menu; hover over the edit, Remove, or Cancel link for an item and look in
123
+ the URL (the link's href) for <code>menu-item=NNN</code> ... the NNN is the menu item id.</p>
124
+
125
+ <h4>How can I find all my posts/pages that have a CMW shortcode so that I can upgrade them?</h4>
126
+
127
+ <p>There is a button on the widget's "assist" - <code>[...]</code> - that will provide a list of posts that contain any CMW shortcode.
128
+ Each entry is a link that opens the item in a new tab/window. The link's title gives a bit more information : post type, id, and
129
+ shortcode(s) concerned. This utility *only* looks at post content; it does not check custom fields, widgets, etc.</p> <hr />
130
+
131
+ <h3>Changelog</h3>
132
+ <h4>2.1.0</h4>
133
+
134
+ <ul>
135
+ <li><p>change : <strong>the minmum requirement for WordPress is v3.6</strong></p></li>
136
+ <li><p>bugfix : handle duplicate menu item ids which were causing elements to be ignored</p></li>
137
+ <li><p>bugfix : fix IE8 levels indentation in the "assist"</p></li>
138
+ <li><p>bugfix : the "assist" is now "fixed" position (toggle-able back to absolute), mainly to get around a bug in jQuery UI draggable</p></li>
139
+ <li><p>remove : take out the automatic selection of shortcode text (inconsistent cross-browser, so just triple click as usual; paste-as-text if possible!)</p></li>
140
+ <li><p>addition : in the "assist", provide collapsible options for those larger menus</p></li>
141
+ <li><p>addition : added utility to the "assist" enabling posts containing a CMW shortcode to be located</p></li>
142
+ <li><p>change : in the "assist", swap the menu Items checkboxes for clickable Ticks</p></li>
143
+ <li><p>change : in the "assist", tweak styling and make more responsive to re-sizing</p></li>
144
+ <li><p>change : made compatible with Widget Customizer</p></li>
145
+ <li><p>change : display of the upgrade notice in the plugins list has been replaced with a simple request to read the changelog before upgrading</p></li>
146
+ <li><p>Note : there is no separate release available for this version!</p></li>
147
+ </ul>
148
+
149
+ <h4>2.0.6</h4>
150
+
151
+ <ul>
152
+ <li><p>change : modified determination of current item to cope better with multiple occurences (still first-found, but within prioritised groups)</p></li>
153
+ <li><p>change : display of the upgrade notice in the plugins list has been replaced with a simple request to read the changelog before upgrading</p></li>
154
+ </ul>
155
+
156
+ <h4>2.0.5</h4>
157
+
158
+ <ul>
159
+ <li>bugfix : prevent PHP warnings of Undefined index/offset</li>
160
+ </ul>
161
+
162
+ <h4>2.0.4</h4>
163
+
164
+ <ul>
165
+ <li><p>bugfix : clearing the container field failed to remove the container from the output</p></li>
166
+ <li><p>addition : in the "assist", added automatic selection of the shortcode text when it is clicked</p></li>
167
+ <li><p>addition : remove WordPress's menu-item-has-children class (introduced in v3.7) when the filtered item no longer has children</p></li>
168
+ <li><p>change : tweaked styles and javascript in admin for WordPress v3.8</p></li>
169
+ </ul>
170
+
171
+ <h4>2.0.3</h4>
172
+
173
+ <ul>
174
+ <li>bugfix : missing global when enqueuing scripts and styles for admin</li>
175
+ </ul>
176
+
177
+ <h4>2.0.2</h4>
178
+
179
+ <ul>
180
+ <li><p>bugfix : the Include Ancestors option was not automatically including the Parent</p></li>
181
+ <li><p>bugfix : the "assist" was incorrectly calculating Depth Relative to Current Item when the current menu item was outside the scope of the Filtered items</p></li>
182
+ <li><p>behaviour change : only recognise the first "current" item found (used to allow subsequent "current" items to override any already encountered)</p></li>
183
+ </ul>
184
+
185
+ <h4>2.0.1</h4>
186
+
187
+ <ul>
188
+ <li>bugfix : an incorrect test for a specific-items filter prevented show-all producing any output</li>
189
+ </ul>
190
+
191
+ <h4>2.0.0</h4>
192
+
193
+ <ul>
194
+ <li><p><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).</p></li>
195
+ <li><p><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!</p></li>
196
+ <li><p>added new option : Items, a comma- or space-delimited list of menu item ids, as an alternative Filter</p></li>
197
+ <li><p>added new option : Depth Relative to Current Item to the Filter section (depth_rel_current=1 in the shortcode)</p></li>
198
+ <li><p>added new option : Must Contain Current Item to the Output section (contains_current=1 in the shortcode)</p></li>
199
+ <li><p>changed the widget's "demo" facility to "assist" and brought it into WordPress admin, with full interactivity with the widget</p></li>
200
+ <li><p>refactored code</p></li>
201
+ </ul>
202
+
203
+ <h4>1.2.2</h4>
204
+
205
+ <ul>
206
+ <li>bugfix : fallback for Current Item with no children was failing because the parent's children weren't being picked out correctly</li>
207
+ </ul>
208
+
209
+ <h4>1.2.1</h4>
210
+
211
+ <ul>
212
+ <li><p>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)</p></li>
213
+ <li><p>corrected 'show all from start level 1' processing so that custom classes get applied and 'Title from "Current" Item' works (regardless of filter settings)</p></li>
214
+ <li><p>changed the defaults for new widgets such that only the Filter section is open by default; all the others are collapsed</p></li>
215
+ <li><p>in demo.html, added output of the shortcode applicable to the selections made</p></li>
216
+ <li><p>in demo.html, added a link to the documentation page</p></li>
217
+ <li><p>corrected 2 of the shortcode examples in the readme.txt, and made emulator (demo) available from the readme</p></li>
218
+ </ul>
219
+
220
+ <h4>1.2.0</h4>
221
+
222
+ <ul>
223
+ <li><p>added custom_menu_wizard shortcode, to run the widget from within content</p></li>
224
+ <li><p>moved the 'no ancestor' fallback into new Fallback collapsible section, and added a fallback for Current Item with no children</p></li>
225
+ <li><p>added an option allowing setting of title from current menu item's title</p></li>
226
+ <li><p>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)</p></li>
227
+ <li><p>don't include menus with no items</p></li>
228
+ <li><p>updated demo.html</p></li>
229
+ </ul>
230
+
231
+ <h4>1.1.0</h4>
232
+
233
+ <ul>
234
+ <li><p>added 'Current Root Item' and 'Current Parent Item' to the <code>Children of</code> filter</p></li>
235
+ <li><p>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</p></li>
236
+ <li><p>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)</p></li>
237
+ <li><p>added max-width style (100%) to the <code>Children of</code> SELECT in the widget options</p></li>
238
+ <li><p>added widget version to the admin js enqueuer</p></li>
239
+ <li><p>ignore/disable Hide Empty option for WP &gt;= v3.6 (wp_nav_menu() now does it automatically)</p></li>
240
+ <li><p>included a stand-alone helper/demo html page</p></li>
241
+ <li><p>rebuilt the <code>Children of</code> SELECT in the widget options to cope with IE's lack of OPTGROUP/OPTION styling</p></li>
242
+ <li><p>moved the setting of 'disabled' attributes on INPUTs/SELECTs from PHP into javascript</p></li>
243
+ </ul>
244
+
245
+ <h4>1.0.0</h4>
246
+
247
+ <p>Initial release</p> <hr />
248
+
249
+ <h3>Upgrade Notice</h3>
250
+ <dl>
251
+ <dt>2.1.0 (incorporated into v3.0.0 release)</dt>
252
+ <dd>Minimum requirement for WordPress is now v3.6! Added the ability to handle duplicate menu ids, and made the widget compatible with Widget Customizer. Tweaked some styling in the &quot;assist&quot;.
253
+ Please note that there is no separate release package for v2.1.0!</dd>
254
+ <dt>2.0.5</dt>
255
+ <dd>Fixed a bug to prevent PHP warnings of Undefined index/offset being output.</dd>
256
+ <dt>2.0.4</dt>
257
+ <dd>Fixed a bug that prevented the container field being removed, and added removal of the menu-item-has-children class when the filtered item no longer has children.
258
+ The admin widget styling and javascript have been tweaked to accommodate WordPress 3.8.</dd>
259
+ <dt>2.0.3</dt>
260
+ <dd>Fixed a minor bug with a missing global when enqueuing script and style for the admin.</dd>
261
+ <dt>2.0.2</dt>
262
+ <dd>Fixed a bug with the Include Ancestors option, where it was not automatically including the Parent.
263
+ Fixed a bug in the &quot;assist&quot;, where it was incorrectly calculating Depth Relative to Current Item when the current menu item was outside the scope of the Filtered items.
264
+ Changed determination</dd>
265
+ <dt>2.0.1</dt>
266
+ <dd>Fixed a bug whereby a test for a specific-items filter prevented show-all from producing any output.</dd>
267
+ <dt>2.0.0</dt>
268
+ <dd>! 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 :
269
+ + if you have set a Children of filter, and you have changed the Start Level to a level greater than 2, or
270
+ + if you have set the </dd>
271
+ <dt>1.2.2</dt>
272
+ <dd>Bugfix : The fallback for Current Item with no children was failing because the parent&#039;s children weren&#039;t being picked out correctly</dd>
273
+ <dt>1.2.1</dt>
274
+ <dd>Added a few extra custom classes, and changed the defaults for new widgets such that only the Filter section is open by default.
275
+ Fixed Show All processing so that custom classes always get applied, and &#039;Title from &quot;Current&quot; Item&#039; works regardless of filter settings.
276
+ Fixed a cou</dd>
277
+ <dt>1.2.0</dt>
278
+ <dd>Added custom_menu_wizard shortcode, to run the widget from within content.
279
+ Added a new fallback for Current Item having no children, and moved all fallbacks into a collapsible Fallbacks section.
280
+ Fixed a bug with optgroups/options made available for the &#039;Children of&#039; selector after the widg</dd>
281
+ </dl>
282
+ <hr />
283
+
284
+ <h3 id='Widget-Options'>Widget Options</h3>
285
+ <p>There are quite a few options, which makes the widget settings box very long. I have therefore grouped most of the options into
286
+ logical sections and made each section collapsible (with remembered state once saved). As of v1.2.1, only the Filter section is
287
+ open by default; all sections below that start off collapsed.</p>
288
+
289
+ <ul>
290
+ <li><p><strong>Title</strong></p>
291
+
292
+ <p>Set the title for your widget.</p></li>
293
+ <li><p><strong>Hide</strong> <em>(checkbox)</em></p>
294
+
295
+ <p>Prevents the entered <code>Title</code> being displayed in the front-end widget output.</p>
296
+
297
+ <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 collapsed, but I
298
+ 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>
299
+ <li><p><strong>Select Menu</strong> <em>(select)</em></p>
300
+
301
+ <p>Choose the appropriate menu from the dropdown list of Custom Menus currently defined in your WordPress application. The
302
+ first one available (alphabetically) is already selected for you by default.</p></li>
303
+ <li><p><strong>Filter</strong></p>
304
+
305
+ <ul>
306
+ <li><p><strong>Show all</strong> <em>(radio, default)</em></p>
307
+
308
+ <p>Don't apply the <code>Children of</code> filter. If you check this option, the <code>Children of</code> select will be disabled, as will the <code>Include Parent</code>,
309
+ <code>Include Ancestors</code> and <code>Title from Parent Item</code> checkboxes, as they are only applicable to a <code>Children of</code> filter.</p></li>
310
+ <li><p><strong>Children of</strong> <em>(radio &amp; select)</em></p>
311
+
312
+ <p>The dropdown list will present a "Current Item" option (default), a "Current Root Item" (v1.1.0)
313
+ and "Current Parent Item" (v1.1.0) option, followed by all the available items from the menu chosen in <code>Select Menu</code>.
314
+ The widget will output the <em>children</em> of the selected item, always assuming that they lie within the bounds of any other parameters set.</p>
315
+
316
+ <ul>
317
+ <li><p>"Current Item" is the menu item that WordPress recognises as being currently on display (current menu item)</p></li>
318
+ <li><p>"Current Parent Item" (v1.1.0) is the <em>immediate</em> ancestor (within <code>Select Menu</code>) of the current menu item</p></li>
319
+ <li><p>"Current Root Item" (v1.1.0) is the <em>ultimate</em> ancestor (within <code>Select Menu</code>) of the current menu item.</p></li>
320
+ </ul>
321
+
322
+ <p>Obviously, if the current menu item (as determined by WordPress) does not
323
+ appear in the <code>Select Menu</code> then there is going to be no output for any of these options.</p>
324
+
325
+ <p>If you change <code>Select Menu</code>, the options presented in this dropdown will change accordingly and the selected option will revert to the default.</p></li>
326
+ <li><p><strong>Items</strong> (radio &amp; text input)* as of v2.0.0</p>
327
+
328
+ <p>Takes a comma- or space-delimited list of menu item ids, specifiying the specific menu items that are required. This is intended
329
+ for situations that it is not possible to handle using other option settings. The simplest way to determine the menu item ids is
330
+ to use the "assist" facility.</p>
331
+
332
+ <p>Note that the <code>Starting Level</code> and <code>Depth</code> options (including <code>Relative to "Current" Item</code>) have no effect if <code>Items</code> is set.</p></li>
333
+ <li><p><strong>Starting Level</strong> <em>(select, default: "1")</em></p>
334
+
335
+ <p>This is the level within the chosen menu (from <code>Select Menu</code>) that the widget will start looking for items to keep. Obviously, level 1
336
+ is the root level (ie. those items that have no parent item); level 2 is all the immediate children of the root level items, and so on.
337
+ Note that for a <code>Children of</code> filter there is no difference between level 1 and level 2 (because there are no children at level 1).
338
+ Also note that this option does not apply if <code>Items</code> is set.</p></li>
339
+ <li><p><strong>For Depth</strong> <em>(select, default: "unlimited")</em></p>
340
+
341
+ <p>This is the maximum depth of the eventual output structure after filtering, and in the case of <code>Flat</code> output being requested it is
342
+ still applied - as if the output were <code>Hierarchical</code> and then flattened at the very last moment.</p>
343
+
344
+ <p>You need to be aware that, by default, the
345
+ <code>For Depth</code> setting is applied relative to the level at which the first item to be kept is found. For example, say you were to set
346
+ <code>Children of</code> to "Current Item", <code>Starting Level</code> to "2", and <code>For Depth</code> to "2 levels" : if the current item was found at level 3,
347
+ then you would get the current item's immediate children (from level 4), plus <em>their</em> immediate children (from level 5).
348
+ Note that this option does not apply if <code>Items</code> is set.</p></li>
349
+ <li><p><strong>Relative to "Current" Item</strong> <em>(checkbox)</em> as of v2.0.0</p>
350
+
351
+ <p>This changes the <code>For Depth</code> option such that depth is applied relative to the current menu item, instead of relative to the
352
+ first item found that is to be kept. It only has any effect when <code>For Depth</code> is set to something other than "unlimited", and when
353
+ the current menu item is within the filtered items (before taking <code>For Depth</code> into account).</p></li>
354
+ </ul></li>
355
+ <li><p><strong>Fallbacks</strong></p>
356
+
357
+ <p>Fallback for <code>Children of</code> being set to either <em>Current Root Item</em> or <em>Current Parent Item</em>, and current item not having an ancestor:</p>
358
+
359
+ <ul>
360
+ <li><p><strong>Switch to Current Item</strong> <em>(checkbox)</em> as of v1.1.0</p>
361
+
362
+ <p>If enabled,
363
+ it provides a fallback of effectively switching the filter to "Current Item" if the current menu item
364
+ (as determined by WordPress) is at Level 1
365
+ (top level) of the selected menu. For example, say you were to set <code>Children of</code> to "Current Parent Item", with <code>Starting Level</code>
366
+ at "1" and <code>For Depth</code> at "unlimited" : if the current menu item was found at level 1 (root level of the menu) then ordinarily
367
+ there would be no output because the current item has no parent! If you were to enable the <code>Switch to Current Item</code> fallback then you
368
+ <em>would</em> have some output - the entire branch below the current item.</p>
369
+
370
+ <ul>
371
+ <li><p><strong>Include Parent...</strong> <em>(checkbox)</em> as of v1.1.0</p>
372
+
373
+ <p>This option extends the <code>Switch to Current Item</code> option (above). If the enabled fallback is actually used, this option can
374
+ temporarily override the equivalent <strong>Output</strong> option to ON. Note that if the <strong>Output</strong> options are already set to include
375
+ the parent item (with or without siblings), this option has absolutely no effect.</p></li>
376
+ <li><p><strong>&amp; its Siblings</strong> <em>(checkbox)</em> as of v1.1.0</p>
377
+
378
+ <p>This option extends the <code>Switch to Current Item</code> option (above). If the enabled fallback is actually used, this option can
379
+ temporarily override the equivalent <strong>Output</strong> option to ON. Note that if the equivalent <strong>Output</strong> option is already enabled,
380
+ this option has absolutely no effect.</p></li>
381
+ </ul></li>
382
+ </ul>
383
+
384
+ <p>Fallback for <code>Children of</code> being set to <em>Current Item</em>, and current item not having any children:</p>
385
+
386
+ <ul>
387
+ <li><p><strong>Switch to Current Parent Item</strong> <em>(checkbox)</em> as of v1.2.0</p>
388
+
389
+ <p>If enabled, it provides a fallback of effectively switching the filter to "Current Parent Item" if looking for children
390
+ of Current Item and there aren't any. For example, say you were to set <code>Children of</code> to "Current Item", with <code>Starting Level</code>
391
+ at "1" and <code>For Depth</code> at "unlimited" : if the current menu item has no children then ordinarily
392
+ there would be no output! If you were to enable the <code>Switch to Current Item</code> fallback then you
393
+ <em>would</em> have some output - the current item and its siblings.</p>
394
+
395
+ <p>Please note that there is one difference between this fallback and the normal "Current Parent Item" filter : if the current item
396
+ has no ancestor (as well as no children) then you will always get the current item and its siblings, regardless of any other settings!</p>
397
+
398
+ <ul>
399
+ <li><p><strong>Include Parent...</strong> <em>(checkbox)</em> as of v1.2.0</p>
400
+
401
+ <p>This option extends the <code>Switch to Current Parent Item</code> option (above). If the enabled fallback is actually used, this option can
402
+ temporarily override the equivalent <strong>Output</strong> option to ON. Note that if the <strong>Output</strong> options are already set to include
403
+ the parent item (with or without siblings), this option has absolutely no effect.</p></li>
404
+ <li><p><strong>&amp; its Siblings</strong> <em>(checkbox)</em> as of v1.2.0</p>
405
+
406
+ <p>This option extends the <code>Switch to Current Parent Item</code> option (above). If the enabled fallback is actually used, this option can
407
+ temporarily override the equivalent <strong>Output</strong> option to ON. Note that if the equivalent <strong>Output</strong> option is already enabled,
408
+ this option has absolutely no effect.</p></li>
409
+ </ul></li>
410
+ </ul></li>
411
+ <li><p><strong>Output</strong></p>
412
+
413
+ <ul>
414
+ <li><p><strong>Hierarchical</strong> <em>(radio, default)</em></p>
415
+
416
+ <p>Output in the standard nested list format.</p></li>
417
+ <li><p><strong>Flat</strong> <em>(radio)</em></p>
418
+
419
+ <p>Output in a single list format, ignoring any parent-child relationship other than to maintain the same physical order as would be
420
+ presented in a <code>Hierarchical</code> output.</p></li>
421
+ <li><p><strong>Must Contain "Current" Item</strong> <em>(checkbox)</em> as of v2.0.0</p>
422
+
423
+ <p>If checked, the widget will not list any menu items unless the current menu item appears somewhere in the list.</p></li>
424
+ <li><p><strong>Include Parent...</strong> <em>(checkbox)</em></p>
425
+
426
+ <p>If checked, include the parent item in the output. Only applies to a successful <code>Children of</code> filter.</p></li>
427
+ <li><p><strong>&amp; its Siblings</strong> <em>(checkbox)</em> as of v1.1.0</p>
428
+
429
+ <p>If checked, include the parent item <strong>and</strong> its siblings in the output. Only applies to a successful <code>Children of</code> filter.</p></li>
430
+ <li><p><strong>Include Ancestors</strong> <em>(checkbox)</em></p>
431
+
432
+ <p>Same as <code>Include Parent</code> except that all ancestors, right back to root level, are included. Only applies to a successful
433
+ <code>Children of</code> filter.</p></li>
434
+ <li><p><strong>Title from Parent</strong> <em>(checkbox)</em></p>
435
+
436
+ <p>Again, this only applies to a successful <code>Children of</code> filter. If checked, use the title of the parent item as the widget's
437
+ title when displaying the output. This will override (ie. ignore) the <code>Hide</code> checkbox setting!</p>
438
+
439
+ <p>Please note that this is <strong>not</strong> the same as asking for the title from "the parent of the current menu item"!</p></li>
440
+ <li><p><strong>Title from "Current" Item</strong> <em>(checkbox)</em></p>
441
+
442
+ <p>If checked, use the title of the current menu item (as determined by WordPress) as the widget's
443
+ title when displaying the output. This will override (ie. ignore) the <code>Hide</code> checkbox setting!</p>
444
+
445
+ <p>Note that the current menu item is not required to be within the resultant output, merely within the <code>Select Menu</code>.
446
+ Also, <code>Title from Parent</code> (if applicable, and if available) takes priority over this option.</p></li>
447
+ <li><p><strong>Change UL to OL</strong></p>
448
+
449
+ <p>The standard for menus is to use UL (unordered list) elements to display the output. These settings give you the option to swap
450
+ the ULs out for OLs (ordered lists).</p>
451
+
452
+ <ul>
453
+ <li><p><strong>Top Level</strong> <em>(checkbox)</em></p>
454
+
455
+ <p>If checked, swap the outermost UL for an OL.</p></li>
456
+ <li><p><strong>Sub-Levels</strong> <em>(checkbox)</em></p>
457
+
458
+ <p>If checked, swap any nested (ie. not the outermost) ULs for an OLs.</p></li>
459
+ </ul></li>
460
+ <li><p><strong>Hide Widget if Empty</strong> <em>(checkbox)</em></p>
461
+
462
+ <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>
463
+
464
+ <p>Please note that as of WordPress v3.6, this option becomes superfluous and will <strong>not</strong> be presented (the wp_nav_menu() function
465
+ has been modified to automatically suppress all HTML output if there are no items to be displayed). The widget will retain
466
+ the setting used on earlier WP versions (in case reversion from WP v3.6 might be required) but <em>will not present the option
467
+ for WP v3.6+</em>.</p></li>
468
+ </ul></li>
469
+ <li><p><strong>Container</strong></p>
470
+
471
+ <ul>
472
+ <li><p><strong>Element</strong> <em>(default: "div")</em></p>
473
+
474
+ <p>This menu list is usually wrapped in a "container" element, and this is the tag for that element. You may change it for another
475
+ tag, or you may clear it out and the container will be completely removed. Please note that WordPress is set by default to only
476
+ accept "div" or "nav", but that could be changed or extended by any template or plugin.</p></li>
477
+ <li><p><strong>Unique ID</strong></p>
478
+
479
+ <p>This allows you to specify your own id (which should be unique) for the container.</p></li>
480
+ <li><p><strong>Class</strong></p>
481
+
482
+ <p>This allows you to add your own class to the container element.</p></li>
483
+ </ul></li>
484
+ <li><p><strong>Classes</strong></p>
485
+
486
+ <ul>
487
+ <li><p><strong>Menu Class</strong> <em>(default: "menu-widget")</em></p>
488
+
489
+ <p>This is the class that will be applied to the list element that holds the entire menu.</p></li>
490
+ <li><p><strong>Widget Class</strong></p>
491
+
492
+ <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>
493
+ </ul></li>
494
+ <li><p><strong>Links</strong></p>
495
+
496
+ <ul>
497
+ <li><p><strong>Before the Link</strong></p>
498
+
499
+ <p>Text or HTML that will be placed immediately before each menu item's link.</p></li>
500
+ <li><p><strong>After the Link</strong></p>
501
+
502
+ <p>Text or HTML that will be placed immediately after each menu item's link.</p></li>
503
+ <li><p><strong>Before the Link Text</strong></p>
504
+
505
+ <p>Text or HTML that will be placed immediately before each menu item's link text.</p></li>
506
+ <li><p><strong>After the Link Text</strong></p>
507
+
508
+ <p>Text or HTML that will be placed immediately after each menu item's link text.</p></li>
509
+ </ul></li>
510
+ </ul>
511
+ <hr />
512
+
513
+ <h3 id='Shortcode-Parameters'>Shortcode Parameters</h3>
514
+ <p>The shortcode is <strong><code>[custom_menu_wizard]</code></strong>. Most of the attributes reflect the options available to the widget, but some have been simplified for easier use in the shortcode format.
515
+ Please note that the <code>Hide Widget if Empty</code> option is not available to the shortcode : it is set to enabled, and if there are no menu items found then there will be no output from the shortcode.</p>
516
+
517
+ <p>The simplest way to build a shortcode is to use the widget's "assist" facility (new in v2.0.0). The facilty is available even when the widget is in
518
+ the Inactive Widgets area, so you don't have to add an unwanted instance of the widget to a sidebar.</p>
519
+
520
+ <ul>
521
+ <li><p><strong>title</strong> <em>(string)</em></p>
522
+
523
+ <p>The output's <code>Title</code>, which may be overridden by <code>title_from</code>. Note that there is no shortcode equivalent of the widget's <code>Hide</code> option for the title.</p></li>
524
+ <li><p><strong>menu</strong> <em>(string | integer)</em></p>
525
+
526
+ <p>Accepts a menu name (most likely usage) or id. If not provided, the shortcode will attempt to find the first menu (ordered by name)
527
+ that has menu items attached to it, and use that.</p></li>
528
+ <li><p><strong>children_of</strong> <em>(string | integer)</em></p>
529
+
530
+ <p>If not empty then it specifies a <code>Children of</code> filter. If neither <code>children_of</code> nor <code>items</code> are supplied (or are empty) then the
531
+ filter defaults back to <code>Show all</code> (see above). Note that <code>items</code>, if supplied, will take precedence over <code>children_of</code>.</p>
532
+
533
+ <ul>
534
+ <li><p>If numeric, it is taken as being the id of a menu item. The widget will look for the <code>Children of</code> that menu item (within <code>menu</code>).
535
+ (Hint : In Menus Admin, hover over the item's <strong>Remove</strong> link and note the number after <em>menu-item=</em> in the URL)</p></li>
536
+ <li><p>Certain specific strings have the following meanings:</p>
537
+
538
+ <ul>
539
+ <li><p><em>'current'</em> or <em>'current-item'</em> : a <code>Children of</code> "Current Item" filter</p></li>
540
+ <li><p><em>'parent'</em> or <em>'current-parent'</em> : a <code>Children of</code> "Current Parent Item" filter</p></li>
541
+ <li><p><em>'root'</em> or <em>'current-ancestor'</em> : a <code>Children of</code> "Current Root Item" filter</p></li>
542
+ </ul></li>
543
+ <li><p>If any other string, it is taken to be the title of a menu item. The widget will look for the <code>Children of</code> that menu item
544
+ (within <code>menu</code>). Please note that the code looks for a <em>caseless</em> title match, so specifying <code>children_of="my menu item"</code> will
545
+ match against a menu item with the title "My Menu Item". Also note that the first match found (hierarchically) is the one that
546
+ gets used (it is quite possible to have same-named items within a menu structure).</p></li>
547
+ </ul></li>
548
+ <li><p><strong>items</strong> <em>(string)</em> See widget's <code>Items</code> option, under <strong>Filter</strong> above.</p></li>
549
+ <li><p><strong>fallback_parent</strong> <em>(string | integer)</em></p>
550
+
551
+ <p>This is the fallback option for when <code>Children of</code> is set to either <em>Current Root Item</em> or <em>Current Parent Item</em>, and
552
+ the current item has no ancestors (see <code>Switch to Current Item</code> under <strong>Fallbacks</strong> above).</p>
553
+
554
+ <ul>
555
+ <li><p>Any "truthy" value (eg. 1, <em>'true'</em>, <em>'on'</em>, <em>'parent'</em>, <em>'siblings'</em>) : Enables widget's <code>Switch to Current Item</code> <strong>Fallbacks</strong> option</p></li>
556
+ <li><p><em>'parent'</em> : Enables widget's <code>Include Parent...</code> <strong>Fallbacks</strong> extension option (in addition to the above)</p></li>
557
+ <li><p><em>'siblings'</em> : Enables widget's <code>&amp; its Siblings</code> <strong>Fallbacks</strong> extension option (in addition to the above)</p></li>
558
+ </ul></li>
559
+ <li><p><strong>fallback_current</strong> <em>(string | integer)</em></p>
560
+
561
+ <p>This is the fallback option for when <code>Children of</code> is set to <em>Current Item</em>, and
562
+ the current item has no children (see <code>Switch to Current Parent Item</code> under <strong>Fallbacks</strong> above).</p>
563
+
564
+ <ul>
565
+ <li><p>Any "truthy" value (eg. 1, <em>'true'</em>, <em>'on'</em>, <em>'parent'</em>, <em>'siblings'</em>) : Enables widget's <code>Switch to Current Parent Item</code> <strong>Fallbacks</strong> option</p></li>
566
+ <li><p><em>'parent'</em> : Enables widget's <code>Include Parent...</code> <strong>Fallbacks</strong> extension option (in addition to the above)</p></li>
567
+ <li><p><em>'siblings'</em> : Enables widget's <code>&amp; its Siblings</code> <strong>Fallbacks</strong> extension option (in addition to the above)</p></li>
568
+ </ul></li>
569
+ <li><p><strong>start_level</strong> <em>(integer, default 1)</em> See widget's <code>Starting Level</code> option, under <strong>Filter</strong> above.</p></li>
570
+ <li><p><strong>depth</strong> <em>(integer, default 0)</em> See widget's <code>For Depth</code> option, under <strong>Filter</strong> above.</p></li>
571
+ <li><p><strong>depth_rel_current</strong> <em>(switch, off by default, 1 to enable)</em> See widget's <code>Relative to "Currrent" Item</code> option, under <strong>Filter</strong> above.</p></li>
572
+ <li><p><strong>flat_output</strong> <em>(switch, off by default, 1 to enable)</em> See widget's <code>Flat</code> option, under <strong>Output</strong> above.</p></li>
573
+ <li><p><strong>contains_current</strong> <em>(switch, off by default, 1 to enable)</em> See widget's <code>Must Contain "Current" Item</code> option, under <strong>Output</strong> above.</p></li>
574
+ <li><p><strong>include</strong> <em>(string)</em></p>
575
+
576
+ <ul>
577
+ <li><p><em>'parent'</em> : Enables widget's <code>Include Parent...</code> <strong>Output</strong> option</p></li>
578
+ <li><p><em>'siblings'</em> : Enables widget's <code>&amp; its Siblings</code> <strong>Output</strong> option</p></li>
579
+ <li><p><em>'ancestors'</em> : Enables widget's <code>Include Ancestors</code> <strong>Output</strong> option</p></li>
580
+ </ul>
581
+
582
+ <p>Supply more than one by separating them with a comma, space or hyphen, eg. <code>include="siblings ancestors"</code>.</p></li>
583
+ <li><p><strong>title_from</strong> <em>(string)</em></p>
584
+
585
+ <ul>
586
+ <li><p><em>'parent'</em> : Enables widget's <code>Title from Parent</code> <strong>Output</strong> option</p></li>
587
+ <li><p><em>'current'</em> : Enables widget's <code>Title from "Current" Item</code> <strong>Output</strong> option</p></li>
588
+ </ul>
589
+
590
+ <p>Supply more than one by separating them with a comma, space or hyphen, eg. <code>title_from="parent,current"</code>.</p></li>
591
+ <li><p><strong>ol_root</strong> <em>(switch, off by default, 1 to enable)</em> See widget's <code>Top Level</code> option, under <strong>Output</strong> above.</p></li>
592
+ <li><p><strong>ol_sub</strong> <em>(switch, off by default, 1 to enable)</em> See widget's <code>Sub-Levels</code> option, under <strong>Output</strong> above.</p></li>
593
+ <li><p><strong>container</strong> <em>(string)</em> See widget's <code>Element</code> option, under <strong>Container</strong> above.</p></li>
594
+ <li><p><strong>container_id</strong> <em>(string)</em> See widget's <code>Unique ID</code> option, under <strong>Container</strong> above.</p></li>
595
+ <li><p><strong>container_class</strong> <em>(string)</em> See widget's <code>Class</code> option, under <strong>Container</strong> above.</p></li>
596
+ <li><p><strong>menu_class</strong> <em>(string)</em> See widget's <code>Menu Class</code> option, under <strong>Classes</strong> above.</p></li>
597
+ <li><p><strong>widget_class</strong> <em>(string)</em> See widget's <code>Widget Class</code> option, under <strong>Classes</strong> above.</p></li>
598
+ <li><p><strong>wrap_link</strong> <em>(string)</em></p>
599
+
600
+ <p>This is an optional tag name (eg. <em>'div'</em>, <em>'p'</em>, <em>'span'</em>) that, if provided, will be made into HTML start/end tags
601
+ and sent through to the widget as its <code>Before the Link</code> and <code>After the Link</code> options. Please note that the shortcode usage - a simple
602
+ tag name - is much more restrictive than the widget's options, which allow HTML.</p></li>
603
+ <li><p><strong>wrap_link_text</strong> <em>(string)</em></p>
604
+
605
+ <p>This is an optional tag name (eg. <em>'span'</em>, <em>'em'</em>, <em>'strong'</em>) that, if provided, will be made into HTML start/end tags
606
+ and sent through to the widget as its <code>Before the Link Text</code> and <code>After the Link Text</code> options. Please note that the shortcode usage - a
607
+ simple tag name - is much more restrictive than the widget's options, which allow HTML.</p></li>
608
+ </ul>
609
+
610
+ <p><strong>Shortcode Examples</strong></p>
611
+
612
+ <ul>
613
+ <li><p>Show the entire "main" menu :</p>
614
+
615
+ <p><code>[custom_menu_wizard menu=main]</code></p></li>
616
+ <li><p>Show the children of the Current Item within the "main" menu, for unlimited depth, and include the Current Item's parent :</p>
617
+
618
+ <p><code>[custom_menu_wizard menu=main children_of=current include=parent]</code></p></li>
619
+ <li><p>From the "animals" menu, show all the items <em>immediately</em> below (depth=1) "Small Dogs", plus "Small Dogs" and its sibling items, as ordered lists :</p>
620
+
621
+ <p><code>[custom_menu_wizard menu="animals" children_of="small dogs" depth=1 include="siblings" ol_root=1 ol_sub=1]</code></p></li>
622
+ </ul>
623
+ <hr />
624
+
625
+ </body>
626
+ </html>