From 5f590d3500adada8b3594bcddc7ad45c1486e152 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Fri, 25 Oct 2019 00:15:16 -0700 Subject: [PATCH] js: Remove /* eslint indent: "off" */ comments. The time has come to dedent these files. Signed-off-by: Anders Kaseorg --- static/js/bot_data.js | 154 +++++---- static/js/lightbox_canvas.js | 614 +++++++++++++++++----------------- static/js/list_render.js | 620 +++++++++++++++++------------------ static/js/portico/help.js | 181 +++++----- static/js/realm_icon.js | 75 +++-- static/js/realm_logo.js | 159 +++++---- static/js/upload_widget.js | 309 +++++++++-------- 7 files changed, 1051 insertions(+), 1061 deletions(-) diff --git a/static/js/bot_data.js b/static/js/bot_data.js index bc2d51a39d..17dcae06bf 100644 --- a/static/js/bot_data.js +++ b/static/js/bot_data.js @@ -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; diff --git a/static/js/lightbox_canvas.js b/static/js/lightbox_canvas.js index 8a5d9efef6..c2a03fec28 100644 --- a/static/js/lightbox_canvas.js +++ b/static/js/lightbox_canvas.js @@ -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 . - 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 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 . - 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 . + 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 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 . + 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') { diff --git a/static/js/list_render.js b/static/js/list_render.js index d79c943cb4..5f45846904 100644 --- a/static/js/list_render.js +++ b/static/js/list_render.js @@ -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 () { */ - 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') { diff --git a/static/js/portico/help.js b/static/js/portico/help.js index 660f9748ae..52bd4e99bf 100644 --- a/static/js/portico/help.js +++ b/static/js/portico/help.js @@ -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'); }()); diff --git a/static/js/realm_icon.js b/static/js/realm_icon.js index 635bdccf17..d7941732fe 100644 --- a/static/js/realm_icon.js +++ b/static/js/realm_icon.js @@ -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') { diff --git a/static/js/realm_logo.js b/static/js/realm_logo.js index 5a135a531f..2c262524f2 100644 --- a/static/js/realm_logo.js +++ b/static/js/realm_logo.js @@ -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') { diff --git a/static/js/upload_widget.js b/static/js/upload_widget.js index f632ffa1cb..70d2f855fc 100644 --- a/static/js/upload_widget.js +++ b/static/js/upload_widget.js @@ -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; }());