<commit_before>###############################################################################
#
# SageMathCloud: A collaborative web-based interface to Sage, IPython, LaTeX and the Terminal.
#
#    Copyright (C) 2016, Sagemath Inc.
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################


###########################################
#
# An Xterm Console Window
#
###########################################

$                = window.$

{debounce}       = require('underscore')

{EventEmitter}   = require('events')
{alert_message}  = require('./alerts')
misc             = require('smc-util/misc')
{copy, filename_extension, required, defaults, to_json, uuid, from_json} = require('smc-util/misc')
{redux}          = require('./smc-react')
{alert_message}  = require('./alerts')

misc_page        = require('./misc_page')

templates        = $("#salvus-console-templates")
console_template = templates.find(".salvus-console")

feature = require('./feature')

IS_MOBILE = feature.IS_MOBILE

CSI = String.fromCharCode(0x9b)

initfile_content = (filename) ->
    """# This initialization file is associated with your terminal in #{filename}.
# It is automatically run whenever it starts up -- restart the terminal via Ctrl-d and Return-key.

# Usually, your ~/.bashrc is executed and this behavior is emulated for completeness:
source ~/.bashrc

# You can export environment variables, e.g. to set custom GIT_* variables
# https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables
#export GIT_AUTHOR_NAME="<NAME>"
#export GIT_AUTHOR_EMAIL="<EMAIL>"
#export GIT_COMMITTER_NAME="<NAME>"
#export GIT_COMMITTER_EMAIL="<EMAIL>"

# It is also possible to automatically start a program ...

#sage
#sage -ipython
#top

# ... or even define a terminal specific function.
#hello () { echo "hello world"; }
"""

focused_console = undefined
client_keydown = (ev) ->
    focused_console?.client_keydown(ev)


class Console extends EventEmitter
    constructor: (opts={}) ->
        @opts = defaults opts,
            element     : required  # DOM (or jQuery) element that is replaced by this console.
            project_id  : required
            path        : required
            session     : undefined  # a console_session; use .set_session to set it later instead.
            title       : ""
            filename    : ""
            rows        : 16
            cols        : 80
            editor      : undefined  # FileEditor instance -- needed for some actions, e.g., opening a file
            close       : undefined  # if defined, called when close button clicked.
            reconnect   : undefined  # if defined, opts.reconnect?() is called when session console wants to reconnect; this should call set_session.

            font        :
                family : undefined
                size   : undefined                           # CSS font-size in points
                line_height : 120                            # CSS line-height percentage

            highlight_mode : 'none'
            color_scheme   : undefined
            on_pause       : undefined # Called after pause_rendering is called
            on_unpause     : undefined # Called after unpause_rendering is called
            on_reconnecting: undefined
            on_reconnected : undefined
            set_title      : undefined

        @_init_default_settings()

        @project_id = @opts.project_id
        @path = @opts.path

        @mark_file_use = debounce(@mark_file_use, 3000)

        @_project_actions = redux.getProjectActions(@project_id)

        # The is_focused variable keeps track of whether or not the
        # editor is focused.  This impacts the cursor, and also whether
        # messages such as open_file or open_directory are handled (see @init_mesg).
        @is_focused = false

        # Create the DOM element that realizes this console, from an HTML template.
        @element = console_template.clone()
        @textarea = @element.find(".salvus-console-textarea")

        # Record on the DOM element a reference to the console
        # instance, which is useful for client code.
        @element.data("console", @)

        # Actually put the DOM element into the (likely visible) DOM
        # in the place specified by the client.
        $(@opts.element).replaceWith(@element)

        # Set the initial title, though of course the term can change
        # this via certain escape codes.
        @set_title(@opts.title)

        # Create the new Terminal object -- this is defined in
        # static/term/term.js -- it's a nearly complete implementation of
        # the xterm protocol.

        @_init_colors()

        @terminal = new Terminal
            cols: @opts.cols
            rows: @opts.rows
        @init_mesg()

        # The first time Terminal.bindKeys is called, it makes Terminal
        # listen on *all* keystrokes for the rest of the program.  It
        # only has to be done once -- any further times are ignored.
        Terminal.bindKeys(client_keydown)

        @scrollbar = @element.find(".salvus-console-scrollbar")

        @scrollbar.scroll () =>
            if @ignore_scroll
                return
            @set_term_to_scrollbar()

        @terminal.on 'scroll', (top, rows) =>
            @set_scrollbar_to_term()

        @_init_ttyjs()

        # Initialize buttons
        @_init_buttons()
        @_init_input_line()

        # Initialize the "set default font size" button that appears.
        @_init_font_make_default()

        # Initialize the paste bin
        @_init_paste_bin()

        # Init pausing rendering when user clicks
        @_init_rendering_pause()

        if not IS_MOBILE
            @textarea.on 'blur', =>
                if @_focusing?          # see comment in @focus.
                    @_focus_hidden_textarea()
                else
                    @blur()

        # delete scroll buttons except on mobile
        if not IS_MOBILE
            @element.find(".salvus-console-up").hide()
            @element.find(".salvus-console-down").hide()

        if opts.session?
            @set_session(opts.session)

    append_to_value: (data) =>
        # this @value is used for copy/paste of the session history.
        @value += data.replace(/\x1b\[.{1,5}m|\x1b\].*0;|\x1b\[.*~|\x1b\[?.*l/g,'')

    init_mesg: () =><commit_msg>fix issue with focus on terminals<commit_after>###############################################################################
#
# SageMathCloud: A collaborative web-based interface to Sage, IPython, LaTeX and the Terminal.
#
#    Copyright (C) 2016, Sagemath Inc.
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################


###########################################
#
# An Xterm Console Window
#
###########################################

$                = window.$

{debounce}       = require('underscore')

{EventEmitter}   = require('events')
{alert_message}  = require('./alerts')
misc             = require('smc-util/misc')
{copy, filename_extension, required, defaults, to_json, uuid, from_json} = require('smc-util/misc')
{redux}          = require('./smc-react')
{alert_message}  = require('./alerts')

misc_page        = require('./misc_page')

templates        = $("#salvus-console-templates")
console_template = templates.find(".salvus-console")

feature = require('./feature')

IS_MOBILE = feature.IS_MOBILE

CSI = String.fromCharCode(0x9b)

initfile_content = (filename) ->
    """# This initialization file is associated with your terminal in #{filename}.
# It is automatically run whenever it starts up -- restart the terminal via Ctrl-d and Return-key.

# Usually, your ~/.bashrc is executed and this behavior is emulated for completeness:
source ~/.bashrc

# You can export environment variables, e.g. to set custom GIT_* variables
# https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables
#export GIT_AUTHOR_NAME="<NAME>"
#export GIT_AUTHOR_EMAIL="<EMAIL>"
#export GIT_COMMITTER_NAME="<NAME>"
#export GIT_COMMITTER_EMAIL="<EMAIL>"

# It is also possible to automatically start a program ...

#sage
#sage -ipython
#top

# ... or even define a terminal specific function.
#hello () { echo "hello world"; }
"""

focused_console = undefined
client_keydown = (ev) ->
    focused_console?.client_keydown(ev)


class Console extends EventEmitter
    constructor: (opts={}) ->
        @opts = defaults opts,
            element     : required  # DOM (or jQuery) element that is replaced by this console.
            project_id  : required
            path        : required
            session     : undefined  # a console_session; use .set_session to set it later instead.
            title       : ""
            filename    : ""
            rows        : 16
            cols        : 80
            editor      : undefined  # FileEditor instance -- needed for some actions, e.g., opening a file
            close       : undefined  # if defined, called when close button clicked.
            reconnect   : undefined  # if defined, opts.reconnect?() is called when session console wants to reconnect; this should call set_session.

            font        :
                family : undefined
                size   : undefined                           # CSS font-size in points
                line_height : 120                            # CSS line-height percentage

            highlight_mode : 'none'
            color_scheme   : undefined
            on_pause       : undefined # Called after pause_rendering is called
            on_unpause     : undefined # Called after unpause_rendering is called
            on_reconnecting: undefined
            on_reconnected : undefined
            set_title      : undefined

        @_init_default_settings()

        @project_id = @opts.project_id
        @path = @opts.path

        @mark_file_use = debounce(@mark_file_use, 3000)

        @_project_actions = redux.getProjectActions(@project_id)

        # The is_focused variable keeps track of whether or not the
        # editor is focused.  This impacts the cursor, and also whether
        # messages such as open_file or open_directory are handled (see @init_mesg).
        @is_focused = false

        # Create the DOM element that realizes this console, from an HTML template.
        @element = console_template.clone()
        @textarea = @element.find(".salvus-console-textarea")

        # Record on the DOM element a reference to the console
        # instance, which is useful for client code.
        @element.data("console", @)

        # Actually put the DOM element into the (likely visible) DOM
        # in the place specified by the client.
        $(@opts.element).replaceWith(@element)

        # Set the initial title, though of course the term can change
        # this via certain escape codes.
        @set_title(@opts.title)

        # Create the new Terminal object -- this is defined in
        # static/term/term.js -- it's a nearly complete implementation of
        # the xterm protocol.

        @_init_colors()

        @terminal = new Terminal
            cols: @opts.cols
            rows: @opts.rows
        @init_mesg()

        # The first time Terminal.bindKeys is called, it makes Terminal
        # listen on *all* keystrokes for the rest of the program.  It
        # only has to be done once -- any further times are ignored.
        Terminal.bindKeys(client_keydown)

        @scrollbar = @element.find(".salvus-console-scrollbar")

        @scrollbar.scroll () =>
            if @ignore_scroll
                return
            @set_term_to_scrollbar()

        @terminal.on 'scroll', (top, rows) =>
            @set_scrollbar_to_term()

        @_init_ttyjs()

        # Initialize buttons
        @_init_buttons()
        @_init_input_line()

        # Initialize the "set default font size" button that appears.
        @_init_font_make_default()

        # Initialize the paste bin
        @_init_paste_bin()

        # Init pausing rendering when user clicks
        @_init_rendering_pause()

        if not IS_MOBILE
            @textarea.on 'blur', =>
                if @_focusing?          # see comment in @focus.
                    @_focus_hidden_textarea()

        # delete scroll buttons except on mobile
        if not IS_MOBILE
            @element.find(".salvus-console-up").hide()
            @element.find(".salvus-console-down").hide()

        if opts.session?
            @set_session(opts.session)

    append_to_value: (data) =>
        # this @value is used for copy/paste of the session history.
        @value += data.replace(/\x1b\[.{1,5}m|\x1b\].*0;|\x1b\[.*~|\x1b\[?.*l/g,'')

    init_mesg: () =>
{'fraction_non_alphanumeric': 0.12369002637770014, 'fraction_numerical': 0.0037071362372567192, 'mean_word_length': 2.370494954348871, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 29, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7633820', 'n_tokens_mistral': 3864, 'n_tokens_neox': 3665, 'n_words': 1233}