Skip to content

Commit

Permalink
improve tutorial navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
thomaskrause committed Oct 13, 2014
2 parents c1f6fcb + ae4ee60 commit d6a9ae2
Show file tree
Hide file tree
Showing 40 changed files with 8,756 additions and 6,102 deletions.
22 changes: 18 additions & 4 deletions annis-gui/src/main/java/annis/gui/SearchUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import annis.libgui.media.PDFControllerImpl;
import annis.service.objects.AnnisCorpus;
import annis.service.objects.CorpusConfig;
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.escape.Escaper;
Expand Down Expand Up @@ -250,13 +251,26 @@ private void checkServiceVersion()
= Helper.getAnnisWebResource().path("version").path("revision");
String revisionService = resRevision.get(String.class);
String revisionGUI = VersionInfo.getBuildRevision();

if(!revisionService.equals(revisionGUI))
{
{
// shorten the strings
String commonPrefix = Strings.commonPrefix(revisionService,
revisionGUI);
int outputLength = Math.max(6, commonPrefix.length()+2);
String revisionServiceShort = revisionService.substring(0,
Math.min(revisionService.length()-1, outputLength));
String revisionGUIShort = revisionGUI.substring(0,
Math.min(revisionGUI.length()-1, outputLength));

Notification n = new Notification("Different service revision",
"The service uses revision " + revisionService
+ " but the user interface is using revision " + revisionGUI
+ ".",
"The service uses revision <code title=\"" + revisionGUI
+ "\">" + revisionServiceShort
+ "</code> but the user interface is using revision <code title=\""
+ revisionGUI + "\">" + revisionGUIShort
+ "</code>.",
Notification.Type.TRAY_NOTIFICATION);
n.setHtmlContentAllowed(true);
n.setDelayMsec(3000);
n.show(Page.getCurrent());
}
Expand Down
51 changes: 51 additions & 0 deletions annis-gui/src/main/java/annis/gui/components/IframeState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2014 Corpuslinguistic working group Humboldt University Berlin.
*
* 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.
*/
package annis.gui.components;

import com.vaadin.shared.ui.JavaScriptComponentState;

/**
*
* @author Thomas Krause <[email protected]>
*/
public class IframeState extends JavaScriptComponentState
{
private String source;
private Integer lastScrollPos;

public String getSource()
{
return source;
}

public void setSource(String source)
{
this.source = source;
}

public Integer getLastScrollPos()
{
return lastScrollPos;
}

public void setLastScrollPos(Integer lastScrollPos)
{
this.lastScrollPos = lastScrollPos;
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* Copyright 2014 Corpuslinguistic working group Humboldt University Berlin.
*
* 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.
*/
package annis.gui.components;

import com.vaadin.annotations.JavaScript;
import com.vaadin.server.ConnectorResource;
import com.vaadin.server.Resource;
import com.vaadin.ui.AbstractJavaScriptComponent;
import com.vaadin.ui.JavaScriptFunction;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.MenuBar.MenuItem;
import com.vaadin.ui.VerticalLayout;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONArray;
import org.json.JSONException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Embedds a single HTML page and adds navigation to it's headers (if they have
* an id).
*
* This is e.g. usefull for documentation.
*
* @author Thomas Krause <[email protected]>
*/
public class NavigateableSinglePage extends VerticalLayout
{

private final static Logger log = LoggerFactory.getLogger(
NavigateableSinglePage.class);

private final IFrameComponent iframe = new IFrameComponent();

private MenuBar navigation;

private final Map<String, MenuItem> menuItemRegistry = new HashMap<>();

private String lastSelectedID;

private final static Pattern regexHeader = Pattern.compile("h([1-6])");

public NavigateableSinglePage(File localFile, URI externalURI)
{
iframe.setSizeFull();

setSpacing(true);
addComponent(iframe);

setExpandRatio(iframe, 1.0f);

setSource(localFile, externalURI);
}

private void onScroll(String headerID)
{
selectChapterInNavigation(headerID);
}

private void setSource(File localFile, URI externalURI)
{
iframe.getState().setSource(externalURI.toASCIIString());
if (navigation != null)
{
removeComponent(navigation);
}
menuItemRegistry.clear();
navigation = createMenubarFromHTML(localFile, externalURI, menuItemRegistry);
addComponent(navigation, 0);
}

private MenuBar createMenubarFromHTML(File localFile, URI externalURI,
Map<String, MenuItem> idToMenuItem)
{

MenuBar mbNavigation = new MenuBar();
mbNavigation.setStyleName("huge");
MenuItem navRoot = mbNavigation.addItem("Choose topic", null);
navRoot.setStyleName("huge");

try(FileInputStream input = new FileInputStream(localFile))
{
Document doc = Jsoup.parse(input, "UTF-8", externalURI.toASCIIString());

ArrayList<MenuItem> itemPath = new ArrayList<>();
// find all headers that have an ID
for (Element e : doc.getElementsByAttribute("id"))
{
Matcher m = regexHeader.matcher(e.tagName());
if (m.matches())
{
int level = Integer.parseInt(m.group(1)) - 1;

// decide wether to expand the path (one level deeper) or to truncate
if (level == 0)
{
itemPath.clear();
}
else if (itemPath.size() >= level)
{
// truncate
itemPath = new ArrayList<>(itemPath.subList(0, level));
}

if (itemPath.isEmpty() && level > 0)
{
// fill the path with empty elements
for (int i = 0; i < level; i++)
{
itemPath.add(createItem(navRoot, itemPath, "<empty>", null));
}
}
MenuItem item = createItem(navRoot, itemPath, e.text(), e.id());
itemPath.add(item);
idToMenuItem.put(e.id(), item);
}
}
}
catch (IOException ex)
{
log.error("Could not parse iframe source", ex);
}
return mbNavigation;
}

private MenuItem createItem(MenuItem rootItem, List<MenuItem> path,
String caption, String id)
{
MenuItem parent;

if (path.isEmpty())
{
parent = rootItem;
}
else
{
parent = path.get(path.size() - 1);

}
MenuItem child = parent.addItem(caption, new IDSelectionCommand(id));
child.setStyleName("huge");
return child;
}

private void selectChapterInNavigation(String idToSelect)
{
if (navigation != null && menuItemRegistry != null && idToSelect != null)
{
MenuItem navRoot = navigation.getItems().get(0);
MenuItem toSelect = menuItemRegistry.get(idToSelect);

if (toSelect != null)
{
navRoot.setText(toSelect.getText());
toSelect.setStyleName("huge-selected");
if(lastSelectedID != null)
{
MenuItem lastSelectedItem = menuItemRegistry.get(lastSelectedID);
if (lastSelectedItem != null && lastSelectedItem != toSelect)
{
lastSelectedItem.setStyleName("huge");
}
}
lastSelectedID = idToSelect;
}
}
}

private class IDSelectionCommand implements MenuBar.Command
{

private final String id;

public IDSelectionCommand(String id)
{
this.id = id;
}

@Override
public void menuSelected(MenuItem selectedItem)
{
if (id != null)
{
iframe.scrollToElement(id);
selectChapterInNavigation(id);
}
}

}

@JavaScript(
{
"vaadin://jquery.js", "navigateablesinglepage.js"
})
private class IFrameComponent extends AbstractJavaScriptComponent
{

public IFrameComponent()
{
addFunction("scrolled", new JavaScriptFunction()
{

@Override
public void call(JSONArray arguments) throws JSONException
{
onScroll(arguments.getString(0));
getState().setLastScrollPos(arguments.getInt(1));
}
});
}

public void scrollToElement(String id)
{
if(id != null)
{
callFunction("scrollToElement", id);
}
}

public void setSource(Resource res)
{
setResource("content", res);
}

@Override
public final IframeState getState()
{
return (IframeState) super.getState();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ChameleonTheme;
import com.vaadin.ui.themes.ValoTheme;
import de.hu_berlin.german.korpling.saltnpepper.salt.saltCommon.SaltProject;
import de.hu_berlin.german.korpling.saltnpepper.salt.saltCommon.sCorpusStructure.SCorpusGraph;
import de.hu_berlin.german.korpling.saltnpepper.salt.saltCommon.sCorpusStructure.SDocument;
Expand Down
Loading

0 comments on commit d6a9ae2

Please sign in to comment.