//==============================================================================
// Copyright (c) 2015 Advanced Micro Devices, Inc. All rights reserved.
/// \author AMD Developer Tools Team
/// \file
/// \brief Javascript for Draggable table and sortable table
//==============================================================================

#ifndef _J_SCRIPT_H_
#define _J_SCRIPT_H_

static const char* g_szDragTableJS =
{
    "/*\n"
    "  dragtable v1.0\n"
    "  June 26, 2008\n"
    "  Dan Vanderkam, http://danvk.org/dragtable/\n"
    "                 http://code.google.com/p/dragtable/\n"
    "\n"
    "  Instructions:\n"
    "    - Download this file\n"
    "    - Add <script src=\"dragtable.js\"></script> to your HTML.\n"
    "    - Add class=\"draggable\" to any table you might like to reorder.\n"
    "    - Drag the headers around to reorder them.\n"
    "\n"
    "  This is code was based on:\n"
    "    - Stuart Langridge's SortTable (kryogenix.org/code/browser/sorttable)\n"
    "    - Mike Hall's draggable class (http://www.brainjar.com/dhtml/drag/)\n"
    "    - A discussion of permuting table columns on comp.lang.javascript\n"
    "\n"
    "  Licensed under the MIT license.\n"
    " */\n"
    "\n"
    "// Here's the notice from Mike Hall's draggable script:\n"
    "//*****************************************************************************\n"
    "// Do not remove this notice.\n"
    "//\n"
    "// Copyright 2001 by Mike Hall.\n"
    "// See http://www.brainjar.com for terms of use.\n"
    "//*****************************************************************************\n"
    "dragtable = {\n"
    "  // How far should the mouse move before it's considered a drag, not a click?\n"
    "  dragRadius2: 100,\n"
    "  setMinDragDistance: function(x) {\n"
    "    dragtable.dragRadius2 = x * x;\n"
    "  },\n"
    "\n"
    "  // How long should cookies persist? (in days)\n"
    "  cookieDays: 365,\n"
    "  setCookieDays: function(x) {\n"
    "    dragtable.cookieDays = x;\n"
    "  },\n"
    "\n"
    "  // Determine browser and version.\n"
    "  // TODO: eliminate browser sniffing except where it's really necessary.\n"
    "  Browser: function() {\n"
    "    var ua, s, i;\n"
    "\n"
    "    this.isIE    = false;\n"
    "    this.isNS    = false;\n"
    "    this.version = null;\n"
    "    ua = navigator.userAgent;\n"
    "\n"
    "    s = \"MSIE\";\n"
    "    if ((i = ua.indexOf(s)) >= 0) {\n"
    "      this.isIE = true;\n"
    "      this.version = parseFloat(ua.substr(i + s.length));\n"
    "      return;\n"
    "    }\n"
    "\n"
    "    s = \"Netscape6/\";\n"
    "    if ((i = ua.indexOf(s)) >= 0) {\n"
    "      this.isNS = true;\n"
    "      this.version = parseFloat(ua.substr(i + s.length));\n"
    "      return;\n"
    "    }\n"
    "\n"
    "    // Treat any other \"Gecko\" browser as NS 6.1.\n"
    "    s = \"Gecko\";\n"
    "    if ((i = ua.indexOf(s)) >= 0) {\n"
    "      this.isNS = true;\n"
    "      this.version = 6.1;\n"
    "      return;\n"
    "    }\n"
    "  },\n"
    "  browser: null,\n"
    "\n"
    "  // Detect all draggable tables and attach handlers to their headers.\n"
    "  init: function() {\n"
    "    // Don't initialize twice\n"
    "    if (arguments.callee.done) return;\n"
    "    arguments.callee.done = true;\n"
    "    if (_dgtimer) clearInterval(_dgtimer);\n"
    "    if (!document.createElement || !document.getElementsByTagName) return;\n"
    "\n"
    "    dragtable.dragObj.zIndex = 0;\n"
    "    dragtable.browser = new dragtable.Browser();\n"
    "    forEach(document.getElementsByTagName('table'), function(table) {\n"
    "      if (table.className.search(/\\bdraggable\\b/) != -1) {\n"
    "        dragtable.makeDraggable(table);\n"
    "      }\n"
    "    });\n"
    "  },\n"
    "\n"
    "  // The thead business is taken straight from sorttable.\n"
    "  makeDraggable: function(table) {\n"
    "    if (table.getElementsByTagName('thead').length == 0) {\n"
    "      the = document.createElement('thead');\n"
    "      the.appendChild(table.rows[0]);\n"
    "      table.insertBefore(the,table.firstChild);\n"
    "    }\n"
    "\n"
    "    // Safari doesn't support table.tHead, sigh\n"
    "    if (table.tHead == null) {\n"
    "      table.tHead = table.getElementsByTagName('thead')[0];\n"
    "    }\n"
    "\n"
    "    var headers = table.tHead.rows[0].cells;\n"
    "    for (var i = 0; i < headers.length; i++) {\n"
    "      headers[i].onmousedown = dragtable.dragStart;\n"
    "    }\n"
    "\n"
    "		// Replay reorderings from cookies if there are any.\n"
    "		if (dragtable.cookiesEnabled() && table.id &&\n"
    "				table.className.search(/\\bforget-ordering\\b/) == -1) {\n"
    "			dragtable.replayDrags(table);\n"
    "		}\n"
    "  },\n"
    "\n"
    "  // Global object to hold drag information.\n"
    "  dragObj: new Object(),\n"
    "\n"
    "  // Climb up the DOM until there's a tag that matches.\n"
    "  findUp: function(elt, tag) {\n"
    "    do {\n"
    "      if (elt.nodeName && elt.nodeName.search(tag) != -1)\n"
    "        return elt;\n"
    "    } while (elt = elt.parentNode);\n"
    "    return null;\n"
    "  },\n"
    "\n"
    "  // clone an element, copying its style and class.\n"
    "  fullCopy: function(elt, deep) {\n"
    "    var new_elt = elt.cloneNode(deep);\n"
    "    new_elt.className = elt.className;\n"
    "    forEach(elt.style,\n"
    "        function(value, key, object) {\n"
    "          if (value == null) return;\n"
    "          if (typeof(value) == \"string\" && value.length == 0) return;\n"
    "\n"
    "          new_elt.style[key] = elt.style[key];\n"
    "        });\n"
    "    return new_elt;\n"
    "  },\n"
    "\n"
    "  eventPosition: function(event) {\n"
    "    var x, y;\n"
    "    if (dragtable.browser.isIE) {\n"
    "      x = window.event.clientX + document.documentElement.scrollLeft\n"
    "        + document.body.scrollLeft;\n"
    "      y = window.event.clientY + document.documentElement.scrollTop\n"
    "        + document.body.scrollTop;\n"
    "      return {x: x, y: y};\n"
    "    }\n"
    "    return {x: event.pageX, y: event.pageY};\n"
    "  },\n"
    "\n"
    " // Determine the position of this element on the page. Many thanks to Magnus\n"
    " // Kristiansen for help making this work with \"position: fixed\" elements.\n"
    " absolutePosition: function(elt, stopAtRelative) {\n"
    "   var ex = 0, ey = 0;\n"
    "   do {\n"
    "     var curStyle = dragtable.browser.isIE ? elt.currentStyle\n"
    "                                           : window.getComputedStyle(elt, '');\n"
    "     var supportFixed = !(dragtable.browser.isIE &&\n"
    "                          dragtable.browser.version < 7);\n"
    "     if (stopAtRelative && curStyle.position == 'relative') {\n"
    "       break;\n"
    "     } else if (supportFixed && curStyle.position == 'fixed') {\n"
    "       // Get the fixed el's offset\n"
    "       ex += parseInt(curStyle.left, 10);\n"
    "       ey += parseInt(curStyle.top, 10);\n"
    "       // Compensate for scrolling\n"
    "       ex += document.body.scrollLeft;\n"
    "       ey += document.body.scrollTop;\n"
    "       // End the loop\n"
    "       break;\n"
    "     } else {\n"
    "       ex += elt.offsetLeft;\n"
    "       ey += elt.offsetTop;\n"
    "     }\n"
    "   } while (elt = elt.offsetParent);\n"
    "   return {x: ex, y: ey};\n"
    " },\n"
    "\n"
    "  // MouseDown handler -- sets up the appropriate mousemove/mouseup handlers\n"
    "  // and fills in the global dragtable.dragObj object.\n"
    "  dragStart: function(event, id) {\n"
    "    var el;\n"
    "    var x, y;\n"
    "    var dragObj = dragtable.dragObj;\n"
    "\n"
    "    var browser = dragtable.browser;\n"
    "    if (browser.isIE)\n"
    "      dragObj.origNode = window.event.srcElement;\n"
    "    else\n"
    "      dragObj.origNode = event.target;\n"
    "    var pos = dragtable.eventPosition(event);\n"
    "\n"
    "    // Drag the entire table cell, not just the element that was clicked.\n"
    "    dragObj.origNode = dragtable.findUp(dragObj.origNode, /T[DH]/);\n"
    "\n"
    "    // Since a column header can't be dragged directly, duplicate its contents\n"
    "    // in a div and drag that instead.\n"
    "    // TODO: I can assume a tHead...\n"
    "    var table = dragtable.findUp(dragObj.origNode, \"TABLE\");\n"
    "    dragObj.table = table;\n"
    "    dragObj.startCol = dragtable.findColumn(table, pos.x);\n"
    "    if (dragObj.startCol == -1) return;\n"
    "\n"
    "    var new_elt = dragtable.fullCopy(table, false);\n"
    "    new_elt.style.margin = '0';\n"
    "\n"
    "    // Copy the entire column\n"
    "    var copySectionColumn = function(sec, col) {\n"
    "      var new_sec = dragtable.fullCopy(sec, false);\n"
    "      forEach(sec.rows, function(row) {\n"
    "        var cell = row.cells[col];\n"
    "        var new_tr = dragtable.fullCopy(row, false);\n"
    "        if (row.offsetHeight) new_tr.style.height = row.offsetHeight + \"px\";\n"
    "        var new_td = dragtable.fullCopy(cell, true);\n"
    "        if (cell.offsetWidth) new_td.style.width = cell.offsetWidth + \"px\";\n"
    "        new_tr.appendChild(new_td);\n"
    "        new_sec.appendChild(new_tr);\n"
    "      });\n"
    "      return new_sec;\n"
    "    };\n"
    "\n"
    "    // First the heading\n"
    "    if (table.tHead) {\n"
    "      new_elt.appendChild(copySectionColumn(table.tHead, dragObj.startCol));\n"
    "    }\n"
    "    forEach(table.tBodies, function(tb) {\n"
    "      new_elt.appendChild(copySectionColumn(tb, dragObj.startCol));\n"
    "    });\n"
    "    if (table.tFoot) {\n"
    "      new_elt.appendChild(copySectionColumn(table.tFoot, dragObj.startCol));\n"
    "    }\n"
    "\n"
    "    var obj_pos = dragtable.absolutePosition(dragObj.origNode, true);\n"
    "    new_elt.style.position = \"absolute\";\n"
    "    new_elt.style.left = obj_pos.x + \"px\";\n"
    "    new_elt.style.top = obj_pos.y + \"px\";\n"
    "    new_elt.style.width = dragObj.origNode.offsetWidth + \"px\";\n"
    "    new_elt.style.height = dragObj.origNode.offsetHeight + \"px\";\n"
    "    new_elt.style.opacity = 0.7;\n"
    "\n"
    "    // Hold off adding the element until this is clearly a drag.\n"
    "    dragObj.addedNode = false;\n"
    "    dragObj.tableContainer = dragObj.table.parentNode || document.body;\n"
    "    dragObj.elNode = new_elt;\n"
    "\n"
    "    // Save starting positions of cursor and element.\n"
    "    dragObj.cursorStartX = pos.x;\n"
    "    dragObj.cursorStartY = pos.y;\n"
    "    dragObj.elStartLeft  = parseInt(dragObj.elNode.style.left, 10);\n"
    "    dragObj.elStartTop   = parseInt(dragObj.elNode.style.top,  10);\n"
    "\n"
    "    if (isNaN(dragObj.elStartLeft)) dragObj.elStartLeft = 0;\n"
    "    if (isNaN(dragObj.elStartTop))  dragObj.elStartTop  = 0;\n"
    "\n"
    "    // Update element's z-index.\n"
    "    dragObj.elNode.style.zIndex = ++dragObj.zIndex;\n"
    "\n"
    "    // Capture mousemove and mouseup events on the page.\n"
    "    if (browser.isIE) {\n"
    "      document.attachEvent(\"onmousemove\", dragtable.dragMove);\n"
    "      document.attachEvent(\"onmouseup\",   dragtable.dragEnd);\n"
    "      window.event.cancelBubble = true;\n"
    "      window.event.returnValue = false;\n"
    "    } else {\n"
    "      document.addEventListener(\"mousemove\", dragtable.dragMove, true);\n"
    "      document.addEventListener(\"mouseup\",   dragtable.dragEnd, true);\n"
    "      event.preventDefault();\n"
    "    }\n"
    "  },\n"
    "\n"
    "  // Move the floating column header with the mouse\n"
    "  // TODO: Reorder columns as the mouse moves for a more interactive feel.\n"
    "  dragMove: function(event) {\n"
    "    var x, y;\n"
    "    var dragObj = dragtable.dragObj;\n"
    "\n"
    "    // Get cursor position with respect to the page.\n"
    "    var pos = dragtable.eventPosition(event);\n"
    "\n"
    "    var dx = dragObj.cursorStartX - pos.x;\n"
    "    var dy = dragObj.cursorStartY - pos.y;\n"
    "    if (!dragObj.addedNode && dx * dx + dy * dy > dragtable.dragRadius2) {\n"
    "      dragObj.tableContainer.insertBefore(dragObj.elNode, dragObj.table);\n"
    "      dragObj.addedNode = true;\n"
    "    }\n"
    "\n"
    "    // Move drag element by the same amount the cursor has moved.\n"
    "    var style = dragObj.elNode.style;\n"
    "    style.left = (dragObj.elStartLeft + pos.x - dragObj.cursorStartX) + \"px\";\n"
    "    style.top  = (dragObj.elStartTop  + pos.y - dragObj.cursorStartY) + \"px\";\n"
    "\n"
    "    if (dragtable.browser.isIE) {\n"
    "      window.event.cancelBubble = true;\n"
    "      window.event.returnValue = false;\n"
    "    } else {\n"
    "      event.preventDefault();\n"
    "    }\n"
    "  },\n"
    "\n"
    "  // Stop capturing mousemove and mouseup events.\n"
    "  // Determine which (if any) column we're over and shuffle the table.\n"
    "  dragEnd: function(event) {\n"
    "    if (dragtable.browser.isIE) {\n"
    "      document.detachEvent(\"onmousemove\", dragtable.dragMove);\n"
    "      document.detachEvent(\"onmouseup\", dragtable.dragEnd);\n"
    "    } else {\n"
    "      document.removeEventListener(\"mousemove\", dragtable.dragMove, true);\n"
    "      document.removeEventListener(\"mouseup\", dragtable.dragEnd, true);\n"
    "    }\n"
    "\n"
    "    // If the floating header wasn't added, the mouse didn't move far enough.\n"
    "    var dragObj = dragtable.dragObj;\n"
    "    if (!dragObj.addedNode) {\n"
    "      return;\n"
    "    }\n"
    "    dragObj.tableContainer.removeChild(dragObj.elNode);\n"
    "\n"
    "    // Determine whether the drag ended over the table, and over which column.\n"
    "    var pos = dragtable.eventPosition(event);\n"
    "    var table_pos = dragtable.absolutePosition(dragObj.table);\n"
    "    if (pos.y < table_pos.y ||\n"
    "        pos.y > table_pos.y + dragObj.table.offsetHeight) {\n"
    "      return;\n"
    "    }\n"
    "    var targetCol = dragtable.findColumn(dragObj.table, pos.x);\n"
    "    if (targetCol != -1 && targetCol != dragObj.startCol) {\n"
    "      dragtable.moveColumn(dragObj.table, dragObj.startCol, targetCol);\n"
    "      if (dragObj.table.id && dragtable.cookiesEnabled() &&\n"
    "					dragObj.table.className.search(/\\bforget-ordering\\b/) == -1) {\n"
    "        dragtable.rememberDrag(dragObj.table.id, dragObj.startCol, targetCol);\n"
    "      }\n"
    "    }\n"
    "  },\n"
    "\n"
    "  // Which column does the x value fall inside of? x should include scrollLeft.\n"
    "  findColumn: function(table, x) {\n"
    "    var header = table.tHead.rows[0].cells;\n"
    "    for (var i = 0; i < header.length; i++) {\n"
    "      //var left = header[i].offsetLeft;\n"
    "      var pos = dragtable.absolutePosition(header[i]);\n"
    "      //if (left <= x && x <= left + header[i].offsetWidth) {\n"
    "      if (pos.x <= x && x <= pos.x + header[i].offsetWidth) {\n"
    "        return i;\n"
    "      }\n"
    "    }\n"
    "    return -1;\n"
    "  },\n"
    "\n"
    "  // Move a column of table from start index to finish index.\n"
    "  // Based on the \"Swapping table columns\" discussion on comp.lang.javascript.\n"
    "  // Assumes there are columns at sIdx and fIdx\n"
    "  moveColumn: function(table, sIdx, fIdx) {\n"
    "    var row, cA;\n"
    "    var i=table.rows.length;\n"
    "    while (i--){\n"
    "      row = table.rows[i]\n"
    "      var x = row.removeChild(row.cells[sIdx]);\n"
    "      if (fIdx < row.cells.length) {\n"
    "        row.insertBefore(x, row.cells[fIdx]);\n"
    "      } else {\n"
    "        row.appendChild(x);\n"
    "      }\n"
    "    }\n"
    "\n"
    "    // For whatever reason, sorttable tracks column indices this way.\n"
    "    // Without a manual update, clicking one column will sort on another.\n"
    "    var headrow = table.tHead.rows[0].cells;\n"
    "    for (var i=0; i<headrow.length; i++) {\n"
    "      headrow[i].sorttable_columnindex = i;\n"
    "    }\n"
    "  },\n"
    "\n"
    "  // Are cookies enabled? We should not attempt to set cookies on a local file.\n"
    "  cookiesEnabled: function() {\n"
    "    return (window.location.protocol != 'file:') && navigator.cookieEnabled;\n"
    "  },\n"
    "\n"
    "  // Store a column swap in a cookie for posterity.\n"
    "  rememberDrag: function(id, a, b) {\n"
    "    var cookieName = \"dragtable-\" + id;\n"
    "    var prev = dragtable.readCookie(cookieName);\n"
    "    var new_val = \"\";\n"
    "    if (prev) new_val = prev + \",\";\n"
    "    new_val += a + \"/\" + b;\n"
    "    dragtable.createCookie(cookieName, new_val, dragtable.cookieDays);\n"
    "  },\n"
    "\n"
    "	// Replay all column swaps for a table.\n"
    "	replayDrags: function(table) {\n"
    "		if (!dragtable.cookiesEnabled()) return;\n"
    "		var dragstr = dragtable.readCookie(\"dragtable-\" + table.id);\n"
    "		if (!dragstr) return;\n"
    "		var drags = dragstr.split(',');\n"
    "		for (var i = 0; i < drags.length; i++) {\n"
    "			var pair = drags[i].split(\"/\");\n"
    "			if (pair.length != 2) continue;\n"
    "			var a = parseInt(pair[0]);\n"
    "			var b = parseInt(pair[1]);\n"
    "			if (isNaN(a) || isNaN(b)) continue;\n"
    "			dragtable.moveColumn(table, a, b);\n"
    "		}\n"
    "	},\n"
    "\n"
    "  // Cookie functions based on http://www.quirksmode.org/js/cookies.html\n"
    "  // Cookies won't work for local files.\n"
    "  cookiesEnabled: function() {\n"
    "    return (window.location.protocol != 'file:') && navigator.cookieEnabled;\n"
    "  },\n"
    "\n"
    "  createCookie: function(name,value,days) {\n"
    "    if (days) {\n"
    "      var date = new Date();\n"
    "      date.setTime(date.getTime()+(days*24*60*60*1000));\n"
    "      var expires = \"; expires=\"+date.toGMTString();\n"
    "    }\n"
    "    else var expires = \"\";\n"
    "\n"
    "		var path = document.location.pathname;\n"
    "    document.cookie = name+\"=\"+value+expires+\"; path=\"+path\n"
    "  },\n"
    "\n"
    "  readCookie: function(name) {\n"
    "    var nameEQ = name + \"=\";\n"
    "    var ca = document.cookie.split(';');\n"
    "    for(var i=0;i < ca.length;i++) {\n"
    "      var c = ca[i];\n"
    "      while (c.charAt(0)==' ') c = c.substring(1,c.length);\n"
    "      if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);\n"
    "    }\n"
    "    return null;\n"
    "  },\n"
    "\n"
    "  eraseCookie: function(name) {\n"
    "    dragtable.createCookie(name,\"\",-1);\n"
    "  }\n"
    "\n"
    "}\n"
    "\n"
    "/* ******************************************************************\n"
    "   Supporting functions: bundled here to avoid depending on a library\n"
    "   ****************************************************************** */\n"
    "\n"
    "// Dean Edwards/Matthias Miller/John Resig\n"
    "// has a hook for dragtable.init already been added? (see below)\n"
    "var dgListenOnLoad = false;\n"
    "\n"
    "/* for Mozilla/Opera9 */\n"
    "if (document.addEventListener) {\n"
    "  dgListenOnLoad = true;\n"
    "  document.addEventListener(\"DOMContentLoaded\", dragtable.init, false);\n"
    "}\n"
    "\n"
    "/* for Internet Explorer */\n"
    "/*@cc_on @*/\n"
    "/*@if (@_win32)\n"
    "  dgListenOnLoad = true;\n"
    "  document.write(\"<script id=__dt_onload defer src=//0)><\\/script>\");\n"
    "  var script = document.getElementById(\"__dt_onload\");\n"
    "  script.onreadystatechange = function() {\n"
    "    if (this.readyState == \"complete\") {\n"
    "      dragtable.init(); // call the onload handler\n"
    "    }\n"
    "  };\n"
    "/*@end @*/\n"
    "\n"
    "/* for Safari */\n"
    "if (/WebKit/i.test(navigator.userAgent)) { // sniff\n"
    "  dgListenOnLoad = true;\n"
    "  var _dgtimer = setInterval(function() {\n"
    "    if (/loaded|complete/.test(document.readyState)) {\n"
    "      dragtable.init(); // call the onload handler\n"
    "    }\n"
    "  }, 10);\n"
    "}\n"
    "\n"
    "/* for other browsers */\n"
    "/* Avoid this unless it's absolutely necessary (it breaks sorttable) */\n"
    "if (!dgListenOnLoad) {\n"
    "  window.onload = dragtable.init;\n"
    "}\n"
    "\n"
    "// Dean's forEach: http://dean.edwards.name/base/forEach.js\n"
    "/*\n"
    "  forEach, version 1.0\n"
    "  Copyright 2006, Dean Edwards\n"
    "  License: http://www.opensource.org/licenses/mit-license.php\n"
    "*/\n"
    "\n"
    "// array-like enumeration\n"
    "if (!Array.forEach) { // mozilla already supports this\n"
    "  Array.forEach = function(array, block, context) {\n"
    "    for (var i = 0; i < array.length; i++) {\n"
    "      block.call(context, array[i], i, array);\n"
    "    }\n"
    "  };\n"
    "}\n"
    "\n"
    "// generic enumeration\n"
    "Function.prototype.forEach = function(object, block, context) {\n"
    "  for (var key in object) {\n"
    "    if (typeof this.prototype[key] == \"undefined\") {\n"
    "      block.call(context, object[key], key, object);\n"
    "    }\n"
    "  }\n"
    "};\n"
    "\n"
    "// character enumeration\n"
    "String.forEach = function(string, block, context) {\n"
    "  Array.forEach(string.split(\"\"), function(chr, index) {\n"
    "    block.call(context, chr, index, string);\n"
    "  });\n"
    "};\n"
    "\n"
    "// globally resolve forEach enumeration\n"
    "var forEach = function(object, block, context) {\n"
    "  if (object) {\n"
    "    var resolve = Object; // default\n"
    "    if (object instanceof Function) {\n"
    "      // functions have a \"length\" property\n"
    "      resolve = Function;\n"
    "    } else if (object.forEach instanceof Function) {\n"
    "      // the object implements a custom forEach method so use that\n"
    "      object.forEach(block, context);\n"
    "      return;\n"
    "    } else if (typeof object == \"string\") {\n"
    "      // the object is a string\n"
    "      resolve = String;\n"
    "    } else if (typeof object.length == \"number\") {\n"
    "      // the object is array-like\n"
    "      resolve = Array;\n"
    "    }\n"
    "    resolve.forEach(object, block, context);\n"
    "  }\n"
    "};\n"
};

static const char* g_szSortedTableJS =
{
    "/*\n"
    "  SortTable\n"
    "  version 2\n"
    "  7th April 2007\n"
    "  Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/\n"
    "  \n"
    "  Instructions:\n"
    "  Download this file\n"
    "  Add <script src=\"sorttable.js\"></script> to your HTML\n"
    "  Add class=\"sortable\" to any table you'd like to make sortable\n"
    "  Click on the headers to sort\n"
    "  \n"
    "  Thanks to many, many people for contributions and suggestions.\n"
    "  Licenced as X11: http://www.kryogenix.org/code/browser/licence.html\n"
    "  This basically means: do what you want with it.\n"
    "*/\n"
    "\n"
    " \n"
    "var stIsIE = /*@cc_on!@*/false;\n"
    "\n"
    "sorttable = {\n"
    "  init: function() {\n"
    "    // quit if this function has already been called\n"
    "    if (arguments.callee.done) return;\n"
    "    // flag this function so we don't do the same thing twice\n"
    "    arguments.callee.done = true;\n"
    "    // kill the timer\n"
    "    if (_timer) clearInterval(_timer);\n"
    "    \n"
    "    if (!document.createElement || !document.getElementsByTagName) return;\n"
    "    \n"
    "    sorttable.DATE_RE = /^(\\d\\d?)[\\/\\.-](\\d\\d?)[\\/\\.-]((\\d\\d)?\\d\\d)$/;\n"
    "    \n"
    "    forEach(document.getElementsByTagName('table'), function(table) {\n"
    "      if (table.className.search(/\\bsortable\\b/) != -1) {\n"
    "        sorttable.makeSortable(table);\n"
    "      }\n"
    "    });\n"
    "    \n"
    "  },\n"
    "  \n"
    "  makeSortable: function(table) {\n"
    "    if (table.getElementsByTagName('thead').length == 0) {\n"
    "      // table doesn't have a tHead. Since it should have, create one and\n"
    "      // put the first table row in it.\n"
    "      the = document.createElement('thead');\n"
    "      the.appendChild(table.rows[0]);\n"
    "      table.insertBefore(the,table.firstChild);\n"
    "    }\n"
    "    // Safari doesn't support table.tHead, sigh\n"
    "    if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0];\n"
    "    \n"
    "    if (table.tHead.rows.length != 1) return; // can't cope with two header rows\n"
    "    \n"
    "    // Sorttable v1 put rows with a class of \"sortbottom\" at the bottom (as\n"
    "    // \"total\" rows, for example). This is B&R, since what you're supposed\n"
    "    // to do is put them in a tfoot. So, if there are sortbottom rows,\n"
    "    // for backwards compatibility, move them to tfoot (creating it if needed).\n"
    "    sortbottomrows = [];\n"
    "    for (var i=0; i<table.rows.length; i++) {\n"
    "      if (table.rows[i].className.search(/\\bsortbottom\\b/) != -1) {\n"
    "        sortbottomrows[sortbottomrows.length] = table.rows[i];\n"
    "      }\n"
    "    }\n"
    "    if (sortbottomrows) {\n"
    "      if (table.tFoot == null) {\n"
    "        // table doesn't have a tfoot. Create one.\n"
    "        tfo = document.createElement('tfoot');\n"
    "        table.appendChild(tfo);\n"
    "      }\n"
    "      for (var i=0; i<sortbottomrows.length; i++) {\n"
    "        tfo.appendChild(sortbottomrows[i]);\n"
    "      }\n"
    "      delete sortbottomrows;\n"
    "    }\n"
    "    \n"
    "    // work through each column and calculate its type\n"
    "    headrow = table.tHead.rows[0].cells;\n"
    "    for (var i=0; i<headrow.length; i++) {\n"
    "      // manually override the type with a sorttable_type attribute\n"
    "      if (!headrow[i].className.match(/\\bsorttable_nosort\\b/)) { // skip this col\n"
    "        mtch = headrow[i].className.match(/\\bsorttable_([a-z0-9]+)\\b/);\n"
    "        if (mtch) { override = mtch[1]; }\n"
    "	      if (mtch && typeof sorttable[\"sort_\"+override] == 'function') {\n"
    "	        headrow[i].sorttable_sortfunction = sorttable[\"sort_\"+override];\n"
    "	      } else {\n"
    "	        headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);\n"
    "	      }\n"
    "	      // make it clickable to sort\n"
    "	      headrow[i].sorttable_columnindex = i;\n"
    "	      headrow[i].sorttable_tbody = table.tBodies[0];\n"
    "	      dean_addEvent(headrow[i],\"click\", function(e) {\n"
    "\n"
    "          if (this.className.search(/\\bsorttable_sorted\\b/) != -1) {\n"
    "            // if we're already sorted by this column, just \n"
    "            // reverse the table, which is quicker\n"
    "            sorttable.reverse(this.sorttable_tbody);\n"
    "            this.className = this.className.replace('sorttable_sorted',\n"
    "                                                    'sorttable_sorted_reverse');\n"
    "            this.removeChild(document.getElementById('sorttable_sortfwdind'));\n"
    "            sortrevind = document.createElement('span');\n"
    "            sortrevind.id = \"sorttable_sortrevind\";\n"
    "            sortrevind.innerHTML = stIsIE ? '&nbsp<font face=\"webdings\">5</font>' : '&nbsp;&#x25B4;';\n"
    "            this.appendChild(sortrevind);\n"
    "            return;\n"
    "          }\n"
    "          if (this.className.search(/\\bsorttable_sorted_reverse\\b/) != -1) {\n"
    "            // if we're already sorted by this column in reverse, just \n"
    "            // re-reverse the table, which is quicker\n"
    "            sorttable.reverse(this.sorttable_tbody);\n"
    "            this.className = this.className.replace('sorttable_sorted_reverse',\n"
    "                                                    'sorttable_sorted');\n"
    "            this.removeChild(document.getElementById('sorttable_sortrevind'));\n"
    "            sortfwdind = document.createElement('span');\n"
    "            sortfwdind.id = \"sorttable_sortfwdind\";\n"
    "            sortfwdind.innerHTML = stIsIE ? '&nbsp<font face=\"webdings\">6</font>' : '&nbsp;&#x25BE;';\n"
    "            this.appendChild(sortfwdind);\n"
    "            return;\n"
    "          }\n"
    "          \n"
    "          // remove sorttable_sorted classes\n"
    "          theadrow = this.parentNode;\n"
    "          forEach(theadrow.childNodes, function(cell) {\n"
    "            if (cell.nodeType == 1) { // an element\n"
    "              cell.className = cell.className.replace('sorttable_sorted_reverse','');\n"
    "              cell.className = cell.className.replace('sorttable_sorted','');\n"
    "            }\n"
    "          });\n"
    "          sortfwdind = document.getElementById('sorttable_sortfwdind');\n"
    "          if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }\n"
    "          sortrevind = document.getElementById('sorttable_sortrevind');\n"
    "          if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }\n"
    "          \n"
    "          this.className += ' sorttable_sorted';\n"
    "          sortfwdind = document.createElement('span');\n"
    "          sortfwdind.id = \"sorttable_sortfwdind\";\n"
    "          sortfwdind.innerHTML = stIsIE ? '&nbsp<font face=\"webdings\">6</font>' : '&nbsp;&#x25BE;';\n"
    "          this.appendChild(sortfwdind);\n"
    "\n"
    "	        // build an array to sort. This is a Schwartzian transform thing,\n"
    "	        // i.e., we \"decorate\" each row with the actual sort key,\n"
    "	        // sort based on the sort keys, and then put the rows back in order\n"
    "	        // which is a lot faster because you only do getInnerText once per row\n"
    "	        row_array = [];\n"
    "	        col = this.sorttable_columnindex;\n"
    "	        rows = this.sorttable_tbody.rows;\n"
    "	        for (var j=0; j<rows.length; j++) {\n"
    "	          row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];\n"
    "	        }\n"
    "	        /* If you want a stable sort, uncomment the following line */\n"
    "	        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);\n"
    "	        /* and comment out this one */\n"
    "	        row_array.sort(this.sorttable_sortfunction);\n"
    "	        \n"
    "	        tb = this.sorttable_tbody;\n"
    "	        for (var j=0; j<row_array.length; j++) {\n"
    "	          tb.appendChild(row_array[j][1]);\n"
    "	        }\n"
    "	        \n"
    "	        delete row_array;\n"
    "	      });\n"
    "	    }\n"
    "    }\n"
    "  },\n"
    "  \n"
    "  guessType: function(table, column) {\n"
    "    // guess the type of a column based on its first non-blank row\n"
    "    sortfn = sorttable.sort_alpha;\n"
    "    for (var i=0; i<table.tBodies[0].rows.length; i++) {\n"
    "      text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);\n"
    "      if (text != '') {\n"
    "        if (text.match(/^-?[£$¤]?[\\d,.]+%?$/)) {\n"
    "          return sorttable.sort_numeric;\n"
    "        }\n"
    "        // check for a date: dd/mm/yyyy or dd/mm/yy \n"
    "        // can have / or . or - as separator\n"
    "        // can be mm/dd as well\n"
    "        possdate = text.match(sorttable.DATE_RE)\n"
    "        if (possdate) {\n"
    "          // looks like a date\n"
    "          first = parseInt(possdate[1]);\n"
    "          second = parseInt(possdate[2]);\n"
    "          if (first > 12) {\n"
    "            // definitely dd/mm\n"
    "            return sorttable.sort_ddmm;\n"
    "          } else if (second > 12) {\n"
    "            return sorttable.sort_mmdd;\n"
    "          } else {\n"
    "            // looks like a date, but we can't tell which, so assume\n"
    "            // that it's dd/mm (English imperialism!) and keep looking\n"
    "            sortfn = sorttable.sort_ddmm;\n"
    "          }\n"
    "        }\n"
    "      }\n"
    "    }\n"
    "    return sortfn;\n"
    "  },\n"
    "  \n"
    "  getInnerText: function(node) {\n"
    "    // gets the text we want to use for sorting for a cell.\n"
    "    // strips leading and trailing whitespace.\n"
    "    // this is *not* a generic getInnerText function; it's special to sorttable.\n"
    "    // for example, you can override the cell text with a customkey attribute.\n"
    "    // it also gets .value for <input> fields.\n"
    "    \n"
    "    hasInputs = (typeof node.getElementsByTagName == 'function') &&\n"
    "                 node.getElementsByTagName('input').length;\n"
    "    \n"
    "    if (node.getAttribute(\"sorttable_customkey\") != null) {\n"
    "      return node.getAttribute(\"sorttable_customkey\");\n"
    "    }\n"
    "    else if (typeof node.textContent != 'undefined' && !hasInputs) {\n"
    "      return node.textContent.replace(/^\\s+|\\s+$/g, '');\n"
    "    }\n"
    "    else if (typeof node.innerText != 'undefined' && !hasInputs) {\n"
    "      return node.innerText.replace(/^\\s+|\\s+$/g, '');\n"
    "    }\n"
    "    else if (typeof node.text != 'undefined' && !hasInputs) {\n"
    "      return node.text.replace(/^\\s+|\\s+$/g, '');\n"
    "    }\n"
    "    else {\n"
    "      switch (node.nodeType) {\n"
    "        case 3:\n"
    "          if (node.nodeName.toLowerCase() == 'input') {\n"
    "            return node.value.replace(/^\\s+|\\s+$/g, '');\n"
    "          }\n"
    "        case 4:\n"
    "          return node.nodeValue.replace(/^\\s+|\\s+$/g, '');\n"
    "          break;\n"
    "        case 1:\n"
    "        case 11:\n"
    "          var innerText = '';\n"
    "          for (var i = 0; i < node.childNodes.length; i++) {\n"
    "            innerText += sorttable.getInnerText(node.childNodes[i]);\n"
    "          }\n"
    "          return innerText.replace(/^\\s+|\\s+$/g, '');\n"
    "          break;\n"
    "        default:\n"
    "          return '';\n"
    "      }\n"
    "    }\n"
    "  },\n"
    "  \n"
    "  reverse: function(tbody) {\n"
    "    // reverse the rows in a tbody\n"
    "    newrows = [];\n"
    "    for (var i=0; i<tbody.rows.length; i++) {\n"
    "      newrows[newrows.length] = tbody.rows[i];\n"
    "    }\n"
    "    for (var i=newrows.length-1; i>=0; i--) {\n"
    "       tbody.appendChild(newrows[i]);\n"
    "    }\n"
    "    delete newrows;\n"
    "  },\n"
    "  \n"
    "  /* sort functions\n"
    "     each sort function takes two parameters, a and b\n"
    "     you are comparing a[0] and b[0] */\n"
    "  sort_numeric: function(a,b) {\n"
    "    aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));\n"
    "    if (isNaN(aa)) aa = 0;\n"
    "    bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); \n"
    "    if (isNaN(bb)) bb = 0;\n"
    "    return aa-bb;\n"
    "  },\n"
    "  sort_revnumeric: function(a,b) {\n"
    "    return -sorttable.sort_numeric(a,b)\n"
    "},\n"
    "  sort_alpha: function(a,b) {\n"
    "    if (a[0]==b[0]) return 0;\n"
    "    if (a[0]<b[0]) return -1;\n"
    "    return 1;\n"
    "  },\n"
    "  sort_ddmm: function(a,b) {\n"
    "    mtch = a[0].match(sorttable.DATE_RE);\n"
    "    y = mtch[3]; m = mtch[2]; d = mtch[1];\n"
    "    if (m.length == 1) m = '0'+m;\n"
    "    if (d.length == 1) d = '0'+d;\n"
    "    dt1 = y+m+d;\n"
    "    mtch = b[0].match(sorttable.DATE_RE);\n"
    "    y = mtch[3]; m = mtch[2]; d = mtch[1];\n"
    "    if (m.length == 1) m = '0'+m;\n"
    "    if (d.length == 1) d = '0'+d;\n"
    "    dt2 = y+m+d;\n"
    "    if (dt1==dt2) return 0;\n"
    "    if (dt1<dt2) return -1;\n"
    "    return 1;\n"
    "  },\n"
    "  sort_mmdd: function(a,b) {\n"
    "    mtch = a[0].match(sorttable.DATE_RE);\n"
    "    y = mtch[3]; d = mtch[2]; m = mtch[1];\n"
    "    if (m.length == 1) m = '0'+m;\n"
    "    if (d.length == 1) d = '0'+d;\n"
    "    dt1 = y+m+d;\n"
    "    mtch = b[0].match(sorttable.DATE_RE);\n"
    "    y = mtch[3]; d = mtch[2]; m = mtch[1];\n"
    "    if (m.length == 1) m = '0'+m;\n"
    "    if (d.length == 1) d = '0'+d;\n"
    "    dt2 = y+m+d;\n"
    "    if (dt1==dt2) return 0;\n"
    "    if (dt1<dt2) return -1;\n"
    "    return 1;\n"
    "  },\n"
    "  \n"
    "  shaker_sort: function(list, comp_func) {\n"
    "    // A stable sort function to allow multi-level sorting of data\n"
    "    // see: http://en.wikipedia.org/wiki/Cocktail_sort\n"
    "    // thanks to Joseph Nahmias\n"
    "    var b = 0;\n"
    "    var t = list.length - 1;\n"
    "    var swap = true;\n"
    "\n"
    "    while(swap) {\n"
    "        swap = false;\n"
    "        for(var i = b; i < t; ++i) {\n"
    "            if ( comp_func(list[i], list[i+1]) > 0 ) {\n"
    "                var q = list[i]; list[i] = list[i+1]; list[i+1] = q;\n"
    "                swap = true;\n"
    "            }\n"
    "        } // for\n"
    "        t--;\n"
    "\n"
    "        if (!swap) break;\n"
    "\n"
    "        for(var i = t; i > b; --i) {\n"
    "            if ( comp_func(list[i], list[i-1]) < 0 ) {\n"
    "                var q = list[i]; list[i] = list[i-1]; list[i-1] = q;\n"
    "                swap = true;\n"
    "            }\n"
    "        } // for\n"
    "        b++;\n"
    "\n"
    "    } // while(swap)\n"
    "  }  \n"
    "}\n"
    "\n"
    "/* ******************************************************************\n"
    "   Supporting functions: bundled here to avoid depending on a library\n"
    "   ****************************************************************** */\n"
    "\n"
    "// Dean Edwards/Matthias Miller/John Resig\n"
    "\n"
    "/* for Mozilla/Opera9 */\n"
    "if (document.addEventListener) {\n"
    "    document.addEventListener(\"DOMContentLoaded\", sorttable.init, false);\n"
    "}\n"
    "\n"
    "/* for Internet Explorer */\n"
    "/*@cc_on @*/\n"
    "/*@if (@_win32)\n"
    "    document.write(\"<script id=__ie_onload defer src=javascript:void(0)><\\/script>\");\n"
    "    var script = document.getElementById(\"__ie_onload\");\n"
    "    script.onreadystatechange = function() {\n"
    "        if (this.readyState == \"complete\") {\n"
    "            sorttable.init(); // call the onload handler\n"
    "        }\n"
    "    };\n"
    "/*@end @*/\n"
    "\n"
    "/* for Safari */\n"
    "if (/WebKit/i.test(navigator.userAgent)) { // sniff\n"
    "    var _timer = setInterval(function() {\n"
    "        if (/loaded|complete/.test(document.readyState)) {\n"
    "            sorttable.init(); // call the onload handler\n"
    "        }\n"
    "    }, 10);\n"
    "}\n"
    "\n"
    "/* for other browsers */\n"
    "window.onload = sorttable.init;\n"
    "\n"
    "// written by Dean Edwards, 2005\n"
    "// with input from Tino Zijdel, Matthias Miller, Diego Perini\n"
    "\n"
    "// http://dean.edwards.name/weblog/2005/10/add-event/\n"
    "\n"
    "function dean_addEvent(element, type, handler) {\n"
    "	if (element.addEventListener) {\n"
    "		element.addEventListener(type, handler, false);\n"
    "	} else {\n"
    "		// assign each event handler a unique ID\n"
    "		if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;\n"
    "		// create a hash table of event types for the element\n"
    "		if (!element.events) element.events = {};\n"
    "		// create a hash table of event handlers for each element/event pair\n"
    "		var handlers = element.events[type];\n"
    "		if (!handlers) {\n"
    "			handlers = element.events[type] = {};\n"
    "			// store the existing event handler (if there is one)\n"
    "			if (element[\"on\" + type]) {\n"
    "				handlers[0] = element[\"on\" + type];\n"
    "			}\n"
    "		}\n"
    "		// store the event handler in the hash table\n"
    "		handlers[handler.$$guid] = handler;\n"
    "		// assign a global event handler to do all the work\n"
    "		element[\"on\" + type] = handleEvent;\n"
    "	}\n"
    "};\n"
    "// a counter used to create unique IDs\n"
    "dean_addEvent.guid = 1;\n"
    "\n"
    "function removeEvent(element, type, handler) {\n"
    "	if (element.removeEventListener) {\n"
    "		element.removeEventListener(type, handler, false);\n"
    "	} else {\n"
    "		// delete the event handler from the hash table\n"
    "		if (element.events && element.events[type]) {\n"
    "			delete element.events[type][handler.$$guid];\n"
    "		}\n"
    "	}\n"
    "};\n"
    "\n"
    "function handleEvent(event) {\n"
    "	var returnValue = true;\n"
    "	// grab the event object (IE uses a global event object)\n"
    "	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);\n"
    "	// get a reference to the hash table of event handlers\n"
    "	var handlers = this.events[event.type];\n"
    "	// execute each event handler\n"
    "	for (var i in handlers) {\n"
    "		this.$$handleEvent = handlers[i];\n"
    "		if (this.$$handleEvent(event) === false) {\n"
    "			returnValue = false;\n"
    "		}\n"
    "	}\n"
    "	return returnValue;\n"
    "};\n"
    "\n"
    "function fixEvent(event) {\n"
    "	// add W3C standard event methods\n"
    "	event.preventDefault = fixEvent.preventDefault;\n"
    "	event.stopPropagation = fixEvent.stopPropagation;\n"
    "	return event;\n"
    "};\n"
    "fixEvent.preventDefault = function() {\n"
    "	this.returnValue = false;\n"
    "};\n"
    "fixEvent.stopPropagation = function() {\n"
    "  this.cancelBubble = true;\n"
    "}\n"
    "\n"
    "// Dean's forEach: http://dean.edwards.name/base/forEach.js\n"
    "/*\n"
    "	forEach, version 1.0\n"
    "	Copyright 2006, Dean Edwards\n"
    "	License: http://www.opensource.org/licenses/mit-license.php\n"
    "*/\n"
    "\n"
    "// array-like enumeration\n"
    "if (!Array.forEach) { // mozilla already supports this\n"
    "	Array.forEach = function(array, block, context) {\n"
    "		for (var i = 0; i < array.length; i++) {\n"
    "			block.call(context, array[i], i, array);\n"
    "		}\n"
    "	};\n"
    "}\n"
    "\n"
    "// generic enumeration\n"
    "Function.prototype.forEach = function(object, block, context) {\n"
    "	for (var key in object) {\n"
    "		if (typeof this.prototype[key] == \"undefined\") {\n"
    "			block.call(context, object[key], key, object);\n"
    "		}\n"
    "	}\n"
    "};\n"
    "\n"
    "// character enumeration\n"
    "String.forEach = function(string, block, context) {\n"
    "	Array.forEach(string.split(\"\"), function(chr, index) {\n"
    "		block.call(context, chr, index, string);\n"
    "	});\n"
    "};\n"
    "\n"
    "// globally resolve forEach enumeration\n"
    "var forEach = function(object, block, context) {\n"
    "	if (object) {\n"
    "		var resolve = Object; // default\n"
    "		if (object instanceof Function) {\n"
    "			// functions have a \"length\" property\n"
    "			resolve = Function;\n"
    "		} else if (object.forEach instanceof Function) {\n"
    "			// the object implements a custom forEach method so use that\n"
    "			object.forEach(block, context);\n"
    "			return;\n"
    "		} else if (typeof object == \"string\") {\n"
    "			// the object is a string\n"
    "			resolve = String;\n"
    "		} else if (typeof object.length == \"number\") {\n"
    "			// the object is array-like\n"
    "			resolve = Array;\n"
    "		}\n"
    "		resolve.forEach(object, block, context);\n"
    "	}\n"
    "};\n"
};

#endif //_J_SCRIPT_H_
