Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: slab/quill
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: lever/quill
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 20 commits
  • 12 files changed
  • 8 contributors

Commits on Nov 10, 2015

  1. Alternative fix for #432 that also works with formats added via quill…

    ….addFormat()
    thomsbg authored and jhchen committed Nov 10, 2015
    Copy the full SHA
    fa9ee27 View commit details
  2. Copy the full SHA
    a03bb8e View commit details
  3. update rich-text for concat

    jhchen committed Nov 10, 2015
    Copy the full SHA
    1bd1b55 View commit details
  4. Add removeHotkey to keyboard module, closes #110

    Takes a string, number, or hotkey object.
    thomsbg authored and jhchen committed Nov 10, 2015
    Copy the full SHA
    f67dd2b View commit details
  5. Rename to removeHotkeys to indicate that an array is returned, and ac…

    …tually do what it says on the tin
    thomsbg authored and jhchen committed Nov 10, 2015
    Copy the full SHA
    dec48b7 View commit details
  6. Copy the full SHA
    846ffd8 View commit details
  7. Fix upstream/#306

    ivan-i authored and jhchen committed Nov 10, 2015
    Copy the full SHA
    f05e07f View commit details
  8. Added soruce for the change as user

    HHogg authored and jhchen committed Nov 10, 2015
    Copy the full SHA
    346b4e7 View commit details
  9. workaround for and closes #415

    jhchen committed Nov 10, 2015
    Copy the full SHA
    5fce997 View commit details
  10. fixes #288

    jhchen committed Nov 10, 2015
    Copy the full SHA
    b9f562d View commit details
  11. reimplement

    git not cherry picking 156a0f3
    jhchen committed Nov 10, 2015
    Copy the full SHA
    f1aa210 View commit details

Commits on Nov 11, 2015

  1. bump version

    jhchen committed Nov 11, 2015
    Copy the full SHA
    01d85b8 View commit details

Commits on Mar 11, 2016

  1. fixed package name and version

    Réal Provencher committed Mar 11, 2016
    Copy the full SHA
    5273169 View commit details
  2. Custom behavior for the enter key + list formats

    thomsbg authored and Réal Provencher committed Mar 11, 2016
    Copy the full SHA
    ffad69b View commit details
  3. Improve comments of peculiar enter-key behavior and add some tests

    thomsbg authored and Réal Provencher committed Mar 11, 2016
    Copy the full SHA
    ebd8dbe View commit details
  4. Extend the enter-on-empty-line behavior to work with any format with …

    …'inherit: true'
    thomsbg authored and Réal Provencher committed Mar 11, 2016
    Copy the full SHA
    59182fc View commit details
  5. when deleting a word, if it is preceded by and followed by a space, d…

    …elete one of them
    Réal Provencher committed Mar 11, 2016
    Copy the full SHA
    cc266ae View commit details
  6. fixed typo in double space deletion

    Réal Provencher committed Mar 11, 2016
    Copy the full SHA
    851990e View commit details
  7. Merge pull request #1 from lever/bugfixes-from-upstream

    Bugfixes from upstream
    Réal Provencher committed Mar 11, 2016
    Copy the full SHA
    4cb57b5 View commit details
  8. 1.1.0

    Réal Provencher committed Mar 11, 2016
    Copy the full SHA
    5c9cc1e View commit details
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "quill",
"version": "0.20.0",
"version": "0.20.1",
"homepage": "http://quilljs.com",
"authors": ["Jason Chen <jhchen7@gmail.com>"],
"contributors": [
2 changes: 1 addition & 1 deletion config/grunt/dist.coffee
Original file line number Diff line number Diff line change
@@ -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'
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "quill",
"version": "0.20.0",
"name": "lever-quill",
"version": "1.1.0",
"description": "Cross browser rich text editor",
"author": "Jason Chen <jhchen7@gmail.com>",
"homepage": "http://quilljs.com",
@@ -50,7 +50,7 @@
"lodash": "~3.9.1",
"lodash-cli": "~3.9.3",
"protractor": "~2.1.0",
"rich-text": "~2.0.0",
"rich-text": "~2.1.0",
"stylify": "~1.0.0",
"stylus": "~0.51.1",
"through": "~2.3.7",
@@ -65,10 +65,10 @@
"license": "BSD-3-Clause",
"repository": {
"type": "git",
"url": "https://github.com/quilljs/quill"
"url": "https://github.com/lever/quill"
},
"bugs": {
"url": "https://github.com/quilljs/quill/issues"
"url": "https://github.com/lever/quill/issues"
},
"scripts": {
"prepublish": "grunt dist",
2 changes: 2 additions & 0 deletions src/core/format.coffee
Original file line number Diff line number Diff line change
@@ -69,12 +69,14 @@ class Format
bullet:
type: Format.types.LINE
exclude: 'list'
inherit: true
parentTag: 'UL'
tag: 'LI'

list:
type: Format.types.LINE
exclude: 'bullet'
inherit: true
parentTag: 'OL'
tag: 'LI'

3 changes: 2 additions & 1 deletion src/core/normalizer.coffee
Original file line number Diff line number Diff line change
@@ -57,9 +57,10 @@ class Normalizer
node.removeAttribute(attribute)
)
# Chrome turns <b> into style in some cases
if (node.style.fontWeight == 'bold')
if (node.style.fontWeight == 'bold' or node.style.fontWeight > 500)
node.style.fontWeight = ''
dom(node).wrap(document.createElement('b'))
node = node.parentNode
this.whitelistStyles(node)
return this.whitelistTags(node)

14 changes: 14 additions & 0 deletions src/core/selection.coffee
Original file line number Diff line number Diff line change
@@ -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)
49 changes: 46 additions & 3 deletions src/modules/keyboard.coffee
Original file line number Diff line number Diff line change
@@ -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)
@@ -53,31 +66,61 @@ class Keyboard
return true unless range?
[line, offset] = @quill.editor.doc.findLineAt(range.start)
[leaf, offset] = line.findLeafAt(offset)
delta = new Delta().retain(range.start).insert('\n', line.formats).delete(range.end - range.start)
delta = new Delta().retain(range.start)
removeInheritedFormats = _.reduce(line.formats, (formats, value, name) =>
format = @quill.editor.doc.formats[name]
if format and format.isType('line') and format.config.inherit
formats[name] = false
return formats
, {})
removeNonInheritedFormats = _.reduce(line.formats, (formats, value, name) =>
format = @quill.editor.doc.formats[name]
if format and format.isType('line') and !format.config.inherit
formats[name] = false
return formats
, {})

# if on an empty line, remove the inheritable formats
if range.isCollapsed() and line.length == 1 and Object.keys(removeInheritedFormats).length > 0
delta.retain(1, removeInheritedFormats)
else
delta.insert('\n', line.formats).delete(range.end - range.start)

# if creating a new empty line (was at the end of the old line),
# remove line formats from the new line that should not be inherited
if !leaf.next and offset == leaf.length
delta.retain(1, removeNonInheritedFormats)

@quill.updateContents(delta, Quill.sources.USER)
_.each(leaf.formats, (value, format) =>
@quill.prepareFormat(format, value)
@toolbar.setActive(format, value) if @toolbar?
return
)
@quill.editor.selection.scrollIntoView()
return false
)

_initDeletes: ->
this.addHotkey([dom.KEYS.DELETE, dom.KEYS.BACKSPACE], (range, hotkey) =>
if range? and @quill.getLength() > 0
if range.start != range.end
before = @quill.getText(Math.max(range.start - 1, 0), range.start)
after = @quill.getText(range.end, Math.min(range.end + 1, @quill.getLength()))
if ' ' == before == after
range.start = range.start - 1
@quill.deleteText(range.start, range.end, Quill.sources.USER)
else
if hotkey.key == dom.KEYS.BACKSPACE
[line, offset] = @quill.editor.doc.findLineAt(range.start)
if offset == 0 and (line.formats.bullet or line.formats.list)
format = if line.formats.bullet then 'bullet' else 'list'
@quill.formatLine(range.start, range.start, format, false)
@quill.formatLine(range.start, range.start, format, false, Quill.sources.USER)
else if range.start > 0
@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
)

@@ -92,7 +135,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
)
1 change: 1 addition & 0 deletions src/modules/multi-cursor.coffee
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ class MultiCursor extends EventEmitter2

moveCursor: (userId, index) ->
cursor = @cursors[userId]
return unless cursor?
cursor.index = index
dom(cursor.elem).removeClass('hidden')
clearTimeout(cursor.timer)
6 changes: 2 additions & 4 deletions src/modules/paste-manager.coffee
Original file line number Diff line number Diff line change
@@ -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;
@@ -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 = ""
)

3 changes: 2 additions & 1 deletion src/modules/toolbar.coffee
Original file line number Diff line number Diff line change
@@ -56,8 +56,9 @@ class Toolbar
@quill.focus()
range = @quill.getSelection()
callback(range, value) if range?
@quill.editor.selection.scrollIntoView() if dom.isIE(11)
@preventUpdate = false
return true
return false
)

setActive: (format, value) ->
45 changes: 45 additions & 0 deletions test/unit/core/selection.coffee
Original file line number Diff line number Diff line change
@@ -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)
)
)
)
87 changes: 87 additions & 0 deletions test/unit/modules/keyboard.coffee
Original file line number Diff line number Diff line change
@@ -31,6 +31,55 @@ describe('Keyboard', ->
)
)

describe('enter key', ->
beforeEach( ->
@container.innerHTML = '<div><div>01234</div></div>'
@quill = new Quill(@container.firstChild)
@keyboard = @quill.getModule('keyboard')
@enterKey = { key: dom.KEYS.ENTER }
)

it('inserts a newline', ->
@quill.setSelection(2, 2)
dom(@quill.root).trigger('keydown', @enterKey)
expect(@quill.root).toEqualHTML('<div>01</div><div>234</div>')
)

it('replaces selected text with a newline', ->
@quill.setSelection(2, 3)
dom(@quill.root).trigger('keydown', @enterKey)
expect(@quill.root).toEqualHTML('<div>01</div><div>34</div>')
)

it('inherits line formats when breaking a line in two', ->
@quill.formatText(5, 6, 'align', 'right')
@quill.setSelection(2, 2)
dom(@quill.root).trigger('keydown', @enterKey)
expect(@quill.root).toEqualHTML('<div style="text-align: right;">01</div><div style="text-align: right;">234</div>')
)

it('does not inherit line formats when inserting a blank line', ->
@quill.formatText(5, 6, 'align', 'right')
@quill.setSelection(5, 5)
dom(@quill.root).trigger('keydown', @enterKey)
expect(@quill.root).toEqualHTML('<div style="text-align: right;">01234</div><div><br></div>')
)

it('inherits line formats for formats with "inherit: true" specified', ->
@quill.formatText(5, 6, 'list', true)
@quill.setSelection(5, 5)
dom(@quill.root).trigger('keydown', @enterKey)
expect(@quill.root).toEqualHTML('<ol><li>01234</li><li><br></li></ol>')
)

it('removes the list format when starting from an empty line', ->
@quill.setHTML('<ol><li>one</li><li><br></li></ol>')
@quill.setSelection(4, 4)
dom(@quill.root).trigger('keydown', @enterKey)
expect(@quill.root).toEqualHTML('<ol><li>one</li></ol><div><br></div>')
)
)

describe('hotkeys', ->
beforeEach( ->
@container.innerHTML = '<div><div>0123</div></div>'
@@ -80,5 +129,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)
)
)
)