2021-03-11 05:43:45 +01:00
import $ from "jquery" ;
2020-08-29 04:08:14 +02:00
import { detect _user _os } from "./tabbed-instructions" ;
import render _tabs from "./team" ;
2017-11-17 19:50:55 +01:00
2017-08-01 07:21:44 +02:00
export function path _parts ( ) {
2020-07-15 01:29:15 +02:00
return window . location . pathname . split ( "/" ) . filter ( ( chunk ) => chunk !== "" ) ;
2017-07-18 04:40:31 +02:00
}
2017-07-01 01:29:15 +02:00
2019-11-02 00:06:25 +01:00
const hello _events = function ( ) {
let counter = 0 ;
2020-07-20 21:26:58 +02:00
$ ( window ) . on ( "scroll" , function ( ) {
2017-04-20 20:44:49 +02:00
if ( counter % 2 === 0 ) {
2020-07-15 00:34:28 +02:00
$ ( ".screen.hero-screen .message-feed" ) . css (
"transform" ,
"translateY(-" + $ ( this ) . scrollTop ( ) / 5 + "px)" ,
) ;
2017-04-20 20:44:49 +02:00
}
counter += 1 ;
} ) ;
$ ( ".footer" ) . addClass ( "hello" ) ;
} ;
2019-11-02 00:06:25 +01:00
const apps _events = function ( ) {
const info = {
2017-07-08 02:28:18 +02:00
windows : {
image : "/static/images/landing-page/microsoft.png" ,
alt : "Windows" ,
2020-07-15 00:34:28 +02:00
description :
"Zulip for Windows is even better than Zulip on the web, with a cleaner look, tray integration, native notifications, and support for multiple Zulip accounts." ,
2020-10-22 13:39:55 +02:00
download _link : "/apps/download/windows" ,
2017-09-28 03:37:05 +02:00
show _instructions : true ,
2018-09-15 21:35:25 +02:00
install _guide : "/help/desktop-app-install-guide" ,
2020-07-15 01:29:15 +02:00
app _type : "desktop" ,
2017-07-08 02:28:18 +02:00
} ,
mac : {
image : "/static/images/landing-page/macbook.png" ,
2017-08-26 09:33:47 +02:00
alt : "macOS" ,
2020-07-15 00:34:28 +02:00
description :
"Zulip on macOS is even better than Zulip on the web, with a cleaner look, tray integration, native notifications, and support for multiple Zulip accounts." ,
2020-10-22 13:39:55 +02:00
download _link : "/apps/download/mac" ,
2021-05-06 00:39:52 +02:00
mac _arm64 _link : "/apps/download/mac-arm64" ,
2017-09-28 03:37:05 +02:00
show _instructions : true ,
2018-09-15 21:35:25 +02:00
install _guide : "/help/desktop-app-install-guide" ,
2020-03-02 04:10:02 +01:00
app _type : "desktop" ,
2017-07-08 02:28:18 +02:00
} ,
android : {
image : "/static/images/app-screenshots/zulip-android.png" ,
alt : "Android" ,
description : "Zulip's native Android app makes it easy to keep up while on the go." ,
2019-07-11 20:46:43 +02:00
show _instructions : false ,
2020-10-27 09:34:57 +01:00
play _store _link : "https://play.google.com/store/apps/details?id=com.zulipmobile" ,
download _link :
"https://github.com/zulip/zulip-mobile/releases/latest/download/app-release.apk" ,
2020-03-02 04:10:02 +01:00
app _type : "mobile" ,
2017-07-08 02:28:18 +02:00
} ,
ios : {
image : "/static/images/app-screenshots/zulip-iphone-rough.png" ,
alt : "iOS" ,
description : "Zulip's native iOS app makes it easy to keep up while on the go." ,
2019-07-11 20:46:43 +02:00
show _instructions : false ,
2020-10-27 09:34:57 +01:00
app _store _link : "https://itunes.apple.com/us/app/zulip/id1203036395" ,
2020-03-02 04:10:02 +01:00
app _type : "mobile" ,
2017-07-08 02:28:18 +02:00
} ,
linux : {
image : "/static/images/landing-page/ubuntu.png" ,
alt : "Linux" ,
2020-07-15 00:34:28 +02:00
description :
"Zulip on the Linux desktop is even better than Zulip on the web, with a cleaner look, tray integration, native notifications, and support for multiple Zulip accounts." ,
2020-10-22 13:39:55 +02:00
download _link : "/apps/download/linux" ,
2017-09-28 03:37:05 +02:00
show _instructions : true ,
2018-09-15 21:35:25 +02:00
install _guide : "/help/desktop-app-install-guide" ,
2020-03-02 04:10:02 +01:00
app _type : "desktop" ,
2017-07-08 02:28:18 +02:00
} ,
} ;
2019-11-02 00:06:25 +01:00
let version ;
2017-07-08 02:28:18 +02:00
2017-07-28 04:29:37 +02:00
function get _version _from _path ( ) {
2019-11-02 00:06:25 +01:00
let result ;
const parts = path _parts ( ) ;
2017-07-08 02:28:18 +02:00
2021-01-22 22:29:08 +01:00
for ( const version of Object . keys ( info ) ) {
js: Convert a.indexOf(…) !== -1 to a.includes(…).
Babel polyfills this for us for Internet Explorer.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import K from "ast-types/gen/kinds";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
recast.visit(ast, {
visitBinaryExpression(path) {
const { operator, left, right } = path.node;
if (
n.CallExpression.check(left) &&
n.MemberExpression.check(left.callee) &&
!left.callee.computed &&
n.Identifier.check(left.callee.property) &&
left.callee.property.name === "indexOf" &&
left.arguments.length === 1 &&
checkExpression(left.arguments[0]) &&
((["===", "!==", "==", "!=", ">", "<="].includes(operator) &&
n.UnaryExpression.check(right) &&
right.operator == "-" &&
n.Literal.check(right.argument) &&
right.argument.value === 1) ||
([">=", "<"].includes(operator) &&
n.Literal.check(right) &&
right.value === 0))
) {
const test = b.callExpression(
b.memberExpression(left.callee.object, b.identifier("includes")),
[left.arguments[0]]
);
path.replace(
["!==", "!=", ">", ">="].includes(operator)
? test
: b.unaryExpression("!", test)
);
changed = true;
}
this.traverse(path);
},
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-08 04:55:06 +01:00
if ( parts . includes ( version ) ) {
2017-07-28 04:29:37 +02:00
result = version ;
2017-07-08 02:28:18 +02:00
}
2021-01-22 22:29:08 +01:00
}
2017-07-08 02:28:18 +02:00
2019-01-14 20:13:47 +01:00
result = result || detect _user _os ( ) ;
2017-07-28 04:29:37 +02:00
return result ;
}
2019-11-02 00:06:25 +01:00
const update _page = function ( ) {
const $download _instructions = $ ( ".download-instructions" ) ;
const $third _party _apps = $ ( "#third-party-apps" ) ;
const $download _android _apk = $ ( "#download-android-apk" ) ;
2020-03-02 04:10:02 +01:00
const $download _from _google _play _store = $ ( ".download-from-google-play-store" ) ;
const $download _from _apple _app _store = $ ( ".download-from-apple-app-store" ) ;
2021-05-06 00:39:52 +02:00
const $download _mac _arm64 = $ ( "#download-mac-arm64" ) ;
2020-03-02 04:10:02 +01:00
const $desktop _download _link = $ ( ".desktop-download-link" ) ;
2019-11-02 00:06:25 +01:00
const version _info = info [ version ] ;
2017-09-28 03:37:05 +02:00
2017-07-08 02:28:18 +02:00
$ ( ".info .platform" ) . text ( version _info . alt ) ;
$ ( ".info .description" ) . text ( version _info . description ) ;
2021-05-06 00:30:02 +02:00
$desktop _download _link . attr ( "href" , version _info . download _link ) ;
$download _from _google _play _store . attr ( "href" , version _info . play _store _link ) ;
$download _from _apple _app _store . attr ( "href" , version _info . app _store _link ) ;
$download _android _apk . attr ( "href" , version _info . download _link ) ;
2021-05-06 00:39:52 +02:00
$download _mac _arm64 . attr ( "href" , version _info . mac _arm64 _link ) ;
2017-07-08 02:28:18 +02:00
$ ( ".image img" ) . attr ( "src" , version _info . image ) ;
2017-09-29 20:27:56 +02:00
$download _instructions . find ( "a" ) . attr ( "href" , version _info . install _guide ) ;
2017-09-28 03:37:05 +02:00
2019-03-15 17:59:52 +01:00
$download _instructions . toggle ( version _info . show _instructions ) ;
2020-03-02 04:10:02 +01:00
$third _party _apps . toggle ( version _info . app _type === "desktop" ) ;
$desktop _download _link . toggle ( version _info . app _type === "desktop" ) ;
2019-03-03 16:59:03 +01:00
$download _android _apk . toggle ( version === "android" ) ;
2020-03-02 04:10:02 +01:00
$download _from _google _play _store . toggle ( version === "android" ) ;
$download _from _apple _app _store . toggle ( version === "ios" ) ;
2021-05-06 00:39:52 +02:00
$download _mac _arm64 . toggle ( version === "mac" ) ;
2017-07-08 02:28:18 +02:00
} ;
2017-07-28 04:29:37 +02:00
// init
version = get _version _from _path ( ) ;
update _page ( ) ;
2017-07-08 02:28:18 +02:00
} ;
2019-11-02 00:06:25 +01:00
const events = function ( ) {
2020-06-09 00:58:42 +02:00
// get the location url like `zulip.com/features/`, cut off the trailing
// `/` and then split by `/` to get ["zulip.com", "features"], then
2017-02-28 01:45:25 +01:00
// pop the last element to get the current section (eg. `features`).
2019-11-02 00:06:25 +01:00
const location = window . location . pathname . replace ( /\/#*$/ , "" ) . split ( /\// ) . pop ( ) ;
2017-02-28 01:45:25 +01:00
2021-02-03 23:23:32 +01:00
$ ( ` [data-on-page=' ${ CSS . escape ( location ) } '] ` ) . addClass ( "active" ) ;
2017-02-28 01:45:25 +01:00
2020-07-20 21:26:58 +02:00
$ ( "body" ) . on ( "click" , ( e ) => {
2019-11-02 00:06:25 +01:00
const $e = $ ( e . target ) ;
2017-02-28 01:45:25 +01:00
2017-06-12 22:05:29 +02:00
if ( $e . is ( "nav ul .exit" ) ) {
2021-08-05 22:59:20 +02:00
$ ( "nav ul" ) . css ( "transform" , "translate(-350px, 0)" ) ;
// See https://ishadeed.com/article/layout-flickering/ for
// more context as to why the following timeout is important.
setTimeout ( ( ) => {
$ ( "nav ul" ) . removeClass ( "show" ) ;
$ ( "nav ul" ) . css ( "transform" , "" ) ;
landing-page: Fix mobile scrolling bug when sidebar is toggled.
On mobile, when the sidebar is toggled, the following three issues
are encountered:
- When none of the sidebar menus are expanded, the sidebar has no
scrollbar, which is expected. But if you scroll, the background
content scrolls, which is a bug.
- When some of the sidebar menus are expanded such that the content
overflows and is "scrollable", once you get to the end of the
sidebar content, the background content keeps scrolling in a weird
way.
- If the mobile screen is wide enough, if you scroll the sidebar
content, it scrolls as expected. But if you move the pointer to
the side of the background content that is still visible, you
can scroll the background content even though it should be fixed.
This commit fixes all of the above issues.
2021-08-16 19:29:48 +02:00
$ ( "body" ) . removeClass ( "noscroll" ) ;
2021-08-05 22:59:20 +02:00
} , 500 ) ;
2017-02-28 01:45:25 +01:00
}
2018-01-31 01:40:31 +01:00
if ( $ ( "nav ul.show" ) && ! $e . closest ( "nav ul.show" ) . length && ! $e . is ( "nav ul.show" ) ) {
$ ( "nav ul" ) . removeClass ( "show" ) ;
landing-page: Fix mobile scrolling bug when sidebar is toggled.
On mobile, when the sidebar is toggled, the following three issues
are encountered:
- When none of the sidebar menus are expanded, the sidebar has no
scrollbar, which is expected. But if you scroll, the background
content scrolls, which is a bug.
- When some of the sidebar menus are expanded such that the content
overflows and is "scrollable", once you get to the end of the
sidebar content, the background content keeps scrolling in a weird
way.
- If the mobile screen is wide enough, if you scroll the sidebar
content, it scrolls as expected. But if you move the pointer to
the side of the background content that is still visible, you
can scroll the background content even though it should be fixed.
This commit fixes all of the above issues.
2021-08-16 19:29:48 +02:00
$ ( "body" ) . removeClass ( "noscroll" ) ;
2018-01-31 01:40:31 +01:00
}
2017-02-28 01:45:25 +01:00
} ) ;
2020-07-20 21:26:58 +02:00
$ ( ".hamburger" ) . on ( "click" , ( e ) => {
2017-02-28 01:45:25 +01:00
$ ( "nav ul" ) . addClass ( "show" ) ;
landing-page: Fix mobile scrolling bug when sidebar is toggled.
On mobile, when the sidebar is toggled, the following three issues
are encountered:
- When none of the sidebar menus are expanded, the sidebar has no
scrollbar, which is expected. But if you scroll, the background
content scrolls, which is a bug.
- When some of the sidebar menus are expanded such that the content
overflows and is "scrollable", once you get to the end of the
sidebar content, the background content keeps scrolling in a weird
way.
- If the mobile screen is wide enough, if you scroll the sidebar
content, it scrolls as expected. But if you move the pointer to
the side of the background content that is still visible, you
can scroll the background content even though it should be fixed.
This commit fixes all of the above issues.
2021-08-16 19:29:48 +02:00
$ ( "body" ) . addClass ( "noscroll" ) ;
2018-01-31 01:40:31 +01:00
e . stopPropagation ( ) ;
2017-02-28 01:45:25 +01:00
} ) ;
2017-05-10 19:55:40 +02:00
js: Convert a.indexOf(…) !== -1 to a.includes(…).
Babel polyfills this for us for Internet Explorer.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import K from "ast-types/gen/kinds";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
recast.visit(ast, {
visitBinaryExpression(path) {
const { operator, left, right } = path.node;
if (
n.CallExpression.check(left) &&
n.MemberExpression.check(left.callee) &&
!left.callee.computed &&
n.Identifier.check(left.callee.property) &&
left.callee.property.name === "indexOf" &&
left.arguments.length === 1 &&
checkExpression(left.arguments[0]) &&
((["===", "!==", "==", "!=", ">", "<="].includes(operator) &&
n.UnaryExpression.check(right) &&
right.operator == "-" &&
n.Literal.check(right.argument) &&
right.argument.value === 1) ||
([">=", "<"].includes(operator) &&
n.Literal.check(right) &&
right.value === 0))
) {
const test = b.callExpression(
b.memberExpression(left.callee.object, b.identifier("includes")),
[left.arguments[0]]
);
path.replace(
["!==", "!=", ">", ">="].includes(operator)
? test
: b.unaryExpression("!", test)
);
changed = true;
}
this.traverse(path);
},
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-08 04:55:06 +01:00
if ( path _parts ( ) . includes ( "apps" ) ) {
2017-07-08 02:28:18 +02:00
apps _events ( ) ;
}
2017-02-28 01:45:25 +01:00
2020-07-15 01:29:15 +02:00
if ( path _parts ( ) . includes ( "hello" ) ) {
2017-04-20 20:44:49 +02:00
hello _events ( ) ;
}
2017-02-28 01:45:25 +01:00
} ;
2020-07-22 21:40:28 +02:00
$ ( ( ) => {
2018-05-01 22:54:30 +02:00
// Initiate the bootstrap carousel logic
2020-07-15 01:29:15 +02:00
$ ( ".carousel" ) . carousel ( {
2018-05-01 22:54:30 +02:00
interval : false ,
} ) ;
2018-05-25 15:16:10 +02:00
// Move to the next slide on clicking inside the carousel container
2020-07-20 21:26:58 +02:00
$ ( ".carousel-inner .item-container" ) . on ( "click" , function ( e ) {
2019-11-02 00:06:25 +01:00
const get _tag _name = e . target . tagName . toLowerCase ( ) ;
const is _button = get _tag _name === "button" ;
const is _link = get _tag _name === "a" ;
2020-07-15 00:34:28 +02:00
const is _last _slide = $ ( "#tour-carousel .carousel-inner .item:last-child" ) . hasClass (
"active" ,
) ;
2018-06-04 21:07:09 +02:00
// Do not trigger this event if user clicks on a button, link
// or if it's the last slide
2019-11-02 00:06:25 +01:00
const move _slide _forward = ! is _button && ! is _link && ! is _last _slide ;
2018-06-04 21:07:09 +02:00
if ( move _slide _forward ) {
2020-07-15 01:29:15 +02:00
$ ( this ) . closest ( ".carousel" ) . carousel ( "next" ) ;
2018-05-25 15:16:10 +02:00
}
} ) ;
2020-07-15 01:29:15 +02:00
$ ( ".carousel" ) . on ( "slid" , function ( ) {
2019-11-02 00:06:25 +01:00
const $this = $ ( this ) ;
2020-07-15 01:29:15 +02:00
$this . find ( ".visibility-control" ) . show ( ) ;
if ( $this . find ( ".carousel-inner .item" ) . first ( ) . hasClass ( "active" ) ) {
$this . find ( ".left.visibility-control" ) . hide ( ) ;
} else if ( $this . find ( ".carousel-inner .item" ) . last ( ) . hasClass ( "active" ) ) {
$this . find ( ".right.visibility-control" ) . hide ( ) ;
2018-06-04 10:18:18 +02:00
}
} ) ;
2017-06-21 03:14:26 +02:00
// Set up events / categories / search
2017-02-28 01:45:25 +01:00
events ( ) ;
2020-07-15 01:29:15 +02:00
if ( window . location . pathname === "/team/" ) {
2017-11-17 19:50:55 +01:00
render _tabs ( ) ;
}
2021-07-20 20:33:48 +02:00
// Source: https://stackoverflow.com/questions/819416/adjust-width-and-height-of-iframe-to-fit-with-content-in-it
// Resize tweet to avoid overlapping with image. Since tweet uses an iframe which doesn't adjust with
// screen resize, we need to manually adjust its width.
function resizeIFrameToFitContent ( iFrame ) {
$ ( iFrame ) . width ( "38vw" ) ;
}
window . addEventListener ( "resize" , ( ) => {
const iframes = document . querySelectorAll ( ".twitter-tweet iframe" ) ;
for ( const iframe of iframes ) {
resizeIFrameToFitContent ( iframe ) ;
}
} ) ;
2017-11-17 19:50:55 +01:00
} ) ;
2021-07-22 19:29:55 +02:00
// Scroll to anchor link when clicked. Note that help.js has a similar
// function; this file and help.js are never included on the same
// page.
$ ( document ) . on ( "click" , ".markdown h1, .markdown h2, .markdown h3" , function ( ) {
window . location . hash = $ ( this ) . attr ( "id" ) ;
} ) ;