<commit_before>var ui = (function () {

var exports = {};

var actively_scrolling = false;

exports.have_scrolled_away_from_top = true;

exports.actively_scrolling = function () {
    return actively_scrolling;
};

// What, if anything, obscures the home tab?

exports.replace_emoji_with_text = function (element) {
    element.find(".emoji").replaceWith(function () {
        return $(this).text();
    });
};

exports.set_up_scrollbar = function (element) {
    element.perfectScrollbar({
        suppressScrollX: true,
        useKeyboard: false,
        wheelSpeed: 0.68,
    });
};

exports.update_scrollbar = function (element) {
    element.scrollTop = 0;
    element.perfectScrollbar('update');
};

function update_message_in_all_views(message_id, callback) {
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (list) {
        if (list === undefined) {
            return;
        }
        var row = list.get_row(message_id);
        if (row === undefined) {
            // The row may not exist, e.g. if you do an action on a message in
            // a narrowed view
            return;
        }
        callback(row);
    });
}

exports.show_error_for_unsupported_platform = function () {
    // Check if the user is using old desktop app
    if (window.bridge !== undefined) {
        // We don't internationalize this string because it is long,
        // and few users will have both the old desktop app and an
        // internationalized version of Zulip anyway.
        var error = "Hello! You're using the unsupported old Zulip desktop app," +
            " which is no longer developed. We recommend switching to the new, " +
            "modern desktop app, which you can download at " +
            "zulipchat.com/apps.";

        ui_report.generic_embed_error(error);
    }
};

exports.find_message = function (message_id) {
    // Try to find the message object. It might be in the narrow list
    // (if it was loaded when narrowed), or only in the message_list.all
    // (if received from the server while in a different narrow)
    var message;
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (msg_list) {
        if (msg_list !== undefined && message === undefined) {
            message = msg_list.get(message_id);
        }
    });
    return message;
};

exports.update_starred = function (message_id, starred) {
    // Update the message object pointed to by the various message
    // lists.
    var message = exports.find_message(message_id);

    // If it isn't cached in the browser, no need to do anything
    if (message === undefined) {
        return;
    }

    unread_ops.mark_message_as_read(message);

    message.starred = starred;

    // Avoid a full re-render, but update the star in each message
    // table in which it is visible.
    update_message_in_all_views(message_id, function update_row(row) {
        var elt = row.find(".star");
        if (starred) {
            elt.addClass("icon-vector-star").removeClass("icon-vector-star-empty").removeClass("empty-star");
        } else {
            elt.removeClass("icon-vector-star").addClass("icon-vector-star-empty").addClass("empty-star");
        }
        var title_state = starred ? i18n.t("Unstar") : i18n.t("Star");
        elt.attr("title", i18n.t("__starred_status__ this message", {starred_status: title_state}));
    });
};

exports.show_message_failed = function (message_id, failed_msg) {
    // Failed to send message, so display inline retry/cancel
    update_message_in_all_views(message_id, function update_row(row) {
        var failed_div = row.find('.message_failed');
        failed_div.toggleClass('notvisible', false);
        failed_div.find('.failed_text').attr('title', failed_msg);
    });
};

exports.remove_message = function (message_id) {
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (list) {
        if (list === undefined) {
            return;
        }
        var row = list.get_row(message_id);
        if (row !== undefined) {
            list.remove_and_rerender([{id: message_id}]);
        }
    });
};

exports.show_failed_message_success = function (message_id) {
    // Previously failed message succeeded
    update_message_in_all_views(message_id, function update_row(row) {
        row.find('.message_failed').toggleClass('notvisible', true);
    });
};

function _setup_info_overlay() {
    var info_overlay_toggle = components.toggle({
        name: "info-overlay-toggle",
        selected: 0,
        values: [
            { label: i18n.t("Keyboard shortcuts"), key: "keyboard-shortcuts" },
            { label: i18n.t("Message formatting"), key: "markdown-help" },
            { label: i18n.t("Search operators"), key: "search-operators" },
        ],
        callback: function (name, key) {
            $(".overlay-modal").hide();
            $("#" + key).show();
            $("#" + key).find(".modal-body").focus();
        },
    }).get();

    $(".informational-overlays .overlay-tabs")
        .append($(info_overlay_toggle).addClass("large"));
}

exports.show_info_overlay = function (target) {
    var overlay = $(".informational-overlays");

    if (!overlay.hasClass("show")) {
        overlays.open_overlay({
            name:  'informationalOverlays',
            overlay: overlay,
            on_close: function () {
                hashchange.changehash("");
            },
        });
    }

    if (target) {
        components.toggle.lookup("info-overlay-toggle").goto(target);
    }
};

exports.maybe_show_keyboard_shortcuts = function () {
    if (overlays.is_active()) {
        return;
    }
    if (popovers.any_active()) {
        return;
    }
    ui.show_info_overlay("keyboard-shortcuts");
};

var loading_more_messages_indicator_showing = false;
exports.show_loading_more_messages_indicator = function () {
    if (! loading_more_messages_indicator_showing) {
        loading.make_indicator($('#loading_more_messages_indicator'),
                                    {abs_positioned: true});
        loading_more_messages_indicator_showing = true;
        floating_recipient_bar.hide();
    }
};

exports.hide_loading_more_messages_indicator = function () {
    if (loading_more_messages_indicator_showing) {
        loading.destroy_indicator($("#loading_more_messages_indicator"));
        loading_more_messages_indicator_showing = false;
    }
};

/* EXPERIMENTS */

/* This method allows an advanced user to use the console
 * to switch the application to span full width of the browser.
 */
exports.switchToFullWidth = function () {
    $("#full-width-style").remove();
    $('head').append('');
    return ("Switched to full width");
};

/* END OF EXPERIMENTS */

function scroll_finished() {
    actively_scrolling = false;

    if ($('#home').hasClass('active')) {
        if (!pointer.suppress_scroll_pointer_update) {
            message_viewport.keep_pointer_in_view();
        } else {
            pointer.suppress_scroll_pointer_update = false;
        }
        floating_recipient_bar.update();
        if (message_viewport.scrollTop() === 0 &&
            ui.have_scrolled_away_from_top) {
            ui.have_scrolled_away_from_top = false;
            message_fetch.load_more_messages(current_msg_list);
        } else if (!ui.have_scrolled_away_from_top) {
            ui.have_scrolled_away_from_top = true;
        }
        // When the window scrolls, it may cause some messages to
        // enter the screen and become read.  Calling
        // unread_ops.process_visible will update necessary
        // data structures and DOM elements.
        setTimeout(unread_ops.process_visible, 0);
    }
}

var scroll_timer;
function scroll_finish() {
    actively_scrolling = true;
    clearTimeout(scroll_timer);
    scroll_timer = setTimeout(scroll_finished, 100);
}

// Save the compose content cursor position and restore when we
// shift-tab back in (see hotkey.js).
var saved_compose_cursor = 0;

$(function () {
    message_viewport.message_pane.scroll($.throttle(50, function () {
        unread_ops.process_visible();
        scroll_finish();
    }));

    $('#new_message_content').blur(function () {
        saved_compose_cursor = $(this).caret();
    });

    // on the end of the modified-message fade in, remove the fade-in-message class.
    var animationEnd = "webkitAnimationEnd oanimationend msAnimationEnd animationend";
    $("body").on(animationEnd, ".fade-in-message", function () {
        $(this).removeClass("fade-in-message");
    });
});

exports.restore_compose_cursor = function () {
    $('#new_message_content')
        .focus()
        .caret(saved_compose_cursor);
};

$(function () {
    if (window.bridge !== undefined) {
        // Disable "spellchecking" in our desktop app. The "spellchecking"
        // in our Mac app is actually autocorrect, and frustrates our
        // users.
        $("#new_message_content").attr('spellcheck', 'false');
        // Modify the zephyr mirroring error message in our desktop
        // app, since it doesn't work from the desktop version.
        $("#webathena_login_menu").hide();
        $("#normal-zephyr-mirror-error-text").addClass("notdisplayed");
        $("#desktop-zephyr-mirror-error-text").removeClass("notdisplayed");
    }
});

exports.initialize = function () {
    i18n.ensure_i18n(_setup_info_overlay);
    exports.show_error_for_unsupported_platform();
};

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = ui;
}
<commit_msg>Eliminate have_scrolled_away_from_top boolean.<commit_after>var ui = (function () {

var exports = {};

var actively_scrolling = false;

exports.actively_scrolling = function () {
    return actively_scrolling;
};

// What, if anything, obscures the home tab?

exports.replace_emoji_with_text = function (element) {
    element.find(".emoji").replaceWith(function () {
        return $(this).text();
    });
};

exports.set_up_scrollbar = function (element) {
    element.perfectScrollbar({
        suppressScrollX: true,
        useKeyboard: false,
        wheelSpeed: 0.68,
    });
};

exports.update_scrollbar = function (element) {
    element.scrollTop = 0;
    element.perfectScrollbar('update');
};

function update_message_in_all_views(message_id, callback) {
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (list) {
        if (list === undefined) {
            return;
        }
        var row = list.get_row(message_id);
        if (row === undefined) {
            // The row may not exist, e.g. if you do an action on a message in
            // a narrowed view
            return;
        }
        callback(row);
    });
}

exports.show_error_for_unsupported_platform = function () {
    // Check if the user is using old desktop app
    if (window.bridge !== undefined) {
        // We don't internationalize this string because it is long,
        // and few users will have both the old desktop app and an
        // internationalized version of Zulip anyway.
        var error = "Hello! You're using the unsupported old Zulip desktop app," +
            " which is no longer developed. We recommend switching to the new, " +
            "modern desktop app, which you can download at " +
            "zulipchat.com/apps.";

        ui_report.generic_embed_error(error);
    }
};

exports.find_message = function (message_id) {
    // Try to find the message object. It might be in the narrow list
    // (if it was loaded when narrowed), or only in the message_list.all
    // (if received from the server while in a different narrow)
    var message;
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (msg_list) {
        if (msg_list !== undefined && message === undefined) {
            message = msg_list.get(message_id);
        }
    });
    return message;
};

exports.update_starred = function (message_id, starred) {
    // Update the message object pointed to by the various message
    // lists.
    var message = exports.find_message(message_id);

    // If it isn't cached in the browser, no need to do anything
    if (message === undefined) {
        return;
    }

    unread_ops.mark_message_as_read(message);

    message.starred = starred;

    // Avoid a full re-render, but update the star in each message
    // table in which it is visible.
    update_message_in_all_views(message_id, function update_row(row) {
        var elt = row.find(".star");
        if (starred) {
            elt.addClass("icon-vector-star").removeClass("icon-vector-star-empty").removeClass("empty-star");
        } else {
            elt.removeClass("icon-vector-star").addClass("icon-vector-star-empty").addClass("empty-star");
        }
        var title_state = starred ? i18n.t("Unstar") : i18n.t("Star");
        elt.attr("title", i18n.t("__starred_status__ this message", {starred_status: title_state}));
    });
};

exports.show_message_failed = function (message_id, failed_msg) {
    // Failed to send message, so display inline retry/cancel
    update_message_in_all_views(message_id, function update_row(row) {
        var failed_div = row.find('.message_failed');
        failed_div.toggleClass('notvisible', false);
        failed_div.find('.failed_text').attr('title', failed_msg);
    });
};

exports.remove_message = function (message_id) {
    _.each([message_list.all, home_msg_list, message_list.narrowed], function (list) {
        if (list === undefined) {
            return;
        }
        var row = list.get_row(message_id);
        if (row !== undefined) {
            list.remove_and_rerender([{id: message_id}]);
        }
    });
};

exports.show_failed_message_success = function (message_id) {
    // Previously failed message succeeded
    update_message_in_all_views(message_id, function update_row(row) {
        row.find('.message_failed').toggleClass('notvisible', true);
    });
};

function _setup_info_overlay() {
    var info_overlay_toggle = components.toggle({
        name: "info-overlay-toggle",
        selected: 0,
        values: [
            { label: i18n.t("Keyboard shortcuts"), key: "keyboard-shortcuts" },
            { label: i18n.t("Message formatting"), key: "markdown-help" },
            { label: i18n.t("Search operators"), key: "search-operators" },
        ],
        callback: function (name, key) {
            $(".overlay-modal").hide();
            $("#" + key).show();
            $("#" + key).find(".modal-body").focus();
        },
    }).get();

    $(".informational-overlays .overlay-tabs")
        .append($(info_overlay_toggle).addClass("large"));
}

exports.show_info_overlay = function (target) {
    var overlay = $(".informational-overlays");

    if (!overlay.hasClass("show")) {
        overlays.open_overlay({
            name:  'informationalOverlays',
            overlay: overlay,
            on_close: function () {
                hashchange.changehash("");
            },
        });
    }

    if (target) {
        components.toggle.lookup("info-overlay-toggle").goto(target);
    }
};

exports.maybe_show_keyboard_shortcuts = function () {
    if (overlays.is_active()) {
        return;
    }
    if (popovers.any_active()) {
        return;
    }
    ui.show_info_overlay("keyboard-shortcuts");
};

var loading_more_messages_indicator_showing = false;
exports.show_loading_more_messages_indicator = function () {
    if (! loading_more_messages_indicator_showing) {
        loading.make_indicator($('#loading_more_messages_indicator'),
                                    {abs_positioned: true});
        loading_more_messages_indicator_showing = true;
        floating_recipient_bar.hide();
    }
};

exports.hide_loading_more_messages_indicator = function () {
    if (loading_more_messages_indicator_showing) {
        loading.destroy_indicator($("#loading_more_messages_indicator"));
        loading_more_messages_indicator_showing = false;
    }
};

/* EXPERIMENTS */

/* This method allows an advanced user to use the console
 * to switch the application to span full width of the browser.
 */
exports.switchToFullWidth = function () {
    $("#full-width-style").remove();
    $('head').append('');
    return ("Switched to full width");
};

/* END OF EXPERIMENTS */

function scroll_finished() {
    actively_scrolling = false;

    if ($('#home').hasClass('active')) {
        if (!pointer.suppress_scroll_pointer_update) {
            message_viewport.keep_pointer_in_view();
        } else {
            pointer.suppress_scroll_pointer_update = false;
        }
        floating_recipient_bar.update();
        if (message_viewport.scrollTop() === 0) {
            message_fetch.load_more_messages(current_msg_list);
        }

        // When the window scrolls, it may cause some messages to
        // enter the screen and become read.  Calling
        // unread_ops.process_visible will update necessary
        // data structures and DOM elements.
        setTimeout(unread_ops.process_visible, 0);
    }
}

var scroll_timer;
function scroll_finish() {
    actively_scrolling = true;
    clearTimeout(scroll_timer);
    scroll_timer = setTimeout(scroll_finished, 100);
}

// Save the compose content cursor position and restore when we
// shift-tab back in (see hotkey.js).
var saved_compose_cursor = 0;

$(function () {
    message_viewport.message_pane.scroll($.throttle(50, function () {
        unread_ops.process_visible();
        scroll_finish();
    }));

    $('#new_message_content').blur(function () {
        saved_compose_cursor = $(this).caret();
    });

    // on the end of the modified-message fade in, remove the fade-in-message class.
    var animationEnd = "webkitAnimationEnd oanimationend msAnimationEnd animationend";
    $("body").on(animationEnd, ".fade-in-message", function () {
        $(this).removeClass("fade-in-message");
    });
});

exports.restore_compose_cursor = function () {
    $('#new_message_content')
        .focus()
        .caret(saved_compose_cursor);
};

$(function () {
    if (window.bridge !== undefined) {
        // Disable "spellchecking" in our desktop app. The "spellchecking"
        // in our Mac app is actually autocorrect, and frustrates our
        // users.
        $("#new_message_content").attr('spellcheck', 'false');
        // Modify the zephyr mirroring error message in our desktop
        // app, since it doesn't work from the desktop version.
        $("#webathena_login_menu").hide();
        $("#normal-zephyr-mirror-error-text").addClass("notdisplayed");
        $("#desktop-zephyr-mirror-error-text").removeClass("notdisplayed");
    }
});

exports.initialize = function () {
    i18n.ensure_i18n(_setup_info_overlay);
    exports.show_error_for_unsupported_platform();
};

return exports;
}());

if (typeof module !== 'undefined') {
    module.exports = ui;
}
<|endoftext|>


<commit_before>/**
 * Created by XiYe on 12/8/2016.
 */

const rp = require('./util').rp.defaults({jar: true})
const sleep = require('es6-sleep').promise
const moment = require('moment')

const AuthUtil = require('../thulib/auth')
class ScheduleUtil {
  static async parseSchedule(resp) {
    if (!resp.includes('no_such_method')) {
      throw 'Fail to Crawl Schedule'
    }
    const jsonStr = resp.slice(15, resp.length - 1)
    const rawSchedules = JSON.parse(jsonStr)
    const schedules = []
    rawSchedules.forEach((item) => {
      const parsedItem = {}
      parsedItem['place'] = item['dd']
      parsedItem['type'] = item['fl']
      parsedItem['startTime'] = item['kssj']
      parsedItem['dueTime'] = item['jssj']
      parsedItem['date'] = item['nq']
      parsedItem['content'] = item['nr']
      parsedItem['description'] = item['sm']
      schedules.push(parsedItem)
    })
    return schedules
  }

  static async splitSemesterSchedule(semesterSchedule) {
    const weeks = ScheduleUtil.NUM_OF_WEEK
    let weekEndMoment = ScheduleUtil.UNDERGRADUATE_SEMESTER_START_DATE.add(6, 'days')

    const weekSchedules = []
    for (let i = 0; i < weeks; ++i) {
      const weekEndStr = weekEndMoment.format('YYYY-MM-DD')
      const before = (ele) => {
        return ele['date'] <= weekEndStr
      }
      const after = (ele) => {
        return ele['date'] > weekEndStr
      }
      const beforeIndex = semesterSchedule.findIndex(before)
      const afterIndex= semesterSchedule.findIndex(after)
      if (beforeIndex === -1) { //beforeIndex === -1
        weekSchedules.push([])
      } else if(afterIndex === -1){ //before index != -1
        const weekSchedule = semesterSchedule.slice(0)
        semesterSchedule = []
        weekSchedules.push(weekSchedule)
      } else {
        const weekSchedule = semesterSchedule.slice(0, afterIndex)
        semesterSchedule = semesterSchedule.slice(afterIndex)
        weekSchedules.push(weekSchedule)

      }
      weekEndMoment = weekEndMoment.add(7, 'days')
    }
    return weekSchedules
  }

  static async getSchedule(username, password, isUndergraduate) {
    const prefix = 'http://zhjw.cic.tsinghua.edu.cn/jxmh.do'
    const startDate = ScheduleUtil.UNDERGRADUATE_SEMESTER_START_DATE.format('YYYYMMDD')
    const endDate = ScheduleUtil.UNDERGRADUATE_SEMESTER_END_DATE.format('YYYYMMDD')
    const scheduleUndergraduateArgs =
      `?m=bks_jxrl_all&p_start_date=${startDate}&p_end_date=${endDate}` +
      '\&jsoncallback=no_such_method'
    const scheduleGraduateArgs =
      `?m=yjs_jxrl_all&p_start_date=${startDate}&p_end_date=${endDate}`+
      '\&jsoncallback=no_such_method'
    const ticket = await AuthUtil.getTicket(username, password, 'ALL_ZHJW')
    await sleep(2000)
    const loginUrl =
      `http://zhjw.cic.tsinghua.edu.cn/j_acegi_login.do?ticket=${ticket}`
    const scheduleUrl = prefix +
      (isUndergraduate ? scheduleUndergraduateArgs: scheduleGraduateArgs)
    const cookies = rp.jar()
    const loginOptions = {
      method: 'GET',
      uri: loginUrl,
      jar: cookies
    }

    const scheduleOptions = {
      method: 'GET',
      uri: scheduleUrl,
      jar: cookies,
    }

    try {
      await rp(loginOptions)
      //Wait for ticket take effect
      const resp = await rp(scheduleOptions)
      const semesterSchedule = await ScheduleUtil.parseSchedule(resp)
      const weekSchedules = await ScheduleUtil.splitSemesterSchedule(semesterSchedule)
      return weekSchedules
    } catch (e) {
      throw e
    }
  }
}

ScheduleUtil.UNDERGRADUATE_SEMESTER_START_DATE = moment('2016-09-12')
ScheduleUtil.UNDERGRADUATE_SEMESTER_END_DATE = moment('2017-01-15')
ScheduleUtil.NUM_OF_WEEK = 18

module.exports = ScheduleUtil
<commit_msg>Await ticket in shedule<commit_after>/**
 * Created by XiYe on 12/8/2016.
 */

const rp = require('./util').rp.defaults({jar: true})
const sleep = require('es6-sleep').promise
const moment = require('moment')

const AuthUtil = require('../thulib/auth')
class ScheduleUtil {
  static async parseSchedule(resp) {
    if (!resp.includes('no_such_method')) {
      throw 'Fail to Crawl Schedule'
    }
    const jsonStr = resp.slice(15, resp.length - 1)
    const rawSchedules = JSON.parse(jsonStr)
    const schedules = []
    rawSchedules.forEach((item) => {
      const parsedItem = {}
      parsedItem['place'] = item['dd']
      parsedItem['type'] = item['fl']
      parsedItem['startTime'] = item['kssj']
      parsedItem['dueTime'] = item['jssj']
      parsedItem['date'] = item['nq']
      parsedItem['content'] = item['nr']
      parsedItem['description'] = item['sm']
      schedules.push(parsedItem)
    })
    return schedules
  }

  static async splitSemesterSchedule(semesterSchedule) {
    const weeks = ScheduleUtil.NUM_OF_WEEK
    let weekEndMoment = ScheduleUtil.UNDERGRADUATE_SEMESTER_START_DATE.add(6, 'days')

    const weekSchedules = []
    for (let i = 0; i < weeks; ++i) {
      const weekEndStr = weekEndMoment.format('YYYY-MM-DD')
      const before = (ele) => {
        return ele['date'] <= weekEndStr
      }
      const after = (ele) => {
        return ele['date'] > weekEndStr
      }
      const beforeIndex = semesterSchedule.findIndex(before)
      const afterIndex= semesterSchedule.findIndex(after)
      if (beforeIndex === -1) { //beforeIndex === -1
        weekSchedules.push([])
      } else if(afterIndex === -1){ //before index != -1
        const weekSchedule = semesterSchedule.slice(0)
        semesterSchedule = []
        weekSchedules.push(weekSchedule)
      } else {
        const weekSchedule = semesterSchedule.slice(0, afterIndex)
        semesterSchedule = semesterSchedule.slice(afterIndex)
        weekSchedules.push(weekSchedule)

      }
      weekEndMoment = weekEndMoment.add(7, 'days')
    }
    return weekSchedules
  }

  static async getSchedule(username, password, isUndergraduate) {
    const prefix = 'http://zhjw.cic.tsinghua.edu.cn/jxmh.do'
    const startDate = ScheduleUtil.UNDERGRADUATE_SEMESTER_START_DATE.format('YYYYMMDD')
    const endDate = ScheduleUtil.UNDERGRADUATE_SEMESTER_END_DATE.format('YYYYMMDD')
    const scheduleUndergraduateArgs =
      `?m=bks_jxrl_all&p_start_date=${startDate}&p_end_date=${endDate}` +
      '\&jsoncallback=no_such_method'
    const scheduleGraduateArgs =
      `?m=yjs_jxrl_all&p_start_date=${startDate}&p_end_date=${endDate}`+
      '\&jsoncallback=no_such_method'
    const ticket = await AuthUtil.getTicket(username, password, 'ALL_ZHJW')
    await sleep(2000)
    const loginUrl =
      `http://zhjw.cic.tsinghua.edu.cn/j_acegi_login.do?ticket=${ticket}`
    const scheduleUrl = prefix +
      (isUndergraduate ? scheduleUndergraduateArgs: scheduleGraduateArgs)
    const cookies = rp.jar()
    const loginOptions = {
      method: 'GET',
      uri: loginUrl,
      jar: cookies
    }

    const scheduleOptions = {
      method: 'GET',
      uri: scheduleUrl,
      jar: cookies,
    }

    try {
      await rp(loginOptions)
      //Wait for ticket take effect
      await sleep(2000)
      const resp = await rp(scheduleOptions)
      const semesterSchedule = await ScheduleUtil.parseSchedule(resp)
      const weekSchedules = await ScheduleUtil.splitSemesterSchedule(semesterSchedule)
      return weekSchedules
    } catch (e) {
      throw e
    }
  }
}

ScheduleUtil.UNDERGRADUATE_SEMESTER_START_DATE = moment('2016-09-12')
ScheduleUtil.UNDERGRADUATE_SEMESTER_END_DATE = moment('2017-01-15')
ScheduleUtil.NUM_OF_WEEK = 18

module.exports = ScheduleUtil
<|endoftext|>


<commit_before>define(['knockout',
        'durandal/app'
],
function (ko,
          app
          ) {
    return {
          name : ko.observable('gagan sm ')
          //showName : app.showMessage(name)
        }
});<commit_msg>Removing the unwanted space<commit_after>define(['knockout',
        'durandal/app'
],
function (ko,
          app
          ) {
    return {
          name : ko.observable('gagan sm')
          //showName : app.showMessage(name)
        }
});<|endoftext|>


<commit_before>import React from 'react';
import { Range, Iterable } from 'immutable';

import ordinal from '../../util/ordinal';
import pureRender from '../../util/pureRender';

// TODO: Make this a url param or something
// Currently disabled due to jitter bug with offset bar...
const ENABLE_3D_ACCEL = false;

const WIDTH = 450;
const LANE_WIDTH = 60;
const CENTER_LANE_WIDTH = 90;
const NOTE_HEIGHT = 20;

const colorMap = {
  0: 'orange',
  1: 'blue',
  2: 'orange',
  3: 'green',
  4: 'orange',
  5: 'blue',
  6: 'orange'
};

class Note extends React.Component {
  render() {
    const note = this.props.note;

    const beatOffset = note.beat * this.props.beatSpacing;
    const offset = (this.props.beatSpacing / 24) * note.offset;
    const y = beatOffset + offset - NOTE_HEIGHT / 2;

    const color = colorMap[note.col];

    let x, width;
    if (note.col < 3) {
      x = note.col * LANE_WIDTH;
      width = LANE_WIDTH;
    } else if (note.col === 3) {
      x = note.col * LANE_WIDTH;
      width = CENTER_LANE_WIDTH;
    } else if (note.col > 3) {
      x = CENTER_LANE_WIDTH + (note.col - 1) * LANE_WIDTH;
      width = LANE_WIDTH;
    }

    return (
      <rect
        x={x}
        y={y}
        width={width}
        height={NOTE_HEIGHT}
        fill={color} />
    );
  }
}

pureRender(Note);

Note.propTypes = {
  note: React.PropTypes.object.isRequired,
  beatSpacing: React.PropTypes.number.isRequired
};

class Measure extends React.Component {
  render() {
    const num = this.props.num;

    return (
      <svg y={num * this.props.beatSpacing * 4} style={{overflow: 'visible'}}>
        <line x1="0" x2={WIDTH}
          y1={this.props.beatSpacing * 0} y2={this.props.beatSpacing * 0} stroke="black" />
        <line x1="0" x2={WIDTH}
          y1={this.props.beatSpacing * 1} y2={this.props.beatSpacing * 1} stroke="grey" />
        <line x1="0" x2={WIDTH}
          y1={this.props.beatSpacing * 2} y2={this.props.beatSpacing * 2} stroke="grey" />
        <line x1="0" x2={WIDTH}
          y1={this.props.beatSpacing * 3} y2={this.props.beatSpacing * 3} stroke="grey" />
      </svg>
    );
  }
}

pureRender(Measure);

Measure.propTypes = {
  beatSpacing: React.PropTypes.number.isRequired,
  num: React.PropTypes.number.isRequired
};

/**
 * This piece is kept separate because it only re-renders if the following props change:
 *
 * - Notes
 * - Number of measures in song
 * - Beat spacing
 *
 * This DRASTICALLY improves playback performance, as it keeps per-frame rerendering in its
 * containing <Chart />, which handles the translation that scrolls the chart based on the
 * current offset.
 */
class InnerChart extends React.Component {
  renderMeasures() {
    const range = Range(0, this.props.numMeasures);

    return range.map((num) => {
      return (
        <Measure key={`measure-${num}`} beatSpacing={this.props.beatSpacing} num={num} />
      );
    });
  }

  renderNotes() {
    const notes = this.props.notes;

    return notes.map((note) => {
      const key = `note-${note.beat}-${note.offset}-${note.col}`;

      return (
        <Note key={key} beatSpacing={this.props.beatSpacing} note={note} />
      );
    });
  }

  render() {
    return (
      <g>
        {this.props.showMeasures ? this.renderMeasures() : null}
        {this.renderNotes()}
      </g>
    );
  }
}

pureRender(InnerChart);

InnerChart.propTypes = {
  numMeasures: React.PropTypes.number.isRequired,
  notes: React.PropTypes.instanceOf(Iterable).isRequired,
  beatSpacing: React.PropTypes.number.isRequired
};

/**
 * This component contains the chart and scrolls it based on the editor's offset.
 *
 * It also renders the offset bar, since it constantly moves during playback.
 */
class Chart extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      containerHeight: 0
    };

    this._handleUpdate = this.updateHeight.bind(this);
  }

  componentDidMount() {
    this.updateHeight();

    window.addEventListener('resize', this._handleUpdate);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this._handleUpdate);
  }

  updateHeight() {
    this.setState({
      containerHeight: React.findDOMNode(this).parentElement.offsetHeight
    });
  }

  renderOffsetText(y) {
    const noteName = ordinal((24 / this.props.scrollResolution) * 4);
    const beat = Math.floor(this.props.offset / 24 / 4) + 1;

    const text = `${noteName}\n ${beat}`;

    return (
      <text x={WIDTH + 10} y={-y} transform={`scale(1, -1)`}
        style={{fontFamily: 'Helvetica, sans-serif', fontSize: '16px',
                dominantBaseline: 'central'}}>
        {text}
      </text>
    );
  }

  renderOffsetBar() {
    // TODO: Setting the `y` of this causes repaints, could a CSS translate work instead?
    const y = this.props.offset * (this.props.beatSpacing / 24);

    return (
      <g>
        <rect x="0" y={y - NOTE_HEIGHT / 2} width={WIDTH} height={NOTE_HEIGHT}
          fill="#4A90E2"/>

        {this.props.showOffsetText ? this.renderOffsetText(y) : null}
      </g>
    );
  }

  render() {
    const height = this.props.numMeasures * this.props.beatSpacing * 4;
    const offset = this.props.offset * (this.props.beatSpacing / 24);

    const scrollY = -1 * (height - offset - (this.state.containerHeight * 0.7));

    let transform;
    if (ENABLE_3D_ACCEL) {
      transform = `translate3d(0, ${scrollY + 10}px, 0) scale3d(1, -1, 1)`;
    } else {
      transform = `translate(0, ${scrollY + 10}px) scale(1, -1)`;
    }

    let width;
    if (this.props.showOffsetText) {
      width = WIDTH + 100;
    } else {
      width = WIDTH;
    }

    return (

          <svg width={width} height={height} className="chart">
            {this.renderOffsetBar()}
            <InnerChart
              notes={this.props.notes}
              numMeasures={this.props.numMeasures}
              beatSpacing={this.props.beatSpacing}
              showMeasures={this.props.showMeasures} />
          </svg>

    );
  }
}

Chart.propTypes = {
  numMeasures: React.PropTypes.number.isRequired,
  notes: React.PropTypes.instanceOf(Iterable).isRequired,

  scrollResolution: React.PropTypes.number.isRequired,
  offset: React.PropTypes.number.isRequired,
  beatSpacing: React.PropTypes.number.isRequired
};

export default Chart;
<commit_msg>Increase measure seperator clarity<commit_after>import React from 'react';
import { Range, Iterable } from 'immutable';

import ordinal from '../../util/ordinal';
import pureRender from '../../util/pureRender';

// TODO: Make this a url param or something
// Currently disabled due to jitter bug with offset bar...
const ENABLE_3D_ACCEL = false;

const WIDTH = 450;
const LANE_WIDTH = 60;
const CENTER_LANE_WIDTH = 90;
const NOTE_HEIGHT = 20;

const colorMap = {
  0: 'orange',
  1: 'blue',
  2: 'orange',
  3: 'green',
  4: 'orange',
  5: 'blue',
  6: 'orange'
};

class Note extends React.Component {
  render() {
    const note = this.props.note;

    const beatOffset = note.beat * this.props.beatSpacing;
    const offset = (this.props.beatSpacing / 24) * note.offset;
    const y = beatOffset + offset - NOTE_HEIGHT / 2;

    const color = colorMap[note.col];

    let x, width;
    if (note.col < 3) {
      x = note.col * LANE_WIDTH;
      width = LANE_WIDTH;
    } else if (note.col === 3) {
      x = note.col * LANE_WIDTH;
      width = CENTER_LANE_WIDTH;
    } else if (note.col > 3) {
      x = CENTER_LANE_WIDTH + (note.col - 1) * LANE_WIDTH;
      width = LANE_WIDTH;
    }

    return (
      <rect
        x={x}
        y={y}
        width={width}
        height={NOTE_HEIGHT}
        fill={color} />
    );
  }
}

pureRender(Note);

Note.propTypes = {
  note: React.PropTypes.object.isRequired,
  beatSpacing: React.PropTypes.number.isRequired
};

class Measure extends React.Component {
  render() {
    const num = this.props.num;

    return (
      <svg y={num * this.props.beatSpacing * 4} style={{overflow: 'visible'}}>
        <line x1="0" x2={WIDTH} style={{strokeWidth: 2}}
          y1={this.props.beatSpacing * 0} y2={this.props.beatSpacing * 0} stroke="black" />
        <line x1="0" x2={WIDTH}
          y1={this.props.beatSpacing * 1} y2={this.props.beatSpacing * 1} stroke="grey" />
        <line x1="0" x2={WIDTH}
          y1={this.props.beatSpacing * 2} y2={this.props.beatSpacing * 2} stroke="grey" />
        <line x1="0" x2={WIDTH}
          y1={this.props.beatSpacing * 3} y2={this.props.beatSpacing * 3} stroke="grey" />
      </svg>
    );
  }
}

pureRender(Measure);

Measure.propTypes = {
  beatSpacing: React.PropTypes.number.isRequired,
  num: React.PropTypes.number.isRequired
};

/**
 * This piece is kept separate because it only re-renders if the following props change:
 *
 * - Notes
 * - Number of measures in song
 * - Beat spacing
 *
 * This DRASTICALLY improves playback performance, as it keeps per-frame rerendering in its
 * containing <Chart />, which handles the translation that scrolls the chart based on the
 * current offset.
 */
class InnerChart extends React.Component {
  renderMeasures() {
    const range = Range(0, this.props.numMeasures);

    return range.map((num) => {
      return (
        <Measure key={`measure-${num}`} beatSpacing={this.props.beatSpacing} num={num} />
      );
    });
  }

  renderNotes() {
    const notes = this.props.notes;

    return notes.map((note) => {
      const key = `note-${note.beat}-${note.offset}-${note.col}`;

      return (
        <Note key={key} beatSpacing={this.props.beatSpacing} note={note} />
      );
    });
  }

  render() {
    return (
      <g>
        {this.props.showMeasures ? this.renderMeasures() : null}
        {this.renderNotes()}
      </g>
    );
  }
}

pureRender(InnerChart);

InnerChart.propTypes = {
  numMeasures: React.PropTypes.number.isRequired,
  notes: React.PropTypes.instanceOf(Iterable).isRequired,
  beatSpacing: React.PropTypes.number.isRequired
};

/**
 * This component contains the chart and scrolls it based on the editor's offset.
 *
 * It also renders the offset bar, since it constantly moves during playback.
 */
class Chart extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      containerHeight: 0
    };

    this._handleUpdate = this.updateHeight.bind(this);
  }

  componentDidMount() {
    this.updateHeight();

    window.addEventListener('resize', this._handleUpdate);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this._handleUpdate);
  }

  updateHeight() {
    this.setState({
      containerHeight: React.findDOMNode(this).parentElement.offsetHeight
    });
  }

  renderOffsetText(y) {
    const noteName = ordinal((24 / this.props.scrollResolution) * 4);
    const beat = Math.floor(this.props.offset / 24 / 4) + 1;

    const text = `${noteName}\n ${beat}`;

    return (
      <text x={WIDTH + 10} y={-y} transform={`scale(1, -1)`}
        style={{fontFamily: 'Helvetica, sans-serif', fontSize: '16px',
                dominantBaseline: 'central'}}>
        {text}
      </text>
    );
  }

  renderOffsetBar() {
    // TODO: Setting the `y` of this causes repaints, could a CSS translate work instead?
    const y = this.props.offset * (this.props.beatSpacing / 24);

    return (
      <g>
        <rect x="0" y={y - NOTE_HEIGHT / 2} width={WIDTH} height={NOTE_HEIGHT}
          fill="#4A90E2"/>

        {this.props.showOffsetText ? this.renderOffsetText(y) : null}
      </g>
    );
  }

  render() {
    const height = this.props.numMeasures * this.props.beatSpacing * 4;
    const offset = this.props.offset * (this.props.beatSpacing / 24);

    const scrollY = -1 * (height - offset - (this.state.containerHeight * 0.7));

    let transform;
    if (ENABLE_3D_ACCEL) {
      transform = `translate3d(0, ${scrollY + 10}px, 0) scale3d(1, -1, 1)`;
    } else {
      transform = `translate(0, ${scrollY + 10}px) scale(1, -1)`;
    }

    let width;
    if (this.props.showOffsetText) {
      width = WIDTH + 100;
    } else {
      width = WIDTH;
    }

    return (

          <svg width={width} height={height} className="chart">
            {this.renderOffsetBar()}
            <InnerChart
              notes={this.props.notes}
              numMeasures={this.props.numMeasures}
              beatSpacing={this.props.beatSpacing}
              showMeasures={this.props.showMeasures} />
          </svg>

    );
  }
}

Chart.propTypes = {
  numMeasures: React.PropTypes.number.isRequired,
  notes: React.PropTypes.instanceOf(Iterable).isRequired,

  scrollResolution: React.PropTypes.number.isRequired,
  offset: React.PropTypes.number.isRequired,
  beatSpacing: React.PropTypes.number.isRequired
};

export default Chart;
<|endoftext|>


<commit_before>/**
 * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 */

CKEDITOR.editorConfig = function( config ) {
    // Define changes to default configuration here.
    // For complete reference see:
    // http://docs.ckeditor.com/#!/api/CKEDITOR.config

    config.skin = 'office2013';

    // The toolbar groups arrangement, optimized for a single toolbar row.
    config.toolbarGroups = [
        { name: 'editing',     groups: [ 'find', 'selection', 'spellchecker' ] },
        { name: 'forms' },
        { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
        { name: 'paragraph',   groups: [ 'list', 'indent', 'blocks', 'align' ] },
        { name: 'links' },
        { name: 'insert' },
        { name: 'styles' },
        { name: 'colors' },
        { name: 'tools' },
        { name: 'others' },
        { name: 'document',	   groups: [ 'mode', 'document', 'doctools' ] }
    ];

    config.height = 120;

    // The default plugins included in the basic setup define some buttons that
    // are not needed in a basic editor. They are removed here.
    config.removeButtons = 'Cut,Copy,Paste,Undo,Redo,Anchor,Underline,Strike,Subscript,Superscript';

    config.plugins = 'basicstyles,clipboard,floatingspace,list,indentlist,enterkey,entities,link,toolbar,undo,wysiwygarea,blockquote,sourcearea';

    // Dialog windows are also simplified.
    config.removeDialogTabs = 'link:advanced';
};<commit_msg>Remove outdent and indent in small editor<commit_after>/**
 * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 */

CKEDITOR.editorConfig = function( config ) {
    // Define changes to default configuration here.
    // For complete reference see:
    // http://docs.ckeditor.com/#!/api/CKEDITOR.config

    config.skin = 'office2013';

    // The toolbar groups arrangement, optimized for a single toolbar row.
    config.toolbarGroups = [
        { name: 'editing',     groups: [ 'find', 'selection', 'spellchecker' ] },
        { name: 'forms' },
        { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
        { name: 'paragraph',   groups: [ 'list', 'indent', 'blocks', 'align' ] },
        { name: 'links' },
        { name: 'insert' },
        { name: 'styles' },
        { name: 'colors' },
        { name: 'tools' },
        { name: 'others' },
        { name: 'document',	   groups: [ 'mode', 'document', 'doctools' ] }
    ];

    config.height = 120;

    // The default plugins included in the basic setup define some buttons that
    // are not needed in a basic editor. They are removed here.
    config.removeButtons = 'Cut,Copy,Paste,Undo,Redo,Anchor,Underline,Strike,Subscript,Superscript,Indent,Outdent';

    config.plugins = 'basicstyles,clipboard,floatingspace,list,indentlist,enterkey,entities,link,toolbar,undo,wysiwygarea,blockquote,sourcearea';

    // Dialog windows are also simplified.
    config.removeDialogTabs = 'link:advanced';
};<|endoftext|>


<commit_before>// example get request: http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=michael%20jackson&song=bad

var RapGenius = function() {

}

var artistsSongs = {
	'kanye west': ['stronger', 'jesus walks', 'through the wire', 'gold digger'],
	'eminem': ['lose yourself', 'not afraid', 'the real slim shady', 'rap god'],
	'rick ross': ['box chevy', '911', 'i\'m not a star', 'hold me back'],
	'schoolboy q': ['studio', 'collard greens', 'man of the year', 'hell of a night'],
	'tupac': ['life goes on', 'thugz mansion', 'hit em up', 'dear mama'],
	'snoop dogg': ['drop it like it\'s hot', 'i wanna rock', 'young, wild & free', 'doggy dogg world'];
};

RapGenius.prototype.getRandomSong = function(artist) {
	var songs = artistsSongs[artist];
	var randIndex = Math.floor(Math.random() * songs.length);

	return songs[randIndex];
}

RapGenius.prototype.getLyric = function(artist) {
	var baseURL = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect";

	var requestObj = {};
	requestObj.artist = artist;
	requestObj.song = this.getRandomSong(artist);

	var randomLyric = "";

	$.get(baseURL, requestObj).done(function(data){
		var lyrics = data.Lyric.split('\n');
		var randIndex = Math.floor(Math.random() * lyrics.length);
		randomLyric = lyrics[randIndex];
	});

	return randomLyric;
}<commit_msg>fixed syntax error for rappers hash table<commit_after>// example get request: http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=michael%20jackson&song=bad

var RapGenius = function() {

}

var artistsSongs = {
	'kanye west': ['stronger', 'jesus walks', 'through the wire', 'gold digger'],
	'eminem': ['lose yourself', 'not afraid', 'the real slim shady', 'rap god'],
	'rick ross': ['box chevy', '911', 'i\'m not a star', 'hold me back'],
	'schoolboy q': ['studio', 'collard greens', 'man of the year', 'hell of a night'],
	'tupac': ['life goes on', 'thugz mansion', 'hit em up', 'dear mama'],
	'snoop dogg': ['drop it like it\'s hot', 'i wanna rock', 'young, wild & free', 'doggy dogg world']
};

RapGenius.prototype.getRandomSong = function(artist) {
	var songs = artistsSongs[artist];
	var randIndex = Math.floor(Math.random() * songs.length);

	return songs[randIndex];
}

RapGenius.prototype.getLyric = function(artist) {
	var baseURL = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect";

	var requestObj = {};
	requestObj.artist = artist;
	requestObj.song = this.getRandomSong(artist);

	var randomLyric = "";

	$.get(baseURL, requestObj).done(function(data){
		var lyrics = data.Lyric.split('\n');
		var randIndex = Math.floor(Math.random() * lyrics.length);
		randomLyric = lyrics[randIndex];
	});

	return randomLyric;
}<|endoftext|>


<commit_before>$.fn.romoAjax = function() {
  return $.map(this, function(element) {
    return new RomoAjax(element);
  });
}

var RomoAjax = function(element) {
  this.elem       = $(element);
  this.targetElem = $(this.elem.data('romo-ajax-target'));

  this.defaultInvokeOn = 'click';
  this.invokeOn        = this.elem.data('romo-ajax-invoke-on');
  if (this.invokeOn === undefined && this.elem.data('romo-ajax-disable-default-invoke-on') !== true) {
    this.invokeOn = this.defaultInvokeOn;
  }

  this.callMethod   = this.elem.data('romo-ajax-call-method') || 'GET';
  this.urlAttr      = this.elem.data('romo-ajax-url-attr')    || 'href';
  this.callOnlyOnce = this.elem.data('romo-ajax-call-once') === true;

  this.invokeQueued  = false;
  this.invokeRunning = false;

  if (this.invokeOn !== undefined) {
    this.elem.unbind(this.invokeOn);
  }

  this.doInit();
  this.doBindElem();
  this._trigger('romoAjax:ready', [this]);
}

RomoAjax.prototype.doInit = function() {
  // override as needed
}

RomoAjax.prototype.doBindElem = function() {
  this.doUnbindElem();
  if (this.invokeOn !== undefined) {
    this.elem.on(this.invokeOn, $.proxy(this.onInvoke, this));
  }
  this.elem.on('romoAjax:triggerInvoke', $.proxy(this.onInvoke, this));
}

RomoAjax.prototype.doUnbindElem = function() {
  if (this.invokeOn !== undefined) {
    this.elem.off(this.invokeOn, $.proxy(this.onInvoke, this));
  }
  this.elem.off('romoAjax:triggerInvoke', $.proxy(this.onInvoke, this));
}

RomoAjax.prototype.onInvoke = function(e) {
  if (e !== undefined) {
    e.preventDefault();
  }

  if (this.elem.hasClass('disabled') === false) {
    this.doInvoke();
  }
}

RomoAjax.prototype.doInvoke = function() {
  this.invokeQueued = true;
  if (this.invokeRunning === false) {
    this._doInvoke();
  }
}

RomoAjax.prototype.doCall = function(callUrl) {
  this._trigger('romoAjax:callStart', [this]);

  $.ajax({
    type:    this.callMethod,
    url:     callUrl,
    success: $.proxy(this.onCallSuccess, this),
    error:   $.proxy(this.onCallError,   this)
  });
}

RomoAjax.prototype.onCallSuccess = function(data, status, xhr) {
  this._trigger('romoAjax:callSuccess', [data, this]);
  this._doCompleteInvoke();
}

RomoAjax.prototype.onCallError = function(xhr, errorType, error) {
  this._trigger('romoAjax:callError', [xhr, this]);
  this._doCompleteInvoke();
}

// private

RomoAjax.prototype._doCompleteInvoke = function() {
  this._trigger('romoAjax:invoke', [this]);
  if (this.invokeQueued === true) {
    this._doInvoke();
  } else {
    this.invokeRunning = false;
  }
}

RomoAjax.prototype._doInvoke = function() {
  this.invokeQueued  = false;
  this.invokeRunning = true;
  var callUrl = this.elem.attr(this.urlAttr);
  if (this.callOnlyOnce === true) {
    this.elem.removeAttr(this.urlAttr);
  }
  if (callUrl !== undefined) {
    this.doCall(callUrl);
  } else {
    this._doCompleteInvoke();
  }
}

RomoAjax.prototype._trigger = function(event_name, event_data) {
  if (this.targetElem[0] !== undefined) {
    this.targetElem.trigger(event_name, event_data);
  } else {
    this.elem.trigger(event_name, event_data);
  }
}

Romo.onInitUI(function(e) {
  Romo.initUIElems(e, '[data-romo-ajax-auto="true"]').romoAjax();
});
<commit_msg>invoke RomoAjax with custom data<commit_after>$.fn.romoAjax = function() {
  return $.map(this, function(element) {
    return new RomoAjax(element);
  });
}

var RomoAjax = function(element) {
  this.elem       = $(element);
  this.targetElem = $(this.elem.data('romo-ajax-target'));

  this.defaultInvokeOn = 'click';
  this.invokeOn        = this.elem.data('romo-ajax-invoke-on');
  if (this.invokeOn === undefined && this.elem.data('romo-ajax-disable-default-invoke-on') !== true) {
    this.invokeOn = this.defaultInvokeOn;
  }

  this.callMethod   = this.elem.data('romo-ajax-call-method') || 'GET';
  this.urlAttr      = this.elem.data('romo-ajax-url-attr')    || 'href';
  this.callOnlyOnce = this.elem.data('romo-ajax-call-once') === true;

  this.invokeQueued  = false;
  this.invokeRunning = false;

  this.queuedInvokeData = undefined;

  if (this.invokeOn !== undefined) {
    this.elem.unbind(this.invokeOn);
  }

  this.doInit();
  this.doBindElem();
  this._trigger('romoAjax:ready', [this]);
}

RomoAjax.prototype.doInit = function() {
  // override as needed
}

RomoAjax.prototype.doBindElem = function() {
  this.doUnbindElem();
  if (this.invokeOn !== undefined) {
    this.elem.on(this.invokeOn, $.proxy(this.onInvoke, this));
  }
  this.elem.on('romoAjax:triggerInvoke', $.proxy(this.onInvoke, this));
}

RomoAjax.prototype.doUnbindElem = function() {
  if (this.invokeOn !== undefined) {
    this.elem.off(this.invokeOn, $.proxy(this.onInvoke, this));
  }
  this.elem.off('romoAjax:triggerInvoke', $.proxy(this.onInvoke, this));
}

RomoAjax.prototype.onInvoke = function(e, data) {
  if (e !== undefined) {
    e.preventDefault();
  }

  if (this.elem.hasClass('disabled') === false) {
    this.doInvoke(data);
  }
}

RomoAjax.prototype.doInvoke = function(data) {
  this.invokeQueued     = true;
  this.queuedInvokeData = data;
  if (this.invokeRunning === false) {
    this._doInvoke();
  }
}

RomoAjax.prototype.doCall = function(callUrl, data) {
  this._trigger('romoAjax:callStart', [this]);

  $.ajax({
    type:    this.callMethod,
    url:     callUrl,
    data:    (data || {}),
    success: $.proxy(this.onCallSuccess, this),
    error:   $.proxy(this.onCallError,   this)
  });
}

RomoAjax.prototype.onCallSuccess = function(data, status, xhr) {
  this._trigger('romoAjax:callSuccess', [data, this]);
  this._doCompleteInvoke();
}

RomoAjax.prototype.onCallError = function(xhr, errorType, error) {
  this._trigger('romoAjax:callError', [xhr, this]);
  this._doCompleteInvoke();
}

// private

RomoAjax.prototype._doCompleteInvoke = function() {
  this._trigger('romoAjax:invoke', [this]);
  if (this.invokeQueued === true) {
    this._doInvoke();
  } else {
    this.invokeRunning = false;
  }
}

RomoAjax.prototype._doInvoke = function() {
  this.invokeQueued  = false;
  this.invokeRunning = true;

  var data = this.queuedInvokeData;
  this.queuedInvokeData = undefined;

  var callUrl = this.elem.attr(this.urlAttr);
  if (this.callOnlyOnce === true) {
    this.elem.removeAttr(this.urlAttr);
  }
  if (callUrl !== undefined) {
    this.doCall(callUrl, data);
  } else {
    this._doCompleteInvoke();
  }
}

RomoAjax.prototype._trigger = function(event_name, event_data) {
  if (this.targetElem[0] !== undefined) {
    this.targetElem.trigger(event_name, event_data);
  } else {
    this.elem.trigger(event_name, event_data);
  }
}

Romo.onInitUI(function(e) {
  Romo.initUIElems(e, '[data-romo-ajax-auto="true"]').romoAjax();
});
<|endoftext|>


<commit_before>---
layout: null
---

(function ($) {
  var $comments = $('.js-comments');

  $('#new_comment').submit(function () {
    var form = this;

    $(form).addClass('disabled');

    {% assign sm = site.staticman -%}
    var endpoint = '{{ sm.endpoint }}';
    var repository = '{{ sm.repository }}';
    var branch = '{{ sm.branch }}';
    let url = endpoint + repository + '/' + branch + '/comments';
    let data = $(this).serialize();

    var xhr = new XMLHttpRequest();
    xhr.open("POST", url);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onreadystatechange = function () {
      if(xhr.readyState === XMLHttpRequest.DONE) {
        var status = xhr.status;
        if (status >= 200 && status < 400) {
          formSubmitted();
        } else {
          formError();
        }
      }
    };

    function formSubmitted() {
      $('#comment-form-submit').addClass('d-none');
      $('#comment-form-submitted').removeClass('d-none');
      $('.page__comments-form .js-notice').removeClass('alert-danger');
      $('.page__comments-form .js-notice').addClass('alert-success');
      showAlert('success');
    }

    function formError() {
      $('#comment-form-submitted').addClass('d-none');
      $('#comment-form-submit').removeClass('d-none');
      $('.page__comments-form .js-notice').removeClass('alert-success');
      $('.page__comments-form .js-notice').addClass('alert-danger');
      showAlert('failure');
      $(form).removeClass('disabled');
    }

    xhr.send(data);

    return false;
  });

  function showAlert(message) {
    $('.page__comments-form .js-notice').removeClass('d-none');
    if (message == 'success') {
      $('.page__comments-form .js-notice-text-success').removeClass('d-none');
      $('.page__comments-form .js-notice-text-failure').addClass('d-none');
    } else {
      $('.page__comments-form .js-notice-text-success').addClass('d-none');
      $('.page__comments-form .js-notice-text-failure').removeClass('d-none');
    }
  }
})(jQuery);
<commit_msg>replaced var with const (#1049)<commit_after>---
layout: null
---

(function ($) {
  $('#new_comment').submit(function () {
    const form = this;

    $(form).addClass('disabled');

    {% assign sm = site.staticman -%}
    const endpoint = '{{ sm.endpoint }}';
    const repository = '{{ sm.repository }}';
    const branch = '{{ sm.branch }}';
    const url = endpoint + repository + '/' + branch + '/comments';
    const data = $(this).serialize();

    const xhr = new XMLHttpRequest();
    xhr.open("POST", url);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onreadystatechange = function () {
      if(xhr.readyState === XMLHttpRequest.DONE) {
        const status = xhr.status;
        if (status >= 200 && status < 400) {
          formSubmitted();
        } else {
          formError();
        }
      }
    };

    function formSubmitted() {
      $('#comment-form-submit').addClass('d-none');
      $('#comment-form-submitted').removeClass('d-none');
      $('.page__comments-form .js-notice').removeClass('alert-danger');
      $('.page__comments-form .js-notice').addClass('alert-success');
      showAlert('success');
    }

    function formError() {
      $('#comment-form-submitted').addClass('d-none');
      $('#comment-form-submit').removeClass('d-none');
      $('.page__comments-form .js-notice').removeClass('alert-success');
      $('.page__comments-form .js-notice').addClass('alert-danger');
      showAlert('failure');
      $(form).removeClass('disabled');
    }

    xhr.send(data);

    return false;
  });

  function showAlert(message) {
    $('.page__comments-form .js-notice').removeClass('d-none');
    if (message == 'success') {
      $('.page__comments-form .js-notice-text-success').removeClass('d-none');
      $('.page__comments-form .js-notice-text-failure').addClass('d-none');
    } else {
      $('.page__comments-form .js-notice-text-success').addClass('d-none');
      $('.page__comments-form .js-notice-text-failure').removeClass('d-none');
    }
  }
})(jQuery);
<|endoftext|>


<commit_before>/**
 * Created by petervandam on 26/10/2016.
 */
$( document ).ready(function() {

    $('.createButton').click(function() {
        $('#createItem').modal({fadeDuration:100});
    });

    $('.submitFeedItem').click(function() {
        $.post('/add-item/', $('#createItem').serialize())
            .done(function(data) {
                if (data === 'Done') {
                    $.modal.close();
                    requestNewFeedItems();
                }
                else {
                    alert(data);
                }
            })
            .fail(function(data) {
                alert(data);
            })
    });

    $('.weatherContent').load('/weather/detail/');

    $('.scroll').jscroll({
        padding: 150,
        nextSelector: 'a.jscroll-next:last',
        contentSelector: '.listItem',
        callback: function() {
            addListEventHandlers('list');
            if (typeof addMobileListeners == 'function') {
                addMobileListeners();
            }
        }
    });

   addListEventHandlers('list');

});

function addListEventHandlers(container) {
    $('.' + container + ' .listItem').click(function() {
        $('.listItem').removeClass('selected');
        $(this).addClass('selected');
        $(this).addClass('used');

        var url = $(this).data('url');
        if (url == '') { url = 'nourl.php'; }

        $('iframe').attr('src', url);
        $('.pageLinkToUrl').text(url);
        $('.pageLinkToUrl').attr('href', url);
    });

    $('.' + container + ' .title a').click(function() {
        $('iframe').attr('src', '/welcome/');
        $('.pageLinkToUrl').text('');
        requestNewFeedItems();
    });

    $('.' + container + ' .pin').click(function(e) {
        e.stopImmediatePropagation();
        var pin = $(this);
        $.ajax("/pin/" +$(this).data('pin-id'))
            .done(function(response) {
                if (response == '1') {
                    $(pin).parent().toggleClass('pinned');
                }
            });
    });
}

var refreshDate;
function setRefreshDate(refreshDateValue) {
    refreshDate = refreshDateValue;
}

function getRefreshDate() {
    return refreshDate;
}

function requestNewFeedItems() {
    $.getJSON('/refresh/' + getRefreshDate())
        .done(function(data) {
            var html = data.html.replace('
', '
');
            $('.list').prepend(html);
            addListEventHandlers('Newlist');
            setRefreshDate(data.refreshDate);
            clearCreateForm();
    });
}

function clearCreateForm() {
    $('#createItem').find("input[type=text], textarea").val("");
}
<commit_msg>Added infinite scroll fix<commit_after>/**
 * Created by petervandam on 26/10/2016.
 */
$( document ).ready(function() {

    $('.createButton').click(function() {
        $('#createItem').modal({fadeDuration:100});
    });

    $('.submitFeedItem').click(function() {
        $.post('/add-item/', $('#createItem').serialize())
            .done(function(data) {
                if (data === 'Done') {
                    $.modal.close();
                    requestNewFeedItems();
                }
                else {
                    alert(data);
                }
            })
            .fail(function(data) {
                alert(data);
            })
    });

    $('.weatherContent').load('/weather/detail/');

    $('.scroll').jscroll({
        padding: 150,
        nextSelector: 'a.jscroll-next:last',
        contentSelector: '.listItem',
        callback: function() {
            addListEventHandlers('list');
            if (typeof addMobileListeners == 'function') {
                /** global: addMobileListeners */
                addMobileListeners();
            }
        }
    });

   addListEventHandlers('list');

});

function addListEventHandlers(container) {
    $('.' + container + ' .listItem').click(function() {
        $('.listItem').removeClass('selected');
        $(this).addClass('selected');
        $(this).addClass('used');

        var url = $(this).data('url');
        if (url == '') { url = 'nourl.php'; }

        $('iframe').attr('src', url);
        $('.pageLinkToUrl').text(url);
        $('.pageLinkToUrl').attr('href', url);
    });

    $('.' + container + ' .title a').click(function() {
        $('iframe').attr('src', '/welcome/');
        $('.pageLinkToUrl').text('');
        requestNewFeedItems();
    });

    $('.' + container + ' .pin').click(function(e) {
        e.stopImmediatePropagation();
        var pin = $(this);
        $.ajax("/pin/" +$(this).data('pin-id'))
            .done(function(response) {
                if (response == '1') {
                    $(pin).parent().toggleClass('pinned');
                }
            });
    });
}

var refreshDate;
function setRefreshDate(refreshDateValue) {
    refreshDate = refreshDateValue;
}

function getRefreshDate() {
    return refreshDate;
}

function requestNewFeedItems() {
    $.getJSON('/refresh/' + getRefreshDate())
        .done(function(data) {
            var html = data.html.replace('
', '
');
            $('.list').prepend(html);
            addListEventHandlers('Newlist');
            setRefreshDate(data.refreshDate);
            clearCreateForm();
    });
}

function clearCreateForm() {
    $('#createItem').find("input[type=text], textarea").val("");
}
<|endoftext|>


<commit_before>/* ========================================================================
 * DOM-based Routing
 * Based on http://goo.gl/EUTi53 by Paul Irish
 *
 * Only fires on body classes that match. If a body class contains a dash,
 * replace the dash with an underscore when adding it to the object below.
 *
 * .noConflict()
 * The routing is enclosed within an anonymous function so that you can
 * always reference jQuery with $, even when in .noConflict() mode.
 * ======================================================================== */

(function($) {

  // Use this variable to set up the common and page specific functions. If you
  // rename this variable, you will also need to rename the namespace below.
  var Sage = {
    // All pages
    'common': {
      init: function() {
        // JavaScript to be fired on all pages
      },
      finalize: function() {
        // JavaScript to be fired on all pages, after page specific JS is fired
      }
    },
    // Home page
    'home': {
      init: function() {
        // JavaScript to be fired on the home page
      },
      finalize: function() {
        // JavaScript to be fired on the home page, after the init JS
      }
    },
    // About us page, note the change from about-us to about_us.
    'about_us': {
      init: function() {
        // JavaScript to be fired on the about us page
      }
    }
  };

  // The routing fires all common scripts, followed by the page specific scripts.
  // Add additional events for more control over timing e.g. a finalize event
  var UTIL = {
    fire: function(func, funcname, args) {
      var fire;
      var namespace = Sage;
      funcname = (funcname === undefined) ? 'init' : funcname;
      fire = func !== '';
      fire = fire && namespace[func];
      fire = fire && typeof namespace[func][funcname] === 'function';

      if (fire) {
        namespace[func][funcname](args);
      }
    },
    loadEvents: function() {
      // Fire common init JS
      UTIL.fire('common');

      // Fire page-specific init JS, and then finalize JS
      $.each(document.body.className.replace(/-/g, '_').split(/\s+/), function(i, classnm) {
        UTIL.fire(classnm);
        UTIL.fire(classnm, 'finalize');
      });

      // Fire common finalize JS
      UTIL.fire('common', 'finalize');
    }
  };

  // Load Events
  $(document).ready(UTIL.loadEvents);

})(jQuery); // Fully reference jQuery after this point.
<commit_msg>main.js formatting.<commit_after>/* ========================================================================
 * DOM-based Routing
 * Based on http://goo.gl/EUTi53 by Paul Irish
 *
 * Only fires on body classes that match. If a body class contains a dash,
 * replace the dash with an underscore when adding it to the object below.
 *
 * .noConflict()
 * The routing is enclosed within an anonymous function so that you can
 * always reference jQuery with $, even when in .noConflict() mode.
 * ======================================================================== */

(function ($) {

    // Use this variable to set up the common and page specific functions. If you
    // rename this variable, you will also need to rename the namespace below.
    var Sage = {
        // All pages
        'common': {
            init: function () {
                // JavaScript to be fired on all pages
            },
            finalize: function () {
                // JavaScript to be fired on all pages, after page specific JS is fired
            }
        },
        // Home page
        'home': {
            init: function () {
                // JavaScript to be fired on the home page
            },
            finalize: function () {
                // JavaScript to be fired on the home page, after the init JS
            }
        },
        // About us page, note the change from about-us to about_us.
        'about_us': {
            init: function () {
                // JavaScript to be fired on the about us page
            }
        }
    };

    // The routing fires all common scripts, followed by the page specific scripts.
    // Add additional events for more control over timing e.g. a finalize event
    var UTIL = {
        fire: function (func, funcname, args) {
            var fire;
            var namespace = Sage;
            funcname = (funcname === undefined) ? 'init' : funcname;
            fire = func !== '';
            fire = fire && namespace[func];
            fire = fire && typeof namespace[func][funcname] === 'function';

            if (fire) {
                namespace[func][funcname](args);
            }
        },
        loadEvents: function () {
            // Fire common init JS
            UTIL.fire('common');

            // Fire page-specific init JS, and then finalize JS
            $.each(document.body.className.replace(/-/g, '_').split(/\s+/), function (i, classnm) {
                UTIL.fire(classnm);
                UTIL.fire(classnm, 'finalize');
            });

            // Fire common finalize JS
            UTIL.fire('common', 'finalize');
        }
    };

    // Load Events
    $(document).ready(UTIL.loadEvents);

})(jQuery); // Fully reference jQuery after this point.
<|endoftext|>


<commit_before>
window.AudioContext = window.AudioContext||window.webkitAudioContext;

function clone(obj) {
  if (null == obj || "object" != typeof obj) return obj;
  var copy = obj.constructor();
  for (var attr in obj) {
    if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
  }
  return copy;
}

(function() {
  var lastTime = 0;
  var vendors = ['webkit', 'moz'];
  for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
    window.cancelAnimationFrame =
      window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
  }

  if (!window.requestAnimationFrame)
    window.requestAnimationFrame = function(callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function() { callback(currTime + timeToCall); },
				 timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };

  if (!window.cancelAnimationFrame)
    window.cancelAnimationFrame = function(id) {
      clearTimeout(id);
    };
}());

var sin_cache={};

function round(n) {
  return Math.round(n);
}

function abs(n) {
  return Math.abs(n);
}

function sin(v) {
  return(Math.sin(v));
  if(!v in sin_cache)
    sin_cache[v]=Math.sin(v);
  return(sin_cache[v]);
}

function cos(v) {
  return(sin(v+Math.PI/2));
}

function tan(v) {
  return Math.tan(v);
}

function normalize(v,length) {
  var x=v[0];
  var y=v[1];
  var angle=Math.atan2(x,y);
  if(!length)
    length=1;
  return([
    sin(angle)*length,
    cos(angle)*length
  ]);
}

function fl(n) {
  return Math.floor(n);
}

function randint(l,h) {
  return(Math.floor(Math.random()*(h-l+1))+l);
}

function elements(obj) {
  var n=0;
  for(var i in obj)
    n+=1;
  return n;
}

function s(i) {
  if(i == 1)
    return "";
  else
    return "s";
}

function within(n,c,r) {
  if((n > c+r) || (n < c-r))
    return false;
  return true;
}

function trange(il,i,ih,ol,oh) {
  return(ol+(oh-ol)*(i-il)/(ih-il));
  i=(i/(ih-il))-il;
  return (i*(oh-ol))+ol;
}

function clamp(l,i,h) {
  if(h == null) {
    if(l > i)
      return l;
    return i;
  }
  var temp;
  if(l > h) {
    temp=h;
    h=l;
    l=temp;
  }
  if(l > i)
    return l;
  if(h < i)
    return h;
  return i;
}

function crange(il,i,ih,ol,oh) {
  return clamp(ol,trange(il,i,ih,ol,oh),oh);
}

function srange(il,i,ih) {
  //    return Math.cos();
}

function distance2d(a,b) {
  var x=a[0]-b[0];
  var y=a[1]-b[1];
  return Math.sqrt((x*x)+(y*y));
}

function degrees(radians) {
  return (radians/(Math.PI*2))*360;
}

function radians(degrees) {
  return (degrees/360)*(Math.PI*2);
}

function choose(l) {
  return l[Math.floor(Math.random()*l.length)];
}

function choose_weight(l) {
  if(l.length == 0) return;
  if(typeof l[0] != typeof []) return choose(l);
  // l = [[item, weight], [item, weight] ... ];
  var weight  = 0;
  for(var i=0;i<l.length;i++) {
    weight += l[i][1];
  }
  var random = Math.random() * weight;
  weight     = 0;
  for(var i=0;i<l.length;i++) {
    weight += l[i][1];
    if(weight > random) {
      return l[i][0];
    }
  }
  console.log("OHSHIT");
  return(null);
}

function mod(a, b) {
  return ((a%b)+b)%b;
};

function lpad(n, width) {
  var x = "0000000000000" + n;
  return x.substr(x.length-width, width);
}

function angle_offset(a, b) {
  a = degrees(a);
  b = degrees(b);
  var invert=false;
  if(b > a) {
    invert=true;
    var temp=a;
    a=b;
    b=temp;
  }
  var offset=mod(a-b, 360);
  if(offset > 180) offset -= 360;
  if(invert) offset *= -1;
  offset = radians(offset);
  return offset;
}

function average() {
  var sum = 0;
  for(var i=0;i<arguments.length;i++) sum += arguments[i];
  return sum / arguments.length;
}

function heading_to_string(heading) {
  return round(mod(degrees(heading), 360));
}

var radio_names = {
  0:"zero",
  1:"one",
  2:"two",
  3:"three",
  4:"four",
  5:"five",
  6:"six",
  7:"seven",
  8:"eight",
  9:"niner",
  a:"alpha",
  b:"bravo",
  c:"charlie",
  d:"delta",
  e:"echo",
  f:"foxtrot",
  g:"golf",
  h:"hotel",
  i:"india",
  j:"juliet",
  k:"kilo",
  l:"lima",
  m:"mike",
  n:"november",
  o:"oscar",
  p:"papa",
  q:"quebec",
  r:"romeo",
  s:"sierra",
  t:"tango",
  u:"uniform",
  v:"victor",
  w:"whiskey",
  x:"x-ray",
  y:"yankee",
  z:"zulu",
  "-":"dash",
  ".":"point",
};

var radio_compass_names = {
  "n":"north",
  "w":"west",
  "s":"south",
  "e":"east",
};

var radio_runway_names = clone(radio_names);

radio_runway_names.l = "left";
radio_runway_names.c = "center";
radio_runway_names.r = "right";

function radio(input) {
  input = input + "";
  input = input.toLowerCase();
  var s = [];
  for(var i=0;i<input.length;i++) {
    var c = radio_names[input[i]];
    if(c) s.push(c);
  }
  return s.join(" ");
}

function radio_runway(input) {
  input = input + "";
  input = input.toLowerCase();
  var s = [];
  for(var i=0;i<input.length;i++) {
    var c = radio_runway_names[input[i]];
    if(c) s.push(c);
  }
  return s.join(" ");
}

function radio_compass(input) {
  input = input + "";
  input = input.toLowerCase();
  var s = [];
  for(var i=0;i<input.length;i++) {
    var c = radio_compass_names[input[i]];
    if(c) s.push(c);
  }
  return s.join(" ");
}

function radio_trend(category, measured, target) {
  var CATEGORIES = {
    "altitude": ["descend to", "climb to",  "maintaining"],
    "speed":    ["set speed",  "set speed", "maintaining"]
  };
  if(measured > target) return CATEGORIES[category][0];
  if(measured < target) return CATEGORIES[category][1];
  return CATEGORIES[category][2];
}

var DIRECTIONS = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];

function compass_direction(angle) {
  angle /= Math.PI*2;
  angle = round(mod(angle, 1) * 8);
  if(angle == 8) return "NW";
  angle = DIRECTIONS[round(angle)];
  return angle;
}
<commit_msg>aircraft confirm with "360" instead of "0"<commit_after>
window.AudioContext = window.AudioContext||window.webkitAudioContext;

function clone(obj) {
  if (null == obj || "object" != typeof obj) return obj;
  var copy = obj.constructor();
  for (var attr in obj) {
    if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
  }
  return copy;
}

(function() {
  var lastTime = 0;
  var vendors = ['webkit', 'moz'];
  for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
    window.cancelAnimationFrame =
      window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
  }

  if (!window.requestAnimationFrame)
    window.requestAnimationFrame = function(callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function() { callback(currTime + timeToCall); },
				 timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };

  if (!window.cancelAnimationFrame)
    window.cancelAnimationFrame = function(id) {
      clearTimeout(id);
    };
}());

var sin_cache={};

function round(n) {
  return Math.round(n);
}

function abs(n) {
  return Math.abs(n);
}

function sin(v) {
  return(Math.sin(v));
  if(!v in sin_cache)
    sin_cache[v]=Math.sin(v);
  return(sin_cache[v]);
}

function cos(v) {
  return(sin(v+Math.PI/2));
}

function tan(v) {
  return Math.tan(v);
}

function normalize(v,length) {
  var x=v[0];
  var y=v[1];
  var angle=Math.atan2(x,y);
  if(!length)
    length=1;
  return([
    sin(angle)*length,
    cos(angle)*length
  ]);
}

function fl(n) {
  return Math.floor(n);
}

function randint(l,h) {
  return(Math.floor(Math.random()*(h-l+1))+l);
}

function elements(obj) {
  var n=0;
  for(var i in obj)
    n+=1;
  return n;
}

function s(i) {
  if(i == 1)
    return "";
  else
    return "s";
}

function within(n,c,r) {
  if((n > c+r) || (n < c-r))
    return false;
  return true;
}

function trange(il,i,ih,ol,oh) {
  return(ol+(oh-ol)*(i-il)/(ih-il));
  i=(i/(ih-il))-il;
  return (i*(oh-ol))+ol;
}

function clamp(l,i,h) {
  if(h == null) {
    if(l > i)
      return l;
    return i;
  }
  var temp;
  if(l > h) {
    temp=h;
    h=l;
    l=temp;
  }
  if(l > i)
    return l;
  if(h < i)
    return h;
  return i;
}

function crange(il,i,ih,ol,oh) {
  return clamp(ol,trange(il,i,ih,ol,oh),oh);
}

function srange(il,i,ih) {
  //    return Math.cos();
}

function distance2d(a,b) {
  var x=a[0]-b[0];
  var y=a[1]-b[1];
  return Math.sqrt((x*x)+(y*y));
}

function degrees(radians) {
  return (radians/(Math.PI*2))*360;
}

function radians(degrees) {
  return (degrees/360)*(Math.PI*2);
}

function choose(l) {
  return l[Math.floor(Math.random()*l.length)];
}

function choose_weight(l) {
  if(l.length == 0) return;
  if(typeof l[0] != typeof []) return choose(l);
  // l = [[item, weight], [item, weight] ... ];
  var weight  = 0;
  for(var i=0;i<l.length;i++) {
    weight += l[i][1];
  }
  var random = Math.random() * weight;
  weight     = 0;
  for(var i=0;i<l.length;i++) {
    weight += l[i][1];
    if(weight > random) {
      return l[i][0];
    }
  }
  console.log("OHSHIT");
  return(null);
}

function mod(a, b) {
  return ((a%b)+b)%b;
};

function lpad(n, width) {
  var x = "0000000000000" + n;
  return x.substr(x.length-width, width);
}

function angle_offset(a, b) {
  a = degrees(a);
  b = degrees(b);
  var invert=false;
  if(b > a) {
    invert=true;
    var temp=a;
    a=b;
    b=temp;
  }
  var offset=mod(a-b, 360);
  if(offset > 180) offset -= 360;
  if(invert) offset *= -1;
  offset = radians(offset);
  return offset;
}

function average() {
  var sum = 0;
  for(var i=0;i<arguments.length;i++) sum += arguments[i];
  return sum / arguments.length;
}

function heading_to_string(heading) {
  heading = round(mod(degrees(heading), 360));
  if(heading == 0) heading = 360;
  return heading;
}

var radio_names = {
  0:"zero",
  1:"one",
  2:"two",
  3:"three",
  4:"four",
  5:"five",
  6:"six",
  7:"seven",
  8:"eight",
  9:"niner",
  a:"alpha",
  b:"bravo",
  c:"charlie",
  d:"delta",
  e:"echo",
  f:"foxtrot",
  g:"golf",
  h:"hotel",
  i:"india",
  j:"juliet",
  k:"kilo",
  l:"lima",
  m:"mike",
  n:"november",
  o:"oscar",
  p:"papa",
  q:"quebec",
  r:"romeo",
  s:"sierra",
  t:"tango",
  u:"uniform",
  v:"victor",
  w:"whiskey",
  x:"x-ray",
  y:"yankee",
  z:"zulu",
  "-":"dash",
  ".":"point",
};

var radio_compass_names = {
  "n":"north",
  "w":"west",
  "s":"south",
  "e":"east",
};

var radio_runway_names = clone(radio_names);

radio_runway_names.l = "left";
radio_runway_names.c = "center";
radio_runway_names.r = "right";

function radio(input) {
  input = input + "";
  input = input.toLowerCase();
  var s = [];
  for(var i=0;i<input.length;i++) {
    var c = radio_names[input[i]];
    if(c) s.push(c);
  }
  return s.join(" ");
}

function radio_runway(input) {
  input = input + "";
  input = input.toLowerCase();
  var s = [];
  for(var i=0;i<input.length;i++) {
    var c = radio_runway_names[input[i]];
    if(c) s.push(c);
  }
  return s.join(" ");
}

function radio_compass(input) {
  input = input + "";
  input = input.toLowerCase();
  var s = [];
  for(var i=0;i<input.length;i++) {
    var c = radio_compass_names[input[i]];
    if(c) s.push(c);
  }
  return s.join(" ");
}

function radio_trend(category, measured, target) {
  var CATEGORIES = {
    "altitude": ["descend to", "climb to",  "maintaining"],
    "speed":    ["set speed",  "set speed", "maintaining"]
  };
  if(measured > target) return CATEGORIES[category][0];
  if(measured < target) return CATEGORIES[category][1];
  return CATEGORIES[category][2];
}

var DIRECTIONS = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];

function compass_direction(angle) {
  angle /= Math.PI*2;
  angle = round(mod(angle, 1) * 8);
  if(angle == 8) return "NW";
  angle = DIRECTIONS[round(angle)];
  return angle;
}
<|endoftext|>