text: /*global require,Blob,JSHINT*/
  /*global gallery_demos*/// defined by gallery/gallery-index.js, created by build
  /*global sandcastleJsHintOptions*/// defined by jsHintOptions.js, created by build
  require({
      baseUrl : '../../Source',
      packages : [{
          name : 'dojo',
          location : '../ThirdParty/dojo-release-1.10.4/dojo'
      }, {
          name : 'dijit',
          location : '../ThirdParty/dojo-release-1.10.4/dijit'
      }, {
          name : 'Sandcastle',
          location : '../Apps/Sandcastle'
      }, {
          name : 'Source',
          location : '.'
      }, {
          name : 'CodeMirror',
          location : '../ThirdParty/codemirror-4.6'
      }]
  }, [
          'CodeMirror/lib/codemirror',
          'dijit/layout/ContentPane',
          'dijit/popup',
          'dijit/registry',
          'dijit/TooltipDialog',
          'dojo/_base/fx',
          'dojo/_base/xhr',
          'dojo/dom',
          'dojo/dom-class',
          'dojo/dom-construct',
          'dojo/io-query',
          'dojo/mouse',
          'dojo/on',
          'dojo/parser',
          'dojo/promise/all',
          'dojo/when',
          'dojo/query',
          'Sandcastle/LinkButton',
          'Source/Cesium',
          'CodeMirror/addon/hint/show-hint',
          'CodeMirror/addon/hint/javascript-hint',
          'CodeMirror/mode/javascript/javascript',
          'CodeMirror/mode/css/css',
          'CodeMirror/mode/xml/xml',
          'CodeMirror/mode/htmlmixed/htmlmixed',
          'dijit/form/Button',
          'dijit/form/DropDownButton',
          'dijit/form/ToggleButton',
          'dijit/form/DropDownButton',
          'dijit/form/TextBox',
          'dijit/form/Textarea',
          'dijit/Menu',
          'dijit/MenuBar',
          'dijit/PopupMenuBarItem',
          'dijit/MenuItem',
          'dijit/layout/BorderContainer',
          'dijit/layout/TabContainer',
          'dijit/Toolbar',
          'dijit/ToolbarSeparator',
          'dojo/domReady!'
      ], function(
          CodeMirror,
          ContentPane,
          popup,
          registry,
          TooltipDialog,
          fx,
          xhr,
          dom,
          domClass,
          domConstruct,
          ioQuery,
          mouse,
          on,
          parser,
          all,
          when,
          query,
          LinkButton,
          Cesium) {
      "use strict";
  
      //In order for CodeMirror auto-complete to work, Cesium needs to be defined as a global.
      window.Cesium = Cesium;
  
      function defined(value) {
          return value !== undefined;
      }
  
      parser.parse();
  
      fx.fadeOut({
          node : 'loading',
          onEnd : function() {
              domConstruct.destroy('loading');
          }
      }).play();
  
      var numberOfNewConsoleMessages = 0;
  
      var logOutput = document.getElementById('logOutput');
      function appendConsole(className, message, showConsole) {
          var ele = document.createElement('span');
          ele.className = className;
          ele.textContent = message + '\n';
          logOutput.appendChild(ele);
          logOutput.parentNode.scrollTop = logOutput.clientHeight + 8 - logOutput.parentNode.clientHeight;
          if (showConsole) {
              hideGallery();
          } else {
              ++numberOfNewConsoleMessages;
              registry.byId('logContainer').set('title', 'Console (' + numberOfNewConsoleMessages + ')');
          }
      }
  
      var URL = window.URL || window.webkitURL;
  
      function findCssStyle(selectorText) {
          for (var iSheets = 0, lenSheets = document.styleSheets.length; iSheets < lenSheets; ++iSheets) {
              var rules = document.styleSheets[iSheets].cssRules;
              for (var iRules = 0, lenRules = rules.length; iRules < lenRules; ++iRules) {
                  if (rules[iRules].selectorText === selectorText) {
                      return rules[iRules];
                  }
              }
          }
      }
  
      var jsEditor;
      var htmlEditor;
      var suggestButton = registry.byId('buttonSuggest');
      var docTimer;
      var docTabs = {};
      var subtabs = {};
      var docError = false;
      var galleryError = false;
      var galleryTooltipTimer;
      var activeGalleryTooltipDemo;
      var demoTileHeightRule = findCssStyle('.demoTileThumbnail');
      var cesiumContainer = registry.byId('cesiumContainer');
      var docNode = dom.byId('docPopup');
      var docMessage = dom.byId('docPopupMessage');
      var local = {
          'docTypes' : [],
          'headers' : '<html><head></head><body>',
          'bucketName' : '',
          'emptyBucket' : ''
      };
      var bucketTypes = {};
      var demoTooltips = {};
      var errorLines = [];
      var highlightLines = [];
      var searchTerm = '';
      var searchRegExp;
      var hintTimer;
      var currentTab = '';
      var newDemo;
      var demoHtml = '';
      var demoJs = '';
  
      var galleryErrorMsg = document.createElement('span');
      galleryErrorMsg.className = 'galleryError';
      galleryErrorMsg.style.display = 'none';
      galleryErrorMsg.textContent = 'No demos match your search terms.';
  
      var bucketFrame = document.getElementById('bucketFrame');
      var bucketPane = registry.byId('bucketPane');
      var bucketWaiting = false;
  
      xhr.get({
          url : '../../Build/Documentation/types.txt',
          handleAs : 'json',
          error : function(error) {
              docError = true;
          }
      }).then(function(value) {
          local.docTypes = value;
      });
  
      var decoderSpan = document.createElement('span');
      function encodeHTML(text) {
          decoderSpan.textContent = text;
          text = decoderSpan.innerHTML;
          decoderSpan.innerHTML = '';
          return text;
      }
      function decodeHTML(text) {
          decoderSpan.innerHTML = text;
          text = decoderSpan.textContent;
          decoderSpan.innerHTML = '';
          return text;
      }
  
      function highlightRun() {
          domClass.add(registry.byId('buttonRun').domNode, 'highlightToolbarButton');
      }
  
      function clearRun() {
          domClass.remove(registry.byId('buttonRun').domNode, 'highlightToolbarButton');
      }
  
      function openDocTab(title, link) {
          if (!defined(docTabs[title])) {
              docTabs[title] = new ContentPane({
                  title : title,
                  focused : true,
                  content : '<iframe class="fullFrame" src="' + link + '"></iframe>',
                  closable : true,
                  onClose : function() {
                      docTabs[this.title] = undefined;
                      // Return true to close the tab.
                      return true;
                  }
              }).placeAt(cesiumContainer);
              // After the iframe loads, re-scroll to selected field.
              docTabs[title].domNode.childNodes[0].onload = function() {
                  this.onload = function() {
                  };
                  this.src = link;
              };
              cesiumContainer.selectChild(docTabs[title]);
          } else {
              // Tab already exists, but maybe not visible.  Firefox needs the tab to
              // be revealed before a re-scroll can happen.  Chrome works either way.
              cesiumContainer.selectChild(docTabs[title]);
              docTabs[title].domNode.childNodes[0].src = link;
          }
      }
  
      function showDocPopup() {
          var selectedText = jsEditor.getSelection();
          var lowerText = selectedText.toLowerCase();
  
          var onDocClick = function() {
              openDocTab(this.textContent, this.href);
              return false;
          };
  
          docTimer = undefined;
          if (docError && selectedText && selectedText.length < 50) {
              hideGallery();
          } else if (lowerText && lowerText in local.docTypes && typeof local.docTypes[lowerText].push === 'function') {
              docMessage.innerHTML = '';
              for (var i = 0, len = local.docTypes[lowerText].length; i < len; ++i) {
                  var member = local.docTypes[lowerText][i];
                  var ele = document.createElement('a');
                  ele.target = '_blank';
                  ele.textContent = member.replace('.html', '').replace('module-', '').replace('#.', '.').replace('#', '.');
                  ele.href = '../../Build/Documentation/' + member;
                  ele.onclick = onDocClick;
                  docMessage.appendChild(ele);
              }
              jsEditor.addWidget(jsEditor.getCursor(true), docNode);
              docNode.style.top = (parseInt(docNode.style.top, 10) - 5) + 'px';
          }
      }
  
      function onCursorActivity() {
          docNode.style.left = '-999px';
          if (defined(docTimer)) {
              window.clearTimeout(docTimer);
          }
          docTimer = window.setTimeout(showDocPopup, 500);
      }
  
      function makeLineLabel(msg, className) {
          var element = document.createElement('abbr');
          element.className = className;
          switch (className) {
          case 'hintMarker':
              element.innerHTML = '&#9650;';
              break;
          case 'errorMarker':
              element.innerHTML = '&times;';
              break;
          default:
              element.innerHTML = '&#9654;';
          }
          element.title = msg;
          return element;
      }
  
      function closeGalleryTooltip() {
          if (defined(activeGalleryTooltipDemo)) {
              popup.close(demoTooltips[activeGalleryTooltipDemo.name]);
              activeGalleryTooltipDemo = undefined;
          }
      }
  
      function openGalleryTooltip() {
          galleryTooltipTimer = undefined;
  
          var selectedTabName = registry.byId('innerPanel').selectedChildWidget.title;
          var suffix = selectedTabName + 'Demos';
          if (selectedTabName === 'All') {
              suffix = '';
          } else if (selectedTabName === 'Search Results') {
              suffix = 'searchDemo';
          }
  
          if (defined(activeGalleryTooltipDemo)) {
              popup.open({
                  popup : demoTooltips[activeGalleryTooltipDemo.name],
                  around : dom.byId(activeGalleryTooltipDemo.name + suffix),
                  orient : ['above', 'below']
              });
          }
      }
  
      function scheduleGalleryTooltip(demo) {
          if (demo !== activeGalleryTooltipDemo) {
              activeGalleryTooltipDemo = demo;
              if (defined(galleryTooltipTimer)) {
                  window.clearTimeout(galleryTooltipTimer);
              }
              galleryTooltipTimer = window.setTimeout(openGalleryTooltip, 220);
          }
      }
  
      function scriptLineToEditorLine(line) {
          // editor lines are zero-indexed, plus 3 lines of boilerplate
          return line - 4;
      }
  
      function clearErrorsAddHints() {
          var line;
          var i;
          var len;
          hintTimer = undefined;
          closeGalleryTooltip();
          jsEditor.clearGutter('hintGutter');
          jsEditor.clearGutter('highlightGutter');
          jsEditor.clearGutter('errorGutter');
          jsEditor.clearGutter('searchGutter');
          while (errorLines.length > 0) {
              line = errorLines.pop();
              jsEditor.removeLineClass(line, 'text');
          }
          while (highlightLines.length > 0) {
              line = highlightLines.pop();
              jsEditor.removeLineClass(line, 'text');
          }
          var code = jsEditor.getValue();
          if (searchTerm !== '') {
              var codeLines = code.split('\n');
              for (i = 0, len = codeLines.length; i < len; ++i) {
                  if (searchRegExp.test(codeLines[i])) {
                      line = jsEditor.setGutterMarker(i, 'searchGutter', makeLineLabel('Search: ' + searchTerm, 'searchMarker'));
                      jsEditor.addLineClass(line, 'text', 'searchLine');
                      errorLines.push(line);
                  }
              }
          }
          // make a copy of the options, JSHint modifies the object it's given
          var options = JSON.parse(JSON.stringify(sandcastleJsHintOptions));
          if (!JSHINT(getScriptFromEditor(false), options)) {
              var hints = JSHINT.errors;
              for (i = 0, len = hints.length; i < len; ++i) {
                  var hint = hints[i];
                  if (hint !== null && defined(hint.reason) && hint.line > 0) {
                      line = jsEditor.setGutterMarker(scriptLineToEditorLine(hint.line), 'hintGutter', makeLineLabel(hint.reason, 'hintMarker'));
                      jsEditor.addLineClass(line, 'text', 'hintLine');
                      errorLines.push(line);
                  }
              }
          }
      }
  
      function scheduleHint() {
          if (defined(hintTimer)) {
              window.clearTimeout(hintTimer);
          }
          hintTimer = setTimeout(clearErrorsAddHints, 550);
          highlightRun();
      }
  
      function scheduleHintNoChange() {
          if (defined(hintTimer)) {
              window.clearTimeout(hintTimer);
          }
          hintTimer = setTimeout(clearErrorsAddHints, 550);
      }
  
      function scrollToLine(lineNumber) {
          if (defined(lineNumber)) {
              jsEditor.setCursor(lineNumber);
              // set selection twice in order to force the editor to scroll
              // to this location if the cursor is already there
              jsEditor.setSelection({
                  line : lineNumber - 1,
                  ch : 0
              }, {
                  line : lineNumber - 1,
                  ch : 0
              });
              jsEditor.focus();
              jsEditor.setSelection({
                  line : lineNumber,
                  ch : 0
              }, {
                  line : lineNumber,
                  ch : 0
              });
          }
      }
  
      function highlightLine(lineNum) {
          var line;
          jsEditor.clearGutter('highlightGutter');
          while (highlightLines.length > 0) {
              line = highlightLines.pop();
              jsEditor.removeLineClass(line, 'text');
          }
          if (lineNum > 0) {
              lineNum = scriptLineToEditorLine(lineNum);
              line = jsEditor.setGutterMarker(lineNum, 'highlightGutter', makeLineLabel('highlighted by demo', 'highlightMarker'));
              jsEditor.addLineClass(line, 'text', 'highlightLine');
              highlightLines.push(line);
              scrollToLine(lineNum);
          }
      }
  
      var tabs = registry.byId('bottomPanel');
  
      function showGallery() {
          tabs.selectChild(registry.byId('innerPanel'));
      }
  
      function hideGallery() {
          closeGalleryTooltip();
          tabs.selectChild(registry.byId('logContainer'));
      }
  
      tabs.watch('selectedChildWidget', function(name, oldValue, newValue) {
          if (newValue === registry.byId('logContainer')) {
              numberOfNewConsoleMessages = 0;
              registry.byId('logContainer').set('title', 'Console');
          }
      });
  
      function registerScroll(demoContainer) {
          if (defined(document.onmousewheel)) {
              demoContainer.addEventListener('mousewheel', function(e) {
                  if (defined(e.wheelDelta) && e.wheelDelta) {
                      demoContainer.scrollLeft -= e.wheelDelta * 70 / 120;
                  }
              }, false);
          } else {
              demoContainer.addEventListener('DOMMouseScroll', function(e) {
                  if (defined(e.detail) && e.detail) {
                      demoContainer.scrollLeft += e.detail * 70 / 3;
                  }
              }, false);
          }
      }
  
      CodeMirror.commands.runCesium = function(cm) {
          clearErrorsAddHints();
          clearRun();
          cesiumContainer.selectChild(bucketPane);
          // Check for a race condition in some browsers where the iframe hasn't loaded yet.
          if (bucketFrame.contentWindow.location.href.indexOf('bucket.html') > 0) {
              bucketFrame.contentWindow.location.reload();
          }
      };
  
      jsEditor = CodeMirror.fromTextArea(document.getElementById('code'), {
          mode : 'javascript',
          gutters : ['hintGutter', 'errorGutter', 'searchGutter', 'highlightGutter'],
          lineNumbers : true,
          matchBrackets : true,
          indentUnit : 4,
          extraKeys : {
              'Ctrl-Space' : 'autocomplete',
              'F8' : 'runCesium',
              'Tab' : 'indentMore',
              'Shift-Tab' : 'indentLess'
          }
      });
  
      jsEditor.on('cursorActivity', onCursorActivity);
      jsEditor.on('change', scheduleHint);
  
      htmlEditor = CodeMirror.fromTextArea(document.getElementById('htmlBody'), {
          mode : 'text/html',
          lineNumbers : true,
          matchBrackets : true,
          indentUnit : 4,
          extraKeys : {
              'F8' : 'runCesium',
              'Tab' : 'indentMore',
              'Shift-Tab' : 'indentLess'
          }
      });
  
      window.onbeforeunload = function (e) {
          var htmlText = (htmlEditor.getValue()).replace(/\s/g, '');
          var jsText = (jsEditor.getValue()).replace(/\s/g, '');
          if (demoHtml !== htmlText || demoJs !== jsText) {
              return 'Be sure to save a copy of any important edits before leaving this page.';
          }
      };
  
      registry.byId('codeContainer').watch('selectedChildWidget', function(name, oldPane, newPane) {
          if (newPane.id === 'jsContainer') {
              jsEditor.focus();
          } else if (newPane.id === 'htmlContainer') {
              htmlEditor.focus();
          }
      });
  
      function getScriptFromEditor(addExtraLine) {
          return 'function startup(Cesium) {\n' +
                 '    "use strict";\n' +
                 '//Sandcastle_Begin\n' +
                 (addExtraLine ? '\n' : '') +
                 jsEditor.getValue() +
                 '//Sandcastle_End\n' +
                 '    Sandcastle.finishedLoading();\n' +
                 '}\n' +
                 'if (typeof Cesium !== "undefined") {\n' +
                 '    startup(Cesium);\n' +
                 '} else if (typeof require === "function") {\n' +
                 '    require(["Cesium"], startup);\n' +
                 '}\n';
      }
  
      var scriptCodeRegex = /\/\/Sandcastle_Begin\s*([\s\S]*)\/\/Sandcastle_End/;
  
      function activateBucketScripts(bucketDoc) {
          var headNodes = bucketDoc.head.childNodes;
          var node;
          var nodes = [];
          for (var i = 0, len = headNodes.length; i < len; ++i) {
              node = headNodes[i];
              // header is included in blank frame.
              if (node.tagName === 'SCRIPT' && node.src.indexOf('Sandcastle-header.js') < 0) {
                  nodes.push(node);
              }
          }
  
          for (i = 0, len = nodes.length; i < len; ++i) {
              bucketDoc.head.removeChild(nodes[i]);
          }
  
          // Apply user HTML to bucket.
          var htmlElement = bucketDoc.createElement('div');
          htmlElement.innerHTML = htmlEditor.getValue();
          bucketDoc.body.appendChild(htmlElement);
  
          var onScriptTagError = function() {
              if (bucketFrame.contentDocument === bucketDoc) {
                  appendConsole('consoleError', 'Error loading ' + this.src, true);
                  appendConsole('consoleError', "Make sure Cesium is built, see the Contributor's Guide for details.", true);
              }
          };
  
          // Load each script after the previous one has loaded.
          var loadScript = function() {
              if (bucketFrame.contentDocument !== bucketDoc) {
                  // A newer reload has happened, abort this.
                  return;
              }
              if (nodes.length > 0) {
                  node = nodes.shift();
                  var scriptElement = bucketDoc.createElement('script');
                  var hasSrc = false;
                  for (var j = 0, numAttrs = node.attributes.length; j < numAttrs; ++j) {
                      var name = node.attributes[j].name;
                      var val = node.attributes[j].value;
                      scriptElement.setAttribute(name, val);
                      if (name === 'src' && val) {
                          hasSrc = true;
                      }
                  }
                  scriptElement.innerHTML = node.innerHTML;
                  if (hasSrc) {
                      scriptElement.onload = loadScript;
                      scriptElement.onerror = onScriptTagError;
                      bucketDoc.head.appendChild(scriptElement);
                  } else {
                      bucketDoc.head.appendChild(scriptElement);
                      loadScript();
                  }
              } else {
                  // Apply user JS to bucket
                  var element = bucketDoc.createElement('script');
  
                  // Firefox line numbers are zero-based, not one-based.
                  var isFirefox = navigator.userAgent.indexOf('Firefox/') >= 0;
  
                  element.textContent = getScriptFromEditor(isFirefox);
                  bucketDoc.body.appendChild(element);
              }
          };
          loadScript();
      }
  
      function applyBucket() {
          if (local.emptyBucket && local.bucketName && typeof bucketTypes[local.bucketName] === 'string') {
              bucketWaiting = false;
              var bucketDoc = bucketFrame.contentDocument;
              if (local.headers.substring(0, local.emptyBucket.length) !== local.emptyBucket) {
                  appendConsole('consoleError', 'Error, first part of ' + local.bucketName + ' must match first part of bucket.html exactly.', true);
              } else {
                  var bodyAttributes = local.headers.match(/<body([^>]*?)>/)[1];
                  var attributeRegex = /([-a-z_]+)\s*="([^"]*?)"/ig;
                  //group 1 attribute name, group 2 attribute value.  Assumes double-quoted attributes.
                  var attributeMatch;
                  while ((attributeMatch = attributeRegex.exec(bodyAttributes)) !== null) {
                      var attributeName = attributeMatch[1];
                      var attributeValue = attributeMatch[2];
                      if (attributeName === 'class') {
                          bucketDoc.body.className = attributeValue;
                      } else {
                          bucketDoc.body.setAttribute(attributeName, attributeValue);
                      }
                  }
  
                  var pos = local.headers.indexOf('</head>');
                  var extraHeaders = local.headers.substring(local.emptyBucket.length, pos);
                  bucketDoc.head.innerHTML += extraHeaders;
                  activateBucketScripts(bucketDoc);
              }
          } else {
              bucketWaiting = true;
          }
      }
  
      function applyBucketIfWaiting() {
          if (bucketWaiting) {
              applyBucket();
          }
      }
  
      xhr.get({
          url : 'templates/bucket.html',
          handleAs : 'text'
      }).then(function(value) {
          var pos = value.indexOf('</head>');
          local.emptyBucket = value.substring(0, pos);
          applyBucketIfWaiting();
      });
  
      function loadBucket(bucketName) {
          if (local.bucketName !== bucketName) {
              local.bucketName = bucketName;
              if (defined(bucketTypes[bucketName])) {
                  local.headers = bucketTypes[bucketName];
              } else {
                  local.headers = '<html><head></head><body data-sandcastle-bucket-loaded="no">';
                  xhr.get({
                      url : 'templates/' + bucketName,
                      handleAs : 'text'
                  }).then(function(value) {
                      var pos = value.indexOf('<body');
                      pos = value.indexOf('>', pos);
                      bucketTypes[bucketName] = value.substring(0, pos + 1);
                      if (local.bucketName === bucketName) {
                          local.headers = bucketTypes[bucketName];
                      }
                      applyBucketIfWaiting();
                  });
              }
          }
      }
  
      function loadFromGallery(demo) {
          document.getElementById('saveAsFile').download = demo.name + '.html';
          registry.byId('description').set('value', decodeHTML(demo.description).replace(/\\n/g, '\n'));
          registry.byId('label').set('value', decodeHTML(demo.label).replace(/\\n/g, '\n'));
  
          return requestDemo(demo.name).then(function(value) {
              demo.code = value;
  
              var parser = new DOMParser();
              var doc = parser.parseFromString(demo.code, 'text/html');
  
              var script = doc.querySelector('script[id="cesium_sandcastle_script"]');
              if (!script) {
                  appendConsole('consoleError', 'Error reading source file: ' + demo.name, true);
                  return;
              }
  
              var scriptMatch = scriptCodeRegex.exec(script.textContent);
              if (!scriptMatch) {
                  appendConsole('consoleError', 'Error reading source file: ' + demo.name, true);
                  return;
              }
  
              var scriptCode = scriptMatch[1];
              demoJs = scriptCode.replace(/\s/g, '');
              jsEditor.setValue(scriptCode);
              jsEditor.clearHistory();
  
              var htmlText = '';
              var childIndex = 0;
              var childNode = doc.body.childNodes[childIndex];
              while (childIndex < doc.body.childNodes.length && childNode !== script) {
                  htmlText += childNode.nodeType === 1 ? childNode.outerHTML : childNode.nodeValue;
                  childNode = doc.body.childNodes[++childIndex];
              }
              htmlText = htmlText.replace(/^\s+/, '');
              demoHtml = htmlText.replace(/\s/g, '');
              htmlEditor.setValue(htmlText);
              htmlEditor.clearHistory();
  
              if (typeof demo.bucket === 'string') {
                  loadBucket(demo.bucket);
              }
              CodeMirror.commands.runCesium(jsEditor);
          });
      }
  
      window.addEventListener('popstate', function(e) {
          if (e.state && e.state.name && e.state.code) {
              loadFromGallery(e.state);
              document.title = e.state.name + ' - Cesium Sandcastle';
          }
      }, false);
  
      window.addEventListener('message', function(e) {
          var line;
          // The iframe (bucket.html) sends this message on load.
          // This triggers the code to be injected into the iframe.
          if (e.data === 'reload') {
              var bucketDoc = bucketFrame.contentDocument;
              if (!local.bucketName) {
                  // Reload fired, bucket not specified yet.
                  return;
              }
              if (bucketDoc.body.getAttribute('data-sandcastle-loaded') !== 'yes') {
                  bucketDoc.body.setAttribute('data-sandcastle-loaded', 'yes');
                  logOutput.innerHTML = '';
                  numberOfNewConsoleMessages = 0;
                  registry.byId('logContainer').set('title', 'Console');
                  // This happens after a Run (F8) reloads bucket.html, to inject the editor code
                  // into the iframe, causing the demo to run there.
                  applyBucket();
                  if (docError) {
                      appendConsole('consoleError', 'Documentation not available.  Please run the "generateDocumentation" build script to generate Cesium documentation.', true);
                      showGallery();
                  }
                  if (galleryError) {
                      appendConsole('consoleError', 'Error loading gallery, please run the build script.', true);
                  }
              }
          } else if (defined(e.data.log)) {
              // Console log messages from the iframe display in Sandcastle.
              appendConsole('consoleLog', e.data.log, false);
          } else if (defined(e.data.error)) {
              // Console error messages from the iframe display in Sandcastle
              var errorMsg = e.data.error;
              var lineNumber = e.data.lineNumber;
              if (defined(lineNumber)) {
                  errorMsg += ' (on line ';
  
                  if (e.data.url) {
                      errorMsg += lineNumber + ' of ' + e.data.url + ')';
                  } else {
                      lineNumber = scriptLineToEditorLine(lineNumber);
                      errorMsg += (lineNumber + 1) + ')';
                      line = jsEditor.setGutterMarker(lineNumber, 'errorGutter', makeLineLabel(e.data.error, 'errorMarker'));
                      jsEditor.addLineClass(line, 'text', 'errorLine');
                      errorLines.push(line);
                      scrollToLine(lineNumber);
                  }
              }
              appendConsole('consoleError', errorMsg, true);
          } else if (defined(e.data.warn)) {
              // Console warning messages from the iframe display in Sandcastle.
              appendConsole('consoleWarn', e.data.warn, true);
          } else if (defined(e.data.highlight)) {
              // Hovering objects in the embedded Cesium window.
              highlightLine(e.data.highlight);
          }
      }, true);
  
      registry.byId('jsContainer').on('show', function() {
          suggestButton.set('disabled', false);
          jsEditor.refresh();
      });
  
      registry.byId('htmlContainer').on('show', function() {
          suggestButton.set('disabled', true);
          htmlEditor.refresh();
      });
  
      registry.byId('search').on('change', function() {
          searchTerm = this.get('value');
          searchRegExp = new RegExp(searchTerm, 'i');
          var numDemosShown = 0;
          if (searchTerm !== '') {
              showSearchContainer();
              var innerPanel = registry.byId('innerPanel');
              innerPanel.selectChild(registry.byId('searchContainer'));
              for (var i = 0; i < gallery_demos.length; i++) {
                  var demo = gallery_demos[i];
                  var demoName = demo.name;
                  if (searchRegExp.test(demoName) || searchRegExp.test(demo.code)) {
                      document.getElementById(demoName + 'searchDemo').style.display = 'inline-block';
                      ++numDemosShown;
                  } else {
                      document.getElementById(demoName + 'searchDemo').style.display = 'none';
                  }
              }
          } else {
              hideSearchContainer();
          }
  
          if (numDemosShown) {
              galleryErrorMsg.style.display = 'none';
          } else {
              galleryErrorMsg.style.display = 'inline-block';
          }
  
          showGallery();
          scheduleHintNoChange();
      });
  
      function hideSearchContainer() {
          if (dom.byId('searchContainer')) {
              var innerPanel = registry.byId('innerPanel');
              innerPanel.removeChild(searchContainer);
          }
      }
  
      function showSearchContainer() {
          if (!dom.byId('searchContainer')) {
              var innerPanel = registry.byId('innerPanel');
              innerPanel.addChild(searchContainer);
          }
      }
  
      registry.byId('buttonNew').on('click', function() {
          var htmlText = (htmlEditor.getValue()).replace(/\s/g, '');
          var jsText = (jsEditor.getValue()).replace(/\s/g, '');
          var confirmChange = true;
          if (demoHtml !== htmlText || demoJs !== jsText) {
              confirmChange = window.confirm('You have unsaved changes. Are you sure you want to navigate away from this demo?');
          }
          if (confirmChange) {
              loadFromGallery(newDemo).then(function() {
                  var demoSrc = newDemo.name + '.html';
                  var queries = window.location.search.substring(1).split('&');
                  for (var i = 0; i < queries.length; i++) {
                      var key = queries[i].split('=')[0];
                      if (key === "src") {
                          if (demoSrc !== queries[i].split('=')[1].replace('%20', ' ')) {
                              window.history.pushState(newDemo, newDemo.name, '?src=' + demoSrc + '&label=' + currentTab);
                          }
                      }
                  }
                  document.title = newDemo.name + ' - Cesium Sandcastle';
              });
          }
      });
      // Clicking the 'Run' button simply reloads the iframe.
      registry.byId('buttonRun').on('click', function() {
          CodeMirror.commands.runCesium(jsEditor);
      });
  
      registry.byId('buttonSuggest').on('click', function() {
          CodeMirror.commands.autocomplete(jsEditor);
      });
  
      function getDemoHtml() {
          return local.headers + '\n' +
                 htmlEditor.getValue() +
                 '<script id="cesium_sandcastle_script">\n' +
                 getScriptFromEditor(false) +
                 '</script>\n' +
                 '</body>\n' +
                 '</html>\n';
      }
  
      registry.byId('dropDownSaveAs').on('show', function() {
          var currentDemoName = ioQuery.queryToObject(window.location.search.substring(1)).src;
          currentDemoName = currentDemoName.replace('.html', '');
          var description = encodeHTML(registry.byId('description').get('value').replace(/\n/g, '\\n')).replace(/\"/g, '&quot;');
          var label = encodeHTML(registry.byId('label').get('value').replace(/\n/g, '\\n')).replace(/\"/g, '&quot;');
  
          var html = getDemoHtml();
          html = html.replace('<title>', '<meta name="description" content="' + description + '">\n    <title>');
          html = html.replace('<title>', '<meta name="cesium-sandcastle-labels" content="' + label + '">\n    <title>');
  
          var octetBlob = new Blob([html], {
              'type' : 'application/octet-stream',
              'endings' : 'native'
          });
          var octetBlobURL = URL.createObjectURL(octetBlob);
          dom.byId('saveAsFile').href = octetBlobURL;
      });
  
      registry.byId('buttonNewWindow').on('click', function() {
          var baseHref = window.location.href;
  
          //Handle case where demo is in a sub-directory.
          var searchLen = window.location.search.length;
          if (searchLen > 0) {
              baseHref = baseHref.substring(0, baseHref.length - searchLen);
          }
  
          var pos = baseHref.lastIndexOf('/');
          baseHref = baseHref.substring(0, pos) + '/gallery/';
  
          var html = getDemoHtml();
          html = html.replace('<head>', '<head>\n    <base href="' + baseHref + '">');
          var htmlBlob = new Blob([html], {
              'type' : 'text/html;charset=utf-8',
              'endings' : 'native'
          });
          var htmlBlobURL = URL.createObjectURL(htmlBlob);
          window.open(htmlBlobURL, '_blank');
          window.focus();
      });
  
      registry.byId('buttonThumbnail').on('change', function(newValue) {
          if (newValue) {
              domClass.add('bucketFrame', 'makeThumbnail');
          } else {
              domClass.remove('bucketFrame', 'makeThumbnail');
          }
      });
  
      var demoContainers = query('.demosContainer');
      demoContainers.forEach(function(demoContainer) {
          registerScroll(demoContainer);
      });
  
      var galleryContainer = registry.byId('innerPanel');
      galleryContainer.demoTileHeightRule = demoTileHeightRule;
      galleryContainer.originalResize = galleryContainer.resize;
      galleryContainer.resize = function(changeSize, resultSize) {
          var newSize = changeSize.h - 88;
          if (newSize < 20) {
              demoTileHeightRule.style.display = 'none';
          } else {
              demoTileHeightRule.style.display = 'inline';
              demoTileHeightRule.style.height = Math.min(newSize, 150) + 'px';
          }
          this.originalResize(changeSize, resultSize);
      };
  
      var queryObject = {};
      if (window.location.search) {
          queryObject = ioQuery.queryToObject(window.location.search.substring(1));
      } else {
          queryObject.src = 'Hello World.html';
          queryObject.label = 'Showcases';
      }
  
      function requestDemo(name) {
          return xhr.get({
              url : 'gallery/' + name + '.html',
              handleAs : 'text',
              error : function(error) {
                  appendConsole('consoleError', error, true);
                  galleryError = true;
              }
          });
      }
  
      function loadDemoFromFile(index) {
          var demo = gallery_demos[index];
  
          return requestDemo(demo.name).then(function(value) {
              // Store the file contents for later searching.
              demo.code = value;
  
              var parser = new DOMParser();
              var doc = parser.parseFromString(value, 'text/html');
  
              var bucket = doc.body.getAttribute('data-sandcastle-bucket');
              demo.bucket = bucket ? bucket : 'bucket-requirejs.html';
  
              var descriptionMeta = doc.querySelector('meta[name="description"]');
              var description = descriptionMeta && descriptionMeta.getAttribute('content');
              demo.description = description ? description : '';
  
              var labelsMeta = doc.querySelector('meta[name="cesium-sandcastle-labels"]');
              var labels = labelsMeta && labelsMeta.getAttribute('content');
              demo.label = labels ? labels : '';
  
              // Select the demo to load upon opening based on the query parameter.
              if (defined(queryObject.src)) {
                  if (demo.name === queryObject.src.replace('.html', '')) {
                      loadFromGallery(demo).then(function() {
                          window.history.replaceState(demo, demo.name, '?src=' + demo.name + '.html&label=' + queryObject.label);
                          document.title = demo.name + ' - Cesium Sandcastle';
                      });
                  }
              }
  
              // Create a tooltip containing the demo's description.
              demoTooltips[demo.name] = new TooltipDialog({
                  id : demo.name + 'TooltipDialog',
                  style : 'width: 200px; font-size: 12px;',
                  content : demo.description.replace(/\\n/g, '<br/>')
              });
  
              addFileToTab(index);
              return demo;
          });
      }
  
      var loading = true;
      function setSubtab(tabName) {
          currentTab = defined(tabName) && !loading ? tabName : queryObject.label;
          queryObject.label = tabName;
          loading = false;
      }
  
      function addFileToGallery(index) {
          var searchDemos = dom.byId('searchDemos');
          createGalleryButton(index, searchDemos, 'searchDemo');
          return loadDemoFromFile(index);
      }
  
      function onShowCallback() {
          return function() {
              setSubtab(this.title);
          };
      }
  
      function addFileToTab(index) {
          var demo = gallery_demos[index];
          if (demo.label !== '') {
              var labels = demo.label.split(',');
              for (var j = 0; j < labels.length; j++) {
                  var label = labels[j];
                  label = label.trim();
                  if (!dom.byId(label + 'Demos')) {
                      var cp = new ContentPane({
                          content : '<div id="' + label + 'Container" class="demosContainer"><div class="demos" id="' + label + 'Demos"></div></div>',
                          title : label,
                          onShow : onShowCallback()
                      }).placeAt('innerPanel');
                      subtabs[label] = cp;
                      registerScroll(dom.byId(label + 'Container'));
                  }
                  var tabName = label + 'Demos';
                  var tab = dom.byId(tabName);
                  createGalleryButton(index, tab, tabName);
              }
          }
      }
  
      function createGalleryButton(index, tab, tabName) {
          var demo = gallery_demos[index];
          var imgSrc = 'templates/Gallery_tile.jpg';
          if (defined(demo.img)) {
              imgSrc = 'gallery/' + demo.img;
          }
  
          var demoLink = document.createElement('a');
          demoLink.id = demo.name + tabName;
          demoLink.className = 'linkButton';
          demoLink.href = 'gallery/' + encodeURIComponent(demo.name) + '.html';
          tab.appendChild(demoLink);
  
          if (demo.name === "Hello World") {
              newDemo = demo;
          }
          demoLink.onclick = function(e) {
              if (mouse.isMiddle(e)) {
                  window.open('gallery/' + demo.name + '.html');
              } else {
                  var htmlText = (htmlEditor.getValue()).replace(/\s/g, '');
                  var jsText = (jsEditor.getValue()).replace(/\s/g, '');
                  var confirmChange = true;
                  if (demoHtml !== htmlText || demoJs !== jsText) {
                      confirmChange = window.confirm('You have unsaved changes. Are you sure you want to navigate away from this demo?');
                  }
                  if (confirmChange) {
                      loadFromGallery(demo).then(function() {
                          var demoSrc = demo.name + '.html';
                          if (demoSrc !== window.location.search.substring(1)) {
                              window.history.pushState(demo, demo.name, '?src=' + demoSrc + '&label=' + currentTab);
                          }
                          document.title = demo.name + ' - Cesium Sandcastle';
                      });
                  }
              }
              e.preventDefault();
          };
  
          new LinkButton({
              'label' : '<div class="demoTileTitle">' + demo.name + '</div>' +
                        '<img src="' + imgSrc + '" class="demoTileThumbnail" alt="" onDragStart="return false;" />'
          }).placeAt(demoLink);
  
          on(dom.byId(demoLink.id), 'mouseover', function() {
              scheduleGalleryTooltip(demo);
          });
  
          on(dom.byId(demoLink.id), 'mouseout', function() {
              closeGalleryTooltip();
          });
      }
  
      var promise;
      if (!defined(gallery_demos)) {
          galleryErrorMsg.textContent = 'No demos found, please run the build script.';
          galleryErrorMsg.style.display = 'inline-block';
      } else {
          var label = 'Showcases';
          var cp = new ContentPane({
              content : '<div id="showcasesContainer" class="demosContainer"><div class="demos" id="ShowcasesDemos"></div></div>',
              title : 'Showcases',
              onShow : function() {
                  setSubtab(this.title);
              }
          }).placeAt('innerPanel');
          subtabs[label] = cp;
          registerScroll(dom.byId('showcasesContainer'));
  
          var i;
          var len = gallery_demos.length;
  
          // Sort alphabetically.  This will eventually be a user option.
          gallery_demos.sort(function(a, b) {
              var aName = a.name.toUpperCase();
              var bName = b.name.toUpperCase();
              return bName < aName ? 1 : bName > aName ? -1 : 0;
          });
  
          var queryInGalleryIndex = false;
          var queryName = queryObject.src.replace('.html', '');
          var promises = [];
          for (i = 0; i < len; ++i) {
              promises.push(addFileToGallery(i));
          }
  
          promise = all(promises).then(function(results) {
              var resultsLength = results.length;
              for (i = 0; i < resultsLength; ++i) {
                  if (results[i].name === queryName) {
                      queryInGalleryIndex = true;
                  }
              }
  
              label = 'All';
              cp = new ContentPane({
                  content : '<div id="allContainer" class="demosContainer"><div class="demos" id="allDemos"></div></div>',
                  title : label,
                  onShow : function() {
                      setSubtab(this.title);
                  }
              }).placeAt('innerPanel');
              subtabs[label] = cp;
              registerScroll(dom.byId('allContainer'));
  
              var demos = dom.byId('allDemos');
              for (i = 0; i < len; ++i) {
                  if (!/Development/i.test(gallery_demos[i].label)) {
                      createGalleryButton(i, demos, 'all');
                  }
              }
  
              if (!queryInGalleryIndex) {
                  gallery_demos.push({
                                         name : queryName,
                                         description : ''
                                     });
                  return addFileToGallery(gallery_demos.length - 1);
              }
          });
      }
  
      var searchContainer;
      when(promise).then(function() {
          dom.byId('searchDemos').appendChild(galleryErrorMsg);
          searchContainer = registry.byId('searchContainer');
  
          hideSearchContainer();
          registry.byId('innerPanel').selectChild(subtabs[currentTab]);
      });
  });
  