Browse files

Initial commit

  • Loading branch information...
0 parents commit b1cf58b51bb332b70cbc8b2563c9ef9e7d8008d3 @sickill committed Jan 7, 2012
Showing with 997 additions and 0 deletions.
  1. +4 −0 Gemfile
  2. +26 −0 Gemfile.lock
  3. +4 −0 Guardfile
  4. +5 −0 Rakefile
  5. +8 −0 coffee/content.coffee
  6. +41 −0 coffee/key-sequence-handler.coffee
  7. +70 −0 coffee/keycodes.coffee
  8. +38 −0 coffee/proxy.coffee
  9. +227 −0 coffee/vimbanery.coffee
  10. +4 −0 css/vimbanery.css
  11. +13 −0 js/content.js
  12. +65 −0 js/key-sequence-handler.js
  13. +72 −0 js/keycodes.js
  14. +57 −0 js/proxy.js
  15. +339 −0 js/vimbanery.js
  16. +24 −0 manifest.json
4 Gemfile
@@ -0,0 +1,4 @@
+source :rubygems
+
+gem 'guard-coffeescript'
+gem 'libnotify'
26 Gemfile.lock
@@ -0,0 +1,26 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ coffee-script (2.2.0)
+ coffee-script-source
+ execjs
+ coffee-script-source (1.2.0)
+ execjs (1.2.13)
+ multi_json (~> 1.0)
+ ffi (1.0.11)
+ guard (0.10.0)
+ ffi (>= 0.5.0)
+ thor (~> 0.14.6)
+ guard-coffeescript (0.5.4)
+ coffee-script (>= 2.2.0)
+ guard (>= 0.8.3)
+ libnotify (0.7.1)
+ multi_json (1.0.4)
+ thor (0.14.6)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ guard-coffeescript
+ libnotify
4 Guardfile
@@ -0,0 +1,4 @@
+# A sample Guardfile
+# More info at https://github.com/guard/guard#readme
+
+guard 'coffeescript', :input => 'coffee', :output => 'js', :bare => true
5 Rakefile
@@ -0,0 +1,5 @@
+task :package do
+ # TODO
+end
+
+task :default => :package
8 coffee/content.coffee
@@ -0,0 +1,8 @@
+p = new Proxy(document)
+
+inject = (path) ->
+ p.injectScript('', null, chrome.extension.getURL(path) + "?#{new Date().getTime()}")
+
+inject 'js/keycodes.js'
+inject 'js/key-sequence-handler.js'
+inject 'js/vimbanery.js'
41 coffee/key-sequence-handler.coffee
@@ -0,0 +1,41 @@
+class KeySequenceHandler
+
+ constructor: ->
+ @sequence = []
+
+ register: (map) ->
+ @map = map
+ $(document).keypress @handleKeyPress.bind(this)
+ $(document).keydown @handleKeyDown.bind(this)
+
+ handleKeyPress: (e) ->
+ tagName = e.target.tagName
+ return true if tagName is 'TEXTAREA' or tagName is 'INPUT'
+ char = KEYCODES[e.which]
+ @sequence.push char
+ @dispatchSequence()
+ false
+
+ handleKeyDown: (e) ->
+ tagName = e.target.tagName
+ return true if tagName is 'TEXTAREA' or tagName is 'INPUT'
+ if e.which >= 37 and e.which <= 40
+ char = KEYCODES[e.which]
+ @sequence.push char
+ @dispatchSequence()
+ false
+ else
+ true
+
+ dispatchSequence: ->
+ seq = @sequence.toString()
+ console.log seq
+ matches = (combo for combo, handler of @map when combo.substring(0, seq.length) is seq)
+
+ if matches.length is 0
+ @sequence = []
+ else if matches.length is 1 and matches[0] is seq
+ @sequence = []
+ @map[seq]()
+ else
+ console.log "candidates: #{matches.join(' ')}"
70 coffee/keycodes.coffee
@@ -0,0 +1,70 @@
+window.KEYCODES =
+ 13: ""
+ 37: ""
+ 38: ""
+ 39: ""
+ 40: ""
+ 47: "/"
+ 48: "0"
+ 49: "1"
+ 50: "2"
+ 51: "3"
+ 52: "4"
+ 53: "5"
+ 54: "6"
+ 55: "7"
+ 56: "8"
+ 57: "9"
+ 63: "?"
+ 65: "A"
+ 66: "B"
+ 67: "C"
+ 68: "D"
+ 69: "E"
+ 70: "F"
+ 71: "G"
+ 72: "H"
+ 73: "I"
+ 74: "J"
+ 75: "K"
+ 76: "L"
+ 77: "M"
+ 78: "N"
+ 79: "O"
+ 80: "P"
+ 81: "Q"
+ 82: "R"
+ 83: "S"
+ 84: "T"
+ 85: "U"
+ 86: "V"
+ 87: "W"
+ 88: "X"
+ 89: "Y"
+ 90: "Z"
+ 97: "a"
+ 98: "b"
+ 99: "c"
+ 100: "d"
+ 101: "e"
+ 102: "f"
+ 103: "g"
+ 104: "h"
+ 105: "i"
+ 106: "j"
+ 107: "k"
+ 108: "l"
+ 109: "m"
+ 110: "n"
+ 111: "o"
+ 112: "p"
+ 113: "q"
+ 114: "r"
+ 115: "s"
+ 116: "t"
+ 117: "u"
+ 118: "v"
+ 119: "w"
+ 120: "x"
+ 121: "y"
+ 122: "z"
38 coffee/proxy.coffee
@@ -0,0 +1,38 @@
+class Proxy
+
+ constructor: (@document) ->
+ @body = @document.getElementsByTagName('body')[0]
+ @injectScript(@_listener)
+
+ injectScript: (code, name=null, src=null) ->
+ code = code.toString()
+
+ if code.length > 0
+ if name?
+ code = "#{name} = #{code}"
+ else
+ code = "(#{code})()"
+
+ if name and script = document.getElementById(name)
+ script.innerHTML = code
+ else
+ script = @document.createElement("script")
+ script.type = "text/javascript"
+ script.id = name if name?
+ script.src = src if src?
+ script.innerHTML = code
+ @document.head.appendChild(script)
+
+ _listener: ->
+ console.log 'adding listener'
+ document.getElementsByTagName('body')[0].addEventListener('vimbanery-exec', -> _vimbaneryAction())
+
+ injectAction: (f) ->
+ console.log 'injecting new action'
+ @injectScript(f, '_vimbaneryAction')
+
+ exec: (f) ->
+ @injectAction(f)
+ event = document.createEvent('Event')
+ event.initEvent('vimbanery-exec', true, true)
+ @body.dispatchEvent(event)
227 coffee/vimbanery.coffee
@@ -0,0 +1,227 @@
+class Vimbanery
+
+ constructor: ->
+ @handler = new KeySequenceHandler()
+ @handler.register
+ 'j' : @goDown
+ '' : @goDown
+ 'k' : @goUp
+ '' : @goUp
+ 'h' : @goLeft
+ '' : @goLeft
+ 'l' : @goRight
+ '' : @goRight
+ 'J' : @shiftDown
+ 'K' : @shiftUp
+ 'H' : @shiftLeft
+ 'L' : @shiftRight
+ 'g,g' : @goTop
+ 'G' : @goBottom
+ 'o' : @toggleOpen
+ '' : @toggleOpen
+ 'y' : @copyLink
+ 'e,e' : @expandAll
+ 'e,c' : @expandComments
+ 'e,s' : @expandSubtasks
+ 'e,a' : @expandAttachments
+ 'e,g' : @expandGithub
+ 'c' : @addComment
+ 's' : @addSubtask
+ 'b' : @addBlocker
+ 't' : @editTitle
+ 'E' : @editDescription
+ 'a' : @addAfter
+ 'A' : @addAtBottom
+ 'i' : @addBefore
+ 'I' : @addAtTop
+ 'x' : @delete
+ 'g,i' : @goIcebox
+
+ goDown: =>
+ if t = @currentTask()
+ if t.pos < @tasksCount(t.col) - 1
+ @focusTask(t.col, t.pos + 1)
+ else
+ @focusTask(0, 0)
+
+ goUp: =>
+ if t = @currentTask()
+ if t.pos > 0
+ @focusTask(t.col, t.pos - 1)
+ else
+ @focusTask(0, 0)
+
+ goLeft: =>
+ if t = @currentTask()
+ n = -1
+ while t.col + n >= 0
+ tc = @tasksCount(t.col + n)
+ if tc > 0
+ if t.pos < tc
+ @focusTask(t.col + n, t.pos)
+ else
+ @focusTask(t.col + n, tc - 1)
+ break
+ n -= 1
+ else
+ @focusTask(0, 0)
+
+ goRight: =>
+ if t = @currentTask()
+ n = 1
+ while t.col + n < @columnsCount()
+ tc = @tasksCount(t.col + n)
+ if tc > 0
+ if t.pos < tc
+ @focusTask(t.col + n, t.pos)
+ else
+ @focusTask(t.col + n, tc - 1)
+ break
+ n += 1
+ else
+ @focusTask(0, 0)
+
+ goTop: =>
+ if t = @currentTask()
+ @focusTask(t.col, 0)
+ else
+ @focusTask(0, 0)
+
+ goBottom: =>
+ if t = @currentTask()
+ col = t.col
+ else
+ col = 0
+ bottom = $(".column-view:eq(#{col}) .task-view").length - 1
+ @focusTask(col, bottom)
+
+ toggleOpen: =>
+ if t = @currentTask()
+ t.task.view().bodyToggler.toggle()
+
+ shiftDown: =>
+ if t = @currentTask()
+ v = t.task.view()
+ m = v.model
+ if m.data.position + 1 <= v.parentView().modelViewList.all().length
+ v.element.next().after(v.element.remove())
+ v.updateModel(position: m.data.position + 1)
+ @showTask(t.task)
+
+ shiftUp: =>
+ if t = @currentTask()
+ v = t.task.view()
+ m = v.model
+ if m.data.position > 1
+ v.element.prev().before(v.element.remove())
+ v.updateModel(position: m.data.position - 1)
+ @showTask(t.task)
+
+ shiftRight: =>
+ if t = @currentTask()
+ @shiftToColumn(t.task, +1)
+
+ shiftLeft: =>
+ if t = @currentTask()
+ @shiftToColumn(t.task, -1)
+
+ shiftToColumn: (task, direction) ->
+ v = task.view()
+ m = v.model
+ columns = @columns(v)
+ newColIndex = m.column().data.position - 1 + direction
+
+ if newColIndex >= 0 and newColIndex < columns.length
+ newColView = v.parentView().modelViewList.htmlList.parentView().
+ parentView().modelViewList.htmlList.element.
+ find(".ul-columns-section-body .column-view:eq(#{newColIndex})").
+ view().listViews.tasks.modelViewList.htmlList.element.
+ find('.ul-tasks-section-body')
+
+ newColView.append(v.element.remove())
+
+ cid = columns[newColIndex].model.data.id
+ newPos = @tasksCount(newColIndex)
+ v.updateModel(column_id: cid, position: newPos)
+ @showTask(task)
+
+ editTitle: =>
+ if t = @currentTask()
+ f = new K.InlineForm(t.task.find('.disp-task-title'))
+
+ editDescription: =>
+ if t = @currentTask()
+ @toggleOpen() if t.task.find('.disp-task-description:visible').length == 0
+ f = new K.InlineForm(t.task.find('.disp-task-description'))
+
+ expandAll: =>
+ @expandComments()
+ @expandSubtasks()
+
+ expandComments: =>
+ if t = @currentTask()
+ v = t.task.view()
+ v.listViews.comments.modelViewList.htmlList.toggleBodyAndShowUp()
+
+ expandSubtasks: =>
+ if t = @currentTask()
+ v = t.task.view()
+ v.listViews.subtasks.modelViewList.htmlList.toggleBodyAndShowUp()
+
+ addComment: =>
+ if t = @currentTask()
+ v = t.task.view()
+ @expandComments() if t.task.find('.comments-section .add-item-link:visible').length == 0
+ t.task.find('.comments-section .add-item-link:visible').click()
+
+ addSubtask: =>
+ if t = @currentTask()
+ v = t.task.view()
+ @expandSubtasks() if t.task.find('.subtasks-section .add-item-link:visible').length == 0
+ t.task.find('.subtasks-section .add-item-link:visible').click()
+
+ columns: (view) ->
+ view.parentView().modelViewList.htmlList.parentView().parentView().modelViewList.all()
+
+ goIcebox: =>
+ console.log 'icebox'
+
+ currentTask: =>
+ task = $('.task-view.v-focus:first')
+ if task.length > 0
+ pos = task.index()
+ col = task.parents('.column-view').index()
+ task: task, col: col, pos: pos
+
+ focusTask: (col, pos) =>
+ $('.task-view').removeClass('v-focus')
+ task = $(".column-view:eq(#{col}) .task-view:eq(#{pos})").addClass('v-focus')
+ task.view().loadBody()
+ @showTask(task)
+ task
+
+ showTask: (task) ->
+ list = task.parents('.tasks-section-body')
+ list.scrollTo(task, { axis: 'y' }) unless @taskVisible(list, task)
+
+ taskVisible: (parent, task) ->
+ parentViewTop = $(parent).offset().top + $(parent).scrollTop()
+ parentViewBottom = parentViewTop + $(parent).height()
+
+ elemTop = $(task).offset().top
+ elemBottom = elemTop + $(task).height()
+
+ (elemBottom >= parentViewTop) and
+ (elemTop <= parentViewBottom) and
+ (elemBottom <= parentViewBottom) and
+ (elemTop >= parentViewTop)
+
+ columnsCount: =>
+ $(".column-view").length
+
+ tasksCount: (col) =>
+ $(".column-view:eq(#{col}) .task-view").length
+
+
+if window.Application
+ v = new Vimbanery()
4 css/vimbanery.css
@@ -0,0 +1,4 @@
+#js-app .v-focus .task-view-header {
+ border-top: 1px solid green;
+ border-bottom: 1px solid green;
+}
13 js/content.js
@@ -0,0 +1,13 @@
+var inject, p;
+
+p = new Proxy(document);
+
+inject = function(path) {
+ return p.injectScript('', null, chrome.extension.getURL(path) + ("?" + (new Date().getTime())));
+};
+
+inject('js/keycodes.js');
+
+inject('js/key-sequence-handler.js');
+
+inject('js/vimbanery.js');
65 js/key-sequence-handler.js
@@ -0,0 +1,65 @@
+var KeySequenceHandler;
+
+KeySequenceHandler = (function() {
+
+ function KeySequenceHandler() {
+ this.sequence = [];
+ }
+
+ KeySequenceHandler.prototype.register = function(map) {
+ this.map = map;
+ $(document).keypress(this.handleKeyPress.bind(this));
+ return $(document).keydown(this.handleKeyDown.bind(this));
+ };
+
+ KeySequenceHandler.prototype.handleKeyPress = function(e) {
+ var char, tagName;
+ tagName = e.target.tagName;
+ if (tagName === 'TEXTAREA' || tagName === 'INPUT') return true;
+ char = KEYCODES[e.which];
+ this.sequence.push(char);
+ this.dispatchSequence();
+ return false;
+ };
+
+ KeySequenceHandler.prototype.handleKeyDown = function(e) {
+ var char, tagName;
+ tagName = e.target.tagName;
+ if (tagName === 'TEXTAREA' || tagName === 'INPUT') return true;
+ if (e.which >= 37 && e.which <= 40) {
+ char = KEYCODES[e.which];
+ this.sequence.push(char);
+ this.dispatchSequence();
+ return false;
+ } else {
+ return true;
+ }
+ };
+
+ KeySequenceHandler.prototype.dispatchSequence = function() {
+ var combo, handler, matches, seq;
+ seq = this.sequence.toString();
+ console.log(seq);
+ matches = (function() {
+ var _ref, _results;
+ _ref = this.map;
+ _results = [];
+ for (combo in _ref) {
+ handler = _ref[combo];
+ if (combo.substring(0, seq.length) === seq) _results.push(combo);
+ }
+ return _results;
+ }).call(this);
+ if (matches.length === 0) {
+ return this.sequence = [];
+ } else if (matches.length === 1 && matches[0] === seq) {
+ this.sequence = [];
+ return this.map[seq]();
+ } else {
+ return console.log("candidates: " + (matches.join(' ')));
+ }
+ };
+
+ return KeySequenceHandler;
+
+})();
72 js/keycodes.js
@@ -0,0 +1,72 @@
+
+window.KEYCODES = {
+ 13: "",
+ 37: "",
+ 38: "",
+ 39: "",
+ 40: "",
+ 47: "/",
+ 48: "0",
+ 49: "1",
+ 50: "2",
+ 51: "3",
+ 52: "4",
+ 53: "5",
+ 54: "6",
+ 55: "7",
+ 56: "8",
+ 57: "9",
+ 63: "?",
+ 65: "A",
+ 66: "B",
+ 67: "C",
+ 68: "D",
+ 69: "E",
+ 70: "F",
+ 71: "G",
+ 72: "H",
+ 73: "I",
+ 74: "J",
+ 75: "K",
+ 76: "L",
+ 77: "M",
+ 78: "N",
+ 79: "O",
+ 80: "P",
+ 81: "Q",
+ 82: "R",
+ 83: "S",
+ 84: "T",
+ 85: "U",
+ 86: "V",
+ 87: "W",
+ 88: "X",
+ 89: "Y",
+ 90: "Z",
+ 97: "a",
+ 98: "b",
+ 99: "c",
+ 100: "d",
+ 101: "e",
+ 102: "f",
+ 103: "g",
+ 104: "h",
+ 105: "i",
+ 106: "j",
+ 107: "k",
+ 108: "l",
+ 109: "m",
+ 110: "n",
+ 111: "o",
+ 112: "p",
+ 113: "q",
+ 114: "r",
+ 115: "s",
+ 116: "t",
+ 117: "u",
+ 118: "v",
+ 119: "w",
+ 120: "x",
+ 121: "y",
+ 122: "z"
+};
57 js/proxy.js
@@ -0,0 +1,57 @@
+var Proxy;
+
+Proxy = (function() {
+
+ function Proxy(document) {
+ this.document = document;
+ this.body = this.document.getElementsByTagName('body')[0];
+ this.injectScript(this._listener);
+ }
+
+ Proxy.prototype.injectScript = function(code, name, src) {
+ var script;
+ if (name == null) name = null;
+ if (src == null) src = null;
+ code = code.toString();
+ if (code.length > 0) {
+ if (name != null) {
+ code = "" + name + " = " + code;
+ } else {
+ code = "(" + code + ")()";
+ }
+ }
+ if (name && (script = document.getElementById(name))) {
+ return script.innerHTML = code;
+ } else {
+ script = this.document.createElement("script");
+ script.type = "text/javascript";
+ if (name != null) script.id = name;
+ if (src != null) script.src = src;
+ script.innerHTML = code;
+ return this.document.head.appendChild(script);
+ }
+ };
+
+ Proxy.prototype._listener = function() {
+ console.log('adding listener');
+ return document.getElementsByTagName('body')[0].addEventListener('vimbanery-exec', function() {
+ return _vimbaneryAction();
+ });
+ };
+
+ Proxy.prototype.injectAction = function(f) {
+ console.log('injecting new action');
+ return this.injectScript(f, '_vimbaneryAction');
+ };
+
+ Proxy.prototype.exec = function(f) {
+ var event;
+ this.injectAction(f);
+ event = document.createEvent('Event');
+ event.initEvent('vimbanery-exec', true, true);
+ return this.body.dispatchEvent(event);
+ };
+
+ return Proxy;
+
+})();
339 js/vimbanery.js
@@ -0,0 +1,339 @@
+var Vimbanery, v,
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+Vimbanery = (function() {
+
+ function Vimbanery() {
+ this.tasksCount = __bind(this.tasksCount, this);
+ this.columnsCount = __bind(this.columnsCount, this);
+ this.focusTask = __bind(this.focusTask, this);
+ this.currentTask = __bind(this.currentTask, this);
+ this.goIcebox = __bind(this.goIcebox, this);
+ this.addSubtask = __bind(this.addSubtask, this);
+ this.addComment = __bind(this.addComment, this);
+ this.expandSubtasks = __bind(this.expandSubtasks, this);
+ this.expandComments = __bind(this.expandComments, this);
+ this.expandAll = __bind(this.expandAll, this);
+ this.editDescription = __bind(this.editDescription, this);
+ this.editTitle = __bind(this.editTitle, this);
+ this.shiftLeft = __bind(this.shiftLeft, this);
+ this.shiftRight = __bind(this.shiftRight, this);
+ this.shiftUp = __bind(this.shiftUp, this);
+ this.shiftDown = __bind(this.shiftDown, this);
+ this.toggleOpen = __bind(this.toggleOpen, this);
+ this.goBottom = __bind(this.goBottom, this);
+ this.goTop = __bind(this.goTop, this);
+ this.goRight = __bind(this.goRight, this);
+ this.goLeft = __bind(this.goLeft, this);
+ this.goUp = __bind(this.goUp, this);
+ this.goDown = __bind(this.goDown, this); this.handler = new KeySequenceHandler();
+ this.handler.register({
+ 'j': this.goDown,
+ '': this.goDown,
+ 'k': this.goUp,
+ '': this.goUp,
+ 'h': this.goLeft,
+ '': this.goLeft,
+ 'l': this.goRight,
+ '': this.goRight,
+ 'J': this.shiftDown,
+ 'K': this.shiftUp,
+ 'H': this.shiftLeft,
+ 'L': this.shiftRight,
+ 'g,g': this.goTop,
+ 'G': this.goBottom,
+ 'o': this.toggleOpen,
+ '': this.toggleOpen,
+ 'y': this.copyLink,
+ 'e,e': this.expandAll,
+ 'e,c': this.expandComments,
+ 'e,s': this.expandSubtasks,
+ 'e,a': this.expandAttachments,
+ 'e,g': this.expandGithub,
+ 'c': this.addComment,
+ 's': this.addSubtask,
+ 'b': this.addBlocker,
+ 't': this.editTitle,
+ 'E': this.editDescription,
+ 'a': this.addAfter,
+ 'A': this.addAtBottom,
+ 'i': this.addBefore,
+ 'I': this.addAtTop,
+ 'x': this["delete"],
+ 'g,i': this.goIcebox
+ });
+ }
+
+ Vimbanery.prototype.goDown = function() {
+ var t;
+ if (t = this.currentTask()) {
+ if (t.pos < this.tasksCount(t.col) - 1) {
+ return this.focusTask(t.col, t.pos + 1);
+ }
+ } else {
+ return this.focusTask(0, 0);
+ }
+ };
+
+ Vimbanery.prototype.goUp = function() {
+ var t;
+ if (t = this.currentTask()) {
+ if (t.pos > 0) return this.focusTask(t.col, t.pos - 1);
+ } else {
+ return this.focusTask(0, 0);
+ }
+ };
+
+ Vimbanery.prototype.goLeft = function() {
+ var n, t, tc, _results;
+ if (t = this.currentTask()) {
+ n = -1;
+ _results = [];
+ while (t.col + n >= 0) {
+ tc = this.tasksCount(t.col + n);
+ if (tc > 0) {
+ if (t.pos < tc) {
+ this.focusTask(t.col + n, t.pos);
+ } else {
+ this.focusTask(t.col + n, tc - 1);
+ }
+ break;
+ }
+ _results.push(n -= 1);
+ }
+ return _results;
+ } else {
+ return this.focusTask(0, 0);
+ }
+ };
+
+ Vimbanery.prototype.goRight = function() {
+ var n, t, tc, _results;
+ if (t = this.currentTask()) {
+ n = 1;
+ _results = [];
+ while (t.col + n < this.columnsCount()) {
+ tc = this.tasksCount(t.col + n);
+ if (tc > 0) {
+ if (t.pos < tc) {
+ this.focusTask(t.col + n, t.pos);
+ } else {
+ this.focusTask(t.col + n, tc - 1);
+ }
+ break;
+ }
+ _results.push(n += 1);
+ }
+ return _results;
+ } else {
+ return this.focusTask(0, 0);
+ }
+ };
+
+ Vimbanery.prototype.goTop = function() {
+ var t;
+ if (t = this.currentTask()) {
+ return this.focusTask(t.col, 0);
+ } else {
+ return this.focusTask(0, 0);
+ }
+ };
+
+ Vimbanery.prototype.goBottom = function() {
+ var bottom, col, t;
+ if (t = this.currentTask()) {
+ col = t.col;
+ } else {
+ col = 0;
+ }
+ bottom = $(".column-view:eq(" + col + ") .task-view").length - 1;
+ return this.focusTask(col, bottom);
+ };
+
+ Vimbanery.prototype.toggleOpen = function() {
+ var t;
+ if (t = this.currentTask()) return t.task.view().bodyToggler.toggle();
+ };
+
+ Vimbanery.prototype.shiftDown = function() {
+ var m, t, v;
+ if (t = this.currentTask()) {
+ v = t.task.view();
+ m = v.model;
+ if (m.data.position + 1 <= v.parentView().modelViewList.all().length) {
+ v.element.next().after(v.element.remove());
+ v.updateModel({
+ position: m.data.position + 1
+ });
+ return this.showTask(t.task);
+ }
+ }
+ };
+
+ Vimbanery.prototype.shiftUp = function() {
+ var m, t, v;
+ if (t = this.currentTask()) {
+ v = t.task.view();
+ m = v.model;
+ if (m.data.position > 1) {
+ v.element.prev().before(v.element.remove());
+ v.updateModel({
+ position: m.data.position - 1
+ });
+ return this.showTask(t.task);
+ }
+ }
+ };
+
+ Vimbanery.prototype.shiftRight = function() {
+ var t;
+ if (t = this.currentTask()) return this.shiftToColumn(t.task, +1);
+ };
+
+ Vimbanery.prototype.shiftLeft = function() {
+ var t;
+ if (t = this.currentTask()) return this.shiftToColumn(t.task, -1);
+ };
+
+ Vimbanery.prototype.shiftToColumn = function(task, direction) {
+ var cid, columns, m, newColIndex, newColView, newPos, v;
+ v = task.view();
+ m = v.model;
+ columns = this.columns(v);
+ newColIndex = m.column().data.position - 1 + direction;
+ if (newColIndex >= 0 && newColIndex < columns.length) {
+ newColView = v.parentView().modelViewList.htmlList.parentView().parentView().modelViewList.htmlList.element.find(".ul-columns-section-body .column-view:eq(" + newColIndex + ")").view().listViews.tasks.modelViewList.htmlList.element.find('.ul-tasks-section-body');
+ newColView.append(v.element.remove());
+ cid = columns[newColIndex].model.data.id;
+ newPos = this.tasksCount(newColIndex);
+ v.updateModel({
+ column_id: cid,
+ position: newPos
+ });
+ return this.showTask(task);
+ }
+ };
+
+ Vimbanery.prototype.editTitle = function() {
+ var f, t;
+ if (t = this.currentTask()) {
+ return f = new K.InlineForm(t.task.find('.disp-task-title'));
+ }
+ };
+
+ Vimbanery.prototype.editDescription = function() {
+ var f, t;
+ if (t = this.currentTask()) {
+ if (t.task.find('.disp-task-description:visible').length === 0) {
+ this.toggleOpen();
+ }
+ return f = new K.InlineForm(t.task.find('.disp-task-description'));
+ }
+ };
+
+ Vimbanery.prototype.expandAll = function() {
+ this.expandComments();
+ return this.expandSubtasks();
+ };
+
+ Vimbanery.prototype.expandComments = function() {
+ var t, v;
+ if (t = this.currentTask()) {
+ v = t.task.view();
+ return v.listViews.comments.modelViewList.htmlList.toggleBodyAndShowUp();
+ }
+ };
+
+ Vimbanery.prototype.expandSubtasks = function() {
+ var t, v;
+ if (t = this.currentTask()) {
+ v = t.task.view();
+ return v.listViews.subtasks.modelViewList.htmlList.toggleBodyAndShowUp();
+ }
+ };
+
+ Vimbanery.prototype.addComment = function() {
+ var t, v;
+ if (t = this.currentTask()) {
+ v = t.task.view();
+ if (t.task.find('.comments-section .add-item-link:visible').length === 0) {
+ this.expandComments();
+ }
+ return t.task.find('.comments-section .add-item-link:visible').click();
+ }
+ };
+
+ Vimbanery.prototype.addSubtask = function() {
+ var t, v;
+ if (t = this.currentTask()) {
+ v = t.task.view();
+ if (t.task.find('.subtasks-section .add-item-link:visible').length === 0) {
+ this.expandSubtasks();
+ }
+ return t.task.find('.subtasks-section .add-item-link:visible').click();
+ }
+ };
+
+ Vimbanery.prototype.columns = function(view) {
+ return view.parentView().modelViewList.htmlList.parentView().parentView().modelViewList.all();
+ };
+
+ Vimbanery.prototype.goIcebox = function() {
+ return console.log('icebox');
+ };
+
+ Vimbanery.prototype.currentTask = function() {
+ var col, pos, task;
+ task = $('.task-view.v-focus:first');
+ if (task.length > 0) {
+ pos = task.index();
+ col = task.parents('.column-view').index();
+ return {
+ task: task,
+ col: col,
+ pos: pos
+ };
+ }
+ };
+
+ Vimbanery.prototype.focusTask = function(col, pos) {
+ var task;
+ $('.task-view').removeClass('v-focus');
+ task = $(".column-view:eq(" + col + ") .task-view:eq(" + pos + ")").addClass('v-focus');
+ task.view().loadBody();
+ this.showTask(task);
+ return task;
+ };
+
+ Vimbanery.prototype.showTask = function(task) {
+ var list;
+ list = task.parents('.tasks-section-body');
+ if (!this.taskVisible(list, task)) {
+ return list.scrollTo(task, {
+ axis: 'y'
+ });
+ }
+ };
+
+ Vimbanery.prototype.taskVisible = function(parent, task) {
+ var elemBottom, elemTop, parentViewBottom, parentViewTop;
+ parentViewTop = $(parent).offset().top + $(parent).scrollTop();
+ parentViewBottom = parentViewTop + $(parent).height();
+ elemTop = $(task).offset().top;
+ elemBottom = elemTop + $(task).height();
+ return (elemBottom >= parentViewTop) && (elemTop <= parentViewBottom) && (elemBottom <= parentViewBottom) && (elemTop >= parentViewTop);
+ };
+
+ Vimbanery.prototype.columnsCount = function() {
+ return $(".column-view").length;
+ };
+
+ Vimbanery.prototype.tasksCount = function(col) {
+ return $(".column-view:eq(" + col + ") .task-view").length;
+ };
+
+ return Vimbanery;
+
+})();
+
+if (window.Application) v = new Vimbanery();
24 manifest.json
@@ -0,0 +1,24 @@
+{
+ "name": "Vimbanery",
+ "version": "0.1",
+ "description": "Vim-like keybindings for Kanbanery",
+
+ "content_scripts": [{
+ "matches": [
+ "https://*.kanbanery.com/projects/*/board",
+ "http://smackaho.st/projects/*/board"
+ ],
+
+ "js": [
+ "js/keycodes.js",
+ "js/proxy.js",
+ "js/key-sequence-handler.js",
+ "js/vimbanery.js",
+ "js/content.js"
+ ],
+
+ "css": [
+ "css/vimbanery.css"
+ ]
+ }]
+}

0 comments on commit b1cf58b

Please sign in to comment.