<?xml version="1.0" encoding="UTF-8" ?>

<!-- 
Copyright (C) 2009 Vipadia Limited.  All Rights Reserved. 
Vipadia and ClackPoint are trademarks of Vipadia Limited.
-->

<Module>
  <ModulePrefs title="ClackPoint" 
               description="ClackPoint gadget for Google Wave provides live audio chat in your waves. Users can call in either directly from the web page, or by using a telephone and entering a PIN.  See http://clackpoint.com/gadgets/wave/ for details. ClackPoint is free while in beta but limitations may apply. Terms and conditions at http://clackpoint.com/terms-and-conditions/"
               thumbnail="http://files.clackpoint.com/images/thumbnail.png"
               screenshot="http://files.clackpoint.com/images/screenshot.png"

               author="Vipadia Limited"
               author_email="beta+wave@clackpoint.com"
               author_location="Cambridge, UK"
               author_affiliation="Vipadia Limited"

               scrolling="true" 
               >
    <Require feature="wave-preview" />
    <Require feature="dynamic-height" />
    <Require feature="analytics" />
  </ModulePrefs>
  
  <Content type="html"><![CDATA[
    <script type="text/javascript" src="http://files.clackpoint.com/plugins/swfobject.js"></script>
    <script type="text/javascript" src="http://files.clackpoint.com/javascript/jquery.js"></script>
    <script type="text/javascript">
        if (!console) // in case no firebug
        {
            var console = {};
            console.log = function(text){return};
        }

        if (!CP)
        {
            var CP = {};
            CP.DEBUG = false;
            CP.code_length = 15;
            CP.code = null;
            CP.wave_loaded = false;
            CP.flash_loaded = false;
            CP.gate_function = null;
            CP.reload_gate = 0;
            CP.nickname = "80087355";

            CP.member_help_clicked = false;
            CP.phone_clicked = false;
            CP.manager = null;
            CP.controls = Array();
            CP.members = Array();
            CP.callers = Array();

            CP.Caller_state = { 'muted'   : 'muted', 
                                'silent'  : 'silent', 
                                'speaking': 'speaking', 
                                };
            CP.caller_state = Array();

            CP.Hand_state = { 'up': 'up', 'down':'down' };
            CP.hand_state = Array();
            CP.my_hand_state = CP.Hand_state.down;

            CP.Mpfx = "_cp__member__";
            CP.Hpfx = "_cp__hand__";
        }

        /**
         * Utility functions
         */        
        err = function(msg) { console.log(msg); }
        dbg = function(msg) { if (CP.DEBUG) console.log(msg); }
        sanitize = function(s) { return s.split(/[^0-9a-zA-Z_:-]/).join("_"); }
        member_id = function(nick) { return "member-"+sanitize(nick); }
        gate_reload = function() 
        { 
            dbg("+ gate_reload: reload_gate:"+CP.reload_gate);
            CP.reload_gate += 1;
            if (CP.reload_gate > 7) 
            {
                CP.reload_gate = 0;
                err("  gate_reload: reloading!");
                location.reload(); 
            }
            dbg("- gate_reload");
        }
        trim_nick = function(nick) 
        {
            var re = /--[0-9]+$/;
            if (re.test(nick)) return RegExp.leftContext;
            return nick;
        } 
        submit_delta = function(delta)
        {
            if (CP.manager == CP.nickname)
                wave.getState().submitDelta(delta);
        }
    
        /**
         * State manipulation   
         */
        member_called = function(nick)
        {
            dbg("+ member_called: nick:"+nick);
            var members = wave.getParticipants();
            var tnick = trim_nick(nick);
            for (var i=0; i < members.length; ++i) 
            {
                var member = members[i];
                dbg("  i:"+i+" tnick:"+tnick+" member:"+member.getId());
                if (member.getId() == tnick) 
                {
                    CP.callers[nick] = member;
                    CP.controls[nick] = (CP.nickname == nick);
                    break;
                }                
            }
            if (nick in CP.callers)
            {
                var caller = CP.callers[nick];
                var photo = caller.getThumbnailUrl();
                var name = caller.getDisplayName();
                dbg("  caller:"+caller+" photo:"+photo+" name:"+name);
                render_member_called(nick, name, photo);
            }
            dbg("- member_called");
        }

        member_departed = function(nick)
        {
            dbg("+ member_departed: nick:"+nick);
            delete CP.callers[nick];
            delete CP.caller_state[nick];
/*
            if (nick == CP.nickname) 
            {
                CP.my_hand_state = CP.Hand_state.down;
                $('#hand a').html("Hand up");
                $('#hand').css('display', 'none');
            }
*/
            dbg("- member_departed");
        }
         
        /**
         * DOM manipulation
         */
        render_member_called = function(nick, name, photo)
        {
            dbg("+ render_member_called: nick:"+nick+" photo:"+photo);
            var kickhand_html =
                '<tr cellspacing="0" cellpadding="0"><td cellspacing="0" cellpadding="0">'
                +  ((CP.nickname == nick)
                    ?'<a id="hand" class="footer" href="#" onclick="member_handupdown(); return false;">Raise hand</a>'
                    :'<a class="footer" href="#" onclick="member_kick('+"'"+nick+"'"+'); return false;">Kick</a>'
                    )
                +'</td></tr>';

            var html =
                '<div id="'+member_id(nick)+'" class="member">'
                +'  <div style="display: block; float: left;">'
                +'    <div>'
                +'      <a href="#" onclick="member_clicked('+"'"+nick+"'"+'); return false;">'
                +'        <img class="photo" src="'+photo+'" title="'+name+'" alt="'+name+'"/>'
                +'      </a>'
                +'    </div>'
                +'    <div class="hand">'
                +'      <img src="http://files.clackpoint.com/images/hand.png" />'
                +'    </div>'
                +'    <div class="speaker muted">'
                +'      <img src="http://files.clackpoint.com/images/muted.png" />'
                +'    </div>'
                +'    <div class="speaker speaking">'
                +'      <img src="http://files.clackpoint.com/images/speaking.png" />'
                +'    </div>'
                +'  </div>'
                +'  <div class="controls"><table cellspacing="0" cellpadding="0"><tr cellspacing="0" cellpadding="0"><td cellspacing="0" cellpadding="0">'
                +kickhand_html
                +'    <tr><td>'
                +'      <a class="footer mute-unmute" href="#"'
                +'          onclick="member_muteunmute('+"'"+nick+"'"+'); return false;">'
                +((CP.caller_state[nick]==CP.Caller_state.muted)?'Unmute':'Mute')+'</a>'
                +'    </td></tr>'
                +'  </table></div>'
                +'</div>';

            if (nick == CP.nickname)
            {
                $('#members-photos').prepend(html);
            }
            else
            {
                $('#members-photos').append(html);
            }
            $("#members-title").css("display", "block");
            $("#members-help").css("display", "block");
            if (nick == CP.nickname)
            {
                var sel = "#"+member_id(nick)+" .controls";
                $(sel).css('display', 'inline');
            }
            gadgets.window.adjustHeight();
            dbg("- render_member_called");
        }

        render_member_departed = function(nick)
        {
            dbg("+ render_member_departed: nick:"+nick);
            var members = document.getElementById('members-photos');
            var member = document.getElementById(member_id(nick));
            members.removeChild(member);
            if (members.children.length == 0)
            {
                $("#members-title").css("display", "none");
                $("#members-help").css("display", "none");
            }
            gadgets.window.adjustHeight();
            dbg("- render_member_departed");
        }

        render_caller_speaking = function(nick)
        {
            dbg("+ render_caller_speaking: nick:"+nick);
            if (nick in CP.callers)
            {
                var sel = "#"+member_id(nick)+" .muted";
                $(sel).css('display', 'none'); 
                var sel = "#"+member_id(nick)+" .speaking";
                $(sel).css('display', 'inline');
                var sel = "#"+member_id(nick)+" .mute-unmute";
                $(sel).html("Mute");
            }
            dbg("- render_caller_speaking");
        }

        render_caller_muted = function(nick)
        {
            dbg("+ render_caller_muted: nick:"+nick);
            if (nick in CP.callers)
            {
                var sel = "#"+member_id(nick)+" .muted";
                $(sel).css('display', 'inline'); 
                var sel = "#"+member_id(nick)+" .speaking";
                $(sel).css('display', 'none');
                var sel = "#"+member_id(nick)+" .mute-unmute";
                $(sel).html("Unmute");
            }
            dbg("- render_caller_muted");
        }

        render_caller_silent = function(nick)
        {
            dbg("+ render_caller_silent: nick:"+nick);
            if (nick in CP.callers)
            {
                var sel = "#"+member_id(nick)+" .muted";
                $(sel).css('display', 'none'); 
                var sel = "#"+member_id(nick)+" .speaking";
                $(sel).css('display', 'none');
                var sel = "#"+member_id(nick)+" .mute-unmute";
                $(sel).html("Mute");
            }
            dbg("- render_caller_silent");
        }

        render_caller_state = function(nick)
        {                   
            dbg("+ render_caller_state: nick:"+nick);
            dbg("  state:"+CP.caller_state[nick]);
            switch (CP.caller_state[nick])
            {
                case CP.Caller_state.silent: 
                    render_caller_silent(nick); 
                    break;
                case CP.Caller_state.muted: 
                    render_caller_muted(nick); 
                    break;
                case CP.Caller_state.speaking: 
                    render_caller_speaking(nick); 
                    break;
            }
            gadgets.window.adjustHeight();
            dbg("- render_caller_state");
        }

        render_hand_state = function(nick)
        {
            dbg("+ render_hand_state: nick:"+nick);
            dbg("  state:"+CP.hand_state[nick]);
            var sel = "#"+member_id(nick)+" .hand";
            switch (CP.hand_state[nick])
            {
                case CP.Hand_state.up:
                    $(sel).css('display', 'inline');
                    break;
                case CP.Hand_state.down:
                    $(sel).css('display', 'none');
                    break;
            }
            gadgets.window.adjustHeight();
            dbg("- render_hand_state");
        }

        phone_clicked = function()
        {
            dbg("+ phone_clicked");
            if (CP.phone_clicked)
            {
                $('#phone-details').css("display", "none");
                CP.phone_clicked = false;
            }
            else
            {
                $('#phone-details').css("display", "block");
                CP.phone_clicked = true;
            }
            gadgets.window.adjustHeight();
            dbg("- phone_clicked");
        }

        member_clicked = function(nick)
        {
            dbg("+ member_clicked");
            var sel = "#"+member_id(nick)+" .controls";
            if (CP.controls[nick])
            {
                $(sel).css('display', 'none');
                CP.controls[nick] = false;
            }
            else
            {
                $(sel).css('display', 'inline');                
                CP.controls[nick] = true;
            }
            gadgets.window.adjustHeight();
            dbg("- member_clicked");
        }

        member_muteunmute = function(nick)
        {
            dbg("+ member_muteunmute: nick:"+nick);
            if (CP.caller_state[nick] == CP.Caller_state.muted)
            {
                document.getElementById('airphone-embed').unmuteMember(nick);
            }
            else
            {
                document.getElementById('airphone-embed').muteMember(nick);
            }
            dbg("- member_muteunmute");
        }

        member_handupdown = function()
        {
            dbg("+ member_handupdown");
            dbg("  my_hand_state:"+CP.my_hand_state);
            if (CP.my_hand_state == CP.Hand_state.up)
            {
                document.getElementById('airphone-embed').lowerHand();
            }
            else
            {
                document.getElementById('airphone-embed').raiseHand();
            }
            dbg("- member_handupdown");
        }

        member_kick = function(nick)
        {
            dbg("+ member_kick");
            document.getElementById('airphone-embed').kickMember(nick+"|voice");
            dbg("- member_kick");
        }

        /**
         * Airphone callbacks
         */
        set_status = function(nick, media, status)
        {
            dbg("+ set_status: nick:"+nick+" media:"+media+" status:"+status);
            switch (media)
            {
                case "voice":
                    switch (status)
                    {                        
                        case "available": status = CP.Caller_state.speaking; break;
                        case "dnd": status = CP.Caller_state.muted; break;
                        case "away": status = CP.Caller_state.silent; break;
                        case "unavailable": status = null; break;
                        default: 
                            dbg("*** unknown status:"+status+" media:"+media+" nick:"+nick); 
                            break;
                    }
                    var delta = {};
                    delta[CP.Mpfx+nick] = status;
                    submit_delta(delta);
                    break;

                case "hand": 
                    var delta = {};
                    delta[CP.Hpfx+nick] = status;
                    submit_delta(delta);
                    break;

                case "text": 
                    dbg("  text: status:"+status+" nick:"+nick);
                    switch (status)
                    {
                        case "unavailable":
                            if (nick == CP.manager)
                            {
                                CP.manager = null;
                                delete CP.members[nick];
                                for (nick in CP.members)
                                {
                                    if (CP.manager == null || nick < CP.manager)
                                    {
                                        CP.manager = nick;
                                    }
                                }
                            }
                            break;
                        default:
                            CP.members[nick] = null;
                            if (CP.manager == null || nick < CP.manager)
                            {
                                CP.manager = nick;
                            }
                            break;
                    }
                    break;
                    
                case "video": break;
                default:
                    dbg("*** unknown media:"+media+" nick:"+nick);            
            }
            dbg("- set_status");
        }
            
        set_participantcode = function(code)
        {
            dbg("+ set_participantcode: code:"+code);            
            CP.flash_loaded = true; // ...we know the flash loaded successfully
            $("#click-to-call").html('Click to call using VoIP');
            gadgets.window.adjustHeight();            
            dbg("- set_participantcode");
        }

        go_large = function() 
        {
            dbg("+ go_large");
            $('#airphone').width("215px");
            $('#airphone').height("138px");
            $('#airphone-embed').width("100%");
            $('#airphone-embed').height("100%");
            gadgets.window.adjustHeight();            
            dbg("- go_large");
        }

        go_small = function()
        {
            dbg("+ go_small");
            $('#airphone').width("120px");
            $('#airphone').height("35px");
            $('#airphone-embed').width("100%");
            $('#airphone-embed').height("35px");
            gadgets.window.adjustHeight();            
            dbg("- go_small");
        }

        /**
         * Main code
         */
        loadAirphone = function(url)
        {
            dbg("+ loadAirphone: url:"+url);
            var flashvars = {};

            var params = {};
            params.quality = "best";
            params.wmode = "window";
            params.bgcolor = "#FFFFFF";
            params.allowScriptAccess = "always";
            
            var attributes = {};
            attributes.id = "airphone-embed";
            err("  loading flash!");
            swfobject.embedSWF(
                url, "airphone-embed", "100%", "35px", "9.0.0",
                "http://files.clackpoint.com/plugins/expressInstall.swf",
                flashvars, params, attributes);
            gadgets.window.adjustHeight();            
            dbg("- loadAirphone");
        }

        buildUrl = function(retval)
        {
            dbg("+ buildUrl: retval:"+retval);
            var data = JSON.parse(retval.text);
            dbg("data:"+data);
            $('#phone-details').html(data.phonedetails);
            var url = "http://files.clackpoint.com/ClackPointWave.swf"
                      +"?code="+data.code
                      +"&bg_color=0XFFFFFF"
                      +"&parent_domain="+location.hostname
                      ;
            loadAirphone(url);
            dbg("- buildUrl");
        }

        state_callback = function()
        {
            // wave claims state has changed.  first thing is to get hold of the
            // state.
            dbg("+ state_callback: gate:"+CP.reload_gate); 
            state = wave.getState();
            dbg("  state:"+state);
            if (!state) 
            {
                // the state was null - something's up.  punt the problem to the
                // future (reloading if it doesn't fix itself).
                err("  null state!  gate:"+CP.reload_gate);
                gate_reload(); 
                CP.gate_function = setTimeout(state_callback, 850);
                return; 
            } 

            // w00t.  we're finally into a running state with wave ready to go.
            // first thing to do is remove the "waiting for wave..." message,
            // and then we can be about our business.
            if (!CP.wave_loaded)
            {
                clearTimeout(CP.gate_function);
                var invite_html = 
                    '<span id="click-to-call">'
                    +' <a href="http://www.adobe.com/go/getflashplayer" target="_blank">'
                    +'Install Adobe Flash player to call using VoIP</a></span> or'
                    +' <a href="#" onclick="phone_clicked(); return false;">call in by phone</a>.';
                $('#phone-invite').html(invite_html);
                CP.wave_loaded = true;
            }

            var code = state.get("_cp__code");
            dbg("  code: "+code);
            if (!code)
            {
                code = Math.floor(Math.random()*Math.pow(10,CP.code_length)).toString();
                code = Array(CP.code_length-code.length+1).join("0") + code;
                dbg("  code': "+code);
                state.submitDelta({'_cp__code': code});
                return;
            }
            code = state.get("_cp__code");
            if (code && CP.code != code)
            {
                CP.code = code;
                CP.nickname = wave.getViewer().getId();

                var url = "http://wave.clackpoint.com/request-code/1/";
                var params = {}
                params[gadgets.io.RequestParameters.METHOD] = 
                    gadgets.io.MethodType.POST;
                params[gadgets.io.RequestParameters.AUTHORIZATION] = 
                    gadgets.io.AuthorizationType.NONE;
                params[gadgets.io.RequestParameters.CONTENT_TYPE] = 
                    gadgets.io.ContentType.JSON;
                
                params[gadgets.io.RequestParameters.POST_DATA] =
                    gadgets.io.encodeValues({
                            nickname: CP.nickname,
                            code: code
                        });
                gadgets.io.makeRequest(url, buildUrl, params);
            }

            var dead = Array();
            for (nick in CP.callers) dead[nick] = null;

            var keys = state.getKeys();
            for (var i=0; i < keys.length; ++i) 
            {
                var key = keys[i];
                dbg("  Mi:"+i+" key:"+key);
                if (key.substr(0, CP.Mpfx.length) == CP.Mpfx)
                {
                    var nick = key.substr(CP.Mpfx.length);
                    if (nick in dead) delete dead[nick];

                    var status = state.get(key);
                    if (!(nick in CP.callers))
                    {
                        member_called(nick);
                    }
                    CP.caller_state[nick] = status;
                    render_caller_state(nick);
                }
            }
            for (var i=0; i < keys.length; ++i)
            {
                var key = keys[i];
                dbg("  Hi:"+i+" key:"+key);
                if (key.substr(0, CP.Hpfx.length) == CP.Hpfx)
                {
                    var nick = key.substr(CP.Hpfx.length);
                    var status = state.get(key);
                    CP.hand_state[nick] = status;
                    render_hand_state(nick);
                    if (nick == CP.nickname && nick in CP.callers)
                    {
                        if (status == CP.Hand_state.up)
                        {
                            $('a#hand').html("Lower hand");
                        }
                        else
                        {
                            $('a#hand').html("Raise hand");
                        }
                        $('#hand').css('display', 'block');
                        CP.my_hand_state = status;
                    }
                }
            }
            for (nick in dead)
            {
                member_departed(nick);
                render_member_departed(nick); 
            }
            dbg("- state_callback");
        }

        main = function()
        {
            $(document).ready(function () {
                err("  installing state callback");
                dbg("+ main");
                if (!(wave && wave.isInWaveContainer())) return; 
                wave.setStateCallback(state_callback);
                state_callback(); // kick off "reload if no wave state" loop
                dbg("- main");
            });
        }

        ensure_in_container = function ()
        {
            dbg("+ ensure_in_container: gate:"+CP.reload_gate);
            gate_reload();
            if (!(wave && wave.isInWaveContainer()))
            {
                dbg("  no container - retrying");
                CP.gate_function = setTimeout(ensure_in_container, 500);
            }
            else
            {
                clearTimeout(CP.gate_function);
                main();
            }
            dbg("- ensure_in_container");
        }

        ensure_wave_exists = function ()
        {
            dbg("+ ensure_wave_exists: gate:"+CP.reload_gate);
            gate_reload();
            if (typeof(wave) == "undefined")
            {
                dbg("  no wave - retrying");
                CP.gate_function = setTimeout(ensure_wave_exists, 500);
            }
            else 
            {
                clearTimeout(CP.gate_function);
                ensure_in_container();
            }
            dbg("- ensure_wave_exists");
        }

        ensure_gadgets_exists = function ()
        {
            err("  gadget initializing");
            dbg("+ ensure_gadgets_exists: gate:"+CP.reload_gate);
            gate_reload();
            if (typeof(gadgets) == "undefined")
                CP.gate_function = setTimeout(ensure_gadgets_exists, 500);
            else 
            {
                clearTimeout(CP.gate_function);
                $(document).ready(ensure_wave_exists);
            }
            dbg("- ensure_gadgets_exists");
        }
    
        ensure_gadgets_exists();
    </script>
            
    <style type="text/css">
        #gadget { height: 82px; width: 100%;
                  padding: 0; margin: 0; border: 0;
                  font-family: Verdana,Lucida,Arial,sans-serif; line-height: 1.3;
                }
        
        #logo { float: right; }
        #logo img { border: 0; }
        #logo a { text-decoration: none; }
            
        #feedback { float: left;
                    padding: 2px 2px 0 5px; margin: 0; border: 0;
                    font-size: 9pt; text-align: left;
                    }
        #help { float: right; 
                padding: 2px 2px 0 0; margin: 0; border: 0;
                font-size: 9pt; text-align: right; display: block;
                }
        #airphone { width: 120px; height: 35px; float: left;
                    padding: 0; margin: 0 0 0 5px; border: 0; 
                    font-size: 10pt; 
                    }

        #phone-invite { float: left;
                        margin: 0 0 0 5px; padding: 0; border: 0;
                        font-size: 9pt; text-align: left; 
                        }
        #phone-details { display: none; 
                         margin: 0 0 0 5px; padding: 0; border: 0;
                         font-size: 9pt; 
                         }
        #phone-details ul { margin: 0; border: 0; padding: 0; list-style-type: none; }
        #phone-details li { float: left; }
        .code { font-weight: bold; }

        #members-help { float: left; display: none;
                        padding: 0 0 0 5px; margin: 0; border: 0;
                        font-size: 9pt; text-align: left;
                        }
        #members { float: left; clear: both; width: 100%; padding-bottom: 0; 
                   }
        #members-title { padding: 0 0 0 5px; margin: 0; border: 0;
                         font-size: 9pt; text-align: left; display: none;
                         }
        #members-photos { width: 100%; }

        .member { height: 32px; float: left;
                  position: relative; display: block;
                  margin: 0 0 8px 8px; background: #CCCCCC; padding: 5px;
                  border-right: 3px solid #B3B3B3;
                  }
        .member img.photo { height: 32px; width: 32px; float: left;
                            border: 0; margin: 0;
                            -ms-interpolation-mode: bicubic;
                            }
        .member .hand { display: none; position: absolute; top: 25px; left: -1px; 
                        }
        .member .speaker { display: none; position: absolute; top: 25px; left: 32px; 
                           }
        .member .hand img { height: 14px; width: 14px; 
                            -ms-interpolation-mode: bicubic; 
                          }
        .member .speaker img { height: 13px; width: 13px; 
                               -ms-interpolation-mode: bicubic; 
                             }
        .member .controls { display: none; float: left; 
                            }

        .member .controls table { border-collapse: collapse; }
        .member .controls a { display: block; 
                              padding: 0.5ex 0.5em 0.5ex 8px;
                              text-decoration: none; 
                              }
                                            
        .name { padding: 0 6px 3px 6px; margin: 0; 
                font-size: 10pt; font-weight: bold;
              }
        .footer { font-size: 8pt; padding: 4px 8px 4px 0; float: left; }
    </style>

    <div id="gadget">
        <div style="border-left: 5px solid #B3B3B3;">
            <div id="airphone">
                <div id="airphone-embed">
                    <a href="http://www.adobe.com/go/getflashplayer" target="_blank">
                        <img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif"
                             alt="Get Adobe Flash player" />
                    </a>
                </div>
            </div>
            <div id="logo">
                <a href="http://clackpoint.com/" target="_blank">
                    <img src="http://files.clackpoint.com/images/poweredby.png" />
                </a>
            </div>
            <div style="clear:both;"></div>
            
            <div id="phone-invite">
                Waiting for Google Wave...
            </div>
            <div style="clear:both;"></div>
            
            <div id="phone-details">
            </div>
            <div style="clear:both;"></div>
            
            <div id="members">
                <div id="members-title">Currently called in:</div>
                <div id="members-photos"></div>
            </div>
            <div style="clear:both;"></div>

            <div id="members-help">
                Click photo of called-in member to toggle controls.
            </div>
            <div style="clear:both;"></div>

            <div id="feedback">
                <a href="http://getsatisfaction.com/clackpoint" target="_blank">Feedback</a>
            </div>
            <div id="help">
                <a href="http://vipadia.com/" target="_blank">&copy;&#x200A;Vipadia</a>
                | <a href="http://clackpoint.com/gadgets/wave/" target="_blank">About</a>
                | <a href="http://clackpoint.com/terms-and-conditions/" target="_blank">Terms</a>
            </div>
            <div style="clear:both;"></div>
        
        </div>
        <script>_IG_Analytics("UA-7508653-5", "/wave-1.0");</script>
    </div>
    ]]>
    </Content>
</Module>
