text: // Licensed to Cloudera, Inc. under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  Cloudera, Inc. licenses this file
  // to you 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.
  
  function BeeswaxViewModel(server, assistHelper) {
    var self = this;
  
    var DESIGN_DEFAULTS = {
      'id': -1,
      'query': {
        'value': '',
        'errors': []
      },
      'name': null,
      'description': null,
      'settings': {
        'values': [],
        'errors': []
      },
      'fileResources': {
        'values': [],
        'errors': []
      },
      'functions': {
        'values': [],
        'errors': []
      },
      'parameters': [],
      'isParameterized': true,
      'email': false,
      'errors': [],
      'explain': false,
      'history': {
        'id': -1
      },
      'results': {
        'rows': [],
        'columns': [],
        'empty': true,
        'expired': false,
        'explanation': null,
        'url': null,
        'errors': [],
        'save': {
          'errors': null,
          'type': 'hdfs-file',
          'path': null,
          'overwrite': true
        }
      },
      'watch': {
        'logs': [],
        'jobUrls': [],
        'url': null,
        'errors': []
      },
      'isRunning': false,
      'statement': '',
      'isFinished': true,
      'isRedacted': false
    };
  
    self.database = ko.observable(null);
  
    var type = server === "beeswax" ? "hive" : "impala";
    huePubSub.subscribe("assist.db.panel.ready", function () {
      // Defer to let the db be set
      window.setTimeout(function () {
        huePubSub.publish('assist.set.database', {
          source: type,
          name: self.database()
        });
      }, 0);
    });
  
    huePubSub.subscribe("assist.database.selected", function (database) {
      if (database.sourceType === type && self.database() !== database.name) {
        self.database(database.name);
      }
    });
  
    huePubSub.subscribe("assist.database.set", function (database) {
      if (database.source === type && self.database() !== database.name) {
        self.database(database.name);
      }
    });
  
    self.design = ko.mapping.fromJS(DESIGN_DEFAULTS);
  
    self.design.inlineErrors = ko.computed(function() {
      return ko.utils.arrayFilter(self.design.errors(), function(err) {
          return err && err.toLowerCase().indexOf("line") > -1;
      });
    });
  
    self.design.watch.inlineErrors = ko.computed(function() {
      return ko.utils.arrayFilter(self.design.watch.errors(), function(err) {
          return err && err.toLowerCase().indexOf("line") > -1;
      });
    });
  
    self.impalaSessionLink = ko.observable("");
  
    self.isEditor = ko.observable(true);
  
    self.chartType = ko.observable("bars");
    self.chartSorting = ko.observable("none");
    self.chartData = ko.observableArray();
  
    self.server = ko.observable(server);
    self.isReady = ko.observable(false);
    // Use a view model attribute so that we don't have to override KO.
    // This allows Hue to disable the execute button until the query placeholder dies.
    self.queryEditorBlank = ko.observable(false);
    self.scrollNotWorking = ko.observable(true);
  
    self.canExecute = ko.computed(function() {
      return !self.design.isRunning() && self.design.isFinished();
    });
  
    self.hasMoreResults = ko.computed(function() {
      return self.design.results.url() != null;
    });
  
    self.design.results.save.saveTargetError = ko.computed(function() {
      return (self.design.results.save.errors() && 'save_target' in self.design.results.save.errors()) ? self.design.results.save.errors()['save_target'] : null;
    });
  
    self.design.results.save.targetTableError = ko.computed(function() {
      return (self.design.results.save.errors() && 'target_table' in self.design.results.save.errors()) ? self.design.results.save.errors()['target_table'] : null;
    });
  
    self.design.results.save.targetDirectoryError = ko.computed(function() {
      return (self.design.results.save.errors() && 'target_dir' in self.design.results.save.errors()) ? self.design.results.save.errors()['target_dir'] : null;
    });
  
    self.design.results.save.targetFileError = ko.computed(function() {
      return (self.design.results.save.errors() && 'target_file' in self.design.results.save.errors()) ? self.design.results.save.errors()['target_file'] : null;
    });
  
    self.hasParametersFilled = ko.computed(function() {
      var hasBlank = false;
      $.each(self.design.parameters(), function(index, element) {
        if (element.value() == '') {
          hasBlank = true;
        }
      });
      return ! hasBlank;
    });
  
    self.hasResults = ko.computed(function() {
      return !self.design.results.empty() && !self.design.results.expired();
    });
  
    self.resetQuery = function() {
      ko.mapping.fromJS(QUERY_DEFAULTS, self.design);
    };
  
    self.resetErrors = function() {
      self.design.errors.removeAll();
      self.design.query.errors.removeAll();
      self.design.settings.errors.removeAll();
      self.design.fileResources.errors.removeAll();
      self.design.functions.errors.removeAll();
    };
  
    self.setErrors = function(message, errors) {
      self.resetErrors();
      if (message) {
        self.design.errors.push(message);
      }
      if (errors) {
        self.design.query.errors.push.apply(self.design.query.errors, errors.query);
        self.design.settings.errors.push.apply(self.design.settings.errors, errors.settings);
        self.design.fileResources.errors.push.apply(self.design.fileResources.errors, errors.file_resources);
        self.design.functions.errors.push.apply(self.design.functions.errors, errors.functions);
      }
    };
  
    self.updateDesign = function(design) {
      self.design.query.value(design.query);
      self.design.id(design.id);
      self.design.name(design.name);
      self.design.description(design.desc);
      self.database(design.database);
      if (typeof design.database !== 'undefined' && design.database !== null) {
        huePubSub.publish('assist.set.database', {
          source: type,
          name: design.database
        });
      }
  
      self.design.isParameterized(design.is_parameterized);
      self.design.email(design.email_notify);
      self.design.isRedacted(design.is_redacted);
  
      self.design.settings.values.removeAll();
      self.design.fileResources.values.removeAll();
      self.design.functions.values.removeAll();
  
      $.each(design.settings, function(index, setting) {
        self.addSetting(setting.key, setting.value);
      });
      $.each(design.file_resources, function(index, file_resource) {
        self.addFileResource(file_resource.type, file_resource.path);
      });
      $.each(design.functions, function(index, _function) {
        self.addFunction(_function.name, _function.class_name);
      });
    };
  
    self.updateHistory = function(history) {
      self.design.history.id(history.id);
      self.design.results.url('/' + self.server() + '/results/' + history.id + '/0?format=json');
      self.design.watch.url('/' + self.server() + '/api/watch/json/' + history.id);
      if (history.design) {
        self.updateDesign(history.design);
      }
    };
  
    self.updateParameters = function(parameters) {
      self.design.parameters.removeAll();
      $.each(parameters, function(index, parameter) {
        self.addParameter(parameter.parameter, parameter.name, '');
      });
    };
  
    self.addParameter = function(parameter, name, value) {
      var obj = {
        'parameter': ko.observable(parameter),
        'name': ko.observable(name),
        'value': ko.observable(value)
      };
      obj.parameter.subscribe(function() {
        self.design.parameters.valueHasMutated();
      });
      obj.name.subscribe(function() {
        self.design.parameters.valueHasMutated();
      });
      obj.value.subscribe(function() {
        self.design.parameters.valueHasMutated();
      });
      self.design.parameters.push(obj);
    };
  
    self.addSetting = function(key, value) {
      var obj = {
        'key': ko.observable(key),
        'value': ko.observable(value)
      };
      obj.key.subscribe(function() {
        self.design.settings.values.valueHasMutated();
      });
      obj.value.subscribe(function() {
        self.design.settings.values.valueHasMutated();
      });
      self.design.settings.values.push(obj);
    };
  
    self.removeSetting = function(index) {
      self.design.settings.values.splice(index, 1);
    };
  
    self.addFileResource = function(type, path) {
      var obj = {
        'type': ko.observable(type),
        'path': ko.observable(path)
      };
      obj.type.subscribe(function() {
        self.design.fileResources.values.valueHasMutated();
      });
      obj.path.subscribe(function() {
        self.design.fileResources.values.valueHasMutated();
      });
      self.design.fileResources.values.push(obj);
    };
  
    self.removeFileResource = function(index) {
      self.design.fileResources.values.splice(index, 1);
    };
  
    self.addFunction = function(name, class_name) {
      var obj = {
        'name': ko.observable(name),
        'class_name': ko.observable(class_name)
      };
      obj.name.subscribe(function() {
        self.design.functions.values.valueHasMutated();
      });
      obj.class_name.subscribe(function() {
        self.design.functions.values.valueHasMutated();
      });
      self.design.functions.values.push(obj);
    };
  
    self.removeFunction = function(index) {
      self.design.functions.values.splice(index, 1);
    };
  
    var advancedErrorHandling = function(advanced_parameter, key) {
      return function(index) {
        var errors = self.design[advanced_parameter].errors();
        if (errors && errors[index]) {
          if (key in errors[index]) {
            return errors[index][key];
          } else {
            return [];
          }
        } else {
          return [];
        }
      };
    };
  
    self.getQueryErrors = advancedErrorHandling('query', 'query');
  
    self.getSettingKeyErrors = advancedErrorHandling('settings', 'key');
    self.getSettingValueErrors = advancedErrorHandling('settings', 'value');
  
    self.getFileResourceTypeErrors = advancedErrorHandling('fileResources', 'type');
    self.getFileResourcePathErrors = advancedErrorHandling('fileResources', 'path');
  
    self.getFunctionNameErrors = advancedErrorHandling('functions', 'name');
    self.getFunctionClassNameErrors = advancedErrorHandling('functions', 'class_name');
  
    function getMultiFormData(prefix, arr, members) {
      var data = {};
      var index = 0;
      data[prefix + '-next_form_id'] = arr.length;
      ko.utils.arrayForEach(arr, function(obj) {
        $.each(members, function(i, member) {
          data[prefix + '-' + index + '-' + member] = obj[member]();
        });
        data[prefix + '-' + index + '-_exists'] = true;
        data[prefix + '-' + index++ + '-_deleted'] = false;
      });
      return data;
    }
  
    self.getSettingsFormData = function() {
      return getMultiFormData('settings', self.design.settings.values(), ['key', 'value']);
    };
  
    self.getFileResourcesFormData = function() {
      return getMultiFormData('file_resources', self.design.fileResources.values(), ['type', 'path']);
    };
  
    self.getFunctionsFormData = function() {
      return getMultiFormData('functions', self.design.functions.values(), ['name', 'class_name']);
    };
  
    self.getParametersFormData = function() {
      var data = {};
      $.each(self.design.parameters(), function(index, parameter) {
        data[parameter.parameter()] = parameter.value();
      });
      return data;
    };
  
    self.getOtherData = function() {
      var data = {
        'query-email_notify': self.design.email(),
        'query-is_parameterized': self.design.isParameterized()
      };
      return data;
    };
  
    var error_fn = function(jqXHR, status, errorThrown) {
      self.design.isRunning(false);
      try {
        $(document).trigger('server.error', $.parseJSON(jqXHR.responseText));
      } catch(e) {
        $(document).trigger('server.unmanageable_error', jqXHR.responseText);
      }
    };
  
    self.fetchingImpalaSession = ko.observable(false);
    self.fetchImpalaSession = function () {
      self.fetchingImpalaSession(true);
      var request = {
        url: '/impala/api/session/',
        dataType: 'json',
        type: 'GET',
        success: function (data) {
          if (data && data.properties && data.properties.http_addr) {
            if (!data.properties.http_addr.match(/^(https?):\/\//)) {
              data.properties.http_addr = window.location.protocol + "//" + data.properties.http_addr;
            }
            self.impalaSessionLink(data.properties.http_addr);
          }
          else {
            self.impalaSessionLink("");
          }
          self.fetchingImpalaSession(false);
        },
        error: error_fn,
        cache: false
      };
      $.ajax(request);
    }
  
    self.fetchDesign = function() {
      $(document).trigger('fetch.design');
  
      var request = {
        url: '/' + self.server() + '/api/design/' + self.design.id() + '/get',
        dataType: 'json',
        type: 'GET',
        success: function(data) {
          self.updateDesign(data.design);
          self.database(data.design.database);
          $(document).trigger('fetched.design', [data]);
        },
        error: error_fn,
        cache: false
      };
      $.ajax(request);
    };
  
    self.fetchQueryHistory = function() {
      $(document).trigger('fetch.query');
  
      var request = {
        url: '/' + self.server() + '/api/query/' + self.design.history.id() + '/get',
        dataType: 'json',
        type: 'GET',
        success: function(data) {
          self.updateHistory(data.query_history);
          if (data.query_history.database) {
            self.database(data.query_history.database);
          }
          $(document).trigger('fetched.query', [data]);
        },
        error: error_fn,
        cache: false
      };
      $.ajax(request);
    };
  
    self.clearQueryHistory = function() {
      var request = {
        url: '/' + self.server() + '/api/query/clear_history/',
        dataType: 'json',
        type: 'POST',
        success: function(data) {
          $(document).trigger('clear.history', [data]);
        },
        error: error_fn,
        cache: false
      };
      $.ajax(request);
    };
  
    self.fetchParameters = function() {
      $(document).trigger('fetch.parameters');
  
      var data = {
        'query-query': self.design.query.value(),
        'query-database': self.database()
      };
      $.extend(data, self.getSettingsFormData());
      $.extend(data, self.getFileResourcesFormData());
      $.extend(data, self.getFunctionsFormData());
      $.extend(data, self.getParametersFormData());
      $.extend(data, self.getOtherData());
      var request = {
        url: '/' + self.server() + '/api/query/parameters',
        dataType: 'json',
        type: 'POST',
        success: function(data) {
          self.updateParameters(data.parameters);
          $(document).trigger('fetched.parameters');
        },
        error: error_fn,
        data: data,
        cache: false
      };
      $.ajax(request);
    };
  
    self.explainQuery = function() {
      $(document).trigger('explain.query', data);
      self.design.explain(true);
      self.design.isRunning(true);
      self.design.isFinished(true);
      self.design.results.expired(false);
      self.design.results.empty(false);
      self.resetErrors();
  
      var data = {
        'query-query': self.design.query.value(),
        'query-database': self.database()
      };
      $.extend(data, self.getSettingsFormData());
      $.extend(data, self.getFileResourcesFormData());
      $.extend(data, self.getFunctionsFormData());
      $.extend(data, self.getParametersFormData());
      $.extend(data, self.getOtherData());
      var request = {
        url: '/' + self.server() + '/api/query/execute/?explain=true',
        dataType: 'json',
        type: 'POST',
        success: function(data) {
          if (data.status == 0) {
            self.design.watch.logs.removeAll();
            self.design.watch.jobUrls.removeAll();
            self.design.statement(data.statement);
            self.design.results.rows.removeAll();
            self.design.results.columns.removeAll();
            self.design.results.explanation(data.explanation);
          } else {
            self.setErrors(data.message);
            $(document).trigger('error.query');
          }
          $(document).trigger('explained.query', data);
          self.design.isRunning(false);
        },
        error: error_fn,
        data: data,
        cache: false
      };
      $.ajax(request);
    };
  
    self.executeQuery = function() {
      $(document).trigger('execute.query', data);
      self.design.explain(false);
      self.design.isRunning(true);
      self.design.isFinished(true);
      self.design.results.expired(false);
      self.design.results.empty(false);
      self.resetErrors();
  
      var data = {
        'query-query': self.design.query.value(),
        'query-database': self.database()
      };
      $.extend(data, self.getSettingsFormData());
      $.extend(data, self.getFileResourcesFormData());
      $.extend(data, self.getFunctionsFormData());
      $.extend(data, self.getParametersFormData());
      $.extend(data, self.getOtherData());
      var url = '/' + self.server() + '/api/query/execute/';
      if (self.design.id() > -1) {
        url += self.design.id();
      }
      var request = {
        url: url,
        dataType: 'json',
        type: 'POST',
        success: function(data) {
          self.design.errors.removeAll();
          self.design.watch.errors.removeAll();
          if (data.status == 0) {
            hueUtils.changeURL('/' + self.server() + '/execute/query/' + data.id + '#query/logs');
            self.design.results.url('/' + self.server() + '/results/' + data.id + '/0?format=json');
            self.design.watch.url(data.watch_url);
            self.design.statement(data.statement);
            self.design.history.id(data.id);
            self.design.isRedacted(data.is_redacted);
            self.watchQueryLoop();
            $(document).trigger('executed.query', data);
          } else {
            self.setErrors(data.message, data.errors);
            self.design.isRunning(false);
            $(document).trigger('error.query');
          }
        },
        error: error_fn,
        data: data,
        cache: false
      };
      $.ajax(request);
    };
  
    self.executeNextStatement = function() {
      $(document).trigger('execute.query', data);
      self.design.explain(false);
      self.design.isRunning(true);
      self.design.isFinished(true);
      self.resetErrors();
  
      var data = {
        'next': true,
        'query-query': self.design.query.value(),
      };
      var request = {
        url: self.design.watch.url(),
        dataType: 'json',
        type: 'POST',
        success: function(data) {
          if (data.status == 0) {
            self.design.watch.logs.removeAll();
            self.design.watch.jobUrls.removeAll();
            self.design.statement(data.statement);
            self.design.watch.url(data.watch_url);
            self.design.results.url('/' + self.server() + '/results/' + data.id + '/0?format=json');
            self.watchQueryLoop();
            $(document).trigger('executed.query', data);
          } else {
            self.setErrors(data.message, data.errors);
            self.design.isRunning(false);
            self.design.isFinished(false); // We propose to skip the failed statement
            $(document).trigger('error.query');
          }
        },
        error: error_fn,
        data: data,
        cache: false
      };
      $.ajax(request);
    };
  
    self.watchQuery = function() {
      var data = {
        'query-query': self.design.query.value(),
        'query-database': self.database(),
        'log-start-over': self.design.watch.logs().length == 0
      };
      $.extend(data, self.getSettingsFormData());
      $.extend(data, self.getFileResourcesFormData());
      $.extend(data, self.getFunctionsFormData());
      $.extend(data, self.getParametersFormData());
      $.extend(data, self.getOtherData());
      var request = {
        url: self.design.watch.url(),
        dataType: 'json',
        type: 'POST',
        success: function(data) {
          $(document).trigger('watched.query', data);
        },
        error: function(jqXHR, status, errorThrown) {
          self.design.isRunning(false);
          try {
            var data = $.parseJSON(jqXHR.responseText);
            self.design.watch.errors.push(data.error);
          } catch(e) {
            $(document).trigger('server.unmanageable_error', jqXHR.responseText);
          }
        },
        data: data,
        cache: false
      };
      $.ajax(request);
    };
  
    self.shouldAppendLogs = false;
  
    self.applyLogs = function(log) {
      var lines = log.split("\n")
  
      if (self.shouldAppendLogs) {
        lines = self.design.watch.logs().concat(lines);
      }
      self.design.watch.logs(lines);
    };
  
    self.watchQueryLoop = function(fn) {
      var TIMEOUT = 100;
      var timer = null;
  
      self.design.isRunning(true);
      self.design.watch.logs.removeAll();
      self.design.watch.jobUrls.removeAll();
      self.design.results.rows.removeAll();
      self.design.results.columns.removeAll();
      self.resetErrors();
  
      var _fn = function() {
        $(document).one('watched.query', function(e, data) {
          var failed = data.isFailure  || data.status != 0;
          if (data.isSuccess || failed) {
            clearTimeout(timer);
            self.design.isRunning(false);
            if (data.log) {
              self.applyLogs(data.log)
              // scroll logs
              if (data.oldLogsApi) {
                self.design.watch.jobUrls(data.jobUrls);
              } else {
                $.each(data.jobUrls, function (i, url) {
                  self.design.watch.jobUrls.push(url);
                });
              }
            }
            if (! failed) {
              $(document).trigger('stop_watch.query');
              if (fn) {
                fn(data);
              } else {
                self.fetchResults();
              }
            }
            else {
              self.design.watch.errors.push(data.message);
              $(document).trigger('stop_watch.query');
            }
          } else {
            self.design.statement(data.statement); // In case new no result statement executed
            if (data.log) {
              self.applyLogs(data.log)
              // scroll logs
              if (data.oldLogsApi) {
                self.design.watch.jobUrls(data.jobUrls);
              } else {
                $.each(data.jobUrls, function (i, url) {
                  self.design.watch.jobUrls.push(url);
                });
              }
            }
  
            TIMEOUT = Math.min(TIMEOUT + 100, 2000);
            timer = setTimeout(_fn, TIMEOUT);
          }
        });
        self.watchQuery();
      };
      $(document).trigger('start_watch.query');
      timer = setTimeout(_fn, TIMEOUT);
    };
  
    self.isFetchingResults = ko.observable(false);
    self.fetchResults = function () {
      if (!self.isFetchingResults()) {
        self.isFetchingResults(true);
        $(document).trigger('fetch.results');
        self.design.errors.removeAll();
        self.design.results.errors.removeAll();
        var request = {
          url: self.design.results.url(),
          dataType: 'text',
          type: 'GET',
          success: function (data) {
            data = JSON.bigdataParse(data);
            if (data.traceback) {
              self.design.isRunning(false);
              $(document).trigger('server.unmanageable_error', data.traceback.length > 0 ? data.traceback[data.traceback.length - 1].join("\n") : "");
            }
            else if (data.error) {
              self.design.results.errors.push(data.message);
              self.design.isRunning(false);
              self.design.results.empty(true);
            }
            else {
              self.design.isRunning(false);
              self.design.isFinished(data.is_finished);
              if (self.design.results.columns().length == 0) {
                if (data.columns != null) {
                  data.columns.unshift({ type: "INT_TYPE", name: "", comment: null});
                }
                self.design.results.columns(data.columns ? data.columns : []); // Some querysets have empty or null for columns
              }
              self.design.results.rows.push.apply(self.design.results.rows, data.results);
              self.design.results.empty(self.design.results.rows().length == 0);
              if (data.has_more) {
                self.design.results.url(data.next_json_set);
              } else {
                self.design.results.url(null);
              }
            }
            self.isFetchingResults(false);
            if (!data.traceback) {
              $(document).trigger('fetched.results', [data]);
            }
          },
          error: function (jqXHR, status, errorThrown) {
            self.isFetchingResults(false);
            error_fn(jqXHR, status, errorThrown);
          },
          cache: false
        };
        $.ajax(request);
      }
    };
  
    self.saveDesign = function() {
      var self = this;
      if (self.design.query.value() && self.design.name()) {
        var data = {
          'query-query': self.design.query.value(),
          'query-database': self.database()
        };
        $.extend(data, self.getSettingsFormData());
        $.extend(data, self.getFileResourcesFormData());
        $.extend(data, self.getFunctionsFormData());
        $.extend(data, self.getParametersFormData());
        $.extend(data, self.getOtherData());
        data['saveform-name'] = self.design.name();
        data['saveform-desc'] = self.design.description();
        if (self.design.id() > 0) {
          data['query-id'] = self.design.id();
        }
        var url = '/' + self.server() + '/api/design/';
        if (self.design.id() && self.design.id() != -1) {
          url += self.design.id();
        }
        var request = {
          url: url,
          dataType: 'json',
          type: 'POST',
          success: function(data) {
            if (data.status == 0) {
              self.design.id(data.design_id);
              $(document).trigger('saved.design', [data.design_id]);
            } else {
              self.setErrors("", data.errors);
              $(document).trigger('error_save.design', [data.message ? data.message : data.errors]);
            }
          },
          error: function() {
            $(document).trigger('error_save.design');
          },
          data: data,
          cache: false
        };
        $.ajax(request);
      }
    };
  
    self.cancelQuery = function() {
      if (self.design.history.id() > 0) {
        $(document).trigger('cancel.query');
        var url = '/' + self.server() + '/api/query/' + self.design.history.id() + '/cancel';
        $.post(url,
          function(response) {
            if (response['status'] != 0) {
              $(document).trigger('error_cancel.query', response['message']);
            } else {
              $(document).trigger('cancelled.query');
            }
          }
        );
      }
    };
  
    self.closeQuery = function() {
      var self = this;
      if (self.design.history.id() > 0 && !self.design.isRunning()) {
        var data = {};
        var url = '/' + self.server() + '/api/query/' + self.design.history.id() + '/close';
        var request = {
          url: url,
          dataType: 'json',
          type: 'POST',
          success: function(data) {
            if (data.status == 0) {
              $(document).trigger('closed.query', data);
              //self.resetQuery(); // Would fail multiqueries
            } else {
              $(document).trigger('error_close.query');
            }
          },
          error: function(data) {
            $(document).trigger('error_close.results');
          },
          data: data,
          async: false, // for multi query
          cache: false
        };
        $.ajax(request);
      }
    };
  
    self.saveResults = function() {
      var self = this;
  
      self.design.isRunning(true);
      self.resetErrors();
      if (self.design.id()) {
        var data, url;
  
        switch(self.design.results.save.type()) {
          case 'hdfs-directory':
          data = {
            'server': self.server(),
            'path': self.design.results.save.path()
          };
          url = '/' + self.server() + '/api/query/' + self.design.history.id() + '/results/save/hdfs/directory';
          break;
  
          case 'hdfs-file':
          data = {
            'server': self.server(),
            'path': self.design.results.save.path(),
            'overwrite': self.design.results.save.overwrite()
          };
          url = '/' + self.server() + '/api/query/' + self.design.history.id() + '/results/save/hdfs/file';
          break;
  
          // case 'hive-table':
          default:
          data = {
            'database': self.database(),
            'server': self.server(),
            'table': self.design.results.save.path()
          };
          url = '/' + self.server() + '/api/query/' + self.design.history.id() + '/results/save/hive/table';
          break;
        }
        var request = {
          url: url,
          dataType: 'json',
          type: 'POST',
          success: function(data) {
            if (data.status == 0) {
              self.design.results.save.errors(null);
  
              var redirect_fn = function() {
                window.location.href = data.success_url + (data.success_url.indexOf("?") > -1 ? "&" : "?") + "refresh=true";
                self.design.isRunning(false);
              };
              if (data.id) {
                // watch this ID.
                self.design.watch.url(data.watch_url);
                self.design.watch.logs.removeAll();
                self.design.watch.jobUrls.removeAll();
                self.watchQueryLoop(redirect_fn);
              } else {
                // redirect to metastore app.
                redirect_fn();
              }
              $(document).trigger('saved.results', data);
            } else {
              self.design.isRunning(false);
              self.design.results.save.errors(data.errors);
              $(document).trigger('error_save.results', [data.message]);
            }
          },
          error: function(data) {
            self.design.isRunning(false);
            self.design.results.save.errors(data);
            $(document).trigger('error_save.results', [data]);
          },
          data: data,
          cache: false
        };
        return $.ajax(request);
      }
    };
  
    // Events
    // Remove watched query event that watchQueryLoop may be bound to.
    $(document).on('server.unmanageable_error', function() {
      $(document).off('watched.query');
    });
  }
  
  
  // For routie
  function showSection(section) {
    $('.section').hide();
    $('#' + section).show();
  }
  
  
  // File browser button
  function getFileBrowseButton(inputElement) {
    return $("<button>").addClass("btn").addClass("fileChooserBtn").text("..").click(function (e) {
      e.preventDefault();
      $("#filechooser").jHueFileChooser({
        initialPath: inputElement.val(),
        onFileChoose: function (filePath) {
          inputElement.val(filePath);
          inputElement.trigger("change");
          $("#chooseFile").modal("hide");
        },
        selectFolder: false,
        createFolder: false
      });
      $("#chooseFile").modal("show");
    });
  }
  
  function folderChooser(inputElement) {
    $("#folderchooser").jHueFileChooser({
      initialPath: inputElement.val(),
      onFolderChoose:function (folderPath) {
        inputElement.val(folderPath);
        inputElement.trigger("change");
        $("#chooseFolder").modal("hide");
      },
      selectFolder: true,
      createFolder: true,
      selectFile: false,
      uploadFile: false
    });
    $("#chooseFolder").modal("show");
  }
  
  function getFolderBrowseButton(inputElement) {
    return $("<button>").addClass("btn").addClass("fileChooserBtn").text("..").click(function (e) {
      e.preventDefault();
      folderChooser(inputElement);
    });
  }
  
  function pathChooser(inputElement) {
    $("#pathchooser").jHueFileChooser({
      initialPath: inputElement.val(),
      onFolderChoose:function (folderPath) {
        if (folderPath[folderPath.length - 1] != '/') {
          folderPath += '/';
        }
        inputElement.val(folderPath + 'results.csv');
        inputElement.trigger("change");
        $("#choosePath").modal("hide");
      },
      onFileChoose: function (filePath) {
        inputElement.val(filePath);
        inputElement.trigger("change");
        $("#choosePath").modal("hide");
      },
      selectFolder: true,
      createFolder: true,
      selectFile: true,
      uploadFile: false
    });
    $("#choosePath").modal("show");
  }
  
  function getPathBrowseButton(inputElement) {
    return $("<button>").addClass("btn").addClass("fileChooserBtn").text("..").click(function (e) {
      e.preventDefault();
      pathChooser(inputElement);
    });
  }
  
  
  // utils
  function clickHard(el) {
    var timer = setInterval(function () {
      if ($(el).length > 0) {
        $(el).click();
        clearInterval(timer);
      }
    }, 100);
  }
  
  function logGA(page) {
    if (typeof trackOnGA == 'function'){
      trackOnGA('beeswax/' + page);
    }
  }
  