/*! * jQuery UI Tooltip @VERSION * http://jqueryui.com * * Copyright jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license */ //>>label: Tooltip //>>group: Widgets //>>description: Shows additional information for any element on hover or focus. //>>docs: http://api.jqueryui.com/tooltip/ //>>demos: http://jqueryui.com/tooltip/ (function( factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define([ "jquery", "./core", "./widget", "./position" ], factory ); } else { // Browser globals factory( jQuery ); } }(function( $ ) { return $.widget( "ui.tooltip", { version: "@VERSION", options: { content: function() { // support: IE<9, Opera in jQuery <1.7 // .text() can't accept undefined, so coerce to a string var title = $( this ).attr( "title" ) || ""; // Escape title, since we're going from an attribute to raw HTML return $( "" ).text( title ).html(); }, hide: true, // Disabled elements have inconsistent behavior across browsers (#8661) items: "[title]:not([disabled])", position: { my: "left top+15", at: "left bottom", collision: "flipfit flip" }, show: true, tooltipClass: null, track: false, // callbacks close: null, open: null }, _addDescribedBy: function( elem, id ) { var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ); describedby.push( id ); elem .data( "ui-tooltip-id", id ) .attr( "aria-describedby", $.trim( describedby.join( " " ) ) ); }, _removeDescribedBy: function( elem ) { var id = elem.data( "ui-tooltip-id" ), describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ), index = $.inArray( id, describedby ); if ( index !== -1 ) { describedby.splice( index, 1 ); } elem.removeData( "ui-tooltip-id" ); describedby = $.trim( describedby.join( " " ) ); if ( describedby ) { elem.attr( "aria-describedby", describedby ); } else { elem.removeAttr( "aria-describedby" ); } }, _create: function() { this._on({ mouseover: "open", focusin: "open" }); // IDs of generated tooltips, needed for destroy this.tooltips = {}; // IDs of parent tooltips where we removed the title attribute this.parents = {}; if ( this.options.disabled ) { this._disable(); } // Append the aria-live region so tooltips announce correctly this.liveRegion = $( "
" ) .attr({ role: "log", "aria-live": "assertive", "aria-relevant": "additions" }) .addClass( "ui-helper-hidden-accessible" ) .appendTo( this.document[ 0 ].body ); }, _setOption: function( key, value ) { var that = this; if ( key === "disabled" ) { this[ value ? "_disable" : "_enable" ](); this.options[ key ] = value; // disable element style changes return; } this._super( key, value ); if ( key === "content" ) { $.each( this.tooltips, function( id, tooltipData ) { that._updateContent( tooltipData.element ); }); } }, _disable: function() { var that = this; // close open tooltips $.each( this.tooltips, function( id, tooltipData ) { var event = $.Event( "blur" ); event.target = event.currentTarget = tooltipData.element[ 0 ]; that.close( event, true ); }); // remove title attributes to prevent native tooltips this.element.find( this.options.items ).addBack().each(function() { var element = $( this ); if ( element.is( "[title]" ) ) { element .data( "ui-tooltip-title", element.attr( "title" ) ) .removeAttr( "title" ); } }); }, _enable: function() { // restore title attributes this.element.find( this.options.items ).addBack().each(function() { var element = $( this ); if ( element.data( "ui-tooltip-title" ) ) { element.attr( "title", element.data( "ui-tooltip-title" ) ); } }); }, open: function( event ) { var that = this, target = $( event ? event.target : this.element ) // we need closest here due to mouseover bubbling, // but always pointing at the same event target .closest( this.options.items ); // No element to show a tooltip for or the tooltip is already open if ( !target.length || target.data( "ui-tooltip-id" ) ) { return; } if ( target.attr( "title" ) ) { target.data( "ui-tooltip-title", target.attr( "title" ) ); } target.data( "ui-tooltip-open", true ); // kill parent tooltips, custom or native, for hover if ( event && event.type === "mouseover" ) { target.parents().each(function() { var parent = $( this ), blurEvent; if ( parent.data( "ui-tooltip-open" ) ) { blurEvent = $.Event( "blur" ); blurEvent.target = blurEvent.currentTarget = this; that.close( blurEvent, true ); } if ( parent.attr( "title" ) ) { parent.uniqueId(); that.parents[ this.id ] = { element: this, title: parent.attr( "title" ) }; parent.attr( "title", "" ); } }); } this._updateContent( target, event ); }, _updateContent: function( target, event ) { var content, contentOption = this.options.content, that = this, eventType = event ? event.type : null; if ( typeof contentOption === "string" ) { return this._open( event, target, contentOption ); } content = contentOption.call( target[0], function( response ) { // ignore async response if tooltip was closed already if ( !target.data( "ui-tooltip-open" ) ) { return; } // IE may instantly serve a cached response for ajax requests // delay this call to _open so the other call to _open runs first that._delay(function() { // jQuery creates a special event for focusin when it doesn't // exist natively. To improve performance, the native event // object is reused and the type is changed. Therefore, we can't // rely on the type being correct after the event finished // bubbling, so we set it back to the previous value. (#8740) if ( event ) { event.type = eventType; } this._open( event, target, response ); }); }); if ( content ) { this._open( event, target, content ); } }, _open: function( event, target, content ) { var tooltipData, tooltip, events, delayedShow, a11yContent, positionOption = $.extend( {}, this.options.position ); if ( !content ) { return; } // Content can be updated multiple times. If the tooltip already // exists, then just update the content and bail. tooltipData = this._find( target ); if ( tooltipData ) { tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content ); return; } // if we have a title, clear it to prevent the native tooltip // we have to check first to avoid defining a title if none exists // (we don't want to cause an element to start matching [title]) // // We use removeAttr only for key events, to allow IE to export the correct // accessible attributes. For mouse events, set to empty string to avoid // native tooltip showing up (happens only when removing inside mouseover). if ( target.is( "[title]" ) ) { if ( event && event.type === "mouseover" ) { target.attr( "title", "" ); } else { target.removeAttr( "title" ); } } tooltipData = this._tooltip( target ); tooltip = tooltipData.tooltip; this._addDescribedBy( target, tooltip.attr( "id" ) ); tooltip.find( ".ui-tooltip-content" ).html( content ); // Support: Voiceover on OS X, JAWS on IE <= 9 // JAWS announces deletions even when aria-relevant="additions" // Voiceover will sometimes re-read the entire log region's contents from the beginning this.liveRegion.children().hide(); if ( content.clone ) { a11yContent = content.clone(); a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" ); } else { a11yContent = content; } $( "
" ).html( a11yContent ).appendTo( this.liveRegion ); function position( event ) { positionOption.of = event; if ( tooltip.is( ":hidden" ) ) { return; } tooltip.position( positionOption ); } if ( this.options.track && event && /^mouse/.test( event.type ) ) { this._on( this.document, { mousemove: position }); // trigger once to override element-relative positioning position( event ); } else { tooltip.position( $.extend({ of: target }, this.options.position ) ); } tooltip.hide(); this._show( tooltip, this.options.show ); // Handle tracking tooltips that are shown with a delay (#8644). As soon // as the tooltip is visible, position the tooltip using the most recent // event. if ( this.options.show && this.options.show.delay ) { delayedShow = this.delayedShow = setInterval(function() { if ( tooltip.is( ":visible" ) ) { position( positionOption.of ); clearInterval( delayedShow ); } }, $.fx.interval ); } this._trigger( "open", event, { tooltip: tooltip } ); events = { keyup: function( event ) { if ( event.keyCode === $.ui.keyCode.ESCAPE ) { var fakeEvent = $.Event(event); fakeEvent.currentTarget = target[0]; this.close( fakeEvent, true ); } } }; // Only bind remove handler for delegated targets. Non-delegated // tooltips will handle this in destroy. if ( target[ 0 ] !== this.element[ 0 ] ) { events.remove = function() { this._removeTooltip( tooltip ); }; } if ( !event || event.type === "mouseover" ) { events.mouseleave = "close"; } if ( !event || event.type === "focusin" ) { events.focusout = "close"; } this._on( true, target, events ); }, close: function( event ) { var tooltip, that = this, target = $( event ? event.currentTarget : this.element ), tooltipData = this._find( target ); // The tooltip may already be closed if ( !tooltipData ) { return; } tooltip = tooltipData.tooltip; // disabling closes the tooltip, so we need to track when we're closing // to avoid an infinite loop in case the tooltip becomes disabled on close if ( tooltipData.closing ) { return; } // Clear the interval for delayed tracking tooltips clearInterval( this.delayedShow ); // only set title if we had one before (see comment in _open()) // If the title attribute has changed since open(), don't restore if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) { target.attr( "title", target.data( "ui-tooltip-title" ) ); } this._removeDescribedBy( target ); tooltipData.hiding = true; tooltip.stop( true ); this._hide( tooltip, this.options.hide, function() { that._removeTooltip( $( this ) ); }); target.removeData( "ui-tooltip-open" ); this._off( target, "mouseleave focusout keyup" ); // Remove 'remove' binding only on delegated targets if ( target[ 0 ] !== this.element[ 0 ] ) { this._off( target, "remove" ); } this._off( this.document, "mousemove" ); if ( event && event.type === "mouseleave" ) { $.each( this.parents, function( id, parent ) { $( parent.element ).attr( "title", parent.title ); delete that.parents[ id ]; }); } tooltipData.closing = true; this._trigger( "close", event, { tooltip: tooltip } ); if ( !tooltipData.hiding ) { tooltipData.closing = false; } }, _tooltip: function( element ) { var tooltip = $( "
" ) .attr( "role", "tooltip" ) .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " + ( this.options.tooltipClass || "" ) ), id = tooltip.uniqueId().attr( "id" ); $( "
" ) .addClass( "ui-tooltip-content" ) .appendTo( tooltip ); tooltip.appendTo( this.document[0].body ); return this.tooltips[ id ] = { element: element, tooltip: tooltip }; }, _find: function( target ) { var id = target.data( "ui-tooltip-id" ); return id ? this.tooltips[ id ] : null; }, _removeTooltip: function( tooltip ) { tooltip.remove(); delete this.tooltips[ tooltip.attr( "id" ) ]; }, _destroy: function() { var that = this; // close open tooltips $.each( this.tooltips, function( id, tooltipData ) { // Delegate to close method to handle common cleanup var event = $.Event( "blur" ), element = tooltipData.element; event.target = event.currentTarget = element[ 0 ]; that.close( event, true ); // Remove immediately; destroying an open tooltip doesn't use the // hide animation $( "#" + id ).remove(); // Restore the title if ( element.data( "ui-tooltip-title" ) ) { // If the title attribute has changed since open(), don't restore if ( !element.attr( "title" ) ) { element.attr( "title", element.data( "ui-tooltip-title" ) ); } element.removeData( "ui-tooltip-title" ); } }); this.liveRegion.r
/*
 * Copyright 1999-2004 The Apache Software Foundation.
 * 
 * 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.
 */

/* $Id$ */

package org.apache.fop.fo.pagination;

// Java
import java.util.ArrayList;

// XML
import org.xml.sax.Attributes;

// FOP
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.FOTreeVisitor;
import org.apache.fop.apps.FOPException;

/**
 * A repeatable-page-master-alternatives formatting object.
 * This contains a list of conditional-page-master-reference
 * and the page master is found from the reference that
 * matches the page number and emptyness.
 */
public class RepeatablePageMasterAlternatives extends FObj
    implements SubSequenceSpecifier {

    private static final int INFINITE = -1;

    /**
     * Max times this page master can be repeated.
     * INFINITE is used for the unbounded case
     */
    private int maximumRepeats;
    private int numberConsumed = 0;

    private ArrayList conditionalPageMasterRefs;

    /**
     * @see org.apache.fop.fo.FONode#FONode(FONode)
     */
    public RepeatablePageMasterAlternatives(FONode parent) {
        super(parent);
    }

    /**
     * @see org.apache.fop.fo.FONode#handleAttrs(Attributes)
     */
    public void handleAttrs(Attributes attlist) throws FOPException {
        super.handleAttrs(attlist);

        conditionalPageMasterRefs = new ArrayList();

        if (parent.getName().equals("fo:page-sequence-master")) {
            PageSequenceMaster pageSequenceMaster = (PageSequenceMaster)parent;
            pageSequenceMaster.addSubsequenceSpecifier(this);
        } else {
            throw new FOPException("fo:repeatable-page-master-alternatives "
                                   + "must be child of fo:page-sequence-master, not "
                                   + parent.getName());
        }

        String mr = getProperty(PR_MAXIMUM_REPEATS).getString();
        if (mr.equals("no-limit")) {
            this.maximumRepeats = INFINITE;
        } else {
            try {
                this.maximumRepeats = Integer.parseInt(mr);
                if (this.maximumRepeats < 0) {
                    getLogger().debug("negative maximum-repeats: "
                                      + this.maximumRepeats);
                    this.maximumRepeats = 0;
                }
            } catch (NumberFormatException nfe) {
                throw new FOPException("Invalid number for "
                                       + "'maximum-repeats' property");
            }
        }
    }

    /**
     * Get the next matching page master from the conditional
     * page master references.
     * @see org.apache.fop.fo.pagination.SubSequenceSpecifier
     */
    public String getNextPageMasterName(boolean isOddPage,
                                        boolean isFirstPage,
                                        boolean isBlankPage) {
        if (maximumRepeats != INFINITE) {
            if (numberConsumed < maximumRepeats) {
                numberConsumed++;
            } else {
                return null;
            }
        }

        for (int i = 0; i < conditionalPageMasterRefs.size(); i++) {
            ConditionalPageMasterReference cpmr =
                (ConditionalPageMasterReference)conditionalPageMasterRefs.get(i);
            if (cpmr.isValid(isOddPage, isFirstPage, isBlankPage)) {
                return cpmr.getMasterName();
            }
        }
        return null;
    }


    /**
     * Adds a new conditional page master reference.
     * @param cpmr the new conditional reference
     */
    public void addConditionalPageMasterReference(ConditionalPageMasterReference cpmr) {
        this.conditionalPageMasterRefs.add(cpmr);
    }

    /**
     * @see org.apache.fop.fo.pagination.SubSequenceSpecifier#reset()
     */
    public void reset() {
        this.numberConsumed = 0;
    }

    public void acceptVisitor(FOTreeVisitor fotv) {
        fotv.serveRepeatablePageMasterAlternatives(this);
    }

}