public interface UserInterfaceText extends Constants {
	
	// Titles
	String Welcome();
	String OopsCannotSearch();
	String WorkingOnThis();
	String Oops();
	String CannotAddPicture();
	String Privacy();
	String ImagePreview();

	// Buttons / Tabs
	String OK();
	String Save();
	String Cancel();
	String Login();
	String Logout();
	String Register();
	String Search();
	String Share();
	String FromUrl();
	String Upload();
	String ManagePerson();
	String ProfileAndContact();
	String Account();
	String Add();

	// Labels
	String YourUsername();
	String YourPassword();
	String ChooseUsername();
	String ChoosePassword();
	String EnterYourEmail();
	String EnterCode();
	String Following();
	String RememberMe();
	String Url();
	String Bio();
	String OnYourLists();
	String Followers();
	String FollowingPeople();
	String DisplayName();
	String FullName();
	String Birthday();
	String Gender();
	String Email();
	String Telephone();
	String Website();
	String ViewProfileOf();
	String YourLists();
	String Avatar();
	String General();
	String YourIdentity();
	String UserName();
	String FirstName();
	String Surname();
	String WhoCanSeeUpdate();
	String With();
	String ChangeLocale();
	String Language();
	String ShoutTo();
	String Pictures();
	String To();
	String VisibleTo();
	
	// Application / window names
	String Activities();
	String Preferences();
	String Contacts();
	String Profile();
	
	// Field Values
	String Male();
	String Female();
	String NotKnown();
	String NotApplicable();
	String Everyone();
	String You();
	
	// Tooltips
	String ShowSearchPanel();
	String HideSearchPanel();
	String ShowUpdatePanel();
	String HideUpdatePanel();
	String ViewProfile();
	String Chatty();
	String Away();
	String DoNotDisturb();
	String ExtendedAway();
	String Online();
	String NotAvailable();
	String DeleteFromContacts();
	String ShoutToContact();
	String FollowPerson();
	String UnfollowPerson();
	String SendPrivateMessage();
	String ChatWithPerson();
	String AddPicture();
	String ChangePrivacy();
	String ShoutToContacts();
	String EditProfile();
	String BackToDefault();
	String AddAvatar();
	String ClearPeople();
	String RemovePicture();
	String RemovePictures();
	String ShowPreview();
	
	// Instructions
	String PleaseUseUserAtDomain();
	String AccountUnavailable();
	String SelectFile();
	String SelectPictureUrl();
	String AddOrRemovePerson();
	String IdentityFixed();
	String SetLanguage();
	
	// Notifications
	String FetchingProfile();
	String FeatureNotImplemented();
	String FetchingActivities();
	String NoActivitiesAvailable();
	String UpdatingStatus();
	String UpdateSuccess();
	String WaitForUpload();
	String FetchingFollowers();
	String FetchingFollowing();
	String NoFollowers();
	String NoFollowing();
	String EmptyUpdate();
	
	// Error 
	String FailedToGetProfile();
	String LoginFailure();
	String UpdateFailure();
	
	// Months
	String January();
	String February();
	String March();
	String April();
	String May();
	String June();
	String July();
	String August();
	String September();
	String October();
	String November();
	String December();
		
} public class DefaultTaskInfo implements TaskInfo {

	private final ObservableHelper<TaskEvent> observableHelper = new ObservableHelper<TaskEvent>();

	private final boolean hasProgress;

	private float progress = 0;

	private String message;

	private Status status = Status.running;

	public DefaultTaskInfo(String message, boolean hasProgress) {
		this.message = message;
		this.hasProgress = hasProgress;
	}

	@Override
	public String getMessage() {
		return message;
	}

	@Override
	public float getProgress() {
		return progress;
	}

	@Override
	public Status getStatus() {
		return status;
	}

	@Override
	public boolean hasProgress() {
		return hasProgress;
	}

	@Override
	public void registerEventHandler(Observer<TaskEvent> handler) {
		observableHelper.registerEventHandler(handler);
	}

	@Override
	public void unregisterEventHandler(Observer<TaskEvent> handler) {
		observableHelper.unregisterEventHandler(handler);
	}

	public void setMessage(String message) {
		this.message = message;
		observableHelper.fireEvent(new DefaultTaskEvent(Type.updated, this));
	}

	public void setProgress(float progress) {
		this.progress = progress;
		observableHelper.fireEvent(new DefaultTaskEvent(Type.updated, this));
	}

	public void complete(String message, Status status) {
		this.status = status;
		this.message = message;
		observableHelper.fireEvent(new DefaultTaskEvent(Type.completed, this));
	}

} public class TaskMonitor implements Observable<TaskEvent> {

	private static TaskMonitor instance;

	private final ObservableHelper<TaskEvent> observableHelper = new ObservableHelper<TaskEvent>();

	private final List<TaskInfo> tasks = new ArrayList<TaskInfo>();

	private final TaskHandler handler = new TaskHandler();

	public static TaskMonitor getInstance() {
		if (instance == null) {
			instance = new TaskMonitor();
		}
		return instance;
	}

	@Override
	public void registerEventHandler(Observer<TaskEvent> handler) {
		observableHelper.registerEventHandler(handler);
	}

	@Override
	public void unregisterEventHandler(Observer<TaskEvent> handler) {
		observableHelper.unregisterEventHandler(handler);
	}

	public void addTask(TaskInfo task) {
		tasks.add(task);
		task.registerEventHandler(handler);
		observableHelper.fireEvent(new DefaultTaskEvent(TaskEvent.Type.added,
				task));
	}

	public List<TaskInfo> getTasks() {
		return Collections.unmodifiableList(tasks);
	}

	private class TaskHandler implements Observer<TaskEvent> {

		@Override
		public void handleEvent(TaskEvent event) {
			if (event.getType().equals(Type.completed)) {
				tasks.remove(event.getTask());
				observableHelper.fireEvent(event);
			}
		}

	}

	// private constructor to maintain singleton
	private TaskMonitor() {
		//
	}

} public class AvatarUploadField extends Composite {
	
	// internationalization
	private UserInterfaceText uiText = (UserInterfaceText) GWT.create(UserInterfaceText.class);
	
	private FlowPanel thumbnailContainer = new FlowPanel();
	private StyledFlowPanel container = new StyledFlowPanel("avatarupload");
	private PictureChooserDialog pictureChooserDialog;
	private final PushButton addPhoto = new PushButton(new Image(OswClient
			.getInstance().getPreference("theme_folder")
			+ "assets/i-camera2.png"));
	private String avatarUri = "";
	private final PictureThumbnail thumb = new PictureThumbnail("");
	
	public AvatarUploadField() {

		init();
		initWidget(container);

	}
	
	public String getAvatarUri() {
		
		return avatarUri; 
	}
	
	private void init() {
		
		// hide thumbnail
		setStateNoAvatar();
		
		// add instruction
		addPhoto.setTitle(uiText.AddAvatar());
		
		pictureChooserDialog = new PictureChooserDialog(new PictureHandler() {
			public void handlePicture(String pictureUrl) {
				if (pictureUrl != null && pictureUrl.length() > 0) {
					thumb.setPicture(pictureUrl);
					avatarUri = pictureUrl;
					setStateAvatarLoaded();
				}
			}
		});
		
		thumbnailContainer.add(thumb);
		container.add(thumbnailContainer);
		container.add(addPhoto);
		
		thumb.setCloseHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				setStateNoAvatar();
			}
		});
		
		addPhoto.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				pictureChooserDialog.show();
			}
		});
	}
	
	public void setAvatar(String url) {
		thumb.setPicture(url);
		setStateAvatarLoaded();
	}
	
	private void setStateAvatarLoaded() {
		thumbnailContainer.setVisible(true);
		addPhoto.setVisible(false);
	}
	
	private void setStateNoAvatar() {
		thumbnailContainer.setVisible(false);
		thumb.setPicture("");
		avatarUri="";
		addPhoto.setVisible(true);
	}
} public class DateField extends Composite {
	
	// internationalization
	private UserInterfaceText uiText = (UserInterfaceText) GWT.create(UserInterfaceText.class);
	
	private ListBox dayL = new ListBox();
	private ListBox monthL = new ListBox();
	private ListBox yearL = new ListBox();
	private HorizontalPanel container = new HorizontalPanel();
	
	public DateField() {
		StyledFlowPanel daypanel = new StyledFlowPanel("subpanel");
		StyledFlowPanel monthpanel = new StyledFlowPanel("subpanel");
		StyledFlowPanel yearpanel = new StyledFlowPanel("subpanel");

		daypanel.add(dayL);
		container.add(daypanel);

		monthpanel.add(monthL);
		container.add(monthpanel);

		yearpanel.add(yearL);
		container.add(yearpanel);
		
		initValues();
		
		initWidget(container);
	}
	
	public void setDate(Date birthday) {
		
		DateTimeFormat fmtDay = DateTimeFormat.getFormat("d");
		DateTimeFormat fmtMonth = DateTimeFormat.getFormat("M");
		DateTimeFormat fmtYear = DateTimeFormat.getFormat("yyyy");
		
		String day ="";
		String month ="";
		String year = "";
		
		try {
			day = fmtDay.format(birthday);
			month = fmtMonth.format(birthday);
			year = fmtYear.format(birthday);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		for (int i=0;i<dayL.getItemCount();i++) {
			if (day.equals(dayL.getItemText(i))) {
				dayL.setSelectedIndex(i);
			}
		}
		
		for (int i=0;i<monthL.getItemCount();i++) {
			if (month.equals(monthL.getValue(i))) {
				monthL.setSelectedIndex(i);
			}
		}
		
		for (int i=0;i<yearL.getItemCount();i++) {
			if (year.equals(yearL.getItemText(i))) {
				yearL.setSelectedIndex(i);
			}
		}
	}

	public Date getDate() {
		
		DateTimeFormat dtf = DateTimeFormat.getFormat("d/M/yyyy");
		
		Date birthday = null;
		
		try {
			birthday = dtf.parse(dayL.getItemText(dayL.getSelectedIndex()) + "/" + monthL.getValue(monthL.getSelectedIndex()) + "/" + yearL.getItemText(yearL.getSelectedIndex()) );           
		} catch (Exception e) {
			e.printStackTrace();
		}

		return birthday;
		
	}
	
	private void initValues() {
		
		dayL.addItem("");
		for (int i = 1; i < 32; i++) {
			dayL.addItem(Integer.toString(i), Integer.toString(i));
		}
		
		monthL.addItem("");		
		monthL.addItem(uiText.January(), "1");
		monthL.addItem(uiText.February(), "2");
		monthL.addItem(uiText.March(), "3");
		monthL.addItem(uiText.April(), "4");
		monthL.addItem(uiText.May(), "5");
		monthL.addItem(uiText.June(), "6");
		monthL.addItem(uiText.July(), "7");
		monthL.addItem(uiText.August(), "8");
		monthL.addItem(uiText.September(), "9");
		monthL.addItem(uiText.October(), "10");
		monthL.addItem(uiText.November(), "11");
		monthL.addItem(uiText.December(), "12");
		
		yearL.addItem("");
		for (int i = 2010; i >= 1900; i--) {
			yearL.addItem(Integer.toString(i), Integer.toString(i));
		}
	}


} public class GenderEditField extends Composite {
	
	// internationalization
	private UserInterfaceText uiText = (UserInterfaceText) GWT
			.create(UserInterfaceText.class);
	
	private ListBox genderList = new ListBox();
	
	public GenderEditField() {
		
		initValues();
		
		initWidget(genderList);
	}
	
	public void setGender(GenderField.Type gender) {
		
		for (int i=0;i<genderList.getItemCount();i++) {
			if (gender.toString().equals(genderList.getValue(i))) {
				genderList.setSelectedIndex(i);
				return;
			}
		}
		
	}
	
	public String getGenderValue() {
		
		return genderList.getValue(genderList.getSelectedIndex());
		
	}
	
	public String getGenderText() {
		
		return genderList.getItemText(genderList.getSelectedIndex());
		
	}
	
	private void initValues() {
		
		genderList.addItem("");
		genderList.addItem(uiText.NotKnown(), GenderField.Type.NOTKNOWN.toString());
		genderList.addItem(uiText.Male(), GenderField.Type.MALE.toString());
		genderList.addItem(uiText.Female(), GenderField.Type.FEMALE.toString());
		genderList.addItem(uiText.NotApplicable(), GenderField.Type.NOTAPPLICABLE.toString());
		
	}
} public class NameEditField extends Composite {
	
	// internationalization
	private UserInterfaceText uiText = (UserInterfaceText) GWT.create(UserInterfaceText.class);
	
	StyledTextBox firstField = new StyledTextBox("subtextbox", "", "150px");
	//StyledTextBox middleField = new StyledTextBox("subtextbox", "", "75px");
	StyledTextBox lastField = new StyledTextBox("subtextbox", "", "150px");
	
	public NameEditField() {

		StyledLabel firstLabel = new StyledLabel("sublabel", uiText.FirstName());
		//StyledLabel middleLabel = new StyledLabel("sublabel", "Middle");
		StyledLabel lastLabel = new StyledLabel("sublabel", uiText.Surname());		

		HorizontalPanel container = new HorizontalPanel();
		StyledFlowPanel firstpanel = new StyledFlowPanel("subpanel");
		//StyledFlowPanel middlepanel = new StyledFlowPanel("subpanel");
		StyledFlowPanel lastpanel = new StyledFlowPanel("subpanel");

		firstpanel.add(firstField);
		firstpanel.add(firstLabel);
		container.add(firstpanel);

		//middlepanel.add(middleField);
		//middlepanel.add(middleLabel);
		//container.add(middlepanel);

		lastpanel.add(lastField);
		lastpanel.add(lastLabel);
		container.add(lastpanel);
		
		initWidget(container);

	}
	
	public void setName(String firstname, String lastname) {
		if (firstname != null && firstname.length() > 0) firstField.setText(firstname);
		//if (middlename != null && middlename.length() > 0) middleField.setText(middlename);
		if (lastname != null && lastname.length() > 0) lastField.setText(lastname);
	}
	
	public String getFirstName() {
		return firstField.getText();
	}
	
	public String getLastName() {
		return lastField.getValue();
	}
	
} public class PrivacySelector extends ListBox {

	private String value;
	public static final String EVERYONE = "Everyone";
	public static final String NETWORK = "Anyone on "
			+ OswClient.getInstance().getPreference("xmpp_domain");
	public static final String FRIENDS = "Friends";
	public static final String PRIVATE = "Only me";
	public static final String CUSTOM = "Custom ...";

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public PrivacySelector(String fieldname) {

		addStyleName("privacyselector");

		// construct
		final Roster roster = OswServiceFactory.getService().getRoster();
		roster.registerEventHandler(new Observer<RosterEvent>() {

			@Override
			public void handleEvent(RosterEvent event) {
				updateVisibility(roster.getGroups());
			}

		});
		updateVisibility(roster.getGroups());
		setVisibleItemCount(1);

	}

	private void updateVisibility(Set<String> groups) {
		clear();
		addItem(EVERYONE);
		// addItem(NETWORK);
		addItem(FRIENDS);
		addItem(PRIVATE);
		for (String group : groups) {
			addItem("People you tagged with '" + group + "'");
		}
		addItem(CUSTOM);
	}

} public class StyledFlowPanel extends FlowPanel {

	public StyledFlowPanel(String classname) {
		addStyleName(classname);
	}
} public class StyledLabel extends HTML {

	public StyledLabel(String classname, String htmlText) {
		if (classname != null && classname.length() > 0)
			addStyleName(classname);
		setHTML(htmlText);
	}
} public class StyledTextBox extends TextBox {

	public StyledTextBox(String classname, String value, String width) {
		if (classname != null && classname.length() > 0)
			addStyleName(classname);
		setText(value);
		setWidth(width);
	}
} public class TooltipPushButton extends PushButton {

	public TooltipPushButton(Image icon, String tooltip) {
		super(icon);
		this.setTitle(tooltip);
	}

} public class OSWUrlBuilder extends UrlBuilder {

	public OSWUrlBuilder() {
		
		// default stuff
		setProtocol(Window.Location.getProtocol()); 
		setHost(Window.Location.getHost()); 
		String path = Window.Location.getPath(); 
		if (path != null && path.length() > 0) { 
			setPath(path); 
		} 
		String hash = Window.Location.getHash(); 
		if (hash != null && hash.length() > 0) {
			setHash(hash);
		} 
		String port = Window.Location.getPort(); 
		if (port != null && port.length() > 0) { 
			setPort(Integer.parseInt(port)); 
		} 
		
		// Add query parameters.
		Map<String, List<String>> params = Window.Location.getParameterMap(); 
		for (Map.Entry<String, List<String>> entry : params.entrySet()) { 
			List<String> values = new ArrayList<String>(entry.getValue()); 
			setParameter(entry.getKey(), values.toArray(new String[values.size()])); 
		} 

	}
	
}
import static org.onesocialweb.gwt.client.util.FormLayoutHelper.addHTMLLabelRow;
import static org.onesocialweb.gwt.client.util.FormLayoutHelper.addWidgetRow;
import java.util.Date;
import java.util.HashMap;
import org.onesocialweb.gwt.client.OswClient;
import org.onesocialweb.gwt.client.i18n.UserInterfaceText;
import org.onesocialweb.gwt.client.task.DefaultTaskInfo;
import org.onesocialweb.gwt.client.task.TaskMonitor;
import org.onesocialweb.gwt.client.task.TaskInfo.Status;
import org.onesocialweb.gwt.client.ui.widget.AvatarUploadField;
import org.onesocialweb.gwt.client.ui.widget.DateField;
import org.onesocialweb.gwt.client.ui.widget.GenderEditField;
import org.onesocialweb.gwt.client.ui.widget.NameEditField;
import org.onesocialweb.gwt.client.ui.widget.PrivacySelector;
import org.onesocialweb.gwt.client.ui.widget.StyledFlowPanel;
import org.onesocialweb.gwt.client.ui.widget.StyledLabel;
import org.onesocialweb.gwt.client.ui.widget.StyledTextBox;
import org.onesocialweb.gwt.client.ui.widget.TooltipPushButton;
import org.onesocialweb.gwt.client.util.OSWUrlBuilder;
import org.onesocialweb.gwt.service.OswServiceFactory;
import org.onesocialweb.gwt.service.RequestCallback;
import org.onesocialweb.model.vcard4.BirthdayField;
import org.onesocialweb.model.vcard4.DefaultGenderField;
import org.onesocialweb.model.vcard4.DefaultNameField;
import org.onesocialweb.model.vcard4.EmailField;
import org.onesocialweb.model.vcard4.FullNameField;
import org.onesocialweb.model.vcard4.GenderField;
import org.onesocialweb.model.vcard4.NameField;
import org.onesocialweb.model.vcard4.NoteField;
import org.onesocialweb.model.vcard4.PhotoField;
import org.onesocialweb.model.vcard4.Profile;
import org.onesocialweb.model.vcard4.TelField;
import org.onesocialweb.model.vcard4.URLField;
import org.onesocialweb.model.vcard4.VCard4Factory;
import org.onesocialweb.model.vcard4.exception.CardinalityException;
import org.onesocialweb.model.vcard4.exception.UnsupportedFieldException;
import com.google.code.gwt.storage.client.Storage;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TabPanel;
import com.reveregroup.gwt.imagepreloader.FitImage;
				uiText.FetchingProfile(), false);
		TaskMonitor.getInstance().addTask(task);

		OswServiceFactory.getService().getProfile(jid,
				new RequestCallback<Profile>() {

					@Override
					public void onFailure() {
						// no profile available
						task.complete("", Status.succes);
						model = null;
						composeProfile();
					}

					@Override
					public void onSuccess(Profile result) {
						task.complete("", Status.succes);
						model = result;
						composeProfile();
					}

				});
	}

	private void refreshProfile() {
		general.removeAllRows();

		if (model.hasField(PhotoField.NAME)) {
			String avatar = model.getPhotoUri();
			avatarD.setUrl(avatar);
			if (avatar != null && avatar.length() > 0)
				addWidgetRow(general, uiText.Avatar(), avatarD);
		}

		if (model.hasField(FullNameField.NAME)) {
			String displayname = model.getFullName();
			if (displayname != null && displayname.length() > 0)
				addHTMLLabelRow(general, uiText.DisplayName(), displayname);
		}
		
		if (model.hasField(NameField.NAME)) {
			String name = model.getName();
			if (name != null && name.length() > 0)
				addHTMLLabelRow(general, uiText.FullName(), name);
		}

		if (model.hasField(BirthdayField.NAME)) {
			Date birthday = model.getBirthday();
			if (birthday != null) {
				DateTimeFormat dtf = DateTimeFormat.getFormat("d MMMM yyyy");
				String bday = dtf.format(birthday);
				addHTMLLabelRow(general, uiText.Birthday(), bday);
			}	
		}

		if (model.hasField(GenderField.NAME)) {
			
			GenderField.Type gender = model.getGender();
			
			// in this case the GenderEditField will provide the display value, not the model
			// because of internationalization
			if (gender != null)
				addHTMLLabelRow(general, uiText.Gender(), genderF.getGenderText());
			
		}

		if (model.hasField(NoteField.NAME)) {
			String bio = model.getNote();
			if (bio != null && bio.length() > 0)
				addHTMLLabelRow(general, uiText.Bio(), bio);
		}
		
		if (model.hasField(EmailField.NAME)) {
			String email = model.getEmail();
			if (email != null && email.length() > 0)
				addHTMLLabelRow(general, uiText.Email(), email);
		}
		
		if (model.hasField(TelField.NAME)) {
			String tel = model.getTel();
			if (tel != null && tel.length() > 0)
				addHTMLLabelRow(general, uiText.Telephone(), tel);
		}
		
		if (model.hasField(URLField.NAME)) {
			String url = model.getUrl();
			if (url != null && url.length() > 0)
				addHTMLLabelRow(general, uiText.Website(), url);
		}
		
	}

	private void composeProfile() {

		// compose profile tab
		sectionGeneral = new StyledFlowPanel("section");
		buttonEditG = new TooltipPushButton(new Image(OswClient.getInstance()
				.getPreference("theme_folder")
				+ "assets/i-edit.png"), uiText.EditProfile());
		buttonEditG.addStyleName("sectionedit");
		StyledLabel titleG = new StyledLabel("grouplabel", uiText.General());
		sectionGeneral.add(buttonEditG);
		sectionGeneral.add(titleG);
		this.profile.add(sectionGeneral);

		// Editor version of general information
		generalEdit = new FlexTable();
		generalEdit.addStyleName("edit");
		
		final AvatarUploadField avatarF = new AvatarUploadField();
		addWidgetRow(generalEdit, uiText.Avatar(), avatarF);
		
		avatarD.setMaxSize(80, 80);
		
		final StyledTextBox displaynameF = new StyledTextBox("", "", "250px");
		addWidgetRow(generalEdit, uiText.DisplayName(), displaynameF);

		final NameEditField nameF = new NameEditField();
		addWidgetRow(generalEdit, uiText.FullName(), nameF);

		final DateField birthdayF = new DateField();