Skip to content

Commit

Permalink
Merge branch 'keep-polling' into reedsy
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrosanta committed Aug 23, 2015
2 parents 5b79426 + 6d77a13 commit 55145df
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 12 deletions.
2 changes: 1 addition & 1 deletion config/grunt/dist.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ module.exports = (grunt) ->
modifier: 'modern'
include: [
'difference', 'intersection', 'last'
'all', 'each', 'find', 'invoke', 'map', 'reduce'
'all', 'each', 'find', 'invoke', 'map', 'reduce', 'partition',
'bind', 'defer', 'partial'
'clone', 'extend', 'defaults', 'omit', 'values'
'isElement', 'isEqual', 'isFunction', 'isNumber', 'isObject', 'isString'
Expand Down
12 changes: 9 additions & 3 deletions src/core/editor.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,25 @@ class Editor
@delta = @doc.toDelta()
@length = @delta.length()
@selection = new Selection(@doc, @quill)
@timer = setInterval(_.bind(this.checkUpdate, this), @options.pollInterval)
@startPolling()
@savedRange = null;
@quill.on("selection-change", (range) =>
@savedRange = range
)
this.enable() unless @options.readOnly

destroy: ->
clearInterval(@timer)
@stopPolling()

disable: ->
this.enable(false)

startPolling: ->
@timer = setInterval(_.bind(this.checkUpdate, this), @options.pollInterval)

stopPolling: ->
clearInterval(@timer)

enable: (enabled = true) ->
@root.setAttribute('contenteditable', enabled)

Expand Down Expand Up @@ -67,7 +73,7 @@ class Editor
@quill.emit(@quill.constructor.events.TEXT_CHANGE, localDelta, Editor.sources.USER)

checkUpdate: (source = 'user') ->
return clearInterval(@timer) unless @root.parentNode?
return @stopPolling() unless @root.parentNode?
delta = this._update()
if delta
@delta = @delta.compose(delta)
Expand Down
3 changes: 2 additions & 1 deletion src/core/line.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ class Line extends LinkedList.Node
[leaf, leafOffset] = this.findLeafAt(offset)
node = _.reduce(formats, (node, value, name) =>
format = @doc.formats[name]
node = format.add(node, value) if format?
if format? and !format.isType(Format.types.LINE)
node = format.add(node, value)
return node
, node)
[prevNode, nextNode] = dom(leaf.node).split(leafOffset)
Expand Down
14 changes: 14 additions & 0 deletions src/core/selection.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ class Selection
else
fn()

scrollIntoView: () ->
return unless @range
editor = @emitter.editor
startBounds = editor.getBounds(@range.start)
endBounds = if @range.isCollapsed() then startBounds else editor.getBounds(@range.end)
containerBounds = editor.root.parentNode.getBoundingClientRect()
containerHeight = containerBounds.bottom - containerBounds.top
if containerHeight < endBounds.top + endBounds.height
[line, offset] = editor.doc.findLineAt(@range.end)
line.node.scrollIntoView(false)
else if startBounds.top < 0
[line, offset] = editor.doc.findLineAt(@range.start)
line.node.scrollIntoView()

setRange: (range, source) ->
if range?
[startNode, startOffset] = this._indexToPosition(range.start)
Expand Down
17 changes: 16 additions & 1 deletion src/modules/keyboard.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ class Keyboard
@hotkeys[which].push(hotkey)
)

removeHotkeys: (hotkey, callback) ->
hotkey = if _.isString(hotkey) then hotkey.toUpperCase() else hotkey
hotkey = if Keyboard.hotkeys[hotkey] then Keyboard.hotkeys[hotkey] else hotkey
hotkey = if _.isObject(hotkey) then hotkey else { key: hotkey }
which = if _.isNumber(hotkey.key) then hotkey.key else hotkey.key.charCodeAt(0)
@hotkeys[which] ?= []
[removed, kept] = _.partition(@hotkeys[which], (handler) ->
_.isEqual(hotkey, _.omit(handler, 'callback')) and
(!callback or callback == handler.callback)
)
@hotkeys[which] = kept
return _.map(removed, 'callback')

toggleFormat: (range, format) ->
if range.isCollapsed()
delta = @quill.getContents(Math.max(0, range.start-1), range.end)
Expand Down Expand Up @@ -60,6 +73,7 @@ class Keyboard
@toolbar.setActive(format, value) if @toolbar?
return
)
@quill.editor.selection.scrollIntoView()
return false
)

Expand All @@ -78,6 +92,7 @@ class Keyboard
@quill.deleteText(range.start - 1, range.start, Quill.sources.USER)
else if range.start < @quill.getLength() - 1
@quill.deleteText(range.start, range.start + 1, Quill.sources.USER)
@quill.editor.selection.scrollIntoView()
return false
)

Expand All @@ -92,7 +107,7 @@ class Keyboard
)
_.each(['bold', 'italic', 'underline'], (format) =>
this.addHotkey(Keyboard.hotkeys[format.toUpperCase()], (range) =>
if (@quill.options.formats.indexOf(format) > -1)
if (@quill.editor.doc.formats[format])
this.toggleFormat(range, format)
return false
)
Expand Down
8 changes: 6 additions & 2 deletions src/modules/multi-cursor.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,12 @@ class MultiCursor extends EventEmitter2

shiftCursors: (index, length, authorId = null) ->
_.each(@cursors, (cursor, id) =>
return unless cursor and (cursor.index > index or cursor.userId == authorId)
cursor.index += Math.max(length, index - cursor.index)
return unless cursor
shift = Math.max(length, index - cursor.index)
if cursor.userId == authorId
this.moveCursor(authorId, cursor.index + shift)
else if cursor.index > index
cursor.index += shift
)

update: ->
Expand Down
6 changes: 2 additions & 4 deletions src/modules/paste-manager.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class PasteManager
constructor: (@quill, options) ->
@container = @quill.addContainer('ql-paste-manager')
@container.setAttribute('contenteditable', true)
@container.setAttribute('tabindex', '-1')
dom(@quill.root).on('paste', _.bind(this._paste, this))
@options = _.defaults(options, PasteManager.DEFAULTS)
@options.onConvert ?= this._onConvert;
Expand Down Expand Up @@ -38,10 +39,7 @@ class PasteManager
@quill.updateContents(delta, 'user')
@quill.setSelection(range.start + lengthAdded, range.start + lengthAdded)
# Make sure bottom of pasted content is visible
[line, offset] = @quill.editor.doc.findLineAt(range.start + lengthAdded)
lineBottom = line.node.getBoundingClientRect().bottom
windowBottom = document.documentElement.clientHeight
line.node.scrollIntoView(false) if lineBottom > windowBottom
@quill.editor.selection.scrollIntoView()
@container.innerHTML = ""
)

Expand Down
4 changes: 4 additions & 0 deletions test/unit/core/line.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ describe('Line', ->
initial: '<b>01</b>'
expected: '<b>0</b><img src="http://quilljs.com/images/cloud.png"><b>1</b>'
offset: 1, formats: { image: 'http://quilljs.com/images/cloud.png' }
'line formats ignored':
initial: 'ab'
expected: 'a<b>|</b>b'
offset: 1, formats: { list: true, bold: true }

_.each(tests, (test, name) ->
it(name, ->
Expand Down
45 changes: 45 additions & 0 deletions test/unit/core/selection.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -324,4 +324,49 @@ describe('Selection', ->
)
)
)

describe('scrollIntoView()', ->
beforeEach( ->
@container.innerHTML = '<div><span style="font-size: 36px;">a<br>b<br>c<br>d</span></div>'
@quill = new Quill(@container.firstChild)
@editor = @quill.editor
@selection = @editor.selection
@height = 80
@editor.root.parentNode.style.height = @height.toString() + "px"
@editor.root.parentNode.style.overflow = "auto"
)

it('scrolls down when cursor too low', ->
@selection.setRange(new Quill.Lib.Range(7, 7))
bounds = @editor.getBounds(7)
expect(bounds.top).toBeGreaterThan(@height)
@selection.scrollIntoView()
bounds = @editor.getBounds(7)
expect(bounds.top).not.toBeLessThan(0)
expect(bounds.top + bounds.height).not.toBeGreaterThan(@height)
)

it('scrolls up when cursor too high', ->
@selection.setRange(new Quill.Lib.Range(1, 1))
@editor.root.parentNode.scrollTop = 100
bounds = @editor.getBounds(1)
expect(bounds.top + bounds.height).toBeLessThan(0)
@selection.scrollIntoView()
bounds = @editor.getBounds(1)
expect(bounds.top).not.toBeLessThan(0)
expect(bounds.top + bounds.height).not.toBeGreaterThan(@height)
)

it('does not scroll if cursor in view', ->
@selection.setRange(new Quill.Lib.Range(1, 1))
bounds = @editor.getBounds(1)
expect(bounds.top).not.toBeLessThan(0)
expect(bounds.top + bounds.height).not.toBeGreaterThan(@height)
@selection.scrollIntoView()
newBounds = @editor.getBounds(1)
expect(bounds.top).toBeApproximately(newBounds.top, 1)
expect(bounds.height).toBeApproximately(newBounds.height, 1)
expect(bounds.left).toBeApproximately(newBounds.left, 1)
)
)
)
38 changes: 38 additions & 0 deletions test/unit/modules/keyboard.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,43 @@ describe('Keyboard', ->
expect(dom($('.ql-bold').get(0)).hasClass('ql-active')).toBe(true)
expect(dom($('.ql-size').get(0)).value()).toBe(size)
)

it('removeHotkeys by name', ->
counter = 0
fn = -> counter += 1
keyboard = @quill.getModule('keyboard')
keyboard.addHotkey('S', fn)
dom(@quill.root).trigger('keydown', { key: 'S' })
expect(counter).toBe(1)
result = keyboard.removeHotkeys('S', fn)
expect(result.length).toBe(1)
expect(result[0]).toBe(fn);
dom(@quill.root).trigger('keydown', { key: 'S' })
expect(counter).toBe(1)
)

it('removeHotkeys by object', ->
counter = 0
fn = -> counter += 1
keyboard = @quill.getModule('keyboard')
keyboard.addHotkey({ key: 'S', metaKey: true }, fn)
dom(@quill.root).trigger('keydown', { key: 'S', metaKey: true })
result = keyboard.removeHotkeys({ key: 'S', metaKey: true })
expect(result.length).toBe(1)
expect(result[0]).toBe(fn)
dom(@quill.root).trigger('keydown', { key: 'S', metaKey: true })
expect(counter).toBe(1)
)

it('removeHotKeys only the specified callback', ->
fn = ->
anotherFn = ->
keyboard = @quill.getModule('keyboard')
keyboard.addHotkey({ key: 'S', metaKey: true }, fn)
keyboard.addHotkey({ key: 'S', metaKey: true }, anotherFn)
result = keyboard.removeHotkeys({ key: 'S', metaKey: true }, fn)
expect(result.length).toBe(1)
expect(result[0]).toBe(fn)
)
)
)

0 comments on commit 55145df

Please sign in to comment.