js: Remove /* eslint indent: "off" */ comments.

The time has come to dedent these files.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
This commit is contained in:
Anders Kaseorg 2019-10-25 00:15:16 -07:00 committed by Tim Abbott
parent 8c065d1fcd
commit 5f590d3500
7 changed files with 1051 additions and 1061 deletions

View File

@ -1,98 +1,96 @@
/* eslint indent: "off" */
var bot_data = (function () {
var exports = {};
var exports = {};
var bots = {};
var bot_fields = ['api_key', 'avatar_url', 'default_all_public_streams',
'default_events_register_stream', 'default_sending_stream',
'email', 'full_name', 'is_active', 'owner', 'bot_type', 'user_id'];
var services = {};
var services_fields = ['base_url', 'interface',
'config_data', 'service_name', 'token'];
var bots = {};
var bot_fields = ['api_key', 'avatar_url', 'default_all_public_streams',
'default_events_register_stream', 'default_sending_stream',
'email', 'full_name', 'is_active', 'owner', 'bot_type', 'user_id'];
var services = {};
var services_fields = ['base_url', 'interface',
'config_data', 'service_name', 'token'];
var send_change_event = _.debounce(function () {
settings_bots.render_bots();
}, 50);
var send_change_event = _.debounce(function () {
settings_bots.render_bots();
}, 50);
var set_can_admin = function bot_data__set_can_admin(bot) {
if (page_params.is_admin) {
bot.can_admin = true;
} else if (bot.owner !== undefined && people.is_current_user(bot.owner)) {
bot.can_admin = true;
} else {
bot.can_admin = false;
}
};
var set_can_admin = function bot_data__set_can_admin(bot) {
if (page_params.is_admin) {
bot.can_admin = true;
} else if (bot.owner !== undefined && people.is_current_user(bot.owner)) {
bot.can_admin = true;
} else {
bot.can_admin = false;
}
};
exports.add = function bot_data__add(bot) {
var clean_bot = _.pick(bot, bot_fields);
bots[bot.user_id] = clean_bot;
set_can_admin(clean_bot);
var clean_services = _.map(bot.services, function (service) {
return _.pick(service, services_fields);
});
services[bot.user_id] = clean_services;
exports.add = function bot_data__add(bot) {
var clean_bot = _.pick(bot, bot_fields);
bots[bot.user_id] = clean_bot;
set_can_admin(clean_bot);
var clean_services = _.map(bot.services, function (service) {
return _.pick(service, services_fields);
});
services[bot.user_id] = clean_services;
send_change_event();
};
send_change_event();
};
exports.deactivate = function bot_data__deactivate(bot_id) {
bots[bot_id].is_active = false;
send_change_event();
};
exports.deactivate = function bot_data__deactivate(bot_id) {
bots[bot_id].is_active = false;
send_change_event();
};
exports.del = function bot_data__del(bot_id) {
delete bots[bot_id];
delete services[bot_id];
send_change_event();
};
exports.del = function bot_data__del(bot_id) {
delete bots[bot_id];
delete services[bot_id];
send_change_event();
};
exports.update = function bot_data__update(bot_id, bot_update) {
var bot = bots[bot_id];
_.extend(bot, _.pick(bot_update, bot_fields));
set_can_admin(bot);
exports.update = function bot_data__update(bot_id, bot_update) {
var bot = bots[bot_id];
_.extend(bot, _.pick(bot_update, bot_fields));
set_can_admin(bot);
// We currently only support one service per bot.
var service = services[bot_id][0];
if (typeof bot_update.services !== 'undefined' && bot_update.services.length > 0) {
_.extend(service, _.pick(bot_update.services[0], services_fields));
}
send_change_event();
};
// We currently only support one service per bot.
var service = services[bot_id][0];
if (typeof bot_update.services !== 'undefined' && bot_update.services.length > 0) {
_.extend(service, _.pick(bot_update.services[0], services_fields));
}
send_change_event();
};
exports.get_all_bots_for_current_user = function bots_data__get_editable() {
return _.filter(bots, function (bot) {
return people.is_current_user(bot.owner);
});
};
exports.get_all_bots_for_current_user = function bots_data__get_editable() {
return _.filter(bots, function (bot) {
return people.is_current_user(bot.owner);
});
};
exports.get_editable = function bots_data__get_editable() {
return _.filter(bots, function (bot) {
return bot.is_active && people.is_current_user(bot.owner);
});
};
exports.get_editable = function bots_data__get_editable() {
return _.filter(bots, function (bot) {
return bot.is_active && people.is_current_user(bot.owner);
});
};
exports.get = function bot_data__get(bot_id) {
return bots[bot_id];
};
exports.get = function bot_data__get(bot_id) {
return bots[bot_id];
};
exports.get_bot_owner_email = function (bot_id) {
return bots[bot_id].owner;
};
exports.get_bot_owner_email = function (bot_id) {
return bots[bot_id].owner;
};
exports.get_services = function bot_data__get_services(bot_id) {
return services[bot_id];
};
exports.get_services = function bot_data__get_services(bot_id) {
return services[bot_id];
};
exports.initialize = function () {
_.each(page_params.realm_bots, function (bot) {
exports.add(bot);
});
delete page_params.realm_bots;
};
exports.initialize = function () {
_.each(page_params.realm_bots, function (bot) {
exports.add(bot);
});
delete page_params.realm_bots;
};
return exports;
return exports;
}());
if (typeof module !== 'undefined') {
module.exports = bot_data;

View File

@ -1,335 +1,333 @@
/* eslint indent: "off" */
var LightboxCanvas = (function () {
var events = {
documentMouseup: [],
windowResize: [],
};
window.onload = function () {
document.body.addEventListener("mouseup", function (e) {
events.documentMouseup = events.documentMouseup.filter(function (event) {
// go through automatic cleanup when running events.
if (!document.body.contains(event.canvas)) {
return false;
}
event.callback.call(this, e);
return true;
});
});
window.addEventListener("resize", function (e) {
events.windowResize = events.windowResize.filter(function (event) {
if (!document.body.contains(event.canvas)) {
return false;
}
event.callback.call(this, e);
return true;
}.bind(this));
});
};
var funcs = {
setZoom: function (meta, zoom) {
// condition to handle zooming event by zoom hotkeys
if (zoom === '+') {
zoom = meta.zoom * 1.2;
} else if (zoom === '-') {
zoom = meta.zoom / 1.2;
}
// make sure the zoom is above 1 and below the maxZoom.
meta.zoom = Math.min(Math.max(zoom, 1), meta.maxZoom);
},
// this is a function given a canvas that attaches all of the events
// required to pan and zoom.
attachEvents: function (canvas, context, meta) {
var mousedown = false;
// wheelEvent.deltaMode is a value that describes what the unit is
// for the `deltaX`, `deltaY`, and `deltaZ` properties.
var DELTA_MODE = {
PIXEL: 0,
LINE: 1,
PAGE: 2,
};
// give object structure in `mousedown`, because its props are only
// ever set once `mousedown` + `mousemove` is triggered.
var lastPosition = {};
// in browsers such as Safari, the `e.movementX` and `e.movementY`
// props don't exist, so we need to create them as a difference of
// where the last `layerX` and `layerY` movements since the last
// `mousemove` event in this `mousedown` event were registered.
var polyfillMouseMovement = function (e) {
e.movementX = e.layerX - lastPosition.x || 0;
e.movementY = e.layerY - lastPosition.y || 0;
lastPosition = {
x: e.layerX,
y: e.layerY,
};
};
// use the wheel event rather than scroll because this isn't
// actually an element that can scroll. The wheel event will
// detect the *gesture* of scrolling over an element, without actually
// worrying about scrollable content.
canvas.addEventListener("wheel", function (e) {
e.preventDefault();
// this is to reverese scrolling directions for the image.
var delta = meta.direction * e.deltaY;
if (e.deltaMode === DELTA_MODE.LINE) {
// the vertical height in pixels of an approximate line.
delta *= 15;
}
if (e.deltaMode === DELTA_MODE.PAGE) {
// the vertical height in pixels of an approximate page.
delta *= 300;
}
// this is calculated as the user defined speed times the normalizer
// (which just is what it takes to take the raw delta and transform
// it to a normal speed), multiply it against the current zoom.
// Example:
// delta = 8
// normalizedDelta = delta * (1 / 20) * 1 = 0.4
// zoom = zoom * (0.4 / 100) + 1
var zoom = meta.zoom * (
meta.speed * meta.internalSpeedMultiplier * delta / 100 + 1
);
funcs.setZoom(meta, zoom);
funcs.displayImage(canvas, context, meta);
var events = {
documentMouseup: [],
windowResize: [],
};
window.onload = function () {
document.body.addEventListener("mouseup", function (e) {
events.documentMouseup = events.documentMouseup.filter(function (event) {
// go through automatic cleanup when running events.
if (!document.body.contains(event.canvas)) {
return false;
});
// the only valid mousedown events should originate inside of the
// canvas.
canvas.addEventListener("mousedown", function () {
mousedown = true;
});
// on mousemove, actually run the pan events.
canvas.addEventListener("mousemove", function (e) {
// to pan, there must be mousedown and mousemove, check if valid.
if (mousedown === true) {
polyfillMouseMovement(e);
// find the percent of movement relative to the canvas width
// since e.movementX, e.movementY are in px.
var percentMovement = {
x: e.movementX / canvas.width,
y: e.movementY / canvas.height,
};
// add the percentMovement to the meta coordinates but divide
// out by the zoom ratio because when zoomed in 10x for example
// moving the photo by 1% will appear like 10% on the <canvas>.
meta.coords.x += percentMovement.x * 2 / meta.zoom;
meta.coords.y += percentMovement.y * 2 / meta.zoom;
// redraw the image.
funcs.displayImage(canvas, context, meta);
}
});
// event listener to handle zoom in and out from using keyboard keys z/Z and +/-
// in the canvas
// these hotkeys are not implemented in static/js/hotkey.js as the code in
// static/js/lightbox_canvas.js and static/js/lightbox.js isn't written a way
// that the LightboxCanvas instance created in lightbox.js can be
// accessed from hotkey.js. Major code refactoring is required in lightbox.js
// to implement these keyboard shortcuts in hotkey.js
document.addEventListener('keydown', function (e) {
if (!overlays.lightbox_open()) {
return;
}
if (e.key === "Z" || e.key === '+') {
funcs.setZoom(meta, '+');
funcs.displayImage(canvas, context, meta);
} else if (e.key === "z" || e.key === '-') {
funcs.setZoom(meta, '-');
funcs.displayImage(canvas, context, meta);
} else if (e.key === 'v') {
overlays.close_overlay('lightbox');
}
e.preventDefault();
e.stopPropagation();
});
// make sure that when the mousedown is lifted on <canvas>to prevent
// panning events.
canvas.addEventListener("mouseup", function () {
mousedown = false;
// reset this to be empty so that the values will `NaN` on first
// mousemove and default to a change of (0, 0).
lastPosition = {};
});
// do so on the document.body as well, though depending on the infra,
// these are less reliable as preventDefault may prevent these events
// from propagating all the way to the <body>.
events.documentMouseup.push({
canvas: canvas,
meta: meta,
callback: function () {
mousedown = false;
},
});
events.windowResize.push({
canvas: canvas,
meta: meta,
callback: function () {
funcs.sizeCanvas(canvas, meta);
funcs.displayImage(canvas, context, meta);
},
});
},
imageRatio: function (image) {
return image.naturalWidth / image.naturalHeight;
},
displayImage: function (canvas, context, meta) {
meta.coords.x = Math.max(1 / (meta.zoom * 2), meta.coords.x);
meta.coords.x = Math.min(1 - 1 / (meta.zoom * 2), meta.coords.x);
meta.coords.y = Math.max(1 / (meta.zoom * 2), meta.coords.y);
meta.coords.y = Math.min(1 - 1 / (meta.zoom * 2), meta.coords.y);
var c = {
x: meta.coords.x - 1,
y: meta.coords.y - 1,
};
var x = meta.zoom * c.x * canvas.width + canvas.width / 2;
var y = meta.zoom * c.y * canvas.height + canvas.height / 2;
var w = canvas.width * meta.zoom;
var h = canvas.height * meta.zoom;
canvas.width = canvas.width;
context.imageSmoothingEnabled = false;
context.drawImage(meta.image, x, y, w, h);
},
// the `sizeCanvas` method figures out the appropriate bounding box for
// the canvas given a parent that has constraints.
// for example, if a photo has a ration of 1.5:1 (w:h), and the parent
// box is 1:1 respectively, we want to stretch the photo to be as large
// as we can, which means that we check if having the photo width = 100%
// means that the height is less than 100% of the parent height. If so,
// then we size the photo as w = 100%, h = 100% / 1.5.
sizeCanvas: function (canvas, meta) {
if (typeof meta.onresize === "function") {
meta.onresize(canvas);
}
var parent = {
width: canvas.parentNode.clientWidth,
height: canvas.parentNode.clientHeight,
};
event.callback.call(this, e);
return true;
});
});
if (parent.height * meta.ratio > parent.width) {
canvas.width = parent.width * 2;
canvas.style.width = parent.width + "px";
canvas.height = parent.width / meta.ratio * 2;
canvas.style.height = parent.width / meta.ratio + "px";
} else {
canvas.height = parent.height * 2;
canvas.style.height = parent.height + "px";
canvas.width = parent.height * meta.ratio * 2;
canvas.style.width = parent.height * meta.ratio + "px";
window.addEventListener("resize", function (e) {
events.windowResize = events.windowResize.filter(function (event) {
if (!document.body.contains(event.canvas)) {
return false;
}
blueslip.warn("Please specify a 'data-width' or 'data-height' argument for canvas.");
},
};
event.callback.call(this, e);
// a class w/ prototype to create a new `LightboxCanvas` instance.
var __LightboxCanvas = function (el) {
var self = this;
return true;
}.bind(this));
});
};
this.meta = {
direction: -1,
zoom: 1,
image: null,
coords: {
x: 0.5,
y: 0.5,
},
speed: 1,
// this is to normalize the speed to what I would consider to be
// "standard" zoom speed.
internalSpeedMultiplier: 0.05,
maxZoom: 10,
var funcs = {
setZoom: function (meta, zoom) {
// condition to handle zooming event by zoom hotkeys
if (zoom === '+') {
zoom = meta.zoom * 1.2;
} else if (zoom === '-') {
zoom = meta.zoom / 1.2;
}
// make sure the zoom is above 1 and below the maxZoom.
meta.zoom = Math.min(Math.max(zoom, 1), meta.maxZoom);
},
// this is a function given a canvas that attaches all of the events
// required to pan and zoom.
attachEvents: function (canvas, context, meta) {
var mousedown = false;
// wheelEvent.deltaMode is a value that describes what the unit is
// for the `deltaX`, `deltaY`, and `deltaZ` properties.
var DELTA_MODE = {
PIXEL: 0,
LINE: 1,
PAGE: 2,
};
if (el instanceof Node) {
this.canvas = el;
} else if (typeof el === "string") {
this.canvas = document.querySelector(el);
} else {
blueslip.warn("Error. 'LightboxCanvas' accepts either string selector or node.");
return;
// give object structure in `mousedown`, because its props are only
// ever set once `mousedown` + `mousemove` is triggered.
var lastPosition = {};
// in browsers such as Safari, the `e.movementX` and `e.movementY`
// props don't exist, so we need to create them as a difference of
// where the last `layerX` and `layerY` movements since the last
// `mousemove` event in this `mousedown` event were registered.
var polyfillMouseMovement = function (e) {
e.movementX = e.layerX - lastPosition.x || 0;
e.movementY = e.layerY - lastPosition.y || 0;
lastPosition = {
x: e.layerX,
y: e.layerY,
};
};
// use the wheel event rather than scroll because this isn't
// actually an element that can scroll. The wheel event will
// detect the *gesture* of scrolling over an element, without actually
// worrying about scrollable content.
canvas.addEventListener("wheel", function (e) {
e.preventDefault();
// this is to reverese scrolling directions for the image.
var delta = meta.direction * e.deltaY;
if (e.deltaMode === DELTA_MODE.LINE) {
// the vertical height in pixels of an approximate line.
delta *= 15;
}
if (e.deltaMode === DELTA_MODE.PAGE) {
// the vertical height in pixels of an approximate page.
delta *= 300;
}
// this is calculated as the user defined speed times the normalizer
// (which just is what it takes to take the raw delta and transform
// it to a normal speed), multiply it against the current zoom.
// Example:
// delta = 8
// normalizedDelta = delta * (1 / 20) * 1 = 0.4
// zoom = zoom * (0.4 / 100) + 1
var zoom = meta.zoom * (
meta.speed * meta.internalSpeedMultiplier * delta / 100 + 1
);
funcs.setZoom(meta, zoom);
funcs.displayImage(canvas, context, meta);
return false;
});
// the only valid mousedown events should originate inside of the
// canvas.
canvas.addEventListener("mousedown", function () {
mousedown = true;
});
// on mousemove, actually run the pan events.
canvas.addEventListener("mousemove", function (e) {
// to pan, there must be mousedown and mousemove, check if valid.
if (mousedown === true) {
polyfillMouseMovement(e);
// find the percent of movement relative to the canvas width
// since e.movementX, e.movementY are in px.
var percentMovement = {
x: e.movementX / canvas.width,
y: e.movementY / canvas.height,
};
// add the percentMovement to the meta coordinates but divide
// out by the zoom ratio because when zoomed in 10x for example
// moving the photo by 1% will appear like 10% on the <canvas>.
meta.coords.x += percentMovement.x * 2 / meta.zoom;
meta.coords.y += percentMovement.y * 2 / meta.zoom;
// redraw the image.
funcs.displayImage(canvas, context, meta);
}
});
// event listener to handle zoom in and out from using keyboard keys z/Z and +/-
// in the canvas
// these hotkeys are not implemented in static/js/hotkey.js as the code in
// static/js/lightbox_canvas.js and static/js/lightbox.js isn't written a way
// that the LightboxCanvas instance created in lightbox.js can be
// accessed from hotkey.js. Major code refactoring is required in lightbox.js
// to implement these keyboard shortcuts in hotkey.js
document.addEventListener('keydown', function (e) {
if (!overlays.lightbox_open()) {
return;
}
if (e.key === "Z" || e.key === '+') {
funcs.setZoom(meta, '+');
funcs.displayImage(canvas, context, meta);
} else if (e.key === "z" || e.key === '-') {
funcs.setZoom(meta, '-');
funcs.displayImage(canvas, context, meta);
} else if (e.key === 'v') {
overlays.close_overlay('lightbox');
}
e.preventDefault();
e.stopPropagation();
});
// make sure that when the mousedown is lifted on <canvas>to prevent
// panning events.
canvas.addEventListener("mouseup", function () {
mousedown = false;
// reset this to be empty so that the values will `NaN` on first
// mousemove and default to a change of (0, 0).
lastPosition = {};
});
// do so on the document.body as well, though depending on the infra,
// these are less reliable as preventDefault may prevent these events
// from propagating all the way to the <body>.
events.documentMouseup.push({
canvas: canvas,
meta: meta,
callback: function () {
mousedown = false;
},
});
events.windowResize.push({
canvas: canvas,
meta: meta,
callback: function () {
funcs.sizeCanvas(canvas, meta);
funcs.displayImage(canvas, context, meta);
},
});
},
imageRatio: function (image) {
return image.naturalWidth / image.naturalHeight;
},
displayImage: function (canvas, context, meta) {
meta.coords.x = Math.max(1 / (meta.zoom * 2), meta.coords.x);
meta.coords.x = Math.min(1 - 1 / (meta.zoom * 2), meta.coords.x);
meta.coords.y = Math.max(1 / (meta.zoom * 2), meta.coords.y);
meta.coords.y = Math.min(1 - 1 / (meta.zoom * 2), meta.coords.y);
var c = {
x: meta.coords.x - 1,
y: meta.coords.y - 1,
};
var x = meta.zoom * c.x * canvas.width + canvas.width / 2;
var y = meta.zoom * c.y * canvas.height + canvas.height / 2;
var w = canvas.width * meta.zoom;
var h = canvas.height * meta.zoom;
canvas.width = canvas.width;
context.imageSmoothingEnabled = false;
context.drawImage(meta.image, x, y, w, h);
},
// the `sizeCanvas` method figures out the appropriate bounding box for
// the canvas given a parent that has constraints.
// for example, if a photo has a ration of 1.5:1 (w:h), and the parent
// box is 1:1 respectively, we want to stretch the photo to be as large
// as we can, which means that we check if having the photo width = 100%
// means that the height is less than 100% of the parent height. If so,
// then we size the photo as w = 100%, h = 100% / 1.5.
sizeCanvas: function (canvas, meta) {
if (typeof meta.onresize === "function") {
meta.onresize(canvas);
}
this.context = this.canvas.getContext("2d");
this.meta.image = new Image();
this.meta.image.src = this.canvas.getAttribute("data-src");
this.meta.image.onload = function () {
self.meta.ratio = funcs.imageRatio(this);
funcs.sizeCanvas(self.canvas, self.meta);
funcs.displayImage(self.canvas, self.context, self.meta);
var parent = {
width: canvas.parentNode.clientWidth,
height: canvas.parentNode.clientHeight,
};
this.canvas.image = this.meta.image;
if (parent.height * meta.ratio > parent.width) {
canvas.width = parent.width * 2;
canvas.style.width = parent.width + "px";
funcs.attachEvents(this.canvas, this.context, self.meta);
canvas.height = parent.width / meta.ratio * 2;
canvas.style.height = parent.width / meta.ratio + "px";
} else {
canvas.height = parent.height * 2;
canvas.style.height = parent.height + "px";
canvas.width = parent.height * meta.ratio * 2;
canvas.style.width = parent.height * meta.ratio + "px";
}
blueslip.warn("Please specify a 'data-width' or 'data-height' argument for canvas.");
},
};
// a class w/ prototype to create a new `LightboxCanvas` instance.
var __LightboxCanvas = function (el) {
var self = this;
this.meta = {
direction: -1,
zoom: 1,
image: null,
coords: {
x: 0.5,
y: 0.5,
},
speed: 1,
// this is to normalize the speed to what I would consider to be
// "standard" zoom speed.
internalSpeedMultiplier: 0.05,
maxZoom: 10,
};
__LightboxCanvas.prototype = {
// set the speed at which scrolling zooms in on a photo.
speed: function (speed) {
this.meta.speed = speed;
},
if (el instanceof Node) {
this.canvas = el;
} else if (typeof el === "string") {
this.canvas = document.querySelector(el);
} else {
blueslip.warn("Error. 'LightboxCanvas' accepts either string selector or node.");
return;
}
// set the max zoom of the `LightboxCanvas` canvas as a mult of the total width.
maxZoom: function (maxZoom) {
this.meta.maxZoom = maxZoom;
},
this.context = this.canvas.getContext("2d");
reverseScrollDirection: function () {
this.meta.direction = 1;
},
this.meta.image = new Image();
this.meta.image.src = this.canvas.getAttribute("data-src");
this.meta.image.onload = function () {
self.meta.ratio = funcs.imageRatio(this);
setZoom: function (zoom) {
funcs.setZoom(this.meta, zoom);
funcs.displayImage(this.canvas, this.context, this.meta);
},
resize: function (callback) {
this.meta.onresize = callback;
},
funcs.sizeCanvas(self.canvas, self.meta);
funcs.displayImage(self.canvas, self.context, self.meta);
};
return __LightboxCanvas;
this.canvas.image = this.meta.image;
funcs.attachEvents(this.canvas, this.context, self.meta);
};
__LightboxCanvas.prototype = {
// set the speed at which scrolling zooms in on a photo.
speed: function (speed) {
this.meta.speed = speed;
},
// set the max zoom of the `LightboxCanvas` canvas as a mult of the total width.
maxZoom: function (maxZoom) {
this.meta.maxZoom = maxZoom;
},
reverseScrollDirection: function () {
this.meta.direction = 1;
},
setZoom: function (zoom) {
funcs.setZoom(this.meta, zoom);
funcs.displayImage(this.canvas, this.context, this.meta);
},
resize: function (callback) {
this.meta.onresize = callback;
},
};
return __LightboxCanvas;
}());
if (typeof module !== 'undefined') {

View File

@ -1,220 +1,281 @@
/* eslint indent: "off" */
var list_render = (function () {
var exports = {};
var exports = {};
var DEFAULTS = {
INITIAL_RENDER_COUNT: 80,
LOAD_COUNT: 20,
instances: {},
var DEFAULTS = {
INITIAL_RENDER_COUNT: 80,
LOAD_COUNT: 20,
instances: {},
};
// @params
// container: jQuery object to append to.
// list: The list of items to progressively append.
// opts: An object of random preferences.
exports.create = function ($container, list, opts) {
// this memoizes the results and will return a previously invoked
// instance's prototype.
if (opts.name && DEFAULTS.instances[opts.name]) {
// the false flag here means "don't run `init`". This is because a
// user is likely reinitializing and will have put .init() afterwards.
// This happens when the same codepath is hit multiple times.
return DEFAULTS.instances[opts.name]
// sets the container to the new container in this prototype's args.
.set_container($container)
// sets the input to the new input in the args.
.set_opts(opts)
.__set_events()
.data(list)
.init();
}
var meta = {
sorting_function: null,
prop: null,
sorting_functions: {},
generic_sorting_functions: {},
offset: 0,
listRenders: {},
list: list,
filtered_list: list,
filter_list: function (value, callback) {
this.filtered_list = this.list.filter(function (item) {
if (typeof callback === "function") {
return callback(item, value);
}
return !!(item.toLocaleLowerCase().indexOf(value) >= 0);
});
},
};
// @params
// container: jQuery object to append to.
// list: The list of items to progressively append.
// opts: An object of random preferences.
exports.create = function ($container, list, opts) {
// this memoizes the results and will return a previously invoked
// instance's prototype.
if (opts.name && DEFAULTS.instances[opts.name]) {
// the false flag here means "don't run `init`". This is because a
// user is likely reinitializing and will have put .init() afterwards.
// This happens when the same codepath is hit multiple times.
return DEFAULTS.instances[opts.name]
// sets the container to the new container in this prototype's args.
.set_container($container)
// sets the input to the new input in the args.
.set_opts(opts)
.__set_events()
.data(list)
.init();
}
if (!opts) {
return;
}
var meta = {
sorting_function: null,
prop: null,
sorting_functions: {},
generic_sorting_functions: {},
offset: 0,
listRenders: {},
list: list,
filtered_list: list,
// we want to assume below that `opts.filter` exists, but may not necessarily
// have any defined specs.
if (!opts.filter) {
opts.filter = {};
}
filter_list: function (value, callback) {
this.filtered_list = this.list.filter(function (item) {
if (typeof callback === "function") {
return callback(item, value);
}
var prototype = {
// Reads the provided list (in the scope directly above)
// and renders the next block of messages automatically
// into the specified contianer.
render: function (load_count) {
load_count = load_count || opts.load_count || DEFAULTS.LOAD_COUNT;
return !!(item.toLocaleLowerCase().indexOf(value) >= 0);
});
},
};
// Stop once the offset reaches the length of the original list.
if (meta.offset >= meta.filtered_list.length) {
return;
}
if (!opts) {
return;
}
var slice = meta.filtered_list.slice(meta.offset, meta.offset + load_count);
// we want to assume below that `opts.filter` exists, but may not necessarily
// have any defined specs.
if (!opts.filter) {
opts.filter = {};
}
var html = _.reduce(slice, function (acc, item) {
var _item = opts.modifier(item);
var prototype = {
// Reads the provided list (in the scope directly above)
// and renders the next block of messages automatically
// into the specified contianer.
render: function (load_count) {
load_count = load_count || opts.load_count || DEFAULTS.LOAD_COUNT;
// 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) {
var html = "";
$nodes.each(function () {
if (this.nodeType === 1) {
html += this.outerHTML;
}
});
// Stop once the offset reaches the length of the original list.
if (meta.offset >= meta.filtered_list.length) {
return;
return html;
}(_item));
}
var slice = meta.filtered_list.slice(meta.offset, meta.offset + load_count);
// if is a valid element, get the outerHTML.
if (_item instanceof Element) {
_item = _item.outerHTML;
}
var html = _.reduce(slice, function (acc, item) {
var _item = opts.modifier(item);
// return the modified HTML or nothing if corrupt (null, undef, etc.).
return acc + (_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) {
var html = "";
$nodes.each(function () {
if (this.nodeType === 1) {
html += this.outerHTML;
}
});
$container.append($(html));
meta.offset += load_count;
return html;
}(_item));
}
return this;
},
// if is a valid element, get the outerHTML.
if (_item instanceof Element) {
_item = _item.outerHTML;
}
// 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 the modified HTML or nothing if corrupt (null, undef, etc.).
return acc + (_item || "");
}, "");
filter: function (map_function) {
meta.filtered_list = meta.list(map_function);
},
$container.append($(html));
meta.offset += load_count;
// 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 (data) {
// 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 (typeof data === "undefined" && arguments.length === 0) {
return meta.list;
}
if (Array.isArray(data)) {
meta.list = data;
if (opts.filter && opts.filter.element) {
var value = $(opts.filter.element).val().toLocaleLowerCase();
meta.filter_list(value, opts.filter.callback);
}
prototype.clear();
return this;
},
}
// 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;
},
filter: function (map_function) {
meta.filtered_list = meta.list(map_function);
},
// 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 (data) {
// 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 (typeof data === "undefined" && arguments.length === 0) {
return meta.list;
}
if (Array.isArray(data)) {
meta.list = data;
if (opts.filter && opts.filter.element) {
var value = $(opts.filter.element).val().toLocaleLowerCase();
meta.filter_list(value, opts.filter.callback);
}
prototype.clear();
return this;
}
blueslip.warn("The data object provided to the progressive" +
blueslip.warn("The data object provided to the progressive" +
" list render is invalid");
return this;
},
return this;
},
clear: function () {
$container.html("");
meta.offset = 0;
return this;
},
clear: function () {
$container.html("");
meta.offset = 0;
return this;
},
// Let's imagine the following:
// list_render is initialized and becomes prototope A with scope A.
// list_render is re-initialized and becomes prototype A with scope A again.
// The issue is that when re-initializing, new variables could have been thrown
// in and old variables could be useless (eg. dead nodes), so we need to
// replace these with new copies if necessary.
set_container: function ($new_container) {
if ($new_container) {
$container = $new_container;
// Let's imagine the following:
// list_render is initialized and becomes prototope A with scope A.
// list_render is re-initialized and becomes prototype A with scope A again.
// The issue is that when re-initializing, new variables could have been thrown
// in and old variables could be useless (eg. dead nodes), so we need to
// replace these with new copies if necessary.
set_container: function ($new_container) {
if ($new_container) {
$container = $new_container;
}
return this;
},
set_opts: function (new_opts) {
if (opts) {
opts = new_opts;
}
return this;
},
reverse: function () {
meta.filtered_list.reverse();
prototype.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.
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[sorting_function](prop);
} else {
meta.sorting_function = meta.sorting_functions[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);
}
set_opts: function (new_opts) {
if (opts) {
opts = new_opts;
}
return this;
},
reverse: function () {
meta.filtered_list.reverse();
if (!do_not_display) {
// clear and re-initialize the list with the newly filtered subset
// of items.
prototype.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.onupdate) {
opts.filter.onupdate();
}
}
},
// the `map` will normalize the values with a function you provide to make
// it easier to sort with.
add_sort_function: function (name, sorting_function) {
meta.sorting_functions[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.
add_generic_sort_function: function (name, sorting_function) {
meta.generic_sorting_functions[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[sorting_function](prop);
} else {
meta.sorting_function = meta.sorting_functions[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 () {
var $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) {
prototype.render();
}
});
if (opts.filter.element) {
opts.filter.element.on(opts.filter.event || "input", function () {
var self = this;
var 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
// `prototype.init()`.
prototype.sort(undefined, meta.prop, true);
meta.filter_list(value, opts.filter.callback);
// clear and re-initialize the list with the newly filtered subset
// of items.
prototype.init();
@ -222,126 +283,63 @@ var list_render = (function () {
if (opts.filter.onupdate) {
opts.filter.onupdate();
}
}
},
add_sort_function: function (name, sorting_function) {
meta.sorting_functions[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[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 () {
var $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) {
prototype.render();
}
});
}
if (opts.filter.element) {
opts.filter.element.on(opts.filter.event || "input", function () {
var self = this;
var value = self.value.toLocaleLowerCase();
return this;
},
};
// 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
// `prototype.init()`.
prototype.sort(undefined, meta.prop, true);
meta.filter_list(value, opts.filter.callback);
prototype.__set_events();
// clear and re-initialize the list with the newly filtered subset
// of items.
prototype.init();
// add built-in generic sort functions.
prototype.add_generic_sort_function("alphabetic", function (prop) {
return function (a, b) {
// The conversion to uppercase helps make the sorting case insensitive.
var str1 = a[prop].toUpperCase();
var str2 = b[prop].toUpperCase();
if (opts.filter.onupdate) {
opts.filter.onupdate();
}
});
}
if (str1 === str2) {
return 0;
} else if (str1 > str2) {
return 1;
}
return this;
},
return -1;
};
});
prototype.__set_events();
prototype.add_generic_sort_function("numeric", function (prop) {
return function (a, b) {
if (parseFloat(a[prop]) > parseFloat(b[prop])) {
return 1;
} else if (parseFloat(a[prop]) === parseFloat(b[prop])) {
return 0;
}
// add built-in generic sort functions.
prototype.add_generic_sort_function("alphabetic", function (prop) {
return function (a, b) {
// The conversion to uppercase helps make the sorting case insensitive.
var str1 = a[prop].toUpperCase();
var str2 = b[prop].toUpperCase();
return -1;
};
});
if (str1 === str2) {
return 0;
} else if (str1 > str2) {
return 1;
}
// Save the instance for potential future retrieval if a name is provided.
if (opts.name) {
DEFAULTS.instances[opts.name] = prototype;
}
return -1;
};
});
// Attach click handler to column heads for sorting rows accordingly
if (opts.parent_container) {
opts.parent_container.on("click", "[data-sort]", exports.handle_sort);
}
prototype.add_generic_sort_function("numeric", function (prop) {
return function (a, b) {
if (parseFloat(a[prop]) > parseFloat(b[prop])) {
return 1;
} else if (parseFloat(a[prop]) === parseFloat(b[prop])) {
return 0;
}
return prototype;
};
return -1;
};
});
exports.get = function (name) {
return DEFAULTS.instances[name] || false;
};
// Save the instance for potential future retrieval if a name is provided.
if (opts.name) {
DEFAULTS.instances[opts.name] = prototype;
}
// Attach click handler to column heads for sorting rows accordingly
if (opts.parent_container) {
opts.parent_container.on("click", "[data-sort]", exports.handle_sort);
}
return prototype;
};
exports.get = function (name) {
return DEFAULTS.instances[name] || false;
};
exports.handle_sort = function () {
/*
exports.handle_sort = function () {
/*
one would specify sort parameters like this:
- name => sort alphabetic.
- age => sort numeric.
@ -358,39 +356,39 @@ var list_render = (function () {
</table>
</div>
*/
var $this = $(this);
var sort_type = $this.data("sort");
var prop_name = $this.data("sort-prop");
var list_name = $this.closest(".progressive-table-wrapper").data("list-render");
var $this = $(this);
var sort_type = $this.data("sort");
var prop_name = $this.data("sort-prop");
var list_name = $this.closest(".progressive-table-wrapper").data("list-render");
var list = list_render.get(list_name);
var list = list_render.get(list_name);
if (!list) {
blueslip.error("Error. This `.progressive-table-wrapper` has no `data-list-render` attribute.");
return;
if (!list) {
blueslip.error("Error. This `.progressive-table-wrapper` has no `data-list-render` attribute.");
return;
}
if ($this.hasClass("active")) {
if (!$this.hasClass("descend")) {
$this.addClass("descend");
} else {
$this.removeClass("descend");
}
if ($this.hasClass("active")) {
if (!$this.hasClass("descend")) {
$this.addClass("descend");
} else {
$this.removeClass("descend");
}
list.reverse();
// Table has already been sorted by this property; do not re-sort.
return;
}
list.reverse();
// Table has already been sorted by this property; do not re-sort.
return;
}
// if `prop_name` is defined, it will trigger the generic codepath,
// and not if it is undefined.
list.sort(sort_type, prop_name);
// if `prop_name` is defined, it will trigger the generic codepath,
// and not if it is undefined.
list.sort(sort_type, prop_name);
$this.siblings(".active").removeClass("active");
$this.addClass("active");
};
$this.siblings(".active").removeClass("active");
$this.addClass("active");
};
return exports;
return exports;
}());
if (typeof module !== 'undefined') {

View File

@ -1,4 +1,3 @@
/* eslint indent: "off" */
import SimpleBar from 'simplebar';
import {activate_correct_tab} from './tabbed-instructions.js';
@ -65,107 +64,107 @@ function scrollToHash(simplebar) {
}
(function () {
var html_map = {};
var loading = {
name: null,
};
var html_map = {};
var loading = {
name: null,
};
var markdownSB = new SimpleBar($(".markdown")[0]);
var markdownSB = new SimpleBar($(".markdown")[0]);
var fetch_page = function (path, callback) {
$.get(path, function (res) {
var $html = $(res).find(".markdown .content");
var fetch_page = function (path, callback) {
$.get(path, function (res) {
var $html = $(res).find(".markdown .content");
callback($html.html().trim());
render_code_sections();
});
};
var update_page = function (html_map, path) {
if (html_map[path]) {
$(".markdown .content").html(html_map[path]);
render_code_sections();
scrollToHash(markdownSB);
} else {
loading.name = path;
fetch_page(path, function (res) {
html_map[path] = res;
$(".markdown .content").html(html_map[path]);
loading.name = null;
scrollToHash(markdownSB);
});
}
};
new SimpleBar($(".sidebar")[0]);
$(".sidebar.slide h2").click(function (e) {
var $next = $(e.target).next();
if ($next.is("ul")) {
// Close other article's headings first
$('.sidebar ul').not($next).hide();
// Toggle the heading
$next.slideToggle("fast", "swing");
}
callback($html.html().trim());
render_code_sections();
});
};
$(".sidebar a").click(function (e) {
var path = $(this).attr("href");
var path_dir = path.split('/')[1];
var current_dir = window.location.pathname.split('/')[1];
// Do not block redirecting to external URLs
if (path_dir !== current_dir) {
return;
}
if (loading.name === path) {
return;
}
history.pushState({}, "", path);
update_page(html_map, path);
$(".sidebar").removeClass("show");
e.preventDefault();
});
if (window.location.pathname === '/help/') {
// Expand the Guides user docs section in sidebar in the /help/ homepage.
$('.help .sidebar h2#guides + ul').show();
}
// Remove ID attributes from sidebar links so they don't conflict with index page anchor links
$('.help .sidebar h1, .help .sidebar h2, .help .sidebar h3').removeAttr('id');
// Scroll to anchor link when clicked
$(document).on('click', '.markdown .content h1, .markdown .content h2, .markdown .content h3', function () {
window.location.hash = $(this).attr("id");
var update_page = function (html_map, path) {
if (html_map[path]) {
$(".markdown .content").html(html_map[path]);
render_code_sections();
scrollToHash(markdownSB);
});
} else {
loading.name = path;
fetch_page(path, function (res) {
html_map[path] = res;
$(".markdown .content").html(html_map[path]);
loading.name = null;
scrollToHash(markdownSB);
});
}
};
$(".hamburger").click(function () {
$(".sidebar").toggleClass("show");
});
new SimpleBar($(".sidebar")[0]);
$(".markdown").click(function () {
if ($(".sidebar.show").length) {
$(".sidebar.show").toggleClass("show");
}
});
$(".sidebar.slide h2").click(function (e) {
var $next = $(e.target).next();
render_code_sections();
if ($next.is("ul")) {
// Close other article's headings first
$('.sidebar ul').not($next).hide();
// Toggle the heading
$next.slideToggle("fast", "swing");
}
});
// Finally, make sure if we loaded a window with a hash, we scroll
// to the right place.
$(".sidebar a").click(function (e) {
var path = $(this).attr("href");
var path_dir = path.split('/')[1];
var current_dir = window.location.pathname.split('/')[1];
// Do not block redirecting to external URLs
if (path_dir !== current_dir) {
return;
}
if (loading.name === path) {
return;
}
history.pushState({}, "", path);
update_page(html_map, path);
$(".sidebar").removeClass("show");
e.preventDefault();
});
if (window.location.pathname === '/help/') {
// Expand the Guides user docs section in sidebar in the /help/ homepage.
$('.help .sidebar h2#guides + ul').show();
}
// Remove ID attributes from sidebar links so they don't conflict with index page anchor links
$('.help .sidebar h1, .help .sidebar h2, .help .sidebar h3').removeAttr('id');
// Scroll to anchor link when clicked
$(document).on('click', '.markdown .content h1, .markdown .content h2, .markdown .content h3', function () {
window.location.hash = $(this).attr("id");
scrollToHash(markdownSB);
});
window.addEventListener("popstate", function () {
var path = window.location.pathname;
update_page(html_map, path);
});
$(".hamburger").click(function () {
$(".sidebar").toggleClass("show");
});
$('body').addClass('noscroll');
$(".markdown").click(function () {
if ($(".sidebar.show").length) {
$(".sidebar.show").toggleClass("show");
}
});
render_code_sections();
// Finally, make sure if we loaded a window with a hash, we scroll
// to the right place.
scrollToHash(markdownSB);
window.addEventListener("popstate", function () {
var path = window.location.pathname;
update_page(html_map, path);
});
$('body').addClass('noscroll');
}());

View File

@ -1,49 +1,48 @@
/* eslint indent: "off" */
var realm_icon = (function () {
var exports = {};
var exports = {};
exports.build_realm_icon_widget = function (upload_function) {
var get_file_input = function () {
return $('#realm_icon_file_input').expectOne();
};
exports.build_realm_icon_widget = function (upload_function) {
var get_file_input = function () {
return $('#realm_icon_file_input').expectOne();
};
if (page_params.realm_icon_source === 'G') {
$("#realm_icon_delete_button").hide();
} else {
$("#realm_icon_delete_button").show();
}
$("#realm_icon_delete_button").on('click', function (e) {
e.preventDefault();
e.stopPropagation();
channel.del({
url: '/json/realm/icon',
});
if (page_params.realm_icon_source === 'G') {
$("#realm_icon_delete_button").hide();
} else {
$("#realm_icon_delete_button").show();
}
$("#realm_icon_delete_button").on('click', function (e) {
e.preventDefault();
e.stopPropagation();
channel.del({
url: '/json/realm/icon',
});
});
return upload_widget.build_direct_upload_widget(
get_file_input,
$("#realm_icon_file_input_error").expectOne(),
$("#realm_icon_upload_button").expectOne(),
upload_function,
page_params.max_icon_file_size
);
};
return upload_widget.build_direct_upload_widget(
get_file_input,
$("#realm_icon_file_input_error").expectOne(),
$("#realm_icon_upload_button").expectOne(),
upload_function,
page_params.max_icon_file_size
);
};
exports.rerender = function () {
$("#realm-settings-icon").attr("src", page_params.realm_icon_url);
if (page_params.realm_icon_source === 'U') {
$("#realm_icon_delete_button").show();
} else {
$("#realm_icon_delete_button").hide();
// Need to clear input because of a small edge case
// where you try to upload the same image you just deleted.
var file_input = $("#realm_icon_file_input");
file_input.val('');
}
};
exports.rerender = function () {
$("#realm-settings-icon").attr("src", page_params.realm_icon_url);
if (page_params.realm_icon_source === 'U') {
$("#realm_icon_delete_button").show();
} else {
$("#realm_icon_delete_button").hide();
// Need to clear input because of a small edge case
// where you try to upload the same image you just deleted.
var file_input = $("#realm_icon_file_input");
file_input.val('');
}
};
return exports;
return exports;
}());
if (typeof module !== 'undefined') {

View File

@ -1,90 +1,89 @@
/* eslint indent: "off" */
var realm_logo = (function () {
var exports = {};
var exports = {};
exports.build_realm_logo_widget = function (upload_function, is_night) {
var logo_section_id = '#day-logo-section';
if (is_night) {
logo_section_id = '#night-logo-section';
}
var delete_button_elem = $(logo_section_id + " .realm-logo-delete-button");
var file_input_elem = $(logo_section_id + " .realm-logo-file-input");
var file_input_error_elem = $(logo_section_id + " .realm-logo-file-input-error");
var upload_button_elem = $(logo_section_id + " .realm-logo-upload-button");
var get_file_input = function () {
return file_input_elem.expectOne();
};
if (page_params.realm_logo_source === 'D') {
delete_button_elem.hide();
} else {
delete_button_elem.show();
}
var data = {night: JSON.stringify(is_night)};
delete_button_elem.on('click', function (e) {
e.preventDefault();
e.stopPropagation();
channel.del({
url: '/json/realm/logo',
data: data,
});
});
return upload_widget.build_direct_upload_widget(
get_file_input,
file_input_error_elem.expectOne(),
upload_button_elem.expectOne(),
upload_function,
page_params.max_logo_file_size
);
};
function change_logo_delete_button(logo_source, logo_delete_button, file_input) {
if (logo_source === 'U') {
logo_delete_button.show();
} else {
logo_delete_button.hide();
// Need to clear input because of a small edge case
// where you try to upload the same image you just deleted.
file_input.val('');
}
exports.build_realm_logo_widget = function (upload_function, is_night) {
var logo_section_id = '#day-logo-section';
if (is_night) {
logo_section_id = '#night-logo-section';
}
exports.rerender = function () {
var file_input = $("#day-logo-section .realm-logo-file-input");
var night_file_input = $("#night-logo-section .realm-logo-file-input");
$("#day-logo-section .realm-logo-img").attr("src", page_params.realm_logo_url);
var delete_button_elem = $(logo_section_id + " .realm-logo-delete-button");
var file_input_elem = $(logo_section_id + " .realm-logo-file-input");
var file_input_error_elem = $(logo_section_id + " .realm-logo-file-input-error");
var upload_button_elem = $(logo_section_id + " .realm-logo-upload-button");
if (page_params.realm_night_logo_source === 'D' &&
page_params.realm_logo_source !== 'D') {
// If no night mode logo is uploaded but a day mode one
// is, use the day mode one; this handles the common case
// of transparent background logos that look good on both
// night and day themes. See also similar code in admin.js.
$("#night-logo-section .realm-logo-img").attr("src", page_params.realm_logo_url);
} else {
$("#night-logo-section .realm-logo-img").attr("src", page_params.realm_night_logo_url);
}
if (page_params.night_mode && page_params.realm_night_logo_source !== 'D') {
$("#realm-logo").attr("src", page_params.realm_night_logo_url);
} else {
$("#realm-logo").attr("src", page_params.realm_logo_url);
}
change_logo_delete_button(page_params.realm_logo_source,
$("#day-logo-section .realm-logo-delete-button"),
file_input);
change_logo_delete_button(page_params.realm_night_logo_source,
$("#night-logo-section .realm-logo-delete-button"),
night_file_input);
var get_file_input = function () {
return file_input_elem.expectOne();
};
return exports;
if (page_params.realm_logo_source === 'D') {
delete_button_elem.hide();
} else {
delete_button_elem.show();
}
var data = {night: JSON.stringify(is_night)};
delete_button_elem.on('click', function (e) {
e.preventDefault();
e.stopPropagation();
channel.del({
url: '/json/realm/logo',
data: data,
});
});
return upload_widget.build_direct_upload_widget(
get_file_input,
file_input_error_elem.expectOne(),
upload_button_elem.expectOne(),
upload_function,
page_params.max_logo_file_size
);
};
function change_logo_delete_button(logo_source, logo_delete_button, file_input) {
if (logo_source === 'U') {
logo_delete_button.show();
} else {
logo_delete_button.hide();
// Need to clear input because of a small edge case
// where you try to upload the same image you just deleted.
file_input.val('');
}
}
exports.rerender = function () {
var file_input = $("#day-logo-section .realm-logo-file-input");
var night_file_input = $("#night-logo-section .realm-logo-file-input");
$("#day-logo-section .realm-logo-img").attr("src", page_params.realm_logo_url);
if (page_params.realm_night_logo_source === 'D' &&
page_params.realm_logo_source !== 'D') {
// If no night mode logo is uploaded but a day mode one
// is, use the day mode one; this handles the common case
// of transparent background logos that look good on both
// night and day themes. See also similar code in admin.js.
$("#night-logo-section .realm-logo-img").attr("src", page_params.realm_logo_url);
} else {
$("#night-logo-section .realm-logo-img").attr("src", page_params.realm_night_logo_url);
}
if (page_params.night_mode && page_params.realm_night_logo_source !== 'D') {
$("#realm-logo").attr("src", page_params.realm_night_logo_url);
} else {
$("#realm-logo").attr("src", page_params.realm_logo_url);
}
change_logo_delete_button(page_params.realm_logo_source,
$("#day-logo-section .realm-logo-delete-button"),
file_input);
change_logo_delete_button(page_params.realm_night_logo_source,
$("#night-logo-section .realm-logo-delete-button"),
night_file_input);
};
return exports;
}());
if (typeof module !== 'undefined') {

View File

@ -1,179 +1,178 @@
/* eslint indent: "off" */
var upload_widget = (function () {
var exports = {};
var exports = {};
var default_max_file_size = 5;
var default_max_file_size = 5;
var supported_types = [
'image/jpeg',
'image/png',
'image/gif',
'image/tiff',
];
var supported_types = [
'image/jpeg',
'image/png',
'image/gif',
'image/tiff',
];
function is_image_format(file) {
var type = file.type;
if (!type) {
return false;
}
return _.indexOf(supported_types, type) >= 0;
function is_image_format(file) {
var type = file.type;
if (!type) {
return false;
}
return _.indexOf(supported_types, type) >= 0;
}
exports.build_widget = function (
get_file_input, // function returns a jQuery file input object
file_name_field, // jQuery object to show file name
input_error, // jQuery object for error text
clear_button, // jQuery button to clear last upload choice
upload_button, // jQuery button to open file dialog
max_file_upload_size
) {
// default value of max upladed file size
max_file_upload_size = max_file_upload_size || default_max_file_size;
function accept(file) {
file_name_field.text(file.name);
input_error.hide();
clear_button.show();
upload_button.hide();
}
exports.build_widget = function (
get_file_input, // function returns a jQuery file input object
file_name_field, // jQuery object to show file name
input_error, // jQuery object for error text
clear_button, // jQuery button to clear last upload choice
upload_button, // jQuery button to open file dialog
max_file_upload_size
) {
// default value of max upladed file size
max_file_upload_size = max_file_upload_size || default_max_file_size;
function clear() {
var control = get_file_input();
control.val('');
file_name_field.text('');
clear_button.hide();
upload_button.show();
}
function accept(file) {
file_name_field.text(file.name);
input_error.hide();
clear_button.show();
upload_button.hide();
}
clear_button.on('click', function (e) {
clear();
e.preventDefault();
});
function clear() {
var control = get_file_input();
control.val('');
file_name_field.text('');
clear_button.hide();
upload_button.show();
}
clear_button.on('click', function (e) {
clear();
e.preventDefault();
});
upload_button.on('drop', function (e) {
var files = e.dataTransfer.files;
if (files === null || files === undefined || files.length === 0) {
return false;
}
get_file_input().get(0).files = files;
e.preventDefault();
upload_button.on('drop', function (e) {
var files = e.dataTransfer.files;
if (files === null || files === undefined || files.length === 0) {
return false;
});
get_file_input().attr('accept', supported_types.toString());
get_file_input().on('change', function (e) {
if (e.target.files.length === 0) {
input_error.hide();
} else if (e.target.files.length === 1) {
var file = e.target.files[0];
if (file.size > max_file_upload_size * 1024 * 1024) {
input_error.text(i18n.t('File size must be < __max_file_size__Mb.', {
max_file_size: max_file_upload_size,
}));
input_error.show();
clear();
} else if (!is_image_format(file)) {
input_error.text(i18n.t('File type is not supported.'));
input_error.show();
clear();
} else {
accept(file);
}
} else {
input_error.text(i18n.t('Please just upload one file.'));
}
});
upload_button.on('click', function (e) {
get_file_input().trigger('click');
e.preventDefault();
});
function close() {
clear();
clear_button.off('click');
upload_button.off('drop');
get_file_input().off('change');
upload_button.off('click');
}
get_file_input().get(0).files = files;
e.preventDefault();
return false;
});
return {
// Call back to clear() in situations like adding bots, when
// we want to use the same widget over and over again.
clear: clear,
// Call back to close() when you are truly done with the widget,
// so you can release handlers.
close: close,
};
};
exports.build_direct_upload_widget = function (
get_file_input, // function returns a jQuery file input object
input_error, // jQuery object for error text
upload_button, // jQuery button to open file dialog
upload_function,
max_file_upload_size
) {
// default value of max upladed file size
max_file_upload_size = max_file_upload_size || default_max_file_size;
function accept() {
get_file_input().attr('accept', supported_types.toString());
get_file_input().on('change', function (e) {
if (e.target.files.length === 0) {
input_error.hide();
if (upload_button.hasClass("night-settings")) {
upload_function(get_file_input(), true);
} else if (upload_button.hasClass("day-settings")) {
upload_function(get_file_input(), false);
} else if (e.target.files.length === 1) {
var file = e.target.files[0];
if (file.size > max_file_upload_size * 1024 * 1024) {
input_error.text(i18n.t('File size must be < __max_file_size__Mb.', {
max_file_size: max_file_upload_size,
}));
input_error.show();
clear();
} else if (!is_image_format(file)) {
input_error.text(i18n.t('File type is not supported.'));
input_error.show();
clear();
} else {
upload_function(get_file_input());
accept(file);
}
} else {
input_error.text(i18n.t('Please just upload one file.'));
}
});
function clear() {
var control = get_file_input();
var new_control = control.clone(true);
control.replaceWith(new_control);
}
upload_button.on('click', function (e) {
get_file_input().trigger('click');
e.preventDefault();
});
upload_button.on('drop', function (e) {
var files = e.dataTransfer.files;
if (files === null || files === undefined || files.length === 0) {
return false;
}
get_file_input().get(0).files = files;
e.preventDefault();
return false;
});
function close() {
clear();
clear_button.off('click');
upload_button.off('drop');
get_file_input().off('change');
upload_button.off('click');
}
get_file_input().attr('accept', supported_types.toString());
get_file_input().on('change', function (e) {
if (e.target.files.length === 0) {
input_error.hide();
} else if (e.target.files.length === 1) {
var file = e.target.files[0];
if (file.size > max_file_upload_size * 1024 * 1024) {
input_error.text(i18n.t('File size must be < __max_file_size__Mb.', {
max_file_size: max_file_upload_size,
}));
input_error.show();
clear();
} else if (!is_image_format(file)) {
input_error.text(i18n.t('File type is not supported.'));
input_error.show();
clear();
} else {
accept();
}
} else {
input_error.text(i18n.t('Please just upload one file.'));
}
});
upload_button.on('click', function (e) {
get_file_input().trigger('click');
e.preventDefault();
});
return {
// Call back to clear() in situations like adding bots, when
// we want to use the same widget over and over again.
clear: clear,
// Call back to close() when you are truly done with the widget,
// so you can release handlers.
close: close,
};
};
exports.build_direct_upload_widget = function (
get_file_input, // function returns a jQuery file input object
input_error, // jQuery object for error text
upload_button, // jQuery button to open file dialog
upload_function,
max_file_upload_size
) {
// default value of max upladed file size
max_file_upload_size = max_file_upload_size || default_max_file_size;
function accept() {
input_error.hide();
if (upload_button.hasClass("night-settings")) {
upload_function(get_file_input(), true);
} else if (upload_button.hasClass("day-settings")) {
upload_function(get_file_input(), false);
} else {
upload_function(get_file_input());
}
}
return exports;
function clear() {
var control = get_file_input();
var new_control = control.clone(true);
control.replaceWith(new_control);
}
upload_button.on('drop', function (e) {
var files = e.dataTransfer.files;
if (files === null || files === undefined || files.length === 0) {
return false;
}
get_file_input().get(0).files = files;
e.preventDefault();
return false;
});
get_file_input().attr('accept', supported_types.toString());
get_file_input().on('change', function (e) {
if (e.target.files.length === 0) {
input_error.hide();
} else if (e.target.files.length === 1) {
var file = e.target.files[0];
if (file.size > max_file_upload_size * 1024 * 1024) {
input_error.text(i18n.t('File size must be < __max_file_size__Mb.', {
max_file_size: max_file_upload_size,
}));
input_error.show();
clear();
} else if (!is_image_format(file)) {
input_error.text(i18n.t('File type is not supported.'));
input_error.show();
clear();
} else {
accept();
}
} else {
input_error.text(i18n.t('Please just upload one file.'));
}
});
upload_button.on('click', function (e) {
get_file_input().trigger('click');
e.preventDefault();
});
};
return exports;
}());