// ---------------------------------------------------------------------------
// (c) 2003 Geotalk, Hans Martin Mohn
// ---------------------------------------------------------------------------
// Utility functions and classes
//
// Dependencies:
//    None
//


// May be used in javascript files that depend upon other script files:
//
//   gtAssert("gtTool != null");  // This gives a runtime error if gtTool.js hasn't been included
function gtAssert(expr_str)
{
  try
  {
    if (!eval(expr_str))
      alert("assertion failed! " + expr_str);
  }
  catch (e)
  {
    alert("exception during assertion! " + expr_str);
  }
} // end gtAssert();


// ----------------------------------------------------------------------------

String.prototype.startsWith = function String_startsWith(part)
{
  if (this.length < part.length)
    return false;

  if (this.substr(0, part.length) == part)
    return true;
    
  return false;    
} // end startsWith()



String.prototype.endsWith = function String_endsWith(part)
{
  if (this.length < part.length)
    return false;

  if (this.substr(this.length - part.length) == part)
    return true;
    
  return false;    
} // end endsWith()



function gtArrayInsert(a, pos, element)
{
  if (pos === 0)
    a.unshift(element);
  else if (pos < a.length)
    a.splice(pos, 0, element);
  else
    a.push(element);
} // end gtArrayInsert()

// ----------------------------------------------------------------------------

function gtSetResizeHandler(w, onResize, onResizeEnd)
{
  w.m_resize_timeout_id = -1;

  w.onresize = function window_onresize()
  {
    onResize();
    if (this.m_resize_timeout_id != -1)
      clearTimeout(this.m_resize_timeout_id);
      
    this.m_resize_timeout_id = setTimeout(
      function()
      {
        clearTimeout(this.m_resize_timeout_id);
        this.m_resize_timeout_id = -1;
        onResizeEnd();
      },
      500);
  }; // end onresize()
  
  onResize();
  onResizeEnd();
} // end gtSetResizeHandler()



function gtAddResizeendHandler(w)
{
  w.m_resize_timeout_id = -1;

  // Use a crowbar-timer to simulate the resizeend-event. NB: IE ONLY!!!
  w.onresize = function()
  {
    if (this.m_resize_timeout_id != -1)
      clearTimeout(this.m_resize_timeout_id);
    this.m_resize_timeout_id = setTimeout(function() {
      clearTimeout(this.m_resize_timeout_id);
      this.m_resize_timeout_id = -1;
      this.onresizeend();
    }, 300);
  }; // end onresize()
} // end gtAddResizeendHandler()



function gtTimer()
{
  this.m_date = new Date();
  this.m_accumulated = 0;
} // end gtTimer()



gtTimer.prototype.getElapsedMilliSeconds = function timer_getElapsedMilliSeconds()
{
  var now = new Date();
  return (now.getMinutes() - this.m_date.getMinutes()) * 60000 + (now.getSeconds() - this.m_date.getSeconds()) * 1000 + now.getMilliseconds() - this.m_date.getMilliseconds();
}; // end getElapsedMilliSeconds()



gtTimer.prototype.start = function timer_start()
{
  this.m_date = new Date();
}; // end start()



gtTimer.prototype.stop = function timer_stop()
{
  this.m_accumulated += this.getElapsedMilliSeconds();
}; // end stop()



gtTimer.prototype.getAccumulated = function timer_getAccumulated()
{
  return this.m_accumulated;
}; // end getAccumulated()



function gtChopDecimals(number, decimals)
{
  var exp = Math.pow(10, decimals);
  return Math.round(number * exp) / exp;
} // end gtChopDecimals()



function log10(a)
{
  return Math.log(a) / Math.log(10);
} // end log10()



function niceScale(s)
{
  // Find largest scale smaller than s of the form: 1, 2.5, 5, 10, 25, 50, 100, .....
  var log = Math.floor(log10(s));
  var factor = Math.pow(10, log - 1);
  var base = s / factor;
  return Math.floor((base + 1) / 5) * 5 * factor;
} // end niceScale()



function formatThousand(number)
{
  if (number >= 1000)
  {
    var newnumber = parseInt(number / 1000);
    var rest = (number - newnumber * 1000).toString();
    if (parseInt(rest) < 10)
      rest = "00" + rest;
    else if (parseInt(rest) < 100)
      rest = "0" + rest;
    return formatThousand(newnumber) + " " + rest;
  }
  else return number;
} // end formatThousand()



// ----------------------------------------------------------------------------
//    Support for remembering window size
// ----------------------------------------------------------------------------

function gtWindowSize(x, y, w, h)
{
  this.m_x = x;
  this.m_y = y;
  this.m_w = w;
  this.m_h = h;
} // end gtWindowSize()


function readCookieWindowSize(doc, name, x, y, w, h)
{
  var cookie = new gtCookie(doc);
  var ws = new gtWindowSize(x, y, w, h);
  var crumb  = cookie.getCrumb(name);
  if (crumb)
  {
    var v = crumb.split("|");
    ws.m_x = parseInt(v[0]);
    ws.m_y = parseInt(v[1]);
    ws.m_w = parseInt(v[2]);
    ws.m_h = parseInt(v[3]);
  }
  return ws;
} // end readCookieWindowSize()



function writeCookieWindowSize(doc, name, x, y, w, h)
{
  var cookie = new gtCookie(doc);
  cookie.setNumDaysValid(10);
  cookie.setCrumb(name, x + "|" + y + "|" + w + "|" + h);
} // end writeCookieWindowSize()



function openWindow(doc, name, url, control, x, y, w, h)
{
  var ws  = readCookieWindowSize(doc, name, x, y, w, h);
  var win = window.open(url, name, control + ",left=" + ws.m_x + ",top=" + ws.m_y + ",width=" + ws.m_w + ",height=" + ws.m_h);

  // If the window was already open, we are not permitted to write to it, so use a try-block for the following:
  try
  {
    win.m_offs_x = win.screenLeft - ws.m_x;
    win.m_offs_y = win.screenTop  - ws.m_y;
    
    gtAddEventListener(win, "unload", function() { saveWindow(doc, win, name); });
  }
  catch (e)
  {
  }
  
//  top.gtTrace("Asked for (" + ws.m_x + ", " + ws.m_y + ") - got (" + w.screenLeft + ", " + w.screenTop + ")\n");
  return win;
} // end openWindow()



function saveWindow(doc, win, name)
{
  var l = win.screenLeft - win.m_offs_x;
  var t = win.screenTop  - win.m_offs_y;
  writeCookieWindowSize(doc, name, l, t, win.document.body.clientWidth, win.document.body.clientHeight);
//  alert("saved window size to cookie: " + name + ", " + l + ", " + t);
} // end saveWindow()



// ----------------------------------------------------------------------------
//    Support for tracing
// ----------------------------------------------------------------------------

try
{
  // If we haven't access to top, an exception is thrown - eat it!
  if (typeof(top.g_trace_window) == "undefined")
    top.g_trace_window = null;

  if (typeof(top.g_save_trace_text) == "undefined")
    top.g_save_trace_text = "";
    
  top.prev_now = null;
}
catch (e)
{
}



function showTraceWindow(yes, pathToGtx)
{
  if (yes)
  {
    try
    {
      top.g_trace_window.focus();
    }
    catch(e)
    {
      top.g_trace_window = openWindow(top.document,
        "trace_window",
        pathToGtx + "trace.htm",
        "location=0,menubar=0,toolbar=0,resizable=1,status=0,titlebar=1",
        100, 100, 500, 400);
    }

    // Callback to let the trace window signal whether it is available or not
    top.g_trace_window.onReady = function(on)
    {
      if (on)
      {
        if (top.g_save_trace_text.length > 0)
        {
          this.trace(top.g_save_trace_text);
          top.g_save_trace_text = "";
        }
      }
      else
      {
        top.g_trace_window = null;
      }
    }; // end onReady()

  }
  else if (!yes && top.g_trace_window !== null)
  {
    top.g_trace_window.close();
    top.g_trace_window = null;
  }
} // end showTraceWindow()



function gtSetTraceText(txt)
{
  top.g_trace_text = txt;
} // end gtSetTraceText()



function gtTraceOnKeyPress(e)
{
  e = e || window.event;
  var keyCode = e.which === undefined ? e.keyCode : e.which;
  gtTraceOnKeyPress.x = gtTraceOnKeyPress.c
  gtTraceOnKeyPress.c = gtTraceOnKeyPress.o
  gtTraceOnKeyPress.o = gtTraceOnKeyPress.n
  gtTraceOnKeyPress.n = String.fromCharCode(keyCode);
  if (gtTraceOnKeyPress.x + gtTraceOnKeyPress.c + gtTraceOnKeyPress.o + gtTraceOnKeyPress.n == "xcon")
  {
    showTraceWindow(top.g_trace_window === null); // toggle trace window
  }
} // end gtTraceOnKeyPress()

gtTraceOnKeyPress.x = "";
gtTraceOnKeyPress.c = "";
gtTraceOnKeyPress.o = "";
gtTraceOnKeyPress.n = "";



function gtTrace(text)
{
  var now = new Date();
  if (top.prev_now)
  {
    var diff = new Date(0, 0, 0, 0, 0, now.getSeconds() - top.prev_now.getSeconds(), now.getMilliseconds() - top.prev_now.getMilliseconds());
    var time = diff.getSeconds() + diff.getMilliseconds() / 1000;
    text = time.toFixed(3) + " - " + text;
  }
  top.prev_now = now;

  if (top.g_trace_text)
  {
    top.g_trace_text.value = text + top.g_trace_text.value;
//    var r = top.g_trace_text.createTextRange(); // Not supported by W3C!
//    r.collapse(false);
//    r.select();
    return;
  }

  try
  {
    if (top.g_trace_window && typeof(top.g_trace_window.trace) != "undefined")
      top.g_trace_window.trace(text);
    else
      top.g_save_trace_text = top.g_save_trace_text + text;
  }
  catch (e)
  {
  }
} // end gtTrace()



function gtTraceLine(text)
{
  gtTrace(text + "\n");
} // end gtTraceLine()



// (mainly) For listing attributes of an element when in the debugger!
function gtListAttributes(e)
{
  var s = "";
  for (var i = 0; i < e.attributes.length; i++)
  {
    var a = e.attributes[i];
    if (a)
    {
      if (a.nodeName)
        s += a.nodeName + " \t= " + a.nodeValue + "\n";
      else
        s += "***noname*** \t= " + a.nodeValue + "\n";
    }
  }
  return s;
} // end gtListAttributes()



function gtListProperties(e)
{
  var s = "";
  for (var property_name in e)
  {
    var value = e[property_name];
//    Debug.write(property_name + " \t= " + value + "\n");
    s += property_name + " \t= " + (value ? value : "<null>") + "\n";
  }
  return s;
} // end gtListProperties()



// Hack copied from IE7's internal resource, which sometimes is not permitted to load...
//
// Circumvent the problem with the stupid "click to activate this control"-shit.
// In essence, interactive controls must be loaded from a different file than the main document (!!)
// For more info, check out
//   http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/activating_activex.asp
//
// Needs
//  * The Javascript variable "objectSource" to contain the URL of the object to display
//  * An element on the page with id "objectDestination" which will be replaced by the resource
function addAttribute(node, name, value)
{
  var a = document.createAttribute(name);
  a.nodeValue = value;
  return node.attributes.setNamedItem(a);
} // end addAttribute()



function gtObjectLoad()
{
  if (!objectSource)
  {
    alert("Cannot load object - Javascript variable 'objectSource' not set");
    return;
  }
  
  // This is only supported by Internet Explorer and Opera, but is neccessary in order to get rid of the
  // "Click to activate"-shit
  var embeddingWorked = true;
  try
  {
    objectDestination.outerHTML = "<embed id='embed' style='position:absolute;top:0;left:0;width:100%;height:" + gtGetWindowHeight() + "px;' fullscreen='yes' src='" + objectSource + "'><noembed>Din nettleser kan ikke vis dokumenter med innbakte PDF-filer</noembed></embed>";
  }
  catch (e)
  {
    embeddingWorked = false;
  }

  var destination = gtGetElementById("objectDestination");  // If the "outerHTML=" above worked, the previous element doesn't exist any more...
  if (destination)
    embeddingWorked = false;

  if (!embeddingWorked)
  {
    // In other browsers, insert it manually...
    var dest = document.createElement("EMBED");
    addAttribute(dest, "src", objectSource);
    addAttribute(dest, "style", "position:absolute;left:0;top:0;width:100%;height:" + gtGetWindowHeight() + "px;");
    document.body.replaceChild(dest, destination);
  }
} // end gtObjectLoad()



// ----------------------------------------------------------------------------
// Miscellaneous helper routines
// ----------------------------------------------------------------------------


// Similar functionality to Array.join(), but for objects (associative arrays)
function objJoin(obj, sep)
{
  var s = "";
  for (var n in obj)
    s += (s.length > 0 ? sep : "") + n;
  return s;
} // end objJoin()



function addOption(dd, text, value)
{
  dd.options[dd.options.length] = new Option(text, value ? value : text);
} // end addOption()



// Try to select the given value in the list. If not available, select the first value in the list.
// Return the displayed value.
function selectValueOrFirstInList(dd, val)
{
  dd.value = val;
  if (dd.value != val)
  {
    // Selected value is not valid - default to first available
    dd.selectedIndex = 0;
  }
  return dd.value;
} // end selectValueOrFirstInList()



function selectValueOrLastInList(dd, val)
{
  dd.value = val;
  if (dd.value != val)
  {
    // Selected value is not valid - default to first available
    dd.selectedIndex = dd.options.length - 1;
  }
  return dd.value;
} // end selectValueOrLastInList()



function toFixed(n, digits)
{
  if (!digits)
    digits = 2;
  var num = parseFloat(n);
  return num.toFixed(digits);
} // end toFixed()



function getLastPathComponent(path, sep)
{
  if (!sep)
    sep = "\\";
  if (path.length == 0)
    return "";
  var p = path.split(sep);
  return p[p.length - 1];
} // end getLastPathComponent()



function getAllButLastPathComponent(path, sep)
{
  if (!sep)
    sep = "\\";
  if (path.length == 0)
    return "";
  var p = path.split(sep);
  p.splice(p.length - 1, 1);  // Remove last element
  return p.join(sep);         // Assemble back into a path
} // end getAllButLastPathComponent()



function getFilenameExtension(path)
{
  if (path.length == 0)
    return "";
  var p = path.split(".");
  return p[p.length - 1];
} // end getFilenameExtension()



function findIndex(array, text)
{
  for (var i = 0; i < array.length; i++)
  {
    if (array[i] == text)
      return i;
  }
  return -1;
} // end findIndex()



function appendClass(e, class_name)
{
  var class_names = e.className.split(" ");
  var pos = findIndex(class_names, class_name);
  if (pos < 0)
    e.className = class_names.join(" ") + " " + class_name;
} // end appendClass()



function removeClass(e, class_name)
{
  var class_names = e.className.split(" ");
  var pos = findIndex(class_names, class_name);
  if (pos >= 0)
  {
    class_names.splice(pos, 1); // Removes it!
    e.className = class_names.join(" ");
    return true;
  }
  return false;
} // end removeClass()



function useAvailableWidth(el, subtract, parentWidth)
{
  var p = el.parentNode;
  if (!parentWidth)
    parentWidth = p.clientWidth;
    
  var s = el.currentStyle;
//  alert(el.id + ": parent width=" + p.clientWidth + ", subtract=" + (parseInt(s.borderLeftWidth) + parseInt(s.borderRightWidth) + parseInt(s.paddingLeft) + parseInt(s.paddingRight) + subtract));
  var w = parentWidth - subtract - parseInt(s.borderLeftWidth) - parseInt(s.borderRightWidth) - parseInt(s.paddingLeft) - parseInt(s.paddingRight);
  if (w > 0)
    gtSetWidth (el, w);
} // end useAvailableWidth()



function useAvailableHeight(el)
{
  var p = el.parentNode;
  var s = el.currentStyle;
  var h = p.clientHeight - parseInt(s.borderTopWidth) - parseInt(s.borderBottomWidth) - parseInt(s.paddingTop) - parseInt(s.paddingBottom);
  if (h > 0)
    gtSetHeight(el, h);
} // end useAvailableHeight()



// ---------------------------------------------------------------------------
// Simple string buffer class to speed up string concatenation
// ---------------------------------------------------------------------------

function StringBuffer()
{
  this.buffer = []; 
} // end StringBuffer()



StringBuffer.prototype.clear = function StringBuffer_clear()
{
  this.buffer.length = 0;
}; // end clear()



StringBuffer.prototype.append = function StringBuffer_append(string)
{
  this.buffer.push(string);
}; // end append()



StringBuffer.prototype.toString = function StringBuffer_toString()
{
  return this.buffer.join("");
}; // end toString()



StringBuffer.prototype.toStringSep = function StringBuffer_toStringSep(sep)
{
  return this.buffer.join(sep);
}; // end toStringSep()



// -----------------------------------------------------------------------------------------
//  Modified Google script that selects Google account depending on which server it runs on
// -----------------------------------------------------------------------------------------

function connectToGoogleAnalytics(doc)
{
  var servers = {};
//  servers["localhost"]          = "UA-58908-1";
  servers["map3"]               = "UA-58908-1";
  servers["www.kystatlas.no"]   = "UA-58908-1";
  servers["fjord"]              = "UA-58908-2";
  servers["fjord.kystatlas.no"] = "UA-58908-2";
  servers["draug"]              = "UA-58908-3";
  servers["ovf.kystatlas.no"]   = "UA-58908-3";
  servers["jnoweb1"]            = "UA-58908-4";
  servers["www.printmpa.no"]    = "UA-58908-4";

  var host = doc.location.host.toLowerCase();
  var acct = servers[host];
  // alert("host=" + host + ", acct=" + acct);
  if (acct)
  {
    // alert("Tracking page on google with account " + acct);
    var pageTracker = _gat._getTracker(acct);
    pageTracker._initData();
    pageTracker._trackPageview();
  }
} // end connectToGoogleAnalytics()
