change some typings, rename $.fn.animatedScrollTop to $.fn.animateScrollTop

This commit is contained in:
David Sevilla Martin 2020-03-21 16:42:24 -04:00
parent 83d0345e93
commit 0dc846bc4a
No known key found for this signature in database
GPG Key ID: F764F1417E16B15F
17 changed files with 121 additions and 157 deletions

3
js/dist/admin.js vendored

@ -11812,7 +11812,7 @@ $.fn.hover = function (hover, leave) {
}; // add animated scroll }; // add animated scroll
$.fn.animatedScrollTop = function (to, duration, callback) { $.fn.animateScrollTop = function (to, duration, callback) {
if (duration === void 0) { if (duration === void 0) {
duration = $.fx.speeds._default; duration = $.fx.speeds._default;
} }
@ -11848,7 +11848,6 @@ $.fn.extend = $.extend.bind($);
* Enable special events on Zepto * Enable special events on Zepto
* @license Original Copyright 2013 Enideo. Released under dual MIT and GPL licenses. * @license Original Copyright 2013 Enideo. Released under dual MIT and GPL licenses.
*/ */
// @ts-ignore
$.event.special = $.event.special || {}; $.event.special = $.event.special || {};
var bindBeforeSpecialEvents = $.fn.bind; var bindBeforeSpecialEvents = $.fn.bind;

File diff suppressed because one or more lines are too long

104
js/dist/forum.js vendored

@ -12767,13 +12767,16 @@ var Application = /*#__PURE__*/function () {
basePath = ''; basePath = '';
} }
m.mount(document.getElementById('modal'), this.modal = new _components_ModalManager__WEBPACK_IMPORTED_MODULE_19__["default"]()); var $modal = document.getElementById('modal');
m.mount(document.getElementById('alerts'), this.alerts = new _components_AlertManager__WEBPACK_IMPORTED_MODULE_22__["default"]({ var $alerts = document.getElementById('alerts');
var $content = document.getElementById('content');
if ($modal) m.mount($modal, this.modal = new _components_ModalManager__WEBPACK_IMPORTED_MODULE_19__["default"]());
if ($alerts) m.mount($alerts, this.alerts = new _components_AlertManager__WEBPACK_IMPORTED_MODULE_22__["default"]({
oninit: function oninit(vnode) { oninit: function oninit(vnode) {
return _this3.alerts = vnode.state; return _this3.alerts = vnode.state;
} }
})); }));
m.route(document.getElementById('content'), basePath + '/', Object(_utils_mapRoutes__WEBPACK_IMPORTED_MODULE_6__["default"])(this.routes, basePath)); // Add a class to the body which indicates that the page has been scrolled if ($content) m.route($content, basePath + '/', Object(_utils_mapRoutes__WEBPACK_IMPORTED_MODULE_6__["default"])(this.routes, basePath)); // Add a class to the body which indicates that the page has been scrolled
// down. // down.
new _utils_ScrollListener__WEBPACK_IMPORTED_MODULE_10__["default"](function (top) { new _utils_ScrollListener__WEBPACK_IMPORTED_MODULE_10__["default"](function (top) {
@ -13128,8 +13131,6 @@ __webpack_require__.r(__webpack_exports__);
/** /**
* The `Model` class represents a local data resource. It provides methods to * The `Model` class represents a local data resource. It provides methods to
* persist changes via the API. * persist changes via the API.
*
* @abstract
*/ */
var Model = /*#__PURE__*/function () { var Model = /*#__PURE__*/function () {
/** /**
@ -13151,18 +13152,14 @@ var Model = /*#__PURE__*/function () {
*/ */
/** /**
* @param {Object} data A resource object from the API. * @param data A resource object from the API.
* @param {Store} store The data store that this model should be persisted to. * @param store The data store that this model should be persisted to.
*/ */
function Model(data, store) { function Model(data, store) {
if (data === void 0) { if (data === void 0) {
data = {}; data = {};
} }
if (store === void 0) {
store = null;
}
this.data = void 0; this.data = void 0;
this.payload = void 0; this.payload = void 0;
this.freshness = void 0; this.freshness = void 0;
@ -13197,7 +13194,6 @@ var Model = /*#__PURE__*/function () {
* Merge new data into this model locally. * Merge new data into this model locally.
* *
* @param data A resource object to merge into this model * @param data A resource object to merge into this model
* @public
*/ */
; ;
@ -13232,7 +13228,7 @@ var Model = /*#__PURE__*/function () {
/** /**
* Merge new attributes into this model locally. * Merge new attributes into this model locally.
* *
* @param {Object} attributes The attributes to merge. * @param attributes The attributes to merge.
*/ */
; ;
@ -13247,7 +13243,6 @@ var Model = /*#__PURE__*/function () {
* @param attributes The attributes to save. If a 'relationships' key * @param attributes The attributes to save. If a 'relationships' key
* exists, it will be extracted and relationships will also be saved. * exists, it will be extracted and relationships will also be saved.
* @param [options] * @param [options]
* @return {Promise}
*/ */
; ;
@ -13358,7 +13353,7 @@ var Model = /*#__PURE__*/function () {
/** /**
* Generate a function which returns the value of the given attribute. * Generate a function which returns the value of the given attribute.
* *
* @param {String} name * @param name
* @param [transform] A function to transform the attribute value * @param [transform] A function to transform the attribute value
*/ */
; ;
@ -13384,7 +13379,7 @@ var Model = /*#__PURE__*/function () {
if (this.data.relationships) { if (this.data.relationships) {
var relationship = this.data.relationships[name]; var relationship = this.data.relationships[name];
if (relationship) { if (relationship && !Array.isArray(relationship.data)) {
return app.store.getById(relationship.data.type, relationship.data.id); return app.store.getById(relationship.data.type, relationship.data.id);
} }
} }
@ -13407,7 +13402,7 @@ var Model = /*#__PURE__*/function () {
if (this.data.relationships) { if (this.data.relationships) {
var relationship = this.data.relationships[name]; var relationship = this.data.relationships[name];
if (relationship) { if (relationship && Array.isArray(relationship.data)) {
return relationship.data.map(function (data) { return relationship.data.map(function (data) {
return app.store.getById(data.type, data.id); return app.store.getById(data.type, data.id);
}); });
@ -13724,10 +13719,6 @@ var Translator = /*#__PURE__*/function () {
}; };
_proto.trans = function trans(id, parameters) { _proto.trans = function trans(id, parameters) {
if (parameters === void 0) {
parameters = null;
}
var translation = this.translations[id]; var translation = this.translations[id];
if (translation) { if (translation) {
@ -13738,10 +13729,6 @@ var Translator = /*#__PURE__*/function () {
}; };
_proto.transText = function transText(id, parameters) { _proto.transText = function transText(id, parameters) {
if (parameters === void 0) {
parameters = null;
}
return Object(_utils_extractText__WEBPACK_IMPORTED_MODULE_2__["default"])(this.trans(id, parameters)); return Object(_utils_extractText__WEBPACK_IMPORTED_MODULE_2__["default"])(this.trans(id, parameters));
}; };
@ -13749,7 +13736,6 @@ var Translator = /*#__PURE__*/function () {
var translation = this.translations[id]; var translation = this.translations[id];
if (translation) { if (translation) {
number = parseInt(number, 10);
translation = this.pluralize(translation, number); translation = this.pluralize(translation, number);
return this.apply(translation, parameters || {}); return this.apply(translation, parameters || {});
} }
@ -17256,7 +17242,7 @@ $.fn.hover = function (hover, leave) {
}; // add animated scroll }; // add animated scroll
$.fn.animatedScrollTop = function (to, duration, callback) { $.fn.animateScrollTop = function (to, duration, callback) {
if (duration === void 0) { if (duration === void 0) {
duration = $.fx.speeds._default; duration = $.fx.speeds._default;
} }
@ -17292,7 +17278,6 @@ $.fn.extend = $.extend.bind($);
* Enable special events on Zepto * Enable special events on Zepto
* @license Original Copyright 2013 Enideo. Released under dual MIT and GPL licenses. * @license Original Copyright 2013 Enideo. Released under dual MIT and GPL licenses.
*/ */
// @ts-ignore
$.event.special = $.event.special || {}; $.event.special = $.event.special || {};
var bindBeforeSpecialEvents = $.fn.bind; var bindBeforeSpecialEvents = $.fn.bind;
@ -18815,8 +18800,8 @@ var DiscussionPage = /*#__PURE__*/function (_Page) {
} }
_this = _Page.call.apply(_Page, [this].concat(args)) || this; _this = _Page.call.apply(_Page, [this].concat(args)) || this;
_this.discussion = void 0; _this.discussion = null;
_this.near = void 0; _this.near = null;
_this.stream = void 0; _this.stream = void 0;
_this.scrubber = void 0; _this.scrubber = void 0;
_this.includedPosts = []; _this.includedPosts = [];
@ -18999,23 +18984,19 @@ var DiscussionPage = /*#__PURE__*/function (_Page) {
var $list = $(vnode.dom); // When the mouse enters and leaves the discussions pane, we want to show var $list = $(vnode.dom); // When the mouse enters and leaves the discussions pane, we want to show
// and hide the pane respectively. We also create a 10px 'hot edge' on the // and hide the pane respectively. We also create a 10px 'hot edge' on the
// left of the screen to activate the pane. // left of the screen to activate the pane.
// TODO pane
var pane = app.pane; // const pane = app.pane;
$list.hover(pane.show.bind(pane), pane.onmouseleave.bind(pane)); // $list.hover(pane.show.bind(pane), pane.onmouseleave.bind(pane));
//
var hotEdge = function hotEdge(e) { // const hotEdge = e => {
if (e.pageX < 10) pane.show(); // if (e.pageX < 10) pane.show();
}; // };
// $(document).on('mousemove', hotEdge);
$(document).on('mousemove', hotEdge); // vnode.dom.onunload = () => $(document).off('mousemove', hotEdge);
// If the discussion we are viewing is listed in the discussion list, then
vnode.dom.onunload = function () {
return $(document).off('mousemove', hotEdge);
}; // If the discussion we are viewing is listed in the discussion list, then
// we will make sure it is visible in the viewport – if it is not we will // we will make sure it is visible in the viewport – if it is not we will
// scroll the list down to it. // scroll the list down to it.
var $discussion = $list.find('.DiscussionListItem.active'); var $discussion = $list.find('.DiscussionListItem.active');
if ($discussion.length) { if ($discussion.length) {
@ -21633,7 +21614,7 @@ var PostStream = /*#__PURE__*/function (_Component) {
$container.scrollTop(top); $container.scrollTop(top);
resolve(); resolve();
} else if (top !== scrollTop) { } else if (top !== scrollTop) {
$container.animatedScrollTop(top, 'fast', resolve); $container.animateScrollTop(top, 'fast', resolve);
} else { } else {
resolve(); resolve();
} }
@ -21876,6 +21857,10 @@ var PostStreamScrubber = /*#__PURE__*/function (_Component) {
; ;
_proto.update = function update(scrollTop) { _proto.update = function update(scrollTop) {
if (scrollTop === void 0) {
scrollTop = 0;
}
var stream = this.stream; var stream = this.stream;
var marginTop = stream.getMarginTop(); var marginTop = stream.getMarginTop();
var viewportTop = scrollTop + marginTop; var viewportTop = scrollTop + marginTop;
@ -21924,6 +21909,7 @@ var PostStreamScrubber = /*#__PURE__*/function (_Component) {
var time = $this.data('time'); var time = $this.data('time');
if (time) period = time; if (time) period = time;
return true;
}); });
this.index = index; this.index = index;
this.visible = visible; this.visible = visible;
@ -22544,11 +22530,11 @@ var Search = /*#__PURE__*/function (_Component) {
_this = _Component.call.apply(_Component, [this].concat(args)) || this; _this = _Component.call.apply(_Component, [this].concat(args)) || this;
_this.value = m.prop(''); _this.value = m.prop('');
_this.hasFocus = false; _this.hasFocus = false;
_this.sources = null; _this.sources = void 0;
_this.loadingSources = 0; _this.loadingSources = 0;
_this.searched = []; _this.searched = [];
_this.index = 0; _this.index = 0;
_this.navigator = void 0; _this.navigator = new _utils_KeyboardNavigatable__WEBPACK_IMPORTED_MODULE_4__["default"]();
_this.searchTimeout = void 0; _this.searchTimeout = void 0;
return _this; return _this;
} }
@ -22619,7 +22605,6 @@ var Search = /*#__PURE__*/function (_Component) {
search.setIndex(search.selectableItems().index(this)); search.setIndex(search.selectableItems().index(this));
}); });
var $input = this.$('input'); var $input = this.$('input');
this.navigator = new _utils_KeyboardNavigatable__WEBPACK_IMPORTED_MODULE_4__["default"]();
this.navigator.onUp(function () { this.navigator.onUp(function () {
return _this3.setIndex(_this3.getCurrentNumericIndex() - 1, true); return _this3.setIndex(_this3.getCurrentNumericIndex() - 1, true);
}).onDown(function () { }).onDown(function () {
@ -22633,7 +22618,7 @@ var Search = /*#__PURE__*/function (_Component) {
search.searchTimeout = setTimeout(function () { search.searchTimeout = setTimeout(function () {
if (search.searched.indexOf(query) !== -1) return; if (search.searched.indexOf(query) !== -1) return;
if (query.length >= 3) { if (query.length >= 3 && search.sources) {
search.sources.map(function (source) { search.sources.map(function (source) {
if (!source.search) return; if (!source.search) return;
search.loadingSources++; search.loadingSources++;
@ -22655,8 +22640,6 @@ var Search = /*#__PURE__*/function (_Component) {
} }
/** /**
* Get the active search in the app's current controller. * Get the active search in the app's current controller.
*
* @return {String}
*/ */
; ;
@ -22709,8 +22692,6 @@ var Search = /*#__PURE__*/function (_Component) {
} }
/** /**
* Get all of the search result items that are selectable. * Get all of the search result items that are selectable.
*
* @return {jQuery}
*/ */
; ;
@ -22719,8 +22700,6 @@ var Search = /*#__PURE__*/function (_Component) {
} }
/** /**
* Get the position of the currently selected search result item. * Get the position of the currently selected search result item.
*
* @return {Integer}
*/ */
; ;
@ -22729,9 +22708,6 @@ var Search = /*#__PURE__*/function (_Component) {
} }
/** /**
* Get the <li> in the search results with the given index (numeric or named). * Get the <li> in the search results with the given index (numeric or named).
*
* @param {String} index
* @return {DOMElement}
*/ */
; ;
@ -22767,7 +22743,7 @@ var Search = /*#__PURE__*/function (_Component) {
} }
var $item = $items.removeClass('active').eq(fixedIndex).addClass('active'); var $item = $items.removeClass('active').eq(fixedIndex).addClass('active');
this.index = $item.attr('data-index') || fixedIndex; this.index = Number($item.attr('data-index') || fixedIndex);
if (scrollToItem) { if (scrollToItem) {
var dropdownScroll = $dropdown.scrollTop(); var dropdownScroll = $dropdown.scrollTop();
@ -22784,9 +22760,7 @@ var Search = /*#__PURE__*/function (_Component) {
} }
if (typeof scrollTop !== 'undefined') { if (typeof scrollTop !== 'undefined') {
$dropdown.animate({ $dropdown.animateScrollTop(scrollTop, 100);
scrollTop: scrollTop
}, 100);
} }
} }
}; };
@ -24215,9 +24189,9 @@ var History = /*#__PURE__*/function () {
/** /**
* Push an item to the top of the stack. * Push an item to the top of the stack.
* *
* @param {String} name The name of the route. * @param name The name of the route.
* @param {String} title The title of the route. * @param title The title of the route.
* @param {String} [url] The URL of the route. The current URL will be used if * @param [url] The URL of the route. The current URL will be used if
* not provided. * not provided.
*/ */
; ;

File diff suppressed because one or more lines are too long

@ -139,11 +139,15 @@ export default abstract class Application {
} }
mount(basePath = '') { mount(basePath = '') {
m.mount(document.getElementById('modal'), (this.modal = new ModalManager())); const $modal = document.getElementById('modal');
const $alerts = document.getElementById('alerts');
const $content = document.getElementById('content');
m.mount(document.getElementById('alerts'), (this.alerts = new AlertManager({ oninit: vnode => (this.alerts = vnode.state) }))); if ($modal) m.mount($modal, (this.modal = new ModalManager()));
m.route(document.getElementById('content'), basePath + '/', mapRoutes(this.routes, basePath)); if ($alerts) m.mount($alerts, (this.alerts = new AlertManager({ oninit: vnode => (this.alerts = vnode.state) })));
if ($content) m.route($content, basePath + '/', mapRoutes(this.routes, basePath));
// Add a class to the body which indicates that the page has been scrolled // Add a class to the body which indicates that the page has been scrolled
// down. // down.
@ -210,7 +214,7 @@ export default abstract class Application {
if (params.hasOwnProperty(key) && !params[key]) delete params[key]; if (params.hasOwnProperty(key) && !params[key]) delete params[key];
} }
const queryString = m.buildQueryString(params); const queryString = m.buildQueryString(params as Mithril.Params);
const prefix = m.route.prefix === '' ? this.forum.attribute('basePath') : ''; const prefix = m.route.prefix === '' ? this.forum.attribute('basePath') : '';
return prefix + url + (queryString ? '?' + queryString : ''); return prefix + url + (queryString ? '?' + queryString : '');
@ -221,8 +225,8 @@ export default abstract class Application {
* *
* @see https://mithril.js.org/request.html * @see https://mithril.js.org/request.html
*/ */
request(originalOptions: Mithril.RequestOptions | any): Promise<any> { request(originalOptions: Mithril.RequestOptions<JSON> | any): Promise<any> {
const options: Mithril.RequestOptions = Object.assign({}, originalOptions); const options: Mithril.RequestOptions<JSON> | any = Object.assign({}, originalOptions);
// Set some default options if they haven't been overridden. We want to // Set some default options if they haven't been overridden. We want to
// authenticate all requests with the session token. We also want all // authenticate all requests with the session token. We also want all
@ -230,7 +234,7 @@ export default abstract class Application {
// prevent redraws from occurring. // prevent redraws from occurring.
options.background = options.background || true; options.background = options.background || true;
extend(options, 'config', (result, xhr: XMLHttpRequest) => xhr.setRequestHeader('X-CSRF-Token', this.session.csrfToken)); extend(options, 'config', (result, xhr: XMLHttpRequest) => xhr.setRequestHeader('X-CSRF-Token', this.session.csrfToken!));
// If the method is something like PATCH or DELETE, which not all servers // If the method is something like PATCH or DELETE, which not all servers
// and clients support, then we'll send it as a POST request with the // and clients support, then we'll send it as a POST request with the
@ -347,7 +351,7 @@ export default abstract class Application {
} }
private showDebug(error: RequestError) { private showDebug(error: RequestError) {
this.alerts.dismiss(this.requestError.alert); this.alerts.dismiss(this.requestError!.alert);
this.modal.show(RequestErrorModal, { error }); this.modal.show(RequestErrorModal, { error });
} }

@ -65,7 +65,7 @@ export default class Component<T extends ComponentProps = any> implements ClassC
} }
render() { render() {
return m(this.constructor, this.props); return m(this.constructor as typeof Component, this.props);
} }
static component(props: ComponentProps | any = {}, children?: Mithril.Children) { static component(props: ComponentProps | any = {}, children?: Mithril.Children) {

@ -1,9 +1,3 @@
/**
* The `Model` class represents a local data resource. It provides methods to
* persist changes via the API.
*
* @abstract
*/
import Store from './Store'; import Store from './Store';
export interface Identifier { export interface Identifier {
@ -16,7 +10,11 @@ export interface Data extends Identifier {
relationships?: { [key: string]: { data: Identifier | Identifier[] } }; relationships?: { [key: string]: { data: Identifier | Identifier[] } };
} }
export default class Model { /**
* The `Model` class represents a local data resource. It provides methods to
* persist changes via the API.
*/
export default abstract class Model {
/** /**
* The resource object from the API. * The resource object from the API.
*/ */
@ -39,13 +37,13 @@ export default class Model {
/** /**
* The data store that this resource should be persisted to. * The data store that this resource should be persisted to.
*/ */
protected store: Store; protected store?: Store;
/** /**
* @param {Object} data A resource object from the API. * @param data A resource object from the API.
* @param {Store} store The data store that this model should be persisted to. * @param store The data store that this model should be persisted to.
*/ */
constructor(data = {}, store = null) { constructor(data = <Data>{}, store?: Store) {
this.data = data; this.data = data;
this.store = store; this.store = store;
@ -73,9 +71,8 @@ export default class Model {
* Merge new data into this model locally. * Merge new data into this model locally.
* *
* @param data A resource object to merge into this model * @param data A resource object to merge into this model
* @public
*/ */
pushData(data: {}) { public pushData(data: {}) {
// Since most of the top-level items in a resource object are objects // Since most of the top-level items in a resource object are objects
// (e.g. relationships, attributes), we'll need to check and perform the // (e.g. relationships, attributes), we'll need to check and perform the
// merge at the second level if that's the case. // merge at the second level if that's the case.
@ -105,7 +102,7 @@ export default class Model {
/** /**
* Merge new attributes into this model locally. * Merge new attributes into this model locally.
* *
* @param {Object} attributes The attributes to merge. * @param attributes The attributes to merge.
*/ */
pushAttributes(attributes: any) { pushAttributes(attributes: any) {
this.pushData({ attributes }); this.pushData({ attributes });
@ -117,7 +114,6 @@ export default class Model {
* @param attributes The attributes to save. If a 'relationships' key * @param attributes The attributes to save. If a 'relationships' key
* exists, it will be extracted and relationships will also be saved. * exists, it will be extracted and relationships will also be saved.
* @param [options] * @param [options]
* @return {Promise}
*/ */
save(attributes: any, options: any = {}): Promise<Model | Model[]> { save(attributes: any, options: any = {}): Promise<Model | Model[]> {
const data: Data = { const data: Data = {
@ -208,7 +204,7 @@ export default class Model {
) )
.then(() => { .then(() => {
this.exists = false; this.exists = false;
this.store.remove(this); this.store!.remove(this);
}); });
} }
@ -229,7 +225,7 @@ export default class Model {
/** /**
* Generate a function which returns the value of the given attribute. * Generate a function which returns the value of the given attribute.
* *
* @param {String} name * @param name
* @param [transform] A function to transform the attribute value * @param [transform] A function to transform the attribute value
*/ */
static attribute(name: string, transform?: Function): () => any { static attribute(name: string, transform?: Function): () => any {
@ -253,7 +249,7 @@ export default class Model {
if (this.data.relationships) { if (this.data.relationships) {
const relationship = this.data.relationships[name]; const relationship = this.data.relationships[name];
if (relationship) { if (relationship && !Array.isArray(relationship.data)) {
return app.store.getById(relationship.data.type, relationship.data.id); return app.store.getById(relationship.data.type, relationship.data.id);
} }
} }
@ -275,7 +271,7 @@ export default class Model {
if (this.data.relationships) { if (this.data.relationships) {
const relationship = this.data.relationships[name]; const relationship = this.data.relationships[name];
if (relationship) { if (relationship && Array.isArray(relationship.data)) {
return relationship.data.map(data => app.store.getById(data.type, data.id)); return relationship.data.map(data => app.store.getById(data.type, data.id));
} }
} }

@ -75,7 +75,7 @@ export default class Store {
* @param query * @param query
* @param options * @param options
*/ */
find<T extends Model = Model>(type: string, id?: number | number[] | any, query = {}, options = {}): Promise<T[]> { find<T extends Model = Model>(type: string, id?: number | number[] | any, query = {}, options = {}): Promise<T | T[]> {
let params = query; let params = query;
let url = `${app.forum.attribute('apiUrl')}/${type}`; let url = `${app.forum.attribute('apiUrl')}/${type}`;
@ -87,7 +87,7 @@ export default class Store {
url += `/${id}`; url += `/${id}`;
} }
return <Promise<T[]>>app return <Promise<T | T[]>>app
.request( .request(
Object.assign( Object.assign(
{ {

@ -15,7 +15,7 @@ export default class Translator {
Object.assign(this.translations, translations); Object.assign(this.translations, translations);
} }
trans(id: string, parameters = null) { trans(id: string, parameters?: any): string | any[] {
const translation = this.translations[id]; const translation = this.translations[id];
if (translation) { if (translation) {
@ -25,16 +25,14 @@ export default class Translator {
return id; return id;
} }
transText(id: string, parameters = null) { transText(id: string, parameters?: any): string {
return extractText(this.trans(id, parameters)); return extractText(this.trans(id, parameters));
} }
transChoice(id: string, number: string | number, parameters: any) { transChoice(id: string, number: number, parameters: any): string | any[] {
let translation = this.translations[id]; let translation: string = this.translations[id];
if (translation) { if (translation) {
number = parseInt(number, 10);
translation = this.pluralize(translation, number); translation = this.pluralize(translation, number);
return this.apply(translation, parameters || {}); return this.apply(translation, parameters || {});
@ -77,7 +75,7 @@ export default class Translator {
return hydrated.filter(part => part); return hydrated.filter(part => part);
} }
pluralize(translation: string, number: number) { pluralize(translation: string, number: number): string | undefined {
const sPluralRegex = new RegExp(/^\w+\: +(.+)$/), const sPluralRegex = new RegExp(/^\w+\: +(.+)$/),
cPluralRegex = new RegExp( cPluralRegex = new RegExp(
/^\s*((\{\s*(\-?\d+[\s*,\s*\-?\d+]*)\s*\})|([\[\]])\s*(-Inf|\-?\d+)\s*,\s*(\+?Inf|\-?\d+)\s*([\[\]]))\s?(.+?)$/ /^\s*((\{\s*(\-?\d+[\s*,\s*\-?\d+]*)\s*\})|([\[\]])\s*(-Inf|\-?\d+)\s*,\s*(\+?Inf|\-?\d+)\s*([\[\]]))\s?(.+?)$/
@ -137,7 +135,7 @@ export default class Translator {
return parseInt(number, 10); return parseInt(number, 10);
} }
pluralPosition(number: number, locale: string) { pluralPosition(number: number, locale: string): number {
if ('pt_BR' === locale) { if ('pt_BR' === locale) {
locale = 'xbr'; locale = 'xbr';
} }

@ -11,7 +11,7 @@
* @param element The element to anchor the scroll position to. * @param element The element to anchor the scroll position to.
* @param callback The callback to run that will change page content. * @param callback The callback to run that will change page content.
*/ */
export default function anchorScroll(element: Element, callback: Function) { export default function anchorScroll(element: HTMLElement, callback: Function) {
const $window = $(window); const $window = $(window);
const $el = $(element); const $el = $(element);

@ -3,7 +3,7 @@ import Tooltip from 'tooltip.js';
// add $.fn.tooltip // add $.fn.tooltip
$.fn.tooltip = function(option) { $.fn.tooltip = function(option) {
return this.each(function() { return this.each(function(this: HTMLElement) {
const $this = $(this); const $this = $(this);
let data = $this.data('bs.tooltip'); let data = $this.data('bs.tooltip');
const options = (typeof option === 'object' && option) || {}; const options = (typeof option === 'object' && option) || {};
@ -59,7 +59,7 @@ $.fn.hover = function(hover, leave) {
}; };
// add animated scroll // add animated scroll
$.fn.animatedScrollTop = function(to, duration = $.fx.speeds._default, callback) { $.fn.animateScrollTop = function(to, duration = $.fx.speeds._default, callback) {
if (typeof to === 'number') to -= window.scrollY || window.pageYOffset; if (typeof to === 'number') to -= window.scrollY || window.pageYOffset;
jump(to, { jump(to, {
@ -94,7 +94,6 @@ $.fn.extend = $.extend.bind($);
* Enable special events on Zepto * Enable special events on Zepto
* @license Original Copyright 2013 Enideo. Released under dual MIT and GPL licenses. * @license Original Copyright 2013 Enideo. Released under dual MIT and GPL licenses.
*/ */
// @ts-ignore
$.event.special = $.event.special || {}; $.event.special = $.event.special || {};
const bindBeforeSpecialEvents = $.fn.bind; const bindBeforeSpecialEvents = $.fn.bind;

@ -18,12 +18,12 @@ export default class DiscussionPage extends Page {
/** /**
* The discussion that is being viewed. * The discussion that is being viewed.
*/ */
discussion?: Discussion; discussion: Discussion | null = null;
/** /**
* The number of the first post that is currently visible in the viewport. * The number of the first post that is currently visible in the viewport.
*/ */
near?: number; near: number | null = null;
stream!: PostStream; stream!: PostStream;
scrubber!: PostStreamScrubber; scrubber!: PostStreamScrubber;
@ -155,7 +155,7 @@ export default class DiscussionPage extends Page {
// component for the first time on page load, then any calls to m.redraw // component for the first time on page load, then any calls to m.redraw
// will be ineffective and thus any configs (scroll code) will be run // will be ineffective and thus any configs (scroll code) will be run
// before stuff is drawn to the page. // before stuff is drawn to the page.
setTimeout(this.show.bind(this, preloadedDiscussion), 0); setTimeout(this.show.bind(this, preloadedDiscussion as Discussion), 0);
} else { } else {
const params = this.requestParams(); const params = this.requestParams();
@ -219,14 +219,16 @@ export default class DiscussionPage extends Page {
// When the mouse enters and leaves the discussions pane, we want to show // When the mouse enters and leaves the discussions pane, we want to show
// and hide the pane respectively. We also create a 10px 'hot edge' on the // and hide the pane respectively. We also create a 10px 'hot edge' on the
// left of the screen to activate the pane. // left of the screen to activate the pane.
const pane = app.pane;
$list.hover(pane.show.bind(pane), pane.onmouseleave.bind(pane));
const hotEdge = e => { // TODO pane
if (e.pageX < 10) pane.show(); // const pane = app.pane;
}; // $list.hover(pane.show.bind(pane), pane.onmouseleave.bind(pane));
$(document).on('mousemove', hotEdge); //
vnode.dom.onunload = () => $(document).off('mousemove', hotEdge); // const hotEdge = e => {
// if (e.pageX < 10) pane.show();
// };
// $(document).on('mousemove', hotEdge);
// vnode.dom.onunload = () => $(document).off('mousemove', hotEdge);
// If the discussion we are viewing is listed in the discussion list, then // If the discussion we are viewing is listed in the discussion list, then
// we will make sure it is visible in the viewport – if it is not we will // we will make sure it is visible in the viewport – if it is not we will

@ -13,7 +13,7 @@ export default class PostMeta extends Component<PostProp> {
// When the dropdown menu is shown, select the contents of the permalink // When the dropdown menu is shown, select the contents of the permalink
// input so that the user can quickly copy the URL. // input so that the user can quickly copy the URL.
const selectPermalink = function(this: Element) { const selectPermalink = function(this: HTMLElement) {
setTimeout(() => setTimeout(() =>
$(this) $(this)
.parent() .parent()

@ -470,7 +470,7 @@ class PostStream<T extends PostStreamProps = PostStreamProps> extends Component<
let startNumber; let startNumber;
let endNumber; let endNumber;
this.$('.PostStream-item').each((index, item: Element) => { this.$('.PostStream-item').each((index, item: HTMLElement) => {
const $item = $(item); const $item = $(item);
const top = $item.offset().top; const top = $item.offset().top;
const height = $item.outerHeight(true); const height = $item.outerHeight(true);
@ -557,7 +557,7 @@ class PostStream<T extends PostStreamProps = PostStreamProps> extends Component<
$container.scrollTop(top); $container.scrollTop(top);
resolve(); resolve();
} else if (top !== scrollTop) { } else if (top !== scrollTop) {
$container.animatedScrollTop(top, 'fast', resolve); $container.animateScrollTop(top, 'fast', resolve);
} else { } else {
resolve(); resolve();
} }

@ -5,6 +5,7 @@ import SubtreeRetainer from '../../common/utils/SubtreeRetainer';
import formatNumber from '../../common/utils/formatNumber'; import formatNumber from '../../common/utils/formatNumber';
import PostStream from './PostStream'; import PostStream from './PostStream';
import { EventHandler } from '../../common/utils/Evented'; import { EventHandler } from '../../common/utils/Evented';
/** /**
* The `PostStreamScrubber` component displays a scrubber which can be used to * The `PostStreamScrubber` component displays a scrubber which can be used to
* navigate/scrub through a post stream. * navigate/scrub through a post stream.
@ -176,7 +177,7 @@ export default class PostStreamScrubber extends Component {
* Update the index/visible/description properties according to the window's * Update the index/visible/description properties according to the window's
* current scroll position. * current scroll position.
*/ */
update(scrollTop?: number) { update(scrollTop: number = 0) {
const stream = this.stream; const stream = this.stream;
const marginTop = stream.getMarginTop(); const marginTop = stream.getMarginTop();
@ -195,7 +196,7 @@ export default class PostStreamScrubber extends Component {
// Now loop through each of the items in the discussion. An 'item' is // Now loop through each of the items in the discussion. An 'item' is
// either a single post or a 'gap' of one or more posts that haven't // either a single post or a 'gap' of one or more posts that haven't
// been loaded yet. // been loaded yet.
$items.each(function() { $items.each(function(this: HTMLElement) {
const $this = $(this); const $this = $(this);
const top = $this.offset().top; const top = $this.offset().top;
const height = $this.outerHeight(true); const height = $this.outerHeight(true);
@ -228,6 +229,8 @@ export default class PostStreamScrubber extends Component {
// scrollbar's current period to a formatted version of this time. // scrollbar's current period to a formatted version of this time.
const time = $this.data('time'); const time = $this.data('time');
if (time) period = time; if (time) period = time;
return true;
}); });
this.index = index; this.index = index;
@ -269,7 +272,7 @@ export default class PostStreamScrubber extends Component {
this.$('.Scrubber-handle') this.$('.Scrubber-handle')
.css('cursor', 'move') .css('cursor', 'move')
.on('mousedown touchstart', this.onmousedown.bind(this)) .on('mousedown touchstart', this.onmousedown.bind(this) as ZeptoEventHandler)
// Exempt the scrollbar handle from the 'jump to' click event. // Exempt the scrollbar handle from the 'jump to' click event.
.click(e => e.stopPropagation()); .click(e => e.stopPropagation());
@ -279,7 +282,7 @@ export default class PostStreamScrubber extends Component {
// some event handlers. These handlers will move the scrollbar/stream- // some event handlers. These handlers will move the scrollbar/stream-
// content as appropriate. // content as appropriate.
$(document) $(document)
.on('mousemove touchmove', (this.handlers.onmousemove = this.onmousemove.bind(this))) .on('mousemove touchmove', (this.handlers.onmousemove = this.onmousemove.bind(this) as ZeptoEventHandler))
.on('mouseup touchend', (this.handlers.onmouseup = this.onmouseup.bind(this))); .on('mouseup touchend', (this.handlers.onmouseup = this.onmouseup.bind(this)));
} }
@ -310,7 +313,7 @@ export default class PostStreamScrubber extends Component {
$scrubber.find('.Scrubber-description').text(this.description); $scrubber.find('.Scrubber-description').text(this.description);
$scrubber.toggleClass('disabled', this.disabled()); $scrubber.toggleClass('disabled', this.disabled());
const heights = {}; const heights: { before?: number; handle?: number; after?: number } = {};
heights.before = Math.max(0, percentPerPost.index * Math.min(index, count - visible)); heights.before = Math.max(0, percentPerPost.index * Math.min(index, count - visible));
heights.handle = Math.min(100 - heights.before, percentPerPost.visible * visible); heights.handle = Math.min(100 - heights.before, percentPerPost.visible * visible);
heights.after = 100 - heights.before - heights.handle; heights.after = 100 - heights.before - heights.handle;

@ -32,7 +32,7 @@ export default class Search extends Component {
/** /**
* An array of SearchSources. * An array of SearchSources.
*/ */
sources: SearchSource[] = null; sources?: SearchSource[];
/** /**
* The number of sources that are still loading results. * The number of sources that are still loading results.
@ -50,11 +50,11 @@ export default class Search extends Component {
* around as new results load), but otherwise it will be numeric (the * around as new results load), but otherwise it will be numeric (the
* sequential position within the list). * sequential position within the list).
*/ */
index: string | number = 0; index: number = 0;
navigator: KeyboardNavigatable; navigator: KeyboardNavigatable = new KeyboardNavigatable();
searchTimeout: number; searchTimeout?: number;
view() { view() {
const currentSearch = this.getCurrentSearch(); const currentSearch = this.getCurrentSearch();
@ -124,14 +124,12 @@ export default class Search extends Component {
.on('click', () => this.$('input').blur()) .on('click', () => this.$('input').blur())
// Whenever the mouse is hovered over a search result, highlight it. // Whenever the mouse is hovered over a search result, highlight it.
.on('mouseenter', '> li:not(.Dropdown-header)', function() { .on('mouseenter', '> li:not(.Dropdown-header)', function(this: HTMLElement) {
search.setIndex(search.selectableItems().index(this)); search.setIndex(search.selectableItems().index(this));
}); });
const $input = this.$('input'); const $input = this.$('input');
this.navigator = new KeyboardNavigatable();
this.navigator this.navigator
.onUp(() => this.setIndex(this.getCurrentNumericIndex() - 1, true)) .onUp(() => this.setIndex(this.getCurrentNumericIndex() - 1, true))
.onDown(() => this.setIndex(this.getCurrentNumericIndex() + 1, true)) .onDown(() => this.setIndex(this.getCurrentNumericIndex() + 1, true))
@ -150,7 +148,7 @@ export default class Search extends Component {
search.searchTimeout = setTimeout(() => { search.searchTimeout = setTimeout(() => {
if (search.searched.indexOf(query) !== -1) return; if (search.searched.indexOf(query) !== -1) return;
if (query.length >= 3) { if (query.length >= 3 && search.sources) {
search.sources.map(source => { search.sources.map(source => {
if (!source.search) return; if (!source.search) return;
@ -168,7 +166,7 @@ export default class Search extends Component {
}, 250); }, 250);
}) })
.on('focus', function() { .on('focus', function(this: HTMLElement) {
$(this) $(this)
.one('mouseup', e => e.preventDefault()) .one('mouseup', e => e.preventDefault())
.select(); .select();
@ -177,10 +175,8 @@ export default class Search extends Component {
/** /**
* Get the active search in the app's current controller. * Get the active search in the app's current controller.
*
* @return {String}
*/ */
getCurrentSearch() { getCurrentSearch(): string | false {
return app.current && typeof app.current.searching === 'function' && app.current.searching(); return app.current && typeof app.current.searching === 'function' && app.current.searching();
} }
@ -233,8 +229,6 @@ export default class Search extends Component {
/** /**
* Get all of the search result items that are selectable. * Get all of the search result items that are selectable.
*
* @return {jQuery}
*/ */
selectableItems() { selectableItems() {
return this.$('.Search-results > li:not(.Dropdown-header)'); return this.$('.Search-results > li:not(.Dropdown-header)');
@ -242,20 +236,15 @@ export default class Search extends Component {
/** /**
* Get the position of the currently selected search result item. * Get the position of the currently selected search result item.
*
* @return {Integer}
*/ */
getCurrentNumericIndex() { getCurrentNumericIndex(): number {
return this.selectableItems().index(this.getItem(this.index)); return this.selectableItems().index(this.getItem(this.index));
} }
/** /**
* Get the <li> in the search results with the given index (numeric or named). * Get the <li> in the search results with the given index (numeric or named).
*
* @param {String} index
* @return {DOMElement}
*/ */
getItem(index) { getItem(index: number): ZeptoCollection {
const $items = this.selectableItems(); const $items = this.selectableItems();
let $item = $items.filter(`[data-index="${index}"]`); let $item = $items.filter(`[data-index="${index}"]`);
@ -290,7 +279,7 @@ export default class Search extends Component {
.eq(fixedIndex) .eq(fixedIndex)
.addClass('active'); .addClass('active');
this.index = $item.attr('data-index') || fixedIndex; this.index = Number($item.attr('data-index') || fixedIndex);
if (scrollToItem) { if (scrollToItem) {
const dropdownScroll = $dropdown.scrollTop(); const dropdownScroll = $dropdown.scrollTop();
@ -307,7 +296,7 @@ export default class Search extends Component {
} }
if (typeof scrollTop !== 'undefined') { if (typeof scrollTop !== 'undefined') {
$dropdown.animate({ scrollTop }, 100); $dropdown.animateScrollTop(scrollTop, 100);
} }
} }
} }

@ -1,7 +1,7 @@
export interface StackItem { export interface StackItem {
name: string; name: string;
title: string; title?: string;
url?: string; url: string;
} }
/** /**
@ -38,9 +38,9 @@ export default class History {
/** /**
* Push an item to the top of the stack. * Push an item to the top of the stack.
* *
* @param {String} name The name of the route. * @param name The name of the route.
* @param {String} title The title of the route. * @param title The title of the route.
* @param {String} [url] The URL of the route. The current URL will be used if * @param [url] The URL of the route. The current URL will be used if
* not provided. * not provided.
*/ */
push(name: string, title?: string, url: string = m.route.get()) { push(name: string, title?: string, url: string = m.route.get()) {