Commit 85d4e5b2 authored by Randall Leeds's avatar Randall Leeds

Actually remove jquery.scrollintoview totally

This change requires us to introduce proxyquire for stubbing modules
that have a single function as their default export, such as the
scroll-into-view module.
parent 06b379d2
baseURI = require('base-url')() baseURI = require('base-url')()
scrollIntoView = require('scroll-into-view')
Annotator = require('annotator') Annotator = require('annotator')
Annotator.Plugin.CrossFrame = require('./plugin/cross-frame')
$ = Annotator.$ $ = Annotator.$
highlighter = require('./highlighter') highlighter = require('./highlighter')
...@@ -117,7 +120,7 @@ module.exports = class Guest extends Annotator ...@@ -117,7 +120,7 @@ module.exports = class Guest extends Annotator
crossframe.on 'scrollToAnnotation', (tag) => crossframe.on 'scrollToAnnotation', (tag) =>
for anchor in @anchors when anchor.highlights? for anchor in @anchors when anchor.highlights?
if anchor.annotation.$$tag is tag if anchor.annotation.$$tag is tag
$(anchor.highlights).scrollintoview() scrollIntoView(anchor.highlights[0])
return return
crossframe.on 'getDocumentInfo', (cb) => crossframe.on 'getDocumentInfo', (cb) =>
this.getDocumentInfo() this.getDocumentInfo()
......
Annotator = require('annotator') highlighter = {}
Guest = require('../guest') anchoring = {}
anchoring = require('../anchoring/html')
highlighter = require('../highlighter') CrossFrame = sinon.stub()
CrossFrame['@noCallThru'] = true
scrollIntoView = sinon.stub()
scrollIntoView['@noCallThru'] = true
proxyquire = require('proxyquire')
Guest = proxyquire('../guest', {
'./highlighter': highlighter,
'./anchoring/html': anchoring,
'./plugin/cross-frame': CrossFrame,
'scroll-into-view': scrollIntoView,
})
$ = require('jquery')
waitForSync = (annotation) -> waitForSync = (annotation) ->
if annotation.$anchors? if annotation.$anchors?
...@@ -20,14 +34,14 @@ describe 'Guest', -> ...@@ -20,14 +34,14 @@ describe 'Guest', ->
beforeEach -> beforeEach ->
sandbox = sinon.sandbox.create() sandbox = sinon.sandbox.create()
fakeCrossFrame = {
onConnect: sinon.stub()
on: sinon.stub()
sync: sinon.stub()
}
fakeCrossFrame = CrossFrame.reset()
onConnect: sandbox.stub() CrossFrame.returns(fakeCrossFrame)
on: sandbox.stub()
sync: sandbox.stub()
Annotator.Plugin.CrossFrame = -> fakeCrossFrame
sandbox.spy(Annotator.Plugin, 'CrossFrame')
afterEach -> afterEach ->
sandbox.restore() sandbox.restore()
...@@ -36,13 +50,13 @@ describe 'Guest', -> ...@@ -36,13 +50,13 @@ describe 'Guest', ->
it 'provides an event bus for the annotation sync module', -> it 'provides an event bus for the annotation sync module', ->
guest = createGuest() guest = createGuest()
options = Annotator.Plugin.CrossFrame.lastCall.args[1] options = CrossFrame.lastCall.args[1]
assert.isFunction(options.on) assert.isFunction(options.on)
assert.isFunction(options.emit) assert.isFunction(options.emit)
it 'provides a formatter for the annotation sync module', -> it 'provides a formatter for the annotation sync module', ->
guest = createGuest() guest = createGuest()
options = Annotator.Plugin.CrossFrame.lastCall.args[1] options = CrossFrame.lastCall.args[1]
assert.isFunction(options.formatter) assert.isFunction(options.formatter)
it 'publishes the "panelReady" event when a connection is established', -> it 'publishes the "panelReady" event when a connection is established', ->
...@@ -58,7 +72,7 @@ describe 'Guest', -> ...@@ -58,7 +72,7 @@ describe 'Guest', ->
beforeEach -> beforeEach ->
guest = createGuest() guest = createGuest()
options = Annotator.Plugin.CrossFrame.lastCall.args[1] options = CrossFrame.lastCall.args[1]
it 'proxies the event into the annotator event system', -> it 'proxies the event into the annotator event system', ->
fooHandler = sandbox.stub() fooHandler = sandbox.stub()
...@@ -79,7 +93,7 @@ describe 'Guest', -> ...@@ -79,7 +93,7 @@ describe 'Guest', ->
beforeEach -> beforeEach ->
guest = createGuest() guest = createGuest()
options = Annotator.Plugin.CrossFrame.lastCall.args[1] options = CrossFrame.lastCall.args[1]
it 'calls deleteAnnotation when an annotationDeleted event is received', -> it 'calls deleteAnnotation when an annotationDeleted event is received', ->
ann = {id: 1, $$tag: 'tag1'} ann = {id: 1, $$tag: 'tag1'}
...@@ -131,7 +145,7 @@ describe 'Guest', -> ...@@ -131,7 +145,7 @@ describe 'Guest', ->
beforeEach -> beforeEach ->
guest = createGuest() guest = createGuest()
options = Annotator.Plugin.CrossFrame.lastCall.args[1] options = CrossFrame.lastCall.args[1]
it 'keeps an existing uri property', -> it 'keeps an existing uri property', ->
ann = {$$tag: 'tag1', uri: 'http://example.com/foo'} ann = {$$tag: 'tag1', uri: 'http://example.com/foo'}
...@@ -190,11 +204,9 @@ describe 'Guest', -> ...@@ -190,11 +204,9 @@ describe 'Guest', ->
assert.isFalse(highlight1.hasClass('annotator-hl-focused')) assert.isFalse(highlight1.hasClass('annotator-hl-focused'))
describe 'on "scrollToAnnotation" event', -> describe 'on "scrollToAnnotation" event', ->
beforeEach ->
$.fn.scrollintoview = sandbox.stub()
afterEach -> beforeEach ->
delete $.fn.scrollintoview scrollIntoView.reset()
it 'scrolls to the anchor with the matching tag', -> it 'scrolls to the anchor with the matching tag', ->
highlight = $('<span></span>') highlight = $('<span></span>')
...@@ -203,7 +215,8 @@ describe 'Guest', -> ...@@ -203,7 +215,8 @@ describe 'Guest', ->
{annotation: {$$tag: 'tag1'}, highlights: highlight.toArray()} {annotation: {$$tag: 'tag1'}, highlights: highlight.toArray()}
] ]
emitGuestEvent('scrollToAnnotation', 'tag1') emitGuestEvent('scrollToAnnotation', 'tag1')
assert.calledOn($.fn.scrollintoview, sinon.match(highlight)) assert.called(scrollIntoView)
assert.calledWith(scrollIntoView, highlight[0])
describe 'on "getDocumentInfo" event', -> describe 'on "getDocumentInfo" event', ->
guest = null guest = null
......
...@@ -66,7 +66,12 @@ module.exports = function(config) { ...@@ -66,7 +66,12 @@ module.exports = function(config) {
browserify: { browserify: {
debug: true, debug: true,
extensions: ['.coffee'] extensions: ['.coffee'],
configure: function (bundle) {
bundle
.transform('coffeeify')
.plugin('proxyquire-universal');
}
}, },
// test results reporter to use // test results reporter to use
......
/*!
* jQuery scrollintoview() plugin and :scrollable selector filter
*
* Version 1.8 (14 Jul 2011)
* Requires jQuery 1.4 or newer
*
* Copyright (c) 2011 Robert Koritnik
* Licensed under the terms of the MIT license
* http://www.opensource.org/licenses/mit-license.php
*/
(function ($) {
var converter = {
vertical: { x: false, y: true },
horizontal: { x: true, y: false },
both: { x: true, y: true },
x: { x: true, y: false },
y: { x: false, y: true }
};
var settings = {
duration: "fast",
direction: "both"
};
var rootrx = /^(?:html)$/i;
// gets border dimensions
var borders = function (domElement, styles) {
styles = styles || (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(domElement, null) : domElement.currentStyle);
var px = document.defaultView && document.defaultView.getComputedStyle ? true : false;
var b = {
top: (parseFloat(px ? styles.borderTopWidth : $.css(domElement, "borderTopWidth")) || 0),
left: (parseFloat(px ? styles.borderLeftWidth : $.css(domElement, "borderLeftWidth")) || 0),
bottom: (parseFloat(px ? styles.borderBottomWidth : $.css(domElement, "borderBottomWidth")) || 0),
right: (parseFloat(px ? styles.borderRightWidth : $.css(domElement, "borderRightWidth")) || 0)
};
return {
top: b.top,
left: b.left,
bottom: b.bottom,
right: b.right,
vertical: b.top + b.bottom,
horizontal: b.left + b.right
};
};
var dimensions = function ($element) {
var win = $(window);
var isRoot = rootrx.test($element[0].nodeName);
return {
border: isRoot ? { top: 0, left: 0, bottom: 0, right: 0} : borders($element[0]),
scroll: {
top: (isRoot ? win : $element).scrollTop(),
left: (isRoot ? win : $element).scrollLeft()
},
scrollbar: {
right: isRoot ? 0 : $element.innerWidth() - $element[0].clientWidth,
bottom: isRoot ? 0 : $element.innerHeight() - $element[0].clientHeight
},
rect: (function () {
var r = $element[0].getBoundingClientRect();
return {
top: isRoot ? 0 : r.top,
left: isRoot ? 0 : r.left,
bottom: isRoot ? $element[0].clientHeight : r.bottom,
right: isRoot ? $element[0].clientWidth : r.right
};
})()
};
};
$.fn.extend({
scrollintoview: function (options) {
/// <summary>Scrolls the first element in the set into view by scrolling its closest scrollable parent.</summary>
/// <param name="options" type="Object">Additional options that can configure scrolling:
/// duration (default: "fast") - jQuery animation speed (can be a duration string or number of milliseconds)
/// direction (default: "both") - select possible scrollings ("vertical" or "y", "horizontal" or "x", "both")
/// complete (default: none) - a function to call when scrolling completes (called in context of the DOM element being scrolled)
/// </param>
/// <return type="jQuery">Returns the same jQuery set that this function was run on.</return>
options = $.extend({}, settings, options);
options.direction = converter[typeof (options.direction) === "string" && options.direction.toLowerCase()] || converter.both;
var dirStr = "";
if (options.direction.x === true) dirStr = "horizontal";
if (options.direction.y === true) dirStr = dirStr ? "both" : "vertical";
var el = this.eq(0);
var scroller = el.closest(":scrollable(" + dirStr + ")");
// check if there's anything to scroll in the first place
if (scroller.length > 0)
{
scroller = scroller.eq(0);
var dim = {
e: dimensions(el),
s: dimensions(scroller)
};
var rel = {
top: dim.e.rect.top - (dim.s.rect.top + dim.s.border.top),
bottom: dim.s.rect.bottom - dim.s.border.bottom - dim.s.scrollbar.bottom - dim.e.rect.bottom,
left: dim.e.rect.left - (dim.s.rect.left + dim.s.border.left),
right: dim.s.rect.right - dim.s.border.right - dim.s.scrollbar.right - dim.e.rect.right
};
var animOptions = {};
// vertical scroll
if (options.direction.y === true)
{
if (rel.top < 0)
{
animOptions.scrollTop = dim.s.scroll.top + rel.top;
}
else if (rel.top > 0 && rel.bottom < 0)
{
animOptions.scrollTop = dim.s.scroll.top + Math.min(rel.top, -rel.bottom);
}
}
// horizontal scroll
if (options.direction.x === true)
{
if (rel.left < 0)
{
animOptions.scrollLeft = dim.s.scroll.left + rel.left;
}
else if (rel.left > 0 && rel.right < 0)
{
animOptions.scrollLeft = dim.s.scroll.left + Math.min(rel.left, -rel.right);
}
}
// scroll if needed
if (!$.isEmptyObject(animOptions))
{
if (rootrx.test(scroller[0].nodeName))
{
scroller = $("html,body");
}
scroller
.animate(animOptions, options.duration)
.eq(0) // we want function to be called just once (ref. "html,body")
.queue(function (next) {
$.isFunction(options.complete) && options.complete.call(scroller[0]);
next();
});
}
else
{
// when there's nothing to scroll, just call the "complete" function
$.isFunction(options.complete) && options.complete.call(scroller[0]);
}
}
// return set back
return this;
}
});
var scrollValue = {
auto: true,
scroll: true,
visible: false,
hidden: false
};
$.extend($.expr[":"], {
scrollable: function (element, index, meta, stack) {
var direction = converter[typeof (meta[3]) === "string" && meta[3].toLowerCase()] || converter.both;
var styles = (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(element, null) : element.currentStyle);
var overflow = {
x: scrollValue[styles.overflowX.toLowerCase()] || false,
y: scrollValue[styles.overflowY.toLowerCase()] || false,
isRoot: rootrx.test(element.nodeName)
};
// check if completely unscrollable (exclude HTML element because it's special)
if (!overflow.x && !overflow.y && !overflow.isRoot)
{
return false;
}
var size = {
height: {
scroll: element.scrollHeight,
client: element.clientHeight
},
width: {
scroll: element.scrollWidth,
client: element.clientWidth
},
// check overflow.x/y because iPad (and possibly other tablets) don't dislay scrollbars
scrollableX: function () {
return (overflow.x || overflow.isRoot) && this.width.scroll > this.width.client;
},
scrollableY: function () {
return (overflow.y || overflow.isRoot) && this.height.scroll > this.height.client;
}
};
return direction.y && size.scrollableY() || direction.x && size.scrollableX();
}
});
})(jQuery);
...@@ -56,6 +56,9 @@ ...@@ -56,6 +56,9 @@
"karma-sinon": "^1.0.4", "karma-sinon": "^1.0.4",
"mocha": "^1.20.1", "mocha": "^1.20.1",
"phantomjs": "^1.9.7", "phantomjs": "^1.9.7",
"proxyquire": "^1.6.0",
"proxyquire-universal": "^1.0.8",
"proxyquireify": "^3.0.0",
"sinon": "^1.15.4" "sinon": "^1.15.4"
}, },
"engines": { "engines": {
...@@ -75,7 +78,6 @@ ...@@ -75,7 +78,6 @@
"homepage": "https://github.com/hypothesis/h", "homepage": "https://github.com/hypothesis/h",
"browserify": { "browserify": {
"transform": [ "transform": [
"coffeeify",
"browserify-ngannotate", "browserify-ngannotate",
"browserify-shim" "browserify-shim"
] ]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment