mirror of https://github.com/zulip/zulip.git
list_render: Dedent 200+ lines of code.
This is all cosmetic. Instead of: const.widget = { foo: function () = { }, bar: function () { }, }; We have: const widget = {}; widget.foo = function () = { }; widget.bar = function () { };
This commit is contained in:
parent
29b22da1ff
commit
42c2e9c429
|
@ -81,238 +81,238 @@ exports.create = function ($container, list, opts) {
|
|||
}
|
||||
exports.validate_filter(opts);
|
||||
|
||||
const widget = {
|
||||
// Reads the provided list (in the scope directly above)
|
||||
// and renders the next block of messages automatically
|
||||
// into the specified container.
|
||||
render: function (load_count) {
|
||||
load_count = load_count || opts.load_count || DEFAULTS.LOAD_COUNT;
|
||||
const widget = {};
|
||||
|
||||
// Stop once the offset reaches the length of the original list.
|
||||
if (meta.offset >= meta.filtered_list.length) {
|
||||
return;
|
||||
// Reads the provided list (in the scope directly above)
|
||||
// and renders the next block of messages automatically
|
||||
// into the specified container.
|
||||
widget.render = function (load_count) {
|
||||
load_count = load_count || opts.load_count || DEFAULTS.LOAD_COUNT;
|
||||
|
||||
// Stop once the offset reaches the length of the original list.
|
||||
if (meta.offset >= meta.filtered_list.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const slice = meta.filtered_list.slice(meta.offset, meta.offset + load_count);
|
||||
|
||||
const finish = blueslip.start_timing('list_render ' + opts.name);
|
||||
let html = "";
|
||||
for (const item of slice) {
|
||||
let _item = opts.modifier(item);
|
||||
|
||||
// if valid jQuery selection, attempt to grab all elements within
|
||||
// and string them together into a giant outerHTML fragment.
|
||||
if (_item.constructor === jQuery) {
|
||||
_item = (function ($nodes) {
|
||||
let html = "";
|
||||
$nodes.each(function () {
|
||||
if (this.nodeType === 1) {
|
||||
html += this.outerHTML;
|
||||
}
|
||||
});
|
||||
|
||||
return html;
|
||||
}(_item));
|
||||
}
|
||||
|
||||
const slice = meta.filtered_list.slice(meta.offset, meta.offset + load_count);
|
||||
|
||||
const finish = blueslip.start_timing('list_render ' + opts.name);
|
||||
let html = "";
|
||||
for (const item of slice) {
|
||||
let _item = opts.modifier(item);
|
||||
|
||||
// if valid jQuery selection, attempt to grab all elements within
|
||||
// and string them together into a giant outerHTML fragment.
|
||||
if (_item.constructor === jQuery) {
|
||||
_item = (function ($nodes) {
|
||||
let html = "";
|
||||
$nodes.each(function () {
|
||||
if (this.nodeType === 1) {
|
||||
html += this.outerHTML;
|
||||
}
|
||||
});
|
||||
|
||||
return html;
|
||||
}(_item));
|
||||
}
|
||||
|
||||
// if is a valid element, get the outerHTML.
|
||||
if (_item instanceof Element) {
|
||||
_item = _item.outerHTML;
|
||||
}
|
||||
|
||||
// append the HTML or nothing if corrupt (null, undef, etc.).
|
||||
html += _item || "";
|
||||
// if is a valid element, get the outerHTML.
|
||||
if (_item instanceof Element) {
|
||||
_item = _item.outerHTML;
|
||||
}
|
||||
|
||||
finish();
|
||||
// append the HTML or nothing if corrupt (null, undef, etc.).
|
||||
html += _item || "";
|
||||
}
|
||||
|
||||
$container.append($(html));
|
||||
meta.offset += load_count;
|
||||
finish();
|
||||
|
||||
return this;
|
||||
},
|
||||
$container.append($(html));
|
||||
meta.offset += load_count;
|
||||
|
||||
// Fills the container with an initial batch of items.
|
||||
// Needs to be enough to exceed the max height, so that a
|
||||
// scrollable area is created.
|
||||
init: function () {
|
||||
this.clear();
|
||||
this.render(DEFAULTS.INITIAL_RENDER_COUNT);
|
||||
return this;
|
||||
},
|
||||
return this;
|
||||
};
|
||||
|
||||
filter: function (map_function) {
|
||||
meta.filtered_list = meta.list(map_function);
|
||||
},
|
||||
// Fills the container with an initial batch of items.
|
||||
// Needs to be enough to exceed the max height, so that a
|
||||
// scrollable area is created.
|
||||
widget.init = function () {
|
||||
this.clear();
|
||||
this.render(DEFAULTS.INITIAL_RENDER_COUNT);
|
||||
return this;
|
||||
};
|
||||
|
||||
// reset the data associated with a list. This is so that instead of
|
||||
// initializing a new progressive list render instance, you can just
|
||||
// update the data of an existing one.
|
||||
data: function (...args) {
|
||||
// if no args are provided then just return the existing data.
|
||||
// this interface is similar to how many jQuery functions operate,
|
||||
// where a call to the method without data returns the existing data.
|
||||
if (args.length === 0) {
|
||||
return meta.list;
|
||||
}
|
||||
const [data] = args;
|
||||
widget.filter = function (map_function) {
|
||||
meta.filtered_list = meta.list(map_function);
|
||||
};
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
meta.list = data;
|
||||
meta.filtered_list = data;
|
||||
// reset the data associated with a list. This is so that instead of
|
||||
// initializing a new progressive list render instance, you can just
|
||||
// update the data of an existing one.
|
||||
widget.data = function (...args) {
|
||||
// if no args are provided then just return the existing data.
|
||||
// this interface is similar to how many jQuery functions operate,
|
||||
// where a call to the method without data returns the existing data.
|
||||
if (args.length === 0) {
|
||||
return meta.list;
|
||||
}
|
||||
const [data] = args;
|
||||
|
||||
if (opts.filter && opts.filter.element) {
|
||||
const value = $(opts.filter.element).val().toLocaleLowerCase();
|
||||
filter_list(value);
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
meta.list = data;
|
||||
meta.filtered_list = data;
|
||||
|
||||
widget.clear();
|
||||
|
||||
return this;
|
||||
if (opts.filter && opts.filter.element) {
|
||||
const value = $(opts.filter.element).val().toLocaleLowerCase();
|
||||
filter_list(value);
|
||||
}
|
||||
|
||||
blueslip.warn("The data object provided to the progressive" +
|
||||
" list render is invalid");
|
||||
return this;
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
$container.html("");
|
||||
meta.offset = 0;
|
||||
return this;
|
||||
},
|
||||
|
||||
set_container: function ($new_container) {
|
||||
if ($new_container) {
|
||||
$container = $new_container;
|
||||
}
|
||||
widget.clear();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
set_opts: function (new_opts) {
|
||||
if (opts) {
|
||||
opts = new_opts;
|
||||
blueslip.warn("The data object provided to the progressive" +
|
||||
" list render is invalid");
|
||||
return this;
|
||||
};
|
||||
|
||||
widget.clear = function () {
|
||||
$container.html("");
|
||||
meta.offset = 0;
|
||||
return this;
|
||||
};
|
||||
|
||||
widget.set_container = function ($new_container) {
|
||||
if ($new_container) {
|
||||
$container = $new_container;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
widget.set_opts = function (new_opts) {
|
||||
if (opts) {
|
||||
opts = new_opts;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
widget.reverse = function () {
|
||||
meta.filtered_list.reverse();
|
||||
widget.init();
|
||||
return this;
|
||||
};
|
||||
|
||||
// the sorting function is either the function or string that calls the
|
||||
// function to sort the list by. The prop is used for generic functions
|
||||
// that can be called to sort with a particular prop.
|
||||
|
||||
// the `map` will normalize the values with a function you provide to make
|
||||
// it easier to sort with.
|
||||
|
||||
// `do_not_display` will signal to not update the DOM, likely because in
|
||||
// the next function it will be updated in the DOM.
|
||||
widget.sort = function (sorting_function, prop, do_not_display) {
|
||||
meta.prop = prop;
|
||||
|
||||
if (typeof sorting_function === "function") {
|
||||
meta.sorting_function = sorting_function;
|
||||
} else if (typeof sorting_function === "string") {
|
||||
if (typeof prop === "string") {
|
||||
/* eslint-disable max-len */
|
||||
meta.sorting_function = meta.generic_sorting_functions.get(sorting_function)(prop);
|
||||
} else {
|
||||
meta.sorting_function = meta.sorting_functions.get(sorting_function);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
// we do not want to sort if we are just looking to reverse
|
||||
// by calling with no sorting_function
|
||||
if (meta.sorting_function) {
|
||||
meta.filtered_list = meta.filtered_list.sort(meta.sorting_function);
|
||||
}
|
||||
|
||||
reverse: function () {
|
||||
meta.filtered_list.reverse();
|
||||
if (!do_not_display) {
|
||||
// clear and re-initialize the list with the newly filtered subset
|
||||
// of items.
|
||||
widget.init();
|
||||
return this;
|
||||
},
|
||||
|
||||
// the sorting function is either the function or string that calls the
|
||||
// function to sort the list by. The prop is used for generic functions
|
||||
// that can be called to sort with a particular prop.
|
||||
if (opts.filter && opts.filter.onupdate) {
|
||||
opts.filter.onupdate();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// the `map` will normalize the values with a function you provide to make
|
||||
// it easier to sort with.
|
||||
widget.add_sort_function = function (name, sorting_function) {
|
||||
meta.sorting_functions.set(name, sorting_function);
|
||||
};
|
||||
|
||||
// `do_not_display` will signal to not update the DOM, likely because in
|
||||
// the next function it will be updated in the DOM.
|
||||
sort: function (sorting_function, prop, do_not_display) {
|
||||
meta.prop = prop;
|
||||
// generic sorting functions are ones that will use a specified prop
|
||||
// and perform a sort on it with the given sorting function.
|
||||
widget.add_generic_sort_function = function (name, sorting_function) {
|
||||
meta.generic_sorting_functions.set(name, sorting_function);
|
||||
};
|
||||
|
||||
if (typeof sorting_function === "function") {
|
||||
meta.sorting_function = sorting_function;
|
||||
} else if (typeof sorting_function === "string") {
|
||||
if (typeof prop === "string") {
|
||||
/* eslint-disable max-len */
|
||||
meta.sorting_function = meta.generic_sorting_functions.get(sorting_function)(prop);
|
||||
} else {
|
||||
meta.sorting_function = meta.sorting_functions.get(sorting_function);
|
||||
}
|
||||
widget.remove_sort = function () {
|
||||
meta.sorting_function = false;
|
||||
};
|
||||
|
||||
// this sets the events given the particular arguments assigned in
|
||||
// the container and opts.
|
||||
widget.__set_events = function () {
|
||||
let $nearestScrollingContainer = $container;
|
||||
while ($nearestScrollingContainer.length) {
|
||||
if ($nearestScrollingContainer.is("body, html")) {
|
||||
blueslip.warn("Please wrap progressive scrolling lists in an element with 'max-height' attribute. Error found in:\n" + blueslip.preview_node($container));
|
||||
break;
|
||||
}
|
||||
|
||||
// we do not want to sort if we are just looking to reverse
|
||||
// by calling with no sorting_function
|
||||
if (meta.sorting_function) {
|
||||
meta.filtered_list = meta.filtered_list.sort(meta.sorting_function);
|
||||
if ($nearestScrollingContainer.css("max-height") !== "none") {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!do_not_display) {
|
||||
$nearestScrollingContainer = $nearestScrollingContainer.parent();
|
||||
}
|
||||
|
||||
// on scroll of the nearest scrolling container, if it hits the bottom
|
||||
// of the container then fetch a new block of items and render them.
|
||||
$nearestScrollingContainer.scroll(function () {
|
||||
if (this.scrollHeight - (this.scrollTop + this.clientHeight) < 10) {
|
||||
widget.render();
|
||||
}
|
||||
});
|
||||
|
||||
if (opts.parent_container) {
|
||||
opts.parent_container.on("click", "[data-sort]", exports.handle_sort);
|
||||
}
|
||||
|
||||
if (opts.filter && opts.filter.element) {
|
||||
opts.filter.element.on(opts.filter.event || "input", function () {
|
||||
const self = this;
|
||||
const value = self.value.toLocaleLowerCase();
|
||||
|
||||
// run the sort algorithm that was used last, which is done
|
||||
// by passing `undefined` -- which will make it use the params
|
||||
// from the last sort.
|
||||
// it will then also not run an update in the DOM (because we
|
||||
// pass `true`), because it will update regardless below at
|
||||
// `widget.init()`.
|
||||
widget.sort(undefined, meta.prop, true);
|
||||
filter_list(value);
|
||||
|
||||
// clear and re-initialize the list with the newly filtered subset
|
||||
// of items.
|
||||
widget.init();
|
||||
|
||||
if (opts.filter && opts.filter.onupdate) {
|
||||
if (opts.filter.onupdate) {
|
||||
opts.filter.onupdate();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
add_sort_function: function (name, sorting_function) {
|
||||
meta.sorting_functions.set(name, sorting_function);
|
||||
},
|
||||
|
||||
// generic sorting functions are ones that will use a specified prop
|
||||
// and perform a sort on it with the given sorting function.
|
||||
add_generic_sort_function: function (name, sorting_function) {
|
||||
meta.generic_sorting_functions.set(name, sorting_function);
|
||||
},
|
||||
|
||||
remove_sort: function () {
|
||||
meta.sorting_function = false;
|
||||
},
|
||||
|
||||
// this sets the events given the particular arguments assigned in
|
||||
// the container and opts.
|
||||
__set_events: function () {
|
||||
let $nearestScrollingContainer = $container;
|
||||
while ($nearestScrollingContainer.length) {
|
||||
if ($nearestScrollingContainer.is("body, html")) {
|
||||
blueslip.warn("Please wrap progressive scrolling lists in an element with 'max-height' attribute. Error found in:\n" + blueslip.preview_node($container));
|
||||
break;
|
||||
}
|
||||
|
||||
if ($nearestScrollingContainer.css("max-height") !== "none") {
|
||||
break;
|
||||
}
|
||||
|
||||
$nearestScrollingContainer = $nearestScrollingContainer.parent();
|
||||
}
|
||||
|
||||
// on scroll of the nearest scrolling container, if it hits the bottom
|
||||
// of the container then fetch a new block of items and render them.
|
||||
$nearestScrollingContainer.scroll(function () {
|
||||
if (this.scrollHeight - (this.scrollTop + this.clientHeight) < 10) {
|
||||
widget.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.parent_container) {
|
||||
opts.parent_container.on("click", "[data-sort]", exports.handle_sort);
|
||||
}
|
||||
|
||||
if (opts.filter && opts.filter.element) {
|
||||
opts.filter.element.on(opts.filter.event || "input", function () {
|
||||
const self = this;
|
||||
const value = self.value.toLocaleLowerCase();
|
||||
|
||||
// run the sort algorithm that was used last, which is done
|
||||
// by passing `undefined` -- which will make it use the params
|
||||
// from the last sort.
|
||||
// it will then also not run an update in the DOM (because we
|
||||
// pass `true`), because it will update regardless below at
|
||||
// `widget.init()`.
|
||||
widget.sort(undefined, meta.prop, true);
|
||||
filter_list(value);
|
||||
|
||||
// clear and re-initialize the list with the newly filtered subset
|
||||
// of items.
|
||||
widget.init();
|
||||
|
||||
if (opts.filter.onupdate) {
|
||||
opts.filter.onupdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
return this;
|
||||
};
|
||||
|
||||
widget.__set_events();
|
||||
|
|
Loading…
Reference in New Issue