// Copyright (C) 2013 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


// Looks at query parameters to decide which language handlers and style-sheets
// to load.

// Query Parameter     Format           Effect                        Default
// +------------------+---------------+------------------------------+--------+
// | autorun=         | true | false  | If true then prettyPrint()   | "true" |
// |                  |               | is called on page load.      |        |
// +------------------+---------------+------------------------------+--------+
// | lang=            | language name | Loads the language handler   | Can    |
// |                  |               | named "lang-<NAME>.js".      | appear |
// |                  |               | See available handlers at    | many   |
// |                  |               | http://code.google.com/p/    | times. |
// |                  |               | google-code-prettify/source/ |        |
// |                  |               | browse/trunk/src             |        |
// +------------------+---------------+------------------------------+--------+
// | skin=            | skin name     | Loads the skin stylesheet    | none.  |
// |                  |               | named "<NAME>.css".          |        |
// |                  |               | http://code.google.com/p/    |        |
// |                  |               | google-code-prettify/source/ |        |
// |                  |               | browse/trunk/styles          |        |
// +------------------+---------------+------------------------------+--------+
// | callback=        | JS identifier | When "prettyPrint" finishes  | none   |
// |                  |               | window.exports[js_ident] is  |        |
// |                  |               | called.                      |        |
// |                  |               | The callback must be under   |        |
// |                  |               | exports to reduce the risk   |        |
// |                  |               | of XSS via query parameter   |        |
// |                  |               | injection.                   |        |
// +------------------+---------------+------------------------------+--------+

// Exmaples
// .../prettify.js?lang=css&skin=sunburst
//   1. Loads the CSS language handler which can be used to prettify CSS
//      stylesheets, HTML <style> element bodies and style="..." attributes
//      values.
//   2. Loads the sunburst.css stylesheet instead of the default prettify.css
//      stylesheet.
//      A gallery of stylesheets is available at
//      https://google-code-prettify.googlecode.com/svn/trunk/styles/index.html
//   3. Since autorun=false is not specified, calls prettyPrint() on page load.

(function () {
  "use strict";

  var win = window;
  var setTimeout = win.setTimeout;
  var doc = document;
  var root = doc.documentElement;
  var head = doc['head'] || doc.getElementsByTagName("head")[0] || root;

  // From http://javascript.nwbox.com/ContentLoaded/contentloaded.js
  // Author: Diego Perini (diego.perini at gmail.com)
  // Summary: cross-browser wrapper for DOMContentLoaded
  // Updated: 20101020
  // License: MIT
  // Version: 1.2
  function contentLoaded(callback) {
    var addEventListener = doc['addEventListener'];
    var done = false, top = true,
        add = addEventListener ? 'addEventListener' : 'attachEvent',
        rem = addEventListener ? 'removeEventListener' : 'detachEvent',
        pre = addEventListener ? '' : 'on',

        init = function(e) {
          if (e.type == 'readystatechange' && doc.readyState != 'complete') {
            return;
          }
          (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
          if (!done && (done = true)) { callback.call(win, e.type || e); }
        },

        poll = function() {
          try {
            root.doScroll('left');
          } catch(e) {
            setTimeout(poll, 50);
            return;
          }
          init('poll');
        };

    if (doc.readyState == 'complete') {
      callback.call(win, 'lazy');
    } else {
      if (doc.createEventObject && root.doScroll) {
        try { top = !win.frameElement; } catch(e) { }
        if (top) { poll(); }
      }
      doc[add](pre + 'DOMContentLoaded', init, false);
      doc[add](pre + 'readystatechange', init, false);
      win[add](pre + 'load', init, false);
    }
  }

  // Given a list of URLs to stylesheets, loads the first that loads without
  // triggering an error event.
  function loadStylesheetsFallingBack(stylesheets) {
    var n = stylesheets.length;
    function load(i) {
      if (i === n) { return; }
      var link = doc.createElement('link');
      link.rel = 'stylesheet';
      link.type = 'text/css';
      if (i + 1 < n) {
        // http://pieisgood.org/test/script-link-events/ indicates that many
        // versions of IE do not support onerror on <link>s, though
        // http://msdn.microsoft.com/en-us/library/ie/ms535848(v=vs.85).aspx
        // indicates that recent IEs do support error.
        link.error = link.onerror = function () { load(i + 1); };
      }
      link.href = stylesheets[i];
      head.appendChild(link);
    }
    load(0);
  }

  var scriptQuery = '';
  // Look for the <script> node that loads this script to get its parameters.
  // This starts looking at the end instead of just considering the last
  // because deferred and async scripts run out of order.
  // If the script is loaded twice, then this will run in reverse order.
  for (var scripts = doc.scripts, i = scripts.length; --i >= 0;) {
    var script = scripts[i];
    var match = script.src.match(
        /^[^?#]*\/run_prettify\.js(\?[^#]*)?(?:#.*)?$/);
    if (match) {
      scriptQuery = match[1] || '';
      // Remove the script from the DOM so that multiple runs at least run
      // multiple times even if parameter sets are interpreted in reverse
      // order.
      script.parentNode.removeChild(script);
      break;
    }
  }

  // Pull parameters into local variables.
  var autorun = true;
  var langs = [];
  var skins = [];
  var callbacks = [];
  scriptQuery.replace(
      /[?&]([^&=]+)=([^&]+)/g,
      function (_, name, value) {
        value = decodeURIComponent(value);
        name = decodeURIComponent(name);
        if (name == 'autorun')   { autorun = !/^[0fn]/i.test(value); } else
        if (name == 'lang')      { langs.push(value);                } else
        if (name == 'skin')      { skins.push(value);                } else
        if (name == 'callback')  { callbacks.push(value);            }
      });

  // Use https to avoid mixed content warnings in client pages and to
  // prevent a MITM from rewrite prettify mid-flight.
  // This only works if this script is loaded via https : something
  // over which we exercise no control.
  var LOADER_BASE_URL =
     'https://google-code-prettify.googlecode.com/svn/loader';

  for (var i = 0, n = langs.length; i < n; ++i) (function (lang) {
    var script = doc.createElement("script");

    // Excerpted from jQuery.ajaxTransport("script") to fire events when
    // a script is finished loading.
    // Attach handlers for each script
    script.onload = script.onerror = script.onreadystatechange = function () {
      if (script && (
            !script.readyState || /loaded|complete/.test(script.readyState))) {
        // Handle memory leak in IE
        script.onerror = script.onload = script.onreadystatechange = null;

        --pendingLanguages;
        checkPendingLanguages();

        // Remove the script
        if (script.parentNode) {
          script.parentNode.removeChild(script);
        }

        script = null;
      }
    };

    script.type = 'text/javascript';
    script.src = LOADER_BASE_URL
      + '/lang-' + encodeURIComponent(langs[i]) + '.js';

    // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
    head.insertBefore(script, head.firstChild);
  })(langs[i]);

  var pendingLanguages = langs.length;
  function checkPendingLanguages() {
    if (!pendingLanguages) {
      setTimeout(onLangsLoaded, 0);
    }
  }

  var skinUrls = [];
  for (var i = 0, n = skins.length; i < n; ++i) {
    skinUrls.push(LOADER_BASE_URL
        + '/skins/' + encodeURIComponent(skins[i]) + '.css');
  }
  skinUrls.push(LOADER_BASE_URL + '/prettify.css');
  loadStylesheetsFallingBack(skinUrls);

  var prettyPrint = (function () {
    include("prettify.js");
    return prettyPrint;
  })();

  // If this script is deferred or async and the document is already
  // loaded we need to wait for language handlers to load before performing
  // any autorun.
  function onLangsLoaded() {
    if (autorun) {
      contentLoaded(
        function () {
          var n = callbacks.length;
          var callback = n ? function () {
            for (var i = 0; i < n; ++i) {
              (function (i) {
                 setTimeout(
                   function () {
                     win['exports'][callbacks[i]].apply(win, arguments);
                   }, 0);
               })(i);
            }
          } : void 0;
          prettyPrint(callback);
        });
    }
  }
  checkPendingLanguages();

}());
