<commit_before>Handler = {
    /**
     * Display a user's message
     */
    event_message: function handle_event_message(e){
        var rooms = JSON.parse($('#rooms').val());
        var data = JSON.parse(e.data);

        /**
         * don't display the message if the user is not on the room
         * where the message comes from
         */
        if(-1 === rooms.indexOf(data['room'])){
            return;
        }

        var lineDiv = document.createElement('div');

        var timeSpan = document.createElement('span');
        var nickSpan = document.createElement('span');
        var msgSpan = document.createElement('span');

        timeSpan.innerHTML = get_current_time() + ' ';
        timeSpan.className = 'time';

        nickSpan.innerHTML = data['nick'] + ': ';
        nickSpan.className = 'nick';

        msgSpan.innerHTML = data['message'] + ' ';
        msgSpan.className = 'msg';

        if(is_mention(data['message'])){
            lineDiv.className = 'line alert alert-info';
        }
        else{
            lineDiv.className = 'line';
        }

        lineDiv.appendChild(timeSpan);
        lineDiv.appendChild(nickSpan);
        lineDiv.appendChild(msgSpan);

        $('#' + data['room']).append(lineDiv);

        notify_activity(data['room']);

        $('div#content').trigger('update_scrollbar');
    },

    /**
     * Update the user list when someone joins the chat
     */
    event_users: function handle_event_users(e){
        var users = $.parseJSON(e.data);
        var usersDiv = $('#user-list')[0];

        usersDiv.innerHTML = '';

        for(i=0; i<users.length-1; i++){
            usersDiv.innerHTML += users[i] + ', ';
        }

        usersDiv.innerHTML += users[users.length-1];
    },

    /**
     * Callback to respond to ping events via AJAX
     */
    event_ping: function handle_event_ping(e){
        $.post($SCRIPT_ROOT + '/_pong', 'PONG!');
    },

    /**
     * Callback that handles message sending errors
     */
    publish_error: function handle_publish_error(e){
        if("" != e.responseText){
            var lineDiv = document.createElement('div');

            lineDiv.className = 'line alert alert-error';
            lineDiv.innerHTML = e.responseText;

            $('.tab-pane.active').append(lineDiv);
            $('div#content').trigger('update_scrollbar');
        }
        else{
            show_error_dialog();
        }
    },

    /**
     * Callback for handling SSE errors
     */
    event_error: function handle_event_error(e){
        this.close(); //here, `this` refers to `stream`
        show_error_dialog();
    },

    /**
     * Callback that handles the join errors
     */
    join_error: function handle_join_error(e){
        var lineDiv = document.createElement('div');

        lineDiv.className = 'line alert alert-error';
        lineDiv.innerHTML = e.responseText;

        $('.tab-pane.active').append(lineDiv);
        $('div#content').trigger('update_scrollbar');
    },

    /**
     * Callback for handling the leave error (these occur when leaving rooms)
     */
    leave_room_error: function handle_leave_room_error(e){
        //if the status is 404 then the user closed his last room, so we logged
        //him out
        if(404 === e.status){
            window.location.replace($SCRIPT_ROOT);
        }
        else{
            var lineDiv = document.createElement('div');

            lineDiv.className = 'line alert alert-error';
            lineDiv.innerHTML = e.responseText;

            $('.tab-pane.active').append(lineDiv);
            $('div#content').trigger('update_scrollbar');
        }
    },
    /**
     * Move the scrollbar at the bottom in order to see the latest message
     */
    update_scrollbar: function handle_updatescrollbar(e){
        $('#content').scrollTop($('#content')[0].scrollHeight + 42);
    }
}

/**
 * Create an AJAX request that will publish the message the user wants to
 * transmit
 */
function publish_message(e){
    e.preventDefault();
    $.post($SCRIPT_ROOT + '/_publish_message', {'message': $('#text').val(),
        'room': $('.tab-pane.active').attr('id')}).fail(Handler.publish_error);
    $('#text').val('');
    $('#text').focus();
}

/**
 * Leave a room
 *
 * An AJAX request is made to the server which will respond with the new room
 * list, also the tabs will be updated
 *
 * @param string room the clicked room (the one the user wants to leave)
 * @param string active_room the room currently active
 */
function leave_room(room, active_room){
    var room_name = room.children().attr("href").slice(1);

    $.post($SCRIPT_ROOT + '/_leave_room', {'room': room_name})
        .fail(Handler.leave_room_error)
        .success(function(e){
            var active_room_name = active_room.children().attr("href").slice(1);

            if(active_room_name == room_name){

                var next_room = active_room.next();
                if(next_room.length){ //the current tab is not the last, go right
                    active_room.next().attr('class', 'active');
                }
                else{ //current tab is the last, move to left
                    active_room.prev().attr('class', 'active');
                }
            }
            else{
                active_room.attr('class', 'active');
            }

            $(room).remove();
            $('#' + room_name).remove();

            $('#rooms').val(e);
        });
}

/**
 * Display the rooms as tabs
 *
 * This function is able to decide whether it needs to create all the tabs or
 * just some of the tabs by array difference
 */
function display_rooms(){
    var rooms = JSON.parse($('#rooms').val());

    var close_btn = $('<button>').attr({
        class: 'close',
        onclick: 'leave_room($(this).parent().parent(), $("li.active"))'
    }).html(' ×');

    /**
     * starting from one when joining rooms at login,
     * because the first room is displayed outside the for loop
     * since I need to set some classes to "active"
     */
    var pos = 1;

    if($('.nav.nav-tabs').length == 0){ //the user joins the rooms at login
        $('#chat').append(
            $('
').attr({class: 'nav nav-tabs'}).append(
                $('
').attr({class: 'active'}).append(
                    $('').attr({
                        href: '#' + rooms[0],
                        'data-toggle': 'tab',
                    }).html(rooms[0] + close_btn.prop('outerHTML'))
                )
            )
        );

        $('#chat').append(
            $('
').attr({class: 'tab-content', id: 'content'}).append(
                $('
').attr({
                    class: 'tab-pane active',
                    id: rooms[0],
                })
            )
        );
    }
    else{ //the user joins the rooms after he logged in, so we display only the
          //new rooms since the rest are already displayed
        var current_rooms = get_current_rooms();

        rooms = rooms.filter(function(i){
            return current_rooms.indexOf(i) < 0;
        });

        /**
         * every room should be processed because if we join we don't
         * have to change the active room
         */
        pos = 0;
    }

    for(i=pos; i<rooms.length; i++){
        $('.nav.nav-tabs').append(
            $('
').append(
                $('').attr({
                    href: '#' + rooms[i],
                    'data-toggle': 'tab',
                }).html(rooms[i] + close_btn.prop('outerHTML'))
            )
        );

        $('.tab-content').append(
            $('
').attr({
                class: 'tab-pane',
                id: rooms[i],
            })
        );

        if(0 == pos && i == rooms.length - 1){
            options = {
                to: $('a[href="' + '#' + rooms[i] + '"]'),
                className: 'ui-effects-transfer'
            };
            $('[name="join"]').effect("transfer", options, 700);
        }
    }
}

/**
 * Get the currently joined rooms
 *
 * @return array of room names
 */
function get_current_rooms(){
    var current_rooms = [];
    $('.tab-pane').each(function(){
        current_rooms.push($(this).attr('id'))
    });

    return current_rooms;
}

/**
 * Create an AJAX request to join more rooms after logging in
 */
function join_rooms(e){
    e.preventDefault();
    $.post($SCRIPT_ROOT + '/_join_rooms', {'join_rooms': $('#join_rooms').val()})
        .fail(Handler.join_error)
        .success(function(e){
            $('#rooms').val(e);
            display_rooms();
            $('#join_rooms').val('');
        });
}

/**
 * Initialize the elements that compose the chat, the EventSource (for the Server Sent
 * Events) and display the rooms the user joined
 */
function load_chat(){
    var stream = new EventSource($SCRIPT_ROOT + '/_sse_stream');

    stream.onmessage = Handler.event_message;
    stream.onerror = Handler.event_error;

    stream.addEventListener('users', Handler.event_users);
    stream.addEventListener('ping', Handler.event_ping);

    display_rooms();
    $('#text').focus();
}

/**
 * Get the current time
 *
 * @return string the current time in HH:MM format
 */
function get_current_time(){
    var currentTime = new Date();

    var h = currentTime.getHours();
    var m = currentTime.getMinutes();

    if(h < 10){
        h = '0' + h;
    }

    if(m < 10){
        m = '0' + m;
    }

    return h + ':' + m;
}

/**
 * Check if the user is mentioned in the message
 *
 * A user is mentioned when his nick appears as a word in a message:
 * "hello @nick"
 * "nick, hello"
 * "nick: hi"
 * "how are you nick?"
 *
 * The nick may be surrounded by punctuation, symbols or spaces, but not other
 * letters, so for the following message this function will return false:
 * "hello foonick"
 *
 * @param message the message that is checked for mentions
 *
 * @return bool true if the user is mentioned, else false
 */
function is_mention(message){
    var nick = $('label[for="text"]').html().slice(0, -1);
    var pos = message.indexOf(nick);

    while(-1 !== pos){
        if((pos === 0 || (pos - 1 >= 0 && !is_letter(message[pos-1]))) &&
           (pos+nick.length == message.length ||
                (pos+nick.length < message.length &&
                !is_letter(message[pos+nick.length])
                )
           )){
            return true;
        }

        message = message.slice(pos+nick.length);
        pos = message.indexOf(nick);
    }

    return false;
}

/**
 * Check if a character is a letter
 *
 * @param l string this should be a single character to be checked
 *
 * @return bool true if l is a letter, else false
 */
function is_letter(l){
    if((l >= 'a' && l <= 'z') || (l >= 'A' && l <= 'Z')){
        return true;
    }

    return false;
}

/**
 * Notify the user that there is activity on the chat
 *
 * @param string room_name room's name where is activity
 */
function notify_activity(room_name){
    //create an activity notice if the tab is not focused
    var a = $('a[href="#' + room_name + '"]');
    if('active' !== a.parent().attr('class')){
        if(0 === $('#icon-' + room_name).length){ //prepend just one icon
            a.prepend($('').attr({
                class: 'icon-comment',
                id: 'icon-' + room_name,
            }));
        }
    }

    if(away){
        Notificon("!", {
            font: '10px arial',
            color: '#ffffff',
            stroke: 'rgba(240, 61, 37, 1)',
            width: 7,
        });
    }
}

/**
 * Add horizontal rules to each object in an array
 *
 * If there are existing
 elements they are removed and a new one is added
 *
 * @param array obj array of objects to add a horizontal rule to
 */
function add_hr(obj){
    for(i=0; i<obj.length; i++){
        if($(obj[i]).has('div.line').length){
            obj[i].innerHTML = obj[i].innerHTML.split('
').join('') + '
';
        }
    }

    $('div#content').trigger('update_scrollbar');
}

/**
 * Adjust the height of the content where the messages appear in order to keep
 * the whole app on the screen.
 */
function adjust_blocks() {
    var win_h = $(window).height();
    var footer_h = $('p.footer').outerHeight(true);
    var box_h = $('div.well').outerHeight(true);
    var tabs_h = $('.nav.nav-tabs').outerHeight(true);
    var body_margins_h = parseInt($('body').css('margin-top')) +
        parseInt($('body').css('margin-bottom'));

    $('#content').css('height', win_h-box_h-footer_h-tabs_h-body_margins_h);
}

/**
 * If the user presses Enter while he's on the message textfield (#text) the
 * message is sent, if he presses Ctrl + Enter a newline is added to the message
 *
 * If the user presses Enter while on the rooms textfield (#join_rooms) he will
 * join them
 */
$(document).keypress(function(e){
    if('join_rooms' == e.target.id && 13 == e.which){
        join_rooms(e);
    }
    else if('text' == e.target.id && (10 == e.which
            //this part is needed in order to solve browser incompatibilities
            || 13 == e.which && e.ctrlKey)){
        $('#text').val($('#text').val() + '\n');
        return false;
    }
    else if('text' == e.target.id && 13 == e.which){
        publish_message(e);
    }
});

/**
 * Display an error dialog
 *
 * When this dialog is shown then the user is advied to reconnect because some
 * connection error occurred
 */
function show_error_dialog(){
    $('#error-dialog').dialog({
        resizable: false,
        modal: true,
        draggable: false,
        closeOnEscape: false,
        buttons: {
            'OK': function(){
                window.location.replace($SCRIPT_ROOT + '/quit');
            }
        },
        open: function(event, ui){
            $('.ui-dialog-buttonpane button').each(function(){
                $(this).attr('class', 'btn btn-danger');
            });
        },
    });
}

//Global initializations
load_chat();

//clear the activity notice upon clicking on the tab
$('a[data-toggle="tab"]').on('show', function(e){
    $('#icon-' + $(e.target).attr('href').slice(1)).remove();

    //add a
 to the last tab in order to mark the activity on that room
    //since the user moved away
    if(typeof e.relatedTarget !== 'undefined'){
        add_hr($('div' + $(e.relatedTarget).attr('href')));
    }
});

//after the tab is shown scroll down
$('a[data-toggle="tab"]').on('shown', function(e){
    Handler.update_scrollbar();
});

//if the browser or the browser's tab is not focused display a Notificon
$(window).focus(function(){
    away = false;
    Notificon('');
});

$(window).blur(function(){
    away = true;

    add_hr($('div.tab-pane'));
});

var away = false;

$('[name="send"]').click(publish_message);
$('[name="join"]').click(join_rooms);

$('div#content').bind('update_scrollbar', Handler.update_scrollbar);

//TODO move other handlers into Handler

$(document).ready(adjust_blocks);
$(window).resize(adjust_blocks);
<commit_msg>focusing the message textfield after changing tabs<commit_after>Handler = {
    /**
     * Display a user's message
     */
    event_message: function handle_event_message(e){
        var rooms = JSON.parse($('#rooms').val());
        var data = JSON.parse(e.data);

        /**
         * don't display the message if the user is not on the room
         * where the message comes from
         */
        if(-1 === rooms.indexOf(data['room'])){
            return;
        }

        var lineDiv = document.createElement('div');

        var timeSpan = document.createElement('span');
        var nickSpan = document.createElement('span');
        var msgSpan = document.createElement('span');

        timeSpan.innerHTML = get_current_time() + ' ';
        timeSpan.className = 'time';

        nickSpan.innerHTML = data['nick'] + ': ';
        nickSpan.className = 'nick';

        msgSpan.innerHTML = data['message'] + ' ';
        msgSpan.className = 'msg';

        if(is_mention(data['message'])){
            lineDiv.className = 'line alert alert-info';
        }
        else{
            lineDiv.className = 'line';
        }

        lineDiv.appendChild(timeSpan);
        lineDiv.appendChild(nickSpan);
        lineDiv.appendChild(msgSpan);

        $('#' + data['room']).append(lineDiv);

        notify_activity(data['room']);

        $('div#content').trigger('update_scrollbar');
    },

    /**
     * Update the user list when someone joins the chat
     */
    event_users: function handle_event_users(e){
        var users = $.parseJSON(e.data);
        var usersDiv = $('#user-list')[0];

        usersDiv.innerHTML = '';

        for(i=0; i<users.length-1; i++){
            usersDiv.innerHTML += users[i] + ', ';
        }

        usersDiv.innerHTML += users[users.length-1];
    },

    /**
     * Callback to respond to ping events via AJAX
     */
    event_ping: function handle_event_ping(e){
        $.post($SCRIPT_ROOT + '/_pong', 'PONG!');
    },

    /**
     * Callback that handles message sending errors
     */
    publish_error: function handle_publish_error(e){
        if("" != e.responseText){
            var lineDiv = document.createElement('div');

            lineDiv.className = 'line alert alert-error';
            lineDiv.innerHTML = e.responseText;

            $('.tab-pane.active').append(lineDiv);
            $('div#content').trigger('update_scrollbar');
        }
        else{
            show_error_dialog();
        }
    },

    /**
     * Callback for handling SSE errors
     */
    event_error: function handle_event_error(e){
        this.close(); //here, `this` refers to `stream`
        show_error_dialog();
    },

    /**
     * Callback that handles the join errors
     */
    join_error: function handle_join_error(e){
        var lineDiv = document.createElement('div');

        lineDiv.className = 'line alert alert-error';
        lineDiv.innerHTML = e.responseText;

        $('.tab-pane.active').append(lineDiv);
        $('div#content').trigger('update_scrollbar');
    },

    /**
     * Callback for handling the leave error (these occur when leaving rooms)
     */
    leave_room_error: function handle_leave_room_error(e){
        //if the status is 404 then the user closed his last room, so we logged
        //him out
        if(404 === e.status){
            window.location.replace($SCRIPT_ROOT);
        }
        else{
            var lineDiv = document.createElement('div');

            lineDiv.className = 'line alert alert-error';
            lineDiv.innerHTML = e.responseText;

            $('.tab-pane.active').append(lineDiv);
            $('div#content').trigger('update_scrollbar');
        }
    },
    /**
     * Move the scrollbar at the bottom in order to see the latest message
     */
    update_scrollbar: function handle_updatescrollbar(e){
        $('#content').scrollTop($('#content')[0].scrollHeight + 42);
    }
}

/**
 * Create an AJAX request that will publish the message the user wants to
 * transmit
 */
function publish_message(e){
    e.preventDefault();
    $.post($SCRIPT_ROOT + '/_publish_message', {'message': $('#text').val(),
        'room': $('.tab-pane.active').attr('id')}).fail(Handler.publish_error);
    $('#text').val('');
    $('#text').focus();
}

/**
 * Leave a room
 *
 * An AJAX request is made to the server which will respond with the new room
 * list, also the tabs will be updated
 *
 * @param string room the clicked room (the one the user wants to leave)
 * @param string active_room the room currently active
 */
function leave_room(room, active_room){
    var room_name = room.children().attr("href").slice(1);

    $.post($SCRIPT_ROOT + '/_leave_room', {'room': room_name})
        .fail(Handler.leave_room_error)
        .success(function(e){
            var active_room_name = active_room.children().attr("href").slice(1);

            if(active_room_name == room_name){

                var next_room = active_room.next();
                if(next_room.length){ //the current tab is not the last, go right
                    active_room.next().attr('class', 'active');
                }
                else{ //current tab is the last, move to left
                    active_room.prev().attr('class', 'active');
                }
            }
            else{
                active_room.attr('class', 'active');
            }

            $(room).remove();
            $('#' + room_name).remove();

            $('#rooms').val(e);
        });
}

/**
 * Display the rooms as tabs
 *
 * This function is able to decide whether it needs to create all the tabs or
 * just some of the tabs by array difference
 */
function display_rooms(){
    var rooms = JSON.parse($('#rooms').val());

    var close_btn = $('<button>').attr({
        class: 'close',
        onclick: 'leave_room($(this).parent().parent(), $("li.active"))'
    }).html(' ×');

    /**
     * starting from one when joining rooms at login,
     * because the first room is displayed outside the for loop
     * since I need to set some classes to "active"
     */
    var pos = 1;

    if($('.nav.nav-tabs').length == 0){ //the user joins the rooms at login
        $('#chat').append(
            $('
').attr({class: 'nav nav-tabs'}).append(
                $('
').attr({class: 'active'}).append(
                    $('').attr({
                        href: '#' + rooms[0],
                        'data-toggle': 'tab',
                    }).html(rooms[0] + close_btn.prop('outerHTML'))
                )
            )
        );

        $('#chat').append(
            $('
').attr({class: 'tab-content', id: 'content'}).append(
                $('
').attr({
                    class: 'tab-pane active',
                    id: rooms[0],
                })
            )
        );
    }
    else{ //the user joins the rooms after he logged in, so we display only the
          //new rooms since the rest are already displayed
        var current_rooms = get_current_rooms();

        rooms = rooms.filter(function(i){
            return current_rooms.indexOf(i) < 0;
        });

        /**
         * every room should be processed because if we join we don't
         * have to change the active room
         */
        pos = 0;
    }

    for(i=pos; i<rooms.length; i++){
        $('.nav.nav-tabs').append(
            $('
').append(
                $('').attr({
                    href: '#' + rooms[i],
                    'data-toggle': 'tab',
                }).html(rooms[i] + close_btn.prop('outerHTML'))
            )
        );

        $('.tab-content').append(
            $('
').attr({
                class: 'tab-pane',
                id: rooms[i],
            })
        );

        if(0 == pos && i == rooms.length - 1){
            options = {
                to: $('a[href="' + '#' + rooms[i] + '"]'),
                className: 'ui-effects-transfer'
            };
            $('[name="join"]').effect("transfer", options, 700);
        }
    }
}

/**
 * Get the currently joined rooms
 *
 * @return array of room names
 */
function get_current_rooms(){
    var current_rooms = [];
    $('.tab-pane').each(function(){
        current_rooms.push($(this).attr('id'))
    });

    return current_rooms;
}

/**
 * Create an AJAX request to join more rooms after logging in
 */
function join_rooms(e){
    e.preventDefault();
    $.post($SCRIPT_ROOT + '/_join_rooms', {'join_rooms': $('#join_rooms').val()})
        .fail(Handler.join_error)
        .success(function(e){
            $('#rooms').val(e);
            display_rooms();
            $('#join_rooms').val('');
        });
}

/**
 * Initialize the elements that compose the chat, the EventSource (for the Server Sent
 * Events) and display the rooms the user joined
 */
function load_chat(){
    var stream = new EventSource($SCRIPT_ROOT + '/_sse_stream');

    stream.onmessage = Handler.event_message;
    stream.onerror = Handler.event_error;

    stream.addEventListener('users', Handler.event_users);
    stream.addEventListener('ping', Handler.event_ping);

    display_rooms();
    $('#text').focus();
}

/**
 * Get the current time
 *
 * @return string the current time in HH:MM format
 */
function get_current_time(){
    var currentTime = new Date();

    var h = currentTime.getHours();
    var m = currentTime.getMinutes();

    if(h < 10){
        h = '0' + h;
    }

    if(m < 10){
        m = '0' + m;
    }

    return h + ':' + m;
}

/**
 * Check if the user is mentioned in the message
 *
 * A user is mentioned when his nick appears as a word in a message:
 * "hello @nick"
 * "nick, hello"
 * "nick: hi"
 * "how are you nick?"
 *
 * The nick may be surrounded by punctuation, symbols or spaces, but not other
 * letters, so for the following message this function will return false:
 * "hello foonick"
 *
 * @param message the message that is checked for mentions
 *
 * @return bool true if the user is mentioned, else false
 */
function is_mention(message){
    var nick = $('label[for="text"]').html().slice(0, -1);
    var pos = message.indexOf(nick);

    while(-1 !== pos){
        if((pos === 0 || (pos - 1 >= 0 && !is_letter(message[pos-1]))) &&
           (pos+nick.length == message.length ||
                (pos+nick.length < message.length &&
                !is_letter(message[pos+nick.length])
                )
           )){
            return true;
        }

        message = message.slice(pos+nick.length);
        pos = message.indexOf(nick);
    }

    return false;
}

/**
 * Check if a character is a letter
 *
 * @param l string this should be a single character to be checked
 *
 * @return bool true if l is a letter, else false
 */
function is_letter(l){
    if((l >= 'a' && l <= 'z') || (l >= 'A' && l <= 'Z')){
        return true;
    }

    return false;
}

/**
 * Notify the user that there is activity on the chat
 *
 * @param string room_name room's name where is activity
 */
function notify_activity(room_name){
    //create an activity notice if the tab is not focused
    var a = $('a[href="#' + room_name + '"]');
    if('active' !== a.parent().attr('class')){
        if(0 === $('#icon-' + room_name).length){ //prepend just one icon
            a.prepend($('').attr({
                class: 'icon-comment',
                id: 'icon-' + room_name,
            }));
        }
    }

    if(away){
        Notificon("!", {
            font: '10px arial',
            color: '#ffffff',
            stroke: 'rgba(240, 61, 37, 1)',
            width: 7,
        });
    }
}

/**
 * Add horizontal rules to each object in an array
 *
 * If there are existing
 elements they are removed and a new one is added
 *
 * @param array obj array of objects to add a horizontal rule to
 */
function add_hr(obj){
    for(i=0; i<obj.length; i++){
        if($(obj[i]).has('div.line').length){
            obj[i].innerHTML = obj[i].innerHTML.split('
').join('') + '
';
        }
    }

    $('div#content').trigger('update_scrollbar');
}

/**
 * Adjust the height of the content where the messages appear in order to keep
 * the whole app on the screen.
 */
function adjust_blocks() {
    var win_h = $(window).height();
    var footer_h = $('p.footer').outerHeight(true);
    var box_h = $('div.well').outerHeight(true);
    var tabs_h = $('.nav.nav-tabs').outerHeight(true);
    var body_margins_h = parseInt($('body').css('margin-top')) +
        parseInt($('body').css('margin-bottom'));

    $('#content').css('height', win_h-box_h-footer_h-tabs_h-body_margins_h);
}

/**
 * If the user presses Enter while he's on the message textfield (#text) the
 * message is sent, if he presses Ctrl + Enter a newline is added to the message
 *
 * If the user presses Enter while on the rooms textfield (#join_rooms) he will
 * join them
 */
$(document).keypress(function(e){
    if('join_rooms' == e.target.id && 13 == e.which){
        join_rooms(e);
    }
    else if('text' == e.target.id && (10 == e.which
            //this part is needed in order to solve browser incompatibilities
            || 13 == e.which && e.ctrlKey)){
        $('#text').val($('#text').val() + '\n');
        return false;
    }
    else if('text' == e.target.id && 13 == e.which){
        publish_message(e);
    }
});

/**
 * Display an error dialog
 *
 * When this dialog is shown then the user is advied to reconnect because some
 * connection error occurred
 */
function show_error_dialog(){
    $('#error-dialog').dialog({
        resizable: false,
        modal: true,
        draggable: false,
        closeOnEscape: false,
        buttons: {
            'OK': function(){
                window.location.replace($SCRIPT_ROOT + '/quit');
            }
        },
        open: function(event, ui){
            $('.ui-dialog-buttonpane button').each(function(){
                $(this).attr('class', 'btn btn-danger');
            });
        },
    });
}

//Global initializations
load_chat();

//clear the activity notice upon clicking on the tab
$('a[data-toggle="tab"]').on('show', function(e){
    $('#icon-' + $(e.target).attr('href').slice(1)).remove();

    //add a
 to the last tab in order to mark the activity on that room
    //since the user moved away
    if(typeof e.relatedTarget !== 'undefined'){
        add_hr($('div' + $(e.relatedTarget).attr('href')));
    }
});

//after the tab is shown scroll down
$('a[data-toggle="tab"]').on('shown', function(e){
    Handler.update_scrollbar();
    $('#text').focus();
});

//if the browser or the browser's tab is not focused display a Notificon
$(window).focus(function(){
    away = false;
    Notificon('');
});

$(window).blur(function(){
    away = true;

    add_hr($('div.tab-pane'));
});

var away = false;

$('[name="send"]').click(publish_message);
$('[name="join"]').click(join_rooms);

$('div#content').bind('update_scrollbar', Handler.update_scrollbar);

//TODO move other handlers into Handler

$(document).ready(adjust_blocks);
$(window).resize(adjust_blocks);
<|endoftext|>


<commit_before>// Author is Fredrik A. Madsen-Malmo

$('tr').hide() // For cool transitions later

function getOnline() {
	// Gets the online status with a simple ajax request
	$('.entry').each(function(index, element) {
		$.ajax({
			type: 'POST',
			url: $(element).data('url'),
			timeout: 10000,
			error: function(x, t, m) {
		        if(t === "timeout") {
					var onlineChild = $(element).find('.online');
					$(onlineChild).data('online', 'False');

		            $(onlineChild).fadeOut(1000, function() {
						$(onlineChild).css('color', '#c0392b');
						$(onlineChild).text('Offline').fadeIn(1000);
					});
		        }
		    },
		    success: function(data) {
		    	var onlineChild = $(element).find('.online');
				$(onlineChild).data('online', data);

			    $(onlineChild).fadeOut(1000, function() {
			    	if ($(onlineChild).data('online') == 'True') {
			    		$(onlineChild).css('color', '#27ae60');
						$(onlineChild).text('Online').fadeIn(1000);
					} else {
						$(onlineChild).css('color', '#c0392b');
						$(onlineChild).text('Offline').fadeIn(1000);
					}
			    });
		    }
		});
	});
}

$(window).load(function() {
	$('tr').each(function(i,e) {
	  $(e).fadeIn(500 + i*200);
	});

	getOnline();

	// Fades away each panel after 1 second + 1 second for each panel
	$('.panel').each(function(index, element) {
		setTimeout(function() { $(element).fadeOut() }, 2000 + parseInt(index) * 1000)
	});

	// This will post the user on the other peer collection website by 'bytecode'
	$('.submit_new').on('click', function() {
		$.ajax({
			type: 'POST',
			url: 'http://aether.mygen.ca/addpeer.php',
			data: {
				alias: $('#name').val(),
				address: $('#id').val(),
				port: $('#port').val()
			}
		});
	});
});

$('#refresh').on('click', function(e) {
	e.preventDefault()

	$('.online').each(function(index, element) {
		$(element).fadeOut(1000, function() {
			$(element).text('Refreshing..');
			$(element).css('color', '#7f8c8d');
			$(element).fadeIn(1000);
		});
	});

	getOnline();
});<commit_msg>increased timeout javascript to 5 minutes<commit_after>// Author is Fredrik A. Madsen-Malmo

$('tr').hide() // For cool transitions later

function getOnline() {
	// Gets the online status with a simple ajax request
	$('.entry').each(function(index, element) {
		$.ajax({
			type: 'POST',
			url: $(element).data('url'),
			timeout: 300000,
			error: function(x, t, m) {
		        if(t === "timeout") {
					var onlineChild = $(element).find('.online');
					$(onlineChild).data('online', 'False');

		            $(onlineChild).fadeOut(1000, function() {
						$(onlineChild).css('color', '#c0392b');
						$(onlineChild).text('Offline').fadeIn(1000);
					});
		        }
		    },
		    success: function(data) {
		    	var onlineChild = $(element).find('.online');
				$(onlineChild).data('online', data);

			    $(onlineChild).fadeOut(1000, function() {
			    	if ($(onlineChild).data('online') == 'True') {
			    		$(onlineChild).css('color', '#27ae60');
						$(onlineChild).text('Online').fadeIn(1000);
					} else {
						$(onlineChild).css('color', '#c0392b');
						$(onlineChild).text('Offline').fadeIn(1000);
					}
			    });
		    }
		});
	});
}

$(window).load(function() {
	$('tr').each(function(i,e) {
	  $(e).fadeIn(500 + i*200);
	});

	getOnline();

	// Fades away each panel after 1 second + 1 second for each panel
	$('.panel').each(function(index, element) {
		setTimeout(function() { $(element).fadeOut() }, 2000 + parseInt(index) * 1000)
	});

	// This will post the user on the other peer collection website by 'bytecode'
	$('.submit_new').on('click', function() {
		$.ajax({
			type: 'POST',
			url: 'http://aether.mygen.ca/addpeer.php',
			data: {
				alias: $('#name').val(),
				address: $('#id').val(),
				port: $('#port').val()
			}
		});
	});
});

$('#refresh').on('click', function(e) {
	e.preventDefault()

	$('.online').each(function(index, element) {
		$(element).fadeOut(1000, function() {
			$(element).text('Refreshing..');
			$(element).css('color', '#7f8c8d');
			$(element).fadeIn(1000);
		});
	});

	getOnline();
});<|endoftext|>


<commit_before>const fs = require('fs-extra')
const git = require('simple-git/promise')
const throwError = require('./throwError')

const isEmpty = async path => {
  const files = await fs.readdir(path)
  if (files.length) return false

  return true
}

const reminder = [
  'This command may cause some files to be overwritten',
  "If you're sure you want to run this command, rerun it with --force.",
].join('\n')
module.exports = async path => {
  if (!(await isEmpty(path))) {
    throwError([
      'The project directory is not a git repository and is a non-empty folder.',
      reminder,
    ].join('\n'))
  } else if (await git(path).checkIsRepo()) {
    throwError([
      'Git working directory not clean',
      reminder,
    ].join('\n'))
  }
}
<commit_msg>chore: check if the working directory is clean<commit_after>const fs = require('fs-extra')
const git = require('simple-git/promise')
const throwError = require('./throwError')

const isEmpty = async path => {
  const files = await fs.readdir(path)
  if (files.length) return false

  return true
}

const reminder = [
  'This command may cause some files to be overwritten',
  "If you're sure you want to run this command, rerun it with --force.",
].join('\n')

const isWorkDirClean = async path => {
  const { files } =  await git(path).status()

  return !files.length
}

module.exports = async path => {
  if (!(await git(path).checkIsRepo()) && !(await isEmpty(path))) {
    throwError([
      'The project directory is not a git repository and is a non-empty folder.',
      reminder,
    ].join('\n'))
  } else if (!(await isWorkDirClean(path))) {
    throwError([
      'Git working directory not clean',
      reminder,
    ].join('\n'))
  }
}
<|endoftext|>


<commit_before>'use strict';

const alt = require('../alt');
const _ = require('lodash');
const styles = require('../../plugins/sidebar/styles');

const { clearName, newFile, processCreate,
  processSave, updateName } = require('../actions/file');

class FileStore {
  constructor() {

    this.bindListeners({
      onClearName: clearName,
      onNewFile: newFile,
      onProcessCreate: processCreate,
      onProcessSave: processSave,
      onUpdateName: updateName
    });

    this.state = {
      fileName: '',
      showSaveOverlay: false
    };

  }

  onClearName() {
    this.setState({
      fileName: ''
    });
  }

  onHideSave() {
    this.setState({
      showSaveOverlay: false
    });
  }

  onProcessCreate(name) {
    const { loadFile, overlay, workspace } = this.getInstance();

    if(!name){
      return;
    }

    workspace.filename.update(() => name);
    // TODO: these should transparently accept cursors for all non-function params
    workspace.saveFile(workspace.filename.deref(), workspace.current)
      .tap(() => loadFile(name, () => this._handleSuccess(`'${name}' created successfully`)))
      .catch(this._handleError)
      .finally(overlay.hide);

    this.onHideSave();
  }

  onProcessSave() {

    const { workspace } = this.getInstance();

    // TODO: reuse with checkUnsaved in sidebar index
    const file = workspace.filename.deref();
    const unnamed = workspace.directory.every(function(x) {
      if(x.get('name') === file) {
        return false;
      }
      else {
        return true;
      }
    });
    if(unnamed) {
      this.setState({
        fileName: file,
        showSaveOverlay: true
      });
    }
    else {
      this.setState({ showSaveOverlay: false });
      this._save();
    }
  }

  _save() {
    const { workspace } = this.getInstance();

    const name = workspace.filename.deref();

    // TODO: these should transparently accept cursors for all non-function params
    workspace.saveFile(name, workspace.current)
      .tap(() => this._handleSuccess(`'${name}' saved successfully`))
      .catch(this._handleError);
  }

  onNewFile() {
    const { workspace, userConfig } = this.getInstance();
    workspace.current.update(() => '');

    const directory = workspace.directory.toJS();
    const untitledNums = _.reduce(directory, function(untitled, dirfile) {
      if(dirfile.name.match(/untitled/)) {
        const getnum = dirfile.name.match(/\d+/);
        if (getnum) {
          untitled.push(_.parseInt(getnum[0]));
        }
      }
      return untitled;
    }, [0]);

    const untitledLast = _.max(untitledNums);

    const builtName = `untitled${untitledLast + 1}`;

    workspace.filename.update(() => builtName);

    userConfig.set('last-file', builtName);
  }

  onUpdateName(value) {
    this.setState({
      fileName: value
    });
  }

  _handleError(err){
    // leaving this in for better debugging of errors
    console.log(err);
    const { toast } = this.getInstance();

    toast.show(err.message, { style: styles.errorToast });
  }

  _handleSuccess(msg){
    const { toast } = this.getInstance();

    toast.show(msg, { style: styles.successToast, timeout: 5000 });
  }

}

FileStore.config = {
  stateKey: 'state'
};

module.exports = alt.createStore(FileStore);
<commit_msg>require order<commit_after>'use strict';

const _ = require('lodash');

const alt = require('../alt');
const styles = require('../../plugins/sidebar/styles');

const { clearName, newFile, processCreate,
  processSave, updateName } = require('../actions/file');

class FileStore {
  constructor() {

    this.bindListeners({
      onClearName: clearName,
      onNewFile: newFile,
      onProcessCreate: processCreate,
      onProcessSave: processSave,
      onUpdateName: updateName
    });

    this.state = {
      fileName: '',
      showSaveOverlay: false
    };

  }

  onClearName() {
    this.setState({
      fileName: ''
    });
  }

  onHideSave() {
    this.setState({
      showSaveOverlay: false
    });
  }

  onProcessCreate(name) {
    const { loadFile, overlay, workspace } = this.getInstance();

    if(!name){
      return;
    }

    workspace.filename.update(() => name);
    // TODO: these should transparently accept cursors for all non-function params
    workspace.saveFile(workspace.filename.deref(), workspace.current)
      .tap(() => loadFile(name, () => this._handleSuccess(`'${name}' created successfully`)))
      .catch(this._handleError)
      .finally(overlay.hide);

    this.onHideSave();
  }

  onProcessSave() {

    const { workspace } = this.getInstance();

    // TODO: reuse with checkUnsaved in sidebar index
    const file = workspace.filename.deref();
    const unnamed = workspace.directory.every(function(x) {
      if(x.get('name') === file) {
        return false;
      }
      else {
        return true;
      }
    });
    if(unnamed) {
      this.setState({
        fileName: file,
        showSaveOverlay: true
      });
    }
    else {
      this.setState({ showSaveOverlay: false });
      this._save();
    }
  }

  _save() {
    const { workspace } = this.getInstance();

    const name = workspace.filename.deref();

    // TODO: these should transparently accept cursors for all non-function params
    workspace.saveFile(name, workspace.current)
      .tap(() => this._handleSuccess(`'${name}' saved successfully`))
      .catch(this._handleError);
  }

  onNewFile() {
    const { workspace, userConfig } = this.getInstance();
    workspace.current.update(() => '');

    const directory = workspace.directory.toJS();
    const untitledNums = _.reduce(directory, function(untitled, dirfile) {
      if(dirfile.name.match(/untitled/)) {
        const getnum = dirfile.name.match(/\d+/);
        if (getnum) {
          untitled.push(_.parseInt(getnum[0]));
        }
      }
      return untitled;
    }, [0]);

    const untitledLast = _.max(untitledNums);

    const builtName = `untitled${untitledLast + 1}`;

    workspace.filename.update(() => builtName);

    userConfig.set('last-file', builtName);
  }

  onUpdateName(value) {
    this.setState({
      fileName: value
    });
  }

  _handleError(err){
    // leaving this in for better debugging of errors
    console.log(err);
    const { toast } = this.getInstance();

    toast.show(err.message, { style: styles.errorToast });
  }

  _handleSuccess(msg){
    const { toast } = this.getInstance();

    toast.show(msg, { style: styles.successToast, timeout: 5000 });
  }

}

FileStore.config = {
  stateKey: 'state'
};

module.exports = alt.createStore(FileStore);
<|endoftext|>


<commit_before>/**
 * @file Stream piping
 * @since 0.2.3
 */
/*#ifndef(UMD)*/
"use strict";
/*global _GPF_START*/ // 0
/*global _gpfArrayTail*/ // [].slice.call(,1)
/*global _gpfEmptyFunc*/ // An empty function
/*global _gpfIFlushableStream*/ // gpf.interfaces.IFlushableStream
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _gpfInterfaceQuery*/ // gpf.interfaces.query
/*global _gpfStreamQueryReadable*/ // Get an IReadableStream or fail if not implemented
/*global _gpfStreamQueryWritable*/ // Get an IWritableStream or fail if not implemented
/*exported _gpfStreamPipe*/ // gpf.stream.pipe
/*#endif*/

var _gpfStreamPipeFakeFlushable = {
    flush: Promise.resolve.bind(Promise)
};

function _gpfStreamPipeToFlushable (stream) {
    return _gpfInterfaceQuery(_gpfIFlushableStream, stream) || _gpfStreamPipeFakeFlushable;
}

function _gpfStreamPipeAllocateCoupler (intermediate, destination) {
    return {
        iReadableIntermediate: _gpfStreamQueryReadable(intermediate),
        iWritableIntermediate: _gpfStreamQueryWritable(intermediate),
        iFlushableIntermediate: _gpfStreamPipeToFlushable(intermediate),
        iWritableDestination: _gpfStreamQueryWritable(destination),
        iFlushableDestination: _gpfStreamPipeToFlushable(destination),
        readInProgress: false,
        readError: null,
        readPromise: Promise.resolve(),
        rejectWrite: _gpfEmptyFunc
    };
}

/*#ifdef(DEBUG)*/

function _gpfStreamPipeCouplerDebug (coupler, message) {
    if (console.expects) {
        console.expects("log", /.*/, true);
    }
    console.log("gpf.stream.pipe/coupler(" + coupler.index + "): " + message);
}

/*#endif*/

function _gpfStreamPipeCouplerRead (coupler) {
    // Read errors must be transmitted up to the initial read, this is done by forwarding it to flush & write
    var iReadableIntermediate = coupler.iReadableIntermediate,
        iWritableDestination = coupler.iWritableDestination;
    if (!coupler.readInProgress) {
        try {
            coupler.readInProgress = true;
/*#ifdef(DEBUG)*/
            _gpfStreamPipeCouplerDebug(coupler, "read started");
/*#endif*/
            coupler.readPromise = iReadableIntermediate.read(iWritableDestination)
                .then(function () {
                    _gpfStreamPipeCouplerDebug(coupler, "read ended");
                    coupler.readInProgress = true;
                }, function (reason) {
                    coupler.readError = reason;
                    coupler.rejectWrite(reason);
                });
        } catch (e) {
            coupler.readError = e;
        }
    }
}

function _gpfStreamPipeCouplerWrite (coupler, promise) {
    return new Promise(function (resolve, reject) {
        promise.then(function (value) {
            resolve(value);
            coupler.rejectWrite = _gpfEmptyFunc;
        }, reject);
        coupler.rejectWrite = reject;
    });
}

function _gpfStreamPipeCheckIfReadError (coupler) {
    if (coupler.readError) {
        return Promise.reject(coupler.readError);
    }
}

/**
 * Create a flushable & writable stream by coupling the intermediate stream with the writable destination
 *
 * @param {Object} intermediate Must implements IReadableStream interface.
 * If it implements the IFlushableStream interface, it is assumed that it retains data
 * until it receives the Flush. Meaning, the read won't complete until the flush call.
 * If it does not implement the IFlushableStream, the read may end before the whole sequence
 * has finished. It means that the next write should trigger a new read and flush must be simulated at
 * least to pass it to the destination
 * @param {Object} destination Must implements IWritableStream interface.
 * If it implements the IFlushableStream, it will be called when the intermediate completes.
 * @param {Number} index zero-based index of the coupler, helps for debugging.
 *
 * @return {Object} Implementing IWritableStream and IFlushableStream
 * @since 0.2.3
 */
function _gpfStreamPipeWeldCoupler (intermediate, destination, index) {
    var coupler = _gpfStreamPipeAllocateCoupler(intermediate, destination),
        iFlushableIntermediate = coupler.iFlushableIntermediate,
        iFlushableDestination = coupler.iFlushableDestination,
        iWritableIntermediate = coupler.iWritableIntermediate;

/*#ifdef(DEBUG)*/
    coupler.index = index;
/*#endif*/

    _gpfStreamPipeCouplerRead(coupler);

    return {

        flush: function () {
/*#ifdef(DEBUG)*/
            _gpfStreamPipeCouplerDebug(coupler, "flush");
/*#endif*/
            return _gpfStreamPipeCheckIfReadError(coupler) || iFlushableIntermediate.flush()
                .then(function () {
                    return iFlushableDestination.flush();
                })
                .then(function () {
                    return coupler.readPromise; // Wait for any pending read
                });
        },

        write: function (data) {
/*#ifdef(DEBUG)*/
            _gpfStreamPipeCouplerDebug(coupler, "write(" + JSON.stringify(data) + ")");
/*#endif*/
            _gpfStreamPipeCouplerRead(coupler);
            return _gpfStreamPipeCheckIfReadError(coupler)
                || _gpfStreamPipeCouplerWrite(coupler, iWritableIntermediate.write(data));
        }

    };
}

function _gpfStreamPipeWeldCouplers (streams) {
    var idx = streams.length,
        iWritableStream = streams[--idx];
    while (idx) {
        iWritableStream = _gpfStreamPipeWeldCoupler(streams[--idx], iWritableStream, idx);
    }
    return iWritableStream;
}

function _gpfStreamPipeToWritable (streams) {
    if (_gpfArrayTail(streams).length) {
        return _gpfStreamPipeWeldCouplers(streams);
    }
    return _gpfStreamQueryWritable(streams[_GPF_START]);
}

/**
 * Pipe streams.
 *
 * @param {gpf.interfaces.IReadableStream} source Source stream
 * @param {...gpf.interfaces.IWritableStream} destination streams to pipe data through.
 * It is assumed that the last destination stream will not block data receiving if readable),
 * every other intermediate stream must also implement {@link gpf.interfaces.IReadableStream} interface
 * @return {Promise} Resolved when reading (and subsequent writings) are done
 * @since 0.2.3
 */
function _gpfStreamPipe (source, destination) {
    _gpfIgnore(destination);
    var iReadableStream = _gpfStreamQueryReadable(source),
        iWritableStream = _gpfStreamPipeToWritable(_gpfArrayTail(arguments)),
        iFlushableStream = _gpfStreamPipeToFlushable(iWritableStream);
    try {
        return iReadableStream.read(iWritableStream)
            .then(function () {
                return iFlushableStream.flush();
            });
    } catch (e) {
        return Promise.reject(e);
    }
}

/**
 * @gpf:sameas _gpfStreamPipe
 * @since 0.2.3
 */
gpf.stream.pipe = _gpfStreamPipe;
<commit_msg>FIX #319<commit_after>/**
 * @file Stream piping
 * @since 0.2.3
 */
/*#ifndef(UMD)*/
"use strict";
/*global _GPF_START*/ // 0
/*global _gpfArrayTail*/ // [].slice.call(,1)
/*global _gpfEmptyFunc*/ // An empty function
/*global _gpfIFlushableStream*/ // gpf.interfaces.IFlushableStream
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _gpfInterfaceQuery*/ // gpf.interfaces.query
/*global _gpfStreamQueryReadable*/ // Get an IReadableStream or fail if not implemented
/*global _gpfStreamQueryWritable*/ // Get an IWritableStream or fail if not implemented
/*exported _gpfStreamPipe*/ // gpf.stream.pipe
/*#endif*/

var _gpfStreamPipeFakeFlushable = {
    flush: Promise.resolve.bind(Promise)
};

function _gpfStreamPipeToFlushable (stream) {
    return _gpfInterfaceQuery(_gpfIFlushableStream, stream) || _gpfStreamPipeFakeFlushable;
}

function _gpfStreamPipeAllocateCoupler (intermediate, destination) {
    return {
        iReadableIntermediate: _gpfStreamQueryReadable(intermediate),
        iWritableIntermediate: _gpfStreamQueryWritable(intermediate),
        iFlushableIntermediate: _gpfStreamPipeToFlushable(intermediate),
        iWritableDestination: _gpfStreamQueryWritable(destination),
        iFlushableDestination: _gpfStreamPipeToFlushable(destination),
        readInProgress: false,
        readError: null,
        readPromise: Promise.resolve(),
        rejectWrite: _gpfEmptyFunc
    };
}

/*#ifdef(DEBUG)*/

function _gpfStreamPipeCouplerDebug (coupler, message) {
    if (console.expects) {
        console.expects("log", /.*/, true);
    }
    console.log("gpf.stream.pipe/coupler(" + coupler.index + "): " + message);
}

/*#endif*/

function _gpfStreamPipeCouplerDrain (coupler) {
    // Read errors must be transmitted up to the initial read, this is done by forwarding it to flush & write
    var iReadableIntermediate = coupler.iReadableIntermediate,
        iWritableDestination = coupler.iWritableDestination;
    /*#ifdef(DEBUG)*/
    if (coupler.readInProgress) {
        _gpfStreamPipeCouplerDebug(coupler, "read in progress");
    }
    /*#endif*/
    if (!coupler.readInProgress) {
        try {
            coupler.readInProgress = true;
            /*#ifdef(DEBUG)*/
            _gpfStreamPipeCouplerDebug(coupler, "read started");
            /*#endif*/
            coupler.readPromise = iReadableIntermediate.read(iWritableDestination)
                .then(function () {
                    /*#ifdef(DEBUG)*/
                    _gpfStreamPipeCouplerDebug(coupler, "read ended");
                    /*#endif*/
                    coupler.readInProgress = false;
                }, function (reason) {
                    coupler.readError = reason;
                    coupler.rejectWrite(reason);
                });
        } catch (e) {
            coupler.readError = e;
        }
    }
}

function _gpfStreamPipeCouplerWrite (coupler, promise) {
    return new Promise(function (resolve, reject) {
        promise.then(function (value) {
            resolve(value);
            coupler.rejectWrite = _gpfEmptyFunc;
        }, reject);
        coupler.rejectWrite = reject;
    });
}

function _gpfStreamPipeCheckIfReadError (coupler) {
    if (coupler.readError) {
        return Promise.reject(coupler.readError);
    }
}

/**
 * Create a flushable & writable stream by coupling the intermediate stream with the writable destination
 *
 * @param {Object} intermediate Must implements IReadableStream interface.
 * If it implements the IFlushableStream interface, it is assumed that it retains data
 * until it receives the Flush. Meaning, the read won't complete until the flush call.
 * If it does not implement the IFlushableStream, the read may end before the whole sequence
 * has finished. It means that the next write should trigger a new read and flush must be simulated at
 * least to pass it to the destination
 * @param {Object} destination Must implements IWritableStream interface.
 * If it implements the IFlushableStream, it will be called when the intermediate completes.
 * @param {Number} index zero-based index of the coupler, helps for debugging.
 *
 * @return {Object} Implementing IWritableStream and IFlushableStream
 * @since 0.2.3
 */
function _gpfStreamPipeWeldCoupler (intermediate, destination, index) {
    var coupler = _gpfStreamPipeAllocateCoupler(intermediate, destination),
        iFlushableIntermediate = coupler.iFlushableIntermediate,
        iFlushableDestination = coupler.iFlushableDestination,
        iWritableIntermediate = coupler.iWritableIntermediate;

    /*#ifdef(DEBUG)*/
    coupler.index = index;
    /*#endif*/

    _gpfStreamPipeCouplerDrain(coupler);

    return {

        flush: function () {
            /*#ifdef(DEBUG)*/
            _gpfStreamPipeCouplerDebug(coupler, "flush");
            /*#endif*/
            return _gpfStreamPipeCheckIfReadError(coupler) || iFlushableIntermediate.flush()
                .then(function () {
                    return iFlushableDestination.flush();
                })
                .then(function () {
                    return coupler.readPromise; // Wait for any pending read
                });
        },

        write: function (data) {
            /*#ifdef(DEBUG)*/
            _gpfStreamPipeCouplerDebug(coupler, "write(" + JSON.stringify(data) + ")");
            /*#endif*/
            _gpfStreamPipeCouplerDrain(coupler);
            return _gpfStreamPipeCheckIfReadError(coupler)
                || _gpfStreamPipeCouplerWrite(coupler, iWritableIntermediate.write(data));
        }

    };
}

function _gpfStreamPipeWeldCouplers (streams) {
    var idx = streams.length,
        iWritableStream = streams[--idx];
    while (idx) {
        iWritableStream = _gpfStreamPipeWeldCoupler(streams[--idx], iWritableStream, idx);
    }
    return iWritableStream;
}

function _gpfStreamPipeToWritable (streams) {
    if (_gpfArrayTail(streams).length) {
        return _gpfStreamPipeWeldCouplers(streams);
    }
    return _gpfStreamQueryWritable(streams[_GPF_START]);
}

/**
 * Pipe streams.
 *
 * @param {gpf.interfaces.IReadableStream} source Source stream
 * @param {...gpf.interfaces.IWritableStream} destination streams to pipe data through.
 * It is assumed that the last destination stream will not block data receiving if readable),
 * every other intermediate stream must also implement {@link gpf.interfaces.IReadableStream} interface
 * @return {Promise} Resolved when reading (and subsequent writings) are done
 * @since 0.2.3
 */
function _gpfStreamPipe (source, destination) {
    _gpfIgnore(destination);
    var iReadableStream = _gpfStreamQueryReadable(source),
        iWritableStream = _gpfStreamPipeToWritable(_gpfArrayTail(arguments)),
        iFlushableStream = _gpfStreamPipeToFlushable(iWritableStream);
    try {
        return iReadableStream.read(iWritableStream)
            .then(function () {
                return iFlushableStream.flush();
            });
    } catch (e) {
        return Promise.reject(e);
    }
}

/**
 * @gpf:sameas _gpfStreamPipe
 * @since 0.2.3
 */
gpf.stream.pipe = _gpfStreamPipe;
<|endoftext|>


<commit_before>/**
 * @file Stream piping
 * @since 0.2.3
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfEmptyFunc*/ // An empty function
/*global _gpfIFlushableStream*/ // gpf.interfaces.IFlushableStream
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _gpfInterfaceQuery*/ // gpf.interfaces.query
/*global _gpfStreamQueryReadable*/ // Get an IReadableStream or fail if not implemented
/*global _gpfStreamQueryWritable*/ // Get an IWritableStream or fail if not implemented
/*exported _gpfStreamPipe*/ // gpf.stream.pipe
/*#endif*/

var _gpfStreamPipeFakeFlushable = {
    flush: Promise.resolve.bind(Promise)
};

function _gpfStreamPipeToFlushable (stream) {
    return _gpfInterfaceQuery(_gpfIFlushableStream, stream) || _gpfStreamPipeFakeFlushable;
}

/**
 * Create a flushable & writable stream by combining the intermediate stream with the writable destination
 *
 * @param {Object} intermediate Must implements IReadableStream interface.
 * If it implements the IFlushableStream interface, it is assumed that it retains data
 * until it receives the Flush. Meaning, the read won't complete until the flush call.
 * If it does not implement the IFlushableStream, the read may end before the whole sequence
 * has finished. It means that the next write should trigger a new read and flush must be simulated at
 * least to pass it to the destination
 * @param {Object} destination Must implements IWritableStream interface.
 * If it implements the IFlushableStream, it will be called when the intermediate completes.
 *
 * @return {Object} Implementing IWritableStream and IFlushableStream
 * @since 0.2.3
 */
function _gpfStreamPipeToFlushableWrite (intermediate, destination) {
    var iReadableIntermediate = _gpfStreamQueryReadable(intermediate),
        iWritableIntermediate = _gpfStreamQueryWritable(intermediate),
        iFlushableIntermediate = _gpfStreamPipeToFlushable(intermediate),
        iWritableDestination = _gpfStreamQueryWritable(destination),
        iFlushableDestination = _gpfStreamPipeToFlushable(destination),
        readingDone = true,
        readError,
        rejectWrite = _gpfEmptyFunc;

    // Read errors must be transmitted up to the initial read, this is done by forwarding it to flush & write
    function _read () {
        if (readingDone) {
            try {
                readingDone = false;
                iReadableIntermediate.read(iWritableDestination)
                    .then(function () {
                        readingDone = true;
                    }, function (reason) {
                        readError = reason;
                        rejectWrite(reason);
                    });
            } catch (e) {
                readError = e;
            }
        }
    }
    _read();

    function _checkIfReadError () {
        if (readError) {
            return Promise.reject(readError);
        }
    }

    function _wrapWrite (writePromise) {
        return new Promise(function (resolve, reject) {
            writePromise.then(function (value) {
                resolve(value);
                rejectWrite = _gpfEmptyFunc;
            }, reject);
            rejectWrite = reject;
        });
    }

    return {

        flush: function () {
            return _checkIfReadError() || iFlushableIntermediate.flush()
                .then(function () {
                    return iFlushableDestination.flush();
                });
        },

        write: function (data) {
            _read();
            return _checkIfReadError() || _wrapWrite(iWritableIntermediate.write(data));
        }

    };
}

function _gpfStreamPipeReduce (streams) {
    var idx = streams.length - 1,
        iWritableStream = streams[idx];
    while (idx > 0) {
        iWritableStream = _gpfStreamPipeToFlushableWrite(streams[--idx], iWritableStream);
    }
    return iWritableStream;
}

function _gpfStreamPipeToWritable (streams) {
    if (streams.length > 1) {
        return _gpfStreamPipeReduce(streams);
    }
    return _gpfStreamQueryWritable(streams[0]);
}

/**
 * Pipe streams.
 *
 * @param {gpf.interfaces.IReadableStream} source Source stream
 * @param {...gpf.interfaces.IWritableStream} destination Writable streams
 * @return {Promise} Resolved when reading (and subsequent writings) are done
 * @since 0.2.3
 */
function _gpfStreamPipe (source, destination) {
    _gpfIgnore(destination);
    var iReadableStream = _gpfStreamQueryReadable(source),
        iWritableStream = _gpfStreamPipeToWritable([].slice.call(arguments, 1)),
        iFlushableStream = _gpfStreamPipeToFlushable(iWritableStream);
    try {
        return iReadableStream.read(iWritableStream)
            .then(function () {
                return iFlushableStream.flush();
            });
    } catch (e) {
        return Promise.reject(e);
    }
}

/**
 * @gpf:sameas _gpfStreamPipe
 * @since 0.2.3
 */
gpf.stream.pipe = _gpfStreamPipe;
<commit_msg>Code simplification (#266)<commit_after>/**
 * @file Stream piping
 * @since 0.2.3
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfEmptyFunc*/ // An empty function
/*global _gpfIFlushableStream*/ // gpf.interfaces.IFlushableStream
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _gpfInterfaceQuery*/ // gpf.interfaces.query
/*global _gpfStreamQueryReadable*/ // Get an IReadableStream or fail if not implemented
/*global _gpfStreamQueryWritable*/ // Get an IWritableStream or fail if not implemented
/*exported _gpfStreamPipe*/ // gpf.stream.pipe
/*#endif*/

var _gpfStreamPipeFakeFlushable = {
    flush: Promise.resolve.bind(Promise)
};

function _gpfStreamPipeToFlushable (stream) {
    return _gpfInterfaceQuery(_gpfIFlushableStream, stream) || _gpfStreamPipeFakeFlushable;
}

function _gpfStreamPipeAllocateState (intermediate, destination) {
    return {
        iReadableIntermediate: _gpfStreamQueryReadable(intermediate),
        iWritableIntermediate: _gpfStreamQueryWritable(intermediate),
        iFlushableIntermediate: _gpfStreamPipeToFlushable(intermediate),
        iWritableDestination: _gpfStreamQueryWritable(destination),
        iFlushableDestination: _gpfStreamPipeToFlushable(destination),
        readError: null,
        rejectWrite: _gpfEmptyFunc
    };
}

function _gpfStreamPipeAllocateRead (state) {
    // Read errors must be transmitted up to the initial read, this is done by forwarding it to flush & write
    var readingDone = true,
        iReadableIntermediate = state.iReadableIntermediate,
        iWritableDestination = state.iWritableDestination;
    return function () {
        if (readingDone) {
            try {
                readingDone = false;
                iReadableIntermediate.read(iWritableDestination)
                    .then(function () {
                        readingDone = true;
                    }, function (reason) {
                        state.readError = reason;
                        state.rejectWrite(reason);
                    });
            } catch (e) {
                state.readError = e;
            }
        }
    };
}

function _gpfStreamPipeWrapWrite (state, promise) {
    return new Promise(function (resolve, reject) {
        promise.then(function (value) {
            resolve(value);
            state.rejectWrite = _gpfEmptyFunc;
        }, reject);
        state.rejectWrite = reject;
    });
}

function _gpfStreamPipeCheckIfReadError (state) {
    if (state.readError) {
        return Promise.reject(state.readError);
    }
}

/**
 * Create a flushable & writable stream by combining the intermediate stream with the writable destination
 *
 * @param {Object} intermediate Must implements IReadableStream interface.
 * If it implements the IFlushableStream interface, it is assumed that it retains data
 * until it receives the Flush. Meaning, the read won't complete until the flush call.
 * If it does not implement the IFlushableStream, the read may end before the whole sequence
 * has finished. It means that the next write should trigger a new read and flush must be simulated at
 * least to pass it to the destination
 * @param {Object} destination Must implements IWritableStream interface.
 * If it implements the IFlushableStream, it will be called when the intermediate completes.
 *
 * @return {Object} Implementing IWritableStream and IFlushableStream
 * @since 0.2.3
 */
function _gpfStreamPipeToFlushableWrite (intermediate, destination) {
    var state = _gpfStreamPipeAllocateState(intermediate, destination),
        read = _gpfStreamPipeAllocateRead(state),
        iFlushableIntermediate = state.iFlushableIntermediate,
        iFlushableDestination = state.iFlushableDestination,
        iWritableIntermediate = state.iWritableIntermediate;

    read();

    return {

        flush: function () {
            return _gpfStreamPipeCheckIfReadError(state) || iFlushableIntermediate.flush()
                .then(function () {
                    return iFlushableDestination.flush();
                });
        },

        write: function (data) {
            read();
            return _gpfStreamPipeCheckIfReadError(state)
                || _gpfStreamPipeWrapWrite(state, iWritableIntermediate.write(data));
        }

    };
}

function _gpfStreamPipeReduce (streams) {
    var idx = streams.length - 1,
        iWritableStream = streams[idx];
    while (idx > 0) {
        iWritableStream = _gpfStreamPipeToFlushableWrite(streams[--idx], iWritableStream);
    }
    return iWritableStream;
}

function _gpfStreamPipeToWritable (streams) {
    if (streams.length > 1) {
        return _gpfStreamPipeReduce(streams);
    }
    return _gpfStreamQueryWritable(streams[0]);
}

/**
 * Pipe streams.
 *
 * @param {gpf.interfaces.IReadableStream} source Source stream
 * @param {...gpf.interfaces.IWritableStream} destination Writable streams
 * @return {Promise} Resolved when reading (and subsequent writings) are done
 * @since 0.2.3
 */
function _gpfStreamPipe (source, destination) {
    _gpfIgnore(destination);
    var iReadableStream = _gpfStreamQueryReadable(source),
        iWritableStream = _gpfStreamPipeToWritable([].slice.call(arguments, 1)),
        iFlushableStream = _gpfStreamPipeToFlushable(iWritableStream);
    try {
        return iReadableStream.read(iWritableStream)
            .then(function () {
                return iFlushableStream.flush();
            });
    } catch (e) {
        return Promise.reject(e);
    }
}

/**
 * @gpf:sameas _gpfStreamPipe
 * @since 0.2.3
 */
gpf.stream.pipe = _gpfStreamPipe;
<|endoftext|>


<commit_before>/**
 * SVG Painter
 * @module zrender/svg/Painter
 */

define(function (require) {
    var svgCore = require('./core');
    var zrLog = require('../core/log');
    var Path = require('../graphic/Path');
    var ZImage = require('../graphic/Image');
    var ZText = require('../graphic/Text');
    var arrayDiff = require('../core/arrayDiff2');

    var svgGraphic = require('./graphic');
    var svgPath = svgGraphic.path;
    var svgImage = svgGraphic.image;
    var svgText = svgGraphic.text;

    var GradientManager = require('./helper/gradient');
    var ClippathManager = require('./helper/clippath');

    var createElement = svgCore.createElement;

    function parseInt10(val) {
        return parseInt(val, 10);
    }

    function getSvgProxy(el) {
        if (el instanceof Path) {
            return svgPath;
        }
        else if (el instanceof ZImage) {
            return svgImage;
        }
        else if (el instanceof ZText) {
            return svgText;
        }
        else {
            return svgPath;
        }
    }

    function checkParentAvailable(parent, child) {
        return child && parent && child.parentNode !== parent;
    }

    function insertAfter(parent, child, prevSibling) {
        if (checkParentAvailable(parent, child) && prevSibling) {
            var nextSibling = prevSibling.nextSibling;
            nextSibling ? parent.insertBefore(child, nextSibling)
                : parent.appendChild(child);
        }
    }

    function prepend(parent, child) {
        if (checkParentAvailable(parent, child)) {
            var firstChild = parent.firstChild;
            firstChild ? parent.insertBefore(child, firstChild)
                : parent.appendChild(child);
        }
    }

    function append(parent, child) {
        if (checkParentAvailable(parent, child)) {
            parent.appendChild(child);
        }
    }

    function remove(parent, child) {
        if (child && parent && child.parentNode === parent) {
            parent.removeChild(child);
        }
    }

    function getTextSvgElement(displayable) {
        return displayable.__textSvgEl;
    }

    function getSvgElement(displayable) {
        return displayable.__svgEl;
    }

    /**
     * @alias module:zrender/svg/Painter
     */
    var SVGPainter = function (root, storage) {

        this.root = root;

        this.storage = storage;

        var svgRoot = createElement('svg');
        this.gradient = new GradientManager(svgRoot);
        this.clipPath = new ClippathManager(svgRoot);

        var viewport = document.createElement('div');
        viewport.style.cssText = 'overflow: hidden;';

        this._svgRoot = svgRoot;
        this._viewport = viewport;

        root.appendChild(viewport);
        viewport.appendChild(svgRoot);

        this.resize();

        this._visibleList = [];
    };

    SVGPainter.prototype = {

        constructor: SVGPainter,

        getType: function () {
            return 'svg';
        },

        getViewportRoot: function () {
            return this._viewport;
        },

        getViewportRootOffset: function () {
            var viewportRoot = this.getViewportRoot();
            if (viewportRoot) {
                return {
                    offsetLeft: viewportRoot.offsetLeft || 0,
                    offsetTop: viewportRoot.offsetTop || 0
                };
            }
        },

        refresh: function () {

            var list = this.storage.getDisplayList(true);

            this._paintList(list);
        },

        _paintList: function (list) {
            this.gradient.markAllUnused();
            this.clipPath.markAllUnused();

            var svgRoot = this._svgRoot;
            var visibleList = this._visibleList;
            var listLen = list.length;

            var newVisibleList = [];
            var i;
            for (i = 0; i < listLen; i++) {
                var displayable = list[i];
                var svgProxy = getSvgProxy(displayable);
                if (!displayable.invisible) {
                    if (displayable.__dirty) {
                        svgProxy && svgProxy.brush(displayable);
                        var el = getSvgElement(displayable)
                            || getTextSvgElement(displayable);

                        // Update clipPath
                        this.clipPath.update(displayable, el);

                        // Update gradient
                        if (displayable.style) {
                            this.gradient.update(displayable.style.fill);
                            this.gradient.update(displayable.style.stroke);
                        }

                        displayable.__dirty = false;
                    }
                    newVisibleList.push(displayable);
                }
            }

            var diff = arrayDiff(visibleList, newVisibleList);
            var prevSvgElement;

            // First do remove, in case element moved to the head and do remove after add
            for (i = 0; i < diff.length; i++) {
                var item = diff[i];
                if (item.removed) {
                    for (var k = 0; k < item.count; k++) {
                        var displayable = visibleList[item.indices[k]];
                        var svgElement = getSvgElement(displayable);
                        var textSvgElement = getTextSvgElement(displayable);
                        remove(svgRoot, svgElement);
                        remove(svgRoot, textSvgElement);
                    }
                }
            }
            for (i = 0; i < diff.length; i++) {
                var item = diff[i];
                if (item.added) {
                    for (var k = 0; k < item.count; k++) {
                        var displayable = newVisibleList[item.indices[k]];
                        var svgElement = getSvgElement(displayable);
                        var textSvgElement = getTextSvgElement(displayable);
                        prevSvgElement ? insertAfter(svgRoot, svgElement, prevSvgElement)
                            : prepend(svgRoot, svgElement);
                        if (svgElement) {
                            insertAfter(svgRoot, textSvgElement, svgElement);
                        }
                        else if (prevSvgElement) {
                            insertAfter(svgRoot, textSvgElement, prevSvgElement);
                        }
                        else {
                            prepend(svgRoot, textSvgElement);
                        }
                        // Insert text
                        insertAfter(svgRoot, textSvgElement, svgElement);
                        prevSvgElement = textSvgElement || svgElement;

                        this.gradient.addWithoutUpdate(svgElement, displayable);
                        this.clipPath.markUsed(displayable);
                    }
                }
                else if (!item.removed) {
                    for (var k = 0; k < item.count; k++) {
                        var displayable = newVisibleList[item.indices[k]];
                        prevSvgElement
                            = svgElement
                            = getTextSvgElement(displayable)
                            || getSvgElement(displayable);

                        this.gradient.markUsed(displayable);
                        this.gradient.addWithoutUpdate(svgElement, displayable);

                        this.clipPath.markUsed(displayable);
                    }
                }
            }

            this.gradient.removeUnused();
            this.clipPath.removeUnused();

            this._visibleList = newVisibleList;
        },

        _getDefs: function (isForceCreating) {
            var svgRoot = this._svgRoot;
            var defs = this._svgRoot.getElementsByTagName('defs');
            if (defs.length === 0) {
                // Not exist
                if (isForceCreating) {
                    var defs = svgRoot.insertBefore(
                        createElement('defs'), // Create new tag
                        svgRoot.firstChild // Insert in the front of svg
                    );
                    if (!defs.contains) {
                        // IE doesn't support contains method
                        defs.contains = function (el) {
                            var children = defs.children;
                            if (!children) {
                                return false;
                            }
                            for (var i = children.length - 1; i >= 0; --i) {
                                if (children[i] === el) {
                                    return true;
                                }
                            }
                            return false;
                        };
                    }
                    return defs;
                }
                else {
                    return null;
                }
            }
            else {
                return defs[0];
            }
        },

        resize: function () {
            var width = this._getWidth();
            var height = this._getHeight();

            if (this._width !== width && this._height !== height) {
                this._width = width;
                this._height = height;

                var viewportStyle = this._viewport.style;
                viewportStyle.width = width + 'px';
                viewportStyle.height = height + 'px';

                var svgRoot = this._svgRoot;
                // Set width by 'svgRoot.width = width' is invalid
                svgRoot.setAttribute('width', width);
                svgRoot.setAttribute('height', height);
            }
        },

        getWidth: function () {
            return this._getWidth();
        },

        getHeight: function () {
            return this._getHeight();
        },

        _getWidth: function () {
            var root = this.root;
            var stl = document.defaultView.getComputedStyle(root);

            return ((root.clientWidth || parseInt10(stl.width))
                    - parseInt10(stl.paddingLeft)
                    - parseInt10(stl.paddingRight)) | 0;
        },

        _getHeight: function () {
            var root = this.root;
            var stl = document.defaultView.getComputedStyle(root);

            return ((root.clientHeight || parseInt10(stl.height))
                    - parseInt10(stl.paddingTop)
                    - parseInt10(stl.paddingBottom)) | 0;
        },

        dispose: function () {
            this.root.innerHTML = '';

            this._svgRoot =
            this._viewport =
            this.storage = null;
        },

        clear: function () {
            if (this._viewport) {
                this.root.removeChild(this._viewport);
            }
        }
    };

    // Not supported methods
    function createMethodNotSupport(method) {
        return function () {
            zrLog('In SVG mode painter not support method "' + method + '"')
        }
    }

    var notSupportedMethods = [
        'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers',
        'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage'
    ];

    for (var i = 0; i < notSupportedMethods.length; i++) {
        var name = notSupportedMethods[i];
        SVGPainter.prototype[name] = createMethodNotSupport(name);
    }

    return SVGPainter;
});
<commit_msg>[svg] Fix rendering order problem caused by empty preSvgElement<commit_after>/**
 * SVG Painter
 * @module zrender/svg/Painter
 */

define(function (require) {
    var svgCore = require('./core');
    var zrLog = require('../core/log');
    var Path = require('../graphic/Path');
    var ZImage = require('../graphic/Image');
    var ZText = require('../graphic/Text');
    var arrayDiff = require('../core/arrayDiff2');

    var svgGraphic = require('./graphic');
    var svgPath = svgGraphic.path;
    var svgImage = svgGraphic.image;
    var svgText = svgGraphic.text;

    var GradientManager = require('./helper/gradient');
    var ClippathManager = require('./helper/clippath');

    var createElement = svgCore.createElement;

    function parseInt10(val) {
        return parseInt(val, 10);
    }

    function getSvgProxy(el) {
        if (el instanceof Path) {
            return svgPath;
        }
        else if (el instanceof ZImage) {
            return svgImage;
        }
        else if (el instanceof ZText) {
            return svgText;
        }
        else {
            return svgPath;
        }
    }

    function checkParentAvailable(parent, child) {
        return child && parent && child.parentNode !== parent;
    }

    function insertAfter(parent, child, prevSibling) {
        if (checkParentAvailable(parent, child) && prevSibling) {
            var nextSibling = prevSibling.nextSibling;
            nextSibling ? parent.insertBefore(child, nextSibling)
                : parent.appendChild(child);
        }
    }

    function prepend(parent, child) {
        if (checkParentAvailable(parent, child)) {
            var firstChild = parent.firstChild;
            firstChild ? parent.insertBefore(child, firstChild)
                : parent.appendChild(child);
        }
    }

    function append(parent, child) {
        if (checkParentAvailable(parent, child)) {
            parent.appendChild(child);
        }
    }

    function remove(parent, child) {
        if (child && parent && child.parentNode === parent) {
            parent.removeChild(child);
        }
    }

    function getTextSvgElement(displayable) {
        return displayable.__textSvgEl;
    }

    function getSvgElement(displayable) {
        return displayable.__svgEl;
    }

    /**
     * @alias module:zrender/svg/Painter
     */
    var SVGPainter = function (root, storage) {

        this.root = root;

        this.storage = storage;

        var svgRoot = createElement('svg');
        this.gradient = new GradientManager(svgRoot);
        this.clipPath = new ClippathManager(svgRoot);

        var viewport = document.createElement('div');
        viewport.style.cssText = 'overflow: hidden;';

        this._svgRoot = svgRoot;
        this._viewport = viewport;

        root.appendChild(viewport);
        viewport.appendChild(svgRoot);

        this.resize();

        this._visibleList = [];
    };

    SVGPainter.prototype = {

        constructor: SVGPainter,

        getType: function () {
            return 'svg';
        },

        getViewportRoot: function () {
            return this._viewport;
        },

        getViewportRootOffset: function () {
            var viewportRoot = this.getViewportRoot();
            if (viewportRoot) {
                return {
                    offsetLeft: viewportRoot.offsetLeft || 0,
                    offsetTop: viewportRoot.offsetTop || 0
                };
            }
        },

        refresh: function () {

            var list = this.storage.getDisplayList(true);

            this._paintList(list);
        },

        _paintList: function (list) {
            this.gradient.markAllUnused();
            this.clipPath.markAllUnused();

            var svgRoot = this._svgRoot;
            var visibleList = this._visibleList;
            var listLen = list.length;

            var newVisibleList = [];
            var i;
            for (i = 0; i < listLen; i++) {
                var displayable = list[i];
                var svgProxy = getSvgProxy(displayable);
                if (!displayable.invisible) {
                    if (displayable.__dirty) {
                        svgProxy && svgProxy.brush(displayable);
                        var el = getSvgElement(displayable)
                            || getTextSvgElement(displayable);

                        // Update clipPath
                        this.clipPath.update(displayable, el);

                        // Update gradient
                        if (displayable.style) {
                            this.gradient.update(displayable.style.fill);
                            this.gradient.update(displayable.style.stroke);
                        }

                        displayable.__dirty = false;
                    }
                    newVisibleList.push(displayable);
                }
            }

            var diff = arrayDiff(visibleList, newVisibleList);
            var prevSvgElement;

            // First do remove, in case element moved to the head and do remove after add
            for (i = 0; i < diff.length; i++) {
                var item = diff[i];
                if (item.removed) {
                    for (var k = 0; k < item.count; k++) {
                        var displayable = visibleList[item.indices[k]];
                        var svgElement = getSvgElement(displayable);
                        var textSvgElement = getTextSvgElement(displayable);
                        remove(svgRoot, svgElement);
                        remove(svgRoot, textSvgElement);
                    }
                }
            }
            for (i = 0; i < diff.length; i++) {
                var item = diff[i];
                if (item.added) {
                    for (var k = 0; k < item.count; k++) {
                        var displayable = newVisibleList[item.indices[k]];
                        var svgElement = getSvgElement(displayable);
                        var textSvgElement = getTextSvgElement(displayable);
                        prevSvgElement ? insertAfter(svgRoot, svgElement, prevSvgElement)
                            : prepend(svgRoot, svgElement);
                        if (svgElement) {
                            insertAfter(svgRoot, textSvgElement, svgElement);
                        }
                        else if (prevSvgElement) {
                            insertAfter(svgRoot, textSvgElement, prevSvgElement);
                        }
                        else {
                            prepend(svgRoot, textSvgElement);
                        }
                        // Insert text
                        insertAfter(svgRoot, textSvgElement, svgElement);
                        prevSvgElement = textSvgElement || svgElement || prevSvgElement;

                        this.gradient.addWithoutUpdate(svgElement, displayable);
                        this.clipPath.markUsed(displayable);
                    }
                }
                else if (!item.removed) {
                    for (var k = 0; k < item.count; k++) {
                        var displayable = newVisibleList[item.indices[k]];
                        prevSvgElement
                            = svgElement
                            = getTextSvgElement(displayable)
                            || getSvgElement(displayable)
                            || prevSvgElement;

                        this.gradient.markUsed(displayable);
                        this.gradient.addWithoutUpdate(svgElement, displayable);

                        this.clipPath.markUsed(displayable);
                    }
                }
            }

            this.gradient.removeUnused();
            this.clipPath.removeUnused();

            this._visibleList = newVisibleList;
        },

        _getDefs: function (isForceCreating) {
            var svgRoot = this._svgRoot;
            var defs = this._svgRoot.getElementsByTagName('defs');
            if (defs.length === 0) {
                // Not exist
                if (isForceCreating) {
                    var defs = svgRoot.insertBefore(
                        createElement('defs'), // Create new tag
                        svgRoot.firstChild // Insert in the front of svg
                    );
                    if (!defs.contains) {
                        // IE doesn't support contains method
                        defs.contains = function (el) {
                            var children = defs.children;
                            if (!children) {
                                return false;
                            }
                            for (var i = children.length - 1; i >= 0; --i) {
                                if (children[i] === el) {
                                    return true;
                                }
                            }
                            return false;
                        };
                    }
                    return defs;
                }
                else {
                    return null;
                }
            }
            else {
                return defs[0];
            }
        },

        resize: function () {
            var width = this._getWidth();
            var height = this._getHeight();

            if (this._width !== width && this._height !== height) {
                this._width = width;
                this._height = height;

                var viewportStyle = this._viewport.style;
                viewportStyle.width = width + 'px';
                viewportStyle.height = height + 'px';

                var svgRoot = this._svgRoot;
                // Set width by 'svgRoot.width = width' is invalid
                svgRoot.setAttribute('width', width);
                svgRoot.setAttribute('height', height);
            }
        },

        getWidth: function () {
            return this._getWidth();
        },

        getHeight: function () {
            return this._getHeight();
        },

        _getWidth: function () {
            var root = this.root;
            var stl = document.defaultView.getComputedStyle(root);

            return ((root.clientWidth || parseInt10(stl.width))
                    - parseInt10(stl.paddingLeft)
                    - parseInt10(stl.paddingRight)) | 0;
        },

        _getHeight: function () {
            var root = this.root;
            var stl = document.defaultView.getComputedStyle(root);

            return ((root.clientHeight || parseInt10(stl.height))
                    - parseInt10(stl.paddingTop)
                    - parseInt10(stl.paddingBottom)) | 0;
        },

        dispose: function () {
            this.root.innerHTML = '';

            this._svgRoot =
            this._viewport =
            this.storage = null;
        },

        clear: function () {
            if (this._viewport) {
                this.root.removeChild(this._viewport);
            }
        }
    };

    // Not supported methods
    function createMethodNotSupport(method) {
        return function () {
            zrLog('In SVG mode painter not support method "' + method + '"')
        }
    }

    var notSupportedMethods = [
        'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers',
        'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage'
    ];

    for (var i = 0; i < notSupportedMethods.length; i++) {
        var name = notSupportedMethods[i];
        SVGPainter.prototype[name] = createMethodNotSupport(name);
    }

    return SVGPainter;
});
<|endoftext|>


<commit_before>import { Router } from 'express';
import { Tag, RestaurantTag } from '../models';
import { loggedIn, errorCatcher } from './ApiHelper';
import {
  postedNewTagToRestaurant,
  postedTagToRestaurant,
  deletedTagFromRestaurant
} from '../actions/restaurants';

const router = new Router({ mergeParams: true });

router
  .post(
    '/',
    loggedIn,
    async (req, res) => {
      const restaurantId = parseInt(req.params.restaurant_id, 10);
      const alreadyAddedError = () => {
        const error = { message: 'Could not add tag to restaurant. Is it already added?' };
        errorCatcher(res, error);
      };
      if (req.body.name !== undefined) {
        Tag.findOrCreate({
          where: {
            name: req.body.name.toLowerCase()
          }
        }).spread(tag =>
          RestaurantTag.create({
            restaurant_id: restaurantId,
            tag_id: tag.id
          }).then(() => {
            const json = tag.toJSON();
            req.wss.broadcast(postedNewTagToRestaurant(restaurantId, json));
            res.status(201).send({ error: false, data: json });
          }).catch(alreadyAddedError)
        ).catch(err => {
          errorCatcher(res, err);
        });
      } else if (req.body.id !== undefined) {
        const id = parseInt(req.body.id, 10);
        RestaurantTag.create({
          restaurant_id: restaurantId,
          tag_id: id
        }).then(obj => {
          const json = obj.toJSON();
          req.wss.broadcast(postedTagToRestaurant(restaurantId, id));
          res.status(201).send({ error: false, data: json });
        }).catch(alreadyAddedError);
      } else {
        errorCatcher(res);
      }
    }
  )
  .delete(
    '/:id',
    loggedIn,
    async (req, res) => {
      const id = parseInt(req.params.id, 10);
      const restaurantId = parseInt(req.params.restaurant_id, 10);
      RestaurantTag.destroy({ where: { restaurant_id: restaurantId, tag_id: id } }).then(() => {
        req.wss.broadcast(deletedTagFromRestaurant(restaurantId, id));
        res.status(204).send({ error: false });
      }).catch(err => errorCatcher(res, err));
    }
  );

export default router;
<commit_msg>Trim tag<commit_after>import { Router } from 'express';
import { Tag, RestaurantTag } from '../models';
import { loggedIn, errorCatcher } from './ApiHelper';
import {
  postedNewTagToRestaurant,
  postedTagToRestaurant,
  deletedTagFromRestaurant
} from '../actions/restaurants';

const router = new Router({ mergeParams: true });

router
  .post(
    '/',
    loggedIn,
    async (req, res) => {
      const restaurantId = parseInt(req.params.restaurant_id, 10);
      const alreadyAddedError = () => {
        const error = { message: 'Could not add tag to restaurant. Is it already added?' };
        errorCatcher(res, error);
      };
      if (req.body.name !== undefined) {
        Tag.findOrCreate({
          where: {
            name: req.body.name.toLowerCase().trim()
          }
        }).spread(tag =>
          RestaurantTag.create({
            restaurant_id: restaurantId,
            tag_id: tag.id
          }).then(() => {
            const json = tag.toJSON();
            req.wss.broadcast(postedNewTagToRestaurant(restaurantId, json));
            res.status(201).send({ error: false, data: json });
          }).catch(alreadyAddedError)
        ).catch(err => {
          errorCatcher(res, err);
        });
      } else if (req.body.id !== undefined) {
        const id = parseInt(req.body.id, 10);
        RestaurantTag.create({
          restaurant_id: restaurantId,
          tag_id: id
        }).then(obj => {
          const json = obj.toJSON();
          req.wss.broadcast(postedTagToRestaurant(restaurantId, id));
          res.status(201).send({ error: false, data: json });
        }).catch(alreadyAddedError);
      } else {
        errorCatcher(res);
      }
    }
  )
  .delete(
    '/:id',
    loggedIn,
    async (req, res) => {
      const id = parseInt(req.params.id, 10);
      const restaurantId = parseInt(req.params.restaurant_id, 10);
      RestaurantTag.destroy({ where: { restaurant_id: restaurantId, tag_id: id } }).then(() => {
        req.wss.broadcast(deletedTagFromRestaurant(restaurantId, id));
        res.status(204).send({ error: false });
      }).catch(err => errorCatcher(res, err));
    }
  );

export default router;
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

//var User = require('')

router.post('/api/authenticate', function (req, res) {
    req.getConnection(function (err, connection) {
        connection.query('SELECT * WHERE email = ? AND password = ?', [req.body.email, req.body.password], function (err, result) {
            if (err) {
                res.json({
                    type: false,
                    data: 'Error ocurred: ' + err
                });
            }
            else
            {
                if(result){
                    res.json({
                        type: true,
                        data: result,
                        token: result.token
                    });
                }
                else{
                    res.json({
                        type: false,
                        data: 'Incorrect email/password'
                    })
                }

            }
        });
    });
});

module.exports = router;<commit_msg>inicio autenticacao<commit_after>var express = require('express');
var router = express.Router();

//var User = require('')

router.post('/api/authenticate', function (req, res) {
    req.getConnection(function (err, connection) {
        connection.query('SELECT * FROM useraccount WHERE email = ? AND password = ?', [req.body.email, req.body.password], function (err, result) {
            if (err) {
                res.json({
                    type: false,
                    data: 'Error ocurred: ' + err
                });
            }
            else
            {
                if(result){
                    res.json({
                        type: true,
                        data: result,
                        token: result.token
                    });
                }
                else{
                    res.json({
                        type: false,
                        data: 'Incorrect email/password'
                    })
                }

            }
        });
    });
});

module.exports = router;<|endoftext|>


<commit_before>define([

  'jquery'
  ,'underscore'
  ,'backbone'

  // Extensions
  ,'jquery-dragon'

], function (

  $
  ,_
  ,Backbone

) {
  'use strict';

  var $win = $(window);

  return Backbone.View.extend({

    CONTAINER_TEMPLATE: [
      '

'
    ].join('')

    ,HANDLE_TEMPLATE: [
      '

'
    ].join('')

    ,CONTENT_WRAPPER_TEMPLATE: [
      '

'
    ].join('')

    ,initialize: function (opts) {
      _.extend(this, opts);
      this.$handle = $(this.HANDLE_TEMPLATE);
      this.$el.wrap($(this.CONTAINER_TEMPLATE));
      this.$el = this.$el.parent();
      this.$el
        .wrapInner($(this.CONTENT_WRAPPER_TEMPLATE))
        .prepend(this.$handle)
        .css({
          left: $win.width() - this.$el.outerWidth(true)
        })
        .dragon({
          within: this.$el.parent()
          ,handle: '.pane-handle'
        });
      this.oldSize = this.getSize();
      $win.on('resize', _.bind(this.onResize, this));
    }

    ,onResize: function () {
      var width = this.$el.outerWidth(true);
      var winWidth = $win.width();

      if ((this.$el.offset().left + width) > winWidth) {
        this.$el.css('left', winWidth - width);
      }
    }

    ,getSize: function () {
      return {
        height: this.$el.height()
        ,width: this.$el.width()
      };
    }

    ,toggle: function () {
      this.$el.fadeToggle(200);
    }

  });
});
<commit_msg>Makes fade toggle speed a variable.<commit_after>define([

  'jquery'
  ,'underscore'
  ,'backbone'

  // Extensions
  ,'jquery-dragon'

], function (

  $
  ,_
  ,Backbone

) {
  'use strict';

  var $win = $(window);

  return Backbone.View.extend({

    FADE_TOGGLE_SPEED: 200

    ,CONTAINER_TEMPLATE: [
      '

'
    ].join('')

    ,HANDLE_TEMPLATE: [
      '

'
    ].join('')

    ,CONTENT_WRAPPER_TEMPLATE: [
      '

'
    ].join('')

    ,initialize: function (opts) {
      _.extend(this, opts);
      this.$handle = $(this.HANDLE_TEMPLATE);
      this.$el.wrap($(this.CONTAINER_TEMPLATE));
      this.$el = this.$el.parent();
      this.$el
        .wrapInner($(this.CONTENT_WRAPPER_TEMPLATE))
        .prepend(this.$handle)
        .css({
          left: $win.width() - this.$el.outerWidth(true)
        })
        .dragon({
          within: this.$el.parent()
          ,handle: '.pane-handle'
        });
      this.oldSize = this.getSize();
      $win.on('resize', _.bind(this.onResize, this));
    }

    ,onResize: function () {
      var width = this.$el.outerWidth(true);
      var winWidth = $win.width();

      if ((this.$el.offset().left + width) > winWidth) {
        this.$el.css('left', winWidth - width);
      }
    }

    ,getSize: function () {
      return {
        height: this.$el.height()
        ,width: this.$el.width()
      };
    }

    ,toggle: function () {
      this.$el.fadeToggle(this.FADE_TOGGLE_SPEED);
    }

  });
});
<|endoftext|>