# -*- coding: utf-8 -*-
'''
MoinMoin 1.6-1.9 - SeeSaw Macro
    @copyright: 2008,2009,2011,2013 Jim Wight
    @licence: GNU GPL, see COPYING for details

PURPOSE
    To enable sections of a page to be see-sawed (toggled) between hidden
    and shown, or unconditionally placed in either state.

DESCRIPTION
    SeeSaw creates a control (link or button) for performing its actions.  It
    has two parts: at any time, one is shown and the other hidden. SeeSaw
    uses the CSS 'display' attribute to switch between the two parts, and to
    hide and show sections. Every page element that SeeSaw affects is normally
    identified with the CSS class name 'seesaw', so that it is unlikely for it
    to accidentally affect unrelated elements.

    The control is created by a call of the form:

        <<SeeSaw(section="section", toshow="toshow", tohide="tohide",
                 show=True|False, bg="background", inline="inline",
                 image="arrow|plumin|attachment|file",
                 speed="slow|normal|fast"|integer,
                 seesaw=True|False, addclass="list", type="link|button",
                 effect="slide|fade")>>
    where

        section in basic usage - extended usage is described below - 'section'
                specifies the name of a section that the control will toggle;
                the name is attached to the parts of the control as a CSS class
                name, and needs to be attached to sections that the control is
                to affect; the default is 'section'
                 
         toshow specifies the text or image to display for showing the section; 
                the default is 'Show', except for inline sections where it
                is '»»'
                 
         tohide specifies the text or image to display for hiding the section;
                the default is 'Hide', except for inline sections where it
                is '««'
                
           show specifies whether the section is to be shown initially;
                the default is False
                 
             bg specifies a background colour to be applied to the section;
                the default is None
                 
         inline specifies the text of an inline section; SeeSaw creates inline
                sections itself, immediately after the controls;
                the default is None
                 
          image specifies the source of a pair of images to be used by the control;
                the default is None;
                
          speed specifies the rate at which the section should appear or 
                disappear, or provides a number of milliseconds for an
                animation to last;
                the default is None
                 
         seesaw SeeSaw will, by default, only toggle sections created for it,
                i.e. bearing the CSS class name 'seesaw'; this option, when
                set to False, enables non-SeeSaw elements to be toggled from a
                SeeSaw control;
                the default is True
                 
       addclass specifies a space-separated list of additional CSS class names
                to be associated with the control, and inline section, if any,
                to enable them to be manipulated using those names in other
                SeeSaw calls;
                the default is None

           type specifies the type of HTML element to create for the control;
                the default is 'link'

         effect specifies an alternative style of animation for showing and
                hiding the section; 
                the default is None

    The (leading) arguments can also be given positionally, in the order of
    the keyword arguments above, e.g.

        <<SeeSaw(section1, Show, Hide, True)>>
        
    is equivalent to
    
        <<SeeSaw(section=section1, toshow=Show, tohide=Hide, show=True)>>

EXTENDED DESCRIPTION
 section and addclass
    Sections are typically set up as follows:

        {{{#!wiki seesaw section   or   {{{#!wiki seesaw/section
        }}}                             }}}

    where the word 'section' matches the value "section" in the corresponding
    SeeSaw call. This creates an HTML div with the classes 'seesaw' and
    'section' applied. In general, a section can be any HTML element to which
    CSS classes can be applied via MoinMoin markup, e.g. divs as just
    described, tables via tableclass (for whole tables), rowclass (for rows)
    and class (for cells), or others via {{{#!html. See also 'inline' below.

    In extended usage, 'section' in the SeeSaw call is a space- or
    slash-separated list of plain or prefixed section or CSS class names
    specifying actions to be carried out when the control is clicked. The
    items can have any of the following forms:

        section  - for toggling itself and other controls and sections
                   identified by "section"
        %section - for toggling controls and sections identified by "section"
        +section - for unconditionally putting controls and sections
                   identified by "section" into their shown state
        -section - for unconditionally putting controls and sections
                   identified by "section" into their hidden state

    If there is more than one plain name, the second and subsequent are treated
    as additional CSS class names (as if added by 'addclass') to be associated
    with the control, and inline section, if any, to enable them to be
    manipulated using those names in other SeeSaw calls. For example,

        <<SeeSaw("s1 s2 all")>>

    would toggle sections identified by "s1" and its control parts would also
    be toggled by calls toggling sections identified by either "s2" or
    "all". It is equivalent to

        <<SeeSaw(s1,addclass="s2 all")>> and <<SeeSaw("s1 s2",addclass=all)>>

    The order of priority, from highest to lowest, is toggles, shows and hides,
    so it is possible, for example, to show a section that is also hidden by
    virtue of a classification in the same sequence, e.g "+s1 -all" would
    result in s1 being shown even if also covered by 'all'.
    
    If the section argument doesn't contain a plain name, then the control has
    a single state and always displays the 'toshow' part (or equivalent for
    images); the 'show' argument is not relevant.

    If you want a section to be manipulated by extra class names, i.e.
    additional plain section names or names appearing in 'addclass' arguments,
    then those names should also be added to the sections, e.g. given

        <<SeeSaw("a both")>> <<SeeSaw("b,addclass=both)>> <<SeeSaw("%both")>>

        {{{#!wiki seesaw a both    and   {{{#!wiki seesaw b both

    the first SeeSaw call would create a control toggling its own parts and the
    first section, the second call would do the same for the second section,
    and the third would toggle both sections and the parts of both controls
    (keeping them in sync with the states of the sections).

 toshow, tohide and show
    By default, sections are hidden initially, but can be shown by adding
    'show' to the relevant markup. 'show' should be set to True in the
    matching SeeSaw call if its 'tohide' and 'toshow' arguments are different
    (so that the correct one can be shown initially).

    The toshow and tohide defaults only apply if both options are not set. If
    only one is set the other is given its value.

    'toshow' and 'tohide' can accommodate text surrounding the control, to
    allow it to be different in the two cases. The three parts are
    distinguished by enclosing the text or image of the control between '<<'
    and '>>', e.g. toshow="<<Open>> me up", tohide="Now <<close>> me down". 

    (The middle parts of) 'toshow' and 'tohide' can be set independently to be
    images using the following notation:

        <<SeeSaw(s, Show, attachment:hide.png )>>

    to use an image from the page's attachments directory, and

        <<SeeSaw(s, file:/path/to/show.png, Hide)>>

    where /path/to/hide.png is a server path for loading the image from the
    filesystem.

 bg
    If a background colour is specified for a section, '"section"-bg'
    needs to be added to the corresponding markup to have the colour
    applied to the section, e.g.

        <<SeeSaw(section1, bg="#FFFFDD")>>

        ||<class="seesaw section1 section1-bg>Data||

    If there are multiple SeeSaw calls for the same section name, it is
    sufficient to use 'bg' in just one of them, with the last taking
    precedence if multiple, but different, values are given.

 inline
    The text of inline sections is embedded in SeeSaw calls. By default, inline
    sections are hidden initially, and the effects of 'show' and 'bg', and the
    addition of the 'section' and 'addclass' classes are handled directly by
    SeeSaw. Inline sections are created as HTML spans. Spans not adjacent to
    controls can be created by using the 3rd-party span macro, available at
    http://moinmo.in/MacroMarket/span.

 image
    Additional image sets can be made available as embedded data or from files.
    Instructions are in the 'Configuration' section of this file (below). When
    'image' specifies one of the provided sets, or one added by the installer,
    the middle parts of 'toshow' and 'tohide' are ignored.

    When 'image' has the value 'attachment', 'toshow' and 'tohide' (strictly
    the middle parts) are interpreted as the names of images in the page's
    attachments directory, e.g.

        <<SeeSaws(s, show.png, hide.png, image=attachment)>>

    Similarly, when 'image' has the value 'file', 'toshow' and 'tohide' are
    interpreted as server paths for directly loading user-specified images from
    the filesystem, e.g.

        <<SeeSaws(s, image=file, toshow=/img/show.png, tohide=/img/hide.png)>>

    Note that if 'image=' is used out of order, 'toshow=' and 'tohide=' must
    be used.

 seesaw 
    By setting the option 'seesaw' to False it is possible to apply SeeSaw to
    non-SeeSaw sections, e.g. the div created by the TableOfContents macro. In
    such a case, the section name in the SeeSaw call should be a class already
    attached to the div, or the class can be added as an extra name or by use
    of the addclass argument. If the section is shown initially (more than
    likely), 'show=True' should be set in order to get the control's text right
    - or the values of 'toshow' and 'tohide' can be reversed.

    When 'seesaw' is True (the default), the control affects only sections with
    'seesaw' and any of the names in the section argument attached as class
    names. When 'seesaw' is False, the control affects all sections with any
    names in the section argument attached as class names. In this case,
    sections will be shown initially.

    It is the presence of 'seesaw' on a section that causes it to be hidden
    initially. Thus, treating user-created sections as non-SeeSaw sections
    (without the addition of 'seesaw') is an option if most are to be shown
    initially.

 speed
    By default, sections are shown and hidden instantaneously. However, if a
    speed is specified, they are gradually revealed and withdrawn via an
    animation which adjusts their width, height and opacity simultaneously.
    The speed is applied to all sections specified in the section argument and
    is independent of the speeds applied to those sections in other calls,
    e.g. given

        <<SeeSaw(s1)>>   <<SeeSaw(s2, speed=fast)>> 
        <<SeeSaw("s3 %s1 %s2", speed=slow)>>

    both s1 and s2, as well as s3, will be animated slowly by the third call.

 effect
    By default, sections are shown and hidden instantaneously. The 'effect'
    option provides animations which are smoother in operation. The effect is
    applied to all sections specified in the section argument and is
    independent of the effects applied to those sections in other calls, as
    above for speed.

EXAMPLES
    Download seesawexamples.tgz from http://moinmo.in/Macromarket/SeeSaw

AUTHOR
    Jim Wight <jkwight@gmail.com>

HISTORY
    [v1.5] 2013-06-09
    Add attachment and file options to image
    Support attachment: and file: prefixes in toshow and tohide for images
    Make <<>> in toshow and tohide apply to images
    Modify first call detection according to MoinMoin version

    [v1.4] 2013-04-28
    Support both execute (Moin 1.6) and macro_MacroName (1.7+) interfaces
    Make loading of JavaScript configurable

    [v1.3] 2013-03-31
    Treat additional plain section names as added classes
    Embed image data
    Encapsulate control in span with different class from sections
    Use HTML custom data to pass bg
    Remove claim re coexistence with MoinMoin comments
    [Applicable to all HTML element types (JavaScript)]

    [v1.2] Not released
    As 1.3 but with different interepretation of effect with multiple sections

    [v1.1] 2011-01-23
    Add effect argument

    [v1.0] 2009-04-11
    Add support for multiple sections
    Add button type

    [v0.5] 2009-01-31
    Add seesaw option
    Remove unnecessary HTML if {pre|post}/{show|hide} not used
    Remove examples

    [v0.4] Not released
    Add speed option

    [v0.3] 2008-07-05
    Add image links

    [v0.2] 2008-05-12
    Accommodate different text surrounding the link

    [v0.1] 2008-05-01
    Initial version
'''


#--- Configuration --------------------------------------------
# 
jqueryjs = 'http://code.jquery.com/jquery-1.10.1.min.js'     # No installation required
# jqueryjs = '/jscript/jquery.js'                            # Or similar to load from a local file 
# jqueryjs = None                                            # If jQuery being loaded via html_head
#                                                            # See INSTALL.html
#
seesawjs = ''                                                # No installation required
# seesawjs = '/jscript/seesaw.js'                            # Or similar to load from a local file
# seesawjs = None                                            # If seesaw.js being loaded via html_head
#                                                            # See INSTALL.html
#
# Change this to extend the list of image sets, either as base64
# encodings or as filenames ('show' image first, in both cases) e.g.
#
moreimages = dict(
#    name1 = ["data:image/png;base64,...", "data:image/jpeg;base64,..."],
#    name2 = ["showname2.jpg", "hidename2.gif"]
)
#
# Change this to the relative location of additional image sets
# provided as filenames, e.g.
#
# imageprefix="/moin_static19x/common"
imageprefix = "/img"
#
#
#--- End of Configuration -------------------------------------


from MoinMoin import wikiutil
Dependencies = []
pagename = ''

# MoinMoin 1.7 and later interface
def macro_SeeSaw(macro, section=u'section', toshow=unicode, tohide=unicode, show=False, bg=u'', inline=u'', image=u'', speed=u'0', seesaw=True, addclass=u'', type=(u'link',u'button'), effect=(u'dflt',u'slide',u'fade')):
    # Compensate for lack of single-quoted argument support
    def squote(item):
        if item in (None, True, False): return item
        return item.lstrip("'").rstrip("'")

    html = SeeSaw(macro, map(squote, (section, toshow, tohide, show, bg, inline, image, speed, seesaw, addclass, type, effect))) 

    return macro.formatter.rawHTML(html)


# Pre MoinMoin 1.7 interface
def execute(macro, args):
    parser = wikiutil.ParameterParser('%(section)s%(toshow)s%(tohide)s%(show)b%(bg)s%(inline)s%(image)s%(speed)s%(seesaw)b%(addclass)s%(type)s%(effect)s')
    count, params = parser.parse_parameters(args)
    section, toshow, tohide, show, bgnd, inline, image, speed, seesaw, addclass, control, effect = (params[x] for x in ('section', 'toshow', 'tohide', 'show', 'bg', 'inline', 'image', 'speed', 'seesaw', 'addclass', 'type', 'effect'))

    if section is None:
        section = 'section'

    if show is None:
        show = False
        
    if bgnd is None:
        bgnd = ''

    if inline is None:
        inline = ''
        
    if image is None:
        image = ''

    if speed is None:
        speed = 0

    if seesaw is None:
        seesaw = True

    if addclass is None:
        addclass = ""

    if control is None:
        control = 'link'

    if effect not in ['slide', 'fade']:
        effect = 'dflt'

    html = SeeSaw(macro, (section, toshow, tohide, show, bgnd, inline, image, speed, seesaw, addclass, control, effect))

    return macro.formatter.rawHTML(html) 


def SeeSaw(macro, (section, toshow, tohide, show, bgnd, inline, image, speed, seesaw, addclass, control, effect)):
    import re
    from MoinMoin import version
    global pagename

    images = dict(
arrow = [
'''data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAU0lEQVQYlZ2QIQ6AQAwEJ9lcU4mt
v09g4OMIeASfwRSLAEoQ4yab3SUimpn1iGiZyR1I6kACq6S5khI4gEXS9CZd2SSNlZTA7u7Dr6Sy
0/O6Lz+d7FJ7hQrZj94AAAAASUVORK5CYII=''',
'''data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAd0lEQVQYlXXPMQ7DIAwF0G/pe/GS
gQkW7polU47XA3RF4gy/C4loGyx5sHmyseWcvfdesYiU0hskC4AGQA/ZSBZIAoBjgQ5JgCS4e32Y
1ty93kgSzOyckZmd19uNImKbUURsf2j8bR9o/+rPxbj0RbIs0bX2t/cBi+KBCE326nsAAAAASUVO
RK5CYII='''],
plumin = [
'''data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAL0lEQVQYlWNkYGD4z0AEIKToPxMx
mrApwgAsOEyAsRlxGY/BJ8o6bIoYsQkQDCcAU8AKB/g5WdcAAAAASUVORK5CYII=''',
'''data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAKklEQVQYlWNkYGD4z0AEIKToPxMx
phCliIWAtYzoihgpso4oRYw43IICAElFBRBz9sLvAAAAAElFTkSuQmCC
''']
)
    images.update(moreimages)

    if tohide is None and toshow is None:
        if not inline:
            toshow = 'Show'; tohide = 'Hide'
        else:
            toshow = u'»»';  tohide = u'««'
    elif tohide is None and toshow is not None:
        tohide = toshow
    elif toshow is None and tohide is not None:
        toshow = tohide

    try:
        speed = int(speed)
    except ValueError:
        speed = "'%s'" % speed

    if seesaw:
        seesaw = 'true' 
    else:
        seesaw = 'false'

    if control == 'button':
        opentag = closetag = 'button'
    else:
        opentag = 'a href=""'; closetag = 'a'

    sections = ' '.join(section.strip().replace(' ', '/').split('/')).split()
    plain = [x.encode("iso-8859-1") for x in sections if not re.compile(r'^[%+-].*').match(x)]
    toggles = [x.lstrip('%').encode("iso-8859-1") for x in sections if x.startswith('%')]
    shows   = [x.lstrip('+').encode("iso-8859-1") for x in sections if x.startswith('+')]
    hides   = [x.lstrip('-').encode("iso-8859-1") for x in sections if x.startswith('-')]
    try:
        section = plain[0]
        sectionarg = '''{toggle:%s, show:%s, hide:%s}''' % ([section] + toggles, shows, hides)
        addclass += ' '.join(plain[1:])
    except IndexError:
        section = ""
        sectionarg = '''{toggle:%s, show:%s, hide:%s}''' % (toggles, shows, hides)

    regex = re.compile(r'(.*)<<(.*)>>(.*)')
    preshw = postshw = prehd = posthd = ""
    matches = regex.match(toshow)
    if matches:
        preshw, toshow, postshw = matches.groups()
    matches = regex.match(tohide)
    if matches:
        prehd, tohide, posthd = matches.groups()

    vermin = int(version.release_short[1])
    if vermin < 9:
        url = macro.request.getScriptname() + macro.request.getPathinfo()
    else:
        url = macro.request.script_root + macro.request.path

    attimg = '''<img src="''' + wikiutil.escape(url + '''?action=AttachFile&do=get&target=%s''') + '''" style="vertical-align:middle"/>'''
    fsimg = '''<img src="''' + wikiutil.escape("%s") + '''" style="vertical-align:middle"/>'''
    if image:
        if image == 'attachment':
            toshow, tohide = (attimg % x for x in (toshow, tohide))
        elif image == 'file':
            toshow, tohide = (fsimg % x for x in (toshow, tohide))
        else:
            imagepair = images.get(image)
            if imagepair:
                if not imagepair[0].startswith("data:image"):
                    imagepair = (imageprefix.rstrip("/") + "/" + x for x in imagepair)
                toshow, tohide = (fsimg % x for x in imagepair)
    else:
        def toshowhide(arg):
            if arg.startswith('attachment:'):
                return attimg % arg[11:]
            if arg.startswith('file:'):
                return fsimg % arg[5:]
            return wikiutil.escape(arg)
        toshow, tohide = toshowhide(toshow), toshowhide(tohide)

    section, preshw, postshw, prehd, posthd, bgnd, inline = (wikiutil.escape(x) for x in (section, preshw, postshw, prehd, posthd, bgnd, inline))

    if not inline:
        inlinesection = ""
    else:
        inlineopts = ""
        if bgnd:
            inlineopts += ''' %s-bg''' % section
        if show:
            inlineopts += " show"
        inlinesection = '''<span class="seesaw %(section)s %(addclass)s %(inlineopts)s">%(inline)s</span>''' % locals()

    showpart = 'show part'; hidepart = 'hide part'
    showstyle = '''style="display:inline"'''; hidestyle = '''style="display:none"'''
    if show and section:
        showstyle, hidestyle = hidestyle, showstyle

    preshow = postshow = prehide = posthide = ''
    prepost = '''<span class="%s" %s>%s</span>'''
    if preshw:  
        preshow  = prepost % (showpart, showstyle, preshw)
    if postshw:  
        postshow = prepost % (showpart, showstyle, postshw)
    showpart = '''<span class="%(showpart)s" %(showstyle)s>%(toshow)s</span>''' % locals()

    if section:
        if prehd:  
            prehide  = prepost % (hidepart, hidestyle, prehd)
        if posthd: 
            posthide = prepost % (hidepart, hidestyle, posthd)
        hidepart = '''<span class="%(hidepart)s" %(hidestyle)s>%(tohide)s</span>''' % locals()
        if bgnd:
            bgdata = '''data-section="%(section)s" data-bg="%(bgnd)s"''' % locals()
        else:
            bgdata = ""
    else:
        hidepart = bgdata = ""

    html = '''
<span class="seesawc %(section)s %(addclass)s" %(bgdata)s>
%(preshow)s
%(prehide)s
<%(opentag)s onClick="seeSaw(%(sectionarg)s, '%(effect)s', %(speed)s, %(seesaw)s); return false;">
%(showpart)s
%(hidepart)s
</%(closetag)s>
%(postshow)s
%(posthide)s
</span>
%(inlinesection)s
''' % locals()

    if vermin < 9:
        if  macro.formatter.page.page_name != pagename:
            pagename = macro.formatter.page.page_name
            html = SeeSawJS(jqueryjs, seesawjs) + html
    else:
        if  macro.request.uid_generator('SeeSaw') == 'SeeSaw':
            html = SeeSawJS(jqueryjs, seesawjs) + html

    return html


def SeeSawJS(jquery, seesaw):
    antijump = '''
<style type="text/css">
div.seesaw, span.seesaw, table.seesaw, tr.seesaw, td.seesaw { display: none; }
div.seesaw.show { display: block; }
span.seesaw.show { display: inline; }
table.seesaw.show { display: table; }
tr.seesaw.show { display: table-row; }
td.seesaw.show { display: table-cell; }
</style>
'''

    if seesaw is None:
        if jquery is None:
            return antijump
        return '<p><span style="font-size:150%;color:red">Incompatible configuration combination of jquery and seesaw</span></p>'

    if jquery is None:
        jqueryjs = ''
    else:	
        jqueryjs = '''
<script type="text/javascript" src="%s"></script>
''' % jquery

    if seesaw:
	seesawjs = '''
<script type="text/javascript" src="%s"></script>
''' % seesaw
    else:
	seesawjs = '''
<script type="text/javascript">var seesawc,seesaws,seesawfx={toggle:{slide:"slideToggle",fade:"fadeToggle",show:"toggle",hide:"toggle"},show:{slide:"slideDown",fade:"fadeIn",show:"hide",hide:"show"},hide:{slide:"slideUp",fade:"fadeOut",show:"show",hide:"hide"}};function seeSaw(f,b,e,a){var d,c;$.each(["toggle","show","hide"],function(){c=seesawfx[this];c.dflt=this;$.each(f[this],function(){seesawc.filter((d="."+this)).find(".part").not(".seesawed").addClass("seesawed").filter(".show")[c.show]().end().filter(".hide")[c.hide]();(a?seesaws.filter(d):$(d)).not(".seesawc,.seesawed").addClass("seesawed").each(function(){e?$(this)[c[b]](e):$(this)[c[b]]()})})});$(".seesawed").removeClass("seesawed")}$(document).ready(function(){var a;(seesaws=$(".seesaw")).not(".show").hide();(seesawc=$(".seesawc")).each(function(){if((a=$(this).data())){seesaws.filter("."+a.section+"-bg").css("background-color",a.bg)}})});</script>
'''

    return antijump + jqueryjs + seesawjs
