var tutorial = (function () { var exports = {}; var is_running = false; var event_handlers = {}; var deferred_work = []; // Keep track of where we are for handling resizing. var current_popover_info; // We'll temporarily set stream colors for the streams we use in the demo // tutorial messages. var real_default_color; var tutorial_default_color = '#76ce90'; // Each message object contains the minimal information necessary for it to be // processed by our system for adding messages to your feed. var today = new Date().getTime() / 1000; var fake_messages = [ { id: 1, content: "
We're working on some new screenshots for our landing page!
", is_stream: true, sender_full_name: "Abbie Patel", sender_email: "abbie@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/5a8d2b3836d546d523f460924a8a9973", display_recipient: "engineering", stream: "engineering", subject: "screenshots", timestr: "12:11", timestamp: today, type: "stream" }, { id: 2, content: "Here's the latest version:
for the Windows app -- thoughts? I'm particularly wondering whether people think the screenshot should be from Windows 7 or some other version.", is_stream: true, sender_full_name: "Abbie Patel", sender_email: "abbie@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/5a8d2b3836d546d523f460924a8a9973", display_recipient: "engineering", stream: "engineering", subject: "screenshots", timestr: "12:11", timestamp: today, type: "stream" }, { id: 3, content: "Looks good to me!
", is_stream: true, sender_full_name: "Jeff Arnold", sender_email: "jeff@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/dd0cf69d6d1989aa0b0d8c722f7d5840", display_recipient: "engineering", stream: "engineering", subject: "screenshots", timestr: "12:16", timestamp: today, type: "stream" }, { id: 4, content: "Adam and I just finished a brainstorming session for the next set of integrations we want to support.
Here are our notes. I'll open tickets for the action items.
", is_stream: true, sender_full_name: "Jessica McKellar", sender_email: "jessica@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/2a1ffb4ef0b4a20c04d540a35f430cf6", display_recipient: "engineering", stream: "engineering", subject: "integrations", timestr: "12:25", timestamp: today, type: "stream" }, { id: 5, content: "Yay, Twitter integration.
", is_stream: true, sender_full_name: "Leo Franchi", sender_email: "leo@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/4309007c980e1e8b9a2453488586482a", display_recipient: "engineering", stream: "engineering", subject: "integrations", timestr: "12:25", timestamp: today, type: "stream" }, { id: 6, content: "We need to add support for a few more markdown features before we can do GitHub integration.
", is_stream: true, sender_full_name: "Li Jing", sender_email: "li@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/2950c2c87fe7daaa56fd6a403ecc2ee0", display_recipient: "engineering", stream: "engineering", subject: "integrations", timestr: "12:26", timestamp: today, type: "stream" }, { id: 7, content: "Good point, I'll add that to the ticket.
", is_stream: true, sender_full_name: "Jessica McKellar", sender_email: "jessica@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/2a1ffb4ef0b4a20c04d540a35f430cf6", display_recipient: "engineering", stream: "engineering", subject: "integrations", timestr: "12:26", timestamp: today, type: "stream" }, { id: 8, content: "Reminder: engineering meeting in 1 hour.
", is_stream: true, sender_full_name: "Reminder Bot", sender_email: "reminder-bot@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/78873d7213d102dc36773046560d403a", display_recipient: "engineering", stream: "engineering", subject: "weekly meeting", timestr: "12:30", timestamp: today, type: "stream" }, { id: 9, content: "Quickly brainstorming my remaining TODO list out loud for you all:
", is_stream: true, sender_full_name: "Abbie Patel", sender_email: "abbie@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/5a8d2b3836d546d523f460924a8a9973", display_recipient: "engineering", stream: "engineering", subject: "screenshots", timestr: "12:32", timestamp: today, type: "stream" }, { id: 10, content: "Oops, I actually took care of the text for iOS and forgot to update the ticket -- I'll do that now.
", is_stream: true, sender_full_name: "Jeff Arnold", sender_email: "jeff@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/dd0cf69d6d1989aa0b0d8c722f7d5840", display_recipient: "engineering", stream: "engineering", subject: "screenshots", timestr: "12:16", timestamp: today, type: "stream" }, { id: 12, content: "No problem, less work for me.
", is_stream: true, sender_full_name: "Abbie Patel", sender_email: "abbie@zulip.com", avatar_url: "https://secure.gravatar.com/avatar/5a8d2b3836d546d523f460924a8a9973", display_recipient: "engineering", stream: "engineering", subject: "screenshots", timestr: "12:32", timestamp: today, type: "stream" } ]; function send_delayed_stream_message(stream, topic, content, delay) { var data = {'type': JSON.stringify('stream'), 'recipient': JSON.stringify(stream), 'topic': JSON.stringify(topic), 'content': JSON.stringify(content)}; setTimeout(function () { $.ajax({ dataType: 'json', url: '/json/tutorial_send_message', type: 'POST', data: data }); }, delay * 1000); // delay is in seconds. } function disable_event_handlers() { $('body').css({'overflow':'hidden'}); // prevents scrolling the feed _.each(["keydown", "keyup", "keypress", "scroll"], function (event_name) { var existing_events = $._data(document, "events")[event_name]; if (existing_events === undefined) { existing_events = []; } event_handlers[event_name] = existing_events; $._data(document, "events")[event_name] = []; }); } function enable_event_handlers() { $('body').css({'overflow':'auto'}); // enables scrolling the feed _.each(["keydown", "keyup", "keypress", "scroll"], function (event_name) { $._data(document, "events")[event_name] = event_handlers[event_name]; }); } function set_tutorial_status(status, callback) { return channel.post({ url: '/json/tutorial_status', data: {status: JSON.stringify(status)}, success: callback }); } exports.is_running = function () { return is_running; }; function box(x, y, width, height) { // Blanket everything ouside the box defined by the parameters in a // translucent black screen, and cover the box itself with a clear screen so // nothing in it is clickable. // // x and y are the coordinates for hte upper-left corner of the box. var doc_width = $(document).width(); var doc_height = $(document).height(); $("#top-screen").css({opacity: 0.7, width: doc_width, height: y}); $("#bottom-screen").offset({top: y + height, left: 0}); $("#bottom-screen").css({opacity: 0.7, width: doc_width, height: doc_height}); $("#left-screen").offset({top: y, left: 0}); $("#left-screen").css({opacity: 0.7, width: x, height: height}); $("#right-screen").offset({top: y, left: x + width}); $("#right-screen").css({opacity: 0.7, width: x, height: height}); $("#clear-screen").css({opacity: 0.0, width: doc_width, height: doc_height}); } function message_groups_in_viewport() { var vp = viewport.message_viewport_info(); var top = vp.visible_top; var height = vp.visible_height; var last_group = rows.get_message_recipient_row(rows.last_visible()); return $.merge(last_group, last_group.prevAll()).filter(function (idx, row) { var row_offset = $(row).offset(); return (row_offset.top > top && row_offset.top < top + height); }); } function small_window() { return !$("#left-sidebar").is(":visible"); } function maybe_tweak_placement(placement) { // If viewed on a small screen, move popovers on the left to the center so // they won't be cut off. if (!small_window()) { return placement; } if (placement === "left") { return "bottom"; } if (placement === "bottom") { return "right"; } } function create_and_show_popover(target_div, placement, title, content_template) { $(".popover").remove(); target_div.popover("destroy"); target_div.popover({ placement: placement, title: templates.render("tutorial_title", {title: title, placement: placement}), content: templates.render(content_template, {placement: placement, page_params: page_params}), trigger: "manual" }); target_div.popover("show"); $(".popover").css("z-index", 20001); $(".popover-title").addClass("popover-" + placement); } exports.defer = function (callback) { deferred_work.push(callback); }; function update_popover_info(popover_func) { current_popover_info = popover_func; } function finale(skip) { var finale_modal = $("#tutorial-finale"); if (skip) { finale_modal.modal("hide"); $(".screen").css({opacity: 0.0, width: 0, height: 0}); } else { $(".screen").css({opacity: 0.0}); finale_modal.css("z-index", 20001); finale_modal.modal("show"); $("#tutorial-get-started").click(function () { finale_modal.modal("hide"); $(".screen").css({opacity: 0.0, width: 0, height: 0}); }).focus(); } // Restore your actual stream colors and rerender to display any // messages received during the tutorial. set_tutorial_status("finished"); is_running = false; current_msg_list.clear(); update_popover_info(undefined); // Force a check on new events before we re-render the message list. server_events.force_get_events(); stream_color.default_color = real_default_color; $('#first_run_message').show(); current_msg_list.rerender(); enable_event_handlers(); _.each(deferred_work, function (callback) { callback(); }); deferred_work = []; // We start you in a narrow so it's not overwhelming. if (stream_data.in_home_view(page_params.notifications_stream)) { narrow.activate([{operator: "stream", operand: page_params.notifications_stream}]); } if (page_params.first_in_realm) { // 'engineering' is the best possible stream since we used it in the // tutorial, but fall back to something else if we have to. var work_stream; if (stream_data.in_home_view("engineering")) { work_stream = "engineering"; } else { work_stream = _.find(stream_data.home_view_stream_names(), function (stream_name) { return (stream_name !== "social") && (stream_name !== page_params.notifications_stream); }); } if (stream_data.in_home_view(page_params.notifications_stream)) { send_delayed_stream_message(page_params.notifications_stream, "welcome", "Practice sending sending some messages here, or starting a new topic.", 15); send_delayed_stream_message(page_params.notifications_stream, page_params.product_name + " tips", "Here's a message on a new topic: `" + page_params.product_name + " tips`.\n\nAs you settle into " + page_params.product_name + ", customize your account and notifications on your [Settings page](#settings).", 30); send_delayed_stream_message(page_params.notifications_stream, page_params.product_name + " tips", "You might also enjoy:\n\n* Our lightweight !modal_link(#markdown-help, message formatting) (including emoji! :thumbsup:)\n* !modal_link(#keyboard-shortcuts, Keyboard shortcuts)\n* [Desktop and mobile apps](/apps)", 40); } if (work_stream !== undefined) { send_delayed_stream_message(work_stream, "projects", "This is a message on stream `" + work_stream + "` with the topic `projects`.", 60); send_delayed_stream_message(work_stream, "projects", "Take a peek at our [integrations](/integrations). Now's a great time to set one up!", 65); } if (stream_data.in_home_view("social")) { send_delayed_stream_message("social", "cute animals", "This is a message on stream `social` with the topic `cute animals`. Try uploading or pasting in some pictures. Here's a [guinea pig](/static/images/cute/guinea.jpg) to get you started:", 75); } } } function box_first_message() { var spotlight_message = rows.first_visible(); var bar = rows.get_message_recipient_row(spotlight_message); var header = bar.find('.message_header'); var x = bar.offset().left; var y = bar.offset().top; var message_height = header.height() + spotlight_message.height(); var message_width = bar.width(); box(x, y, message_width, message_height); } function box_messagelist() { var spotlight_message_row = rows.get_message_recipient_row(rows.first_visible()); var x = spotlight_message_row.offset().left; var y = spotlight_message_row.offset().top; var height = 0; _.each(message_groups_in_viewport(), function (row) { height += $(row).height(); }); box(x, y, spotlight_message_row.width(), height); } function reply() { var spotlight_message = rows.get_message_recipient_row(rows.first_visible()); box_messagelist(); create_and_show_popover(spotlight_message, maybe_tweak_placement("left"), "Replying", "tutorial_reply"); var my_popover = $("#tutorial-reply").closest(".popover"); my_popover.offset({left: my_popover.offset().left - 10}); update_popover_info(reply, spotlight_message); $("#tutorial-reply-next").click(function () { spotlight_message.popover("destroy"); finale(false); }).focus(); } function home() { var spotlight_message = rows.get_message_recipient_header(rows.first_visible()); box_messagelist(); create_and_show_popover(spotlight_message, maybe_tweak_placement("left"), "Narrowing", "tutorial_home"); var my_popover = $("#tutorial-home").closest(".popover"); my_popover.offset({left: my_popover.offset().left - 10}); update_popover_info(home, spotlight_message); $("#tutorial-home-next").click(function () { spotlight_message.popover("destroy"); reply(); }).focus(); } function subject() { var spotlight_message = rows.first_visible(); var bar = rows.get_message_recipient_header(spotlight_message); var placement = maybe_tweak_placement("bottom"); box_first_message(); create_and_show_popover(bar, placement, "Topics", "tutorial_subject"); var my_popover = $("#tutorial-subject").closest(".popover"); if (placement === "bottom") { // Wider screen, popover is on bottom. my_popover.offset({left: bar.offset().left + 140 - my_popover.width() / 2}); } else { my_popover.offset({left: bar.offset().left + 194}); } update_popover_info(subject, bar); $("#tutorial-subject-next").click(function () { bar.popover("destroy"); home(); }).focus(); } function stream() { var bar = rows.get_message_recipient_header(rows.first_visible()); var placement = maybe_tweak_placement("bottom"); box_first_message(); create_and_show_popover(bar, placement, "Streams", "tutorial_stream"); var my_popover = $("#tutorial-stream").closest(".popover"); if (placement === "bottom") { // Wider screen, popover is on bottom. my_popover.offset({left: bar.offset().left + 50 - my_popover.width() / 2}); } else { // Smaller screen, popover is to the right of the stream label. my_popover.offset({left: bar.offset().left + 98}); } update_popover_info(stream, bar); $("#tutorial-stream-next").click(function () { bar.popover("destroy"); subject(); }).focus(); } function welcome() { // Grey out everything. $('#top-screen').css({opacity: 0.7, width: $(document).width(), height: $(document).height()}); var spotlight_message = rows.first_visible(); var bar = rows.get_message_recipient_header(spotlight_message); box_first_message(); create_and_show_popover(bar, maybe_tweak_placement("left"), "Welcome to " + page_params.product_name, "tutorial_message"); var my_popover = $("#tutorial-message").closest(".popover"); my_popover.offset({left: my_popover.offset().left - 10}); update_popover_info(welcome, bar); $("#tutorial-message-next").click(function () { bar.popover("destroy"); stream(); }).focus(); $("#tutorial-message-skip").click(function () { bar.popover("destroy"); finale(true); }); } exports.start = function () { if (ui.home_tab_obscured()) { ui.change_tab_to('#home'); } narrow.deactivate(); // Set temporarly colors for the streams used in the tutorial. real_default_color = stream_color.default_color; stream_color.default_color = tutorial_default_color; // Add the fake messages to the feed and get started. current_msg_list.add_and_rerender(fake_messages); disable_event_handlers(); is_running = true; set_tutorial_status("started"); welcome(); }; exports.initialize = function () { if (page_params.needs_tutorial) { exports.start(); } $(window).resize($.debounce(100, function () { if (current_popover_info !== undefined) { current_popover_info(); } })); }; return exports; }()); if (typeof module !== 'undefined') { module.exports = tutorial; }