import sys, re from bazbase.wiki import (macros,environment,illegal,divided, WikiException, NoContentException, safesplit, parse_macro_args, recurse, baz_eval, full_eval, leafprop, render_propval, render_allow_override, get_format, get_element, get_current_element, get_propname, atom, eoverride, moverride, poverride) from bazbase.flavors import FORMATS from bazbase import config from redbeans.tokens import * from . import custom def moverride_thunk(ename, pname, form): with moverride(dict(form=form)): for t in recurse(ename, pname): yield t def get_headfoot(form, ename): return (moverride_thunk(ename, u'topleft', form), moverride_thunk(ename, u'topright', form), moverride_thunk(ename, u'bottomleft', form), moverride_thunk(ename, u'bottomright', form)) def html_div_headers(headfoot): yield Literal('') yield Literal('
') for t in headfoot[1]: yield t yield Literal('
') yield Literal('
') for t in headfoot[2]: yield t yield Literal('
') yield Literal('
') for t in headfoot[3]: yield t yield Literal('
') def html_table_header(headfoot): yield Literal('') for t in headfoot[0]: yield t yield Literal('') for t in headfoot[1]: yield t yield Literal('') def html_table_footer(headfoot): yield Literal('') for t in headfoot[2]: yield t yield Literal('') for t in headfoot[3]: yield t yield Literal('') def latex_cmd_headers(headfoot): yield Literal('\\lhead{') for t in headfoot[0]: yield t yield Literal('}\\rhead{') for t in headfoot[1]: yield t yield Literal('}\\lfoot{') for t in headfoot[2]: yield t yield Literal('}\\rfoot{') for t in headfoot[3]: yield t yield Literal('}\n') def latex_arg_headers(headfoot): for hf in headfoot: for t in hf: yield t yield Literal('}{') def form(argstr): """Placeholder that evaluates to 'sheet' or 'card' or 'badge' when evaluating headers and footers.""" return "" macros.form = form def sheet(argstr, content): """Render content as a sheet. Takes an element to get headers and footers from, and optionally a 'color' parameter for paper color.""" args = parse_macro_args(argstr) color = "" headfoot = [(),(),(),()] for key,val in args.items(): if key == 'color': color = baz_eval(val).strip() elif key == 1: headfoot = get_headfoot('sheet', val) else: raise WikiException("Unknown arg '%s' in sheet!" % (key)) if not color.strip(): color = "white" if get_format() == FORMATS['html']: yield Literal('
' % color) for t in html_div_headers(headfoot): yield t for t in content: yield t yield Literal('
') elif get_format() == FORMATS['tex']: yield Literal('\\begin{sheet}\n') for t in latex_cmd_headers(headfoot): yield t for t in content: yield t yield Literal('\n\\end{sheet}') else: for t in content: yield t macros.sheet = sheet flippat = re.compile(r'<>') def card(argstr, content): """Render content as a card. Takes an element to get headers and footers from, and optionally a 'color' parameter for paper color and a 'size' parameter.""" parts = safesplit(content,flippat) args = parse_macro_args(argstr) color = "" size = "" headfoot = [(),(),(),()] for key,val in args.items(): if key == 'color': color = baz_eval(val).strip() elif key == 'size': size = " "+baz_eval(val).strip() elif key == 1: headfoot = get_headfoot('card', val) else: raise WikiException("Unknown arg '%s' in card!" % (key)) if not color.strip(): color = "white" if len(parts) > 2: raise WikiException("More than one ##flip## in a ##card##!") else: if len(parts) < 2: front = parts[0] back = "" else: front,back = parts if get_format() == FORMATS['html']: yield Literal('\n' % (color, size)) for t in html_table_header(headfoot): yield t yield Literal('\n\n') yield Literal('\n\n') for t in html_table_footer(headfoot): yield t yield Literal('
\n') for t in front: yield t yield Literal('\n\n') for t in back: yield t yield Literal('\n
\n') elif get_format() == FORMATS['tex']: yield Literal('\\begin{card}%s\n' % ("[%s]" % size.strip() if size.strip() else "")) yield Literal('\\front{') for t in latex_cmd_headers(headfoot): yield t for t in front: yield t yield Literal('}\n\\back{') for t in back: yield t yield Literal('}\n\\end{card}\n') else: for t in front: yield t yield Entity(ENV_BREAK) for t in back: yield t macros.card = card macros.flip = illegal('flip','card') def packet(argstr,content): """Render content as a packet. Takes a label for the outside and an element to get headers and footers from, and optionally a 'color' parameter for paper color and a 'style' parameter ("envelope" or "folding").""" args = parse_macro_args(argstr) label = "" color = "" style = "Envelope" headfoot = [(), (), (), ()] for key,val in args.items(): if key == 'color': color = baz_eval(val).strip() elif key == 1: label = baz_eval(val).strip() elif key == 2: headfoot = get_headfoot('packet', val) elif key == 'style': style = baz_eval(val).strip().capitalize() if style not in ("Envelope","Folding"): raise WikiException('The only valid packet styles are "envelope" and "folding"!') else: raise WikiException("Unknown arg '%s' in packet!" % (key)) if not color.strip(): color = "white" if get_format() == FORMATS['html']: yield Literal('\n' % (style.lower(), color)) for t in html_table_header(headfoot): yield t yield Literal('\n\n') yield Literal('\n\n') for t in html_table_footer(headfoot): yield t yield Literal('
\n') for t in label: yield t yield Literal('\n\n') for t in content: yield t yield Literal('\n
\n') elif get_format() == FORMATS['tex']: yield Literal('\\%sPacket{' % (style,)) for t in latex_arg_headers(headfoot): yield t for t in label: yield t yield Literal('}{') for t in content: yield t yield Literal('}') else: yield Text("Packet: ") for t in label: yield t macros.packet = packet def notebook(argstr,content): args = parse_macro_args(argstr) front = "" color = "" headfoot = [(), (), (), ()] for key,val in args.items(): if key == 'color': color = baz_eval(val).strip() elif key == 1: front = full_eval(val) elif key == 2: headfoot = get_headfoot('notebook', val) else: raise WikiException("Unknown arg '%s' in notebook!" % (key)) if not color.strip(): color = "white" if get_format() == FORMATS['html']: yield Literal('
\n' % (color)) yield Literal('
') for t in html_div_headers(headfoot): yield t for t in front: yield t yield Literal('
') for t in content: yield t yield Literal('
\n') elif get_format() == FORMATS['tex']: yield Literal('\\startnotebook{') for t in latex_arg_headers(headfoot): yield t for t in front: yield t yield Literal('}{') for t in content: yield t yield Literal('\n\\endnotebook\n') else: yield Text("Notebook: ") for t in front: yield t macros.notebook = notebook def page(argstr,content): """Dummy page for previewing outside a notebook.""" args = parse_macro_args(argstr) if 1 in args: title = baz_eval(argstr).strip() else: title = getattr(get_format(), 'gknextpage', 1) get_format().gknextpage = title+1 title = unicode(title) if get_format() == FORMATS['html']: yield Literal('

') yield Text(title) yield Literal('

\n') for t in content: yield t yield Literal('
') elif get_format() == FORMATS['tex']: yield Literal(r"\begin{nbpage}{") yield Text(title) yield Literal('}\n') for t in content: yield t yield Literal('\n\\end{nbpage}\n') else: yield Start(HEADING, 2) yield Text(Title) yield End(HEADING, 2) for t in content: yield t macros.page = page def badge(argstr,content): args = parse_macro_args(argstr) color = "" style = "light" headfoot = [(), (), (), ()] for key,val in args.items(): if key == 'color': color = baz_eval(val).strip() elif key == 1: headfoot = get_headfoot('badge', val) elif key == 'style': style = baz_eval(val).strip() if style not in ("light","dark"): raise WikiException('The only valid badge styles are "light" and "dark"!') else: raise WikiException("Unknown arg '%s' in badge!" % (key)) if not color.strip(): color = "white" if get_format() == FORMATS['html']: yield Literal('
\n' % (style.lower(), color)) for t in html_div_headers(headfoot): yield t for t in content: yield t yield Literal('
\n') elif get_format() == FORMATS['tex']: yield Literal('\\NameBadge[%s]{' % (style,)) for t in latex_arg_headers(headfoot): yield t for t in content: yield t yield Literal('}') else: yield Text("Badge: ") for t in content: yield t macros.badge = badge def sign(argstr,content): args = parse_macro_args(argstr) name = "" color = "" size = "Big" location = "" blurb = "" headfoot = [(), (), (), ()] for key,val in args.items(): if key == 'color': color = baz_eval(val).strip() elif key == 'blurb': blurb = baz_eval(val).strip() elif key == 'location': location = baz_eval(val).strip() elif key == 'size': size = baz_eval(val).strip().capitalize() if size not in ("Big","Medium","Small"): raise WikiException("Sign size must be big, medium, or small!") elif key == 1: name = baz_eval(val).strip() elif key == 2: headfoot = get_headfoot('sign', val) else: raise WikiException("Unknown arg '%s' in packet!" % (key)) if not color.strip(): color = "white" if get_format() == FORMATS['html']: yield Literal('
\n' % (size.lower(), color)) for t in html_div_headers(headfoot): yield t yield Literal('
') yield Text(name) yield Literal('
') yield Literal('
\n') for t in content: yield t yield Literal('
\n') yield Literal('
') yield Text(blurb) yield Literal('
\n') yield Literal('
\n') elif get_format() == FORMATS['tex']: yield Literal('\\%sSign{' % (size,)) for t in latex_arg_headers(headfoot): yield t yield Text(name) yield Literal('}{') yield Text(location) yield Literal('}{') for t in content: yield t yield Literal('}{') yield Text(blurb) yield Literal('}') else: yield Text('Sign: ') for t in content: yield t macros.sign = sign #macros.header = divided('header','gap','left','right') #macros.footer = divided('footer','gap','left','right') #macros.gap = illegal('gap','header','footer') def imageheader(argstr, content=None): args = parse_macro_args(argstr) if 1 not in args or 2 not in args or 3 not in args or len(args) != 3: raise WikiException( "##imageheader## takes exactly 3 parameters!") if content is None: raise NoContentException("##imageheader## needs content!") width = baz_eval(args[2]).strip() height = baz_eval(args[3]).strip() img = Entity(IMAGE, "%s^d%sx%s" % (args[1], width, height)) if get_format() == FORMATS['html']: yield Literal('\n') yield Literal('\n') yield Literal('\n') yield Literal('
') yield img yield Literal('\n') for t in content: yield t yield Literal('
\n') elif get_format() == FORMATS['tex']: yield Literal('\\imageheader{') yield img yield Literal('}{%spx}{%spx}{' % (width, height)) for t in content: yield t yield Literal('}') else: yield Start(HEADER, 1) for t in content: yield t yield End(HEADER, 1) macros.imageheader = imageheader macros.secret = environment( 'secret', doc="""Mark this content as secret, for printing.""") def current_run(): return '1' def runtime(argstr): ename, propname = argstr.split('.', 1) element = get_element(ename) propname = get_propname(propname) propval = element[propname] run = current_run() if propval.has_overlay(run): yield Start(STRIKE) yield Entity(MACRO, ('%s.%s' % (ename, propname), None)) yield End(STRIKE) yield Text(' ') yield Start(BOLD) yield Text(propval.get_overlay(run)) yield End(BOLD) else: for t in recurse(ename, propname): yield t macros.runtime = runtime def contexteval(argstr): """Call as {{{<>}}} in an element's substitution to allow that element to serve as a context for name/pronoun/prop evaluation.""" args = parse_macro_args(argstr) if 1 not in args or 2 not in args or 3 not in args or len(args) != 3: raise WikiException( "##contexteval## takes exactly 3 parameters!") ename = args[1] current = get_element(ename) carg = render_allow_override(current, args[2]) ccontent = render_allow_override(current, args[3]) with eoverride({u'current': current}): if ccontent is not None: for t in ccontent(None, None): yield t elif carg: yield Entity(MACRO, (carg, None)) else: for t in recurse(ename, u'name'): yield t macros.contexteval = contexteval GENDER_CONV_MAP = {'?': 'A'} def pronoun(**mfna): def pn(argstr): e = get_current_element() genderp = u'gender' gen = render_allow_override(e, genderp, '?')[0:1].upper() if gen in GENDER_CONV_MAP: gen = GENDER_CONV_MAP[gen] if gen in mfna: yield Text(mfna[gen]) else: yield Text(mfna['A']) return pn # Helpers def _pron(mac, M, F, **mfna): mfna['M'] = M mfna['F'] = F if 'A' not in mfna: mfna['A'] = u'%s/%s' % (mfna['M'], mfna['F']) if 'S' not in mfna and mac.startswith('th'): mfna['S'] = mac[2:] for a in [mac] + mfna.keys(): if not hasattr(macros, a): pn = pronoun(**mfna) if a == mac: pn.hidden = 'pronoun' else: pn.hidden = 'pronounvariation' setattr(macros, a, pn) upper = dict((k, mfna[k][0].upper() + mfna[k][1:]) for k in mfna) mac = mac[0].upper()+mac[1:] for a in [mac] + upper.keys(): if not hasattr(macros, a): pn = pronoun(**upper) pn.hidden = 'pronounupper' setattr(macros, a, pn) # First in the list wins _pron('they', 'he', 'she', N='it') _pron('them', 'him', 'her', N='it') _pron('their', 'his', 'her', N='its') _pron('theirs', 'his', 'hers', N='its') _pron('themself', 'himself', 'herself', N='itself') _pron('spouse', 'husband', 'wife', N='spouse', A='spouse') _pron('offspring', 'son', 'daughter', N='child', A='child') _pron('kid', 'boy', 'girl', N='kid', A='kid') _pron('sibling', 'brother', 'sister', N='sibling', A='sibling') _pron('parent', 'father', 'mother', N='parent', A='parent') _pron('uncle', 'uncle', 'aunt') _pron('nephew', 'nephew', 'niece') _pron('human', 'man', 'woman', N='human', A='human') _pron('gender', 'male', 'female', N='neuter', A='uncertain', S='other') PRE='<<
>>'
SUF='<<>>'
def parsename(name):
    gd = dict((k, '') for k in ('first', 'middle', 'last',
                                'prefix', 'suffix'))
    gd['full'] = name
    if SUF in name:
        name, gd['suffix'] = name.split(SUF, 1)
        gd['full'] = gd['full'].replace(SUF, ' ')
    elif ", " in name:
        name, gd['suffix'] = name.split(", ", 1)

    if PRE in name:
        gd['prefix'], name = name.split(PRE, 1)
        gd['full'] = gd['full'].replace(PRE, ' ')
    
    bits = name.split()
    assert len(bits) >= 1
    if len(bits) == 1:
        gd['first'] = bits[0]
    elif not gd['prefix'] and bits[0].endswith('.'):
        gd['prefix'] = bits[0]
        if len(bits) == 2:
            gd['last'] = bits[1]
        elif len(bits) == 3:
            gd['first'], gd['last'] = bits[1:]
        else:
            gd['first'] = bits[1]
            gd['last'] = bits[-1]
            gd['middle'] = ' '.join(bits[2:-1])
    else:
        if len(bits) == 2:
            gd['first'], gd['last'] = bits
        else:
            gd['first'] = bits[0]
            gd['last'] = bits[-1]
            gd['middle'] = ' '.join(bits[1:-1])

    gd['informal'] = gd['first'] or gd['last']
    
    gd['formal'] = gd['last'] or gd['first']
    if gd['prefix']:
        gd['formal'] = "%s %s" % (gd['prefix'], gd['formal'])
          
    return gd
    
def namepart(part, nameprop=u'name'):
    def np(argstr):
        if argstr.strip():
            namep = argstr.strip()
        else:
            namep = nameprop
        e = get_current_element()
        name = render_allow_override(e, namep,
                                     moverrides=dict(pre=PRE, suf=SUF))
        if name is None:
            raise WikiException("%s.%s is not defined for name parsing!"
                                % (e.ename,namep))
        #print >>sys.stderr, repr(name)
        namedict = parsename(name)
        yield Text(namedict[part])
    np.hidden = 'namepart'
    return np

macros.pre = atom(
    default=" ",
    doc="Marks the end of a title or prefix for name parsing.")
macros.suf = atom(
    default=" ",
    doc="Marks the start of an end title or suffix for name parsing.")
macros.first = namepart('first')
macros.middle = namepart('middle')
macros.last = namepart('last')
macros.full = namepart('full')
macros.formal = namepart('formal')
macros.informal = namepart('informal')
macros.player = leafprop(u'player')
macros.contact = leafprop(u'contact')