/** * Copyright 2010 Google Inc. * * 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 org.waveprotocol.wave.client.wavepanel.view.dom.full; import static org.waveprotocol.wave.client.uibuilder.OutputHelper.appendSpan; import static org.waveprotocol.wave.client.uibuilder.OutputHelper.close; import static org.waveprotocol.wave.client.uibuilder.OutputHelper.closeSpan; import static org.waveprotocol.wave.client.uibuilder.OutputHelper.open; import static org.waveprotocol.wave.client.uibuilder.OutputHelper.openSpan; import static org.waveprotocol.wave.client.uibuilder.OutputHelper.openSpanWith; import com.google.common.annotations.VisibleForTesting; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.resources.client.ImageResource; import org.waveprotocol.wave.client.common.safehtml.SafeHtmlBuilder; import org.waveprotocol.wave.client.uibuilder.BuilderHelper.Component; import org.waveprotocol.wave.client.uibuilder.HtmlClosureCollection; import org.waveprotocol.wave.client.uibuilder.UiBuilder; import org.waveprotocol.wave.client.wavepanel.view.View.Type; /** * UiBuilder for a collection of participants. * */ public final class ParticipantsViewBuilder implements UiBuilder { /** Height of the regular (collapsed) panel. */ final static int COLLAPSED_HEIGHT_PX = 51; /** Resources used by this widget. */ interface Resources extends ClientBundle { /** CSS */ @Source("Participants.css") Css css(); @Source("expand_button.png") ImageResource expandButton(); @Source("collapse_button.png") ImageResource collapseButton(); @Source("add_button.png") ImageResource addButton(); } /** CSS for this widget. */ interface Css extends CssResource { String participant(); String panel(); String flow(); String extra(); String toggleGroup(); String simple(); String expandButton(); String collapseButton(); String addButton(); } /** An enum for all the components of a participants view. */ public enum Components implements Component { /** Element to which participant UIs are attached. */ CONTAINER("C"), /** Add button. */ ADD("A"); private final String postfix; Components(String postfix) { this.postfix = postfix; } @Override public String getDomId(String baseId) { return baseId + postfix; } } private final Css css; private final HtmlClosureCollection participantUis; private final String id; @VisibleForTesting ParticipantsViewBuilder(Css css, String id, HtmlClosureCollection participantUis) { this.css = css; this.id = id; this.participantUis = participantUis; } /** * Creates a new ParticipantsViewBuilder. * * @param id attribute-HTML-safe encoding of the view's HTML id */ public static ParticipantsViewBuilder create(String id, HtmlClosureCollection participantUis) { return new ParticipantsViewBuilder( WavePanelResourceLoader.getParticipants().css(), id, participantUis); } @Override public void outputHtml(SafeHtmlBuilder output) { open(output, id, css.panel(), TypeCodes.kind(Type.PARTICIPANTS)); { open(output, null, css.flow(), null); { open(output, Components.CONTAINER.getDomId(id), null, null); { participantUis.outputHtml(output); // Overflow-mode panel. openSpan(output, null, css.extra(), null); { openSpanWith(output, null, css.toggleGroup(), null, "onclick=\"" + onClickJs() + "\""); { appendSpan(output, null, css.expandButton(), null); openSpan(output, null, null, null); { output.appendPlainText("more"); } closeSpan(output); } closeSpan(output); appendSpan(output, null, css.addButton(), TypeCodes.kind(Type.ADD_PARTICIPANT)); } closeSpan(output); // Single-line mode panel. openSpan(output, null, css.simple(), null); { appendSpan(output, null, css.addButton(), TypeCodes.kind(Type.ADD_PARTICIPANT)); } closeSpan(output); } close(output); } close(output); } close(output); } // Rather than install a regular handler, this is an experiment at injecting // JS directly, so that this piece of UI is functional from the initial // rendering, without needing to wait for any scripts to load (like the GWT // app). /** @return a JS click handler for toggling expanded and collapsed modes. */ private String onClickJs() { String js = "" // + "var p=document.getElementById('" + id + "');" // + "var x=p.getAttribute('s')=='e';" // + "var l=this.lastChild;" // + "p.style.height=x?'':'auto';" // + "p.setAttribute('s',x?'':'e');" // + "lastChild.innerHTML=x?'more':'less';" // + "firstChild.className=x?'" + css.expandButton() + "':'" + css.collapseButton() + "';" // + "parentNode.nextSibling.style.display=x?'':'none';" // ; // The constructed string has no double-quote characters in it, so it can be // double-quoted verbatim. assert !js.contains("\""); return js; } }