mirror of
https://github.com/rstudio/shiny.git
synced 2026-02-08 21:55:02 -05:00
Simplify DnD for fileInputs, fix #2142 (Firefox 57+)
- Simplified dragHover "plugin" by counting children instead of storing them. Counting children fixes Firefox 57+ bug (to be found or filed) that causes text object of input element to produce drag events - Removed multimethod since it's no longer used anywhere - Firefox 57+ appears not to trigger a change event when the `files` field is modified, which prevented uploads from occuring. This commit triggers a change event manually and doesn't impact the functioning of other browsers.
This commit is contained in:
196
srcjs/utils.js
196
srcjs/utils.js
@@ -326,199 +326,3 @@ exports.compareVersion = function(a, op, b) {
|
||||
else if (op === "<") return (diff < 0);
|
||||
else throw `Unknown operator: ${op}`;
|
||||
};
|
||||
|
||||
|
||||
// multimethod: Creates functions — "multimethods" — that are polymorphic on one
|
||||
// or more of their arguments.
|
||||
//
|
||||
// Multimethods can take any number of arguments. Arguments are passed to an
|
||||
// applicable function or "method", returning its result. By default, if no
|
||||
// method was applicable, an exception is thrown.
|
||||
//
|
||||
// Methods are searched in the order that they were added, and the first
|
||||
// applicable method found is the one used.
|
||||
//
|
||||
// A method is applicable when the "dispatch value" associated with it
|
||||
// corresponds to the value returned by the dispatch function. The dispatch
|
||||
// function defaults to the value of the first argument passed to the
|
||||
// multimethod.
|
||||
//
|
||||
// The correspondence between the value returned by the dispatch function and
|
||||
// any method's dispatch value is determined by the test function, which is
|
||||
// user-definable and defaults to `equal` or deep equality.
|
||||
//
|
||||
// # Chainable Functions
|
||||
//
|
||||
// The function returned by `multimethod()` exposes functions as properties.
|
||||
// These functions generally return the multimethod, and so can be chained.
|
||||
//
|
||||
// - dispatch([function newDispatch]): Sets the dispatch function. The dispatch
|
||||
// function can take any number of arguments, but must return a dispatch
|
||||
// value. The default dispatch function returns the first argument passed to
|
||||
// the multimethod.
|
||||
//
|
||||
// - test([function newTest]): Sets the test function. The test function takes
|
||||
// two arguments: the dispatch value produced by the dispatch function, and
|
||||
// the dispatch value associated with some method. It must return a boolean
|
||||
// indicating whether or not to select the method. The default test function
|
||||
// is `equal`.
|
||||
//
|
||||
// - when(object dispatchVal, function method): Adds a new dispatch value/method
|
||||
// combination.
|
||||
//
|
||||
// - whenAny(array<object> dispatchVals, function method): Like `when`, but
|
||||
// associates the method with every dispatch value in the `dispatchVals`
|
||||
// array.
|
||||
//
|
||||
// - else(function newDefaultMethod): Sets the default function. This function
|
||||
// is invoked when no methods apply. If left unset, the multimethod will throw
|
||||
// an exception when no methods are applicable.
|
||||
//
|
||||
// - clone(): Returns a new, functionally-equivalent multimethod. This is a way
|
||||
// to extend an existing multimethod in a local context — such as inside a
|
||||
// function — without modifying the original. NOTE: The array of methods is
|
||||
// copied, but the dispatch values themselves are not.
|
||||
//
|
||||
// # Self-reference
|
||||
//
|
||||
// The multimethod function can be obtained inside its method bodies without
|
||||
// referring to it by name.
|
||||
//
|
||||
// This makes it possible for one method to call another, or to pass the
|
||||
// multimethod to other functions as a callback from within methods.
|
||||
//
|
||||
// The mechanism is: the multimethod itself is bound as `this` to methods when
|
||||
// they are called. Since arrow functions cannot be bound to objects, **self-reference
|
||||
// is only possible within methods created using the `function` keyword**.
|
||||
//
|
||||
// # Tail recursion
|
||||
//
|
||||
// A method can call itself in a way that will not overflow the stack by using
|
||||
// `this.recur`.
|
||||
//
|
||||
// `this.recur` is a function available in methods created using `function`.
|
||||
// When the return value of a call to `this.recur` is returned by a method, the
|
||||
// arguments that were supplied to `this.recur` are used to call the
|
||||
// multimethod.
|
||||
//
|
||||
// # Examples
|
||||
//
|
||||
// Handling events:
|
||||
//
|
||||
// var handle = multimethod()
|
||||
// .dispatch(e => [e.target.tagName.toLowerCase(), e.type])
|
||||
// .when(["h1", "click"], e => "you clicked on an h1")
|
||||
// .when(["p", "mouseover"], e => "you moused over a p"})
|
||||
// .else(e => {
|
||||
// let tag = e.target.tagName.toLowerCase();
|
||||
// return `you did ${e.type} to an ${tag}`;
|
||||
// });
|
||||
//
|
||||
// $(document).on("click mouseover mouseup mousedown", e => console.log(handle(e)))
|
||||
//
|
||||
// Self-calls:
|
||||
//
|
||||
// var demoSelfCall = multimethod()
|
||||
// .when(0, function(n) {
|
||||
// this(1);
|
||||
// })
|
||||
// .when(1, function(n) {
|
||||
// doSomething(this);
|
||||
// })
|
||||
// .when(2, _ => console.log("tada"));
|
||||
//
|
||||
// Using (abusing?) the test function:
|
||||
//
|
||||
// var fizzBuzz = multimethod()
|
||||
// .test((x, divs) => divs.map(d => x % d === 0).every(Boolean))
|
||||
// .when([3, 5], x => "FizzBuzz")
|
||||
// .when([3], x => "Fizz")
|
||||
// .when([5], x => "Buzz")
|
||||
// .else(x => x);
|
||||
//
|
||||
// for(let i = 0; i <= 100; i++) console.log(fizzBuzz(i));
|
||||
//
|
||||
// Getting carried away with tail recursion:
|
||||
//
|
||||
// var factorial = multimethod()
|
||||
// .when(0, () => 1)
|
||||
// .when(1, (_, prod = 1) => prod)
|
||||
// .else(function(n, prod = 1) {
|
||||
// return this.recur(n-1, n*prod);
|
||||
// });
|
||||
//
|
||||
// var fibonacci = multimethod()
|
||||
// .when(0, (_, a = 0) => a)
|
||||
// .else(function(n, a = 0, b = 1) {
|
||||
// return this.recur(n-1, b, a+b);
|
||||
// });
|
||||
function multimethod(dispatch = (firstArg) => firstArg,
|
||||
test = equal,
|
||||
defaultMethod = null,
|
||||
methods = []) {
|
||||
|
||||
var trampolining = false;
|
||||
|
||||
function Sentinel (args) { this.args = args; }
|
||||
|
||||
function trampoline(f) {
|
||||
return (...args) => {
|
||||
trampolining = true;
|
||||
var ret = f.apply(invoke, args);
|
||||
while (ret instanceof Sentinel)
|
||||
ret = f.apply(invoke, ret.args);
|
||||
trampolining = false;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
let invoke = trampoline((...args) => {
|
||||
var dispatchVal = dispatch.apply(null, args);
|
||||
for (let i = 0; i < methods.length; i++) {
|
||||
let [methodVal, methodFn] = methods[i];
|
||||
if (test(dispatchVal, methodVal)) {
|
||||
return methodFn.apply(invoke, args);
|
||||
}
|
||||
}
|
||||
if (defaultMethod) {
|
||||
return defaultMethod.apply(invoke, args);
|
||||
} else {
|
||||
throw new Error(`No method for dispatch value ${dispatchVal}`);
|
||||
}
|
||||
});
|
||||
|
||||
invoke.recur = (...args) => {
|
||||
if (!trampolining) throw new Error("recur can only be called inside a method");
|
||||
return new Sentinel(args);
|
||||
};
|
||||
|
||||
invoke.dispatch = (newDispatch) => {
|
||||
dispatch = newDispatch;
|
||||
return invoke;
|
||||
};
|
||||
|
||||
invoke.test = (newTest) => {
|
||||
test = newTest;
|
||||
return invoke;
|
||||
};
|
||||
|
||||
invoke.when = (dispatchVal, methodFn) => {
|
||||
methods = methods.concat([[dispatchVal, methodFn]]);
|
||||
return invoke;
|
||||
};
|
||||
|
||||
invoke.whenAny = (dispatchVals, methodFn) => {
|
||||
return dispatchVals.reduce((self, val) => invoke.when(val, methodFn), invoke);
|
||||
};
|
||||
|
||||
invoke.else = (newDefaultMethod = null) => {
|
||||
defaultMethod = newDefaultMethod;
|
||||
return invoke;
|
||||
};
|
||||
|
||||
invoke.clone = () => {
|
||||
return multimethod(dispatch, test, defaultMethod, methods.slice());
|
||||
};
|
||||
|
||||
return invoke;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user