(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.main = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


var api = require('./api');


module.exports = {
  template: '#admin-view-template',
  props: ['config', 'state'],


  data: function () {
    return {
      configRestored: false,
      confirmReset: false,
      configReset: false,
      hostnameSet: false,
      usernameSet: false,
      passwordSet: false,
      redirectTimeout: 0,
      latest: '',
      hostname: '',
      username: '',
      current: '',
      password: '',
      password2: '',
      autoCheckUpgrade: true,
    }
  },


  events: {
    connected: function () {
      if (this.firmwareUpgrading) location.reload(true);
    },

    latest_version: function (version) {this.latest = version}
  },


  ready: function () {
    this.autoCheckUpgrade = this.config.admin['auto-check-upgrade']

    api.get('hostname').done(function (hostname) {
      this.hostname = hostname;
    }.bind(this));

    api.get('remote/username').done(function (username) {
      this.username = username;
    }.bind(this));
  },


  methods: {
    redirect: function (hostname) {
      if (0 < this.redirectTimeout) {
        this.redirectTimeout -= 1;
        setTimeout(function () {this.redirect(hostname)}.bind(this), 1000);

      } else {
        location.hostname = hostname;
        this.hostnameSet = false;
      }
    },


    set_hostname: function () {
      api.put('hostname', {hostname: this.hostname}).done(function () {
        this.redirectTimeout = 45;
        this.hostnameSet = true;

        api.put('reboot').always(function () {
          var hostname = this.hostname;
          if (String(location.hostname).endsWith('.local'))
            hostname += '.local';
          this.redirect(hostname);
        }.bind(this));

      }.bind(this)).fail(function (error) {
        alert('Set hostname failed: ' + JSON.stringify(error));
      })
    },


    set_username: function () {
      api.put('remote/username', {username: this.username}).done(function () {
        this.usernameSet = true;
      }.bind(this)).fail(function (error) {
        alert('Set username failed: ' + JSON.stringify(error));
      })
    },


    set_password: function () {
      if (this.password != this.password2) {
        alert('Passwords to not match');
        return;
      }

      if (this.password.length < 6) {
        alert('Password too short');
        return;
      }

      api.put('remote/password', {
        current: this.current,
        password: this.password
      }).done(function () {
        this.passwordSet = true;
      }.bind(this)).fail(function (error) {
        alert('Set password failed: ' + JSON.stringify(error));
      })
    },


    backup: function () {
      document.getElementById('download-target').src = '/api/config/download';
    },


    restore: function (e) {
      var files = e.target.files || e.dataTransfer.files;
      if (!files.length) return;

      var fr = new FileReader();
      fr.onload = function (e) {
        var config;

        try {
          config = JSON.parse(e.target.result);
        } catch (ex) {
          alert("Invalid config file");
          return;
        }

        api.put('config/save', config).done(function (data) {
          this.$dispatch('update');
          this.configRestored = true;

        }.bind(this)).fail(function (error) {
          alert('Restore failed: ' + error);
        })
      }.bind(this);

      fr.readAsText(files[0]);
    },


    reset: function () {
      this.confirmReset = false;
      api.put('config/reset').done(function () {
        this.$dispatch('update');
        this.configReset = true;

      }.bind(this)).fail(function (error) {
        alert('Reset failed: ' + error);
      });
    },


    check: function () {this.$dispatch('check')},
    upgrade: function () {this.$dispatch('upgrade')},


    upload: function (e) {
      var files = e.target.files || e.dataTransfer.files;
      if (!files.length) return;
      this.$dispatch('upload', files[0]);
    },


    change_auto_check_upgrade: function () {
      this.config.admin['auto-check-upgrade'] = this.autoCheckUpgrade;
      this.$dispatch('config-changed');
    }
  }
}

},{"./api":2}],2:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


function api_cb(method, url, data, config) {
  config = $.extend({
    type: method,
    url: '/api/' + url,
    dataType: 'json',
    cache: false
  }, config);

  if (typeof data == 'object') {
    config.data = JSON.stringify(data);
    config.contentType = 'application/json; charset=utf-8';
  }

  var d = $.Deferred();

  $.ajax(config).success(function (data, status, xhr) {
    d.resolve(data, status, xhr);

  }).error(function (xhr, status, error) {
    var text = xhr.responseText;
    try {text = $.parseJSON(xhr.responseText)} catch(e) {}
    d.reject(text, xhr, status, error);
    console.debug('API Error: ' + url + ': ' + xhr.responseText);
  })

  return d.promise();
}


module.exports = {
  get: function (url, config) {
    return api_cb('GET', url, undefined, config);
  },


  put: function(url, data, config) {
    return api_cb('PUT', url, data, config);
  },


  post: function(url, data, config) {
    return api_cb('POST', url, data, config);
  },


  upload: function(url, data, config) {
    config = $.extend({
      processData: false,
      contentType: false,
      cache: false,
      data: data
    }, config);

    return api_cb('PUT', url, undefined, config);
  },


  'delete': function (url, config) {
    return api_cb('DELETE', url, undefined, config);
  }
}

},{}],3:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'

var api = require('./api');
var Sock = require('./sock');


function compare_versions(a, b) {
    var reStripTrailingZeros = /(\.0+)+$/;
    var segsA = a.replace(reStripTrailingZeros, '').split('.');
    var segsB = b.replace(reStripTrailingZeros, '').split('.');
    var l = Math.min(segsA.length, segsB.length);

    for (var i = 0; i < l; i++) {
      var diff = parseInt(segsA[i], 10) - parseInt(segsB[i], 10);
      if (diff) return diff;
    }

    return segsA.length - segsB.length;
}


module.exports = new Vue({
  el: 'body',


  data: function () {
    return {
      status: 'connecting',
      currentView: 'loading',
      index: -1,
      modified: false,
      template: {motors: {}, axes: {}},
      config: {motors: [{}]},
      state: {},
      messages: [],
      errorTimeout: 30,
      errorTimeoutStart: 0,
      errorShow: false,
      errorMessage: '',
      confirmUpgrade: false,
      confirmUpload: false,
      firmwareUpgrading: false,
      checkedUpgrade: false,
      firmwareName: '',
      latestVersion: '',
      password: ''
    }
  },


  components: {
    'estop': {template: '#estop-template'},
    'loading-view': {template: '<h1>Loading...</h1>'},
    'control-view': require('./control-view'),
    'motor-view': require('./motor-view'),
    'tool-view': require('./tool-view'),
    'io-view': require('./io-view'),
    'gcode-view': require('./gcode-view'),
    'admin-view': require('./admin-view'),
    'help-view': {template: '#help-view-template'}
  },


  events: {
    'config-changed': function () {this.modified = true;},


    send: function (msg) {
      if (this.status == 'connected') {
        console.debug('>', msg);
        this.sock.send(msg);
      }
    },


    connected: function () {this.update()},
    update: function () {this.update()},


    check: function () {
      this.latestVersion = '';

      $.ajax({
        type: 'GET',
        url: 'https://buildbotics.com/bbctrl/latest.txt',
        data: {hid: this.state.hid},
        cache: false

      }).done(function (data) {
        this.latestVersion = data;
        this.$broadcast('latest_version', data);
      }.bind(this))
    },


    upgrade: function () {
      this.password = '';
      this.confirmUpgrade = true;
    },


    upload: function (firmware) {
      this.firmware = firmware;
      this.firmwareName = firmware.name;
      this.password = '';
      this.confirmUpload = true;
    },


    error: function (msg) {
      // Honor user error blocking
      if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000)
        return;

      // Wait at least 1 sec to pop up repeated errors
      if (1 < msg.repeat && Date.now() - msg.ts < 1000) return;

      // Popup error dialog
      this.errorShow = true;
      this.errorMessage = msg.msg;
    }
  },


  ready: function () {
    $(window).on('hashchange', this.parse_hash);
    this.connect();
  },


  methods: {
    block_error_dialog: function () {
      this.errorTimeoutStart = Date.now();
      this.errorShow = false;
    },


    estop: function () {
      if (this.state.xx == 'ESTOPPED') api.put('clear');
      else api.put('estop');
    },


    upgrade_confirmed: function () {
      this.confirmUpgrade = false;

      api.put('upgrade', {password: this.password}).done(function () {
        this.firmwareUpgrading = true;

      }.bind(this)).fail(function () {
        alert('Invalid password');
      }.bind(this))
    },


    upload_confirmed: function () {
      this.confirmUpload = false;

      var form = new FormData();
      form.append('firmware', this.firmware);
      if (this.password) form.append('password', this.password);

      $.ajax({
        url: '/api/firmware/update',
        type: 'PUT',
        data: form,
        cache: false,
        contentType: false,
        processData: false

      }).success(function () {
        this.firmwareUpgrading = true;

      }.bind(this)).error(function () {
        alert('Invalid password or bad firmware');
      }.bind(this))
    },


    show_upgrade: function () {
      if (!this.latestVersion) return false;
      return compare_versions(this.config.version, this.latestVersion) < 0;
    },


    update: function () {
      $.ajax({type: 'GET', url: '/config-template.json', cache: false})
        .success(function (data, status, xhr) {
          this.template = data;

          api.get('config/load').done(function (data) {
            this.config = data;
            this.parse_hash();

            if (!this.checkedUpgrade) {
              this.checkedUpgrade = true;

              var check = this.config.admin['auto-check-upgrade'];
              if (typeof check == 'undefined' || check)
                this.$emit('check');
            }
          }.bind(this))
        }.bind(this))
    },


    connect: function () {
      this.sock = new Sock('//' + window.location.host + '/sockjs');

      this.sock.onmessage = function (e) {
        var msg = e.data;

        if (typeof msg == 'object')
          for (var key in msg) {
            if (key == 'msg') this.$broadcast('message', msg.msg);
            else Vue.set(this.state, key, msg[key]);
          }
      }.bind(this)

      this.sock.onopen = function (e) {
        this.status = 'connected';
        this.$emit(this.status);
        this.$broadcast(this.status);
      }.bind(this)

      this.sock.onclose = function (e) {
        this.status = 'disconnected';
        this.$broadcast(this.status);
      }.bind(this)
    },


    parse_hash: function () {
      var hash = location.hash.substr(1);

      if (!hash.trim().length) {
        location.hash = 'control';
        return;
      }

      var parts = hash.split(':');

      if (parts.length == 2) this.index = parts[1];

      this.currentView = parts[0];
    },


    save: function () {
      api.put('config/save', this.config).done(function (data) {
        this.modified = false;
      }.bind(this)).fail(function (error) {
        alert('Save failed: ' + error);
      });
    }
  }
})

},{"./admin-view":1,"./api":2,"./control-view":6,"./gcode-view":7,"./io-view":9,"./motor-view":12,"./sock":13,"./tool-view":15}],4:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


module.exports = {
  template: '#axis-control-template',
  props: ['axes', 'colors', 'enabled'],


  methods: {
    jog: function (axis, power) {this.$dispatch('jog', this.axes[axis], power)},
    release: function (axis) {this.$dispatch('jog', this.axes[axis], 0)}
  }
}

},{}],5:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


function _msg_equal(a, b) {
  return a.level == b.level && a.location == b.location && a.code == b.code &&
    a.msg == b.msg;
}


// Shared among all instances
var messages = [];


module.exports = {
  template: '#console-template',


  data: function () {
    return {messages: messages}
  },


  events: {
    message: function (msg) {
      // There may be multiple instances of this module so ignore messages
      // that have already been processed.
      if (msg.logged) return;
      msg.logged = true;

      // Make sure we have a message level
      msg.level = msg.level || 'info';

      // Add to message log and count and collapse repeats
      var repeat = messages.length && _msg_equal(msg, messages[0]);
      if (repeat) messages[0].repeat++;
      else {
        msg.repeat = 1;
        messages.unshift(msg);
        while (256 < messages.length) messages.pop();
      }
      msg.ts = Date.now();

      // Write message to browser console for debugging
      var text = JSON.stringify(msg);
      if (msg.level == 'error' || msg.level == 'critical') console.error(text);
      else if (msg.level == 'warning') console.warn(text);
      else if (msg.level == 'debug' && console.debug) console.debug(text);
      else console.log(text);

      // Event on errors
      if (msg.level == 'error' || msg.level == 'critical')
        this.$dispatch('error', msg);
    }
  },


  methods: {
    clear: function () {messages.splice(0, messages.length);},
  }
}

},{}],6:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'

var api = require('./api');

var maxLines = 1000;
var pageSize = Math.round(maxLines / 10);


function _is_array(x) {
  return Object.prototype.toString.call(x) === '[object Array]';
}


function escapeHTML(s) {
  var entityMap = {'&': '&amp;', '<': '&lt;', '>': '&gt;'};
  return String(s).replace(/[&<>]/g, function (s) {return entityMap[s];});
}


module.exports = {
  template: '#control-view-template',
  props: ['config', 'template', 'state'],


  data: function () {
    return {
      mdi: '',
      last_file: '',
      files: [],
      axes: 'xyzabc',
      all_gcode: [],
      gcode: [],
      gcode_offset: 0,
      history: [],
      speed_override: 1,
      feed_override: 1,
      manual_home: {x: false, y: false, z: false, a: false, b: false, c: false},
      position_msg:
      {x: false, y: false, z: false, a: false, b: false, c: false},
      axis_position: 0,
      video_url: '',
      deleteGCode: false
    }
  },


  components: {
    'axis-control': require('./axis-control')
  },


  watch: {
    'state.line': function () {this.update_gcode_line()},
    'state.selected': function () {this.load()}
  },


  events: {
    jog: function (axis, power) {
      var data = {};
      data[axis] = power;
      api.put('jog', data);
    },

    connected: function () {this.update()}
  },


  ready: function () {
    this.update();
    this.load();
  },


  methods: {
    get_state: function () {
      if (typeof this.state.cycle != 'undefined' &&
          this.state.cycle != 'idle' && this.state.xx == 'RUNNING')
        return this.state.cycle.toUpperCase();
      return this.state.xx || ''
    },


    get_reason: function () {
      if (this.state.xx == 'ESTOPPED') return this.state.er;
      if (this.state.xx == 'HOLDING') return this.state.pr;
      return '';
    },


    highlight_reason: function () {return this.get_reason() != ''},
    send: function (msg) {this.$dispatch('send', msg)},


    get_axis_motor_id: function (axis) {
      axis = axis.toLowerCase();

      for (var i = 0; i < this.config.motors.length; i++) {
        var motor = this.config.motors[i];
        if (motor.axis.toLowerCase() == axis) return i;
      }

      return -1;
    },


    get_axis_motor: function (axis) {
      var motor = this.get_axis_motor_id(axis);
      if (motor != -1) return this.config.motors[motor];
    },


    get_axis_motor_param: function (axis, name) {
      var motor = this.get_axis_motor(axis);
      if (typeof motor == 'undefined') return;
      if (typeof motor[name] != 'undefined') return motor[name];

      for (var section in this.template.motors) {
        var sec = this.template.motors[section];
        if (typeof sec[name] != 'undefined') return sec[name]['default'];
      }
    },


    enabled: function (axis) {
      var pm = this.get_axis_motor_param(axis, 'power-mode')
      return typeof pm != 'undefined' && pm != 'disabled';
    },


    is_homed: function (axis) {
      var motor = this.get_axis_motor_id(axis);
      return motor != -1 && this.state[motor + 'homed'];
    },


    gcode_move_up: function (count) {
      var lines = 0;

      for (var i = 0; i < count; i++) {
        if (!this.gcode_offset) break;

        this.gcode.unshift(this.all_gcode[this.gcode_offset - 1])
        this.gcode.pop();
        this.gcode_offset--;
        lines++;
      }

      return lines;
    },


    gcode_move_down: function (count) {
      var lines = 0;

      for (var i = 0; i < count; i++) {
        if (this.all_gcode.length <= this.gcode_offset + this.gcode.length)
          break;

        this.gcode.push(this.all_gcode[this.gcode_offset + this.gcode.length])
        this.gcode.shift();
        this.gcode_offset++;
        lines++
      }

      return lines;
    },


    gcode_scroll: function (e) {
      if (this.gcode.length == this.all_gcode.length) return;

      var t = e.target;
      var percentScroll = t.scrollTop / (t.scrollHeight - t.clientHeight);

      var lines = 0;
      if (percentScroll < 0.2) lines = this.gcode_move_up(pageSize);
      else if (0.8 < percentScroll) lines = -this.gcode_move_down(pageSize);
      else return;

      if (lines) t.scrollTop += t.scrollHeight * lines / maxLines;
    },


    update_gcode_line: function () {
      if (typeof this.last_line != 'undefined') {
        $('#gcode-line-' + this.last_line).removeClass('highlight');
        this.last_line = undefined;
      }

      if (0 <= this.state.line) {
        var line = this.state.line - 1;

        // Make sure the current GCode is loaded
        if (line < this.gcode_offset ||
            this.gcode_offset + this.gcode.length <= line) {
          this.gcode_offset = line - pageSize;
          if (this.gcode_offset < 0) this.gcode_offset = 0;

          this.gcode = this.all_gcode.slice(this.gcode_offset, maxLines);
        }

        Vue.nextTick(function () {
          var e = $('#gcode-line-' + line);
          if (e.length)
            e.addClass('highlight')[0]
            .scrollIntoView({behavior: 'smooth'});

          this.last_line = line;
        }.bind(this));
      }
    },


    update: function () {
      // Update file list
      api.get('file').done(function (files) {this.files = files}.bind(this))
    },


    submit_mdi: function () {
      this.send(this.mdi);
      if (!this.history.length || this.history[0] != this.mdi)
        this.history.unshift(this.mdi);
      this.mdi = '';
    },


    load_history: function (index) {this.mdi = this.history[index];},


    open: function (e) {
      $('.gcode-file-input').click();
    },


    upload: function (e) {
      var files = e.target.files || e.dataTransfer.files;
      if (!files.length) return;

      var file = files[0];
      var fd = new FormData();

      fd.append('gcode', file);

      api.upload('file', fd)
        .done(function () {
          file.name;
          if (file.name == this.last_file) this.last_file = '';
          this.update();
        }.bind(this));
    },


    load: function () {
      var file = this.state.selected;
      if (file == this.last_file) return;

      api.get('file/' + file)
        .done(function (data) {
          this.all_gcode = data.trimRight().split(/\r?\n/);
          this.gcode = this.all_gcode.slice(0, maxLines);
          this.gcode_offset = 0;
          this.last_file = file;

          Vue.nextTick(this.update_gcode_line);
        }.bind(this));
    },


    deleteCurrent: function () {
      if (this.state.selected)
        api.delete('file/' + this.state.selected).done(this.update);
      this.deleteGCode = false;
    },


    deleteAll: function () {
      api.delete('file').done(this.update);
      this.deleteGCode = false;
    },


    home: function (axis) {
      if (typeof axis == 'undefined') api.put('home');

      else {
        if (this.get_axis_motor_param(axis, 'homing-mode') != 'manual')
          api.put('home/' + axis);
        else this.manual_home[axis] = true;
      }
    },


    set_home: function (axis, position) {
      this.manual_home[axis] = false;
      api.put('home/' + axis + '/set', {position: parseFloat(position)});
    },


    show_set_position: function (axis) {
      this.axis_position = 0;
      this.position_msg[axis] = true;
    },


    get_offset: function (axis) {return this.state['offset_' + axis] || 0},


    set_position: function (axis, position) {
      this.position_msg[axis] = false;
      api.put('position/' + axis, {'position': parseFloat(position)});
    },


    zero_axis: function (axis) {this.set_position(axis, 0)},


    start_pause: function () {
      if (this.state.xx == 'RUNNING') this.pause();

      else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING')
        this.unpause();

      else this.start();
    },


    start: function () {api.put('start')},
    pause: function () {api.put('pause')},
    unpause: function () {api.put('unpause')},
    optional_pause: function () {api.put('pause/optional')},
    stop: function () {api.put('stop')},
    step: function () {api.put('step')},


    override_feed: function () {api.put('override/feed/' + this.feed_override)},


    override_speed: function () {
      api.put('override/speed/' + this.speed_override)
    },


    current: function (axis, value) {
      var x = value / 32.0;
      if (this.state[axis + 'pl'] == x) return;

      var data = {};
      data[axis + 'pl'] = x;
      this.send(JSON.stringify(data));
    },


    load_video: function () {
      this.video_url = '//' + document.location.hostname + ':8000/stream/0?=' +
        Math.random();
    },


    reload_video: function () {
      if (typeof this.lastVideoReset != 'undefined' &&
          Date.now() - this.lastVideoReset < 15000) return;

      this.lastVideoReset = Date.now();
      api.put('video/reload');
      setTimeout(this.load_video, 4000);
    }
  }
}

},{"./api":2,"./axis-control":4}],7:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


module.exports = {
  template: '#gcode-view-template',
  props: ['config', 'template'],


  data: function () {
    return {
      gcode: {}
    }
  },


  events: {
    'input-changed': function() {
      this.$dispatch('config-changed');
      return false;
    }
  },


  ready: function () {this.update()},


  methods: {
    update: function () {
      Vue.nextTick(function () {
        if (this.config.hasOwnProperty('gcode'))
          this.gcode = this.config.gcode;

        var template = this.template.gcode;
        for (var key in template)
          if (!this.gcode.hasOwnProperty(key))
            this.$set('gcode["' + key + '"]', template[key].default);
      }.bind(this));
    }
  }
}

},{}],8:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


module.exports = {
  template: '#indicators-template',
  props: ['state'],


  methods: {
    is_motor_enabled: function (motor) {
      return typeof this.state[motor + 'pm'] != 'undefined' &&
        this.state[motor + 'pm'];
    },


    get_min_pin: function (motor) {
      switch (motor) {
      case 0: return 3;
      case 1: return 5;
      case 2: return 9;
      case 3: return 11;
      }
    },


    get_max_pin: function (motor) {
      switch (motor) {
      case 0: return 4;
      case 1: return 8;
      case 2: return 10;
      case 3: return 12;
      }
    },


    get_class: function (code) {
      if (typeof this.state[code] != 'undefined') {
        var state = this.state[code];

        if (state == 0 || state === false) return 'logic-lo fa-circle';
        if (state == 1 || state === true) return 'logic-hi fa-circle';
        if (state == 2) return 'logic-tri fa-circle-o';
      }

      return 'fa-exclamation-triangle warn';
    }
  }
}

},{}],9:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


module.exports = {
  template: '#io-view-template',
  props: ['config', 'template'],


  data: function () {
    return {
      switches: {},
      outputs: {}
    }
  },


  events: {
    'input-changed': function() {
      this.$dispatch('config-changed');
      return false;
    }
  },


  ready: function () {this.update()},


  methods: {
    update: function () {
      Vue.nextTick(function () {
        // Switches
        if (this.config.hasOwnProperty('switches'))
          this.switches = this.config.switches;
        else this.switches = {};

        var template = this.template.switches;
        for (var key in template)
          if (!this.switches.hasOwnProperty(key))
            this.$set('switches["' + key + '"]', template[key].default);

        // Outputs
        if (this.config.hasOwnProperty('outputs'))
          this.outputs = this.config.outputs;
        else this.outputs = {};

        template = this.template.outputs;
        for (key in template)
          if (!this.outputs.hasOwnProperty(key))
            this.$set('outputs["' + key + '"]', template[key].default);
      }.bind(this));
    }
  }
}

},{}],10:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict';


$(function() {
  // Vue debugging
  Vue.config.debug = true;
  //Vue.util.warn = function (msg) {console.debug('[Vue warn]: ' + msg)}

  // Register global components
  Vue.component('templated-input', require('./templated-input'));
  Vue.component('message', require('./message'));
  Vue.component('indicators', require('./indicators'));
  Vue.component('console', require('./console'));

  Vue.filter('percent', function (value, precision) {
    if (typeof precision == 'undefined') precision = 2;
    return (value * 100.0).toFixed(precision) + '%';
  });

  Vue.filter('fixed', function (value, precision) {
    if (typeof value == 'undefined') return '';
    return parseFloat(value).toFixed(precision)
  });

  Vue.filter('upper', function (value) {
    if (typeof value == 'undefined') return '';
    return value.toUpperCase()
  });

  // Vue app
  require('./app');
});

},{"./app":3,"./console":5,"./indicators":8,"./message":11,"./templated-input":14}],11:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


module.exports = {
  template: '#message-template',

  props: {
    show: {
      type: Boolean,
      required: true,
      twoWay: true
    }
  }
}

},{}],12:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


module.exports = {
  template: '#motor-view-template',
  props: ['index', 'config', 'template'],


  data: function () {
    return {
      active: false,
      motor: {}
    }
  },


  watch: {
    index: function() {this.update()}
  },


  events: {
    'input-changed': function() {
      this.slave_update();
      this.$dispatch('config-changed');
      return false;
    }
  },


  attached: function () {this.active = true; this.update()},
  detached: function () {this.active = false},


  methods: {
    slave_update: function () {
      var slave = false;
      for (var i = 0; i < this.index; i++)
        if (this.motor.axis == this.config.motors[i].axis)
          slave = true;

      var el = $(this.$el);

      if (slave) {
        el.find('.axis .units').text('(slave motor)');
        el.find('.limits, .homing, .motion').find('input, select')
          .attr('disabled', 1);
        el.find('.power-mode select').attr('disabled', 1);
        el.find('.motion .reverse input').removeAttr('disabled');

      } else {
        el.find('.axis .units').text('');
        el.find('input,select').removeAttr('disabled');
      }
    },


    update: function () {
      if (!this.active) return;

      Vue.nextTick(function () {
        if (this.config.hasOwnProperty('motors'))
          this.motor = this.config.motors[this.index];
        else this.motor = {};

        var template = this.template.motors;
        for (var category in template)
          for (var key in template[category])
            if (!this.motor.hasOwnProperty(key))
              this.$set('motor["' + key + '"]',
                        template[category][key].default);

        this.slave_update();
      }.bind(this));
    }
  }
}

},{}],13:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


var Sock = function (url, retry, timeout) {
  if (!(this instanceof Sock)) return new Sock(url, retry);

  if (typeof retry == 'undefined') retry = 2000;
  if (typeof timeout == 'undefined') timeout = 8000;

  this.url = url;
  this.retry = retry;
  this.timeout = timeout;

  this.connect();
}


Sock.prototype.onmessage = function () {}
Sock.prototype.onopen = function () {}
Sock.prototype.onclose = function () {}


Sock.prototype.connect = function () {
  console.debug('connecting to', this.url);
  this.close();

  this._sock = new SockJS(this.url);

  this._sock.onmessage = function (e) {
    console.debug('msg:', e.data);
    this._set_timeout();
    this.onmessage(e);
  }.bind(this);


  this._sock.onopen = function () {
    console.debug('connected');
    this._set_timeout();
    this.onopen();
  }.bind(this);


  this._sock.onclose = function () {
    console.debug('disconnected');
    this._cancel_timeout();

    this.onclose();
    if (typeof this._sock != 'undefined')
      setTimeout(this.connect.bind(this), this.retry);
  }.bind(this);
}


Sock.prototype._set_timeout = function () {
  this._cancel_timeout();
  this._timeout = setTimeout(this._timedout.bind(this), this.timeout);
}


Sock.prototype._timedout = function () {
  console.debug('connection timedout');
  this._timeout = undefined;
  this._sock.close();
}


Sock.prototype._cancel_timeout = function () {
  clearTimeout(this._timeout);
  this._timeout = undefined;
}


Sock.prototype.close = function () {
  if (typeof this._sock != 'undefined') {
    var sock = this._sock;
    this._sock = undefined;
    sock.close();
  }
}


Sock.prototype.send = function (msg) {
  this._sock.send(msg);
}


module.exports = Sock

},{}],14:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict'


module.exports = {
  replace: true,
  template: '#templated-input-template',
  props: ['name', 'model', 'template'],


  methods: {
    change: function () {
      this.$dispatch('input-changed');
    }
  }
}

},{}],15:[function(require,module,exports){
/******************************************************************************\

                 This file is part of the Buildbotics firmware.

                   Copyright (c) 2015 - 2018, Buildbotics LLC
                              All rights reserved.

      This file ("the software") is free software: you can redistribute it
      and/or modify it under the terms of the GNU General Public License,
       version 2 as published by the Free Software Foundation. You should
       have received a copy of the GNU General Public License, version 2
      along with the software. If not, see <http://www.gnu.org/licenses/>.

      The software is distributed in the hope that it will be useful, but
           WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
                 License along with the software.  If not, see
                        <http://www.gnu.org/licenses/>.

                 For information regarding this software email:
                   "Joseph Coffland" <joseph@buildbotics.com>

\******************************************************************************/

'use strict';


module.exports = {
  template: '#tool-view-template',
  props: ['config', 'template'],


  data: function () {
    return {
      tool: {},
      pwmSpindle: {},
      huanyangSpindle: {}
    }
  },


  events: {
    'input-changed': function() {
      this.$dispatch('config-changed');
      return false;
    }
  },


  ready: function () {this.update()},


  methods: {
    update: function () {
      Vue.nextTick(function () {
        // Tool
        if (this.config.hasOwnProperty('tool'))
          this.tool = this.config.tool;

        var template = this.template.tool;
        for (var key in template)
          if (!this.tool.hasOwnProperty(key))
            this.$set('tool["' + key + '"]', template[key].default);

        // PWM
        if (this.config.hasOwnProperty('pwm-spindle'))
          this.pwmSpindle = this.config['pwm-spindle'];

        template = this.template['pwm-spindle'];
        for (key in template)
          if (!this.pwmSpindle.hasOwnProperty(key))
            this.$set('pwmSpindle["' + key + '"]', template[key].default);

        // Huanyang
        if (this.config.hasOwnProperty('huanyang-spindle'))
          this.huanyangSpindle = this.config['huanyang-spindle'];

        template = this.template['huanyang-spindle'];
        for (key in template)
          if (!this.huanyangSpindle.hasOwnProperty(key))
            this.$set('huanyangSpindle["' + key + '"]', template[key].default);
      }.bind(this));
    }
  }
}

},{}]},{},[10])(10)
});