package org.openrosa.client.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
import org.openrosa.client.model.GroupDef;
import org.openrosa.client.model.IFormElement;
import org.openrosa.client.model.QuestionDef;
import org.openrosa.client.util.FormUtil;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.Widget;
/**
* Utilities used by the form designer.
*
* @author daniel
*
*/
public class FormDesignerUtil {
/** The form designer title. */
private static String title = "PurcForms FormDesigner";
/**
* Creates an HTML fragment that places an image & caption together, for use
* in a group header.
*
* @param imageProto an image prototype for an image
* @param caption the group caption
* @return the header HTML fragment
*/
public static String createHeaderHTML(ImageResource imageProto, String caption) {
//Add the image and text to a horizontal panel
HorizontalPanel hPanel = new HorizontalPanel();
hPanel.setSpacing(0);
hPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
hPanel.add(FormUtil.createImage(imageProto));
HTML headerText = new HTML(caption);
hPanel.add(headerText);
return hPanel.getElement().getString();
}
/**
* Adds formatting to an XML string
*/
public static String formatXml(String xmlContent) {
return FormUtil.formatXml(xmlContent);
}
/**
* Disables the browsers default context menu for the specified element.
*
* @param elem the element whose context menu will be disabled
*/
public static native void disableContextMenu(Element elem) /*-{
elem.oncontextmenu=function() { return false};
}-*/;
/**
* Puts a widget at a given position.
*
* @param w the widget.
* @param left the left position in pixels.
* @param top the top position in pixels.
*/
public static void setWidgetPosition(Widget w, String left, String top) {
FormUtil.setWidgetPosition(w, left, top);
}
/**
* Loads a list of questions into a MultiWordSuggestOracle for a given reference question.
*
* @param questions the list of questions.
* @param refQuestion the reference question.
* @param oracle the MultiWordSuggestOracle.
* @param dynamicOptions set to true if we are loading for dynamic options.
* @param sameTypesOnly set to true if you want to load only questions of the same type
* as the referenced question.
*/
public static void loadQuestions(List<IFormElement> questions, IFormElement refQuestion, MultiWordSuggestOracle oracle, boolean dynamicOptions, boolean sameTypesOnly, QuestionDef parentQuestionDef){
if(questions == null)
return;
for(int i=0; i<questions.size(); i++){
IFormElement questionDef = questions.get(i);
if(!dynamicOptions && refQuestion != null && refQuestion.getDataType() != questionDef.getDataType() && sameTypesOnly)
continue;
if(dynamicOptions && !(questionDef.getDataType() == QuestionDef.QTN_TYPE_LIST_EXCLUSIVE ||
questionDef.getDataType() == QuestionDef.QTN_TYPE_LIST_EXCLUSIVE_DYNAMIC))
continue;
if(dynamicOptions && refQuestion == questionDef)
continue;
if(!dynamicOptions && refQuestion == questionDef)
continue;
if(questionDef == parentQuestionDef)
continue;
String displayText = Itext.getDisplayText(questionDef);
oracle.add(displayText);
//TODO Allowed for now since repeat questions will have ids which cant be equal to
//those of parents. But test this to ensure it does not bring in bugs.
if(questionDef instanceof GroupDef){
loadQuestions(((GroupDef)questionDef).getChildren(),refQuestion,oracle,dynamicOptions,sameTypesOnly,parentQuestionDef);
}else if(questionDef.getDataType() == QuestionDef.QTN_TYPE_REPEAT){
loadQuestions(questionDef.getChildren(),refQuestion,oracle,dynamicOptions,sameTypesOnly,parentQuestionDef);
}
//if(questionDef.getDataType() == QuestionDef.QTN_TYPE_REPEAT)
// loadQuestions(questionDef.getRepeatQtnsDef().getQuestions(),refQuestion,oracle,dynamicOptions,sameTypesOnly); //TODO These have different id sets and hence we are leaving them out for now
}
}
/**
* Loads a list of questions into a MultiWordSuggestOracle for a given reference question.
*
* @param questions the list of questions.
* @param refQuestion the reference question.
* @param oracle the MultiWordSuggestOracle.
* @param dynamicOptions set to true if we are loading for dynamic options.
*/
public static void loadQuestions(List<IFormElement> questions, QuestionDef refQuestion, MultiWordSuggestOracle oracle, boolean dynamicOptions){
loadQuestions(questions, refQuestion, oracle, dynamicOptions,false, null);
}
/**
* Draws a widget selection rubber band on the mouse down event.
*
* @param event the current mouse down event.
* @param elem the rubber band widget.
*/
public static native void startRubber(Event event,Element elem) /*-{
elem.style.width = 0;
elem.style.height = 0;
elem.style.left = event.x;
elem.style.top = event.y;
elem.style.visibility = 'visible';
}-*/;
/**
* Removes the widget selection rubber band on the mouse up event.
*
* @param event the current mouse up event.
* @param elem the rubber band element.
*/
public static native void stopRubber(Event event,Element elem) /*-{
elem.style.visibility = 'hidden';
}-*/;
/**
* Moves the rubber band on the mouse move event.
*
* @param event the current mouse move event.
* @param elem the rubber band element.
*/
public static native void moveRubber(Event event, Element elem) /*-{
elem.style.width = event.x - elem.style.left;
elem.style.height = event.y - elem.style.top;
}-*/;
/**
* Gets the title of the form designer.
*
* @return the form designer title.
*/
public static String getTitle(){
return title;
}
/**
* Sets the title of the form designer.
*/
public static void setDesignerTitle(){
String s = FormUtil.getDivValue("title");
if(s != null && s.trim().length() > 0)
title = s;
Window.setTitle(title);
}
/**
* Checks if the CTRL key is currently pressed.
*
* @return true if pressed, else false.
*/
public static boolean getCtrlKey(){
Event event = DOM.eventGetCurrentEvent();
if(event == null)
return false;
return event.getCtrlKey();
}
/**
* Converts a string into a valid XML token (tag name)
*
* @param s string to convert into XML token
* @return valid XML token based on s
*/
public static String getXmlTagName(String s) {
// Converts a string into a valid XML token (tag name)
// No spaces, start with a letter or underscore, not 'xml*'
// if len(s) < 1, return '_blank'
if (s == null || s.length() < 1)
return "_blank";
// xml tokens must start with a letter
String letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
// after the leading letter, xml tokens may have
// digits, period, or hyphen
String nameChars = letters + "0123456789.-";
// special characters that should be replaced with valid text
// all other invalid characters will be removed
HashMap<String, String> swapChars = new HashMap<String, String>();
swapChars.put("!", "bang");
swapChars.put("#", "pound");
swapChars.put("\\*", "star");
swapChars.put("'", "apos");
swapChars.put("\"", "quote");
swapChars.put("%", "percent");
swapChars.put("<", "lt");
swapChars.put(">", "gt");
swapChars.put("=", "eq");
swapChars.put("/", "slash");
swapChars.put("\\\\", "backslash");
s = s.replace("'", "");
// start by cleaning whitespace and converting to lowercase
s = s.replaceAll("^\\s+", "").replaceAll("\\s+$", "").replaceAll("\\s+", "_").toLowerCase();
// swap characters
Set<Entry<String, String>> swaps = swapChars.entrySet();
for (Entry<String, String> entry : swaps) {
if (entry.getValue() != null)
s = s.replaceAll(entry.getKey(), "_" + entry.getValue() + "_");
else
s = s.replaceAll(String.valueOf(entry.getKey()), "");
}
// ensure that invalid characters and consecutive underscores are
// removed
String token = "";
boolean underscoreFlag = false;
for (int i = 0; i < s.length(); i++) {
if (nameChars.indexOf(s.charAt(i)) != -1) {
if (s.charAt(i) != '_' || !underscoreFlag) {
token += s.charAt(i);
underscoreFlag = (s.charAt(i) == '_');
}
}
}
// remove extraneous underscores before returning token
token = token.replaceAll("_+", "_");
token = token.replaceAll("_+$", "");
// make sure token starts with valid letter
if (letters.indexOf(token.charAt(0)) == -1 || token.startsWith("xml"))
token = "_" + token;
// return token
return token;
}
}