View Javadoc
1   /*
2    * Copyright (c) 2011-2024 PrimeFaces Extensions
3    *
4    *  Permission is hereby granted, free of charge, to any person obtaining a copy
5    *  of this software and associated documentation files (the "Software"), to deal
6    *  in the Software without restriction, including without limitation the rights
7    *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    *  copies of the Software, and to permit persons to whom the Software is
9    *  furnished to do so, subject to the following conditions:
10   *
11   *  The above copyright notice and this permission notice shall be included in
12   *  all copies or substantial portions of the Software.
13   *
14   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   *  THE SOFTWARE.
21   */
22  package org.primefaces.extensions.component.keynote;
23  
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  import javax.faces.FacesException;
29  import javax.faces.application.ResourceDependency;
30  import javax.faces.component.ContextCallback;
31  import javax.faces.component.UIComponent;
32  import javax.faces.component.UINamingContainer;
33  import javax.faces.component.behavior.ClientBehaviorHolder;
34  import javax.faces.component.visit.VisitCallback;
35  import javax.faces.component.visit.VisitContext;
36  import javax.faces.context.FacesContext;
37  import javax.faces.event.AjaxBehaviorEvent;
38  import javax.faces.event.BehaviorEvent;
39  import javax.faces.event.FacesEvent;
40  import javax.faces.event.PhaseId;
41  
42  import org.primefaces.component.api.PrimeClientBehaviorHolder;
43  import org.primefaces.component.api.Widget;
44  import org.primefaces.extensions.component.base.AbstractDynamicData;
45  import org.primefaces.extensions.event.KeynoteEvent;
46  import org.primefaces.extensions.model.common.KeyData;
47  import org.primefaces.extensions.model.keynote.KeynoteItem;
48  import org.primefaces.util.Constants;
49  import org.primefaces.util.MapBuilder;
50  
51  @ResourceDependency(library = "primefaces", name = "jquery/jquery.js")
52  @ResourceDependency(library = "primefaces", name = "jquery/jquery-plugins.js")
53  @ResourceDependency(library = "primefaces", name = "core.js")
54  @ResourceDependency(library = "primefaces-extensions", name = "primefaces-extensions.js")
55  @ResourceDependency(library = "primefaces-extensions", name = "keynote/keynote.js")
56  @ResourceDependency(library = "primefaces-extensions", name = "keynote/keynote.css")
57  public class Keynote extends AbstractDynamicData implements Widget, ClientBehaviorHolder, PrimeClientBehaviorHolder {
58  
59      public static final String COMPONENT_TYPE = "org.primefaces.extensions.component.Keynote";
60      public static final String COMPONENT_FAMILY = "org.primefaces.extensions.component";
61      public static final String DEFAULT_RENDERER = "org.primefaces.extensions.component.KeynoteRenderer";
62  
63      private static final String DEFAULT_EVENT = "slideChanged";
64  
65      private static final Map<String, Class<? extends BehaviorEvent>> BEHAVIOR_EVENT_MAPPING = MapBuilder.<String, Class<? extends BehaviorEvent>> builder()
66                  .put("slideTransitionEnd", null)
67                  .put(DEFAULT_EVENT, null)
68                  .build();
69  
70      private static final Collection<String> EVENT_NAMES = BEHAVIOR_EVENT_MAPPING.keySet();
71  
72      private Map<String, UIKeynoteItem> items;
73  
74      // @formatter:off
75      @SuppressWarnings("java:S115")
76      protected enum PropertyKeys {
77          widgetVar,
78          width,
79          height,
80          margin,
81          minScale,
82          maxScale,
83          autoSlide,
84          center,
85          controls,
86          disableLayout,
87          embedded,
88          loop,
89          navigationMode,
90          progress,
91          showNotes,
92          slideNumber,
93          touch,
94          transition,
95          transitionSpeed,
96          backgroundTransition,
97          theme,
98          library,
99          style,
100         styleClass
101     }
102     //@formatter:on
103 
104     public Keynote() {
105         setRendererType(DEFAULT_RENDERER);
106     }
107 
108     @Override
109     public Map<String, Class<? extends BehaviorEvent>> getBehaviorEventMapping() {
110         return BEHAVIOR_EVENT_MAPPING;
111     }
112 
113     @Override
114     public Collection<String> getEventNames() {
115         return EVENT_NAMES;
116     }
117 
118     @Override
119     public String getDefaultEventName() {
120         return DEFAULT_EVENT;
121     }
122 
123     @Override
124     public String getFamily() {
125         return COMPONENT_FAMILY;
126     }
127 
128     public int getWidth() {
129         return (Integer) getStateHelper().eval(PropertyKeys.width, 960);
130     }
131 
132     public void setWidth(final int width) {
133         getStateHelper().put(PropertyKeys.width, width);
134     }
135 
136     public int getHeight() {
137         return (Integer) getStateHelper().eval(PropertyKeys.height, 700);
138     }
139 
140     public void setHeight(final int height) {
141         getStateHelper().put(PropertyKeys.height, height);
142     }
143 
144     public Double getMargin() {
145         return (Double) getStateHelper().eval(PropertyKeys.margin, 0.04);
146     }
147 
148     public void setMargin(final Double margin) {
149         getStateHelper().put(PropertyKeys.margin, margin);
150     }
151 
152     public Double getMinScale() {
153         return (Double) getStateHelper().eval(PropertyKeys.minScale, 0.2);
154     }
155 
156     public void setMinScale(final Double minScale) {
157         getStateHelper().put(PropertyKeys.minScale, minScale);
158     }
159 
160     public Double getMaxScale() {
161         return (Double) getStateHelper().eval(PropertyKeys.maxScale, 2.0);
162     }
163 
164     public void setMaxScale(final Double maxScale) {
165         getStateHelper().put(PropertyKeys.maxScale, maxScale);
166     }
167 
168     public int getAutoSlide() {
169         return (Integer) getStateHelper().eval(PropertyKeys.autoSlide, 0);
170     }
171 
172     public void setAutoSlide(final int autoSlide) {
173         getStateHelper().put(PropertyKeys.autoSlide, autoSlide);
174     }
175 
176     public Boolean isCenter() {
177         return (Boolean) getStateHelper().eval(PropertyKeys.center, true);
178     }
179 
180     public void setCenter(final Boolean center) {
181         getStateHelper().put(PropertyKeys.center, center);
182     }
183 
184     public Boolean isControls() {
185         return (Boolean) getStateHelper().eval(PropertyKeys.controls, true);
186     }
187 
188     public void setControls(final Boolean controls) {
189         getStateHelper().put(PropertyKeys.controls, controls);
190     }
191 
192     public Boolean isDisableLayout() {
193         return (Boolean) getStateHelper().eval(PropertyKeys.disableLayout, false);
194     }
195 
196     public void setDisableLayout(final Boolean disableLayout) {
197         getStateHelper().put(PropertyKeys.disableLayout, disableLayout);
198     }
199 
200     public Boolean isEmbedded() {
201         return (Boolean) getStateHelper().eval(PropertyKeys.embedded, false);
202     }
203 
204     public void setEmbedded(final Boolean embedded) {
205         getStateHelper().put(PropertyKeys.embedded, embedded);
206     }
207 
208     public Boolean isLoop() {
209         return (Boolean) getStateHelper().eval(PropertyKeys.loop, false);
210     }
211 
212     public void setLoop(final Boolean loop) {
213         getStateHelper().put(PropertyKeys.loop, loop);
214     }
215 
216     public String getNavigationMode() {
217         return (String) getStateHelper().eval(PropertyKeys.navigationMode, "default");
218     }
219 
220     public void setNavigationMode(final String navigationMode) {
221         getStateHelper().put(PropertyKeys.navigationMode, navigationMode);
222     }
223 
224     public Boolean isProgress() {
225         return (Boolean) getStateHelper().eval(PropertyKeys.progress, true);
226     }
227 
228     public void setProgress(final Boolean progress) {
229         getStateHelper().put(PropertyKeys.progress, progress);
230     }
231 
232     public Boolean isShowNotes() {
233         return (Boolean) getStateHelper().eval(PropertyKeys.showNotes, false);
234     }
235 
236     public void setShowNotes(final Boolean showNotes) {
237         getStateHelper().put(PropertyKeys.showNotes, showNotes);
238     }
239 
240     public String getSlideNumber() {
241         return (String) getStateHelper().eval(PropertyKeys.slideNumber, "false");
242     }
243 
244     public void setSlideNumber(final String slideNumber) {
245         getStateHelper().put(PropertyKeys.slideNumber, slideNumber);
246     }
247 
248     public Boolean isTouch() {
249         return (Boolean) getStateHelper().eval(PropertyKeys.touch, true);
250     }
251 
252     public void setTouch(final Boolean touch) {
253         getStateHelper().put(PropertyKeys.touch, touch);
254     }
255 
256     public String getTransition() {
257         return (String) getStateHelper().eval(PropertyKeys.transition, "slide");
258     }
259 
260     public void setTransition(final String transition) {
261         getStateHelper().put(PropertyKeys.transition, transition);
262     }
263 
264     public String getTransitionSpeed() {
265         return (String) getStateHelper().eval(PropertyKeys.transitionSpeed, "default");
266     }
267 
268     public void setTransitionSpeed(final String transitionSpeed) {
269         getStateHelper().put(PropertyKeys.transitionSpeed, transitionSpeed);
270     }
271 
272     public String getBackgroundTransition() {
273         return (String) getStateHelper().eval(PropertyKeys.backgroundTransition, "fade");
274     }
275 
276     public void setBackgroundTransition(final String backgroundTransition) {
277         getStateHelper().put(PropertyKeys.backgroundTransition, backgroundTransition);
278     }
279 
280     public String getTheme() {
281         return (String) getStateHelper().eval(PropertyKeys.theme, "none");
282     }
283 
284     public void setTheme(final String theme) {
285         getStateHelper().put(PropertyKeys.theme, theme);
286     }
287 
288     public String getLibrary() {
289         return (String) getStateHelper().eval(PropertyKeys.library, org.primefaces.extensions.util.Constants.LIBRARY);
290     }
291 
292     public void setLibrary(final String library) {
293         getStateHelper().put(PropertyKeys.library, library);
294     }
295 
296     public String getWidgetVar() {
297         return (String) getStateHelper().eval(PropertyKeys.widgetVar, null);
298     }
299 
300     public void setWidgetVar(final String widgetVar) {
301         getStateHelper().put(PropertyKeys.widgetVar, widgetVar);
302     }
303 
304     public String getStyle() {
305         return (String) getStateHelper().eval(PropertyKeys.style, null);
306     }
307 
308     public void setStyle(final String style) {
309         getStateHelper().put(PropertyKeys.style, style);
310     }
311 
312     public String getStyleClass() {
313         return (String) getStateHelper().eval(PropertyKeys.styleClass, null);
314     }
315 
316     public void setStyleClass(final String styleClass) {
317         getStateHelper().put(PropertyKeys.styleClass, styleClass);
318     }
319 
320     @Override
321     public void queueEvent(final FacesEvent event) {
322         if (event instanceof AjaxBehaviorEvent) {
323             final FacesContext context = getFacesContext();
324             final AjaxBehaviorEvent behaviorEvent = (AjaxBehaviorEvent) event;
325             final Map<String, String> params = context.getExternalContext().getRequestParameterMap();
326             final String eventName = params.get(Constants.RequestParams.PARTIAL_BEHAVIOR_EVENT_PARAM);
327 
328             if ("slideTransitionEnd".equals(eventName)) {
329                 final boolean slideTransitionEnd = Boolean.parseBoolean(
330                             params.get(getClientId(context) + "_slideTransitionEnd"));
331                 final boolean lastSlide = Boolean.parseBoolean(params.get(getClientId(context) + "_lastSlide"));
332                 final KeynoteEvent keynoteEvent = new KeynoteEvent(this, behaviorEvent.getBehavior(),
333                             slideTransitionEnd, lastSlide);
334                 keynoteEvent.setPhaseId(event.getPhaseId());
335                 super.queueEvent(keynoteEvent);
336             }
337             else if (DEFAULT_EVENT.equals(eventName)) {
338                 final boolean slideChanged = Boolean.parseBoolean(params.get(getClientId(context) + "_slideChanged"));
339                 final boolean lastSlide = Boolean.parseBoolean(params.get(getClientId(context) + "_lastSlide"));
340                 final KeynoteEvent keynoteEvent = new KeynoteEvent(this, behaviorEvent.getBehavior(), slideChanged,
341                             lastSlide);
342                 keynoteEvent.setPhaseId(event.getPhaseId());
343                 super.queueEvent(keynoteEvent);
344             }
345             else {
346                 super.queueEvent(event);
347             }
348         }
349         else {
350             super.queueEvent(event);
351         }
352     }
353 
354     public UIKeynoteItem getItem(final String type) {
355         final UIKeynoteItem item = getItems().get(type);
356 
357         if (item == null) {
358             throw new FacesException("UIKeynoteItem to type " + type + " was not found");
359         }
360         else {
361             return item;
362         }
363     }
364 
365     protected Map<String, UIKeynoteItem> getItems() {
366         if (items == null) {
367             items = new HashMap<>();
368             for (final UIComponent child : getChildren()) {
369                 if (child instanceof UIKeynoteItem) {
370                     final UIKeynoteItem keynoteItem = (UIKeynoteItem) child;
371                     items.put(keynoteItem.getType(), keynoteItem);
372                 }
373             }
374         }
375 
376         return items;
377     }
378 
379     protected static void checkModelInstance(final Object value) {
380         if (!(value instanceof Collection<?>)) {
381             throw new FacesException("Value in Keynote must be of type Collection / List");
382         }
383     }
384 
385     @Override
386     protected KeyData findData(final String key) {
387         final Object value = getValue();
388         if (value == null) {
389             return null;
390         }
391 
392         checkModelInstance(value);
393 
394         final Collection<KeynoteItem> col = (Collection<KeynoteItem>) value;
395         for (final KeynoteItem keynoteItem : col) {
396             if (key.equals(keynoteItem.getKey())) {
397                 return keynoteItem;
398             }
399         }
400 
401         return null;
402     }
403 
404     @Override
405     protected void processChildren(final FacesContext context, final PhaseId phaseId) {
406         if (getVar() != null) {
407             // dynamic items
408             final Object value = getValue();
409             if (value != null) {
410                 checkModelInstance(value);
411 
412                 final Collection<KeynoteItem> col = (Collection<KeynoteItem>) value;
413                 for (final KeynoteItem keynoteItem : col) {
414                     processKeynoteDynamicItems(context, phaseId, keynoteItem);
415                 }
416             }
417 
418             resetData();
419         }
420         else {
421             // static items
422             processKeynoteStaticItems(context, phaseId);
423         }
424     }
425 
426     @Override
427     protected boolean visitChildren(final VisitContext context, final VisitCallback callback) {
428         if (getVar() != null) {
429             // dynamic items
430             final Object value = getValue();
431             if (value == null) {
432                 return false;
433             }
434 
435             checkModelInstance(value);
436 
437             final Collection<KeynoteItem> col = (Collection<KeynoteItem>) value;
438             for (final KeynoteItem keynoteItem : col) {
439                 if (visitKeynoteDynamicItems(context, callback, keynoteItem)) {
440                     return true;
441                 }
442             }
443 
444             resetData();
445         }
446         else {
447             // static items
448             return visitKeynoteStaticItems(context, callback);
449         }
450 
451         return false;
452     }
453 
454     @Override
455     protected boolean invokeOnChildren(final FacesContext context, final String clientId,
456                 final ContextCallback callback) {
457 
458         final Object value = getValue();
459         if (value == null) {
460             return false;
461         }
462 
463         checkModelInstance(value);
464 
465         if (getChildCount() > 0) {
466             // extract the keynoteItem key from the clientId
467             // it's similar to rowKey in UIData
468             String key = clientId.substring(getClientId().length() + 1);
469             key = key.substring(0, key.indexOf(UINamingContainer.getSeparatorChar(context)));
470 
471             final Collection<KeynoteItem> keynoteItems = (Collection<KeynoteItem>) value;
472             for (final KeynoteItem keynoteItem : keynoteItems) {
473 
474                 // determine associated KeynoteItem
475                 if (keynoteItem.getKey().equals(key)) {
476 
477                     // get UI control for KeynoteItem
478                     UIKeynoteItem uiKeynoteItem = null;
479                     if (getVar() == null) {
480                         for (final UIComponent child : getChildren()) {
481                             if (child instanceof UIKeynoteItem &&
482                                         ((UIKeynoteItem) child).getType().equals(keynoteItem.getType())) {
483                                 uiKeynoteItem = (UIKeynoteItem) child;
484                             }
485                         }
486                     }
487                     else {
488                         uiKeynoteItem = (UIKeynoteItem) getChildren().get(0);
489                     }
490 
491                     if (uiKeynoteItem == null) {
492                         continue;
493                     }
494 
495                     try {
496                         // push the associated data before visiting the child components
497                         setData(keynoteItem);
498 
499                         // visit children
500                         if (uiKeynoteItem.invokeOnComponent(context, clientId, callback)) {
501                             return true;
502                         }
503                     }
504                     finally {
505                         resetData();
506                     }
507 
508                 }
509             }
510         }
511 
512         return false;
513     }
514 
515     private void processKeynoteDynamicItems(final FacesContext context, final PhaseId phaseId,
516                 final KeynoteItem keynoteItem) {
517         for (final UIComponent kid : getChildren()) {
518             if (!(kid instanceof UIKeynoteItem) || !kid.isRendered()
519                         || !((UIKeynoteItem) kid).getType().equals(keynoteItem.getType())) {
520                 continue;
521             }
522 
523             for (final UIComponent grandkid : kid.getChildren()) {
524                 if (!grandkid.isRendered()) {
525                     continue;
526                 }
527 
528                 setData(keynoteItem);
529                 if (getData() == null) {
530                     return;
531                 }
532 
533                 if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
534                     grandkid.processDecodes(context);
535                 }
536                 else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
537                     grandkid.processValidators(context);
538                 }
539                 else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
540                     grandkid.processUpdates(context);
541                 }
542                 else {
543                     throw new IllegalArgumentException();
544                 }
545             }
546         }
547     }
548 
549     private void processKeynoteStaticItems(final FacesContext context, final PhaseId phaseId) {
550         for (final UIComponent kid : getChildren()) {
551             if (!(kid instanceof UIKeynoteItem) || !kid.isRendered()) {
552                 continue;
553             }
554 
555             for (final UIComponent grandkid : kid.getChildren()) {
556                 if (!grandkid.isRendered()) {
557                     continue;
558                 }
559 
560                 if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
561                     grandkid.processDecodes(context);
562                 }
563                 else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
564                     grandkid.processValidators(context);
565                 }
566                 else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
567                     grandkid.processUpdates(context);
568                 }
569                 else {
570                     throw new IllegalArgumentException();
571                 }
572             }
573         }
574     }
575 
576     private boolean visitKeynoteDynamicItems(final VisitContext context, final VisitCallback callback,
577                 final KeynoteItem keynoteItem) {
578         if (getChildCount() > 0) {
579             for (final UIComponent child : getChildren()) {
580                 if (child instanceof UIKeynoteItem
581                             && ((UIKeynoteItem) child).getType().equals(keynoteItem.getType())) {
582                     setData(keynoteItem);
583                     if (getData() == null) {
584                         return false;
585                     }
586 
587                     if (child.visitTree(context, callback)) {
588                         return true;
589                     }
590                 }
591             }
592         }
593 
594         return false;
595     }
596 
597     private boolean visitKeynoteStaticItems(final VisitContext context, final VisitCallback callback) {
598         if (getChildCount() > 0) {
599             for (final UIComponent child : getChildren()) {
600                 if (child instanceof UIKeynoteItem && child.visitTree(context, callback)) {
601                     return true;
602                 }
603             }
604         }
605 
606         return false;
607     }
608 
609     @Override
610     public Object saveState(final FacesContext context) {
611         // reset component for MyFaces view pooling
612         items = null;
613 
614         return super.saveState(context);
615     }
616 
617 }