<commit_before>if(typeof translations == 'undefined'){
    translations = {};
}

translations.ru_RU = {
// header
    'Search': 'Поиск',
    // Nav menu
    'Add': 'Добавить',
    'By URIs': 'URL-адреса',
    'By Torrents': 'Torrent-файлы',
    'By Metalinks': 'Metalink-файлы',
    'Manage': 'Управление',
    'Pause All': 'Приостановить Все',
    'Resume Paused': 'Возобновить Все',
    'Purge Completed': 'Удалиь Завершенные',
    'Settings': 'Настройки',
    'Connection Settings': 'Настройки Соединения',
    'Global Settings': 'Глобальные Настройки',
    'Server info': 'Информация о Сервере',
    'About and contribute': 'Информация и сотрудничество',
    'Toggle navigation': 'Переключение навигации',
// body
// nav side bar
    'Miscellaneous': 'Разное',
    'Global Statistics': 'Глобальная статистика',
    'About': 'Об',
    'Displaying': 'Показано',
    'of': 'из',
    'downloads': 'загрузки',
    'Language': 'Язык',
// download filters
    'Download Filters': 'Фильтр Загрузок',
    'Running': 'Запущенные',
    'Active': 'Активные',
    'Waiting': 'Ожидающие',
    'Complete': 'Завершенные',
    'Error': 'Ошибка',
    'Paused': 'Приостановленные',
    'Removed': 'Удаленные',
    'Hide linked meta-data': 'Скрыть связанные метаданные',
    'Displaying': 'Показано',
    'of': 'из',
    'downloads': 'загрузок',
    'Toggle': 'Переключить',
    'Reset filters': 'Сбросить фильтры',
// starred properties
    'Quick Access Settings': 'Настройки Быстрого Доступа',
    'Save settings': 'Сохранить настройки',
    'Currently no download in line to display, use the': 'На данный момент ничего не загружается, используйте кнопку',
    'download button to start downloading files!': 'чтобы начать загрузку файла!',
    'Peers': 'Пиры',
    'More Info': 'Информация',
    'Remove': 'Удалить',
    '# of': '# из',
    'Length': 'Размер',
// modals
    'Add Downloads By URIs': 'Добавить загрузки из URL-адресов',
    '- You can add multiple downloads (files) at the same time by putting URIs for each file on a separate line.':
        '- Вы можете добавить несколько загрузок (файлов), одновременно, помещая URL-адреса для каждого файла на отдельной строке.',
    '- You can also add multiple URIs (mirrors) for the *same* file. To do this, separate the URIs by a space.':
        '- Можно также добавить несколько URL-адресов (зеркал) для *одного* файла. Для этого отделите URL-адреса пробелом.',
    '- A URI can be HTTP(S)/FTP/BitTorrent-Magnet.': '- URL-адрес может быть HTTP(S)/FTP/BitTorrent-Magnet.',
    'E.g. to add 2 files (downloads) f1.jpg and f2.mp4 with 2 URIs (mirrors) each, add URIs as follows':
        'Например для добавления 2 файлов (загрузок) f1.jpg и f2.mp4 с 2 URL-адресов (зеркал), добавьте URL-адреса следующим образом',
    'Download settings': 'Настройки загрузки',
    'Advanced settings': 'Расширенные настройки',
    'Cancel': 'Отмена',
    'Start': 'Начать',
    'Choose': 'Выбрать',
    'Quick Access (shown on the main page)': 'Постой доступ (смотреть на главной странице)',
// add torrent modal
    'Add Downloads By Torrents': 'Добавить Загрузку из Torrent-файлов',
    '- Select the torrent from the local filesystem to start the download.': '- Выберите Torrent-файлы из локальной файловой системы, чтобы начать загрузку.',
    '- You can select multiple torrents to start multiple downloads.': '- Вы можете выбрать несколько Torrent-файлы для запуска нескольких загрузок.',
    '- To add a BitTorrent-Magnet URL, use the Add By URI option and add it there.': '- Для добавления BitTorrent-Magnet ссылки, воспользуйтесь пунктом меню  Добавить из URL-адреса.',
    'Select Torrents': 'Выберите Торренты',
    'Select a Torrent': 'Выберите Торрент',
// add metalink modal
    'Add Downloads By Metalinks': 'Добавить Загрузку из Metalink-файлов',
    'Select Metalinks': 'Вырать Metalink-файлы',
    '- Select the Metalink from the local filesystem to start the download.': '- Выберите Metalink-файлы из локальной файловой системы, чтобы начать загрузку.',
    '- You can select multiple Metalinks to start multiple downloads.': '- Вы можете выбрать несколько Metalink-файлов для запуска нескольких загрузок.',
    'Select a Metalink': 'Выберите Metalink',
// select file modal
    'Choose files to start download for': 'Выберите файлы, чтобы начать загрузку для',
    'Select to download': 'Выберите для загрузки',
// settings modal
    'Aria2 RPC host and port': 'Aria2 RPC хост и порт',
    'Enter the host': 'Введите хост',
    'Enter the IP or DNS name of the server on which the RPC for Aria2 is running (default: localhost)':
        'Введите IP или DNS-имя сервера, на котором запущена Aria2 со включенным RPC (по умолчанию: localhost)',
    'Enter the port': 'Введите порт',
    'Enter the port of the server on which the RPC for Aria2 is running (default: 6800)':
        'Введите порт сервера, на котором запущена Aria2 со включенным RPC (по умолчанию: 6800)',
    'Enter the RPC path': 'Enter the RPC path',
    'Enter the path for the Aria2 RPC endpoint (default: /jsonrpc)': 'Введите конечный путь, для Aria2 RPC (по умолчанию: /jsonrpc)',
    'SSL/TLS encryption': 'SSL/TLS шифрование',
    'Enable SSL/TLS encryption': 'Разрешить SSL/TLS шифрование',
    'Enter the secret token (optional)': 'Введите секретный токен (необязательно)',
    'Enter the Aria2 RPC secret token (leave empty if authentication is not enabled)':
        'Введите Aria2 RPC секретный токен (оставьте пустым, если авторизация не включена)',
    'Enter the username (optional)': 'Введите имя пользователя (необязательно)',
    'Enter the Aria2 RPC username (empty if authentication not enabled)':
        'Введите имя пользователя Aria2 RPC (оставьте пустым, если авторизация не включена)',
    'Enter the password (optional)': 'Введите пароль (необязательно)',
    'Enter the Aria2 RPC password (empty if authentication not enabled)': 'Введите пароль для Aria2 RPC (оставьте пустым, если авторизация не включена)',
    'Enter base URL (optional)': 'Введите базовый URL-адрес (необязательно)',
    'Direct Download': 'Прямая Загрузка',
    'If supplied, links will be created to enable direct download from the Aria2 server.':
        'При поставке, ссылки будут созданы для того, чтобы разрешить прямые загрузки с сервера Aria2.',
    '(Requires appropriate webserver to be configured.)': '(Требуется соответствующий веб-сервер для настройки.)',
    'Save Connection configuration': 'Сохранить Настройки Соединения',
// server info modal
    'Aria2 server info': 'Информация о сервере Aria2',
    'Aria2 Version': 'Версия Aria2',
    'Features Enabled': 'Добавть Функции',
// about modal
    'To download the latest version of the project, add issues or to contribute back, head on to':
        'Чтобы загрузить последнюю версию проекта, добавить вопросы или внести свой вклад, передите на',
    'Or you can open the latest version in the browser through': 'Или вы можете открыть последнюю версию в браузере через',
    'Close': 'Закрыть',
// lables
    'Download status':'Статус загрузки',
    'Download Speed':'Скорость загрузки',
    'Upload Speed':'Скорость отдачи',
    'Estimated time':'Оставшееся время',
    'Download Size':'Размер Загрузки',
    'Downloaded':'Загружено',
    'Progress':'Прогресс',
    'Download Path':'Путь для загрузки',
    'Uploaded':'Отдано',
    'Download GID':'Загруженый GID',
    'Number of Pieces':'Количество Частей',
    'Piece Length': 'Размер Частей',
    'Shutdown Server': 'Выключить Сервер',

    'The last connection attempt was unsuccessful. Trying another configuration': 'Последняя попытка подключения была неудачной. Попробуйте другую конфигурацию',
    'Oh Snap!': 'Опаньки!',
    'Could not connect to the aria2 RPC server. Will retry in 10 secs. You might want to check the connection settings by going to Settings > Connection Settings': 'Не удалось подключиться к серверу aria2 RPC. Попытка будет повторена в течение 10 секунд. Вы можете проверить параметры подключения, перейдя в меню Настройки> Настройки соединения',
    'Successfully connected to Aria2 through its remote RPC …': 'Успешное подключение к Aria2 через удаленный RPC …',
    'Successfully connected to Aria2 through remote RPC, however the connection is still insecure. For complete security try adding an authorization secret token while starting Aria2 (through the flag --rpc-secret)': 'Успешное подключение к Aria2 через удаленный RPC, однако соединение все еще небезопасно. Для обеспечения лучшей безопасности добавьте секретный токен авторизации при запуске aria2 (через флаг --rpc-secret)',
    'Trying to connect to aria2 using the new connection configuration': 'Попытка подключиться к aria2 с использованием новой конфигурации',
};
<commit_msg>Update ru_RU.js<commit_after>if(typeof translations == 'undefined'){
    translations = {};
}

translations.ru_RU = {
// header
    'Search': 'Поиск',
    // Nav menu
    'Add': 'Добавить',
    'By URIs': 'URL-адреса',
    'By Torrents': 'Torrent-файлы',
    'By Metalinks': 'Metalink-файлы',
    'Manage': 'Управление',
    'Pause All': 'Приостановить Все',
    'Resume Paused': 'Возобновить Все',
    'Purge Completed': 'Удалиь Завершенные',
    'Settings': 'Настройки',
    'Connection Settings': 'Настройки Соединения',
    'Global Settings': 'Глобальные Настройки',
    'Server info': 'Информация о Сервере',
    'About and contribute': 'Информация и сотрудничество',
    'Toggle navigation': 'Переключение навигации',
// body
// nav side bar
    'Miscellaneous': 'Разное',
    'Global Statistics': 'Глобальная статистика',
    'About': 'Об',
    'Displaying': 'Показано',
    'of': 'из',
    'downloads': 'загрузки',
    'Language': 'Язык',
// download filters
    'Download Filters': 'Фильтр Загрузок',
    'Running': 'Запущенные',
    'Active': 'Активные',
    'Waiting': 'Ожидающие',
    'Complete': 'Завершенные',
    'Error': 'С ошибками',
    'Paused': 'Приостановленные',
    'Removed': 'Удаленные',
    'Hide linked meta-data': 'Скрыть связанные метаданные',
    'Displaying': 'Показано',
    'of': 'из',
    'downloads': 'загрузок',
    'Toggle': 'Переключить',
    'Reset filters': 'Сбросить фильтры',
// starred properties
    'Quick Access Settings': 'Настройки Быстрого Доступа',
    'Save settings': 'Сохранить настройки',
    'Currently no download in line to display, use the': 'На данный момент ничего не загружается, используйте кнопку',
    'download button to start downloading files!': 'чтобы начать загрузку файла!',
    'Peers': 'Пиры',
    'More Info': 'Информация',
    'Remove': 'Удалить',
    '# of': '# из',
    'Length': 'Размер',
// modals
    'Add Downloads By URIs': 'Добавить загрузки из URL-адресов',
    '- You can add multiple downloads (files) at the same time by putting URIs for each file on a separate line.':
        '- Вы можете добавить несколько загрузок (файлов), одновременно, помещая URL-адреса для каждого файла на отдельной строке.',
    '- You can also add multiple URIs (mirrors) for the *same* file. To do this, separate the URIs by a space.':
        '- Можно также добавить несколько URL-адресов (зеркал) для *одного* файла. Для этого отделите URL-адреса пробелом.',
    '- A URI can be HTTP(S)/FTP/BitTorrent-Magnet.': '- URL-адрес может быть HTTP(S)/FTP/BitTorrent-Magnet.',
    'E.g. to add 2 files (downloads) f1.jpg and f2.mp4 with 2 URIs (mirrors) each, add URIs as follows':
        'Например для добавления 2 файлов (загрузок) f1.jpg и f2.mp4 с 2 URL-адресов (зеркал), добавьте URL-адреса следующим образом',
    'Download settings': 'Настройки загрузки',
    'Advanced settings': 'Расширенные настройки',
    'Cancel': 'Отмена',
    'Start': 'Начать',
    'Choose': 'Выбрать',
    'Quick Access (shown on the main page)': 'Постой доступ (смотреть на главной странице)',
// add torrent modal
    'Add Downloads By Torrents': 'Добавить Загрузку из Torrent-файлов',
    '- Select the torrent from the local filesystem to start the download.': '- Выберите Torrent-файлы из локальной файловой системы, чтобы начать загрузку.',
    '- You can select multiple torrents to start multiple downloads.': '- Вы можете выбрать несколько Torrent-файлы для запуска нескольких загрузок.',
    '- To add a BitTorrent-Magnet URL, use the Add By URI option and add it there.': '- Для добавления BitTorrent-Magnet ссылки, воспользуйтесь пунктом меню  Добавить из URL-адреса.',
    'Select Torrents': 'Выберите Торренты',
    'Select a Torrent': 'Выберите Торрент',
// add metalink modal
    'Add Downloads By Metalinks': 'Добавить Загрузку из Metalink-файлов',
    'Select Metalinks': 'Вырать Metalink-файлы',
    '- Select the Metalink from the local filesystem to start the download.': '- Выберите Metalink-файлы из локальной файловой системы, чтобы начать загрузку.',
    '- You can select multiple Metalinks to start multiple downloads.': '- Вы можете выбрать несколько Metalink-файлов для запуска нескольких загрузок.',
    'Select a Metalink': 'Выберите Metalink',
// select file modal
    'Choose files to start download for': 'Выберите файлы, чтобы начать загрузку для',
    'Select to download': 'Выберите для загрузки',
// settings modal
    'Aria2 RPC host and port': 'Aria2 RPC хост и порт',
    'Enter the host': 'Введите хост',
    'Enter the IP or DNS name of the server on which the RPC for Aria2 is running (default: localhost)':
        'Введите IP или DNS-имя сервера, на котором запущена Aria2 со включенным RPC (по умолчанию: localhost)',
    'Enter the port': 'Введите порт',
    'Enter the port of the server on which the RPC for Aria2 is running (default: 6800)':
        'Введите порт сервера, на котором запущена Aria2 со включенным RPC (по умолчанию: 6800)',
    'Enter the RPC path': 'Enter the RPC path',
    'Enter the path for the Aria2 RPC endpoint (default: /jsonrpc)': 'Введите конечный путь, для Aria2 RPC (по умолчанию: /jsonrpc)',
    'SSL/TLS encryption': 'SSL/TLS шифрование',
    'Enable SSL/TLS encryption': 'Разрешить SSL/TLS шифрование',
    'Enter the secret token (optional)': 'Введите секретный токен (необязательно)',
    'Enter the Aria2 RPC secret token (leave empty if authentication is not enabled)':
        'Введите Aria2 RPC секретный токен (оставьте пустым, если авторизация не включена)',
    'Enter the username (optional)': 'Введите имя пользователя (необязательно)',
    'Enter the Aria2 RPC username (empty if authentication not enabled)':
        'Введите имя пользователя Aria2 RPC (оставьте пустым, если авторизация не включена)',
    'Enter the password (optional)': 'Введите пароль (необязательно)',
    'Enter the Aria2 RPC password (empty if authentication not enabled)': 'Введите пароль для Aria2 RPC (оставьте пустым, если авторизация не включена)',
    'Enter base URL (optional)': 'Введите базовый URL-адрес (необязательно)',
    'Direct Download': 'Прямая Загрузка',
    'If supplied, links will be created to enable direct download from the Aria2 server.':
        'При поставке, ссылки будут созданы для того, чтобы разрешить прямые загрузки с сервера Aria2.',
    '(Requires appropriate webserver to be configured.)': '(Требуется соответствующий веб-сервер для настройки.)',
    'Save Connection configuration': 'Сохранить Настройки Соединения',
// server info modal
    'Aria2 server info': 'Информация о сервере Aria2',
    'Aria2 Version': 'Версия Aria2',
    'Features Enabled': 'Добавть Функции',
// about modal
    'To download the latest version of the project, add issues or to contribute back, head on to':
        'Чтобы загрузить последнюю версию проекта, добавить вопросы или внести свой вклад, передите на',
    'Or you can open the latest version in the browser through': 'Или вы можете открыть последнюю версию в браузере через',
    'Close': 'Закрыть',
// lables
    'Download status':'Статус загрузки',
    'Download Speed':'Скорость загрузки',
    'Upload Speed':'Скорость отдачи',
    'Estimated time':'Оставшееся время',
    'Download Size':'Размер Загрузки',
    'Downloaded':'Загружено',
    'Progress':'Прогресс',
    'Download Path':'Путь для загрузки',
    'Uploaded':'Отдано',
    'Download GID':'Загруженый GID',
    'Number of Pieces':'Количество Частей',
    'Piece Length': 'Размер Частей',
    'Shutdown Server': 'Выключить Сервер',

    'The last connection attempt was unsuccessful. Trying another configuration': 'Последняя попытка подключения была неудачной. Попробуйте другую конфигурацию',
    'Oh Snap!': 'Опаньки!',
    'Could not connect to the aria2 RPC server. Will retry in 10 secs. You might want to check the connection settings by going to Settings > Connection Settings': 'Не удалось подключиться к серверу aria2 RPC. Попытка будет повторена в течение 10 секунд. Вы можете проверить параметры подключения, перейдя в меню Настройки> Настройки соединения',
    'Successfully connected to Aria2 through its remote RPC …': 'Успешное подключение к Aria2 через удаленный RPC …',
    'Successfully connected to Aria2 through remote RPC, however the connection is still insecure. For complete security try adding an authorization secret token while starting Aria2 (through the flag --rpc-secret)': 'Успешное подключение к Aria2 через удаленный RPC, однако соединение все еще небезопасно. Для обеспечения лучшей безопасности добавьте секретный токен авторизации при запуске aria2 (через флаг --rpc-secret)',
    'Trying to connect to aria2 using the new connection configuration': 'Попытка подключиться к aria2 с использованием новой конфигурации',
};
<|endoftext|>


<commit_before>var App = {
    model: {},
    collection: {},
    view: {},
};

/**
 * Card model
 */
App.model.Card = Backbone.Model.extend({});

/**
 * Cards collection
 */
App.collection.Cards = Backbone.Collection.extend({});

/**
 * Board model
 */
App.model.Board = Backbone.Model.extend({});

/**
 * Board collection
 */
App.collection.Boards = Backbone.Collection.extend({});

/**
 * Render a card on fullcalendar
 */
App.view.Card = Backbone.View.extend({
    initialize: function() {
        this.model.bind('change', this.render, this);
    },

    render: function() {
        if (this.model.get('hidden')) {
            $(this.el).fullCalendar('removeEvents', this.model.id);
        } else {
            $(this.el).fullCalendar('renderEvent', {
                id: this.model.id,
                title: this.model.get('name'),
                start: this.model.get('badges').due,
                color: _(this.model.get('idMembers')).include(this.options.me.id) ? 'red' : 'green',
                url: this.model.get('url')
            }, true);
        }
        return this;
    }
});

App.view.Board = Backbone.View.extend({
    events: {
        "click input": "click"
    },

    tagName: 'label',

    click: function(e) {
        var hidden = true;
        if ($(e.target).is(':checked')) {
            hidden = false
        }
        this.model.set({hidden: hidden});
    },

    render: function() {
        var input = this.make('input', {type: 'checkbox',
                                        value: this.model.id,
                                        checked: true});
        $(this.el).text(this.model.get('name')).append(input);
        return this;
    }
});

$(document).ready(function() {
    Trello.authorize({
        interactive: false,
        success: onAuthorize
    });

    if (!Trello.authorized()) {
        return Trello.authorize({ success: onAuthorize });
    }

    var calendar = $('#calendar').fullCalendar({
        header: {
	    left: 'prev,next today',
	    center: 'title',
	    right: 'month,agendaWeek,agendaDay'
	},
        height: $(document).height() - 50
    });
    $(window).resize(function() {
        calendar.fullCalendar('option', 'height', $(document).height() - 50);
    });

    var currentUser;
    var boards = new App.collection.Boards();
    var cards = new App.collection.Cards();

    function renderCard(card) {
        new App.view.Card({model: card,
                           me: currentUser,
                           el: calendar.get(0)}).render();
    }

    function renderBoard(board) {
        var view = new App.view.Board({model: board}).render();
        $(view.el).appendTo($('#boards'));
        board.bind('change:hidden', function() {
            cards.chain().filter(function(card) {
                return card.get('idBoard') == board.id;
            }).each(function(card) {
                card.set({hidden: board.get('hidden')});
            });
        });
    }

    cards.bind('add', renderCard);
    boards.bind('add', renderBoard);

    function listBoards(me) {
        return function(tBoards) {
            _(tBoards).each(function(board) {
                boards.add(new App.model.Board(board));
                Trello.get('/boards/'+ board.id +'/cards/all', {badges: true}).done(function(tCards) {
                    _(tCards).each(function(card) {
                        if (!card.badges.due) return;
                        cards.add(new App.model.Card(card));
                    });
                });
            });
        }
    }

    function showMe(me) {
        currentUser = me;
        Trello.get('/members/my/boards', {filter: 'open'}).done(listBoards(me));
    }

    function onAuthorize() {
        if (!Trello.authorized()) return Trello.authorize({ success: onAuthorize });

        Trello.members.get('me').done(showMe);
    }
});
<commit_msg>By default, events are not allDay.<commit_after>var App = {
    model: {},
    collection: {},
    view: {},
};

/**
 * Card model
 */
App.model.Card = Backbone.Model.extend({});

/**
 * Cards collection
 */
App.collection.Cards = Backbone.Collection.extend({});

/**
 * Board model
 */
App.model.Board = Backbone.Model.extend({});

/**
 * Board collection
 */
App.collection.Boards = Backbone.Collection.extend({});

/**
 * Render a card on fullcalendar
 */
App.view.Card = Backbone.View.extend({
    initialize: function() {
        this.model.bind('change', this.render, this);
    },

    render: function() {
        if (this.model.get('hidden')) {
            $(this.el).fullCalendar('removeEvents', this.model.id);
        } else {
            $(this.el).fullCalendar('renderEvent', {
                id: this.model.id,
                allDay: false,
                title: this.model.get('name'),
                start: this.model.get('badges').due,
                color: _(this.model.get('idMembers')).include(this.options.me.id) ? 'red' : 'green',
                url: this.model.get('url')
            }, true);
        }
        return this;
    }
});

App.view.Board = Backbone.View.extend({
    events: {
        "click input": "click"
    },

    tagName: 'label',

    click: function(e) {
        var hidden = true;
        if ($(e.target).is(':checked')) {
            hidden = false
        }
        this.model.set({hidden: hidden});
    },

    render: function() {
        var input = this.make('input', {type: 'checkbox',
                                        value: this.model.id,
                                        checked: true});
        $(this.el).text(this.model.get('name')).append(input);
        return this;
    }
});

$(document).ready(function() {
    Trello.authorize({
        interactive: false,
        success: onAuthorize
    });

    if (!Trello.authorized()) {
        return Trello.authorize({ success: onAuthorize });
    }

    var calendar = $('#calendar').fullCalendar({
        header: {
	    left: 'prev,next today',
	    center: 'title',
	    right: 'month,agendaWeek,agendaDay'
	},
        height: $(document).height() - 50
    });
    $(window).resize(function() {
        calendar.fullCalendar('option', 'height', $(document).height() - 50);
    });

    var currentUser;
    var boards = new App.collection.Boards();
    var cards = new App.collection.Cards();

    function renderCard(card) {
        new App.view.Card({model: card,
                           me: currentUser,
                           el: calendar.get(0)}).render();
    }

    function renderBoard(board) {
        var view = new App.view.Board({model: board}).render();
        $(view.el).appendTo($('#boards'));
        board.bind('change:hidden', function() {
            cards.chain().filter(function(card) {
                return card.get('idBoard') == board.id;
            }).each(function(card) {
                card.set({hidden: board.get('hidden')});
            });
        });
    }

    cards.bind('add', renderCard);
    boards.bind('add', renderBoard);

    function listBoards(me) {
        return function(tBoards) {
            _(tBoards).each(function(board) {
                boards.add(new App.model.Board(board));
                Trello.get('/boards/'+ board.id +'/cards/all', {badges: true}).done(function(tCards) {
                    _(tCards).each(function(card) {
                        if (!card.badges.due) return;
                        cards.add(new App.model.Card(card));
                    });
                });
            });
        }
    }

    function showMe(me) {
        currentUser = me;
        Trello.get('/members/my/boards', {filter: 'open'}).done(listBoards(me));
    }

    function onAuthorize() {
        if (!Trello.authorized()) return Trello.authorize({ success: onAuthorize });

        Trello.members.get('me').done(showMe);
    }
});
<|endoftext|>


<commit_before>// twister_actions.js
// 2013 Miguel Freitas
//
// This file contains some twister "actions" like requesting
// posts from dht, sending posts, replies. It seems to be a
// intermediate layer between twister_io and interface, but
// separation is not clearly defined. Perhaps it would be better
// to get rid of this file altogether.

// global variables

var postsPerRefresh = 10;
var maxExpandPost = 8;
var maxExpandPostTop = 4;
var _hashtagProcessedMap = {};
var _hashtagPendingPosts = [];
var autoUpdateHashtag = false;

// ----------------

function requestRepliedBefore(postLi)
{
    if(postLi.siblings().length >= maxExpandPostTop)
        return;

    var originalPost = postLi.find(".post-data");
    var reply_n = originalPost.attr('data-replied-to-screen-name');
    var reply_k = originalPost.attr('data-replied-to-id');

    if( reply_n != undefined && reply_k != undefined ) {
        dhtget( reply_n, "post" + reply_k, "s",
               function(postLi, postFromJson) {
                   var newStreamPost = postToElem(postFromJson, "related");
                   newStreamPost.hide();
                   postLi.before(newStreamPost);
                   newStreamPost.slideDown("fast");
                   $.MAL.relatedPostLoaded();
                   requestRepliedBefore(newStreamPost);
               }, postLi);
    }
}

function requestRepliesAfter(postLi)
{
    if($.MAL.getExpandedPostsCount(postLi) >= maxExpandPost)
        return;

    var originalPost = postLi.find(".post-data");
    var original_n = originalPost.attr('data-screen-name');
    var original_k = originalPost.attr('data-id');

    if( original_n != undefined && original_k != undefined ) {
        dhtget( original_n, "replies" + original_k, "m", $.MAL.reqRepAfterCB, postLi);
    }
}

function getTopPostOfConversation(postLi, post, postboard) {
    var reply_n;
    var reply_k;

    if (post && typeof(post) !== 'undefined' && "reply" in post["userpost"]) {
        reply_k = post["userpost"]["reply"]["k"];
        reply_n = post["userpost"]["reply"]["n"];
    } else if (postLi && typeof(postLi) !== 'undefined') {
        var originalPost = postLi.find(".post-data");
        reply_n = originalPost.attr('data-replied-to-screen-name');
        reply_k = originalPost.attr('data-replied-to-id');
    }

    if( reply_n != undefined && reply_k != undefined ) {
        dhtget( reply_n, "post" + reply_k, "s",
            function(postLi, postFromJson) {
                getTopPostOfConversation(null, postFromJson, postboard);
            }, postLi);
    } else {
        var newStreamPost;
        if (post)
            newStreamPost = postToElem(post, "related");
        else {
            newStreamPost = postLi.clone(true);
            newStreamPost.removeClass('original');
            newStreamPost.addClass('related');
            newStreamPost.find('.expanded-content').hide();
            newStreamPost.find('.show-more').hide();
        }
        requestRepliesAfterAll(newStreamPost);
        newStreamPost.find('.post-expand').remove();
        newStreamPost.unbind('click');
        newStreamPost.hide();
        postboard.append(newStreamPost);
        newStreamPost.slideDown("fast");
    }
}

function requestRepliesAfterAll(postLi)
{
    var originalPost = postLi.find(".post-data");
    var original_n = originalPost.attr('data-screen-name');
    var original_k = originalPost.attr('data-id');

    if( original_n != undefined && original_k != undefined ) {
        dhtget( original_n, "replies" + original_k, "m", $.MAL.reqRepAfterCB, postLi);
    }
}

function requestRTs(postLi)
{
    var originalPost = postLi.find(".post-data");

    var original_n = originalPost.attr('data-screen-name');
    var original_k = originalPost.attr('data-id');

    if( original_n != undefined && original_k != undefined ) {
        dhtget( original_n, "rts" + original_k, "m",
               function(originalPost, postsFromJson) {

                    if( postsFromJson.length ) {
                        var statCountValue = originalPost.find(".stat-count-value");
                        statCountValue.text( postsFromJson.length );

                        var avatarRow = originalPost.find(".avatar-row");
                        avatarRow.empty();
                        for( var i = 0; i < postsFromJson.length && i < 8; i++) {
                            var n = postsFromJson[i]["userpost"]["n"];
                            var elemUser = $("#avatar-row-template").clone(true);
                            elemUser.removeAttr('id');
                            elemUser.attr('href',$.MAL.userUrl(n));
                            getFullname(n,elemUser.find(".user-name-tooltip"));
                            getAvatar(n,elemUser.find(".size24"));
                            avatarRow.append( elemUser );
                        }

                        originalPost.find(".post-stats").slideDown("fast");
                    }
               }, originalPost);
    }
}

function appendPostToContainer(postFromJson, containerToAppend)
{
    var newStreamPost = postToElem(postFromJson, "original");
    newStreamPost.hide();
    containerToAppend.append( newStreamPost );
    newStreamPost.slideDown("fast");
    $.MAL.postboardLoaded();
}

var profilePostsLoading = false;

function requestPost(containerToAppend,username,resource,cbFunc,cbArgs){

    console.log('dhtget '+username+' '+resource);

    dhtget( username, resource, "s",
        function(args, postFromJson) {
            if( postFromJson ) {

            console.log(postFromJson);

            appendPostToContainer(postFromJson,args.containerToAppend);

            if(args.cbFunc!=undefined) args.cbFunc(args.cbArgs);

            }
        },
        {containerToAppend:containerToAppend,cbFunc:cbFunc,cbArgs:cbArgs}
    );

}

function requestPostRecursively(containerToAppend,username,resource,count,useGetposts)
{
    var max_id = -1;
    if( !resource ) {
        var streamItems = containerToAppend.children();
        if( streamItems.length != 0 ) {
            var lastItem = streamItems.eq(streamItems.length-1);
            resource = "post" + lastItem.find(".post-data").attr("data-lastk");
            max_id = parseInt(lastItem.find(".post-data").attr("data-id"))-1;
        }
    }

    profilePostsLoading = true;

    if( useGetposts ) {
        req = {username: username}
        if( max_id != -1 ) {
            req.max_id = max_id;
        }

        twisterRpc("getposts", [count,[req]],
                       function(args, posts) {
                           for( var i = 0; i < posts.length; i++ ) {
                              appendPostToContainer(posts[i],args.containerToAppend);
                           }
                           profilePostsLoading = false;
                       }, {containerToAppend:containerToAppend},
                       function(args, ret) {
                           profilePostsLoading = false;
                       }, {});
    } else {
        dhtget( username, resource, "s",
            function(args, postFromJson) {
               if( postFromJson ) {
                   appendPostToContainer(postFromJson,args.containerToAppend);

                   if( args.count > 1 ) {
                       var userpost = postFromJson["userpost"];
                       var n = userpost["n"];
                       var lastk = userpost["lastk"];
                       if( lastk == undefined )
                           lastk = userpost["k"] - 1; // not true with directmsgs in stream

                       requestPostRecursively(args.containerToAppend, n, "post"+lastk, count-1);
                   } else {
                       profilePostsLoading = false;
                       args.containerToAppend.scroll();
                   }
               } else {
                   profilePostsLoading = false;
               }
           }, {containerToAppend:containerToAppend, count:count} );
    }
}

function newPostMsg(msg, $postOrig) {
    if( lastPostId != undefined ) {
        var params = [defaultScreenName, lastPostId + 1, msg]
        if( $postOrig.length ) {
            params.push($postOrig.attr('data-screen-name'));
            params.push(parseInt($postOrig.attr('data-id')));
        }
        twisterRpc("newpostmsg", params,
                   function(arg, ret) { incLastPostId(); }, null,
                   function(arg, ret) { var msg = ("message" in ret) ? ret.message : ret;
                                        alert(polyglot.t("ajax_error", { error: msg })); }, null);
    } else {
        alert(polyglot.t("Internal error: lastPostId unknown (following yourself may fix!)"));
    }
}

function newRtMsg($postOrig) {
    var content_to_rt = $postOrig.attr('data-content_to_rt');
    var content_to_sigrt = $postOrig.attr('data-content_to_sigrt');

    var sig_userpost = String(content_to_sigrt);
    var userpost = $.evalJSON(String(content_to_rt));
    var rtObj = { sig_userpost :sig_userpost, userpost : userpost };

    if( lastPostId != undefined ) {
        var params = [defaultScreenName, lastPostId+1, rtObj]

        twisterRpc("newrtmsg", params,
                   function(arg, ret) { incLastPostId(); }, null,
                   function(arg, ret) { var msg = ("message" in ret) ? ret.message : ret;
                                        alert(polyglot.t("ajax_error", { error: msg })); }, null);
    } else {
        alert(polyglot.t("Internal error: lastPostId unknown (following yourself may fix!)"));
    }
}

function updateProfileData(profileModalContent, username) {

    //profileModalContent.find("a").attr("href",$.MAL.userUrl(username));
    profileModalContent.filter(".profile-card").attr("data-screen-name", username);
    profileModalContent.find(".profile-screen-name b").text(username);
    profileModalContent.find("a.follow").attr("href", $.MAL.followUrl(username));
    profileModalContent.find("a.direct-messages-with-user").attr("href", $.MAL.dmchatUrl(username));
    profileModalContent.find("a.new-post-to").attr("href", $.MAL.newPostToUrl(username));
    profileModalContent.find("a.mentions-from-user").attr("href", $.MAL.mentionsUrl(username));
    getFullname( username, profileModalContent.find(".profile-name") );
    getLocation( username, profileModalContent.find(".profile-location") );
    getWebpage( username, profileModalContent.find(".profile-url") );
    getBio( username, profileModalContent.find(".profile-bio") );
    getTox( username, profileModalContent.find(".profile-tox") );
    getBitmessage( username, profileModalContent.find(".profile-bitmessage") );
    getAvatar( username, profileModalContent.find(".profile-card-photo") );
    getPostsCount( username,  profileModalContent.find(".posts-count") );
    getFollowers( username, profileModalContent.find(".followers-count") );
    getNumFollowing( username, profileModalContent.find(".following-count") );
    getWhoFollows ( username, profileModalContent.find(".who-follow") );

    profileModalContent.find(".following-count").parent().attr("href", $.MAL.followingUrl(username));

    var postsView = profileModalContent.find(".postboard-posts");

    // try using getposts first. fallback to dht.
    twisterRpc("getposts", [1,[{username: username}]],
                       function(args, posts) {
                           updateProfilePosts(postsView, username, posts.length);
                       }, {},
                       function(args, ret) {
                           updateProfilePosts(postsView, username, false);
                       }, {});
}

function updateProfilePosts(postsView, username, useGetposts) {
    requestPostRecursively(postsView,username,"status",postsPerRefresh, useGetposts);

    postsView.scroll(function(){
        if (!profilePostsLoading) {
            var $this = $(this);
            if ($this.scrollTop() >= this.scrollHeight - $this.height() - 20) {
                requestPostRecursively($this,username,"",postsPerRefresh, useGetposts);
            }
        }
     });
}

function updateFollowingData(followingModalContent, username) {
    followingModalContent.find(".following-screen-name b").text(username);
    loadFollowingIntoList( username, $(followingModalContent[1]) );
}

function clearHashtagProcessed() {
    _hashtagProcessedMap = {};
    _hashtagPendingPosts = [];
}

function requestHashtag(postboard,hashtag,resource, timeoutArgs) {
    dhtget( hashtag, resource, "m",
           function(args, data) {
               processHashtag(args.postboard, args.hashtag, data);
           }, {postboard:postboard,hashtag:hashtag},
           timeoutArgs);
}

function processHashtag(postboard, hashtag, data) {
    if( data && window.location.hash.indexOf(encodeURIComponent(hashtag)) != -1 ) {
        for( var i = data.length-1; i >= 0; i-- ) {
            var userpost = data[i]["userpost"];
            var key = userpost["n"] + ";" + userpost["time"];
            if( !(key in _hashtagProcessedMap) ) {
                _hashtagProcessedMap[key] = true;
                _hashtagPendingPosts.push(data[i]);
            }
        }

        if(!postboard.children().length && !_hashtagPendingPosts.length && hashtag != defaultScreenName)
            postboard.closest("div").find(".no-posts-found-message").show();

        if( _hashtagPendingPosts.length ) {
            if( !postboard.children().length || autoUpdateHashtag ) {
                displayHashtagPending(postboard);
            } else {
                var newTweetsBar = postboard.closest("div").find(".postboard-news");
                newTweetsBar.text(polyglot.t("new_posts", _hashtagPendingPosts.length));
                newTweetsBar.fadeIn("slow");
            }
            postboard.closest("div").find(".no-posts-found-message").hide();
        }
    }
}

function displayHashtagPending(postboard) {
    for( var i = 0; i < _hashtagPendingPosts.length; i++ ) {
        var streamPost = postToElem(_hashtagPendingPosts[i], "original");
        var timePost = _hashtagPendingPosts[i]["userpost"]["time"];

        var streamItems = postboard.children();
        if( streamItems.length == 0) {
            postboard.prepend( streamPost );
        } else {
            var j = 0;
            for( j = 0; j < streamItems.length; j++) {
                var streamItem = streamItems.eq(j);
                var timeItem = streamItem.attr("data-time");
                if( timeItem == undefined ||
                    timePost > parseInt(timeItem) ) {
                    // this post in stream is older, so post must be inserted above
                    streamItem.before(streamPost);
                    break;
                }
            }
            if( j == streamItems.length ) {
                postboard.append( streamPost );
            }
        }
    }
    $.MAL.postboardLoaded();
    _hashtagPendingPosts = [];
}

<commit_msg>Fixing duplicate posts when crolling in profile view<commit_after>// twister_actions.js
// 2013 Miguel Freitas
//
// This file contains some twister "actions" like requesting
// posts from dht, sending posts, replies. It seems to be a
// intermediate layer between twister_io and interface, but
// separation is not clearly defined. Perhaps it would be better
// to get rid of this file altogether.

// global variables

var postsPerRefresh = 10;
var maxExpandPost = 8;
var maxExpandPostTop = 4;
var _hashtagProcessedMap = {};
var _hashtagPendingPosts = [];
var autoUpdateHashtag = false;

// ----------------

function requestRepliedBefore(postLi)
{
    if(postLi.siblings().length >= maxExpandPostTop)
        return;

    var originalPost = postLi.find(".post-data");
    var reply_n = originalPost.attr('data-replied-to-screen-name');
    var reply_k = originalPost.attr('data-replied-to-id');

    if( reply_n != undefined && reply_k != undefined ) {
        dhtget( reply_n, "post" + reply_k, "s",
               function(postLi, postFromJson) {
                   var newStreamPost = postToElem(postFromJson, "related");
                   newStreamPost.hide();
                   postLi.before(newStreamPost);
                   newStreamPost.slideDown("fast");
                   $.MAL.relatedPostLoaded();
                   requestRepliedBefore(newStreamPost);
               }, postLi);
    }
}

function requestRepliesAfter(postLi)
{
    if($.MAL.getExpandedPostsCount(postLi) >= maxExpandPost)
        return;

    var originalPost = postLi.find(".post-data");
    var original_n = originalPost.attr('data-screen-name');
    var original_k = originalPost.attr('data-id');

    if( original_n != undefined && original_k != undefined ) {
        dhtget( original_n, "replies" + original_k, "m", $.MAL.reqRepAfterCB, postLi);
    }
}

function getTopPostOfConversation(postLi, post, postboard) {
    var reply_n;
    var reply_k;

    if (post && typeof(post) !== 'undefined' && "reply" in post["userpost"]) {
        reply_k = post["userpost"]["reply"]["k"];
        reply_n = post["userpost"]["reply"]["n"];
    } else if (postLi && typeof(postLi) !== 'undefined') {
        var originalPost = postLi.find(".post-data");
        reply_n = originalPost.attr('data-replied-to-screen-name');
        reply_k = originalPost.attr('data-replied-to-id');
    }

    if( reply_n != undefined && reply_k != undefined ) {
        dhtget( reply_n, "post" + reply_k, "s",
            function(postLi, postFromJson) {
                getTopPostOfConversation(null, postFromJson, postboard);
            }, postLi);
    } else {
        var newStreamPost;
        if (post)
            newStreamPost = postToElem(post, "related");
        else {
            newStreamPost = postLi.clone(true);
            newStreamPost.removeClass('original');
            newStreamPost.addClass('related');
            newStreamPost.find('.expanded-content').hide();
            newStreamPost.find('.show-more').hide();
        }
        requestRepliesAfterAll(newStreamPost);
        newStreamPost.find('.post-expand').remove();
        newStreamPost.unbind('click');
        newStreamPost.hide();
        postboard.append(newStreamPost);
        newStreamPost.slideDown("fast");
    }
}

function requestRepliesAfterAll(postLi)
{
    var originalPost = postLi.find(".post-data");
    var original_n = originalPost.attr('data-screen-name');
    var original_k = originalPost.attr('data-id');

    if( original_n != undefined && original_k != undefined ) {
        dhtget( original_n, "replies" + original_k, "m", $.MAL.reqRepAfterCB, postLi);
    }
}

function requestRTs(postLi)
{
    var originalPost = postLi.find(".post-data");

    var original_n = originalPost.attr('data-screen-name');
    var original_k = originalPost.attr('data-id');

    if( original_n != undefined && original_k != undefined ) {
        dhtget( original_n, "rts" + original_k, "m",
               function(originalPost, postsFromJson) {

                    if( postsFromJson.length ) {
                        var statCountValue = originalPost.find(".stat-count-value");
                        statCountValue.text( postsFromJson.length );

                        var avatarRow = originalPost.find(".avatar-row");
                        avatarRow.empty();
                        for( var i = 0; i < postsFromJson.length && i < 8; i++) {
                            var n = postsFromJson[i]["userpost"]["n"];
                            var elemUser = $("#avatar-row-template").clone(true);
                            elemUser.removeAttr('id');
                            elemUser.attr('href',$.MAL.userUrl(n));
                            getFullname(n,elemUser.find(".user-name-tooltip"));
                            getAvatar(n,elemUser.find(".size24"));
                            avatarRow.append( elemUser );
                        }

                        originalPost.find(".post-stats").slideDown("fast");
                    }
               }, originalPost);
    }
}

function appendPostToContainer(postFromJson, containerToAppend)
{
    var newStreamPost = postToElem(postFromJson, "original");
    newStreamPost.hide();
    containerToAppend.append( newStreamPost );
    newStreamPost.slideDown("fast");
    $.MAL.postboardLoaded();
}

var profilePostsLoading = false;

function requestPost(containerToAppend,username,resource,cbFunc,cbArgs){

    console.log('dhtget '+username+' '+resource);

    dhtget( username, resource, "s",
        function(args, postFromJson) {
            if( postFromJson ) {

            console.log(postFromJson);

            appendPostToContainer(postFromJson,args.containerToAppend);

            if(args.cbFunc!=undefined) args.cbFunc(args.cbArgs);

            }
        },
        {containerToAppend:containerToAppend,cbFunc:cbFunc,cbArgs:cbArgs}
    );

}

function requestPostRecursively(containerToAppend,username,resource,count,useGetposts)
{
    var max_id = -1;
    if( !resource ) {
        var streamItems = containerToAppend.children();
        if( streamItems.length != 0 ) {
            var lastItem = streamItems.eq(streamItems.length-1);
            resource = "post" + lastItem.find(".post-data").attr("data-lastk");
            max_id = parseInt(lastItem.find(".post-data").attr("data-lastk"));
        }
    }

    profilePostsLoading = true;

    if( useGetposts ) {
        req = {username: username}
        if( max_id != -1 ) {
            req.max_id = max_id;
        }

        twisterRpc("getposts", [count,[req]],
                       function(args, posts) {
                           for( var i = 0; i < posts.length; i++ ) {
                              appendPostToContainer(posts[i],args.containerToAppend);
                           }
                           profilePostsLoading = false;
                       }, {containerToAppend:containerToAppend},
                       function(args, ret) {
                           profilePostsLoading = false;
                       }, {});
    } else {
        dhtget( username, resource, "s",
            function(args, postFromJson) {
               if( postFromJson ) {
                   appendPostToContainer(postFromJson,args.containerToAppend);

                   if( args.count > 1 ) {
                       var userpost = postFromJson["userpost"];
                       var n = userpost["n"];
                       var lastk = userpost["lastk"];
                       if( lastk == undefined )
                           lastk = userpost["k"] - 1; // not true with directmsgs in stream

                       requestPostRecursively(args.containerToAppend, n, "post"+lastk, count-1);
                   } else {
                       profilePostsLoading = false;
                       args.containerToAppend.scroll();
                   }
               } else {
                   profilePostsLoading = false;
               }
           }, {containerToAppend:containerToAppend, count:count} );
    }
}

function newPostMsg(msg, $postOrig) {
    if( lastPostId != undefined ) {
        var params = [defaultScreenName, lastPostId + 1, msg]
        if( $postOrig.length ) {
            params.push($postOrig.attr('data-screen-name'));
            params.push(parseInt($postOrig.attr('data-id')));
        }
        twisterRpc("newpostmsg", params,
                   function(arg, ret) { incLastPostId(); }, null,
                   function(arg, ret) { var msg = ("message" in ret) ? ret.message : ret;
                                        alert(polyglot.t("ajax_error", { error: msg })); }, null);
    } else {
        alert(polyglot.t("Internal error: lastPostId unknown (following yourself may fix!)"));
    }
}

function newRtMsg($postOrig) {
    var content_to_rt = $postOrig.attr('data-content_to_rt');
    var content_to_sigrt = $postOrig.attr('data-content_to_sigrt');

    var sig_userpost = String(content_to_sigrt);
    var userpost = $.evalJSON(String(content_to_rt));
    var rtObj = { sig_userpost :sig_userpost, userpost : userpost };

    if( lastPostId != undefined ) {
        var params = [defaultScreenName, lastPostId+1, rtObj]

        twisterRpc("newrtmsg", params,
                   function(arg, ret) { incLastPostId(); }, null,
                   function(arg, ret) { var msg = ("message" in ret) ? ret.message : ret;
                                        alert(polyglot.t("ajax_error", { error: msg })); }, null);
    } else {
        alert(polyglot.t("Internal error: lastPostId unknown (following yourself may fix!)"));
    }
}

function updateProfileData(profileModalContent, username) {

    //profileModalContent.find("a").attr("href",$.MAL.userUrl(username));
    profileModalContent.filter(".profile-card").attr("data-screen-name", username);
    profileModalContent.find(".profile-screen-name b").text(username);
    profileModalContent.find("a.follow").attr("href", $.MAL.followUrl(username));
    profileModalContent.find("a.direct-messages-with-user").attr("href", $.MAL.dmchatUrl(username));
    profileModalContent.find("a.new-post-to").attr("href", $.MAL.newPostToUrl(username));
    profileModalContent.find("a.mentions-from-user").attr("href", $.MAL.mentionsUrl(username));
    getFullname( username, profileModalContent.find(".profile-name") );
    getLocation( username, profileModalContent.find(".profile-location") );
    getWebpage( username, profileModalContent.find(".profile-url") );
    getBio( username, profileModalContent.find(".profile-bio") );
    getTox( username, profileModalContent.find(".profile-tox") );
    getBitmessage( username, profileModalContent.find(".profile-bitmessage") );
    getAvatar( username, profileModalContent.find(".profile-card-photo") );
    getPostsCount( username,  profileModalContent.find(".posts-count") );
    getFollowers( username, profileModalContent.find(".followers-count") );
    getNumFollowing( username, profileModalContent.find(".following-count") );
    getWhoFollows ( username, profileModalContent.find(".who-follow") );

    profileModalContent.find(".following-count").parent().attr("href", $.MAL.followingUrl(username));

    var postsView = profileModalContent.find(".postboard-posts");

    // try using getposts first. fallback to dht.
    twisterRpc("getposts", [1,[{username: username}]],
                       function(args, posts) {
                           updateProfilePosts(postsView, username, posts.length);
                       }, {},
                       function(args, ret) {
                           updateProfilePosts(postsView, username, false);
                       }, {});
}

function updateProfilePosts(postsView, username, useGetposts) {
    requestPostRecursively(postsView,username,"status",postsPerRefresh, useGetposts);

    postsView.scroll(function(){
        if (!profilePostsLoading) {
            var $this = $(this);
            if ($this.scrollTop() >= this.scrollHeight - $this.height() - 20) {
                requestPostRecursively($this,username,"",postsPerRefresh, useGetposts);
            }
        }
     });
}

function updateFollowingData(followingModalContent, username) {
    followingModalContent.find(".following-screen-name b").text(username);
    loadFollowingIntoList( username, $(followingModalContent[1]) );
}

function clearHashtagProcessed() {
    _hashtagProcessedMap = {};
    _hashtagPendingPosts = [];
}

function requestHashtag(postboard,hashtag,resource, timeoutArgs) {
    dhtget( hashtag, resource, "m",
           function(args, data) {
               processHashtag(args.postboard, args.hashtag, data);
           }, {postboard:postboard,hashtag:hashtag},
           timeoutArgs);
}

function processHashtag(postboard, hashtag, data) {
    if( data && window.location.hash.indexOf(encodeURIComponent(hashtag)) != -1 ) {
        for( var i = data.length-1; i >= 0; i-- ) {
            var userpost = data[i]["userpost"];
            var key = userpost["n"] + ";" + userpost["time"];
            if( !(key in _hashtagProcessedMap) ) {
                _hashtagProcessedMap[key] = true;
                _hashtagPendingPosts.push(data[i]);
            }
        }

        if(!postboard.children().length && !_hashtagPendingPosts.length && hashtag != defaultScreenName)
            postboard.closest("div").find(".no-posts-found-message").show();

        if( _hashtagPendingPosts.length ) {
            if( !postboard.children().length || autoUpdateHashtag ) {
                displayHashtagPending(postboard);
            } else {
                var newTweetsBar = postboard.closest("div").find(".postboard-news");
                newTweetsBar.text(polyglot.t("new_posts", _hashtagPendingPosts.length));
                newTweetsBar.fadeIn("slow");
            }
            postboard.closest("div").find(".no-posts-found-message").hide();
        }
    }
}

function displayHashtagPending(postboard) {
    for( var i = 0; i < _hashtagPendingPosts.length; i++ ) {
        var streamPost = postToElem(_hashtagPendingPosts[i], "original");
        var timePost = _hashtagPendingPosts[i]["userpost"]["time"];

        var streamItems = postboard.children();
        if( streamItems.length == 0) {
            postboard.prepend( streamPost );
        } else {
            var j = 0;
            for( j = 0; j < streamItems.length; j++) {
                var streamItem = streamItems.eq(j);
                var timeItem = streamItem.attr("data-time");
                if( timeItem == undefined ||
                    timePost > parseInt(timeItem) ) {
                    // this post in stream is older, so post must be inserted above
                    streamItem.before(streamPost);
                    break;
                }
            }
            if( j == streamItems.length ) {
                postboard.append( streamPost );
            }
        }
    }
    $.MAL.postboardLoaded();
    _hashtagPendingPosts = [];
}

<|endoftext|>


<commit_before>var version = {
	updateInterval: 600000,
	intervalId: null
}

/**
 * Checks the version and refreshes the page if a new version has been pulled
 */
version.checkVersion = function () {

	$.ajax({
		type: 'GET',
		url: 'githash.php',
		success: function (data) {
			// The githash variable is located in index.php
			if (data && data.gitHash !== gitHash) {
				window.location.reload();
				window.location.href = window.location.href;
			}
		},
		error: function () {

		}
	});

}

version.init = function () {

	this.intervalId = setInterval(function () {
		this.checkVersion();
	}.bind(this), this.updateInterval);

}<commit_msg>Update version.js<commit_after>var version = {
	updateInterval: 600000,
	intervalId: null
}

/**
 * Checks the version and refreshes the page if a new version has been pulled
 */
version.checkVersion = function () {

	$.ajax({
		type: 'GET',
		url: 'controllers/hash.php',
		success: function (data) {
			// The githash variable is located in index.php
			if (data && data.gitHash !== gitHash) {
				window.location.reload();
				window.location.href = window.location.href;
			}
		},
		error: function () {

		}
	});

}

version.init = function () {

	this.intervalId = setInterval(function () {
		this.checkVersion();
	}.bind(this), this.updateInterval);

}
<|endoftext|>


<commit_before>const fs = require('fs');
const parse = require('parse-yarn-lock').default
const semver = require('semver')

// A map of version requirements to a lockfile entry
let locks = parse(fs.readFileSync("/dev/stdin", {encoding: 'utf8'})).object
function getLockEntry(name, requirement) {
    return locks[`${name}@${requirement}`]
}

// A map of resolved versions to their lockfile entries
let versionLockEntry = {}
function getVersionLockEntry(name, version) {
    return versionLockEntry[`${name}@${version}`]
}

// A map of module names to a list of available versions
let packageVersions = {}

// Returns the module name from a version requirement
function getName(key) {
    let nameVer = key.split("@")
    if(key.startsWith("@")) {
        return `@${nameVer[1]}`
    }
    return nameVer[0]
}

// formats a yarn rule to download the module
function formatYarnRule(name, resolvedBy, out, version, deps) {
    if (deps.length === 0) {
        console.log(`
yarn_module(
    name = '${name}',
    out = '${out}',
    resolved_by = '${resolvedBy}',
    version = '${version}',
)`
        )
    } else {
        let formattedDeps = []
        deps.forEach(function (dep) {
            let formattedDep = `        ":_${dep}#download"`
            if(!formattedDeps.includes(formattedDep)) {
                formattedDeps.push(formattedDep)
            }
        })

        console.log(`
yarn_module(
    name = '${name}',
    out = '${out}',
    resolved_by = '${resolvedBy}',
    version = '${version}',
    deps = [
${formattedDeps.sort().join(",\n")},
    ],
)`
        )
    }
}

// Formats a module name and version to the correct please rule name
function getRuleNameForDep(name, version) {
    let versions = packageVersions[name]
    name = name.replace("/", "-")

    // If there is some version for this dependency greater than this version, add the version number to the rule name
    // assuming this will only be depended on by other module rules
    if(versions.some(function (ver) {
        return semver.gt(ver, version)
    })) {
        return `${name}-${version}`
    }
    // Otherwise we are the latest version, use the raw name so others can depend on us easily
    return name
}

// Resolves a dependency requirement to a build rule
function resolveDependency(name, requirement) {
    return getRuleNameForDep(name, locks[`${name}@${requirement}`].version)
}

// Resolves a dependency requirement to a list of modules rules that are transitively required
function resolveTransitiveDependencies(name, requirement, seen = new Set()) {
    // Avoid cycles
    if (seen.has(name + requirement)) {
        return []
    }
    seen.add(name + requirement)

    let lockEntry = getLockEntry(name, requirement)

    let deps = [resolveDependency(name, requirement)]

    if (lockEntry.dependencies !== undefined) {
        let transitiveDeps = Object.keys(lockEntry.dependencies).flatMap(function (name) {
            let requirement = lockEntry.dependencies[name]
            return resolveTransitiveDependencies(name, requirement, seen)
        })
        deps.push(...transitiveDeps)
    }
    if (lockEntry.optionalDependencies !== undefined) {
        let transitiveDeps = Object.keys(lockEntry.optionalDependencies).flatMap(function (name) {
            let requirement = lockEntry.optionalDependencies[name]
            return resolveTransitiveDependencies(name, requirement, seen)
        })
        deps.push(...transitiveDeps)
    }
    return deps
}

function generateYarnRule(name, version, versions) {
    let lockfileEntry = getVersionLockEntry(name, version)
    let ruleName = getRuleNameForDep(name, lockfileEntry.version, versions)

    let deps = []
    if (lockfileEntry.dependencies !== undefined) {
        deps = Object.keys(lockfileEntry.dependencies).flatMap(function (name) {
            let requirement = lockfileEntry.dependencies[name]
            return resolveTransitiveDependencies(name, requirement)
        })
    }
    if (lockfileEntry.optionalDependencies !== undefined) {
        Object.keys(lockfileEntry.optionalDependencies).forEach(function (name) {
            let requirement = lockfileEntry.optionalDependencies[name]
            deps.push(...resolveTransitiveDependencies(name, requirement))
        })
    }
    formatYarnRule(ruleName, lockfileEntry.resolved, `${name}-${lockfileEntry.version}.tgz`.replace("/", "-"), lockfileEntry.version, deps)
}

// Find out what versions of each lib we have installed
Object.keys(locks).forEach(function (key) {
    let name = getName(key)

    let versions = packageVersions[name]
    if (!versions) {
        versions = []
    }

    let version = locks[key].version
    if (!versions.includes(version)) {
        let resolution = `${name}@${version}`
        versionLockEntry[resolution] = locks[key]
        versions.push(version)
    }

    packageVersions[name] = versions
})

// TODO(jpoole): we could deal with files and generate a unit test to check the yarn.lock hash hasn't changed
// since the last time this was run
console.log("# This file was autogenerated by ///pleasings//js/yarn_deps from a yarn.lock file. Do not modify.\n")
console.log("subinclude('///pleasings//js:yarn')")
console.log("package(default_visibility = ['PUBLIC'])\n")

// Generate a yarn rule for each unique module version
Object.keys(packageVersions).forEach(function (name) {
    let versions = packageVersions[name]
    versions.forEach(function (version) {
        generateYarnRule(name, version, versions)
    })
})
<commit_msg>Makes minor adjustments to yarn_deps<commit_after>const fs = require("fs");
const parse = require("parse-yarn-lock").default;
const semver = require("semver");

// A map of version requirements to a lockfile entry
let locks = parse(fs.readFileSync("/dev/stdin", { encoding: "utf8" })).object;
function getLockEntry(name, requirement) {
  return locks[`${name}@${requirement}`];
}

// A map of resolved versions to their lockfile entries
let versionLockEntry = {};
function getVersionLockEntry(name, version) {
  return versionLockEntry[`${name}@${version}`];
}

// A map of module names to a list of available versions
let packageVersions = {};

// Returns the module name from a version requirement
function getName(key) {
  let nameVer = key.split("@");
  if (key.startsWith("@")) {
    return `@${nameVer[1]}`;
  }
  return nameVer[0];
}

// formats a yarn rule to download the module
function formatYarnRule(name, resolvedBy, out, version, deps) {
  if (deps.length === 0) {
    console.log(`
yarn_module(
    name = "${name}",
    out = "${out}",
    resolved_by = "${resolvedBy}",
    version = "${version}",
)`);
  } else {
    let formattedDeps = [];
    deps.forEach(function (dep) {
      let formattedDep = `        ":_${dep}#download"`;
      if (!formattedDeps.includes(formattedDep)) {
        formattedDeps.push(formattedDep);
      }
    });

    console.log(`
yarn_module(
    name = "${name}",
    out = "${out}",
    resolved_by = "${resolvedBy}",
    version = "${version}",
    deps = [
${formattedDeps.sort().join(",\n")},
    ],
)`);
  }
}

// Formats a module name and version to the correct please rule name
function getRuleNameForDep(name, version) {
  let versions = packageVersions[name];
  name = name.replace("/", "--");

  // If there is some version for this dependency greater than this version, add the version number to the rule name
  // assuming this will only be depended on by other module rules
  if (
    versions.some(function (ver) {
      return semver.gt(ver, version);
    })
  ) {
    return `${name}@${version}`;
  }
  // Otherwise we are the latest version, use the raw name so others can depend on us easily
  return name;
}

// Resolves a dependency requirement to a build rule
function resolveDependency(name, requirement) {
  return getRuleNameForDep(name, locks[`${name}@${requirement}`].version);
}

// Resolves a dependency requirement to a list of modules rules that are transitively required
function resolveTransitiveDependencies(name, requirement, seen = new Set()) {
  // Avoid cycles
  if (seen.has(name + requirement)) {
    return [];
  }
  seen.add(name + requirement);

  let lockEntry = getLockEntry(name, requirement);

  let deps = [resolveDependency(name, requirement)];

  if (lockEntry.dependencies !== undefined) {
    let transitiveDeps = Object.keys(lockEntry.dependencies).flatMap(function (
      name
    ) {
      let requirement = lockEntry.dependencies[name];
      return resolveTransitiveDependencies(name, requirement, seen);
    });
    deps.push(...transitiveDeps);
  }
  if (lockEntry.optionalDependencies !== undefined) {
    let transitiveDeps = Object.keys(lockEntry.optionalDependencies).flatMap(
      function (name) {
        let requirement = lockEntry.optionalDependencies[name];
        return resolveTransitiveDependencies(name, requirement, seen);
      }
    );
    deps.push(...transitiveDeps);
  }
  return deps;
}

function generateYarnRule(name, version, versions) {
  let lockfileEntry = getVersionLockEntry(name, version);
  let ruleName = getRuleNameForDep(name, lockfileEntry.version, versions);

  let deps = [];
  if (lockfileEntry.dependencies !== undefined) {
    deps = Object.keys(lockfileEntry.dependencies).flatMap(function (name) {
      let requirement = lockfileEntry.dependencies[name];
      return resolveTransitiveDependencies(name, requirement);
    });
  }
  if (lockfileEntry.optionalDependencies !== undefined) {
    Object.keys(lockfileEntry.optionalDependencies).forEach(function (name) {
      let requirement = lockfileEntry.optionalDependencies[name];
      deps.push(...resolveTransitiveDependencies(name, requirement));
    });
  }
  formatYarnRule(
    ruleName,
    lockfileEntry.resolved,
    `${name}-${lockfileEntry.version}.tgz`.replace("/", "-"),
    lockfileEntry.version,
    deps
  );
}

// Find out what versions of each lib we have installed
Object.keys(locks).forEach(function (key) {
  let name = getName(key);

  let versions = packageVersions[name];
  if (!versions) {
    versions = [];
  }

  let version = locks[key].version;
  if (!versions.includes(version)) {
    let resolution = `${name}@${version}`;
    versionLockEntry[resolution] = locks[key];
    versions.push(version);
  }

  packageVersions[name] = versions;
});

// TODO(jpoole): we could deal with files and generate a unit test to check the yarn.lock hash hasn't changed
// since the last time this was run
console.log(
  "# This file was autogenerated by ///pleasings//js/yarn_deps from a yarn.lock file. Do not modify.\n"
);
console.log("subinclude('///pleasings//js:yarn')");
console.log("package(default_visibility = ['PUBLIC'])\n");

// Generate a yarn rule for each unique module version
Object.keys(packageVersions).forEach(function (name) {
  let versions = packageVersions[name];
  versions.forEach(function (version) {
    generateYarnRule(name, version, versions);
  });
});
<|endoftext|>


<commit_before>// this module loads the tunes
//from the tunes array into the DOM

var Pickin = (function (pickin) {
  'use strict';

  console.log("tunesToDom.js:7 - Pickin = ", Pickin);
  console.log("tunesToDom.js:8 - pickin = ", pickin);

  pickin.tunesToDom = function (tunes) {

    console.log("tunesToDom.js:12 - tunes = ", tunes);

    var tunesTable = document.getElementById("tunes-table");
    var numTunes = tunes.length;
    // var cumTable = "

Id	Artist	Title	Album

";
    var cumTable = "

Id	Title

";

    // populate table
    for (var i = 0; i < numTunes; i++)
    {
      // console.log("tunes[i] = ", i, tunes[i]);
      var tableRow = "
" + tunes[i].TuneId
        // + "	"
        // + tunes[i].artist
        + "	"
        + tunes[i].TuneTitle
        // + "	"
        // + tunes[i].album
        + "
";
      console.log("tableRow[i] = ", i, tunes[i]);
      cumTable += tableRow;

    }
    tunesTable.innerHTML = cumTable;

    // add click handlers to table rows to support selection
    var rows = tunesTable.getElementsByTagName("tr");
    console.log("rows = ", rows);
    for (i = 0; i < rows.length; i++) {
      var currentRow = tunesTable.rows[i];
      var createClickHandler =
        function(row) {
          console.log("inside createClickHandler");
          return function() {
            console.log("clickHandler called");
            var cell = row.getElementsByTagName("td")[0];
            var id = cell.innerHTML;
            alert("id:" + id);
          };
        };
      currentRow.onclick = createClickHandler(currentRow);
    }

  }

  // all done
  return pickin;

})(Pickin || {});<commit_msg>added Delete button to each row in tunes list<commit_after>// this module loads the tunes
//from the tunes array into the DOM

var Pickin = (function (pickin) {
  'use strict';

  console.log("tunesToDom.js:7 - Pickin = ", Pickin);
  console.log("tunesToDom.js:8 - pickin = ", pickin);

  pickin.tunesToDom = function (tunes) {

    console.log("tunesToDom.js:12 - tunes = ", tunes);

    var tunesTable = document.getElementById("tunes-table");
    var numTunes = tunes.length;
    // var cumTable = "

Id	Artist	Title	Album

";
    var cumTable = "

Id	Title

";

    // populate table
    for (var i = 0; i < numTunes; i++)
    {
      // console.log("tunes[i] = ", i, tunes[i]);
      var tableRow = "
" + tunes[i].TuneId
        // + "	"
        // + tunes[i].artist
        + "	"
        + tunes[i].TuneTitle
        + "	"
        + "<button type="button" class="btn btn-default btn-xs">Delete</button>"
        // + tunes[i].album
        + "
";
      console.log("tableRow[i] = ", i, tunes[i]);
      cumTable += tableRow;

    }
    tunesTable.innerHTML = cumTable;

    // now prepare to sensitize the rows
    var rows = tunesTable.getElementsByTagName("tr");
    console.log("rows = ", rows);

    // first add click handlers to table rows in support of selection
    for (i = 0; i < rows.length; i++) {
      var currentRow = tunesTable.rows[i];
      var createClickHandler =
        function(row) {
          console.log("inside createClickHandler");
          return function() {
            console.log("clickHandler called");
            var cell = row.getElementsByTagName("td")[0];
            var id = cell.innerHTML;
            alert("id:" + id);
          };
        };
      currentRow.onclick = createClickHandler(currentRow);
    }

/*
    // and then add "Delete" buttons
    for (i = 0; i < rows.length; i++) {
      var currentRow = tunesTable.rows[i];
      var createClickHandler =
        function(row) {
          console.log("inside createClickHandler");
          return function() {
            console.log("clickHandler called");
            var cell = row.getElementsByTagName("td")[0];
            var id = cell.innerHTML;
            alert("id:" + id);
          };
        };
      currentRow.onclick = createClickHandler(currentRow);
    }
*/

  }

  // all done
  return pickin;

})(Pickin || {});<|endoftext|>


<commit_before>// twitterlib.js (c) 2009 Remy Sharp
// Licensed under the terms of the MIT license.
(function (twitter, container) {
  var ify = function() {
    return {
      link: function(t) {
        return t.replace(/[a-z]+:\/\/[a-z0-9-_]+\.[a-z0-9-_:~%&\?\/.=]+[^:\.,\)\s*$]/ig, function(m) {
          return '' + ((m.length > 25) ? m.substr(0, 24) + '...' : m) + '';
        });
      },
      at: function(t) {
        return t.replace(/(^|[^\w]+)\@([a-zA-Z0-9_]{1,15}(\/[a-zA-Z0-9-_]+)*)/g, function(m, m1, m2) {
          return m1 + '@' + m2 + '';
        });
      },
      hash: function(t) {
        return t.replace(/(^|[^\w'"]+)\#([a-zA-Z0-9_]+)/g, function(m, m1, m2) {
          return m1 + '#' + m2 + '';
        });
      },
      clean: function(tweet) {
        return this.hash(this.at(this.link(tweet)));
      }
    };
  }();

  var time = function () {
    return {
      time: function (date) {
        var hour = date.getHours(),
            min = date.getMinutes() + "",
            ampm = 'AM';

        if (hour == 0) {
          hour = 12;
        } else if (hour == 12) {
          ampm = 'PM';
        } else if (hour > 12) {
          hour -= 12;
          ampm = 'PM';
        }

        if (min.length == 1) {
          min = '0' + min;
        }

        return hour + ':' + min + ' ' + ampm;
      },
      date: function (date) {
        var ds = date.toDateString().split(/ /),
            mon = monthDict[date.getMonth()],
            day = date.getDate()+'',
            dayi = ~~(day),
            year = date.getFullYear(),
            thisyear = (new Date()).getFullYear(),
            th = 'th';

        // anti-'th' - but don't do the 11th, 12th or 13th
        if ((dayi % 10) == 1 && day.substr(0, 1) != '1') {
          th = 'st';
        } else if ((dayi % 10) == 2 && day.substr(0, 1) != '1') {
          th = 'nd';
        } else if ((dayi % 10) == 3 && day.substr(0, 1) != '1') {
          th = 'rd';
        }

        if (day.substr(0, 1) == '0') {
          day = day.substr(1);
        }

        return mon + ' ' + day + th + (thisyear != year ? ', ' + year : '');
      },
      relative: function (time_value) {
        var values = time_value.split(" "),
            parsed_date = Date.parse(values[1] + " " + values[2] + ", " + values[5] + " " + values[3]),
            date = new Date(parsed_date),
            relative_to = (arguments.length > 1) ? arguments[1] : new Date(),
            delta = ~~((relative_to.getTime() - parsed_date) / 1000),
            r = '';

        delta = delta + (relative_to.getTimezoneOffset() * 60);

        if (delta < 5) {
          r = 'less than 5 seconds ago';
        } else if (delta < 30) {
          r = 'half a minute ago';
        } else if (delta < 60) {
          r = 'less than a minute ago';
        } else if (delta < 120) {
          r = '1 minute ago';
        } else if (delta < (45*60)) {
          r = (~~(delta / 60)).toString() + ' minutes ago';
        } else if (delta < (2*90*60)) { // 2* because sometimes read 1 hours ago
          r = 'about 1 hour ago';
        } else if (delta < (24*60*60)) {
          r = 'about ' + (~~(delta / 3600)).toString() + ' hours ago';
        } else {
          if (delta < (48*60*60)) {
            r = this.time(date) + ' yesterday';
          } else {
            r = this.time(date) + ' ' + this.date(date);
          }
        }

        return r;
      }
    };
  }();

  var filter = (function () {
   return {
     match: function (tweet, search, includeHighlighted) {
       var i = 0, s = '', text = tweet.text.toLowerCase();

       if (typeof search == "string") {
         search = this.format(search);
       }

       // loop ignore first
       if (search['not'].length) {
         for (i = 0; i < search['not'].length; i++) {
           if (text.indexOf(search['not'][i]) !== -1) {
             return false;
           }
         }
       }

       if (search['and'].length) {
         for (i = 0; i < search['and'].length; i++) {
           s = search['and'][i];

           if (s.substr(0, 3) === 'to:') {
             if (!RegExp('^@' + s.substr(3)).test(text)) {
               return false;
             }
           } else if (s.substr(0, 5) == 'from:') {
             if (tweet.user.screen_name !== s.substr(5)) {
               return false;
             }
           } else if (text.indexOf(s) === -1) {
             return false;
           }
         }
       }

       if (search['or'].length) {
         for (i = 0; i < search['or'].length; i++) {
           s = search['or'][i];

           if (s.substr(0, 3) === 'to:') {
             if (RegExp('^@' + s.substr(3)).test(text)) {
               return true;
             }
           } else if (s.substr(0, 5) == 'from:') {
             if (tweet.user.screen_name === s.substr(5)) {
               return true;
             }
           } else if (text.indexOf(search['or'][i]) !== -1) {
             return true;
           }
         }
       } else if (search['and'].length) {
         return true;
       }

       return false;
     },

     format: function (search, caseSensitive) {
       // search can match search.twitter.com format
       var blocks = [], ors = [], ands = [], i = 0, negative = [], since = '', until = '';

       search.replace(/(["'](.*?)["']|\S+\b)/g, function (m) {
         m = m.replace(/^["']+|["']+$/g, '');
         blocks.push(m);
       });

       for (i = 0; i < blocks.length; i++) {
         if (blocks[i] == 'OR' && blocks[i+1]) {
           ors.push(blocks[i-1].toLowerCase());
           ors.push(blocks[i+1].toLowerCase());
           i++;
           ands.pop(); // remove the and test from the last loop
         } else if (blocks[i].substr(0, 1) == '-') {
           negative.push(blocks[i].substr(1).toLowerCase());
         } else {
           ands.push(blocks[i].toLowerCase());
         }
       }

       return {
         'or' : ors,
         'and' : ands,
         'not' : negative
       };
     },

     // tweets typeof Array
     matchTweets: function (tweets, search, includeHighlighted) {
       var updated = [], tmp, i = 0;

       if (typeof search == 'string') {
         search = this.format(search);
       }

       for (i = 0; i < tweets.length; i++) {
         if (this.match(tweets[i], search, includeHighlighted)) {
           updated.push(tweets[i]);
         }
       }

       return updated;
     }
   };
  })();

  var guid = +new Date,
      head = document.getElementsByTagName('head')[0],
      doc = document,
      last = {}, // memorisation object for the next method
      URLS = {
        search: 'http://search.twitter.com/search.json?q=%search%&page=%page|1%&rpp=%limit|100%',
        timeline: 'http://twitter.com/statuses/user_timeline/%user%.json?count=%limit|200%&page=%page|1%',
        listTimeline: 'http://api.twitter.com/1/%user%/lists/%list%/statuses.json?page=%page|1%&per_page=%limit|200%',
        favs: 'http://twitter.com/favorites/%user%.json?page=%page|1%'
      },
      undefined;

  function load(url, options, callback) {
    var script = doc.createElement('script'), match = null;
    if (options == undefined) options = {};
    guid++;

    window[twitter + guid] = (function (guid, options) { // args are now private and static
      return function (tweets) {
        // remove original script include
        head.removeChild(doc.getElementById(twitter + guid));

        if (tweets.results) {
          tweets = tweets.results;
        }

        if (options.filter) {
          tweets = filter.matchTweets(tweets, options.filter);
        }
        callback.call(container[twitter], tweets, options);
      };
    })(guid, options);

    match = url.match(/callback=(.*?)/);
    if (match != null && match.length > 1) {
      window[match[1]] = window[twitter + guid];
    } else {
      url += '&callback=' + twitter + guid;
    }

    script.src = url;
    script.id = twitter + guid;
    head.appendChild(script);
  }

  function getUrl(type, options) {
    return URLS[type].replace(/%(.*?)(\|.*?)?%/g, function (a, key, def) {
      return options[key] === undefined ? def.substr(1) : options[key];
    });
  }

  function normaliseArgs(options, callback) {
    if (typeof options == 'function') {
      callback = options;
      options = {};
    }
    if (options === undefined) options = {};
    options.page = options.page || 1;
    options.callback = callback;
    // don't bother returning the options since they're being modified
    return options;
  }

  function setLast(method, arg, options) {
    last = {
      method: method,
      arg: arg,
      options: options,
      callback: options.callback,
      page: options.page || 1
    };
  }

  container[twitter] = {
    // search is an exception case
    search: function (q, options, callback) {
      options = normaliseArgs(options, callback);

      if (options.filter) {
        // remove filter as it conflicts with content
        delete options.filter;
      }

      options.search = encodeURIComponent(q);

      setLast('search', q, options);
      if (options.callback) load(getUrl('search', options), options, options.callback);
      return this;
    },
    status: function (user, options, callback) { // alias function
      options = normaliseArgs(options, callback);
      options.limit = 1;
      setLast('status', user, options); // setting after limit = 1 to keep this intact
      return this.timeline(user, options, options.callback);
    },
    timeline: function (user, options, callback) {
      options = normaliseArgs(options, callback);
      setLast('timeline', user, options);
      options.user = user;
      if (options.callback) load(getUrl('timeline', options), options, options.callback);
      return this;
    },
    list: function (list, options, callback) {
      var parts = list.split('/');
      options = normaliseArgs(options, callback);
      setLast('list', list, options);
      options.user = parts[0];
      options.list = parts[1];
      if (callback) load(getUrl('listTimeline', options), options, options.callback);
      return this;
    },
    favs: function (user, options, callback) {
      options = normaliseArgs(options, callback);
      setLast('favs', user, options);
      options.user = user;
      if (callback) load(getUrl('favs', options), options, options.callback);
      return this;
    },
    next: function () {
      if (last.method) {
        last.page++;
        last.options.page = last.page;
        this[last.method](last.arg, last.options, last.callback);
      } // else we won't do anything
      return this;
    },

    // appending on pre-existing utilities
    time: time,
    ify: ify,
    filter: filter,
    debug: function (urls) {
      for (var url in urls) {
        URLS[url] = urls[url];
      }
      return this;
    }
  };
})('twitterlib', this);<commit_msg>Fixed the not search when this is only filter param<commit_after>// twitterlib.js (c) 2009 Remy Sharp
// Licensed under the terms of the MIT license.
(function (twitter, container) {
  var ify = function() {
    return {
      link: function(t) {
        return t.replace(/[a-z]+:\/\/[a-z0-9-_]+\.[a-z0-9-_:~%&\?\/.=]+[^:\.,\)\s*$]/ig, function(m) {
          return '' + ((m.length > 25) ? m.substr(0, 24) + '...' : m) + '';
        });
      },
      at: function(t) {
        return t.replace(/(^|[^\w]+)\@([a-zA-Z0-9_]{1,15}(\/[a-zA-Z0-9-_]+)*)/g, function(m, m1, m2) {
          return m1 + '@' + m2 + '';
        });
      },
      hash: function(t) {
        return t.replace(/(^|[^\w'"]+)\#([a-zA-Z0-9_]+)/g, function(m, m1, m2) {
          return m1 + '#' + m2 + '';
        });
      },
      clean: function(tweet) {
        return this.hash(this.at(this.link(tweet)));
      }
    };
  }();

  var time = function () {
    return {
      time: function (date) {
        var hour = date.getHours(),
            min = date.getMinutes() + "",
            ampm = 'AM';

        if (hour == 0) {
          hour = 12;
        } else if (hour == 12) {
          ampm = 'PM';
        } else if (hour > 12) {
          hour -= 12;
          ampm = 'PM';
        }

        if (min.length == 1) {
          min = '0' + min;
        }

        return hour + ':' + min + ' ' + ampm;
      },
      date: function (date) {
        var ds = date.toDateString().split(/ /),
            mon = monthDict[date.getMonth()],
            day = date.getDate()+'',
            dayi = ~~(day),
            year = date.getFullYear(),
            thisyear = (new Date()).getFullYear(),
            th = 'th';

        // anti-'th' - but don't do the 11th, 12th or 13th
        if ((dayi % 10) == 1 && day.substr(0, 1) != '1') {
          th = 'st';
        } else if ((dayi % 10) == 2 && day.substr(0, 1) != '1') {
          th = 'nd';
        } else if ((dayi % 10) == 3 && day.substr(0, 1) != '1') {
          th = 'rd';
        }

        if (day.substr(0, 1) == '0') {
          day = day.substr(1);
        }

        return mon + ' ' + day + th + (thisyear != year ? ', ' + year : '');
      },
      relative: function (time_value) {
        var values = time_value.split(" "),
            parsed_date = Date.parse(values[1] + " " + values[2] + ", " + values[5] + " " + values[3]),
            date = new Date(parsed_date),
            relative_to = (arguments.length > 1) ? arguments[1] : new Date(),
            delta = ~~((relative_to.getTime() - parsed_date) / 1000),
            r = '';

        delta = delta + (relative_to.getTimezoneOffset() * 60);

        if (delta < 5) {
          r = 'less than 5 seconds ago';
        } else if (delta < 30) {
          r = 'half a minute ago';
        } else if (delta < 60) {
          r = 'less than a minute ago';
        } else if (delta < 120) {
          r = '1 minute ago';
        } else if (delta < (45*60)) {
          r = (~~(delta / 60)).toString() + ' minutes ago';
        } else if (delta < (2*90*60)) { // 2* because sometimes read 1 hours ago
          r = 'about 1 hour ago';
        } else if (delta < (24*60*60)) {
          r = 'about ' + (~~(delta / 3600)).toString() + ' hours ago';
        } else {
          if (delta < (48*60*60)) {
            r = this.time(date) + ' yesterday';
          } else {
            r = this.time(date) + ' ' + this.date(date);
          }
        }

        return r;
      }
    };
  }();

  var filter = (function () {
    return {
     match: function (tweet, search, includeHighlighted) {
       var i = 0, s = '', text = tweet.text.toLowerCase();

       if (typeof search == "string") {
         search = this.format(search);
       }

       // loop ignore first
       if (search['not'].length) {
         for (i = 0; i < search['not'].length; i++) {
           if (text.indexOf(search['not'][i]) !== -1) {
             return false;
           }
         }

         if (!search['and'].length && !search['or'].length) {
           return true;
         }
       }

       if (search['and'].length) {
         for (i = 0; i < search['and'].length; i++) {
           s = search['and'][i];

           if (s.substr(0, 3) === 'to:') {
             if (!RegExp('^@' + s.substr(3)).test(text)) {
               return false;
             }
           } else if (s.substr(0, 5) == 'from:') {
             if (tweet.user.screen_name !== s.substr(5)) {
               return false;
             }
           } else if (text.indexOf(s) === -1) {
             return false;
           }
         }
       }

       if (search['or'].length) {
         for (i = 0; i < search['or'].length; i++) {
           s = search['or'][i];

           if (s.substr(0, 3) === 'to:') {
             if (RegExp('^@' + s.substr(3)).test(text)) {
               return true;
             }
           } else if (s.substr(0, 5) == 'from:') {
             if (tweet.user.screen_name === s.substr(5)) {
               return true;
             }
           } else if (text.indexOf(search['or'][i]) !== -1) {
             return true;
           }
         }
       } else if (search['and'].length) {
         return true;
       }

       return false;
     },

     format: function (search, caseSensitive) {
       // search can match search.twitter.com format
       var blocks = [], ors = [], ands = [], i = 0, negative = [], since = '', until = '';

       search.replace(/(["'](.*?)["']|\S+\b)/g, function (m) {
         m = m.replace(/^["']+|["']+$/g, '');
         blocks.push(m);
       });

       for (i = 0; i < blocks.length; i++) {
         if (blocks[i] == 'OR' && blocks[i+1]) {
           ors.push(blocks[i-1].toLowerCase());
           ors.push(blocks[i+1].toLowerCase());
           i++;
           ands.pop(); // remove the and test from the last loop
         } else if (blocks[i].substr(0, 1) == '-') {
           negative.push(blocks[i].substr(1).toLowerCase());
         } else {
           ands.push(blocks[i].toLowerCase());
         }
       }

       return {
         'or' : ors,
         'and' : ands,
         'not' : negative
       };
     },

     // tweets typeof Array
     matchTweets: function (tweets, search, includeHighlighted) {
       var updated = [], tmp, i = 0;

       if (typeof search == 'string') {
         search = this.format(search);
       }

       for (i = 0; i < tweets.length; i++) {
         if (this.match(tweets[i], search, includeHighlighted)) {
           updated.push(tweets[i]);
         }
       }

       return updated;
     }
   };
  })();

  var guid = +new Date,
      head = document.getElementsByTagName('head')[0],
      doc = document,
      last = {}, // memorisation object for the next method
      URLS = {
        search: 'http://search.twitter.com/search.json?q=%search%&page=%page|1%&rpp=%limit|100%',
        timeline: 'http://twitter.com/statuses/user_timeline/%user%.json?count=%limit|200%&page=%page|1%',
        listTimeline: 'http://api.twitter.com/1/%user%/lists/%list%/statuses.json?page=%page|1%&per_page=%limit|200%',
        favs: 'http://twitter.com/favorites/%user%.json?page=%page|1%'
      },
      undefined;

  function load(url, options, callback) {
    var script = doc.createElement('script'), match = null;
    if (options == undefined) options = {};
    guid++;

    window[twitter + guid] = (function (guid, options) { // args are now private and static
      return function (tweets) {
        // remove original script include
        head.removeChild(doc.getElementById(twitter + guid));

        if (tweets.results) {
          tweets = tweets.results;
        }

        if (options.filter) {
          tweets = filter.matchTweets(tweets, options.filter);
        }
        callback.call(container[twitter], tweets, options);
      };
    })(guid, options);

    match = url.match(/callback=(.*?)/);
    if (match != null && match.length > 1) {
      window[match[1]] = window[twitter + guid];
    } else {
      url += '&callback=' + twitter + guid;
    }

    script.src = url;
    script.id = twitter + guid;
    head.appendChild(script);
  }

  function getUrl(type, options) {
    return URLS[type].replace(/%(.*?)(\|.*?)?%/g, function (a, key, def) {
      return options[key] === undefined ? def.substr(1) : options[key];
    });
  }

  function normaliseArgs(options, callback) {
    if (typeof options == 'function') {
      callback = options;
      options = {};
    }
    if (options === undefined) options = {};
    options.page = options.page || 1;
    options.callback = callback;
    // don't bother returning the options since they're being modified
    return options;
  }

  function setLast(method, arg, options) {
    last = {
      method: method,
      arg: arg,
      options: options,
      callback: options.callback,
      page: options.page || 1
    };
  }

  container[twitter] = {
    // search is an exception case
    search: function (q, options, callback) {
      options = normaliseArgs(options, callback);

      if (options.filter) {
        // remove filter as it conflicts with content
        delete options.filter;
      }

      options.search = encodeURIComponent(q);

      setLast('search', q, options);
      if (options.callback) load(getUrl('search', options), options, options.callback);
      return this;
    },
    status: function (user, options, callback) { // alias function
      options = normaliseArgs(options, callback);
      options.limit = 1;
      setLast('status', user, options); // setting after limit = 1 to keep this intact
      return this.timeline(user, options, options.callback);
    },
    timeline: function (user, options, callback) {
      options = normaliseArgs(options, callback);
      setLast('timeline', user, options);
      options.user = user;
      if (options.callback) load(getUrl('timeline', options), options, options.callback);
      return this;
    },
    list: function (list, options, callback) {
      var parts = list.split('/');
      options = normaliseArgs(options, callback);
      setLast('list', list, options);
      options.user = parts[0];
      options.list = parts[1];
      if (callback) load(getUrl('listTimeline', options), options, options.callback);
      return this;
    },
    favs: function (user, options, callback) {
      options = normaliseArgs(options, callback);
      setLast('favs', user, options);
      options.user = user;
      if (callback) load(getUrl('favs', options), options, options.callback);
      return this;
    },
    next: function () {
      if (last.method) {
        last.page++;
        last.options.page = last.page;
        this[last.method](last.arg, last.options, last.callback);
      } // else we won't do anything
      return this;
    },

    // appending on pre-existing utilities
    time: time,
    ify: ify,
    filter: filter,
    debug: function (urls) {
      for (var url in urls) {
        URLS[url] = urls[url];
      }
      return this;
    }
  };
})('twitterlib', this);<|endoftext|>