<?php
/**
 * File containing the ezcDocumentXhtmlFootnoteElementFilter class
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 Document
 * @version //autogen//
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 * @access private
 */

/**
 * Filter for XHtml footnotes, as generated by ezcDocument.
 *
 * Footnotes, generated by the Document component itself, are links with the
 * class footnote and a list at the bottom of the document, also with the class
 * footnotes. Those should be converted back to Docbook footnotes.
 *
 * It is nearly impossible to generally filter footnote markup - if you come
 * across some, feel free to provide another filter for that.
 *
 * @package Document
 * @version //autogen//
 * @access private
 */
class ezcDocumentXhtmlFootnoteElementFilter extends ezcDocumentXhtmlElementBaseFilter
{
    /**
     * Array with extracted footnotes
     *
     * @var array
     */
    protected $footnotes = null;

    /**
     * Filter a single element
     *
     * @param DOMElement $element
     * @return void
     */
    public function filterElement( DOMElement $element )
    {
        // Extract footnotes, if not done yet.
        if ( $this->footnotes === null )
        {
            $this->extractFootnotes( $element );

            if ( $this->footnotes === null )
            {
                // No footnotes could be extracted from the document, so we
                // can't fetch the footnote text, just leave the element to the
                // other filters.
                return;
            }
        }

        $footnoteName = trim( $element->textContent );
        if ( !isset( $this->footnotes[$footnoteName] ) )
        {
            // We could ont find a footnote target for the current footnode, so
            // just skip this one.
            return;
        }

        // Get link text content, which is the footnote label, and remove it,
        // so it does not show up as part of the footnote
        $label = $element->textContent;
        while ( $element->firstChild )
        {
            $element->removeChild( $element->firstChild );
        }

        // Finally create footnote from element
        $element->setProperty( 'type', 'footnote' );
        $element->setProperty( 'attributes', array(
            'label' => $label,
        ) );

        $paragraph = new ezcDocumentPropertyContainerDomElement( 'span', $this->footnotes[$footnoteName] );
        $element->appendChild( $paragraph );
        $paragraph->setProperty( 'type', 'para' );
    }

    /**
     * Extract footnotes
     *
     * Extract footnotes from the given XHtml document.
     *
     * @param DOMElement $element
     * @return void
     */
    protected function extractFootnotes( DOMElement $element )
    {
        $doc = $element->ownerDocument;

        $xpath = new DOMXPath( $doc );
        // We cannot use the normal way for finding elements (//ul), as the
        // element may be part of some XHtml namespace or no namespace, which
        // may vary. We just use the localname and check for the attribute,
        // which still may give us false positives from unwanted namespaces. As
        // those external namespaces are seldomly integrated with XHtml / HTML
        // this should work in most cases.
        $nodes = $xpath->query( '//*[( local-name() = "ul" ) and contains( @class, "footnote" )]' );

        // From the found footnote lists, we extract content in a way, which
        // should work in most cases. The name is expected to be embedded in an
        // a-tag, and the contents in paragraphs. After the extraction, we
        // remove the list, as the contents will be embedded later at the
        // footnote references.
        //
        // The footnote extraction is still quite volatile as it is.
        foreach ( $nodes as $node )
        {
            // Extract all paragraphs, to get the footnote contents
            foreach ( $xpath->query( '*[local-name() = "li"]', $node ) as $footnote )
            {
                // The footnote name should be embedded in the first a-tag, as
                // it is a link target.
                $footnoteRef  = $xpath->query( './/*[local-name() = "a"]', $footnote )->item( 0 );
                $footnoteName = trim( $footnoteRef->textContent );
                $footnoteRef->parentNode->removeChild( $footnoteRef );

                // All text embedded in paragraphs
                $footnoteText = '';
                foreach ( $xpath->query( '*[local-name() = "p"]', $footnote ) as $paragraph )
                {
                    $footnoteText .= $paragraph->textContent;
                }

                // Add footnote to list of footnotes
                $this->footnotes[$footnoteName] = trim( $footnoteText );
            }

            // Remove node
            $node->parentNode->removeChild( $node );
        }
    }

    /**
     * Check if filter handles the current element
     *
     * Returns a boolean value, indicating weather this filter can handle
     * the current element.
     *
     * @param DOMElement $element
     * @return void
     */
    public function handles( DOMElement $element )
    {
        return ( ( $element->tagName === 'a' ) &&
                 $this->hasClass( $element, 'footnote' ) );
    }
}

?>
