/*
* Copyright 2010 Richard Nichols.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package com.visural.wicket.component.fancybox;
import com.visural.wicket.util.images.ImageReference;
import com.visural.common.Function;
import com.visural.common.StringUtil;
import com.visural.wicket.security.IPrivilege;
import com.visural.wicket.security.ISecureEnableInstance;
import com.visural.wicket.security.ISecureRenderInstance;
import java.util.HashMap;
import java.util.Map;
import net.fancybox.FancyBoxCSSRef;
import net.fancybox.FancyBoxJavascriptRef;
import net.fancybox.JQueryMouseWheelJSRef;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.JavascriptPackageResource;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
/**
* A wicket-integration of the Fancybox component from http://fancybox.net/
*
* Apply to a `<a href="">...</a>` anchor tag.
*
* Used in the following way: `<a href="#" wicket:id="myBox">...</a>`
*
* Fancybox can display an image, another markup container (e.g. div) or an iframe
* with a URL as a pop-up model box.
*
* Typically it would be used as a "lightbox" image viewer component, but videos
* or other embedded media may also be used.
*
* Fancyboxes can be added to groups, however only the first element added to the
* group's settings will take effect. i.e. the first element in the group's settings
* will apply to the whole group. This is a limitation of the underlying plugin.
*
* When applying fancybox to another WebMarkupContainer, you should enclose the
* container in a hidden div, e.g.
* `<div style="display: none;"><div wicket:id="myContentToShow">....</div></div>`
* This is less optimal but has changed due to Fancybox changes in the latest version.
*
* @version $Id: Fancybox.java 239 2010-11-23 00:35:45Z tibes80@gmail.com $
* @author Richard Nichols
*/
public class Fancybox extends WebMarkupContainer implements ISecureEnableInstance, ISecureRenderInstance {
private static final long serialVersionUID = 1L;
/**
* Use this javascript to close an iframe fancybox from the child iframe code.
*/
public static final String IFRAME_PARENT_CLOSE_JS = "parent.$.fancybox.close();";
private FancyboxGroup group;
private final ImageReference image;
private final IModel iframeURL;
private final WebMarkupContainer container;
private String boxTitle;
public Fancybox(String id, ImageReference image) {
super(id);
this.image = image;
this.iframeURL = null;
this.container = null;
initAll();
}
/**
* Create a fancy box link to an iframe URL.
* @param id
* @param linkText
* @param iframeURL
*/
public Fancybox(String id, IModel iframeURL) {
super(id);
this.image = null;
this.iframeURL = iframeURL;
this.container = null;
initAll();
}
/**
* Create a fancy box link to an iframe URL.
* @param id
* @param linkText
* @param iframeURL
*/
public Fancybox(String id, final String iframeURL) {
super(id);
this.image = null;
this.iframeURL = new AbstractReadOnlyModel() {
@Override
public Object getObject() {
return iframeURL;
}
};
this.container = null;
initAll();
}
/**
* Create a fancy box link to another DOM element
* @param id
* @param linkText
* @param iframeURL
*/
public Fancybox(String id, WebMarkupContainer container) {
super(id);
this.image = null;
this.iframeURL = null;
this.container = container;
initAll();
}
/**
* Override and return false to suppress static Javascript and CSS contributions.
* (May be desired if you are concatenating / compressing resources as part of build process)
* @return
*/
protected boolean autoAddToHeader() {
return true;
}
@Override
protected void onComponentTag(ComponentTag tag) {
checkComponentTag(tag, "a");
super.onComponentTag(tag);
if (boxTitle != null) {
tag.put("title", boxTitle);
}
if (group != null) {
tag.put("rel", group.getName());
}
if (isImage()) {
tag.put("href", image.getURL());
} else if (isIframe()) {
tag.put("href", Function.nvl(this.iframeURL.getObject(), "").toString());
} else if (isDiv()) {
if (!container.getOutputMarkupId()) {
throw new IllegalStateException("The container component " + container.getId() + " is not getOutputMarkupId() == true.");
}
tag.put("href", "#" + container.getMarkupId());
}
}
private boolean isImage() {
return (this.image != null);
}
private boolean isIframe() {
return (this.iframeURL != null);
}
private boolean isDiv() {
return (this.container != null);
}
private void initAll() {
this.setOutputMarkupId(true);
// we default the "hide on content click" to false if this isn't an image being displayed.
if (!this.isImage()) {
this.setHideOnContentClick(false);
}
if (autoAddToHeader()) {
add(JavascriptPackageResource.getHeaderContribution(new FancyBoxJavascriptRef()));
if (isMouseWheelEnabled()) {
add(JavascriptPackageResource.getHeaderContribution(new JQueryMouseWheelJSRef()));
}
add(CSSPackageResource.getHeaderContribution(new FancyBoxCSSRef()));
}
add(new HeaderContributor(new IHeaderContributor() {
public void renderHead(IHeaderResponse response) {
String js = getFancyBoxJS();
if (js != null) {
response.renderOnDomReadyJavascript(js);
}
}
}));
}
private String getFancyBoxJS() {
if (group != null && group.getMain() != this) {
return null;
}
String selector = (group != null ? group.getGroupSelector() : "#" + getMarkupId());
String options = getBoxOptionsAsString();
if (StringUtil.isBlankStr(options)) {
return "jQuery('" + selector + "').fancybox();";
} else {
return "jQuery('" + selector + "').fancybox({" + options + "});";
}
}
public FancyboxGroup getGroup() {
return group;
}
public Fancybox setGroup(FancyboxGroup group) {
if (this.group != null) {
this.group.removeFancybox(this);
}
this.group = group;
group.addFancybox(this);
return this;
}
public String getBoxTitle() {
return boxTitle;
}
public Fancybox setBoxTitle(String boxTitle) {
this.boxTitle = boxTitle;
return this;
}
/**
* Return whether to enable mouse wheel scroll on fancybox groups.
*
* By default mouse wheel functionality is disabled as it increases the
* amount of Javascript required and only applies to box groups.
*
* Override and return true to enable.
*
* @return
*/
public boolean isMouseWheelEnabled() {
return false;
}
private String getBoxOptionsAsString() {
StringBuilder result = new StringBuilder();
Map<String, String> options = getBoxOptions();
if (isIframe()) {
// force iframe, otherwise have to use element class, which creates styling issues
options.put("type", "'iframe'");
}
if (isImage()) {
options.put("type", "'image'");
}
boolean addBreak = false;
for (String option : options.keySet()) {
if (addBreak) {
result.append(",\n");
}
result.append("'");
result.append(option);
result.append("': ");
result.append(options.get(option));
addBreak = true;
}
return result.toString();
}
private Map<String, String> getBoxOptions() {
HashMap<String, String> result = new HashMap<String, String>();
if (padding != null) {
result.put("padding", padding.toString());
}
if (margin != null) {
result.put("margin", margin.toString());
}
if (opacity != null) {
result.put("opacity", opacity.toString());
}
if (modal != null) {
result.put("modal", modal.toString());
}
if (cyclic != null) {
result.put("cyclic", cyclic.toString());
}
if (scrolling != null) {
result.put("scrolling", scrolling.toString());
}
if (width != null) {
result.put("width", width.toString());
}
if (height != null) {
result.put("height", height.toString());
}
if (autoScale != null) {
result.put("autoScale", autoScale.toString());
}
if (autoDimensions != null) {
result.put("autoDimensions", autoDimensions.toString());
}
if (centerOnScroll != null) {
result.put("centerOnScroll", centerOnScroll.toString());
}
if (hideOnOverlayClick != null) {
result.put("hideOnOverlayClick", hideOnOverlayClick.toString());
}
if (hideOnContentClick != null) {
result.put("hideOnContentClick", hideOnContentClick.toString());
}
if (overlayShow != null) {
result.put("overlayShow", overlayShow.toString());
}
if (overlayOpacity != null) {
result.put("overlayOpacity", overlayOpacity.toString());
}
if (titleShow != null) {
result.put("titleShow", titleShow.toString());
}
if (speedIn != null) {
result.put("speedIn", speedIn.toString());
}
if (speedOut != null) {
result.put("speedOut", speedOut.toString());
}
if (changeSpeed != null) {
result.put("changeSpeed", changeSpeed.toString());
}
if (showCloseButton != null) {
result.put("showCloseButton", showCloseButton.toString());
}
if (showNavArrows != null) {
result.put("showNavArrows", showNavArrows.toString());
}
if (enableEscapeButton != null) {
result.put("enableEscapeButton", enableEscapeButton.toString());
}
if (overlayColor != null) {
result.put("overlayColor", "'" + overlayColor + "'");
}
if (titlePosition != null) {
result.put("titlePosition", "'" + titlePosition.getValue() + "'");
}
if (transitionIn != null) {
result.put("transitionIn", "'" + transitionIn + "'");
}
if (transitionOut != null) {
result.put("transitionOut", "'" + transitionOut + "'");
}
if (changeFade != null) {
result.put("changeFade", "'" + changeFade + "'");
}
if (easingIn != null) {
result.put("easingIn", "'" + easingIn + "'");
}
if (easingOut != null) {
result.put("easingOut", "'" + easingOut + "'");
}
if (onStart != null) {
result.put("onStart", "function() {" + onStart + "}");
}
if (onCancel != null) {
result.put("onCancel", "function() {" + onCancel + "}");
}
if (onComplete != null) {
result.put("onComplete", "function() {" + onComplete + "}");
}
if (onCleanup != null) {
result.put("onCleanup", "function() {" + onCleanup + "}");
}
if (onClosed != null) {
result.put("onClosed", "function() {" + onClosed + "}");
}
return result;
}
// fancybox properties follow
//ajax { } Ajax options (error, success will be overwritten)
//swf {wmode: 'transparent'} Flashvars to put on the swf object
//titleFormat null Callback to customize title area. You can set any html - custom image counter or even custom navigation
private Integer padding; // 10 Space between FancyBox wrapper and content
private Integer margin; // 20 Space between viewport and FancyBox wrapper
private Boolean opacity; // false When true, transparency of content is changed for elastic transitions
private Boolean modal; // false When true, 'overlayShow' is set to 'true' and 'hideOnOverlayClick', 'hideOnContentClick', 'enableEscapeButton', 'showCloseButton' are set to 'false'
private Boolean cyclic; // false When true, galleries will be cyclic, allowing you to keep pressing next/back.
private Boolean scrolling; // 'auto' Set the overflow CSS property to create or hide scrollbars. Can be set to 'auto', 'yes', or 'no'
private Integer width; // 560 Width for content types 'iframe' and 'swf'. Also set for inline content if 'autoDimensions' is set to 'false'
private Integer height; // 340 Height for content types 'iframe' and 'swf'. Also set for inline content if 'autoDimensions' is set to 'false'
private Boolean autoScale; // true If true, FancyBox is scaled to fit in viewport
private Boolean autoDimensions; // true For inline and ajax views, resizes the view to the element recieves. Make sure it has dimensions otherwise this will give unexpected results
private Boolean centerOnScroll; // false When true, FancyBox is centered while scrolling page
private Boolean hideOnOverlayClick; // true Toggle if clicking the overlay should close FancyBox
private Boolean hideOnContentClick; // false Toggle if clicking the content should close FancyBox
private Boolean overlayShow; // true Toggle overlay
private Float overlayOpacity; // 0.3 Opacity of the overlay (from 0 to 1; default - 0.3)
private Boolean titleShow; // true Toggle title
private String overlayColor; // '#666' Color of the overlay
private TitlePosition titlePosition; // 'outside' The position of title. Can be set to 'outside', 'inside' or 'over'
private String transitionIn; //
private String transitionOut; // 'fade' The transition type. Can be set to 'elastic', 'fade' or 'none'
private String changeFade; // 'fast' Speed of the content fading while changing gallery items
private String easingIn; //
private String easingOut; // 'swing' Easing used for elastic animations
private String onStart; // null Will be called right before attempting to load the content
private String onCancel; // null Will be called after loading is canceled
private String onComplete; // null Will be called once the content is displayed
private String onCleanup; // null Will be called just before closing
private String onClosed; // null Will be called once FancyBox is closed
private Integer speedIn; //
private Integer speedOut; // 300 Speed of the fade and elastic transitions, in milliseconds
private Integer changeSpeed; // 300 Speed of resizing when changing gallery items, in milliseconds
private Boolean showCloseButton; // true Toggle close button
private Boolean showNavArrows; // true Toggle navigation arrows
private Boolean enableEscapeButton; // true Toggle if pressing Esc button closes FancyBox
public Integer getPadding() {
return padding;
}
/**
* Space between FancyBox wrapper and content
* @param padding
* @return
*/
public Fancybox setPadding(Integer padding) {
this.padding = padding;
return this;
}
public Integer getMargin() {
return margin;
}
/**
* Space between viewport and FancyBox wrapper
* @param margin
* @return
*/
public Fancybox setMargin(Integer margin) {
this.margin = margin;
return this;
}
public Boolean getOpacity() {
return opacity;
}
/**
* When true, transparency of content is changed for elastic transitions
* @param opacity
* @return
*/
public Fancybox setOpacity(Boolean opacity) {
this.opacity = opacity;
return this;
}
public Boolean getModal() {
return modal;
}
/**
* When true, 'overlayShow' is set to 'true' and 'hideOnOverlayClick',
* 'hideOnContentClick', 'enableEscapeButton', 'showCloseButton' are set
* to 'false'
* @param modal
* @return
*/
public Fancybox setModal(Boolean modal) {
this.modal = modal;
return this;
}
public Boolean getCyclic() {
return cyclic;
}
/**
* When true, galleries will be cyclic, allowing you to keep pressing next/back.
* @param cyclic
* @return
*/
public Fancybox setCyclic(Boolean cyclic) {
this.cyclic = cyclic;
return this;
}
public Boolean getScrolling() {
return scrolling;
}
/**
* Set the overflow CSS property to create or hide scrollbars. Can be set to 'auto', 'yes', or 'no'
* @param scrolling
* @return
*/
public Fancybox setScrolling(Boolean scrolling) {
this.scrolling = scrolling;
return this;
}
public Integer getWidth() {
return width;
}
/**
* Width for content types 'iframe' and 'swf'. Also set for inline content if 'autoDimensions' is set to 'false'
* @param width
* @return
*/
public Fancybox setWidth(Integer width) {
this.width = width;
return this;
}
public Integer getHeight() {
return height;
}
/**
* Height for content types 'iframe' and 'swf'. Also set for inline content if 'autoDimensions' is set to 'false'
* @param height
* @return
*/
public Fancybox setHeight(Integer height) {
this.height = height;
return this;
}
public Boolean getAutoScale() {
return autoScale;
}
/**
* If true, FancyBox is scaled to fit in viewport
* @param autoScale
* @return
*/
public Fancybox setAutoScale(Boolean autoScale) {
this.autoScale = autoScale;
return this;
}
public Boolean getAutoDimensions() {
return autoDimensions;
}
/**
* For inline and ajax views, resizes the view to the element recieves.
* Make sure it has dimensions otherwise this will give unexpected results
* @param autoDimensions
* @return
*/
public Fancybox setAutoDimensions(Boolean autoDimensions) {
this.autoDimensions = autoDimensions;
return this;
}
public Boolean getCenterOnScroll() {
return centerOnScroll;
}
/**
* When true, FancyBox is centered while scrolling page
* @param centerOnScroll
* @return
*/
public Fancybox setCenterOnScroll(Boolean centerOnScroll) {
this.centerOnScroll = centerOnScroll;
return this;
}
public Boolean getHideOnOverlayClick() {
return hideOnOverlayClick;
}
/**
* Toggle if clicking the overlay should close FancyBox
* @param hideOnOverlayClick
* @return
*/
public Fancybox setHideOnOverlayClick(Boolean hideOnOverlayClick) {
this.hideOnOverlayClick = hideOnOverlayClick;
return this;
}
public Boolean getHideOnContentClick() {
return hideOnContentClick;
}
/**
* Toggle if clicking the content should close FancyBox
* @param hideOnContentClick
* @return
*/
public Fancybox setHideOnContentClick(Boolean hideOnContentClick) {
this.hideOnContentClick = hideOnContentClick;
return this;
}
public Boolean getOverlayShow() {
return overlayShow;
}
/**
* Toggle overlay
* @param overlayShow
* @return
*/
public Fancybox setOverlayShow(Boolean overlayShow) {
this.overlayShow = overlayShow;
return this;
}
public Float getOverlayOpacity() {
return overlayOpacity;
}
/**
* Opacity of the overlay (from 0 to 1; default - 0.3)
* @param overlayOpacity
* @return
*/
public Fancybox setOverlayOpacity(Float overlayOpacity) {
this.overlayOpacity = overlayOpacity;
return this;
}
public String getOverlayColor() {
return overlayColor;
}
/**
* Color of the overlay
* @param overlayColor
* @return
*/
public Fancybox setOverlayColor(String overlayColor) {
this.overlayColor = overlayColor;
return this;
}
public Boolean getTitleShow() {
return titleShow;
}
/**
* Toggle title
* @param titleShow
* @return
*/
public Fancybox setTitleShow(Boolean titleShow) {
this.titleShow = titleShow;
return this;
}
public TitlePosition getTitlePosition() {
return titlePosition;
}
/**
* The position of title. Can be set to 'outside', 'inside' or 'over'
* @param titlePosition
* @return
*/
public Fancybox setTitlePosition(TitlePosition titlePosition) {
this.titlePosition = titlePosition;
return this;
}
public FancyboxTransition getTransitionIn() {
return transitionIn == null ? null : FancyboxTransition.valueOf(transitionIn);
}
/**
* The transition type. Can be set to 'elastic', 'fade' or 'none'
* @param transitionIn
* @return
*/
public Fancybox setTransitionIn(FancyboxTransition transitionIn) {
this.transitionIn = transitionIn.name();
return this;
}
public FancyboxTransition getTransitionOut() {
return transitionOut == null ? null : FancyboxTransition.valueOf(transitionOut);
}
/**
* The transition type. Can be set to 'elastic', 'fade' or 'none'
* @param transitionOut
* @return
*/
public Fancybox setTransitionOut(FancyboxTransition transitionOut) {
this.transitionOut = transitionOut.name();
return this;
}
public Integer getSpeedIn() {
return speedIn;
}
/**
* Speed of the fade and elastic transitions, in milliseconds
* @param speedIn
* @return
*/
public Fancybox setSpeedIn(Integer speedIn) {
this.speedIn = speedIn;
return this;
}
public Integer getSpeedOut() {
return speedOut;
}
/**
* Speed of the fade and elastic transitions, in milliseconds
* @return
*/
public Fancybox setSpeedOut(Integer speedOut) {
this.speedOut = speedOut;
return this;
}
public Integer getChangeSpeed() {
return changeSpeed;
}
/**
* Speed of resizing when changing gallery items, in milliseconds
* @return
*/
public Fancybox setChangeSpeed(Integer changeSpeed) {
this.changeSpeed = changeSpeed;
return this;
}
public String getChangeFade() {
return changeFade;
}
/**
* Speed of the content fading while changing gallery items
* @param changeFade
* @return
*/
public Fancybox setChangeFade(String changeFade) {
this.changeFade = changeFade;
return this;
}
public String getEasingIn() {
return easingIn;
}
/**
* Easing used for elastic animations
* @param easingIn
* @return
*/
public Fancybox setEasingIn(String easingIn) {
this.easingIn = easingIn;
return this;
}
public String getEasingOut() {
return easingOut;
}
/**
* Easing used for elastic animations
* @param easingIn
* @return
*/
public Fancybox setEasingOut(String easingOut) {
this.easingOut = easingOut;
return this;
}
public Boolean getShowCloseButton() {
return showCloseButton;
}
/**
* Toggle close button
* @param showCloseButton
* @return
*/
public Fancybox setShowCloseButton(Boolean showCloseButton) {
this.showCloseButton = showCloseButton;
return this;
}
public Boolean getShowNavArrows() {
return showNavArrows;
}
/**
* Toggle navigation arrows
* @param showNavArrows
* @return
*/
public Fancybox setShowNavArrows(Boolean showNavArrows) {
this.showNavArrows = showNavArrows;
return this;
}
public Boolean getEnableEscapeButton() {
return enableEscapeButton;
}
/**
* Toggle if pressing Esc button closes FancyBox
* @param enableEscapeButton
* @return
*/
public Fancybox setEnableEscapeButton(Boolean enableEscapeButton) {
this.enableEscapeButton = enableEscapeButton;
return this;
}
public String getOnStart() {
return onStart;
}
/**
* Will be called right before attempting to load the content
* @param onStart
* @return
*/
public Fancybox setOnStart(String onStart) {
this.onStart = onStart;
return this;
}
public String getOnCancel() {
return onCancel;
}
/**
* Will be called after loading is canceled
* @param onCancel
* @return
*/
public Fancybox setOnCancel(String onCancel) {
this.onCancel = onCancel;
return this;
}
public String getOnComplete() {
return onComplete;
}
/**
* Will be called once the content is displayed
* @param onComplete
* @return
*/
public Fancybox setOnComplete(String onComplete) {
this.onComplete = onComplete;
return this;
}
public String getOnCleanup() {
return onCleanup;
}
/**
* Will be called just before closing
* @param onCleanup
* @return
*/
public Fancybox setOnCleanup(String onCleanup) {
this.onCleanup = onCleanup;
return this;
}
public String getOnClosed() {
return onClosed;
}
/**
* Will be called once FancyBox is closed
* @param onClosed
* @return
*/
public Fancybox setOnClosed(String onClosed) {
this.onClosed = onClosed;
return this;
}
public IPrivilege getRenderPrivilege() {
return IPrivilege.NULL;
}
public IPrivilege getEnablePrivilege() {
return IPrivilege.NULL;
}
}