mirror of https://github.com/zulip/zulip.git
zjquery: Split out zjquery_element.js.
The new file is an easier place to point developers for the most typical questions about zjquery's capabilities.
This commit is contained in:
parent
82394308fd
commit
103c4c3995
|
@ -2,19 +2,14 @@
|
|||
|
||||
const {strict: assert} = require("assert");
|
||||
|
||||
const noop = function () {};
|
||||
|
||||
class Event {
|
||||
constructor(type, props) {
|
||||
if (!(this instanceof Event)) {
|
||||
return new Event(type, props);
|
||||
}
|
||||
this.type = type;
|
||||
Object.assign(this, props);
|
||||
}
|
||||
preventDefault() {}
|
||||
stopPropagation() {}
|
||||
}
|
||||
/*
|
||||
When using zjquery, the first call to $("#foo")
|
||||
returns a new instance of the FakeElement pseudoclass,
|
||||
and then subsequent calls to $("#foo") get the
|
||||
same instance.
|
||||
*/
|
||||
const FakeElement = require("./zjquery_element");
|
||||
const FakeEvent = require("./zjquery_event");
|
||||
|
||||
function verify_selector_for_zulip(selector) {
|
||||
const is_valid =
|
||||
|
@ -40,405 +35,6 @@ function verify_selector_for_zulip(selector) {
|
|||
}
|
||||
}
|
||||
|
||||
function make_event_store(selector) {
|
||||
/*
|
||||
|
||||
This function returns an event_store object that
|
||||
simulates the behavior of .on and .off from jQuery.
|
||||
|
||||
It also has methods to retrieve handlers that have
|
||||
been set via .on (or similar methods), which can
|
||||
be useful for tests that want to test the actual
|
||||
handlers.
|
||||
|
||||
*/
|
||||
const on_functions = new Map();
|
||||
const child_on_functions = new Map();
|
||||
let focused = false;
|
||||
|
||||
const self = {
|
||||
get_on_handler(name, child_selector) {
|
||||
let handler;
|
||||
|
||||
if (child_selector === undefined) {
|
||||
handler = on_functions.get(name);
|
||||
if (!handler) {
|
||||
throw new Error("no " + name + " handler for " + selector);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
const child_on = child_on_functions.get(child_selector);
|
||||
if (child_on) {
|
||||
handler = child_on.get(name);
|
||||
}
|
||||
|
||||
if (!handler) {
|
||||
throw new Error("no " + name + " handler for " + selector + " " + child_selector);
|
||||
}
|
||||
|
||||
return handler;
|
||||
},
|
||||
|
||||
off(event_name, ...args) {
|
||||
if (args.length === 0) {
|
||||
on_functions.delete(event_name);
|
||||
return;
|
||||
}
|
||||
|
||||
// In the Zulip codebase we never use this form of
|
||||
// .off in code that we test: $(...).off('click', child_sel);
|
||||
//
|
||||
// So we don't support this for now.
|
||||
throw new Error("zjquery does not support this call sequence");
|
||||
},
|
||||
|
||||
on(event_name, ...args) {
|
||||
// parameters will either be
|
||||
// (event_name, handler) or
|
||||
// (event_name, sel, handler)
|
||||
if (args.length === 1) {
|
||||
const [handler] = args;
|
||||
if (on_functions.has(event_name)) {
|
||||
console.info("\nEither the app or the test can be at fault here..");
|
||||
console.info("(sometimes you just want to call $.clear_all_elements();)\n");
|
||||
throw new Error("dup " + event_name + " handler for " + selector);
|
||||
}
|
||||
|
||||
on_functions.set(event_name, handler);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length !== 2) {
|
||||
throw new Error("wrong number of arguments passed in");
|
||||
}
|
||||
|
||||
const [sel, handler] = args;
|
||||
assert.equal(typeof sel, "string", "String selectors expected here.");
|
||||
assert.equal(typeof handler, "function", "An handler function expected here.");
|
||||
|
||||
if (!child_on_functions.has(sel)) {
|
||||
child_on_functions.set(sel, new Map());
|
||||
}
|
||||
|
||||
const child_on = child_on_functions.get(sel);
|
||||
|
||||
if (child_on.has(event_name)) {
|
||||
throw new Error("dup " + event_name + " handler for " + selector + " " + sel);
|
||||
}
|
||||
|
||||
child_on.set(event_name, handler);
|
||||
},
|
||||
|
||||
one(event_name, handler) {
|
||||
self.on(event_name, function (ev) {
|
||||
self.off(event_name);
|
||||
return handler.call(this, ev);
|
||||
});
|
||||
},
|
||||
|
||||
trigger($element, ev, data) {
|
||||
if (typeof ev === "string") {
|
||||
ev = new Event(ev);
|
||||
}
|
||||
if (!ev.target) {
|
||||
ev.target = $element;
|
||||
}
|
||||
const func = on_functions.get(ev.type);
|
||||
|
||||
if (func) {
|
||||
// It's possible that test code will trigger events
|
||||
// that haven't been set up yet, but we are trying to
|
||||
// eventually deprecate trigger in our codebase, so for
|
||||
// now we just let calls to trigger silently do nothing.
|
||||
// (And I think actual jQuery would do the same thing.)
|
||||
func.call($element, ev, data);
|
||||
}
|
||||
|
||||
if (ev.type === "focus" || ev.type === "focusin") {
|
||||
focused = true;
|
||||
} else if (ev.type === "blur" || ev.type === "focusout") {
|
||||
focused = false;
|
||||
}
|
||||
},
|
||||
|
||||
is_focused() {
|
||||
return focused;
|
||||
},
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
function make_new_elem(selector, opts) {
|
||||
let html = "never-been-set";
|
||||
let text = "never-been-set";
|
||||
let value;
|
||||
let shown = false;
|
||||
let height;
|
||||
|
||||
const find_results = new Map();
|
||||
let my_parent;
|
||||
const parents_result = new Map();
|
||||
const properties = new Map();
|
||||
const attrs = new Map();
|
||||
const classes = new Map();
|
||||
const event_store = make_event_store(selector);
|
||||
|
||||
const self = {
|
||||
addClass(class_name) {
|
||||
classes.set(class_name, true);
|
||||
return self;
|
||||
},
|
||||
append(arg) {
|
||||
html = html + arg;
|
||||
return self;
|
||||
},
|
||||
attr(name, val) {
|
||||
if (val === undefined) {
|
||||
return attrs.get(name);
|
||||
}
|
||||
attrs.set(name, val);
|
||||
return self;
|
||||
},
|
||||
data(name, val) {
|
||||
if (val === undefined) {
|
||||
return attrs.get("data-" + name);
|
||||
}
|
||||
attrs.set("data-" + name, val);
|
||||
return self;
|
||||
},
|
||||
delay() {
|
||||
return self;
|
||||
},
|
||||
debug() {
|
||||
return {
|
||||
value,
|
||||
shown,
|
||||
selector,
|
||||
};
|
||||
},
|
||||
empty(arg) {
|
||||
if (arg === undefined) {
|
||||
find_results.clear();
|
||||
}
|
||||
return self;
|
||||
},
|
||||
eq() {
|
||||
return self;
|
||||
},
|
||||
expectOne() {
|
||||
// silently do nothing
|
||||
return self;
|
||||
},
|
||||
fadeTo: noop,
|
||||
find(child_selector) {
|
||||
const child = find_results.get(child_selector);
|
||||
if (child) {
|
||||
return child;
|
||||
}
|
||||
if (child === false) {
|
||||
// This is deliberately set to simulate missing find results.
|
||||
// Return an empty array, the most common check is
|
||||
// if ($.find().length) { //success }
|
||||
return [];
|
||||
}
|
||||
throw new Error("Cannot find " + child_selector + " in " + selector);
|
||||
},
|
||||
get_on_handler(name, child_selector) {
|
||||
return event_store.get_on_handler(name, child_selector);
|
||||
},
|
||||
hasClass(class_name) {
|
||||
return classes.has(class_name);
|
||||
},
|
||||
height() {
|
||||
if (height === undefined) {
|
||||
throw new Error(`Please call $("${selector}").set_height`);
|
||||
}
|
||||
return height;
|
||||
},
|
||||
hide() {
|
||||
shown = false;
|
||||
return self;
|
||||
},
|
||||
html(arg) {
|
||||
if (arg !== undefined) {
|
||||
html = arg;
|
||||
return self;
|
||||
}
|
||||
return html;
|
||||
},
|
||||
is(arg) {
|
||||
if (arg === ":visible") {
|
||||
return shown;
|
||||
}
|
||||
if (arg === ":focus") {
|
||||
return self.is_focused();
|
||||
}
|
||||
return self;
|
||||
},
|
||||
is_focused() {
|
||||
// is_focused is not a jQuery thing; this is
|
||||
// for our testing
|
||||
return event_store.is_focused();
|
||||
},
|
||||
off(...args) {
|
||||
event_store.off(...args);
|
||||
return self;
|
||||
},
|
||||
offset() {
|
||||
return {
|
||||
top: 0,
|
||||
left: 0,
|
||||
};
|
||||
},
|
||||
on(...args) {
|
||||
event_store.on(...args);
|
||||
return self;
|
||||
},
|
||||
one(...args) {
|
||||
event_store.one(...args);
|
||||
return self;
|
||||
},
|
||||
parent() {
|
||||
return my_parent;
|
||||
},
|
||||
parents(parents_selector) {
|
||||
const result = parents_result.get(parents_selector);
|
||||
assert.ok(
|
||||
result,
|
||||
"You need to call set_parents_result for " + parents_selector + " in " + selector,
|
||||
);
|
||||
return result;
|
||||
},
|
||||
prepend(arg) {
|
||||
html = arg + html;
|
||||
return self;
|
||||
},
|
||||
prop(name, val) {
|
||||
if (val === undefined) {
|
||||
return properties.get(name);
|
||||
}
|
||||
properties.set(name, val);
|
||||
return self;
|
||||
},
|
||||
removeAttr(name) {
|
||||
attrs.delete(name);
|
||||
return self;
|
||||
},
|
||||
removeClass(class_names) {
|
||||
class_names = class_names.split(" ");
|
||||
for (const class_name of class_names) {
|
||||
classes.delete(class_name);
|
||||
}
|
||||
return self;
|
||||
},
|
||||
remove() {
|
||||
throw new Error(`
|
||||
We don't support remove in zjuery.
|
||||
|
||||
You can do $(...).remove = ... if necessary.
|
||||
|
||||
But you are probably writing too deep a test
|
||||
for node testing.
|
||||
`);
|
||||
},
|
||||
removeData: noop,
|
||||
replaceWith() {
|
||||
return self;
|
||||
},
|
||||
scrollTop() {
|
||||
return self;
|
||||
},
|
||||
serializeArray() {
|
||||
return self;
|
||||
},
|
||||
set_find_results(find_selector, jquery_object) {
|
||||
find_results.set(find_selector, jquery_object);
|
||||
},
|
||||
set_height(fake_height) {
|
||||
height = fake_height;
|
||||
},
|
||||
set_parent(parent_elem) {
|
||||
my_parent = parent_elem;
|
||||
},
|
||||
set_parents_result(selector, result) {
|
||||
parents_result.set(selector, result);
|
||||
},
|
||||
show() {
|
||||
shown = true;
|
||||
return self;
|
||||
},
|
||||
slice() {
|
||||
return self;
|
||||
},
|
||||
stop() {
|
||||
return self;
|
||||
},
|
||||
text(...args) {
|
||||
if (args.length !== 0) {
|
||||
if (args[0] !== undefined) {
|
||||
text = args[0].toString();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
return text;
|
||||
},
|
||||
toggle(show) {
|
||||
assert.ok([true, false].includes(show));
|
||||
shown = show;
|
||||
return self;
|
||||
},
|
||||
tooltip() {
|
||||
return self;
|
||||
},
|
||||
trigger(ev) {
|
||||
event_store.trigger(self, ev);
|
||||
return self;
|
||||
},
|
||||
val(...args) {
|
||||
if (args.length === 0) {
|
||||
return value || "";
|
||||
}
|
||||
[value] = args;
|
||||
return self;
|
||||
},
|
||||
visible() {
|
||||
return shown;
|
||||
},
|
||||
};
|
||||
|
||||
if (opts.children) {
|
||||
self.map = (f) => opts.children.map((i, elem) => f(elem, i));
|
||||
self.each = (f) => {
|
||||
for (const child of opts.children) {
|
||||
f.call(child);
|
||||
}
|
||||
};
|
||||
self[Symbol.iterator] = function* () {
|
||||
for (const child of opts.children) {
|
||||
yield child;
|
||||
}
|
||||
};
|
||||
|
||||
for (const [i, child] of opts.children.entries()) {
|
||||
self[i] = child;
|
||||
}
|
||||
|
||||
self.length = opts.children.length;
|
||||
}
|
||||
|
||||
if (selector[0] === "<") {
|
||||
self.html(selector);
|
||||
}
|
||||
|
||||
self.selector = selector;
|
||||
|
||||
self.__zjquery = true;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
function make_zjquery() {
|
||||
const elems = new Map();
|
||||
|
||||
|
@ -447,7 +43,7 @@ function make_zjquery() {
|
|||
const fn = {};
|
||||
|
||||
function new_elem(selector, create_opts) {
|
||||
const elem = make_new_elem(selector, {...create_opts});
|
||||
const elem = FakeElement(selector, {...create_opts});
|
||||
Object.assign(elem, fn);
|
||||
|
||||
// Create a proxy handler to detect missing stubs.
|
||||
|
@ -571,7 +167,7 @@ function make_zjquery() {
|
|||
return res;
|
||||
};
|
||||
|
||||
zjquery.Event = Event;
|
||||
zjquery.Event = FakeEvent;
|
||||
|
||||
fn.popover = () => {
|
||||
throw new Error(`
|
||||
|
|
|
@ -0,0 +1,409 @@
|
|||
"use strict";
|
||||
|
||||
const {strict: assert} = require("assert");
|
||||
|
||||
const FakeEvent = require("./zjquery_event");
|
||||
|
||||
const noop = function () {};
|
||||
|
||||
// TODO: convert this to a true class
|
||||
function FakeElement(selector, opts) {
|
||||
let html = "never-been-set";
|
||||
let text = "never-been-set";
|
||||
let value;
|
||||
let shown = false;
|
||||
let height;
|
||||
|
||||
const find_results = new Map();
|
||||
let my_parent;
|
||||
const parents_result = new Map();
|
||||
const properties = new Map();
|
||||
const attrs = new Map();
|
||||
const classes = new Map();
|
||||
const event_store = make_event_store(selector);
|
||||
|
||||
const self = {
|
||||
addClass(class_name) {
|
||||
classes.set(class_name, true);
|
||||
return self;
|
||||
},
|
||||
append(arg) {
|
||||
html = html + arg;
|
||||
return self;
|
||||
},
|
||||
attr(name, val) {
|
||||
if (val === undefined) {
|
||||
return attrs.get(name);
|
||||
}
|
||||
attrs.set(name, val);
|
||||
return self;
|
||||
},
|
||||
data(name, val) {
|
||||
if (val === undefined) {
|
||||
return attrs.get("data-" + name);
|
||||
}
|
||||
attrs.set("data-" + name, val);
|
||||
return self;
|
||||
},
|
||||
delay() {
|
||||
return self;
|
||||
},
|
||||
debug() {
|
||||
return {
|
||||
value,
|
||||
shown,
|
||||
selector,
|
||||
};
|
||||
},
|
||||
empty(arg) {
|
||||
if (arg === undefined) {
|
||||
find_results.clear();
|
||||
}
|
||||
return self;
|
||||
},
|
||||
eq() {
|
||||
return self;
|
||||
},
|
||||
expectOne() {
|
||||
// silently do nothing
|
||||
return self;
|
||||
},
|
||||
fadeTo: noop,
|
||||
find(child_selector) {
|
||||
const child = find_results.get(child_selector);
|
||||
if (child) {
|
||||
return child;
|
||||
}
|
||||
if (child === false) {
|
||||
// This is deliberately set to simulate missing find results.
|
||||
// Return an empty array, the most common check is
|
||||
// if ($.find().length) { //success }
|
||||
return [];
|
||||
}
|
||||
throw new Error("Cannot find " + child_selector + " in " + selector);
|
||||
},
|
||||
get_on_handler(name, child_selector) {
|
||||
return event_store.get_on_handler(name, child_selector);
|
||||
},
|
||||
hasClass(class_name) {
|
||||
return classes.has(class_name);
|
||||
},
|
||||
height() {
|
||||
if (height === undefined) {
|
||||
throw new Error(`Please call $("${selector}").set_height`);
|
||||
}
|
||||
return height;
|
||||
},
|
||||
hide() {
|
||||
shown = false;
|
||||
return self;
|
||||
},
|
||||
html(arg) {
|
||||
if (arg !== undefined) {
|
||||
html = arg;
|
||||
return self;
|
||||
}
|
||||
return html;
|
||||
},
|
||||
is(arg) {
|
||||
if (arg === ":visible") {
|
||||
return shown;
|
||||
}
|
||||
if (arg === ":focus") {
|
||||
return self.is_focused();
|
||||
}
|
||||
return self;
|
||||
},
|
||||
is_focused() {
|
||||
// is_focused is not a jQuery thing; this is
|
||||
// for our testing
|
||||
return event_store.is_focused();
|
||||
},
|
||||
off(...args) {
|
||||
event_store.off(...args);
|
||||
return self;
|
||||
},
|
||||
offset() {
|
||||
return {
|
||||
top: 0,
|
||||
left: 0,
|
||||
};
|
||||
},
|
||||
on(...args) {
|
||||
event_store.on(...args);
|
||||
return self;
|
||||
},
|
||||
one(...args) {
|
||||
event_store.one(...args);
|
||||
return self;
|
||||
},
|
||||
parent() {
|
||||
return my_parent;
|
||||
},
|
||||
parents(parents_selector) {
|
||||
const result = parents_result.get(parents_selector);
|
||||
assert.ok(
|
||||
result,
|
||||
"You need to call set_parents_result for " + parents_selector + " in " + selector,
|
||||
);
|
||||
return result;
|
||||
},
|
||||
prepend(arg) {
|
||||
html = arg + html;
|
||||
return self;
|
||||
},
|
||||
prop(name, val) {
|
||||
if (val === undefined) {
|
||||
return properties.get(name);
|
||||
}
|
||||
properties.set(name, val);
|
||||
return self;
|
||||
},
|
||||
removeAttr(name) {
|
||||
attrs.delete(name);
|
||||
return self;
|
||||
},
|
||||
removeClass(class_names) {
|
||||
class_names = class_names.split(" ");
|
||||
for (const class_name of class_names) {
|
||||
classes.delete(class_name);
|
||||
}
|
||||
return self;
|
||||
},
|
||||
remove() {
|
||||
throw new Error(`
|
||||
We don't support remove in zjuery.
|
||||
|
||||
You can do $(...).remove = ... if necessary.
|
||||
|
||||
But you are probably writing too deep a test
|
||||
for node testing.
|
||||
`);
|
||||
},
|
||||
removeData: noop,
|
||||
replaceWith() {
|
||||
return self;
|
||||
},
|
||||
scrollTop() {
|
||||
return self;
|
||||
},
|
||||
serializeArray() {
|
||||
return self;
|
||||
},
|
||||
set_find_results(find_selector, jquery_object) {
|
||||
find_results.set(find_selector, jquery_object);
|
||||
},
|
||||
set_height(fake_height) {
|
||||
height = fake_height;
|
||||
},
|
||||
set_parent(parent_elem) {
|
||||
my_parent = parent_elem;
|
||||
},
|
||||
set_parents_result(selector, result) {
|
||||
parents_result.set(selector, result);
|
||||
},
|
||||
show() {
|
||||
shown = true;
|
||||
return self;
|
||||
},
|
||||
slice() {
|
||||
return self;
|
||||
},
|
||||
stop() {
|
||||
return self;
|
||||
},
|
||||
text(...args) {
|
||||
if (args.length !== 0) {
|
||||
if (args[0] !== undefined) {
|
||||
text = args[0].toString();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
return text;
|
||||
},
|
||||
toggle(show) {
|
||||
assert.ok([true, false].includes(show));
|
||||
shown = show;
|
||||
return self;
|
||||
},
|
||||
tooltip() {
|
||||
return self;
|
||||
},
|
||||
trigger(ev) {
|
||||
event_store.trigger(self, ev);
|
||||
return self;
|
||||
},
|
||||
val(...args) {
|
||||
if (args.length === 0) {
|
||||
return value || "";
|
||||
}
|
||||
[value] = args;
|
||||
return self;
|
||||
},
|
||||
visible() {
|
||||
return shown;
|
||||
},
|
||||
};
|
||||
|
||||
if (opts.children) {
|
||||
self.map = (f) => opts.children.map((i, elem) => f(elem, i));
|
||||
self.each = (f) => {
|
||||
for (const child of opts.children) {
|
||||
f.call(child);
|
||||
}
|
||||
};
|
||||
self[Symbol.iterator] = function* () {
|
||||
for (const child of opts.children) {
|
||||
yield child;
|
||||
}
|
||||
};
|
||||
|
||||
for (const [i, child] of opts.children.entries()) {
|
||||
self[i] = child;
|
||||
}
|
||||
|
||||
self.length = opts.children.length;
|
||||
}
|
||||
|
||||
if (selector[0] === "<") {
|
||||
self.html(selector);
|
||||
}
|
||||
|
||||
self.selector = selector;
|
||||
|
||||
self.__zjquery = true;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
function make_event_store(selector) {
|
||||
/*
|
||||
|
||||
This function returns an event_store object that
|
||||
simulates the behavior of .on and .off from jQuery.
|
||||
|
||||
It also has methods to retrieve handlers that have
|
||||
been set via .on (or similar methods), which can
|
||||
be useful for tests that want to test the actual
|
||||
handlers.
|
||||
|
||||
*/
|
||||
const on_functions = new Map();
|
||||
const child_on_functions = new Map();
|
||||
let focused = false;
|
||||
|
||||
const self = {
|
||||
get_on_handler(name, child_selector) {
|
||||
let handler;
|
||||
|
||||
if (child_selector === undefined) {
|
||||
handler = on_functions.get(name);
|
||||
if (!handler) {
|
||||
throw new Error("no " + name + " handler for " + selector);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
const child_on = child_on_functions.get(child_selector);
|
||||
if (child_on) {
|
||||
handler = child_on.get(name);
|
||||
}
|
||||
|
||||
if (!handler) {
|
||||
throw new Error("no " + name + " handler for " + selector + " " + child_selector);
|
||||
}
|
||||
|
||||
return handler;
|
||||
},
|
||||
|
||||
off(event_name, ...args) {
|
||||
if (args.length === 0) {
|
||||
on_functions.delete(event_name);
|
||||
return;
|
||||
}
|
||||
|
||||
// In the Zulip codebase we never use this form of
|
||||
// .off in code that we test: $(...).off('click', child_sel);
|
||||
//
|
||||
// So we don't support this for now.
|
||||
throw new Error("zjquery does not support this call sequence");
|
||||
},
|
||||
|
||||
on(event_name, ...args) {
|
||||
// parameters will either be
|
||||
// (event_name, handler) or
|
||||
// (event_name, sel, handler)
|
||||
if (args.length === 1) {
|
||||
const [handler] = args;
|
||||
if (on_functions.has(event_name)) {
|
||||
console.info("\nEither the app or the test can be at fault here..");
|
||||
console.info("(sometimes you just want to call $.clear_all_elements();)\n");
|
||||
throw new Error("dup " + event_name + " handler for " + selector);
|
||||
}
|
||||
|
||||
on_functions.set(event_name, handler);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length !== 2) {
|
||||
throw new Error("wrong number of arguments passed in");
|
||||
}
|
||||
|
||||
const [sel, handler] = args;
|
||||
assert.equal(typeof sel, "string", "String selectors expected here.");
|
||||
assert.equal(typeof handler, "function", "An handler function expected here.");
|
||||
|
||||
if (!child_on_functions.has(sel)) {
|
||||
child_on_functions.set(sel, new Map());
|
||||
}
|
||||
|
||||
const child_on = child_on_functions.get(sel);
|
||||
|
||||
if (child_on.has(event_name)) {
|
||||
throw new Error("dup " + event_name + " handler for " + selector + " " + sel);
|
||||
}
|
||||
|
||||
child_on.set(event_name, handler);
|
||||
},
|
||||
|
||||
one(event_name, handler) {
|
||||
self.on(event_name, function (ev) {
|
||||
self.off(event_name);
|
||||
return handler.call(this, ev);
|
||||
});
|
||||
},
|
||||
|
||||
trigger($element, ev, data) {
|
||||
if (typeof ev === "string") {
|
||||
ev = new FakeEvent(ev);
|
||||
}
|
||||
if (!ev.target) {
|
||||
ev.target = $element;
|
||||
}
|
||||
const func = on_functions.get(ev.type);
|
||||
|
||||
if (func) {
|
||||
// It's possible that test code will trigger events
|
||||
// that haven't been set up yet, but we are trying to
|
||||
// eventually deprecate trigger in our codebase, so for
|
||||
// now we just let calls to trigger silently do nothing.
|
||||
// (And I think actual jQuery would do the same thing.)
|
||||
func.call($element, ev, data);
|
||||
}
|
||||
|
||||
if (ev.type === "focus" || ev.type === "focusin") {
|
||||
focused = true;
|
||||
} else if (ev.type === "blur" || ev.type === "focusout") {
|
||||
focused = false;
|
||||
}
|
||||
},
|
||||
|
||||
is_focused() {
|
||||
return focused;
|
||||
},
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
module.exports = FakeElement;
|
|
@ -0,0 +1,15 @@
|
|||
"use strict";
|
||||
|
||||
class FakeEvent {
|
||||
constructor(type, props) {
|
||||
if (!(this instanceof FakeEvent)) {
|
||||
return new FakeEvent(type, props);
|
||||
}
|
||||
this.type = type;
|
||||
Object.assign(this, props);
|
||||
}
|
||||
preventDefault() {}
|
||||
stopPropagation() {}
|
||||
}
|
||||
|
||||
module.exports = FakeEvent;
|
Loading…
Reference in New Issue