scribe-plugin-curly-quotes
Advanced tools
Comparing version 0.1.2 to 0.1.4
@@ -5,4 +5,4 @@ { | ||
"requirejs": "~2.1.9", | ||
"scribe": "~0.1.10" | ||
"scribe": "guardian/scribe#master" | ||
} | ||
} |
{ | ||
"name": "scribe-plugin-curly-quotes", | ||
"version": "0.1.2", | ||
"version": "0.1.4", | ||
"main": "src/scribe-plugin-curly-quotes.js", | ||
@@ -16,3 +16,3 @@ "devDependencies": { | ||
"selenium-webdriver": "~2.41.0", | ||
"scribe-test-harness": "~0.0.1" | ||
"scribe-test-harness": "~0.0.6" | ||
}, | ||
@@ -19,0 +19,0 @@ "scripts": { |
@@ -23,5 +23,11 @@ /* | ||
glob('src/scribe-plugin-curly-quotes.js'), | ||
requireJS(), | ||
requireJS({ | ||
// FIXME: auto? | ||
preserveLicenseComments: false, | ||
paths: { | ||
'lodash-amd': '../bower_components/lodash-amd', | ||
} | ||
}), | ||
writeBoth | ||
]; | ||
}; |
@@ -1,2 +0,2 @@ | ||
define(function () { | ||
define([], function () { | ||
@@ -7,7 +7,2 @@ 'use strict'; | ||
var keys = { | ||
34: '"', | ||
39: '\'' | ||
}; | ||
var openDoubleCurly = '“'; | ||
@@ -22,56 +17,38 @@ var closeDoubleCurly = '”'; | ||
return function (scribe) { | ||
// Substitute quotes while typing | ||
scribe.el.addEventListener('keypress', input); | ||
/** | ||
* Run the formatter as you type on the current paragraph. | ||
* | ||
* FIXME: We wouldn't have to do this if the formatters were run on text | ||
* node mutations, but that's expensive unil we have a virtual DOM. | ||
*/ | ||
// Substitute quotes on setting content or paste | ||
scribe.registerHTMLFormatter('normalize', substituteCurlyQuotes); | ||
var keys = { | ||
34: '"', | ||
39: '\'' | ||
}; | ||
var curlyQuoteChar; | ||
function input(event) { | ||
var curlyChar; | ||
// `input` doesn't tell us what key was pressed, so we grab it beforehand | ||
scribe.el.addEventListener('keypress', function (event) { | ||
curlyQuoteChar = keys[event.charCode]; | ||
}); | ||
// If previous char is real content, close quote; else, open | ||
// TODO: annoying Chrome/Firefox | ||
var currentChar = keys[event.charCode]; | ||
if (currentChar === '"') { | ||
if (wordBeforeSelectedRange()) { | ||
curlyChar = closeDoubleCurly; | ||
} else { | ||
curlyChar = openDoubleCurly; | ||
} | ||
} else if (currentChar === '\'') { | ||
if (wordBeforeSelectedRange()) { | ||
curlyChar = closeSingleCurly; | ||
} else { | ||
curlyChar = openSingleCurly; | ||
} | ||
} | ||
// Substitute entered char with curly replacement | ||
if (curlyChar) { | ||
event.preventDefault(); | ||
scribe.transactionManager.run(function() { | ||
var quoteText = replaceSelectedRangeWith(curlyChar); | ||
placeCaretAfter(quoteText); | ||
// When the character is actually inserted, format it to transform. | ||
scribe.el.addEventListener('input', function (event) { | ||
if (curlyQuoteChar) { | ||
var selection = new scribe.api.Selection(); | ||
var pElement = selection.getContaining(function (node) { | ||
return node.nodeName === 'P'; | ||
}); | ||
selection.placeMarkers(); | ||
pElement.innerHTML = substituteCurlyQuotes(pElement.innerHTML); | ||
selection.selectMarkers(); | ||
// Reset | ||
curlyQuoteChar = undefined; | ||
} | ||
} | ||
}); | ||
function wordBeforeSelectedRange() { | ||
var prevChar = charBeforeSelectedRange() || ''; | ||
return isWordCharacter(prevChar); | ||
} | ||
// Substitute quotes on setting content or paste | ||
scribe.registerHTMLFormatter('normalize', substituteCurlyQuotes); | ||
function charBeforeSelectedRange() { | ||
var selection = new scribe.api.Selection(); | ||
var context = selection.range.commonAncestorContainer.textContent; | ||
return context[selection.range.startOffset - 1]; | ||
} | ||
function charAfterSelectedRange() { | ||
var selection = new scribe.api.Selection(); | ||
var context = selection.range.commonAncestorContainer.textContent; | ||
return context[selection.range.endOffset]; | ||
} | ||
function isWordCharacter(character) { | ||
@@ -81,23 +58,2 @@ return /[^\s()]/.test(character); | ||
/** Delete any selected text, insert text instead */ | ||
function replaceSelectedRangeWith(text) { | ||
var textNode = document.createTextNode(text); | ||
var selection = new scribe.api.Selection(); | ||
selection.range.deleteContents(); | ||
selection.range.insertNode(textNode); | ||
return textNode; | ||
} | ||
function placeCaretAfter(node) { | ||
var rangeAfter = document.createRange(); | ||
rangeAfter.setStartAfter(node); | ||
rangeAfter.setEndAfter(node); | ||
var selection = new scribe.api.Selection(); | ||
selection.selection.removeAllRanges(); | ||
selection.selection.addRange(rangeAfter); | ||
} | ||
function substituteCurlyQuotes(html) { | ||
@@ -104,0 +60,0 @@ // We don't want to replace quotes within the HTML markup |
@@ -9,4 +9,5 @@ var chai = require('chai'); | ||
var given = helpers.given; | ||
var initializeScribe = helpers.initializeScribe.bind(null, 'scribe'); | ||
var initializeScribe = helpers.initializeScribe.bind(null, '../../bower_components/scribe/src/scribe'); | ||
var seleniumBugs = helpers.seleniumBugs; | ||
var givenContentOf = helpers.givenContentOf; | ||
var browserName = helpers.browserName; | ||
@@ -51,3 +52,3 @@ | ||
// Firefox (23, 24, 25): '<p>“””<br></p>' | ||
expect(innerHTML).to.have.html('<p>“<bogus-br></p>'); | ||
expect(innerHTML).to.have.html('<p>“<firefox-bogus-br></p>'); | ||
}); | ||
@@ -184,3 +185,3 @@ }); | ||
return driver.executeScript(function () { | ||
window.scribe.insertHTML("<p>Hello 'world'!</p>"); | ||
window.scribe.insertHTML('<p>Hello \'world\'!</p>'); | ||
}); | ||
@@ -199,3 +200,3 @@ }); | ||
return driver.executeScript(function () { | ||
window.scribe.insertHTML("<p>'Hello world!'</p>"); | ||
window.scribe.insertHTML('<p>\'Hello world!\'</p>'); | ||
}); | ||
@@ -215,3 +216,3 @@ }); | ||
// Misplaced inline elements wrt whitespace, but can happen | ||
window.scribe.insertHTML("<p>'<em>Hello world!</em>' <strong>And </strong>'other'<strong> example</strong></p>"); | ||
window.scribe.insertHTML('<p>\'<em>Hello world!</em>\' <strong>And </strong>\'other\'<strong> example</strong></p>'); | ||
}); | ||
@@ -230,3 +231,3 @@ }); | ||
return driver.executeScript(function () { | ||
window.scribe.insertHTML("<p>1\n'<em>2</em>'\n3</p>"); | ||
window.scribe.insertHTML('<p>1\n\'<em>2</em>\'\n3</p>'); | ||
}); | ||
@@ -237,3 +238,3 @@ }); | ||
return scribeNode.getInnerHTML().then(function (innerHTML) { | ||
expect(innerHTML).to.equal("<p>1\n‘<em>2</em>’\n3</p>"); | ||
expect(innerHTML).to.equal('<p>1\n‘<em>2</em>’\n3</p>'); | ||
}); | ||
@@ -247,3 +248,3 @@ }); | ||
// Misplaced inline elements wrt whitespace, but can happen | ||
window.scribe.insertHTML("<p>'<em>Hello world!</em>'</p>"); | ||
window.scribe.insertHTML('<p>\'<em>Hello world!</em>\'</p>'); | ||
}); | ||
@@ -265,3 +266,3 @@ }); | ||
return driver.executeScript(function () { | ||
window.scribe.insertHTML("<p><em class='foo'>Just text</em></p>"); | ||
window.scribe.insertHTML('<p><em class=\'foo\'>Just text</em></p>'); | ||
}); | ||
@@ -281,3 +282,3 @@ }); | ||
return driver.executeScript(function () { | ||
window.scribe.insertHTML("<p><p class='foo'>1</p></p>"); | ||
window.scribe.insertHTML('<p><p class=\'foo\'>1</p></p>'); | ||
}); | ||
@@ -288,3 +289,3 @@ }); | ||
return scribeNode.getInnerHTML().then(function (innerHTML) { | ||
expect(innerHTML).to.equal("<p><p class='foo'>1</p></p>"); | ||
expect(innerHTML).to.equal('<p><p class=\'foo\'>1</p></p>'); | ||
}); | ||
@@ -399,2 +400,44 @@ }); | ||
}); | ||
givenContentOf('<|>', function () { | ||
when('the user types an ascii single quote', function () { | ||
beforeEach(function () { | ||
return scribeNode.sendKeys('\''); | ||
}); | ||
it('should not convert to a curly single quote', function () { | ||
return scribeNode.getInnerHTML().then(function (innerHTML) { | ||
expect(innerHTML).to.have.html('<p><\'></p>'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
givenContentOf('<|>', function () { | ||
when('the user types an ascii double quote', function () { | ||
beforeEach(function () { | ||
return scribeNode.sendKeys('"'); | ||
}); | ||
it('should not convert to a curly double quote', function () { | ||
return scribeNode.getInnerHTML().then(function (innerHTML) { | ||
expect(innerHTML).to.have.html('<p><"></p>'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
givenContentOf('<foo|>', function () { | ||
when('the user types content containing ascii single quotes (e.g. HTML attributes)', function () { | ||
beforeEach(function () { | ||
return scribeNode.sendKeys(' bar=\'baz\''); | ||
}); | ||
it('should not convert to curly single quotes', function () { | ||
return scribeNode.getInnerHTML().then(function (innerHTML) { | ||
expect(innerHTML).to.have.html('<p><foo bar=\'baz\'></p>'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -7,7 +7,5 @@ var Mocha = require('mocha'); | ||
/** | ||
* FIXME: We have to set a ridiculous timeout (20 minutes) because Travis’ | ||
* concurrent builds will sometimes exceed Sauce Labs’ concurrency. We should | ||
* track the following issue to add an option to Travis for limiting | ||
* concurrency: https://github.com/travis-ci/travis-ci/issues/1366 | ||
* Wait for the connection to Sauce Labs to finish. | ||
*/ | ||
mocha.timeout(15 * 1000); | ||
mocha.timeout(1200000); | ||
@@ -14,0 +12,0 @@ mocha.reporter('spec'); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
25686
17
511