text: /* ***** BEGIN LICENSE BLOCK *****
   * Distributed under the BSD license:
   *
   * Copyright (c) 2012, Ajax.org B.V.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *     * Redistributions of source code must retain the above copyright
   *       notice, this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in the
   *       documentation and/or other materials provided with the distribution.
   *     * Neither the name of Ajax.org B.V. nor the
   *       names of its contributors may be used to endorse or promote products
   *       derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * ***** END LICENSE BLOCK ***** */
  
  define(function(require, exports, module) {
  "use strict";
  
  var Renderer = require("../virtual_renderer").VirtualRenderer;
  var Editor = require("../editor").Editor;
  var Range = require("../range").Range;
  var event = require("../lib/event");
  var lang = require("../lib/lang");
  var dom = require("../lib/dom");
  
  var $singleLineEditor = function(el) {
      var renderer = new Renderer(el);
  
      renderer.$maxLines = 4;
  
      var editor = new Editor(renderer);
  
      editor.setHighlightActiveLine(false);
      editor.setShowPrintMargin(false);
      editor.renderer.setShowGutter(false);
      editor.renderer.setHighlightGutterLine(false);
  
      editor.$mouseHandler.$focusWaitTimout = 0;
      editor.$highlightTagPending = true;
  
      return editor;
  };
  
  var AcePopup = function(parentNode) {
      var el = dom.createElement("div");
      var popup = new $singleLineEditor(el);
  
      if (parentNode)
          parentNode.appendChild(el);
      el.style.display = "none";
      popup.renderer.content.style.cursor = "default";
      popup.renderer.setStyle("ace_autocomplete");
  
      popup.setOption("displayIndentGuides", false);
      popup.setOption("dragDelay", 150);
  
      var noop = function(){};
  
      popup.focus = noop;
      popup.$isFocused = true;
  
      popup.renderer.$cursorLayer.restartTimer = noop;
      popup.renderer.$cursorLayer.element.style.opacity = 0;
  
      popup.renderer.$maxLines = 8;
      popup.renderer.$keepTextAreaAtCursor = false;
  
      popup.setHighlightActiveLine(false);
      // set default highlight color
      popup.session.highlight("");
      popup.session.$searchHighlight.clazz = "ace_highlight-marker";
  
      popup.on("mousedown", function(e) {
          var pos = e.getDocumentPosition();
          popup.selection.moveToPosition(pos);
          selectionMarker.start.row = selectionMarker.end.row = pos.row;
          e.stop();
      });
  
      var lastMouseEvent;
      var hoverMarker = new Range(-1,0,-1,Infinity);
      var selectionMarker = new Range(-1,0,-1,Infinity);
      selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine");
      popup.setSelectOnHover = function(val) {
          if (!val) {
              hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine");
          } else if (hoverMarker.id) {
              popup.session.removeMarker(hoverMarker.id);
              hoverMarker.id = null;
          }
      };
      popup.setSelectOnHover(false);
      popup.on("mousemove", function(e) {
          if (!lastMouseEvent) {
              lastMouseEvent = e;
              return;
          }
          if (lastMouseEvent.x == e.x && lastMouseEvent.y == e.y) {
              return;
          }
          lastMouseEvent = e;
          lastMouseEvent.scrollTop = popup.renderer.scrollTop;
          var row = lastMouseEvent.getDocumentPosition().row;
          if (hoverMarker.start.row != row) {
              if (!hoverMarker.id)
                  popup.setRow(row);
              setHoverMarker(row);
          }
      });
      popup.renderer.on("beforeRender", function() {
          if (lastMouseEvent && hoverMarker.start.row != -1) {
              lastMouseEvent.$pos = null;
              var row = lastMouseEvent.getDocumentPosition().row;
              if (!hoverMarker.id)
                  popup.setRow(row);
              setHoverMarker(row, true);
          }
      });
      popup.renderer.on("afterRender", function() {
          var row = popup.getRow();
          var t = popup.renderer.$textLayer;
          var selected = t.element.childNodes[row - t.config.firstRow];
          if (selected == t.selectedNode)
              return;
          if (t.selectedNode)
              dom.removeCssClass(t.selectedNode, "ace_selected");
          t.selectedNode = selected;
          if (selected)
              dom.addCssClass(selected, "ace_selected");
      });
      var hideHoverMarker = function() { setHoverMarker(-1) };
      var setHoverMarker = function(row, suppressRedraw) {
          if (row !== hoverMarker.start.row) {
              hoverMarker.start.row = hoverMarker.end.row = row;
              if (!suppressRedraw)
                  popup.session._emit("changeBackMarker");
              popup._emit("changeHoverMarker");
          }
      };
      popup.getHoveredRow = function() {
          return hoverMarker.start.row;
      };
  
      event.addListener(popup.container, "mouseout", hideHoverMarker);
      popup.on("hide", hideHoverMarker);
      popup.on("changeSelection", hideHoverMarker);
  
      popup.session.doc.getLength = function() {
          return popup.data.length;
      };
      popup.session.doc.getLine = function(i) {
          var data = popup.data[i];
          if (typeof data == "string")
              return data;
          return (data && data.value) || "";
      };
  
      var bgTokenizer = popup.session.bgTokenizer;
      bgTokenizer.$tokenizeRow = function(row) {
          var data = popup.data[row];
          var tokens = [];
          if (!data)
              return tokens;
          if (typeof data == "string")
              data = {value: data};
          if (!data.caption)
              data.caption = data.value || data.name;
  
          var last = -1;
          var flag, c;
          for (var i = 0; i < data.caption.length; i++) {
              c = data.caption[i];
              flag = data.matchMask & (1 << i) ? 1 : 0;
              if (last !== flag) {
                  tokens.push({type: data.className || "" + ( flag ? "completion-highlight" : ""), value: c});
                  last = flag;
              } else {
                  tokens[tokens.length - 1].value += c;
              }
          }
  
          if (data.meta) {
              var maxW = popup.renderer.$size.scrollerWidth / popup.renderer.layerConfig.characterWidth;
              var metaData = data.meta;
              if (metaData.length + data.caption.length > maxW - 2) {
                  // trim meta to fit this popup and add ellipsis
                  metaData = metaData.substr(0, maxW - data.caption.length - 3) + "\u2026"
              }
              tokens.push({type: "rightAlignedText", value: metaData});
          }
          return tokens;
      };
      bgTokenizer.$updateOnChange = noop;
      bgTokenizer.start = noop;
  
      popup.session.$computeWidth = function() {
          return this.screenWidth = 0;
      };
  
      popup.$blockScrolling = Infinity;
  
      // public
      popup.isOpen = false;
      popup.isTopdown = false;
  
      popup.data = [];
      popup.setData = function(list) {
          popup.setValue(lang.stringRepeat("\n", list.length), -1);
          popup.data = list || [];
          popup.setRow(0);
      };
      popup.getData = function(row) {
          return popup.data[row];
      };
  
      popup.getRow = function() {
          return selectionMarker.start.row;
      };
      popup.setRow = function(line) {
          line = Math.max(0, Math.min(this.data.length, line));
          if (selectionMarker.start.row != line) {
              popup.selection.clearSelection();
              selectionMarker.start.row = selectionMarker.end.row = line || 0;
              popup.session._emit("changeBackMarker");
              popup.moveCursorTo(line || 0, 0);
              if (popup.isOpen)
                  popup._signal("select");
          }
      };
  
      popup.on("changeSelection", function() {
          if (popup.isOpen)
              popup.setRow(popup.selection.lead.row);
          popup.renderer.scrollCursorIntoView();
      });
  
      popup.hide = function() {
          this.container.style.display = "none";
          this._signal("hide");
          popup.isOpen = false;
      };
      popup.show = function(pos, lineHeight, topdownOnly) {
          var el = this.container;
          var screenHeight = window.innerHeight;
          var screenWidth = window.innerWidth;
          var renderer = this.renderer;
          // var maxLines = Math.min(renderer.$maxLines, this.session.getLength());
          var maxH = renderer.$maxLines * lineHeight * 1.4;
          var top = pos.top + this.$borderSize;
          var allowTopdown = top > screenHeight / 2 && !topdownOnly;
          if (allowTopdown && top + lineHeight + maxH > screenHeight) {
              renderer.$maxPixelHeight = top - 2 * this.$borderSize;
              el.style.top = "";
              el.style.bottom = screenHeight - top + "px";
              popup.isTopdown = false;
          } else {
              top += lineHeight;
              renderer.$maxPixelHeight = screenHeight - top - 0.2 * lineHeight;
              el.style.top = top + "px";
              el.style.bottom = "";
              popup.isTopdown = true;
          }
  
          el.style.display = "";
          this.renderer.$textLayer.checkForSizeChanges();
  
          var left = pos.left;
          if (left + el.offsetWidth > screenWidth)
              left = screenWidth - el.offsetWidth;
  
          el.style.left = left + "px";
  
          this._signal("show");
          lastMouseEvent = null;
          popup.isOpen = true;
      };
  
      popup.getTextLeftOffset = function() {
          return this.$borderSize + this.renderer.$padding + this.$imageSize;
      };
  
      popup.$imageSize = 0;
      popup.$borderSize = 1;
  
      return popup;
  };
  
  dom.importCssString("\
  .ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\
      background-color: #CAD6FA;\
      z-index: 1;\
  }\
  .ace_editor.ace_autocomplete .ace_line-hover {\
      border: 1px solid #abbffe;\
      margin-top: -1px;\
      background: rgba(233,233,253,0.4);\
  }\
  .ace_editor.ace_autocomplete .ace_line-hover {\
      position: absolute;\
      z-index: 2;\
  }\
  .ace_editor.ace_autocomplete .ace_scroller {\
     background: none;\
     border: none;\
     box-shadow: none;\
  }\
  .ace_rightAlignedText {\
      color: gray;\
      display: inline-block;\
      position: absolute;\
      right: 4px;\
      text-align: right;\
      z-index: -1;\
  }\
  .ace_editor.ace_autocomplete .ace_completion-highlight{\
      color: #000;\
      text-shadow: 0 0 0.01em;\
  }\
  .ace_editor.ace_autocomplete {\
      width: 280px;\
      z-index: 200000;\
      background: #fbfbfb;\
      color: #444;\
      border: 1px lightgray solid;\
      position: fixed;\
      box-shadow: 2px 3px 5px rgba(0,0,0,.2);\
      line-height: 1.4;\
  }");
  
  exports.AcePopup = AcePopup;
  
  });
  