com.cgxlib.core.component.affix.Affix.java Source code

Java tutorial

Introduction

Here is the source code for com.cgxlib.core.component.affix.Affix.java

Source

package com.cgxlib.core.component.affix;

/*
 * #%L
 * CGXlib
 * %%
 * Copyright (C) 2016 CGXlib (http://www.cgxlib.com)
 * %%
 * 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.
 * #L%
 */

import com.cgxlib.core.*;
import com.cgxlib.xq.client.Function;
import com.cgxlib.xq.client.XQ;
import com.cgxlib.xq.client.js.JsUtils;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.Timer;

import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class Affix extends CGXComponentBase<AffixView.ViewHandler> {

    public static final Class<Affix> CGX = CGXlib.registerPlugin(Affix.class,
            new CGXPlugin<Affix, AffixView.ViewHandler>() {
                @Override
                public List<HTML5DataAPILoader> autoLoaders() {
                    HTML5DataAPILoader selector = GWT.create(AffixHTML5Loader.class);

                    List<HTML5DataAPILoader> selectors = new ArrayList<HTML5DataAPILoader>();
                    selectors.add(selector);

                    return selectors;
                }

                @Override
                public Affix init(CGXComponentBase xq,
                        ViewHandlerFactory<? extends AffixView.ViewHandler> factory) {
                    return createOrGetData(xq, factory.make());
                }

                @Override
                public Affix init(XQ xq) {
                    return createOrGetData(xq, null);
                }
            });
    protected AffixOptions options;
    protected XQ $target;
    protected AffixOffset offset;
    protected AffixedState affixed = AffixedState.initializing;
    protected Integer unpin;
    protected Integer pinnedOffset;
    protected AffixView.ViewHandler viewHandler;

    protected Affix(XQ xq) {
        this(xq, new AffixBSViewHandler());
    }

    protected Affix(XQ xq, AffixView.ViewHandler viewHandler) {
        super(xq, viewHandler);
    }

    protected static Affix createOrGetData(XQ xq, AffixView.ViewHandler viewHandler) {
        String name = "cgx.affix";
        Affix comp = xq.data(name);
        if (comp == null) {
            if (viewHandler == null) {
                comp = new Affix(xq);
            } else {
                comp = new Affix(xq, viewHandler);
            }
            xq.data(name, comp);
        }

        return comp;
    }

    @Override
    protected void apply(XQ xq, AffixView.ViewHandler viewHandler) {
        viewHandler.init(this);
        this.viewHandler = viewHandler;
    }

    public Affix affix() {
        return affix(new AffixOptions());
    }

    public Affix affix(AffixOptions affixOptions) {
        options = affixOptions;
        offset = affixOptions.offset();
        if (offset == null) {
            offset = new AffixOffset();
        }

        String targetStr = this.attr("data-target");
        if (targetStr != null && targetStr.length() > 0) {
            options.target($(targetStr));
        }
        $target = options.target();

        $target.on("scroll.cgx.affix.data-api", new Function() {
            @Override
            public void f() {
                Affix.this.affixCheckPosition();
            }
        });
        $target.on("click.cgx.affix.data-api", new Function() {
            @Override
            public void f() {
                Affix.this.checkPositionWithEventLoop();
            }
        });

        String offsetTopStr = this.attr("data-offset-top");
        String offsetBottomStr = this.attr("data-offset-bottom");
        String offsetStr = this.attr("data-offset");

        try {
            if (offsetStr != null && offsetStr.length() > 0) {
                int offsetInt = Integer.parseInt(offsetBottomStr);
                offset.bottom(offsetInt);
                offset.top(offsetInt);
            }
            if (offsetBottomStr != null && offsetBottomStr.length() > 0) {
                offset.bottom(Integer.parseInt(offsetBottomStr));
            }
            if (offsetTopStr != null && offsetTopStr.length() > 0) {
                offset.top(Integer.parseInt(offsetTopStr));
            }
        } catch (Exception ex) {
        }

        affixCheckPosition();

        return this;
    }

    public Affix affixCheckPosition() {
        if (!viewHandler.isVisible()) {
            return this;
        }

        int height = viewHandler.height();
        int scrollHeight = viewHandler.scrollHeight();
        Integer offsetTop = offset.top();
        Integer offsetBottom = offset.bottom();

        AffixedState affix = getState(scrollHeight, height, offsetTop, offsetBottom);

        if (affixed != affix) {
            if (unpin != null) {
                viewHandler.clearTop();
            }

            boolean include = affix != null && affix != AffixedState.initializing;
            String affixType = "affix" + (include ? "-" + affix.name() : "");
            NativeEvent e = CGXHelper.createNativeEvent(affixType, "cgx.affix");
            this.trigger(e);

            if (JsUtils.isDefaultPrevented(e)) {
                return this;
            }

            affixed = affix;
            unpin = affix == AffixedState.bottom ? getPinnedOffset() : null;

            viewHandler.affixReset();

            viewHandler.affixSetType(affixType);
            this.trigger(affixType.replace("affix", "affixed") + ".cgx.affix");
        }

        if (affix == AffixedState.bottom) {
            this.offset().top = scrollHeight - height - offsetBottom;
        }

        return this;
    }

    protected AffixedState getState(Integer scrollHeight, Integer height, Integer offsetTop, Integer offsetBottom) {
        int scrollTop = this.$target.scrollTop();
        Offset position = this.offset();
        int targetHeight = this.$target.height();

        if (offsetTop != null && this.affixed == AffixedState.top) {
            return scrollTop < offsetTop ? AffixedState.top : null;
        }

        if (this.affixed == AffixedState.bottom) {
            if (offsetTop != null) {
                return (scrollTop + this.unpin <= position.top) ? null : AffixedState.bottom;
            }
            return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? null : AffixedState.bottom;
        }

        boolean initializing = this.affixed == AffixedState.initializing;
        int colliderTop = initializing ? scrollTop : position.top;
        int colliderHeight = initializing ? targetHeight : height;

        if (offsetTop != null && scrollTop <= offsetTop) {
            return AffixedState.top;
        }
        if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) {
            return AffixedState.bottom;
        }

        return null;
    }

    protected void checkPositionWithEventLoop() {
        Timer timer = new Timer() {
            @Override
            public void run() {
                affixCheckPosition();
            }
        };
        timer.schedule(1);
    }

    protected Integer getPinnedOffset() {
        if (pinnedOffset != null) {
            return pinnedOffset;
        }

        viewHandler.affixReset();
        viewHandler.affixSet();
        int scrollTop = viewHandler.targetScrollTop($target);
        Offset position = this.offset();
        pinnedOffset = position.top - scrollTop;

        return pinnedOffset;
    }

    protected enum AffixedState {
        initializing, top, bottom;
    }
}