2021-03-11 05:43:45 +01:00
import $ from "jquery" ;
2020-08-29 04:08:14 +02:00
import * as google _analytics from "./google-analytics" ;
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 ;
}
function get _path _from _version ( ) {
2020-07-15 01:29:15 +02:00
return "/apps/" + version ;
2017-07-28 04:29:37 +02:00
}
2017-07-08 02:28:18 +02:00
2017-07-28 04:29:37 +02:00
function update _path ( ) {
2019-11-02 00:06:25 +01:00
const next _path = get _path _from _version ( ) ;
2020-07-15 01:29:15 +02:00
history . pushState ( version , "" , next _path ) ;
2017-07-08 02:28:18 +02:00
}
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
} ;
2020-07-15 01:29:15 +02:00
$ ( window ) . on ( "popstate" , ( ) => {
2017-07-28 04:29:37 +02:00
version = get _version _from _path ( ) ;
update _page ( ) ;
2020-07-16 22:40:18 +02:00
$ ( "body" ) . animate ( { scrollTop : 0 } , 200 ) ;
2020-05-08 06:37:58 +02:00
google _analytics . config ( { page _path : window . location . pathname } ) ;
2017-07-28 04:29:37 +02:00
} ) ;
2017-07-08 02:28:18 +02:00
2020-07-20 21:26:58 +02:00
$ ( ".apps a .icon" ) . on ( "click" , ( e ) => {
2020-07-15 00:34:28 +02:00
const next _version = $ ( e . target ) . closest ( "a" ) . attr ( "href" ) . replace ( "/apps/" , "" ) ;
2017-07-28 04:29:37 +02:00
version = next _version ;
2017-07-08 02:28:18 +02:00
2017-07-28 04:29:37 +02:00
update _path ( ) ;
update _page ( ) ;
2020-07-16 22:40:18 +02:00
$ ( "body" ) . animate ( { scrollTop : 0 } , 200 ) ;
2020-05-08 06:37:58 +02:00
google _analytics . config ( { page _path : window . location . pathname } ) ;
2017-07-28 04:29:37 +02:00
return false ;
2017-07-08 02:28:18 +02:00
} ) ;
2017-07-28 04:29:37 +02:00
// init
version = get _version _from _path ( ) ;
2020-07-15 01:29:15 +02:00
history . replaceState ( version , "" , get _path _from _version ( ) ) ;
2017-07-28 04:29:37 +02:00
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" , "" ) ;
} , 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" ) ;
}
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" ) ;
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" ) ;
} ) ;