#!/usr/bin/python

# html_server_button.py
#       --copyright--                   Copyright 2009 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       November 21, 2009       bar
#       November 24, 2009       bar     some class and style changes
#       November 25, 2009       bar     use onmousedown
#       November 29, 2009       bar     take initial slash out of name
#                                       take query args off comparison path
#                                       compare to end of path, not the whole thing
#       December 1, 2009        bar     routine name change
#       December 6, 2009        bar     optional up/down sensing
#       November 29, 2011       bar     pyflake cleanup
#       --eodstamps--
##      \file
#
#
#       Implement a simple HTML button that actually is just a thing to ping the server with its name.
#       Its most complete code assumes a tz_http_server is driving it.
#
#       The http hit gets (we can send in deliver_web_page()) TINY_PNG, named by the button's name.
#           The GET has this query variable:
#               history             "...d###u###d###u###"       Latest HISTORY number of characters of events.
#                   Events are a "d" or "u" followed by a number (javascript Date().getTime().
#                   As time goes on the first event may be only partially in the 'history' value. Ignore anything on the left until the first 'd' or 'u'.
#
#       TODO:
#
#           Capture the mouse when it goes "down" in attempt to get the matching "up".
#
#


import  re

import  tzlib



WIDTH                   = 40
HITE                    = 25

MINIMUM_HISTORY         =  28
HISTORY                 = 196            # how many characters to remember and send with each event (so that data is not lost) (14 events)


EVT_UP                  = 'u'
EVT_DOWN                = 'd'


TINY_PNG                = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53\xde\x00\x00\x00\x12\x49\x44\x41\x54\x78\x9c\x62\x48\x68\x38\x00\x00\x00\x00\xff\xff\x03\x00\x02\xe4\x01\xa1\xfc\xf9\x44\x4e\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"


pure_url_re             = re.compile(r"\?.*", re.DOTALL)

event_re                = re.compile(r"([ud])(\d+)", re.DOTALL)


class   an_event(object) :
    def __init__(me, evt, tick) :
        me.evt          = str(evt)
        me.tick         = int(tick)
    def __cmp__(me, om) :
        return(cmp(me.tick, om.tick))
    def __str__(me)     :
        return("{ 'evt':%s 'tick':%d }" % ( me.evt, me.tick ) )
    #   an_event



class   an_html_server_button(object) :

    button_style        = "display:inline-block;border-style:outset;border-width:1px;background:#c0c0c0;"

    head_injection_re   = re.compile(r"(</head)", re.IGNORECASE)
    html_injection_re   = re.compile(r"(</html)", re.IGNORECASE)

    injection_html      = ( ""
                          + """\r\n<script language="javascript">\r\n<!--\r\n""" + re.sub("\s+", " ", tzlib.multiline_strip("""

                            function    cancel_html_server_button_event(e)
                            {
                                e   = e ? e : (window.event ? window.event : null);
                                if (e)
                                {
                                    e.cancelBubble  = true;
                                    if (e.stopPropagation)
                                    {
                                        e.stopPropagation(true);
                                    }

                                    e.returnValue   = false;
                                    if (e.preventDefault)
                                    {
                                        e.preventDefault();
                                    }
                                }

                                return(false);
                            };

                            function    do_an_html_server_button(e, name, url, down_up, his_cnt)
                            {
                                his_cnt = (his_cnt && (+his_cnt >= %u)) ? his_cnt : %u;
                                down_up = down_up ? down_up : "_unknown_";

                                var el  = document.getElementById(name + '_img');

                                var hnm = 'an_html_server_button_his';
                                var his = el.getAttribute(hnm) ? el.getAttribute(hnm) : "";
                                his     = "" + his + down_up + new Date().getTime();
                                his     = his.substring(Math.max(0, his.length - his_cnt));
                                el.setAttribute(hnm, his);

                                el.src  = url + '?history=' + his;      /* hit the server */

                                return(cancel_html_server_button_event(e));
                            };

                            """.strip()))
                          + """\r\n-->\r\n</script>\r\n"""
                          ) % ( MINIMUM_HISTORY, HISTORY )
    pass


    @staticmethod
    def inject_into_html(html = "") :
        """ Put our generic javascript in to the given web page. May safely be called more than once on a web page string."""

        html    = html or ""

        if  html.find(an_html_server_button.injection_html) >= 0 :
            return(html)

        if  an_html_server_button.head_injection_re.search(html) :
            return(an_html_server_button.head_injection_re.sub(an_html_server_button.injection_html + r"\1", html, 1))

        if  an_html_server_button.html_injection_re.search(html) :
            return(an_html_server_button.html_injection_re.sub(an_html_server_button.injection_html + r"\1", html, 1))

        return(html + an_html_server_button.injection_html)


    @staticmethod
    def deliver_web_page(handler, is_head = False) :
        if  handler.send_http_headers(is_head, content_type = "image/png") :
            handler.write_data(TINY_PNG)
        pass



    def __init__(me, name, width = WIDTH, hite = HITE, button_class = None, button_style = None, history_count = HISTORY, sense_down = True, sense_up = False) :

        width               = width or WIDTH
        hite                = hite  or HITE
        button_style        = button_style or ""
        button_class        = button_class or ""
        if  (not button_style) and (not button_class) :
            button_style    = me.button_style
        me.his_cnt          = ((history_count and (history_count >= MINIMUM_HISTORY)) and history_count) or HISTORY

        me.name = name
        me.url  = me.name + ".png"

        me.ondn = """onmousedown="return(do_an_html_server_button(event, '%s', '%s', '%s', '%u'));" """ % ( me.name, me.url, EVT_DOWN, me.his_cnt, )
        me.onup = """onmouseup  ="return(do_an_html_server_button(event, '%s', '%s', '%s', '%u'));" """ % ( me.name, me.url, EVT_UP,   me.his_cnt, )

        ds      = (sense_down and me.ondn) or ""
        us      = (sense_up   and me.onup) or ""

        me.htm  = """\r\n<div id    = "%s_div"
                              %s
                              style = "width:%upx;height:%upx;%s"
                              %s
                              %s
                              >
                         <img id="%s_img" src="%s" width="0px" height="0px" style="visibility:hidden;margin:0px;padding:0px;border-style:none;">
                         </div>\r\n""" % ( me.name, button_class, width, hite, button_style, ds, us, me.name, me.url, )
        me.htm  = tzlib.multiline_strip(me.htm)

        me.latest_event     = None


    def is_this_button(me, path) :
        path        =  pure_url_re.sub("", path)
        return(path.endswith(me.url))


    def get_new_events_from_args(me, args, get_all_events_from_args = False) :
        ea                  = []
        his                 = args.get('history', [ "" ])[0]
        if  his             :
            ea              = event_re.findall(his)
            if  len(ea)     :
                try         :
                    ea      = [ an_event(evt[0], evt[1]) for evt in ea ]
                except ( ValueError, TypeError) :
                    ea      = []

                t           = ((not get_all_events_from_args) and me.latest_event) or an_event('_', -1)
                ea          = [ evt for evt in ea if evt > t ]

                if  len(ea) :
                    evt     = ea[-1]
                    me.latest_event = an_event(evt.evt, evt.tick)
                pass
            pass

        return(ea)


    pass    # an_html_server_button


#
#
#       Run
#
#
if __name__ == '__main__' :
    import  os
    import  sys

    import  TZCommandLineAtFile


    program_name    = os.path.basename(sys.argv.pop(0))
    TZCommandLineAtFile.expand_at_sign_command_line_files(sys.argv)

    me  = an_html_server_button("button_17")
    print me.url, me.inject_into_html()
    print str([ str(evt) for evt in me.get_new_events_from_args( { 'history' : [ 'd123u456' ] } ) ])
    print str([ str(evt) for evt in me.get_new_events_from_args( { 'history' : [ 'd123u456d457' ] } ) ])


#
#
#
# eof

