// Generated by CoffeeScript 1.8.0
(function() {
  angular.module('ajenti.terminal', ['core', 'ajenti.ace']);

}).call(this);

// Generated by CoffeeScript 1.8.0
(function() {
  angular.module('core').config(function($routeProvider) {
    $routeProvider.when('/view/terminal', {
      templateUrl: '/terminal:resources/partial/index.html',
      controller: 'TerminalIndexController'
    });
    return $routeProvider.when('/view/terminal/:id', {
      templateUrl: '/terminal:resources/partial/view.html',
      controller: 'TerminalViewController'
    });
  });

}).call(this);

// Generated by CoffeeScript 1.8.0
(function() {
  var colors;

  colors = {
    normal: {
      black: '#073642',
      white: '#eee8d5',
      green: '#859900',
      brown: '#af8700',
      red: '#dc322f',
      magenta: '#d33682',
      violet: '#6c71c4',
      blue: '#268bd2',
      cyan: '#2aa198'
    },
    bright: {
      black: '#074a5c',
      white: '#f6f2e6',
      green: '#bbd320',
      brown: '#efbc10',
      red: '#e5423f',
      magenta: '#dd458f',
      violet: '#7a7fd0',
      blue: '#3198e1',
      cyan: '#2abbb0'
    }
  };

  angular.module('ajenti.terminal').directive('terminal', function($timeout, $log, $q, socket, notify, terminals, hotkeys, gettext) {
    return {
      scope: {
        id: '=?',
        onReady: '&?',
        textData: '=?'
      },
      template: '<div>\n    <canvas></canvas>\n    <div class="paste-area" ng:class="{focus: pasteAreaFocused}">\n        <i class="fa fa-paste"></i>\n        <span ng:show="pasteAreaFocused">\n            Paste now\n        </span>\n\n        <textarea\n            ng:model="pasteData"\n            ng:focus="pasteAreaFocused = true"\n            ng:blur="pasteAreaFocused = false"\n        ></textarea>\n    </div>\n\n    <textarea\n        class="mobile-input-area"\n        ng:if="isMobile"\n        autocomplete="off" \n        autocorrect="off" \n        autocapitalize="off" \n        spellcheck="false"\n    ></textarea>\n\n    <a class="extra-keyboard-toggle btn btn-default" ng:click="extraKeyboardVisible=!extraKeyboardVisible" ng:show="isMobile">\n        <i class="fa fa-keyboard-o"></i>\n    </a>\n\n    <div class="extra-keyboard" ng:show="extraKeyboardVisible">\n        <a class="btn btn-default" ng:click="extraKeyboardCtrl = true" ng:class="{active: extraKeyboardCtrl}">\n            Ctrl\n        </a>\n        <a class="btn btn-default" ng:click="fakeKeyEvent(38)">\n            <i class="fa fa-arrow-up"></i>\n        </a>\n        <a class="btn btn-default" ng:click="fakeKeyEvent(40)">\n            <i class="fa fa-arrow-down"></i>\n        </a>\n        <a class="btn btn-default" ng:click="fakeKeyEvent(37)">\n            <i class="fa fa-arrow-left"></i>\n        </a>\n        <a class="btn btn-default" ng:click="fakeKeyEvent(39)">\n            <i class="fa fa-arrow-right"></i>\n        </a>\n    </div>\n</div>',
      link: function($scope, element, attrs) {
        var handler;
        element.addClass('block-element');
        $scope.isMobile = new MobileDetect(window.navigator.userAgent).mobile();
        $scope.extraKeyboardVisible = false;
        $scope.charWidth = 7;
        $scope.charHeight = 14;
        $scope.canvas = element.find('canvas')[0];
        $scope.context = $scope.canvas.getContext('2d');
        $scope.font = '12px monospace';
        $scope.ready = false;
        $scope.textLines = [];
        $scope.pasteData = null;
        $scope.clear = function() {
          $scope.dataWidth = 0;
          return $scope.dataHeight = 0;
        };
        $scope.fullReload = function() {
          var q;
          q = $q.defer();
          terminals.full($scope.id).then(function(data) {
            if (!data) {
              q.reject();
              return;
            }
            socket.send('terminal', {
              action: 'subscribe',
              id: $scope.id
            });
            $scope.clear();
            $scope.draw(data);
            if (!$scope.ready) {
              $scope.ready = true;
              $scope.onReady();
              $timeout(function() {
                return $scope.autoResize();
              });
            }
            return q.resolve();
          });
          return q.promise;
        };
        $scope.clear();
        $scope.fullReload()["catch"](function() {
          $scope.disabled = true;
          $scope.onReady();
          return notify.info(gettext('Terminal was closed'));
        });
        $scope.scheduleResize = function(w, h) {
          $timeout.cancel($scope.resizeTimeout);
          return $scope.resizeTimeout = $timeout(function() {
            return $scope.resize(w, h);
          }, 1000);
        };
        $scope.resize = function(w, h) {
          socket.send('terminal', {
            action: 'resize',
            id: $scope.id,
            width: w,
            height: h
          });
          $scope.canvas.width = $scope.charWidth * w;
          $scope.canvas.height = $scope.charHeight * h;
          return $scope.fullReload();
        };
        $scope.autoResize = function() {
          var availableHeight, availableWidth, cols, rows;
          availableWidth = element.parent().width() - 40;
          availableHeight = $(window).height() - 60 - 40;
          cols = Math.floor(availableWidth / $scope.charWidth);
          rows = Math.floor(availableHeight / $scope.charHeight);
          return $scope.scheduleResize(cols, rows);
        };
        $scope.$on('window:resize', function() {
          return $scope.autoResize();
        });
        $scope.$on('navigation:toggle', function() {
          return $timeout(function() {
            return $scope.autoResize();
          });
        });
        $scope.$on('widescreen:toggle', function() {
          return $timeout(function() {
            return $scope.autoResize();
          });
        });
        $scope.$on('terminal:paste', function() {
          return element.find('textarea').focus();
        });
        $scope.$on('socket:terminal', function($event, data) {
          if (data.id !== $scope.id || $scope.disabled) {
            return;
          }
          if (data.type === 'closed') {
            $scope.disabled = true;
            notify.info(gettext('Terminal was closed'));
          }
          if (data.type === 'data') {
            return $scope.draw(data.data);
          }
        });
        $scope.draw = function(data) {
          var cell, colorMap, defaultBG, defaultFG, line, lns, row, t, x, y, _i, _ref, _results;
          if ($scope.dataWidth !== data.w || $scope.dataHeight !== data.h) {
            $scope.dataWidth = data.w;
            $scope.dataHeight = data.h;
          }
          $scope.cursor = data.cursor;
          if (data.cursor) {
            $scope.cursx = data.cx;
            $scope.cursy = data.cy;
          } else {
            $scope.cursx = -1;
          }
          $scope.context.font = $scope.font;
          $scope.context.textBaseline = 'top';
          for (y in data.lines) {
            row = data.lines[y];
            line = '';
            for (x = _i = 0, _ref = row.length; 0 <= _ref ? _i < _ref : _i > _ref; x = 0 <= _ref ? ++_i : --_i) {
              cell = row[x];
              if (cell) {
                line += cell[0];
              }
            }
            $scope.textLines[parseInt(y)] = line;
          }
          $scope.textData = $scope.textLines.join('\n');
          lns = element.find('div');
          _results = [];
          for (y in data.lines) {
            row = data.lines[y];
            y = parseInt(y);
            $scope.context.fillStyle = colors.normal.black;
            $scope.context.fillRect(0, $scope.charHeight * y, $scope.charWidth * $scope.dataWidth, $scope.charHeight);
            _results.push((function() {
              var _j, _ref1, _results1;
              _results1 = [];
              for (x = _j = 0, _ref1 = row.length; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; x = 0 <= _ref1 ? ++_j : --_j) {
                cell = row[x];
                if (!cell) {
                  continue;
                }
                defaultFG = 'white';
                defaultBG = 'black';
                if (cell[7]) {
                  t = cell[1];
                  cell[1] = cell[2];
                  cell[2] = t;
                  defaultFG = 'black';
                  defaultBG = 'white';
                }
                if (cell[3]) {
                  $scope.context.font = 'bold ' + $scope.context.font;
                }
                if (cell[4]) {
                  $scope.context.font = 'italic ' + $scope.context.font;
                }
                if (cell[2]) {
                  if (cell[2] !== 'default' || cell[7]) {
                    $scope.context.fillStyle = colors.normal[cell[2]] || colors.normal[defaultBG];
                    $scope.context.fillRect($scope.charWidth * x, $scope.charHeight * y, $scope.charWidth, $scope.charHeight);
                  }
                }
                if (y === $scope.cursy && x === $scope.cursx) {
                  $scope.context.fillStyle = colors.normal['white'];
                  $scope.context.fillRect($scope.charWidth * x, $scope.charHeight * y, $scope.charWidth, $scope.charHeight);
                }
                if (cell[1]) {
                  colorMap = cell[3] ? colors.bright : colors.normal;
                  $scope.context.fillStyle = colorMap[cell[1]] || colorMap[defaultFG];
                  $scope.context.fillText(cell[0], $scope.charWidth * x, $scope.charHeight * y);
                  if (cell[5]) {
                    $scope.context.fillRect($scope.charWidth * x, $scope.charHeight * (y + 1) - 1, $scope.charWidth, 1);
                  }
                }
                if (cell[3] || cell[4]) {
                  _results1.push($scope.context.font = $scope.font);
                } else {
                  _results1.push(void 0);
                }
              }
              return _results1;
            })());
          }
          return _results;
        };
        $scope.parseKey = function(event, event_name, ign_arrows) {
          var ch, fNumber;
          ch = null;
          if (event.ctrlKey && event.keyCode === 17) {
            return;
          }
          if (event.ctrlKey && event.keyCode > 64) {
            return String.fromCharCode(event.keyCode - 64);
          }
          if (event_name === 'keypress' && (event.charCode || event.which)) {
            ch = String.fromCharCode(event.which);
            if (ch === '\r') {
              ch = '\n';
            }
            return ch;
          }
          if (event_name === 'keydown' && event.keyCode >= 112 && event.keyCode <= 123) {
            fNumber = event.keyCode - 111;
            switch (fNumber) {
              case 1:
                ch = '\x1bOP';
                break;
              case 2:
                ch = '\x1bOQ';
                break;
              case 3:
                ch = '\x1bOR';
                break;
              case 4:
                ch = '\x1bOS';
                break;
              default:
                ch = "\x1b[" + (fNumber + 10) + "~";
            }
            return ch;
          }
          switch (event.keyCode) {
            case 8:
              ch = '\b';
              break;
            case 9:
              if (!ign_arrows) {
                ch = '\t';
              }
              break;
            case 13:
            case 10:
              ch = '\r';
              break;
            case 38:
              if (!ign_arrows) {
                ch = '\x1b[A';
              }
              break;
            case 40:
              if (!ign_arrows) {
                ch = '\x1b[B';
              }
              break;
            case 39:
              if (!ign_arrows) {
                ch = '\x1b[C';
              }
              break;
            case 37:
              if (!ign_arrows) {
                ch = '\x1b[D';
              }
              break;
            case 35:
              ch = '\x1b[F';
              break;
            case 36:
              ch = '\x1b[H';
              break;
            case 34:
              ch = '\x1b[6~';
              break;
            case 33:
              ch = '\x1b[5~';
              break;
            case 27:
              ch = '\x1b';
          }
          if (ch) {
            return ch;
          }
          return null;
        };
        $scope.sendInput = function(data) {
          return socket.send('terminal', {
            action: 'input',
            id: $scope.id,
            data: data
          });
        };
        handler = function(key, event, mode) {
          var ch;
          if ($scope.pasteAreaFocused || $scope.disabled) {
            return;
          }
          if ($scope.extraKeyboardCtrl) {
            event.ctrlKey = true;
            $scope.extraKeyboardCtrl = false;
          }
          ch = $scope.parseKey(event, mode);
          if (!ch) {
            return false;
          }
          $scope.sendInput(ch);
          return true;
        };
        hotkeys.on($scope, function(k, e) {
          return handler(k, e, 'keypress');
        }, 'keypress:global');
        hotkeys.on($scope, function(k, e) {
          return handler(k, e, 'keydown');
        }, 'keydown:global');
        $scope.fakeKeyEvent = function(code) {
          return handler(null, {
            keyCode: code
          }, 'keydown');
        };
        return $scope.$watch('pasteData', function() {
          if ($scope.pasteData) {
            $scope.sendInput($scope.pasteData);
          }
          $scope.pasteData = '';
          return element.find('textarea').blur();
        });
      }
    };
  });

}).call(this);

// Generated by CoffeeScript 1.8.0
(function() {
  angular.module('ajenti.terminal').controller('TerminalIndexController', function($scope, $location, $q, pageTitle, terminals, gettext) {
    pageTitle.set(gettext('Terminals'));
    $scope.refresh = function() {
      var q;
      q = $q.defer();
      terminals.list().then(function(list) {
        $scope.terminals = list;
        return q.resolve();
      });
      return q.promise;
    };
    $scope.create = function() {
      return terminals.create().then(function(id) {
        return $location.path("/view/terminal/" + id);
      });
    };
    $scope.runCommand = function() {
      return terminals.create({
        command: $scope.command,
        autoclose: true
      }).then(function(id) {
        return $location.path("/view/terminal/" + id);
      });
    };
    $scope.kill = function(terminal) {
      return terminals.kill(terminal.id).then(function() {
        return $scope.refresh();
      });
    };
    return $scope.refresh().then(function() {});
  });

}).call(this);

// Generated by CoffeeScript 1.8.0
(function() {
  angular.module('ajenti.terminal').controller('TerminalViewController', function($scope, $routeParams, hotkeys, pageTitle) {
    pageTitle.set('Terminal');
    $scope.id = $routeParams.id;
    $scope.ready = false;
    $scope.copyData = '';
    $scope.copyDialogVisible = false;
    $scope.onReady = function() {
      return $scope.ready = true;
    };
    hotkeys.on($scope, function(k, e) {
      if (k === 'C' && e.ctrlKey && e.shiftKey) {
        $scope.copyDialogVisible = true;
        return true;
      }
      if (k === 'V' && e.ctrlKey && e.shiftKey) {
        $scope.$broadcast('terminal:paste');
        return true;
      }
    });
    return $scope.hideCopyDialogVisible = function() {
      return $scope.copyDialogVisible = false;
    };
  });

}).call(this);

// Generated by CoffeeScript 1.8.0
(function() {
  angular.module('ajenti.terminal').controller('ScriptWidgetController', function($scope, $location, notify, terminals, gettext) {
    return $scope.run = function() {
      var input, script;
      script = $scope.widget.config.script;
      input = $scope.widget.config.input;
      if ($scope.widget.config.terminal) {
        return terminals.create({
          command: script,
          autoclose: true
        }).then(function(id) {
          return $location.path("/view/terminal/" + id);
        });
      } else {
        notify.info(gettext('Starting the script'), "" + (script.substring(0, 100)) + "...");
        return terminals.script({
          script: script,
          input: input
        }).then(function(data) {
          if (data.code === 0) {
            return notify.success(gettext('Script has finished'), data.output + data.stderr);
          } else {
            return notify.warning(gettext('Script has failed'), data.stderr + data.output);
          }
        })["catch"](function(err) {
          return notify.error(gettext('Could not launch the script'), err.message);
        });
      }
    };
  });

}).call(this);

// Generated by CoffeeScript 1.8.0
(function() {
  angular.module('ajenti.terminal').service('terminals', function($http, $q) {
    this.script = function(options) {
      var q;
      q = $q.defer();
      $http.post('/api/terminal/script', options).success(function(data) {
        return q.resolve(data);
      }).error(function(err) {
        return q.reject(err);
      });
      return q.promise;
    };
    this.list = function() {
      var q;
      q = $q.defer();
      $http.get("/api/terminal/list").success(function(data) {
        var cmd, t, tokens, _i, _len;
        for (_i = 0, _len = data.length; _i < _len; _i++) {
          t = data[_i];
          cmd = t.command.split(' ')[0];
          tokens = cmd.split('/');
          t.title = tokens[tokens.length - 1];
        }
        return q.resolve(data);
      }).error(function(err) {
        return q.reject(err);
      });
      return q.promise;
    };
    this.kill = function(id) {
      var q;
      q = $q.defer();
      $http.get("/api/terminal/kill/" + id).success(function(data) {
        return q.resolve(data);
      }).error(function(err) {
        return q.reject(err);
      });
      return q.promise;
    };
    this.create = function(options) {
      var q;
      if (options == null) {
        options = {};
      }
      q = $q.defer();
      $http.post("/api/terminal/create", options).success(function(data) {
        return q.resolve(data);
      }).error(function(err) {
        return q.reject(err);
      });
      return q.promise;
    };
    this.full = function(id) {
      var q;
      q = $q.defer();
      $http.get("/api/terminal/full/" + id).success(function(data) {
        return q.resolve(data);
      }).error(function(err) {
        return q.reject(err);
      });
      return q.promise;
    };
    return this;
  });

}).call(this);

