Sfoglia il codice sorgente

HUE-8638 [editor] Move context popover and tooltip logic out of the aceEditor binding

Johan Ahlen 7 anni fa
parent
commit
af6b4244da
1 ha cambiato i file con 276 aggiunte e 272 eliminazioni
  1. 276 272
      desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

+ 276 - 272
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -4123,20 +4123,22 @@
 
     var STATEMENT_COUNT_AROUND_ACTIVE = 10;
 
-    function AceLocationHandler (editor, editorId, snippet) {
+    function AceLocationHandler (options) {
       var self = this;
-      self.editor = editor;
-      self.editorId = editorId;
-      self.snippet = snippet;
-      self.sqlSyntaxWorkerSub = null;
+      self.editor = options.editor;
+      self.editorId = options.editorId;
+      self.snippet = options.snippet;
+      self.expandStar = (options.i18n && options.i18n.expandStar) || 'Right-click to expand with columns';
+      self.contextTooltip = (options.i18n && options.i18n.contextTooltip) || 'Right-click for details';
 
+      self.sqlSyntaxWorkerSub = null;
       self.disposeFunctions = [];
-
       self.databaseIndex = {};
 
       self.attachStatementLocator();
       self.attachSqlWorker();
       self.attachGutterHandler();
+      self.attachMouseListeners();
 
       var updateDatabaseIndex = function (databaseList) {
         self.databaseIndex = {};
@@ -4154,6 +4156,273 @@
       updateDatabaseIndex(self.snippet.availableDatabases());
     }
 
+    AceLocationHandler.prototype.attachMouseListeners = function () {
+      var self = this;
+
+      var Tooltip = ace.require("ace/tooltip").Tooltip;
+      var AceRange = ace.require('ace/range').Range;
+
+      var contextTooltip = new Tooltip(self.editor.container);
+      var tooltipTimeout = -1;
+      var disableTooltip = false;
+      var lastHoveredToken = null;
+      var activeMarkers = [];
+      var keepLastMarker = false;
+
+      var hideContextTooltip = function () {
+        clearTimeout(tooltipTimeout);
+        contextTooltip.hide();
+      };
+
+      var clearActiveMarkers = function () {
+        hideContextTooltip();
+        while (activeMarkers.length > keepLastMarker ? 1 : 0) {
+          self.editor.session.removeMarker(activeMarkers.shift());
+        }
+      };
+
+      var markLocation = function (parseLocation) {
+        var range;
+        if (parseLocation.type === 'function') {
+          // Todo: Figure out why functions need an extra char at the end
+          range = new AceRange(parseLocation.location.first_line - 1, parseLocation.location.first_column - 1, parseLocation.location.last_line - 1, parseLocation.location.last_column);
+        } else {
+          range = new AceRange(parseLocation.location.first_line - 1, parseLocation.location.first_column - 1, parseLocation.location.last_line - 1, parseLocation.location.last_column - 1);
+        }
+        activeMarkers.push(self.editor.session.addMarker(range, 'hue-ace-location'));
+        return range;
+      };
+
+      var popoverShownSub = huePubSub.subscribe('context.popover.shown', function () {
+        hideContextTooltip();
+        keepLastMarker = true;
+        disableTooltip = true;
+      });
+
+      self.disposeFunctions.push(function () {
+        popoverShownSub.remove();
+      });
+
+      var popoverHiddenSub = huePubSub.subscribe('context.popover.hidden', function () {
+        disableTooltip = false;
+        clearActiveMarkers();
+        keepLastMarker = false;
+      });
+
+      self.disposeFunctions.push(function () {
+        popoverHiddenSub.remove();
+      });
+
+      var mousemoveListener = self.editor.on('mousemove', function (e) {
+        clearTimeout(tooltipTimeout);
+        var selectionRange = self.editor.selection.getRange();
+        if (selectionRange.isEmpty()) {
+          var pointerPosition = self.editor.renderer.screenToTextCoordinates(e.clientX + 5, e.clientY);
+          var endTestPosition = self.editor.renderer.screenToTextCoordinates(e.clientX + 15, e.clientY);
+          if (endTestPosition.column !== pointerPosition.column) {
+            var token = self.editor.session.getTokenAt(pointerPosition.row, pointerPosition.column);
+            if (token !== null && !token.notFound && token.parseLocation && !disableTooltip && token.parseLocation.type !== 'alias') {
+              tooltipTimeout = window.setTimeout(function () {
+                if (token.parseLocation) {
+                  var endCoordinates = self.editor.renderer.textToScreenCoordinates(pointerPosition.row, token.start);
+
+                  var tooltipText = token.parseLocation.type === 'asterisk' ? self.expandStar : self.contextTooltip;
+                  var colType;
+                  if (token.parseLocation.type === 'column') {
+                    var tableChain = token.parseLocation.identifierChain.concat();
+                    var lastIdentifier = tableChain.pop();
+                    if (tableChain.length > 0 && lastIdentifier && lastIdentifier.name) {
+                      var colName = lastIdentifier.name.toLowerCase();
+                      // Note, as cachedOnly is set to true it will call the successCallback right away (or not at all)
+                      DataCatalog.getEntry({
+                        sourceType: self.snippet.type(),
+                        namespace: self.snippet.namespace(),
+                        compute: self.snippet.compute(),
+                        temporaryOnly: self.snippet.autocompleteSettings.temporaryOnly,
+                        path: $.map(tableChain, function (identifier) { return identifier.name })
+                      }).done(function (entry) {
+                        entry.getSourceMeta({ cachedOnly: true, silenceErrors: true }).done(function (sourceMeta) {
+                          if (sourceMeta && sourceMeta.extended_columns) {
+                            sourceMeta.extended_columns.every(function (col) {
+                              if (col.name.toLowerCase() === colName) {
+                                colType = col.type.match(/^[^<]*/g)[0];
+                                return false;
+                              }
+                              return true;
+                            })
+                          }
+                        });
+                      });
+                    }
+                  }
+                  if (token.parseLocation.identifierChain) {
+                    var sqlIdentifier = $.map(token.parseLocation.identifierChain, function (identifier) {
+                      return identifier.name
+                    }).join('.');
+                    if (colType) {
+                      sqlIdentifier += ' (' + colType + ')';
+                    }
+                    tooltipText = sqlIdentifier + ' - ' + tooltipText;
+                  } else if (token.parseLocation.function) {
+                    tooltipText = token.parseLocation.function + ' - ' + tooltipText;
+                  }
+                  contextTooltip.show(tooltipText, endCoordinates.pageX, endCoordinates.pageY + self.editor.renderer.lineHeight + 3);
+                }
+              }, 500);
+            } else if (token !== null && token.notFound) {
+              tooltipTimeout = window.setTimeout(function () {
+                // TODO: i18n
+                if (token.notFound && token.syntaxError) {
+                  var tooltipText;
+                  if (token.syntaxError.expected.length > 0) {
+                    tooltipText = HUE_I18n.syntaxChecker.didYouMean + ' "' + token.syntaxError.expected[0].text + '"?';
+                  } else {
+                    tooltipText = HUE_I18n.syntaxChecker.couldNotFind + ' "' + (token.qualifiedIdentifier || token.value) + '"';
+                  }
+                  var endCoordinates = self.editor.renderer.textToScreenCoordinates(pointerPosition.row, token.start);
+                  contextTooltip.show(tooltipText, endCoordinates.pageX, endCoordinates.pageY + self.editor.renderer.lineHeight + 3);
+                }
+              }, 500);
+            } else if (token !== null && token.syntaxError) {
+              tooltipTimeout = window.setTimeout(function () {
+                if (token.syntaxError) {
+                  var tooltipText;
+                  if (token.syntaxError.expected.length > 0) {
+                    tooltipText = HUE_I18n.syntaxChecker.didYouMean + ' "' + token.syntaxError.expected[0].text + '"?';
+                  } else if (token.syntaxError.expectedStatementEnd) {
+                    tooltipText = HUE_I18n.syntaxChecker.expectedStatementEnd;
+                  }
+                  if (tooltipText) {
+                    var endCoordinates = self.editor.renderer.textToScreenCoordinates(pointerPosition.row, token.start);
+                    contextTooltip.show(tooltipText, endCoordinates.pageX, endCoordinates.pageY + self.editor.renderer.lineHeight + 3);
+                  }
+                }
+              }, 500);
+            } else {
+              hideContextTooltip();
+            }
+            if (lastHoveredToken !== token) {
+              clearActiveMarkers();
+              if (token !== null && !token.notFound && token.parseLocation && ['alias', 'whereClause', 'limitClause', 'selectList'].indexOf(token.parseLocation.type) === -1) {
+                markLocation(token.parseLocation);
+              }
+              lastHoveredToken = token;
+            }
+          } else {
+            clearActiveMarkers();
+            lastHoveredToken = null;
+          }
+        }
+      });
+
+      self.disposeFunctions.push(function () {
+        self.editor.off('mousemove', mousemoveListener);
+      });
+
+      var inputListener = self.editor.on('input', function (e) {
+        clearActiveMarkers();
+        lastHoveredToken = null;
+      });
+
+      self.disposeFunctions.push(function () {
+        self.editor.off('input', inputListener);
+      });
+
+      var mouseoutListener = function (e) {
+        clearActiveMarkers();
+        clearTimeout(tooltipTimeout);
+        contextTooltip.hide();
+        lastHoveredToken = null;
+      };
+
+      self.editor.container.addEventListener('mouseout', mouseoutListener);
+
+      self.disposeFunctions.push(function () {
+        self.editor.container.removeEventListener('mouseout', mouseoutListener);
+      });
+
+      var onContextMenu = function (e) {
+        var selectionRange = self.editor.selection.getRange();
+        huePubSub.publish('context.popover.hide');
+        huePubSub.publish('sql.syntax.dropdown.hide');
+        if (selectionRange.isEmpty()) {
+          var pointerPosition = self.editor.renderer.screenToTextCoordinates(e.clientX + 5, e.clientY);
+          var token = self.editor.session.getTokenAt(pointerPosition.row, pointerPosition.column);
+          if (token && ((token.parseLocation && ['alias', 'whereClause', 'limitClause', 'selectList'].indexOf(token.parseLocation.type) === -1) || token.syntaxError)) {
+            var range = token.parseLocation ? markLocation(token.parseLocation) : new AceRange(token.syntaxError.loc.first_line - 1, token.syntaxError.loc.first_column, token.syntaxError.loc.last_line - 1, token.syntaxError.loc.first_column + token.syntaxError.text.length);
+            var startCoordinates = self.editor.renderer.textToScreenCoordinates(range.start.row, range.start.column);
+            var endCoordinates = self.editor.renderer.textToScreenCoordinates(range.end.row, range.end.column);
+            var source = {
+              // TODO: add element likely in the event
+              left: startCoordinates.pageX - 3,
+              top: startCoordinates.pageY,
+              right: endCoordinates.pageX - 3,
+              bottom: endCoordinates.pageY + self.editor.renderer.lineHeight
+            };
+
+            if (token.parseLocation && token.parseLocation.identifierChain && !token.notFound) {
+              token.parseLocation.resolveCatalogEntry().done(function (entry) {
+                huePubSub.publish('context.popover.show', {
+                  data: {
+                    type: 'catalogEntry',
+                    catalogEntry: entry
+                  },
+                  pinEnabled: true,
+                  source: source
+                });
+              }).fail(function () {
+                token.notFound = true;
+              });
+            } else if (token.parseLocation && !token.notFound) {
+              // Asterisk, function etc.
+              if (token.parseLocation.type === 'file') {
+                AssistStorageEntry.getEntry(token.parseLocation.path).done(function (entry) {
+                  entry.open(true);
+                  huePubSub.publish('context.popover.show', {
+                    data: {
+                      type: 'storageEntry',
+                      storageEntry: entry,
+                      editorLocation: token.parseLocation.location
+                    },
+                    pinEnabled: true,
+                    source: source
+                  });
+                });
+              } else {
+                huePubSub.publish('context.popover.show', {
+                  data: token.parseLocation,
+                  sourceType: self.snippet.type(),
+                  namespace: self.snippet.namespace(),
+                  compute: self.snippet.compute(),
+                  defaultDatabase: self.snippet.database(),
+                  pinEnabled: true,
+                  source: source
+                });
+              }
+            } else if (token.syntaxError) {
+              huePubSub.publish('sql.syntax.dropdown.show', {
+                snippet: self.snippet,
+                data: token.syntaxError,
+                editor: self.editor,
+                range: range,
+                sourceType: self.snippet.type(),
+                defaultDatabase: self.snippet.database(),
+                source: source
+              });
+            }
+            e.preventDefault();
+            return false;
+          }
+        }
+      };
+
+      var contextmenuListener = self.editor.container.addEventListener('contextmenu', onContextMenu);
+
+      self.disposeFunctions.push(function () {
+        self.editor.container.removeEventListener('contextmenu', contextmenuListener);
+      });
+    };
+
     AceLocationHandler.prototype.attachGutterHandler = function () {
       var self = this;
       var lastMarkedGutterLines = [];
@@ -4887,7 +5156,6 @@
       $el.text(snippet.statement_raw());
 
       var editor = ace.edit($el.attr("id"));
-      var Tooltip = ace.require("ace/tooltip").Tooltip;
       var AceRange = ace.require('ace/range').Range;
 
       var resizeAce = function () {
@@ -4907,7 +5175,7 @@
         resizePubSub.remove();
       });
 
-      var aceLocationHandler = new AceLocationHandler(editor, $el.attr("id"), snippet);
+      var aceLocationHandler = new AceLocationHandler({ editor: editor, editorId: $el.attr("id"), snippet: snippet, i18n: { expandStar: options.expandStar, contextTooltip: options.contextTooltip }});
       disposeFunctions.push(function () {
         aceLocationHandler.dispose();
       });
@@ -5186,270 +5454,6 @@
         editor.off('blur', blurListener);
       });
 
-      // TODO: Move context menu logic to separate module
-      (function () {
-        var contextTooltip = new Tooltip(editor.container);
-        var tooltipTimeout = -1;
-        var disableTooltip = false;
-        var lastHoveredToken = null;
-        var activeMarkers = [];
-        var keepLastMarker = false;
-
-        var hideContextTooltip = function () {
-          clearTimeout(tooltipTimeout);
-          contextTooltip.hide();
-        };
-
-        var clearActiveMarkers = function () {
-          hideContextTooltip();
-          while (activeMarkers.length > keepLastMarker ? 1 : 0) {
-            editor.session.removeMarker(activeMarkers.shift());
-          }
-        };
-
-        var markLocation = function (parseLocation) {
-          var range;
-          if (parseLocation.type === 'function') {
-            // Todo: Figure out why functions need an extra char at the end
-            range = new AceRange(parseLocation.location.first_line - 1, parseLocation.location.first_column - 1, parseLocation.location.last_line - 1, parseLocation.location.last_column);
-          } else {
-            range = new AceRange(parseLocation.location.first_line - 1, parseLocation.location.first_column - 1, parseLocation.location.last_line - 1, parseLocation.location.last_column - 1);
-          }
-          activeMarkers.push(editor.session.addMarker(range, 'hue-ace-location'));
-          return range;
-        };
-
-        var popoverShownSub = huePubSub.subscribe('context.popover.shown', function () {
-          hideContextTooltip();
-          keepLastMarker = true;
-          disableTooltip = true;
-        });
-
-        disposeFunctions.push(function () {
-          popoverShownSub.remove();
-        });
-
-        var popoverHiddenSub = huePubSub.subscribe('context.popover.hidden', function () {
-          disableTooltip = false;
-          clearActiveMarkers();
-          keepLastMarker = false;
-        });
-
-        disposeFunctions.push(function () {
-          popoverHiddenSub.remove();
-        });
-
-        var mousemoveListener = editor.on('mousemove', function (e) {
-          clearTimeout(tooltipTimeout);
-          var selectionRange = editor.selection.getRange();
-          if (selectionRange.isEmpty()) {
-            var pointerPosition = editor.renderer.screenToTextCoordinates(e.clientX + 5, e.clientY);
-            var endTestPosition = editor.renderer.screenToTextCoordinates(e.clientX + 15, e.clientY);
-            if (endTestPosition.column !== pointerPosition.column) {
-              var token = editor.session.getTokenAt(pointerPosition.row, pointerPosition.column);
-              if (token !== null && !token.notFound && token.parseLocation && !disableTooltip && token.parseLocation.type !== 'alias') {
-                tooltipTimeout = window.setTimeout(function () {
-                  if (token.parseLocation) {
-                    var endCoordinates = editor.renderer.textToScreenCoordinates(pointerPosition.row, token.start);
-
-                    var tooltipText = token.parseLocation.type === 'asterisk' ? options.expandStar : options.contextTooltip;
-                    var colType;
-                    if (token.parseLocation.type === 'column') {
-                      var tableChain = token.parseLocation.identifierChain.concat();
-                      var lastIdentifier = tableChain.pop();
-                      if (tableChain.length > 0 && lastIdentifier && lastIdentifier.name) {
-                        var colName = lastIdentifier.name.toLowerCase();
-                        // Note, as cachedOnly is set to true it will call the successCallback right away (or not at all)
-                        DataCatalog.getEntry({
-                          sourceType: snippet.type(),
-                          namespace: snippet.namespace(),
-                          compute: snippet.compute(),
-                          path: $.map(tableChain, function (identifier) { return identifier.name })
-                        }).done(function (entry) {
-                          entry.getSourceMeta({ cachedOnly: true, silenceErrors: true }).done(function (sourceMeta) {
-                            if (sourceMeta && sourceMeta.extended_columns) {
-                              sourceMeta.extended_columns.every(function (col) {
-                                if (col.name.toLowerCase() === colName) {
-                                  colType = col.type.match(/^[^<]*/g)[0];
-                                  return false;
-                                }
-                                return true;
-                              })
-                            }
-                          });
-                        });
-                      }
-                    }
-                    if (token.parseLocation.identifierChain) {
-                      var sqlIdentifier = $.map(token.parseLocation.identifierChain, function (identifier) {
-                          return identifier.name
-                        }).join('.');
-                      if (colType) {
-                        sqlIdentifier += ' (' + colType + ')';
-                      }
-                      tooltipText = sqlIdentifier + ' - ' + tooltipText;
-                    } else if (token.parseLocation.function) {
-                      tooltipText = token.parseLocation.function + ' - ' + tooltipText;
-                    }
-                    contextTooltip.show(tooltipText, endCoordinates.pageX, endCoordinates.pageY + editor.renderer.lineHeight + 3);
-                  }
-                }, 500);
-              } else if (token !== null && token.notFound) {
-                tooltipTimeout = window.setTimeout(function () {
-                  // TODO: i18n
-                  if (token.notFound && token.syntaxError) {
-                    var tooltipText;
-                    if (token.syntaxError.expected.length > 0) {
-                      tooltipText = HUE_I18n.syntaxChecker.didYouMean + ' "' + token.syntaxError.expected[0].text + '"?';
-                    } else {
-                      tooltipText = HUE_I18n.syntaxChecker.couldNotFind + ' "' + (token.qualifiedIdentifier || token.value) + '"';
-                    }
-                    var endCoordinates = editor.renderer.textToScreenCoordinates(pointerPosition.row, token.start);
-                    contextTooltip.show(tooltipText, endCoordinates.pageX, endCoordinates.pageY + editor.renderer.lineHeight + 3);
-                  }
-                }, 500);
-              } else if (token !== null && token.syntaxError) {
-                tooltipTimeout = window.setTimeout(function () {
-                  // TODO: i18n
-                  if (token.syntaxError) {
-                    var tooltipText;
-                    if (token.syntaxError.expected.length > 0) {
-                      tooltipText = HUE_I18n.syntaxChecker.didYouMean + ' "' + token.syntaxError.expected[0].text + '"?';
-                    } else if (token.syntaxError.expectedStatementEnd) {
-                      tooltipText = HUE_I18n.syntaxChecker.expectedStatementEnd;
-                    }
-                    if (tooltipText) {
-                      var endCoordinates = editor.renderer.textToScreenCoordinates(pointerPosition.row, token.start);
-                      contextTooltip.show(tooltipText, endCoordinates.pageX, endCoordinates.pageY + editor.renderer.lineHeight + 3);
-                    }
-                  }
-                }, 500);
-              } else {
-                hideContextTooltip();
-              }
-              if (lastHoveredToken !== token) {
-                clearActiveMarkers();
-                if (token !== null && !token.notFound && token.parseLocation && ['alias', 'whereClause', 'limitClause', 'selectList'].indexOf(token.parseLocation.type) === -1) {
-                  markLocation(token.parseLocation);
-                }
-                lastHoveredToken = token;
-              }
-            } else {
-              clearActiveMarkers();
-              lastHoveredToken = null;
-            }
-          }
-        });
-
-        disposeFunctions.push(function () {
-          editor.off('mousemove', mousemoveListener);
-        });
-
-        var inputListener = editor.on('input', function (e) {
-          clearActiveMarkers();
-          lastHoveredToken = null;
-        });
-
-        disposeFunctions.push(function () {
-          editor.off('input', inputListener);
-        });
-
-        var mouseoutListener = function (e) {
-          clearActiveMarkers();
-          clearTimeout(tooltipTimeout);
-          contextTooltip.hide();
-          lastHoveredToken = null;
-        };
-
-        editor.container.addEventListener('mouseout', mouseoutListener);
-
-        disposeFunctions.push(function () {
-          editor.container.removeEventListener('mouseout', mouseoutListener);
-        });
-
-        var onContextMenu = function (e) {
-          var selectionRange = editor.selection.getRange();
-          huePubSub.publish('context.popover.hide');
-          huePubSub.publish('sql.syntax.dropdown.hide');
-          if (selectionRange.isEmpty()) {
-            var pointerPosition = editor.renderer.screenToTextCoordinates(e.clientX + 5, e.clientY);
-            var token = editor.session.getTokenAt(pointerPosition.row, pointerPosition.column);
-            if (token && ((token.parseLocation && ['alias', 'whereClause', 'limitClause', 'selectList'].indexOf(token.parseLocation.type) === -1) || token.syntaxError)) {
-              var range = token.parseLocation ? markLocation(token.parseLocation) : new AceRange(token.syntaxError.loc.first_line - 1, token.syntaxError.loc.first_column, token.syntaxError.loc.last_line - 1, token.syntaxError.loc.first_column + token.syntaxError.text.length);
-              var startCoordinates = editor.renderer.textToScreenCoordinates(range.start.row, range.start.column);
-              var endCoordinates = editor.renderer.textToScreenCoordinates(range.end.row, range.end.column);
-              var source = {
-                 // TODO: add element likely in the event
-                left: startCoordinates.pageX - 3,
-                top: startCoordinates.pageY,
-                right: endCoordinates.pageX - 3,
-                bottom: endCoordinates.pageY + editor.renderer.lineHeight
-              };
-
-              if (token.parseLocation && token.parseLocation.identifierChain && !token.notFound) {
-                token.parseLocation.resolveCatalogEntry().done(function (entry) {
-                  huePubSub.publish('context.popover.show', {
-                    data: {
-                      type: 'catalogEntry',
-                      catalogEntry: entry
-                    },
-                    pinEnabled: true,
-                    source: source
-                  });
-                }).fail(function () {
-                  token.notFound = true;
-                });
-              } else if (token.parseLocation && !token.notFound) {
-                // Asterisk, function etc.
-                if (token.parseLocation.type === 'file') {
-                  AssistStorageEntry.getEntry(token.parseLocation.path).done(function (entry) {
-                    entry.open(true);
-                    huePubSub.publish('context.popover.show', {
-                      data: {
-                        type: 'storageEntry',
-                        storageEntry: entry,
-                        editorLocation: token.parseLocation.location
-                      },
-                      pinEnabled: true,
-                      source: source
-                    });
-                  });
-                } else {
-                  huePubSub.publish('context.popover.show', {
-                    data: token.parseLocation,
-                    sourceType: snippet.type(),
-                    namespace: snippet.namespace(),
-                    compute: snippet.compute(),
-                    defaultDatabase: snippet.database(),
-                    pinEnabled: true,
-                    source: source
-                  });
-                }
-              } else if (token.syntaxError) {
-                huePubSub.publish('sql.syntax.dropdown.show', {
-                  snippet: snippet,
-                  data: token.syntaxError,
-                  editor: editor,
-                  range: range,
-                  sourceType: snippet.type(),
-                  defaultDatabase: snippet.database(),
-                  source: source
-                });
-              }
-              e.preventDefault();
-              return false;
-            }
-          }
-        };
-
-        var contextmenuListener = editor.container.addEventListener('contextmenu', onContextMenu);
-
-        disposeFunctions.push(function () {
-          editor.container.removeEventListener('contextmenu', contextmenuListener);
-        });
-
-      }());
-
       editor.previousSize = 0;
 
       // TODO: Get rid of this