001// Licensed under the Apache License, Version 2.0 (the "License");
002// you may not use this file except in compliance with the License.
003// You may obtain a copy of the License at
004//
005// http://www.apache.org/licenses/LICENSE-2.0
006//
007// Unless required by applicable law or agreed to in writing, software
008// distributed under the License is distributed on an "AS IS" BASIS,
009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010// See the License for the specific language governing permissions and
011// limitations under the License.
012
013package org.apache.tapestry5.corelib.components;
014
015import org.apache.tapestry5.ComponentResources;
016import org.apache.tapestry5.MarkupWriter;
017import org.apache.tapestry5.annotations.Import;
018import org.apache.tapestry5.annotations.Parameter;
019import org.apache.tapestry5.annotations.SupportsInformalParameters;
020import org.apache.tapestry5.ioc.annotations.Inject;
021import org.apache.tapestry5.services.DateUtilities;
022
023import java.util.Date;
024
025/**
026 * Used to present an interval value using Moment.js's from() or fromNow() functions. The interval
027 * is determined in terms of a start and end date; either (but not both) may be omitted, in which
028 * case the client will dynamically update the element.  In that case, the value will live update, approximately every second.
029 *
030 * This component will render an empty element. The element will match the template element,
031 * or a "span" if the template did not provide an element. Informal parameters will be rendered
032 * into the element.
033 *
034 * When the end date is after the start date, the rendered text will be prefixed with "in".
035 * When the end date precedes the start date, the rendered text will be suffixed with "ago".
036 * The plain parameter is used to turn off the prefix or suffix.
037 *
038 * @tapestrydoc
039 * @see LocalDate
040 * @since 5.4
041 */
042@SupportsInformalParameters
043@Import(module = "t5/core/time-interval")
044public class TimeInterval
045{
046    /**
047     * The start date for the interval. If omitted, the start time is considered "now" and will automatically
048     * update in the browser.
049     */
050    @Parameter
051    Date start;
052
053    /**
054     * The end date for the interval. If omitted, the end time is considered "now" and will update automatically
055     * in the browser.
056     */
057    @Parameter()
058    Date end;
059
060    /**
061     * If true, then output is plain: no "in" prefix or "ago" suffix.
062     */
063    @Parameter
064    boolean plain;
065
066    @Inject
067    ComponentResources resources;
068
069    @Inject
070    DateUtilities dateUtilities;
071
072    private String toISO(Date date)
073    {
074        return date == null ? null : dateUtilities.formatISO8601(date);
075    }
076
077    boolean beginRender(MarkupWriter writer)
078    {
079        writer.element(resources.getElementName("span"),
080                // Trigger the client-side logic:
081                "data-timeinterval", "true",
082                "data-timeinterval-start", toISO(start),
083                "data-timeinterval-end", toISO(end));
084
085        if (plain)
086        {
087            writer.attributes("data-timeinterval-plain", true);
088        }
089
090        resources.renderInformalParameters(writer);
091
092        writer.end();
093
094        // Skip the body regardless.
095        return false;
096    }
097
098}