<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <!--
  
    poly/clock.xml
    
    PolyClock gadget
    
    Copyright 2006-8 Udell Enterprises, Inc
  
  -->
  <ModulePrefs title="PolyClock"
               title_url="http://gad.getpla.net/poly/" 
               directory_title="PolyClock for GMail"
               description="Designed specifically for GMail, this gadget shows clocks from multiple time zones. Also works on iGoogle!"
               author="Sterling Udell"
               author_email="sterling.udell+gadgets@gmail.com"
               author_location="North Wales, UK"
               author_affiliation="Udell Enterprises, Inc"
               author_photo="http://gad.getpla.net/images/my_photo.png"
               author_aboutme="Free-range programmer developing gadgets, map applications, and anything else that catches my interest."
               author_link="http://gad.getpla.net"
               author_quote="Is there no sun in this cursed country?"
               height="63"
               width="162"
               scrolling="false"
               screenshot="http://gad.getpla.net/poly/clock_120.png"
               thumbnail="http://gad.getpla.net/poly/clock_120.png">
    <Require feature="analytics" />
    <Require feature="dynamic-height"/>
    <Require feature="minimessage" />
    <Require feature="setprefs" />
  </ModulePrefs>
  <UserPref name="clocks" datatype="hidden" default_value="" />
  <Content type="html"><![CDATA[
    <table id="clock_grid">
    </table>
    <p id="new_area">
      Loading...
    </p>
    <div id="msg_box"></div>

    <style type="text/css">
      body * {margin: 0; padding: 0}
      table {font-size: 80%; width: 158px; overflow: hidden; border-collapse: collapse;}
      p {font-size: 80%; overflow: hidden;}
      .placename {padding-left: 1px; width: 87px;}
      #renamer {width: 82px; border: none; background-color: #fffdd4;}
      .time {text-align: right; font-weight: bold; padding-left: 3px; cursor: n-resize;}
      .time em {font-style: normal;}
      .yesterday {color: red;}
      .tomorrow {color: green;}
      .time span {font-size: 80%; padding-left: 1px;}
      td img {padding-left: 3px; cursor: pointer;}
      #new_selector {width: 158px;}
    </style>

    <script type="text/javascript">
      var newSelector;
      var clockGrid   = _gel('clock_grid');
      var clocks = [];
      var xPng = _IG_GetImageUrl('http://gad.getpla.net/poly/x.png');
      var masterZones = {};
      var prefs = new _IG_Prefs(__MODULE_ID__);
      var msgr  = new _IG_MiniMessage(__MODULE_ID__, _gel('msg_box'));
      var delMsg = false;
      var lastMinute = -1;
      var dragging = -1;
      var mouseTimer = false;

      var clientTime = Number(new Date());
      var serverTime = clientTime; // default in case time server unreachable
      document.write(String.fromCharCode(60) + 'script src="' +  
        'http://gad.getpla.net/include/current_utc.js.php?rand=' + Math.random() + 
        '" type="text/javascript"><\/script>');
      var timeCorrection = 0;

      var platform = '';
      var referer = document.referrer;
      if (!referer && (window._args instanceof Function))
        referer = _args()['parent'];
      if (!referer || !(referer.search instanceof Function))
        platform = 'other/unknown';
      else if ((referer.search('//maps.google') > -1) ||
               (referer.search(/google\.[.a-z]+\/maps/) > -1))
        platform = 'gmaps';
      else if (referer.search('//www.google') > -1)
        platform = 'igoogle';
      else if (referer.search('//mail.google') > -1)
        platform = 'gmail';
      else
        platform = 'other/' + document.referrer.split('/')[2];

      function receiveZones(responseText) {
        _gel('new_area').innerHTML = responseText;
        newSelector = _gel('new_selector');

        if (!responseText ||
            (responseText === '') ||
            !newSelector) {
          _gel('new_area').innerHTML = '<i>Invalid timezone data received from Google.</i>';
          return;
        }

        if (platform !== 'gmail') {
          clockGrid.style.width    = '100%';
          clockGrid.style.maxWidth = '200px';
        }

        zones = newSelector.options;
        for (var p = 0; p < zones.length; p++)
          masterZones[zones[p].value] = {name: zones[p].text,
                                         offset: 1000 * parseInt(zones[p].className)};

//        var loadData = prefs.getString('clocks');
        var loadData = decodeURIComponent(getURLParm('up_clocks'));
        if (loadData !== '')
        {
          loadData = loadData.split(',');
          for (var c = 0; c < loadData.length; c++)
          {
            var newClock = loadData[c].split('=');
            addClock(newClock[0], decodeURIComponent(newClock[1]));
          }
        }

        window.setInterval('updateTime()', 1000);
        adjustHeight();
      };

      function adjustHeight()
      {
        // _IG_AdjustIFrameHeight sometimes throws errors on GMail
        try {
          _IG_AdjustIFrameHeight();
        } catch (e) {}
      };

      function getURLParm(name)
      {
        // ToDo: merge with getCookie
        name = name.replace(/[\[]/, '\\\[').replace(/[\]]/, '\\\]');
        var regexS = '[\\?&]' + name + '=([^&#]*)';
        var regex = new RegExp(regexS);
        var result = regex.exec(window.location.href);
        if (result == null)
          return '';
        else
          return result[1];
      };
  
      function save()
      {
        var saveData = [];

        for (var r = 0; r < clockGrid.rows.length; r++) 
        {
          var index = clockGrid.rows[r].id.replace('row_', '');
          if (clocks[index].active)
            saveData.push(clocks[index].zone + '=' + encodeURIComponent(clocks[index].name));
        }

        prefs.set('clocks', saveData.join(','));
      };
  
      function newClock()
      {
        if (newSelector.selectedIndex == -1)
          return;
        
        newValue = newSelector.options[newSelector.selectedIndex].value;
        if (newValue == '')
          return;

        addClock(newValue);
        
        save();
        newSelector.selectedIndex = 0;
        adjustHeight();
      };
  
      function addClock(newZone, newName)
      {
        if (newName == null)
          newName = masterZones[newZone].name;

        var index = clocks.length;

        lastMinute = -1;
        clocks.push({zone: newZone,
                     offset: masterZones[newZone].offset,
                     name: newName,
                     active: false,
                     time: 'Loading...',
                     row: null});

        insertRow(index);
      };
  
      function insertRow(clockIndex, rowIndex)
      {
        if (rowIndex == null)
        {
          rowIndex = clockGrid.rows.length;
          var newRow = clockGrid.insertRow(-1);
        }
        else
          var newRow = clockGrid.insertRow(rowIndex);

        newRow.id = 'row_' + clockIndex;
        newRow.onmouseover = function () {rowMouseOver(newRow)};
        newRow.onmouseup   = endDrag;
  
        var newCell = newRow.insertCell(-1);
        newCell.id = 'name_' + clockIndex;
        newCell.className = 'placeName';
        newCell.innerHTML = _hesc(clocks[clockIndex].name);
        newCell.onclick = function () {initRename(clockIndex)};
        newCell.title = 'Click to rename';
        newCell.cursor = 'default';
        disableSelection(newCell);

        newCell = newRow.insertCell(-1);
        newCell.id = 'time_' + clockIndex;
        newCell.className = 'time';
        newCell.innerHTML = clocks[clockIndex].time;
        newCell.onmousedown = function () {timeMouseDown(clockIndex)};
        newCell.title = 'Drag this clock up or down';
        disableSelection(newCell);

        newCell = newRow.insertCell(-1);
        newCell.title = 'Delete this clock';
        newCell.innerHTML = '<img src="' + xPng + '" width="12" height="12" onclick="deleteClock(' + clockIndex + ')" />';

        clocks[clockIndex].row    = newRow;
        clocks[clockIndex].active = true;
      };

      function disableSelection(element) 
      {
        element.onselectstart = function () {return false;};
        element.unselectable = 'on';
        element.style.MozUserSelect = 'none';
      };

      function enableSelection(element) 
      {
        element.onselectstart = null;
        element.unselectable = '';
        element.style.MozUserSelect = '';
      };

      function formatDate(time)
      {
        var result = time.getUTCFullYear() + '/' + 
                     ('0' + (time.getUTCMonth() + 1)).substr(-2) + '/' + 
                     ('0' + time.getUTCDate()).substr(-2);
        
        return result;
      };
      
      function formatTime(time)
      {
//        return time.toLocaleTimeString();
        var hour = time.getUTCHours();
        if (hour >= 12)
        {
          var hemi = 'PM';
          if (hour > 12)
            hour -= 12;
        }
        else
        {
          var hemi = 'AM';
          if (hour == 0)
            hour = 12;
        }
        
        var minute = time.getUTCMinutes();
        if (minute < 10)
          minute = '0' + minute;

        var result = hour + ':' + minute;

        var today = new Date();
        today = formatDate(new Date(Number(today) - today.getTimezoneOffset() * 60*1000));
        var target = formatDate(time)
        if (target > today)
          result = '<em class="tomorrow" title="' + result + hemi + ' tomorrow: ' + 
                   target + '">' + result + '<span>' + hemi + '</span>' + '</em>';
        else if (target < today)
          result = '<em class="yesterday" title="' + result + hemi + ' yesterday: ' + 
                   target + '">' + result + '<span>' + hemi + '</span>' + '</em>';
        else 
          result = result + '<span>' + hemi + '</span>';

        return result;
      };
      
      function deleteClock(index)
      {
        if ((index < 0) ||
            (index > clocks.length))
          return;

        clocks[index].active = false;
        _gel('row_' + index).style.display = 'none';

        delMsg = msgr.createDismissibleMessage(
                   '"' + clocks[index].name + '" deleted. <a href="#" onclick="restoreClock(' + index + '); return false">Undo</a>',
                   msgClosed);
        window.setTimeout('closeMsg()', 30000);

        save();
        adjustHeight();
      };
      
      function restoreClock(index)
      {
        if ((index < 0) ||
            (index > clocks.length))
          return;

        clocks[index].active = true;
        _gel('row_' + index).style.display = '';

        closeMsg();
        save();
      };
      
      function closeMsg()
      {
        if (delMsg)
        {
          msgr.dismissMessage(delMsg);
          delMsg = false;
          adjustHeight();
        }
      };
      
      function msgClosed()
      {
        delMsg = false;
        adjustHeight();
      };

      function updateTime()
      {
        if (timeCorrection == 0)
          timeCorrection = (serverTime - clientTime);
        var now = new Date(Number(new Date()) + timeCorrection);

        if (now.getUTCMinutes() != lastMinute)
        {
          lastMinute = now.getUTCMinutes();
  
          now = Number(now);
          for (var a = 0; a < clocks.length; a++) 
            if (clocks[a].active)
            {
              var timeCell = _gel('time_' + a);
              var newTime = new Date(now + clocks[a].offset);
              clocks[a].time = timeCell.innerHTML = formatTime(newTime);
            }
        }
      };

      function initRename(index)
      {
        // Initialize clock renaming
        
        var nameElement = _gel('name_' + index);
        if (nameElement &&
            clocks[index].active)
        {
          // Replace the displayed name with a text input in the HTML
          nameElement.onclick   = null;
          nameElement.title     = null;
          nameElement.innerHTML = '<input type="text" id="renamer" value="' + clocks[index].name + '" ' + 
                                  'onblur="applyRename(' + index + ', this.value)" ' +
                                  'onkeypress="if (event.keyCode == 13) this.blur()" />';
          enableSelection(nameElement);

          // Set focus to the text input and select its contents
          var renamer = document.getElementById('renamer');
          if (renamer)
          {
            renamer.select();
            renamer.focus();
          }
        }
      };
      
      function _hunesc(str)
      {
        return str.replace('&lt;', '<').replace('&gt;', '>').replace('&quot;', '"');
      }
      
      function applyRename(index, value)
      {
        // Finalize clock renaming
        if (value != '')
        {
          // Rename the internal clock object
          clocks[index].name = value;
      
          // Apply the new name to the HTML
          var nameElement = document.getElementById('name_' + index);
          if (nameElement)
          {
            nameElement.innerHTML = _hesc(value);
            nameElement.onclick   = function() {initRename(index)};
            nameElement.title     = 'Click to rename';
            disableSelection(nameElement);
          }
          
          save();
          adjustHeight();
        }
      };
      
      function timeMouseDown(index)
      {
        dragging = index;
      };
      
      function endDrag()
      {
        if (dragging > -1)
        {
          dragging = false;

          save();
//console.log('timer completed');
        }
      };
      
      function rowMouseOver(thisRow)
      {
        if ((dragging == -1) ||
            (clocks[dragging].row == thisRow))
          return;

        if (mouseTimer)
        {
          clearTimeout(mouseTimer);
          mouseTimer = false;
//console.log('timer stopped');
        }

        if (clocks[dragging].row.rowIndex > thisRow.rowIndex)
          var newIndex = thisRow.rowIndex;
        else if (thisRow.rowIndex < clockGrid.rows.length - 1)
          var newIndex = thisRow.rowIndex + 1;
        else
          var newIndex = -1;

        clockGrid.deleteRow(clocks[dragging].row.rowIndex);
        insertRow(dragging, newIndex);
      };

        clockGrid.onmouseout = 
      function () //tableMouseOut
      {
        if (!mouseTimer)
          mouseTimer = setTimeout('endDrag()', 1000);
//console.log('timer started');
      };

//      _IG_FetchContent(location.protocol + '//gad.getpla.net/poly/zones.php', 
      _IG_FetchContent('http://gad.getpla.net/poly/zones.php', 
                       receiveZones, 
                       {refreshInterval: 0});
      _IG_Analytics('UA-1556555-7', '/polyclock/' + platform + '/');
    </script>
  ]]></Content>
</Module> 