/*
  Created:  2009-09-20
  Issued:   2009-09-20
  Modified: 2009-09-20

  Copyright (c) 2009 by Philip Shaw, phil@codestyle.org

  This work is licensed under the Creative Commons
  Attribution-NonCommercial-ShareAlike License.
  To view a copy of this license, visit
  http://creativecommons.org/licenses/by-nc-sa/2.0/
  or send a letter to Creative Commons, 559 Nathan
  Abbott Way, Stanford, California 94305, USA.
*/

/**
 *  Append a function to the onload handler.
 *  @param  functionName  The name of the function to append.
 */
function addLoadListener(functionName) {

  if (typeof window.addEventListener != 'undefined') {

    window.addEventListener('load', functionName, false);
  }
  else {

    if (typeof document.addEventListener != 'undefined') {

      document.addEventListener('load', functionName, false);
    }
    else {

      if (typeof window.attachEvent != 'undefined') {

        window.attachEvent('onload', functionName);
      }
      else {

        var oldFunctionName = window.onload;

        if (typeof window.onload != 'function') {

          window.onload = functionName;
        }
        else {

          window.onload = function() {

            oldFunctionName();
            functionName();
          }
        }
      }
    }
  }
}
/*
  Created:  2009-07-31
  Issued:   2009-07-31
  Modified: 2009-09-06

  Copyright (c) 2009 by Philip Shaw, phil@codestyle.org

  This work is licensed under the Creative Commons
  Attribution-NonCommercial-ShareAlike License.
  To view a copy of this license, visit
  http://creativecommons.org/licenses/by-nc-sa/2.0/
  or send a letter to Creative Commons, 559 Nathan
  Abbott Way, Stanford, California 94305, USA.
*/

/**
 *  The browser extension prefixes to check.
 *    Moz     = Firefox
 *    Webkit  = Safari, Chrome
 *    O       = Opera
 *    Khtml   = Konqueror (no operable styles confirmed)
 */
var PREFIXES = new Array('Moz', 'Webkit', 'O', 'Khtml');

/**
 *  A constant to mark and check un-supported extension styles.
 */
var NOT_SUPPORTED = 'not supported';

/**
 *  The CSS3 box shadow DOM style property name.
 */
var BOX_SHADOW = 'BoxShadow';

/**
 *  The CSS3 border radius DOM style property name.
 */
var BORDER_RADIUS = 'BorderRadius';

/**
 *  The CSS3 background size DOM style property name.
 */
var BACKGROUND_SIZE = 'BackgroundSize';

/**
 *  The CSS3 opacity DOM style property name.
 */
var OPACITY = 'Opacity';

/**
 *  The CSS3 text overflow DOM style property name.
 */
var TEXT_OVERFLOW = 'TextOverflow';

/**
 *  The user agent extension style property name store.
 */
var AGENT_STYLE = new Array();

/**
 *  The page elements store for quick look-up.
 */
var PAGE_ELEMENTS = new Array();

/**
 *  The page ID elements store for quick look-up.
 */
var PAGE_ID_ELEMENTS = new Array();

/**
 *  The page class elements store for quick look-up.
 */
var PAGE_CLASS_ELEMENTS = new Array();

/**
 *  Apply specified styles to the document (after it has loaded).
 */
function setExtensionStyles() {

  // Performance testing
  // var start = new Date();

  // IE test
  // setTextOverflowById('Footbar',          'ellipsis');

  // Opera test
  // setBackgroundSizeById('Footbar',        '50% auto');

  // Konqueror test (failed)
  // setTextOverflowById('Footbar',          'ellipsis');

  //setBorderRadiusById('Footbar',        '5px');

  setBorderRadiusByClass('MenuBox',     '5px');
  //setBoxShadowByClass('MenuBox',        '0px 1px 3px #DDD');

  setBorderRadiusByClass('FAQAction',   '5px');
  //setBoxShadowByClass('FAQAction',      '0px 1px 3px #DDD');

  setBorderRadiusByClass('FAQSearchBox','5px');
  //setBoxShadowByClass('FAQSearchBox',   '0px 1px 3px #DDD');

  //setBoxShadowById('Footbar',           '0px 2px 4px #999');

  setBorderRadiusByClass('Information', '5px');
  //setBoxShadowByClass('Information',    '0px 1px 3px #DDD');

  setBorderRadiusByClass('LogAction',   '5px');
  //setBoxShadowByClass('LogAction',      '0px 1px 3px #DDD');

  setBorderRadiusByClass('NavBar',      '5px');
  //setBoxShadowByClass('NavBar',         '0px 1px 3px #DDD');

  setBorderRadiusByClass('RgtBox',      '5px');
  //setBoxShadowByClass('RgtBox',         '0px 1px 3px #DDD');

  //setBoxShadowByClass('ScrollMenuBox',  '0px 1px 3px #DDD');

  //setBoxShadowById('SiteSearch',        '0px 1px 2px #999');

  setBoxShadowByClass('Thumbnail',      '0px 1px 3px #DDD');

  setBorderRadiusByClass('TOC',         '0px 10px 10px 0px');
  //setBoxShadowByClass('TOC',            '0px 1px 3px #DDD');

  // Performance testing
  // var stop = new Date();
  //alert('Styling completed in ' + (stop.getTime() - start.getTime()) + ' milliseconds');
}

/**
 *  Generic method to set an extension style by ID name.
 *  @param  idName    The ID name of the element to select.
 *  @param  styleName The standard DOM style property name.
 *  @param  value     The property value to apply (no semi-colon).
 */
function setExtensionStyleById(idName, styleName, value) {

  if (typeof document.getElementById != 'undefined') {

    if (typeof PAGE_ID_ELEMENTS[idName] == 'undefined') {

      PAGE_ID_ELEMENTS[idName] = document.getElementById(idName);
    }

    setExtensionStyle(PAGE_ID_ELEMENTS[idName], styleName, value);
  }
}

/**
 *  Generic method to set an extension style by class name.
 *  @param  className The class name of the elements to select.
 *  @param  styleName The standard DOM style property name.
 *  @param  value     The property value to apply (no semi-colon).
 */
function setExtensionStyleByClass(className, styleName, value) {

  if (typeof document.getElementsByClassName != 'undefined') {

    if (typeof PAGE_CLASS_ELEMENTS[className] == 'undefined') {

      PAGE_CLASS_ELEMENTS[className] = document.getElementsByClassName(className);
    }

    for (var i = 0; i < PAGE_CLASS_ELEMENTS[className].length; i++) {

      setExtensionStyle(PAGE_CLASS_ELEMENTS[className][i], styleName, value);
    }
  }
}

/**
 *  Generic method to set an extension style by element name.
 *  @param  elementName The name of the elements to select.
 *  @param  styleName   The standard DOM style property name.
 *  @param  value       The property value to apply (no semi-colon).
 */
function setExtensionStyleByTagName(elementName, styleName, value) {

  if (typeof document.getElementsByTagName != 'undefined') {

    if (typeof PAGE_ELEMENTS[elementName] == 'undefined') {

      PAGE_ELEMENTS[elementName] = document.getElementsByTagName(elementName);
    }

    for (var i = 0; i < PAGE_ELEMENTS[elementName].length; i++) {

      setExtensionStyle(PAGE_ELEMENTS[elementName][i], styleName, value);
    }
  }
}

/**
 *  Convert a standard DOM style property name to Microsoft format.
 *  @param  styleName The style name to convert.
 *  @return The style name with lower case initial.
 */
function toLowerCaseInitial(styleName) {

  var initial = styleName.substring(0, 1);

  return initial.toLowerCase() + styleName.substring(1, styleName.length);
}

/**
 *  Get the extension style property name for the user agent.
 *  @param  element   A reference element to check for style properties.
 *  @param  styleName The standard style name for the property.
 *  @return The appropriate extension style property name for the user agent,
 *          or the NOT_SUPPORTED constant if not matched.
 */
function getExtensionStyleName(element, styleName) {

  if (typeof AGENT_STYLE[styleName] == 'undefined') {

    for (var i = 0; i < PREFIXES.length; i++) {

      if (typeof element.style[PREFIXES[i] + styleName] != 'undefined') {

        AGENT_STYLE[styleName] = PREFIXES[i] + styleName;

        return AGENT_STYLE[styleName];
      }
    }

    var msStyle = toLowerCaseInitial(styleName);

    if (typeof element.style[msStyle] != 'undefined') {

      AGENT_STYLE[styleName] = msStyle;

      return AGENT_STYLE[styleName];
    }

    AGENT_STYLE[styleName] = NOT_SUPPORTED;

    return AGENT_STYLE[styleName];
  }
  else {

    return AGENT_STYLE[styleName];
  }
}

/**
 *  Core method to set an extension style on the given DOM element.
 *  @param  element   The element to which the style should be applied.
 *  @param  styleName The standard DOM style property name.
 *  @param  value     The style value to apply (no semi-colon).
 */
function setExtensionStyle(element, styleName, value) {

  if ((element != null) &&
      (typeof element != 'undefined') &&
      (typeof element.style != 'undefined')) {

    if (typeof element.style[styleName] != 'undefined') {

      // CSS3 property name matched
      // Should be declared in a standard cascading style sheet
      return;
    }
    else {

      var extensionStyleName = getExtensionStyleName(element, styleName);

      if (extensionStyleName != NOT_SUPPORTED) {

        element.style[extensionStyleName] = value;

        return;
      }
    }
  }

  // Fall through
  return;
}

/**
 *  Shorthand method to set CSS text overflow style by ID name.
 *  @param  idName  The ID name of the element to select.
 *  @param  value   The text overflow property value to apply (no semi-colon).
 */
function setTextOverflowById(idName, value) {

  setExtensionStyleById(idName, TEXT_OVERFLOW, value);
}

/**
 *  Shorthand method to set CSS text overflow style by class name.
 *  @param  className  The class name of the elements to select.
 *  @param  value   The text overflow value to apply (no semi-colon).
 */
function setTextOverflowByClass(className, value) {

  setExtensionStyleByClass(className, TEXT_OVERFLOW, value);
}

/**
 *  Shorthand method to set CSS text overflow style by element name.
 *  @param  elementName The class name of the elements to select.
 *  @param  value       The text overflow property value to apply (no semi-colon).
 */
function setTextOverflowByTagName(elementName, value) {

  setExtensionStyleByTagName(elementName, TEXT_OVERFLOW, value);
}

/**
 *  Shorthand method to set CSS background size style by ID name.
 *  @param  idName  The ID name of the element to select.
 *  @param  value   The background size property value to apply (no semi-colon).
 */
function setBackgroundSizeById(idName, value) {

  setExtensionStyleById(idName, BACKGROUND_SIZE, value);
}

/**
 *  Shorthand method to set CSS background size style by class name.
 *  @param  className  The class name of the elements to select.
 *  @param  value   The background size value to apply (no semi-colon).
 */
function setBackgroundSizeByClass(className, value) {

  setExtensionStyleByClass(className, BACKGROUND_SIZE, value);
}

/**
 *  Shorthand method to set CSS background size style by element name.
 *  @param  elementName The class name of the elements to select.
 *  @param  value       The background size property value to apply (no semi-colon).
 */
function setBackgroundSizeByTagName(elementName, value) {

  setExtensionStyleByTagName(elementName, BACKGROUND_SIZE, value);
}

/**
 *  Shorthand method to set CSS box shadow style by ID name.
 *  @param  idName  The ID name of the element to select.
 *  @param  value   The box shadow property value to apply (no semi-colon).
 */
function setBoxShadowById(idName, value) {

  setExtensionStyleById(idName, BOX_SHADOW, value);
}

/**
 *  Shorthand method to set CSS box shadow style by class name.
 *  @param  className  The class name of the elements to select.
 *  @param  value   The box property value to apply (no semi-colon).
 */
function setBoxShadowByClass(className, value) {

  setExtensionStyleByClass(className, BOX_SHADOW, value);
}

/**
 *  Shorthand method to set CSS box shadow style by element name.
 *  @param  elementName The class name of the elements to select.
 *  @param  value       The box shadow property value to apply (no semi-colon).
 */
function setBoxShadowByTagName(elementName, value) {

  setExtensionStyleByTagName(elementName, BOX_SHADOW, value);
}

/**
 *  Shorthand method to set CSS border radius style by ID name.
 *  @param  idName  The ID name of the element to select.
 *  @param  value   The border radius style to apply (no semi-colon).
 */
function setBorderRadiusById(idName, value) {

  setExtensionStyleById(idName, BORDER_RADIUS, value);
}

/**
 *  Shorthand method to set CSS border radius style by class name.
 *  @param  className The class name of the elements to select.
 *  @param  value     The border radius style to apply (no semi-colon).
 */
function setBorderRadiusByClass(className, value) {

  setExtensionStyleByClass(className, BORDER_RADIUS, value);
}

/**
 *  Shorthand method to set CSS border radius style by element name.
 *  @param  elementName The class name of the elements to select.
 *  @param  value       The border radius property value to apply (no semi-colon).
 */
function setBorderRadiusByTagName(elementName, value) {

  setExtensionStyleByTagName(elementName, BORDER_RADIUS, value);
}

/**
 *  Append the extension styles handler to the onload event.
 *  Requires /scripts/CommonFunctions.js
 */
addLoadListener(setExtensionStyles);
/*
  Created:  2009-09-20
  Issued:   2009-09-20
  Modified: 2009-11-16

  Copyright (c) 2009 by Philip Shaw, phil@codestyle.org

  This work is licensed under the Creative Commons
  Attribution-NonCommercial-ShareAlike License.
  To view a copy of this license, visit
  http://creativecommons.org/licenses/by-nc-sa/2.0/
  or send a letter to Creative Commons, 559 Nathan
  Abbott Way, Stanford, California 94305, USA.
*/

/**
 *  The query prefix for the FAQ search form.
 */
var PREFIX_FAQ = 'intitle:FAQ';

/**
 *  The query prefix for the site log search form.
 */
var PREFIX_LOG = 'intitle:"site log"';

/**
 *  The search engine referrers to check.
 */
var ENGINES = [// Domain      Query   Common name
                ['google.',   'q',    'Google'],
                ['yahoo.',    'p',    'Yahoo'],
                ['bing.',     'q',    'Bing'],
                ['search.',   'q',    'Search.com'],
                ['ask.',      'q',    'Ask'],
                ['aol.',      'q',    'AOL'],
                ['altavista.','q',    'AltaVista'],
                ['csdev.',    'q',    'Code Style test domain']
              ];

/**
 *  Get the referring search query.
 *  @return The decoded and sanitised entrance keyword query terms, or null
 *          if any necessary conditions fail.
 */
function getReferringQuery() {

  if (document.referrer != '') {

    var queryParam = getQueryParameter(document.referrer);

    if (queryParam != null) {

      var querySplit = document.referrer.split('?');

      if ((querySplit.length > 1) &&
          (querySplit[1] != '')) {

        var paramSplit = querySplit[1].split('&');

        for (var i = 0; i < paramSplit.length; i++) {

          if (paramSplit[i] != '') {

            var valueSplit = paramSplit[i].split('=');

            if ((valueSplit.length > 1) &&
                (valueSplit[1] != '')) {

              if (valueSplit[0] == queryParam) {

                return urlDecode(valueSplit[1]);
              }
            }
          }
        }
      }
    }
  }

  // Fall through
  return null;
}

/**
 *  Get the query parameter name for a given URL.
 *  @param  url The URL from which to identify the query parameter name.
 *  @return The query parameter name for the given URL or null if not matched.
 */
function getQueryParameter(url) {

  var domain = getDomain(url);

  for (var i = 0; i < ENGINES.length; i++) {

    var engine = ENGINES[i][0].toLowerCase();

    if (domain.indexOf(engine) > -1) {

      return ENGINES[i][1];
    }
  }

  // Fall through
  return null;
}

/**
 *  Get the domain part of a given URL.
 *  @param  url The URL from which to extract the domain name.
 *  @return The domain part of the given URL in lower case.
 */
function getDomain(url) {

  var schemeSlashIndex = url.indexOf('//');

  if (schemeSlashIndex > -1) {

    var schemePart = url.substring(schemeSlashIndex + 2, url.length);

    var pathSlashIndex = schemePart.indexOf('/');

    if (pathSlashIndex > -1) {

      return schemePart.substring(0, pathSlashIndex);
    }
    else {

      return schemePart;
    }
  }

  return domain.toLowerCase();
}

/**
 *  URL decode and sanitise a given parameter input value.
 *  @param  input The parameter input value to process.
 *  @return The URL decoded input value with original spaces and plus symbols
 *          retained. Value is passed through cleanMarkup() function to strip
 *          out potentially malicious markup.
 */
function urlDecode(input) {

  var decodeChunk = null;

  var cleanChunk = null;

  var keywordSplit = null;

  var keywordJoin = '';

  var first = true;

  // Mark real plus symbols to restore below.
  var plusSplit = input.split('%2B');

  for (var i = 0; i < plusSplit.length; i++) {

    if (first) {

      first = false;
    }
    else {

      // Restore real plus symbols
      keywordJoin += '+'
    }

    // Un-escape input, spaces remain as + symbols
    decodeChunk = unescape(plusSplit[i]);

    // Convert any markup to entities
    cleanChunk = cleanMarkup(decodeChunk);

    // Split on spaces encoded as plus symbols
    keywordSplit = cleanChunk.split('+');

    // Rejoin with real spaces
    keywordJoin += keywordSplit.join(' ');
  }

  return keywordJoin;
}

/**
 *  Strip potentially malicious markup from a given parameter input value.
 *  @param  input The parameter input value to process.
 *  @return The input value with <, > and & converted to HTML entities.
 */
function cleanMarkup(input) {

  var output = '';

  for (var i = 0; i < input.length; i++) {

    if (input.charAt(i) == '<') {

      output += '&lt;';
      continue;
    }
    if (input.charAt(i) == '>') {

      output += '&gt;';
      continue;
    }
    if (input.charAt(i) == '&') {

      output += '&amp;';
      continue;
    }

    output += input.charAt(i);
  }

  return output;
}

/**
 *  Insert the entrance keywords in the site search forms.
 */
function setEntranceKeywords() {

  var referringQuery = getReferringQuery();

  if (referringQuery != null) {

    if (typeof document.getElementById != 'undefined') {

      var siteSearch = document.getElementById('search-input');
      var faqSearch = document.getElementById('faq-search-input');
      var logSearch = document.getElementById('log-search-input');

      var prefix = -1;

      if (siteSearch != null) {

        siteSearch.value = referringQuery;
      }
      if (faqSearch != null) {

        prefix = referringQuery.indexOf(PREFIX_FAQ);

        if (prefix > -1) {

          faqSearch.value = referringQuery;
        }
        else {

          faqSearch.value = PREFIX_FAQ + ' ' + referringQuery;
        }
      }
      if (logSearch != null) {

        prefix = referringQuery.indexOf(PREFIX_LOG);

        if (prefix > -1) {

          logSearch.value = referringQuery;
        }
        else {

          logSearch.value = PREFIX_LOG + ' ' + referringQuery;
        }
      }
    }
  }
}

/**
 *  Append the extension styles handler to the onload event.
 *  Requires /scripts/CommonFunctions.js
 */
addLoadListener(setEntranceKeywords);
