nl.mpi.tg.eg.experiment.client.view.TimedStimulusView.java Source code

Java tutorial

Introduction

Here is the source code for nl.mpi.tg.eg.experiment.client.view.TimedStimulusView.java

Source

/*
 * Copyright (C) 2015 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package nl.mpi.tg.eg.experiment.client.view;

import com.google.gwt.core.client.Duration;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.MediaElement;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.CanPlayThroughEvent;
import com.google.gwt.event.dom.client.CanPlayThroughHandler;
import com.google.gwt.event.dom.client.EndedEvent;
import com.google.gwt.event.dom.client.EndedHandler;
import com.google.gwt.event.dom.client.ErrorEvent;
import com.google.gwt.event.dom.client.ErrorHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.media.client.Video;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.Widget;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import nl.mpi.tg.eg.experiment.client.exception.AudioException;
import nl.mpi.tg.eg.experiment.client.listener.AudioEventListner;
import nl.mpi.tg.eg.experiment.client.listener.AudioExceptionListner;
import nl.mpi.tg.eg.experiment.client.listener.CancelableStimulusListener;
import nl.mpi.tg.eg.experiment.client.listener.PresenterEventListner;
import nl.mpi.tg.eg.experiment.client.listener.SingleShotEventListner;
import nl.mpi.tg.eg.experiment.client.listener.StimulusButton;
import nl.mpi.tg.eg.frinex.common.listener.TimedStimulusListener;
import nl.mpi.tg.eg.experiment.client.model.StimulusFreeText;
import nl.mpi.tg.eg.experiment.client.model.UserId;
import nl.mpi.tg.eg.experiment.client.service.AudioPlayer;
import nl.mpi.tg.eg.experiment.client.service.LocalStorage;
import nl.mpi.tg.eg.experiment.client.service.TimedEventMonitor;
import nl.mpi.tg.eg.experiment.client.service.VideoPlayer;
import nl.mpi.tg.eg.frinex.common.model.Stimulus;

/**
 * @since Jun 26, 2015 10:24:34 AM (creation date)
 * @author Peter Withers <peter.withers@mpi.nl>
 */
public class TimedStimulusView extends ComplexView {

    private final Map<String, AudioPlayer> audioList = new HashMap<>();
    private StimulusGrid stimulusGrid = null;
    private final Map<String, Video> videoList = new HashMap<>();
    private final List<Timer> timerList = new ArrayList<>();
    private final List<CancelableStimulusListener> cancelableListnerList = new ArrayList<>();

    public TimedStimulusView() {
        super();
    }

    public void startGrid(final String menuOuter) {
        outerPanel.setStylePrimaryName(menuOuter);
        stimulusGrid = new StimulusGrid(domHandlerArray);
        outerPanel.add(stimulusGrid);
    }

    public void endGrid() {
        stimulusGrid = null;
    }

    public StimulusButton addStringItem(final PresenterEventListner menuItemListerner, final String labelString,
            final int rowIndex, final int columnIndex, final int hotKeyIndex) {
        return stimulusGrid.addStringItem(menuItemListerner, labelString, rowIndex, columnIndex, hotKeyIndex);
    }

    public StimulusButton addImageItem(final PresenterEventListner menuItemListerner, final SafeUri imagePath,
            final int rowIndex, final int columnIndex, final String widthString, final String styleName,
            final int hotKeyIndex) {
        return stimulusGrid.addImageItem(menuItemListerner, imagePath, rowIndex, columnIndex, widthString,
                styleName, hotKeyIndex);
    }

    public void preloadImage(final TimedEventMonitor timedEventMonitor, SafeUri imagePath,
            final TimedStimulusListener timedStimulusListener) {
        if (timedEventMonitor != null) {
            timedEventMonitor.registerEvent("preloadImage");
        }
        final Image image = new Image(imagePath);
        image.setVisible(false);
        image.addLoadHandler(new LoadHandler() {

            @Override
            public void onLoad(LoadEvent event) {
                if (timedEventMonitor != null) {
                    timedEventMonitor.registerEvent("onPreloadImage");
                }
                timedStimulusListener.postLoadTimerFired();
            }
        });
        getActivePanel().add(image);
    }

    public void addBackgroundImage(final SafeUri imagePath, final String styleName, final int postLoadMs,
            final TimedStimulusListener timedStimulusListener) {
        //        final Image image = new Image(imagePath);
        //            this.getElement().getStyle().setBackgroundColor("green");
        if (imagePath == null) {
            this.getElement().getStyle().clearBackgroundImage();
        } else {
            this.getElement().getStyle().setBackgroundImage("url(" + imagePath.asString() + ")");
        }
        this.getElement().getStyle().setProperty("backgroundRepeat", "no-repeat");
        //            this.getElement().getStyle().setProperty("backgroundSize", "100% 100%");
        this.getElement().getStyle().setProperty("backgroundRepeat", "no-repeat");
        this.getElement().getStyle().setProperty("backgroundPosition", "50% 50%");
        // remove the custom styles but keep the page width style
        this.setStyleName(this.getStyleName().contains("normalWidth") ? "normalWidth" : "narrowWidth");
        if (styleName != null && !styleName.isEmpty()) {
            this.addStyleName(styleName);
        } else {
            this.getElement().getStyle().setProperty("backgroundSize", "cover");
            //            resizeView(); // this is to put back the screen size styles
        }
        //        image.addLoadHandler(new LoadHandler() {
        //
        //            @Override
        //            public void onLoad(LoadEvent event) {
        Timer timer = new Timer() {
            @Override
            public void run() {
                timedStimulusListener.postLoadTimerFired();
            }
        };
        timerList.add(timer);
        timer.schedule(postLoadMs);
        //            }
        //        });
        //        outerPanel.add(image);
    }

    @Override
    public void clearPageAndTimers(String styleName) {
        stopListeners();
        stopTimers();
        stopAudio(); // note that stop audio here is probably redundant 
        stopAllMedia();
        this.getElement().getStyle().setBackgroundImage(null);
        super.clearPageAndTimers(styleName);
        videoList.clear();
        audioList.clear();
    }

    public StimulusButton addTimedImage(final TimedEventMonitor timedEventMonitor, SafeUri imagePath,
            final String styleName, final int postLoadMs, final CancelableStimulusListener onLoadStimulusListener,
            final CancelableStimulusListener postLoadMsListener,
            final CancelableStimulusListener failedStimulusListener,
            final CancelableStimulusListener clickedStimulusListener) {
        cancelableListnerList.add(onLoadStimulusListener);
        cancelableListnerList.add(postLoadMsListener);
        cancelableListnerList.add(failedStimulusListener);
        cancelableListnerList.add(clickedStimulusListener);
        if (timedEventMonitor != null) {
            timedEventMonitor.registerEvent("addTimedImage");
        }
        final Image image = new Image(imagePath);
        if (styleName != null) {
            image.addStyleName(styleName);
        }
        image.setVisible(false);
        image.addErrorHandler(new ErrorHandler() {
            @Override
            public void onError(ErrorEvent event) {
                if (timedEventMonitor != null) {
                    timedEventMonitor.registerEvent("imageOnError");
                }
                failedStimulusListener.postLoadTimerFired();
            }
        });
        image.addLoadHandler(new LoadHandler() {

            @Override
            public void onLoad(LoadEvent event) {
                if (timedEventMonitor != null) {
                    timedEventMonitor.registerEvent("imageOnLoad");
                }
                image.setVisible(true);
                if (onLoadStimulusListener != null) {
                    onLoadStimulusListener.postLoadTimerFired();
                }
                Timer timer = new Timer() {
                    @Override
                    public void run() {
                        postLoadMsListener.postLoadTimerFired();
                    }
                };
                timerList.add(timer);
                timer.schedule(postLoadMs);
            }
        });
        final SingleShotEventListner singleShotEventListner;
        if (clickedStimulusListener != null) {
            singleShotEventListner = new SingleShotEventListner() {

                @Override
                protected void singleShotFired() {
                    clickedStimulusListener.postLoadTimerFired();
                    resetSingleShot();
                }
            };
            image.addClickHandler(singleShotEventListner);
            image.addTouchStartHandler(singleShotEventListner);
            image.addTouchMoveHandler(singleShotEventListner);
            image.addTouchEndHandler(singleShotEventListner);
        } else {
            singleShotEventListner = null;
        }
        getActivePanel().add(image);
        return new StimulusButton() {
            @Override
            public Widget getWidget() {
                return image;
            }

            @Override
            public void addStyleName(String styleName) {
                image.addStyleName(styleName);
            }

            @Override
            public void removeStyleName(String styleName) {
                image.removeStyleName(styleName);
            }

            @Override
            public void setEnabled(boolean enabled) {
                if (singleShotEventListner != null) {
                    singleShotEventListner.setEnabled(enabled);
                }
            }

            @Override
            public boolean isEnabled() {
                if (singleShotEventListner != null) {
                    return singleShotEventListner.isEnabled();
                } else {
                    return false;
                }
            }

            @Override
            public void setVisible(boolean visible) {
                image.setVisible(visible);
            }

            @Override
            public void triggerSingleShotEventListner() {
                clickedStimulusListener.postLoadTimerFired();
            }
        };
    }

    @Deprecated
    public void addTimedImage(final TimedEventMonitor timedEventMonitor, SafeUri imagePath, int percentOfPage,
            int maxHeight, int maxWidth, final String animateStyle, final Integer fixedPositionY,
            final int postLoadMs, final CancelableStimulusListener shownStimulusListener,
            final CancelableStimulusListener loadedStimulusListener,
            final CancelableStimulusListener failedStimulusListener,
            final CancelableStimulusListener clickedStimulusListener) {
        cancelableListnerList.add(shownStimulusListener);
        cancelableListnerList.add(loadedStimulusListener);
        cancelableListnerList.add(failedStimulusListener);
        cancelableListnerList.add(clickedStimulusListener);
        if (timedEventMonitor != null) {
            timedEventMonitor.registerEvent("addTimedImage");
        }
        final Image image = new Image(imagePath);
        if (animateStyle != null && !animateStyle.isEmpty()) {
            image.addStyleName(animateStyle);
            if (fixedPositionY != null) {
                image.getElement().getStyle().setLeft(fixedPositionY, Style.Unit.PCT);
            }
        }
        addSizeAttributes(image.getElement(), percentOfPage, maxHeight, maxWidth);
        image.addErrorHandler(new ErrorHandler() {
            @Override
            public void onError(ErrorEvent event) {
                if (timedEventMonitor != null) {
                    timedEventMonitor.registerEvent("onError");
                }
                if (failedStimulusListener != null) {
                    failedStimulusListener.postLoadTimerFired();
                }
            }
        });
        image.addLoadHandler(new LoadHandler() {

            @Override
            public void onLoad(LoadEvent event) {
                if (timedEventMonitor != null) {
                    timedEventMonitor.registerEvent("onLoad");
                }
                if (shownStimulusListener != null) {
                    shownStimulusListener.postLoadTimerFired();
                }
                Timer timer = new Timer() {
                    @Override
                    public void run() {
                        loadedStimulusListener.postLoadTimerFired();
                    }
                };
                timerList.add(timer);
                timer.schedule(postLoadMs);
            }
        });
        if (clickedStimulusListener != null) {
            final SingleShotEventListner singleShotEventListner = new SingleShotEventListner() {

                @Override
                protected void singleShotFired() {
                    clickedStimulusListener.postLoadTimerFired();
                    resetSingleShot();
                }
            };
            image.addClickHandler(singleShotEventListner);
            image.addTouchStartHandler(singleShotEventListner);
            image.addTouchMoveHandler(singleShotEventListner);
            image.addTouchEndHandler(singleShotEventListner);
        }
        final HTMLPanel htmlPanel = new HTMLPanel("");
        htmlPanel.setStylePrimaryName("gridCell");
        htmlPanel.add(image);
        getActivePanel().add(htmlPanel);
    }

    public void addSvgImage(String svgContent, int percentWidth) {
        final HTMLPanel htmlPanel = new HTMLPanel(svgContent);
        htmlPanel.setWidth(percentWidth + "%");
        getActivePanel().add(htmlPanel);
    }

    public StimulusFreeText addStimulusValidation(final LocalStorage localStorage, final UserId userId,
            final Stimulus currentStimulus, final String postName, final String validationRegex,
            final String validationChallenge, final int dataChannel) {
        final Label errorLabel = new Label(validationChallenge);
        errorLabel.setStylePrimaryName("metadataErrorMessage");
        errorLabel.setVisible(false);
        getActivePanel().add(errorLabel);
        return new StimulusFreeText() {
            @Override
            public Stimulus getStimulus() {
                return currentStimulus;
            }

            @Override
            public String getPostName() {
                return postName;
            }

            @Override
            public String getResponseTimes() {
                return null;
            }

            @Override
            public String getValue() {
                JSONObject storedStimulusJSONObject = localStorage.getStoredJSONObject(userId, currentStimulus);
                final JSONValue codeResponse = (storedStimulusJSONObject == null) ? null
                        : storedStimulusJSONObject.get(postName);
                return (codeResponse != null) ? codeResponse.isString().stringValue() : null;
            }

            @Override
            public boolean isValid() {
                final String currentValue = getValue();
                final boolean isValid;
                if (currentValue != null) {
                    isValid = currentValue.matches(validationRegex);
                } else {
                    isValid = false;
                }
                if (isValid) {
                    errorLabel.setVisible(false);
                    return true;
                } else {
                    errorLabel.setText(validationChallenge);
                    errorLabel.setVisible(true);
                    return false;
                }
            }

            @Override
            public int getDataChannel() {
                return dataChannel;
            }

            @Override
            public void setFocus(boolean wantsFocus) {
                // todo: we could use a scroll to method to focus the message here
            }
        };
    }

    public StimulusFreeText addStimulusFreeText(final Stimulus stimulus, final String postName,
            final String validationRegex, final String keyCodeChallenge, final String validationChallenge,
            final String allowedCharCodes, final SingleShotEventListner enterKeyListner, final int hotKey,
            final String styleName, final int dataChannel, final String textValue) {
        final int inputLengthLimit = 1000; // this coud be a parameter from the configuraiton file, however the validationRegex can also limit the input length.
        // perhaps consider removing allowedCharCodes and doing a regex test on each key?
        final Label errorLabel = new Label(validationChallenge);
        errorLabel.setStylePrimaryName("metadataErrorMessage");
        errorLabel.setVisible(false);
        getActivePanel().add(errorLabel);
        final Duration duration = new Duration();
        final StringBuilder responseTimes = new StringBuilder();
        final TextArea textBox = new TextArea();
        if (textValue != null) {
            textBox.setText(textValue);
        }
        if (hotKey == KeyCodes.KEY_ENTER) {
            textBox.setVisibleLines(1);
            textBox.getElement().getStyle().setProperty("minHeight", "26px");
        }
        if (styleName != null) {
            textBox.addStyleName(styleName);
        }
        textBox.setStylePrimaryName("metadataOK");
        getActivePanel().add(textBox);
        textBox.setFocus(true);
        textBox.addKeyPressHandler(new KeyPressHandler() {
            @Override
            public void onKeyPress(KeyPressEvent event) {
                final char charCode = event.getCharCode();
                if (charCode > -1 && charCode == hotKey) {
                    event.getNativeEvent().preventDefault();
                    enterKeyListner.eventFired();
                    errorLabel.setVisible(false);
                } else if (charCode == 0) {
                    // firefox needs these events to be handled by allowing the event to pass
                    return;
                } else if (textBox.getText().length() > inputLengthLimit) {
                    event.getNativeEvent().preventDefault();
                    // todo: update this to give a sensible message
                    errorLabel.setText(
                            keyCodeChallenge.replace("<keycode>", "" + inputLengthLimit) + validationChallenge);
                    errorLabel.setVisible(true);
                } else if (allowedCharCodes != null) {
                    if (0 > allowedCharCodes.indexOf(charCode)) {
                        event.getNativeEvent().preventDefault();
                        final char invertedCaseCode = (Character.isLowerCase(charCode))
                                ? Character.toUpperCase(charCode)
                                : Character.toLowerCase(charCode);
                        if (0 > allowedCharCodes.indexOf(invertedCaseCode)) {
                            // if the key is not allowed, then show a message
                            //                            final String messageString = "The key '<keycode>' is not allowed. " + validationChallenge;
                            errorLabel.setText(
                                    keyCodeChallenge.replace("<keycode>", "" + charCode) + validationChallenge);
                            errorLabel.setVisible(true);
                        } else {
                            responseTimes.append(duration.elapsedMillis());
                            responseTimes.append(",");
                            // if the case is not allowed, then modify the case to what is
                            final int cursorPos = textBox.getCursorPos();
                            String pretext = textBox.getText().substring(0, cursorPos);
                            String posttext = textBox.getText().substring(textBox.getCursorPos());
                            textBox.setText(pretext + invertedCaseCode + posttext);
                            textBox.setCursorPos(cursorPos + 1);
                            errorLabel.setVisible(false);
                        }
                    } else {
                        responseTimes.append(duration.elapsedMillis());
                        responseTimes.append(",");
                        errorLabel.setVisible(false);
                    }
                } else {
                    responseTimes.append(duration.elapsedMillis());
                    responseTimes.append(",");
                    errorLabel.setVisible(false);
                }
            }
        });
        final StimulusFreeText stimulusFreeText = new StimulusFreeText() {
            @Override
            public Stimulus getStimulus() {
                return stimulus;
            }

            @Override
            public String getValue() {
                return textBox.getValue();
            }

            @Override
            public String getResponseTimes() {
                return responseTimes.substring(0, responseTimes.length() - 1);
            }

            @Override
            public boolean isValid() {
                if ((getValue().length() <= inputLengthLimit + 2)
                        && (validationRegex == null || getValue().matches(validationRegex))) {
                    textBox.setStylePrimaryName("metadataOK");
                    errorLabel.setVisible(false);
                    return true;
                } else {
                    textBox.setStylePrimaryName("metadataError");
                    errorLabel.setText(validationChallenge);
                    errorLabel.setVisible(true);
                    textBox.setFocus(true);
                    return false;
                }
            }

            @Override
            public String getPostName() {
                return postName;
            }

            @Override
            public int getDataChannel() {
                return dataChannel;
            }

            @Override
            public void setFocus(boolean wantsFocus) {
                textBox.setFocus(wantsFocus);
            }

        };

        return stimulusFreeText;
    }

    public void addTimedAudio(final TimedEventMonitor timedEventMonitor, final SafeUri oggPath,
            final SafeUri mp3Path, boolean showPlaybackIndicator,
            final CancelableStimulusListener loadedStimulusListener,
            final CancelableStimulusListener failedStimulusListener,
            final CancelableStimulusListener playbackStartedStimulusListener,
            final CancelableStimulusListener playedStimulusListener, final boolean autoPlay, final String mediaId) {
        cancelableListnerList.add(loadedStimulusListener);
        cancelableListnerList.add(failedStimulusListener);
        cancelableListnerList.add(playbackStartedStimulusListener);
        cancelableListnerList.add(playedStimulusListener);
        try {
            if (timedEventMonitor != null) {
                timedEventMonitor.registerEvent("addTimedAudio");
            }
            final AudioPlayer audioPlayer = new AudioPlayer(new AudioExceptionListner() {
                @Override
                public void audioExceptionFired(AudioException audioException) {
                    if (timedEventMonitor != null) {
                        timedEventMonitor.registerEvent("audioExceptionFired");
                    }
                    failedStimulusListener.postLoadTimerFired();
                }
            }, oggPath, mp3Path, autoPlay);
            audioList.put(mediaId, audioPlayer);
            //        audioPlayer.stopAll(); // Note that this stop all change will be a change in default behaviour, however there shouldn't be any instances where this is depended on, but that should be checked
            final Label playbackIndicator = new Label();
            final Timer playbackIndicatorTimer = new Timer() {
                public void run() {
                    playbackIndicator.setText("CurrentTime: " + audioPlayer.getCurrentTime());
                    //                    playbackIndicator.setWidth();
                    this.schedule(100);
                }
            };
            timerList.add(playbackIndicatorTimer);
            if (showPlaybackIndicator) {
                playbackIndicator.setStylePrimaryName("playbackIndicator");
                getActivePanel().add(playbackIndicator);
                playbackIndicatorTimer.schedule(500);
            }
            audioPlayer.setEventListner(new AudioEventListner() {
                @Override
                public void audioLoaded() {
                    if (timedEventMonitor != null) {
                        timedEventMonitor.registerEvent("audioLoaded");
                    }
                    loadedStimulusListener.postLoadTimerFired();
                }

                @Override
                public void audioStarted() {
                    if (timedEventMonitor != null) {
                        timedEventMonitor.registerEvent("audioStarted");
                    }
                    playbackStartedStimulusListener.postLoadTimerFired();
                }

                @Override
                public void audioFailed() {
                    if (timedEventMonitor != null) {
                        timedEventMonitor.registerEvent("audioFailed");
                    }
                    failedStimulusListener.postLoadTimerFired();
                }

                @Override
                public void audioEnded() {
                    if (timedEventMonitor != null) {
                        timedEventMonitor.registerEvent("audioEnded");
                        timedEventMonitor.registerMediaLength(mediaId,
                                (long) (audioPlayer.getCurrentTime() * 1000));
                    }
                    //                    playbackIndicatorTimer.cancel();
                    //                    playbackIndicator.removeFromParent();
                    //                    audioPlayer.setEventListner(null); // prevent multiple triggering
                    playedStimulusListener.postLoadTimerFired();
                }
            });
        } catch (AudioException audioException) {
            failedStimulusListener.postLoadTimerFired();
        }
    }

    public Element addTimedVideo(final TimedEventMonitor timedEventMonitor, final SafeUri oggPath,
            final SafeUri ogvPath, final SafeUri mp4Path, int percentOfPage, int maxHeight, int maxWidth,
            final String styleName, final boolean autoPlay, final boolean loop, final boolean showControls,
            final CancelableStimulusListener loadedStimulusListener,
            final CancelableStimulusListener failedStimulusListener,
            final CancelableStimulusListener playbackStartedStimulusListener,
            final CancelableStimulusListener playedStimulusListener, final String mediaId) {
        cancelableListnerList.add(loadedStimulusListener);
        cancelableListnerList.add(failedStimulusListener);
        cancelableListnerList.add(playbackStartedStimulusListener);
        cancelableListnerList.add(playedStimulusListener);
        final Element videoElement;
        if (timedEventMonitor != null) {
            timedEventMonitor.registerEvent("addTimedVideo");
        }
        final Video video = Video.createIfSupported();
        if (video == null) {
            failedStimulusListener.postLoadTimerFired();
            videoElement = null;
        } else {
            video.setAutoplay(autoPlay);
            videoList.put(mediaId, video);
            //            video.setPoster(poster);
            video.setControls(showControls);
            video.setPreload(MediaElement.PRELOAD_AUTO);
            if (styleName != null && !styleName.isEmpty()) {
                video.addStyleName(styleName);
            } else {
                addSizeAttributes(video.getElement(), percentOfPage, maxHeight, maxWidth);
            }
            getActivePanel().add(video);
            video.addCanPlayThroughHandler(new CanPlayThroughHandler() {
                boolean hasTriggeredOnLoaded = false;

                @Override
                public void onCanPlayThrough(CanPlayThroughEvent event) {
                    if (!hasTriggeredOnLoaded) {
                        if (timedEventMonitor != null) {
                            timedEventMonitor.registerEvent("videoCanPlayThrough");
                        }
                        hasTriggeredOnLoaded = true;
                        loadedStimulusListener.postLoadTimerFired();
                        if (autoPlay) {
                            video.play();
                        }
                        if (video.getError() != null) {
                            // todo: check that this method is functioning correctly and if not then use the method in audioPlayer.@nl.mpi.tg.eg.experiment.client.service.AudioPlayer::onAudioFailed()();
                            failedStimulusListener.postLoadTimerFired();
                        }
                    }
                }
            });
            new VideoPlayer().addNativeCallbacks(timedEventMonitor, video.getVideoElement(),
                    playbackStartedStimulusListener);
            //            todo: move the video handling code from here into the VideoPlayer class, in such a way is it can be used like the AudioPlayer class of the same package
            video.addEndedHandler(new EndedHandler() {
                private boolean triggered = false;

                @Override
                public void onEnded(EndedEvent event) {
                    if (timedEventMonitor != null) {
                        timedEventMonitor.registerEvent("videoEnded");
                        timedEventMonitor.registerMediaLength(mediaId, (long) (video.getCurrentTime() * 1000));
                    }
                    // prevent multiple triggering
                    if (!triggered) {
                        triggered = true;
                        //                        Timer timer = new Timer() {
                        //                            public void run() {
                        playedStimulusListener.postLoadTimerFired();
                        //                            }
                        //                        };
                        //                        timerList.add(timer);
                        //                        timer.schedule(postLoadMs);
                    }
                }
            });
            if (oggPath != null) {
                video.addSource(oggPath.asString(), "video/ogg");
            }
            if (ogvPath != null) {
                video.addSource(ogvPath.asString(), "video/ogg");
            }
            if (mp4Path != null) {
                video.addSource(mp4Path.asString(), "video/mp4");
            }

            if (!autoPlay) {
                video.pause();
                video.setCurrentTime(0);
            }
            video.setLoop(loop);
            video.load();
            if (video.getError() != null) {
                // todo: check that this method is functioning correctly and if not then use the method in audioPlayer.@nl.mpi.tg.eg.experiment.client.service.AudioPlayer::onAudioFailed()();
                failedStimulusListener.postLoadTimerFired();
            }
            videoElement = video.getElement();
        }
        return videoElement;
    }

    public void addAudioPlayerGui(final String mediaId) {
        getActivePanel().add(audioList.get(mediaId).getAudioPlayer());
    }

    // should this be deprecated/removed?
    public void stopAudio() {
        for (AudioPlayer audioPlayer : audioList.values()) {
            if (audioPlayer != null) {
                audioPlayer.stopAll();
            }
        }
    }

    public void stopAllMedia() {
        for (Video video : videoList.values()) {
            if (video != null) {
                video.pause();
            }
        }
        for (AudioPlayer audioPlayer : audioList.values()) {
            if (audioPlayer != null) {
                audioPlayer.stopAll();
            }
        }
    }

    public void stopMedia(final String mediaId) {
        for (String key : videoList.keySet()) {
            if (key.matches(mediaId)) {
                Video video = videoList.get(key);
                if (video != null) {
                    video.pause();
                }
            }
        }
        for (String key : audioList.keySet()) {
            if (key.matches(mediaId)) {
                AudioPlayer audioPlayer = audioList.get(key);
                if (audioPlayer != null) {
                    audioPlayer.stop();
                }
            }
        }
    }

    public void startMedia(final String mediaId) {
        for (String key : videoList.keySet()) {
            if (key.matches(mediaId)) {
                Video video = videoList.get(key);
                if (video != null) {
                    video.play();
                }
            }
        }
        for (String key : audioList.keySet()) {
            if (key.matches(mediaId)) {
                AudioPlayer audioPlayer = audioList.get(key);
                if (audioPlayer != null) {
                    audioPlayer.play();
                }
            }
        }
    }

    public void rewindMedia(final String mediaId) {
        for (String key : videoList.keySet()) {
            if (key.matches(mediaId)) {
                Video video = videoList.get(key);
                if (video != null) {
                    video.setCurrentTime(0);
                }
            }
        }
        for (String key : audioList.keySet()) {
            if (key.matches(mediaId)) {
                AudioPlayer audioPlayer = audioList.get(key);
                if (audioPlayer != null) {
                    audioPlayer.rewind();
                }
            }
        }
    }

    public void stopTimers() {
        for (Timer timer : timerList) {
            if (timer != null) {
                timer.cancel();
            }
        }
        timerList.clear();
    }

    public void stopListeners() {
        for (CancelableStimulusListener listener : cancelableListnerList) {
            if (listener != null) {
                listener.cancel();
            }
        }
        timerList.clear();
    }
}