Odyssey
CreateMenu.php
1 <?php
2 /**
3  * CreateMenu class
4  * @copyright HomeCu 05/2019
5  *
6  * An abstract class to extend and create menus from any JSON file (possibly other formats,
7  * such as HTML templates, in future versions.)
8  *
9  * Usage: See Documentation at the bottom of this file.
10  */
11 
12 abstract class CreateMenu
13 {
14  /** @var array the array passed around system */
15  protected $HB_ENV = [];
16 
17  /** @var array store params for substitution array here. */
18  protected $params = [];
19 
20  /** @var array array decoded from JSON */
21  protected $menu_array = [];
22 
23  /** @var string the return HTML (or error) string. */
24  protected $menu_html = '';
25 
26  /** @var array If any errors, store them in this array. */
27  protected $errors = [];
28 
29  /**
30  * CreateMenu constructor
31  * @return void
32  */
33  public function __construct($HB_ENV = [])
34  {
35  if (count($HB_ENV) == 0) {
36  return;
37  }
38 
39  $this->HB_ENV = $HB_ENV;
40  }
41 
42  /**
43  * If we have no CU and user, no need to output anything.
44  * @return bool
45  */
46  public function isCuAndUser()
47  {
48  return (
49  isset($this->HB_ENV['Cu']) &&
50  (strlen($this->HB_ENV['Cu']) > 0) &&
51  isset($this->HB_ENV['Uid']) &&
52  (strlen($this->HB_ENV['Uid']) > 0)
53  );
54  }
55 
56  /**
57  * Main entry point, convert the JSON array to a menu, substituting any values
58  * required and return final HTML.
59  * @param array $params
60  * @return string
61  */
62  public function makeMenu($params = [])
63  {
64  return $this
65  ->setMenuArray($params)
66  ->setMenuParams($params)
67  ->makeMenuHtml()
68  ->returnMenuHtml();
69  }
70 
71  /**
72  * Using the same methods above, return the content of a single node in the
73  * array. Digs recursively into the array until it finds $node_id and
74  * depending on the wrapper flag, returns the full node or its
75  * content only.
76  * @param string $node_id
77  * @param array $params
78  * @param bool $full_node - give us the element as is in the array false = just its content
79  * @return string
80  */
81  public function makeSingleMenuNode($node_id = '', $params = [], $full_node = false)
82  {
83  $this
84  ->setMenuArray($params)
85  ->setMenuParams($params);
86 
87  $node = $this->findNode($node_id);
88  if (count($node) > 0) {
89  return ($full_node)?
90  $this->createElementHtml($node, true) :
91  $this->addElementContent($node_id, $node);
92  }
93 
94  return '';
95  }
96 
97  /**
98  * Helper for makeSingleMenuNode(), find the node in the array identified by $node_id.
99  * @param string $node_id
100  * @return array
101  */
102  protected function findNode($node_id)
103  {
104  if (! (isset($this->menu_array['items']) && is_array($this->menu_array['items']))) {
105  return [];
106  }
107 
108  foreach ($this->menu_array['items'] as $item) {
109 
110  $arr = $this->recursivelyWalkArray($node_id, $item);
111  if (count($arr) > 0) {
112  return $arr;
113  }
114  }
115  return [];
116  }
117 
118  /**
119  * Helper for findNode(). Recursively dig into the array and attempt to
120  * locate the array identified by $node_id.
121  * @param string $node_id
122  * @param array $item
123  * @return array
124  */
125  protected function recursivelyWalkArray($node_id, $item)
126  {
127  if (! is_array($item)) {
128  return[];
129  }
130 
131  // Check top level, if it's there cool, we're done.
132  $id = $this->getElementId($item);
133  if (($id == $node_id)) {
134  return $item;
135  }
136 
137  // Walk $item array, if a matching ID is found, also done.
138  foreach ($item as $key => $value) {
139 
140  $id = $this->getElementId($value);
141  if (($id == $node_id)) {
142  return $value;
143  }
144 
145  // If we have nothing, recurse this function and walk through sub arrays.
146  $element_array = $this->recurseDigArray($node_id, $value);
147  if (count($element_array) > 0) {
148  return $element_array;
149  }
150  }
151 
152  return [];
153  }
154 
155  /**
156  * Helper for recursivelyWalkArray(), if we have a numerically indexed array,
157  * walk it and look for the node identified by $node_id.
158  * @param string $node_id
159  * @param array $sub_array
160  * @return array
161  */
162  protected function recurseDigArray($node_id, $sub_array)
163  {
164  if (is_array($sub_array) && ! $this->isAssociativeArray($sub_array)) {
165 
166  foreach ($sub_array as $node_array) {
167 
168  $element_array = $this->recursivelyWalkArray($node_id, $node_array);
169  $id = $this->getElementId($element_array);
170 
171  if (($id == $node_id)) {
172  return $element_array;
173  }
174  }
175  }
176 
177  return [];
178  }
179 
180  /**
181  * Extract the JSON file path and convert to an array. Or try to.
182  * @param array $params
183  * @return $this
184  */
185  protected function setMenuArray($params = [])
186  {
187  $json_path = $this->jsonPathParam($params);
188 
189  if (! is_readable($json_path)) {
190  $this->errors[] = "File $json_path is not readable.";
191  return $this;
192  }
193 
194  $arr = json_decode(file_get_contents($json_path), 1);
195  if (! $this->isValidConfig($arr)) {
196  return $this;
197  }
198 
199  $this->menu_array = $arr;
200 
201  return $this;
202  }
203 
204  /**
205  * Get the JSON file path from params.
206  * @param $params
207  * @return string|null
208  */
209  protected function jsonPathParam($params)
210  {
211  if (! (isset($params['json_path']) && ! empty($params['json_path']))) {
212  return null;
213  }
214 
215  return $params['json_path'];
216  }
217 
218  /**
219  * Check that the array decoded from JSON has valid array members.
220  * @param array $arr
221  * @return bool
222  */
223  protected function isValidConfig($arr = [])
224  {
225  if ($arr === false) {
226  $this->errors[] = "Could not read the JSON configuration.";
227  return false;
228  }
229 
230  if (! (isset($arr['container']) && is_array($arr['container']))) {
231  $this->errors[] = "There is no container element in the configuration.";
232  }
233 
234  if (! (isset($arr['items']) && is_array($arr['items']))) {
235  $this->errors[] = "There is no menu items array in the configuration.";
236  }
237 
238  return true;
239  }
240 
241  /**
242  * Extract substitution values from input and set them as input params as an internal property.
243  * @param array $params
244  * @return $this
245  */
246  protected function setMenuParams($params = [])
247  {
248  if ($this->isErrors()) {
249  return $this;
250  }
251 
252  if (
253  ! isset($params['substitute_values']) &&
254  is_array($params['substitute_values'])
255  ) {
256  return $this;
257  }
258 
259  foreach ($params['substitute_values'] as $key => $value) {
260  $this->params[$key] = $value;
261  }
262 
263  return $this;
264  }
265 
266  /**
267  * Make the menu and store in $this->menu_html.
268  * @return $this
269  */
270  protected function makeMenuHtml()
271  {
272  if ($this->isErrors()) {
273  return $this;
274  }
275 
276  // Already checked for these keys.
277  $container = $this->menu_array['container'];
278  $items = $this->menu_array['items'];
279 
280  $this->menu_html =
281  $this->createMenuToggleElement() .
282  $this->createElementHtml($container, false) .
283  $this->walkMenuItems($items) .
284  $this->closeHtmlElement($container);
285 
286  return $this;
287  }
288 
289  /**
290  * Create the "non menu" toggle element if it exists. It is still contained by the
291  * parent wrapper but not in the menu element list.
292  * @return string|null
293  */
294  protected function createMenuToggleElement()
295  {
296  if (!
297  isset($this->menu_array['toggle-element']) &&
298  is_array($this->menu_array['toggle-element'])
299  ) {
300  return null;
301  }
302 
303  return $this->createElementHtml($this->menu_array['toggle-element'], true);
304  }
305 
306  /**
307  * Helper for makeMenuHtml(), walk the menu items and compose the HTML string.
308  * @param array $items
309  * @return string|null
310  */
311  protected function walkMenuItems($items = [])
312  {
313  $html = null;
314 
315  foreach ($items as $item) {
316  $html .= $this->createElementHtml($item, true);
317  }
318 
319  return $html;
320  }
321 
322  /**
323  * The meat of the meal, output an HTML element. As we encounter content elements,
324  * this method is called recursively, allowing deeply nested elements to output.
325  * @param array $arr
326  * @param bool $close_tag - container needs to be closed after items done.
327  * @return string|null
328  */
329  protected function createElementHtml($arr = [], $close_tag = true)
330  {
331  // This check allows raw data in the content member, i.e. "content" : ["nothing but a string"]
332  if (isset($arr[0]) && is_string($arr[0])) {
333  return $arr . PHP_EOL;
334  }
335 
336  $id = $this->getElementId($arr);
337  if (! $this->hasDisplayValue($id, $arr)) {
338  return null;
339  }
340 
341  $html =
342  $this->openHtmlElement($arr) .
343  $this->addElementAttributes($id, $arr) .
344  $this->addElementClasses($id, $arr) .
345  $this->closeElementOpen($arr) .
346  $this->addElementContent($id, $arr);
347 
348  if ($close_tag) {
349  $html .= $this->closeHtmlElement($arr);
350  }
351 
352  return $html;
353  }
354 
355  /**
356  * Get the ID of an element if it exists.
357  * @param array $element
358  * @return string|null
359  */
360  protected function getElementId($element = [])
361  {
362  if (! isset($element['attributes']['id'])) {
363  return null;
364  }
365 
366  return $element['attributes']['id'];
367  }
368 
369  /**
370  * Helper for recursively walking array, looking for ID.
371  * @param array $arr
372  * @return bool
373  */
374  protected function isAssociativeArray($arr = [])
375  {
376  if (! is_array($arr)) {
377  return false;
378  }
379 
380  return array_keys($arr) !== range(0, count($arr) - 1);
381  }
382 
383  /**
384  * See if an element has an explicitly set display value. Default to true.
385  * @param string $id
386  * @param array $element
387  * @return bool
388  */
389  protected function hasDisplayValue($id, $element = [])
390  {
391  if (! isset($element['display'])) {
392  return true;
393  }
394 
395  return $this->hasUserDisplayValue($id, $element['display']);
396  }
397 
398  /**
399  * See if there is a display value set on the member.
400  * @param string $id
401  * @param bool $display
402  * @return bool|mixed
403  */
404  protected function hasUserDisplayValue($id, $display)
405  {
406  $key = $id . '.display';
407  if (array_key_exists($key, $this->params)) {
408  return $this->params[$key];
409  }
410 
411  return $display === true;
412  }
413 
414  /**
415  * Open a valid HTML element.
416  * @param array $element
417  * @return string|null
418  */
419  protected function openHtmlElement($element = [])
420  {
421  if (! isset($element['html-element'])) {
422  return null;
423  }
424 
425  return
426  $this->addIndent($element['html-element']) .
427  "<{$element['html-element']}";
428  }
429 
430  /**
431  * Output the closing tag for any opened element. Only outputs for valid tags,
432  * allowing raw content members to output.
433  * @param array $element
434  * @return string|null
435  */
436  protected function closeHtmlElement($element = [])
437  {
438  if (! isset($element['html-element'])) {
439  return null;
440  }
441 
442  return
443  "</{$element['html-element']}>" .
444  $this->addNewLine($element['html-element']);
445  }
446 
447  /**
448  * Add indents after some elements
449  * @TODO let's see if we can better format output?
450  * @param $tag
451  * @return string|null
452  */
453  protected function addIndent($tag)
454  {
455  if (in_array($tag, ['p', 'li', 'div'])) {
456  return " ";
457  }
458 
459  return null;
460  }
461 
462  /**
463  * Add newlines after some elements
464  * @TODO let's see if we can better format output?
465  * @param $tag
466  * @return string|null
467  */
468  protected function addNewLine($tag)
469  {
470  if (in_array($tag, ['ul', 'li', 'p', 'div'])) {
471  return PHP_EOL;
472  }
473 
474  return null;
475  }
476 
477  /**
478  * Return the ending carat for an opened HTML tag.
479  * @param array $element
480  * @return string|null
481  */
482  protected function closeElementOpen($element = [])
483  {
484  // This check allows raw data in the content member, i.e. content : ["string without a tag"]
485  if (! array_key_exists('html-element', $element)) {
486  return null;
487  }
488 
489  // @TODO hinky
490  if (in_array($element['html-element'], ['i', 'em', 'b', 'strong', 'label', 'span'])) {
491  return '>';
492  }
493 
494  return '>' . PHP_EOL;
495  }
496 
497  /**
498  * Walk the attributes array and add all attributes EXCEPT class, special handling.
499  * @param string $id
500  * @param array $element
501  * @return string|null
502  */
503  protected function addElementAttributes($id, $element = [])
504  {
505  if (! $this->isAttributeMember($element)) {
506  return null;
507  }
508 
509  $attributes = null;
510 
511  foreach ($element['attributes'] as $attr => $value) {
512  if ($attr == 'class') {
513  continue;
514  }
515 
516  $val = $this->setMemberValue($id, $value, $attr, $element);
517  $set_value = $this->translateValuesIfRequired($attr, $val);
518 
519  if ($set_value) {
520  $attributes .= " $attr=\"$set_value\" ";
521  }
522 
523  }
524 
525  return rtrim($attributes);
526  }
527 
528  /**
529  * @param $attr
530  * @param $value
531  * @return string
532  */
533  protected function translateValuesIfRequired($attr, $value)
534  {
535  if (! in_array($attr, ['title', 'aria-label', 'alt', 'label'])) {
536  return $value;
537  }
538  $translate = $this->HB_ENV['MC']->msg($value);
539  return (! empty($translate))? $translate : $value;
540  }
541 
542  /**
543  * Helper for addElementAttributes, is this an attributes array member?
544  * @param array $element
545  * @return bool
546  */
547  protected function isAttributeMember($element = [])
548  {
549  return
550  isset($element['attributes']) &&
551  is_array($element['attributes']);
552  }
553 
554  /**
555  * Add classes to the HTML element.
556  * @param string $id
557  * @param array $element
558  * @return string|null
559  */
560  protected function addElementClasses($id, $element = [])
561  {
562  if (! $this->isClassMember($element)) {
563  return null;
564  }
565 
566  $classes = null;
567 
568  foreach ($element['attributes']['class'] as $classname => $value) {
569 
570  $set_value = $this->setMemberValue($id, $value, $classname, $element);
571 
572  if ($set_value) {
573  $classes .= "$classname ";
574  }
575  }
576 
577  return (empty($classes))? null : ' class="' . rtrim($classes) . '"';
578  }
579 
580  /**
581  * Set substitution value if found, otherwise return what's already in the array.
582  * @param string $id, the element ID
583  * @param mixed $value, existing value in array
584  * @param string $key_to_find, the current member we are looking for if found in input
585  * @param array $element
586  * @return mixed|null
587  */
588  protected function setMemberValue($id, $value, $key_to_find, $element = [])
589  {
590  $sub_value = $this->setSubValue($id, $key_to_find, $element);
591 
592  if (! is_null($sub_value)) {
593  return $sub_value;
594  }
595 
596  return $value;
597  }
598 
599  /**
600  * If there is a substitution value in input params, return it.
601  * @param string $id
602  * @param string $key_to_find, the current member we are looking for if found in input
603  * @param array $element
604  * @return mixed|null
605  */
606  protected function setSubValue($id, $key_to_find, $element = [])
607  {
608  foreach ($this->params as $identifier => $value) {
609 
610  $ret = $this->isKeyInArray($id, $key_to_find, $identifier, $element);
611 
612  if ($ret) {
613  return $value;
614  }
615 
616  }
617  return null;
618  }
619 
620  /**
621  * Recursively dig into the element array looking for a match on the dot syntax string.
622  * If found, return true and we will use the input substitution value for the existing
623  * array value.
624  * @param string $id, the element container ID attribute
625  * @param string $key_to_find, the current member key we are looking for
626  * @param string $input_string, the dot-syntax input string
627  * @param array $element, current element array
628  * @return bool
629  */
630  protected function isKeyInArray($id, $key_to_find, $input_string, $element = [])
631  {
632  if (!is_string($input_string) || empty($input_string) || !count($element))
633  {
634  return false;
635  }
636 
637  if (strpos($input_string, '.') !== false)
638  {
639  $key_found = false;
640  $keys = explode('.', $input_string);
641  // We are already checking the parent element
642  $find_id = array_shift($keys);
643  if (! ($find_id == $id)) {
644  return false;
645  }
646 
647  foreach ($keys as $inner_key)
648  {
649  if (array_key_exists($inner_key, $element)) {
650 
651  if ($inner_key == $key_to_find) {
652  $key_found = true;
653  break;
654  }
655 
656  $element = $element[$inner_key];
657  }
658  }
659 
660  return $key_found;
661  }
662 
663  // No one home
664  return false;
665 }
666 
667  /**
668  * Helper for addElementClasses, is this a class array member?
669  * @param array $element
670  * @return bool
671  */
672  protected function isClassMember($element = [])
673  {
674  return
675  isset($element['attributes']['class']) &&
676  is_array($element['attributes']['class']);
677  }
678 
679  /**
680  * Add content members. This method is called recursively for nested content members.
681  * @param string id, container id
682  * @param array $element
683  * @return string|null
684  */
685  protected function addElementContent($id, $element = [])
686  {
687  $translate = null;
688  if (! $this->isContentMember($element)) {
689  return null;
690  }
691 
692  // Do not translate - these items should already be translated.
693  $user_content = $this->userContentOverride($id);
694  if (! empty($user_content)) {
695  return $user_content;
696  }
697 
698  $content = null;
699 
700  foreach ($element['content'] as $content_arr) {
701  $content .= $this->createElementHtml($content_arr, true);
702  }
703 
704  return $content;
705  }
706 
707  /**
708  * Users **may** override content, such as a dynamically created link text or other content.
709  * The first and last elements (container id and identifier 'content') are all we need
710  * in this case. if the ID and identifier match, return user-provided content.
711  * @param string $id
712  * @return string|null
713  */
714  protected function userContentOverride($id)
715  {
716  foreach ($this->params as $identifier => $value) {
717 
718  $path = explode('.', $identifier);
719  $find_id = array_shift($path);
720  $target = array_pop($path);
721 
722  if (($target == 'content') && ($find_id == $id)) {
723  return $value;
724  }
725  }
726 
727  return null;
728  }
729 
730  /**
731  * Helper for addElementContent, is this a content member in the array?
732  * @param array $element
733  * @return bool
734  */
735  protected function isContentMember($element = [])
736  {
737  return
738  isset($element['content']) &&
739  is_array($element['content']);
740  }
741 
742  /**
743  * Return the response, whether it's errors or the menu.
744  * @return string
745  */
746  protected function returnMenuHtml()
747  {
748  if ($this->isErrors()) {
749  return $this->composeErrors();
750  }
751 
752  return $this->menu_html;
753  }
754 
755  /**
756  * Basic filter (could use improvement, but all values should be internal)
757  * @param mixed $value
758  * @return mixed
759  */
760  protected function filterInputValue($value)
761  {
762  return filter_var($value, FILTER_UNSAFE_RAW);
763  }
764 
765  /**
766  * Compose error HTML from $this->errors array.
767  * @return string
768  */
769  protected function composeErrors()
770  {
771  $errors = "<ul class=\"error\">";
772 
773  foreach ($this->errors as $error) {
774  $errors .= "<li>$error</li>";
775  }
776 
777  return "$errors</ul>";
778  }
779 
780  /**
781  * Determine if there are errors.
782  * @return bool
783  */
784  protected function isErrors()
785  {
786  return (count($this->errors) > 0);
787  }
788 
789 }
790 
791 /**************************************************************************************
792  * DOCUMENTATION
793  **************************************************************************************
794  *
795  * Basic usage: (Full menu, see methods to access a node below)
796  *
797  * $CreateMenuObject = new YourConcreteClassExtendingCreateMenu($HB_ENV);
798  * echo $CreateMenuObject->buildYourParamsAndOutput($params);
799  *
800  * Within your concrete extended class, create a method to build substitution params
801  * and pass the params to $this->makeMenu($this->sub_params).
802  *
803  * If no substitution values are provided, this class will output everything in the JSON as HTML
804  * for all nodes that do not have a property of display: false or classname: false. To populate
805  * dynamic and conditional values in the JSON array dynamically, extend this class and add
806  * methods in your concrete class to create a substitution array as below, then set it as
807  * a param to $this->makeMenu(). These will overwrite default values set in the JSON.
808  *
809  * Substituting Dynamic Values:
810  *
811  * This class parses keys as a dot syntax for the substitution values. This may seem complicated
812  * but is quite simple: imagine your html flattened into a single string with the id attribute
813  * the first member of the string. This is used to set dynamic values externally or internally
814  * in your concrete class. A simple example:
815  *
816  * $params = [
817  * config_path => path to JSON,
818  * substitute_values => [
819  * 'container_id.attributes.href.class.some-class' => someFunctionCaclulatingBoolean(),
820  * 'link_id.attributes.href' => 'some-external-dynamic-link.php',
821  * 'link_id.content' => 'Go to Dynamic Link Here',
822  * ];
823  *
824  * This would result in something like
825  *
826  * <p id="container_id" class="some-other-class-in-json some-class">
827  * <a id="link_id" href="some-external-dynamic-link.php">Go to Dynamic Link Here</a>
828  * </p>
829  *
830  * Four basic rules for substituting values:
831  * - Must have container and items members, see below.
832  * - Substitution keys are STRINGS, this is not Javascript. :-) , do not mistakenly use
833  * identifier.attributes.property instead of 'identifier.attributes.property'
834  * - The element to substitute **MUST** have an ID attribute.
835  * - The element to substitute **MUST** exist in the JSON, it will not create new
836  * nodes (in this version, maybe later.)
837  *
838  * The provided JSON object must contain two members:
839  *
840  * - container: the parent container object of the menu, usually UL
841  * - items: array of item objects in the container
842  *
843  * {
844  * "container" : {
845  * },
846  * "items": [
847  * {
848  * }
849  * ]
850  * }
851  *
852  * Both the container object and the item objects adhere to the same structure. The only exception is
853  * content in items, explained below.
854  *
855  * JSON Properties
856  *
857  * {
858  * "display" : [boolean],
859  "html-element" : [string],
860  "attributes" : {
861  [id] : [string],
862  "class" : {
863  "css-class-name" [string] : [boolean]
864  }
865  },
866  * "content" [
867  * [mixed: string or {other HTML element objects } ]
868  * ]
869  * }
870  *
871  * display: boolean, optional, if omitted defaults to true, if present must be true|false. This
872  * determines an HTML element is added to the output by default. For example if the display
873  * property of a div is set to false and you conditionally want to enable it,
874  *
875  * 'element_id.display' = true
876  *
877  * html-element: string, required, any standard HTML element that opens and closes, examples ul, li, p, a.
878  * Self-closing HTML elements can be included raw in a content node, see below.
879  *
880  * attributes: object, optional, any attributes to assign to html-element with the exception of
881  * special case class, below. Examples: id, href, role, data-something. **ID IS REQUIRED** if you
882  * want to leverage the simplicity of dot syntax (or drive yourself crazy doing something like
883  * $this->menu_array['items'][5]['attributes']['class']['some-class-name'] = false )
884  *
885  * class: object, special attribute to allow control over multiple class assignments. Setting a
886  * class value to true will display it, false will remove it from the HTML. Classes specified
887  * in the JSON will only be overwritten if specified in the input attributes.class object.
888  * Example
889  *
890  * "class" : {
891  * "show-me" : true
892  * "do-not-show-me" : false
893  * }
894  *
895  * 'element_id.attributes.class.show-me' = false
896  * 'element_id.attributes.class.do-not-show-me' = true
897  *
898  * Result: the do-not-show-me class will be added to the DOM and show-me not added:
899  *
900  * <p id="element_id" class="do-not-show-me">
901  *
902  * content: array of content objects, applies only to items and not container. Content
903  * can be a simple string (but must be in an array)
904  *
905  * "content" : ["I am the egg man", '<br>', '&nbsp; <!-- ugh -->' ]
906  *
907  * or it can be an array of HTML elements that contain content objects themselves. Content
908  * objects adhere to the same structure as parent objects, and can contain as many other
909  * content objects as needed. You **do not** need to traverse the entire tree to access
910  * nested objects, which is why the ID's are so important.
911  *
912  * This class recursively reads nested content objects, just find it by ID.
913  *
914  * {
915  * "html-element" : "li",
916  * "attributes": {
917  * "id" : "unique-id",
918  * "title" : My List Item",
919  * "data-list-count" : "first item",
920  * "role" : "list item",
921  * "class" : {
922  * "my-normal-display-class" : true,
923  * "my-warning-display-class" : false
924  * }
925  * },
926  * "content" : [
927  * {
928  * "html-element" : "a",
929  * "attributes": {
930  * "id" : "my-list-item-link",
931  * "href" : "",
932  * "aria-label" : "my list item link",
933  * "class" : {
934  * "my-normal-display-class" : true,
935  * "my-warning-display-class" : false
936  * }
937  * },
938  * "content": []
939  * },
940  *
941  * '<hr>'
942  * ],
943  * }
944  *
945  * You can guess what the following might do.
946  *
947  * [
948  * 'unique-id.attributes.class.my-warning-display-class' => someFunctionThatChecksForWarning(),
949  * 'my-list-item-link.attributes.class.my-warning-display-class' => someFunctionThatChecksForWarning(),
950  * 'my-list-item-link.attributes.href' => 'you-have-been-warned.html',
951  * 'my-list-item-link' => 'Warning, Link Text Here'
952  * ]
953  *
954  **************************************************************************************
955  * Access a single node of the menu
956  **************************************************************************************
957  *
958  * Sometimes you just want a single node. The full_node param determines whether you want
959  * just the content of the node or the wrapper (usually an <li>.)
960  *
961  * Setup is the same, in your concrete class call makeSingleMenuNode() instead.
962  *
963  * makeSingleMenuNode([string DOM ID if element to get], [array of input params], [optional bool full node])
964  *
965  * CreateMenuObject = new YourConcreteClassExtendingCreateMenu($HB_ENV);
966  * echo $CreateMenuObject->buildYourSingleNodeParamsAndOutput($params);
967  *
968  * Without full node,
969  * $compass_menu->singleCompassMenuNode($json_path, $json, 'dropdown-user-menu-ul-li', true)
970  *
971  * will give you the element content with the <li> wrapper. Eliminate/set to false and outputs
972  * just the content.
973  */
makeMenu($params=[])
Definition: CreateMenu.php:62
jsonPathParam($params)
Definition: CreateMenu.php:209
setMenuArray($params=[])
Definition: CreateMenu.php:185
filterInputValue($value)
Definition: CreateMenu.php:760
isValidConfig($arr=[])
Definition: CreateMenu.php:223
__construct($HB_ENV=[])
Definition: CreateMenu.php:33
closeElementOpen($element=[])
Definition: CreateMenu.php:482
addElementClasses($id, $element=[])
Definition: CreateMenu.php:560
makeSingleMenuNode($node_id='', $params=[], $full_node=false)
Definition: CreateMenu.php:81
setMenuParams($params=[])
Definition: CreateMenu.php:246
addElementAttributes($id, $element=[])
Definition: CreateMenu.php:503
isClassMember($element=[])
Definition: CreateMenu.php:672
setMemberValue($id, $value, $key_to_find, $element=[])
Definition: CreateMenu.php:588
getElementId($element=[])
Definition: CreateMenu.php:360
createMenuToggleElement()
Definition: CreateMenu.php:294
hasDisplayValue($id, $element=[])
Definition: CreateMenu.php:389
walkMenuItems($items=[])
Definition: CreateMenu.php:311
hasUserDisplayValue($id, $display)
Definition: CreateMenu.php:404
userContentOverride($id)
Definition: CreateMenu.php:714
translateValuesIfRequired($attr, $value)
Definition: CreateMenu.php:533
isAttributeMember($element=[])
Definition: CreateMenu.php:547
addElementContent($id, $element=[])
Definition: CreateMenu.php:685
addIndent($tag)
Definition: CreateMenu.php:453
recurseDigArray($node_id, $sub_array)
Definition: CreateMenu.php:162
isKeyInArray($id, $key_to_find, $input_string, $element=[])
Definition: CreateMenu.php:630
addNewLine($tag)
Definition: CreateMenu.php:468
openHtmlElement($element=[])
Definition: CreateMenu.php:419
isContentMember($element=[])
Definition: CreateMenu.php:735
closeHtmlElement($element=[])
Definition: CreateMenu.php:436
setSubValue($id, $key_to_find, $element=[])
Definition: CreateMenu.php:606
findNode($node_id)
Definition: CreateMenu.php:102
createElementHtml($arr=[], $close_tag=true)
Definition: CreateMenu.php:329
recursivelyWalkArray($node_id, $item)
Definition: CreateMenu.php:125
isAssociativeArray($arr=[])
Definition: CreateMenu.php:374