// Copyright (c) IPython Development Team. // Distributed under the terms of the Modified BSD License. define(['require' ], function(require) { "use strict"; var ActionHandler = function (env) { this.env = env || {}; Object.seal(this); }; /** * A bunch of predefined `Simple Actions` used by IPython. * `Simple Actions` have the following keys: * help (optional): a short string the describe the action. * will be used in various context, like as menu name, tool tips on buttons, * and short description in help menu. * help_index (optional): a string used to sort action in help menu. * icon (optional): a short string that represent the icon that have to be used with this * action. this should mainly correspond to a Font_awesome class. * handler : a function which is called when the action is activated. It will receive at first parameter * a dictionary containing various handle to element of the notebook. * * action need to be registered with a **name** that can be use to refer to this action. * * * if `help` is not provided it will be derived by replacing any dash by space * in the **name** of the action. It is advised to provide a prefix to action name to * avoid conflict the prefix should be all lowercase and end with a dot `.` * in the absence of a prefix the behavior of the action is undefined. * * All action provided by IPython are prefixed with `ipython.`. * * One can register extra actions or replace an existing action with another one is possible * but is considered undefined behavior. * **/ var _action = { 'run-select-next': { icon: 'fa-play', help : 'run cell, select below', help_index : 'ba', handler : function (env) { env.notebook.execute_cell_and_select_below(); } }, 'execute-in-place':{ help : 'run cell', help_index : 'bb', handler : function (env) { env.notebook.execute_cell(); } }, 'execute-and-insert-after':{ help : 'run cell, insert below', help_index : 'bc', handler : function (env) { env.notebook.execute_cell_and_insert_below(); } }, 'go-to-command-mode': { help : 'command mode', help_index : 'aa', handler : function (env) { env.notebook.command_mode(); } }, 'split-cell-at-cursor': { help : 'split cell', help_index : 'ea', handler : function (env) { env.notebook.split_cell(); } }, 'enter-edit-mode' : { help_index : 'aa', handler : function (env) { env.notebook.edit_mode(); } }, 'select-previous-cell' : { help_index : 'da', handler : function (env) { var index = env.notebook.get_selected_index(); if (index !== 0 && index !== null) { env.notebook.select_prev(); env.notebook.focus_cell(); } } }, 'select-next-cell' : { help_index : 'db', handler : function (env) { var index = env.notebook.get_selected_index(); if (index !== (env.notebook.ncells()-1) && index !== null) { env.notebook.select_next(); env.notebook.focus_cell(); } } }, 'cut-selected-cell' : { icon: 'fa-cut', help_index : 'ee', handler : function (env) { env.notebook.cut_cell(); } }, 'copy-selected-cell' : { icon: 'fa-copy', help_index : 'ef', handler : function (env) { env.notebook.copy_cell(); } }, 'paste-cell-before' : { help_index : 'eg', handler : function (env) { env.notebook.paste_cell_above(); } }, 'paste-cell-after' : { icon: 'fa-paste', help_index : 'eh', handler : function (env) { env.notebook.paste_cell_below(); } }, 'insert-cell-before' : { help_index : 'ec', handler : function (env) { env.notebook.insert_cell_above(); env.notebook.select_prev(); env.notebook.focus_cell(); } }, 'insert-cell-after' : { icon : 'fa-plus', help_index : 'ed', handler : function (env) { env.notebook.insert_cell_below(); env.notebook.select_next(); env.notebook.focus_cell(); } }, 'change-selected-cell-to-code-cell' : { help : 'to code', help_index : 'ca', handler : function (env) { env.notebook.to_code(); } }, 'change-selected-cell-to-markdown-cell' : { help : 'to markdown', help_index : 'cb', handler : function (env) { env.notebook.to_markdown(); } }, 'change-selected-cell-to-raw-cell' : { help : 'to raw', help_index : 'cc', handler : function (env) { env.notebook.to_raw(); } }, 'change-selected-cell-to-heading-1' : { help : 'to heading 1', help_index : 'cd', handler : function (env) { env.notebook.to_heading(undefined, 1); } }, 'change-selected-cell-to-heading-2' : { help : 'to heading 2', help_index : 'ce', handler : function (env) { env.notebook.to_heading(undefined, 2); } }, 'change-selected-cell-to-heading-3' : { help : 'to heading 3', help_index : 'cf', handler : function (env) { env.notebook.to_heading(undefined, 3); } }, 'change-selected-cell-to-heading-4' : { help : 'to heading 4', help_index : 'cg', handler : function (env) { env.notebook.to_heading(undefined, 4); } }, 'change-selected-cell-to-heading-5' : { help : 'to heading 5', help_index : 'ch', handler : function (env) { env.notebook.to_heading(undefined, 5); } }, 'change-selected-cell-to-heading-6' : { help : 'to heading 6', help_index : 'ci', handler : function (env) { env.notebook.to_heading(undefined, 6); } }, 'toggle-output-visibility-selected-cell' : { help : 'toggle output', help_index : 'gb', handler : function (env) { env.notebook.toggle_output(); } }, 'toggle-output-scrolling-selected-cell' : { help : 'toggle output scrolling', help_index : 'gc', handler : function (env) { env.notebook.toggle_output_scroll(); } }, 'move-selected-cell-down' : { icon: 'fa-arrow-down', help_index : 'eb', handler : function (env) { env.notebook.move_cell_down(); } }, 'move-selected-cell-up' : { icon: 'fa-arrow-up', help_index : 'ea', handler : function (env) { env.notebook.move_cell_up(); } }, 'toggle-line-number-selected-cell' : { help : 'toggle line numbers', help_index : 'ga', handler : function (env) { env.notebook.cell_toggle_line_numbers(); } }, 'show-keyboard-shortcut-help-dialog' : { help_index : 'ge', handler : function (env) { env.quick_help.show_keyboard_shortcuts(); } }, 'delete-cell': { help_index : 'ej', handler : function (env) { env.notebook.delete_cell(); } }, 'interrupt-kernel':{ icon: 'fa-stop', help_index : 'ha', handler : function (env) { env.notebook.kernel.interrupt(); } }, 'restart-kernel':{ icon: 'fa-repeat', help_index : 'hb', handler : function (env) { env.notebook.restart_kernel(); } }, 'undo-last-cell-deletion' : { help_index : 'ei', handler : function (env) { env.notebook.undelete_cell(); } }, 'merge-selected-cell-with-cell-after' : { help : 'merge cell below', help_index : 'ek', handler : function (env) { env.notebook.merge_cell_below(); } }, 'close-pager' : { help_index : 'gd', handler : function (env) { env.pager.collapse(); } } }; /** * A bunch of `Advance actions` for IPython. * Cf `Simple Action` plus the following properties. * * handler: first argument of the handler is the event that triggerd the action * (typically keypress). The handler is responsible for any modification of the * event and event propagation. * Is also responsible for returning false if the event have to be further ignored, * true, to tell keyboard manager that it ignored the event. * * the second parameter of the handler is the environemnt passed to Simple Actions * **/ var custom_ignore = { 'ignore':{ handler : function () { return true; } }, 'move-cursor-up-or-previous-cell':{ handler : function (env, event) { var index = env.notebook.get_selected_index(); var cell = env.notebook.get_cell(index); var cm = env.notebook.get_selected_cell().code_mirror; var cur = cm.getCursor(); if (cell && cell.at_top() && index !== 0 && cur.ch === 0) { if(event){ event.preventDefault(); } env.notebook.command_mode(); env.notebook.select_prev(); env.notebook.edit_mode(); cm = env.notebook.get_selected_cell().code_mirror; cm.setCursor(cm.lastLine(), 0); } return false; } }, 'move-cursor-down-or-next-cell':{ handler : function (env, event) { var index = env.notebook.get_selected_index(); var cell = env.notebook.get_cell(index); if (cell.at_bottom() && index !== (env.notebook.ncells()-1)) { if(event){ event.preventDefault(); } env.notebook.command_mode(); env.notebook.select_next(); env.notebook.edit_mode(); var cm = env.notebook.get_selected_cell().code_mirror; cm.setCursor(0, 0); } return false; } }, 'scroll-down': { handler: function(env, event) { if(event){ event.preventDefault(); } return env.notebook.scroll_manager.scroll(1); }, }, 'scroll-up': { handler: function(env, event) { if(event){ event.preventDefault(); } return env.notebook.scroll_manager.scroll(-1); }, }, 'save-notebook':{ help: "Save and Checkpoint", help_index : 'fb', icon: 'fa-save', handler : function (env, event) { env.notebook.save_checkpoint(); if(event){ event.preventDefault(); } return false; } }, }; // private stuff that prepend `.ipython` to actions names // and uniformize/fill in missing pieces in of an action. var _prepare_handler = function(registry, subkey, source){ registry['ipython.'+subkey] = {}; registry['ipython.'+subkey].help = source[subkey].help||subkey.replace(/-/g,' '); registry['ipython.'+subkey].help_index = source[subkey].help_index; registry['ipython.'+subkey].icon = source[subkey].icon; return source[subkey].handler; }; // Will actually generate/register all the IPython actions var fun = function(){ var final_actions = {}; for(var k in _action){ // Js closure are function level not block level need to wrap in a IIFE // and append ipython to event name these things do intercept event so are wrapped // in a function that return false. var handler = _prepare_handler(final_actions, k, _action); (function(key, handler){ final_actions['ipython.'+key].handler = function(env, event){ handler(env); if(event){ event.preventDefault(); } return false; }; })(k, handler); } for(var k in custom_ignore){ // Js closure are function level not block level need to wrap in a IIFE // same as above, but decide for themselves wether or not they intercept events. var handler = _prepare_handler(final_actions, k, custom_ignore); (function(key, handler){ final_actions['ipython.'+key].handler = function(env, event){ return handler(env, event); }; })(k, handler); } return final_actions; }; ActionHandler.prototype._actions = fun(); /** * extend the environment variable that will be pass to handlers **/ ActionHandler.prototype.extend_env = function(env){ for(var k in env){ this.env[k] = env[k]; } }; ActionHandler.prototype.register = function(action, name, prefix){ /** * Register an `action` with an optional name and prefix. * * if name and prefix are not given they will be determined automatically. * if action if just a `function` it will be wrapped in an anonymous action. * * @return the full name to access this action . **/ action = this.normalise(action); if( !name ){ name = 'autogenerated-'+String(action.handler); } prefix = prefix || 'auto'; var full_name = prefix+'.'+name; this._actions[full_name] = action; return full_name; }; ActionHandler.prototype.normalise = function(data){ /** * given an `action` or `function`, return a normalised `action` * by setting all known attributes and removing unknown attributes; **/ if(typeof(data) === 'function'){ data = {handler:data}; } if(typeof(data.handler) !== 'function'){ throw('unknown datatype, cannot register'); } var _data = data; data = {}; data.handler = _data.handler; data.help = _data.help || ''; data.icon = _data.icon || ''; data.help_index = _data.help_index || ''; return data; }; ActionHandler.prototype.get_name = function(name_or_data){ /** * given an `action` or `name` of a action, return the name attached to this action. * if given the name of and corresponding actions does not exist in registry, return `null`. **/ if(typeof(name_or_data) === 'string'){ if(this.exists(name_or_data)){ return name_or_data; } else { return null; } } else { return this.register(name_or_data); } }; ActionHandler.prototype.get = function(name){ return this._actions[name]; }; ActionHandler.prototype.call = function(name, event, env){ return this._actions[name].handler(env|| this.env, event); }; ActionHandler.prototype.exists = function(name){ return (typeof(this._actions[name]) !== 'undefined'); }; return {init:ActionHandler}; });