diff --git a/usability/search-replace/icon.png b/usability/search-replace/icon.png new file mode 100644 index 0000000..ebe66aa Binary files /dev/null and b/usability/search-replace/icon.png differ diff --git a/usability/search-replace/main.css b/usability/search-replace/main.css new file mode 100644 index 0000000..b285266 --- /dev/null +++ b/usability/search-replace/main.css @@ -0,0 +1,26 @@ + + +#searchbar-wrapper { + position: absolute; + background-color: #ffffff; + border-style: solid; + border-radius: 5px; + border-color: #aaaaaa; + padding-left: 10px; + width:430px; + top: 100px; + right: 5px; + float: right; + opacity: 1; + z-index: 100; + font-size: 12px; + font-weight: bold; +} + +.searchbar_buttons { + height: 30px; +} + +.searchbar_input { + width: 200px; +} \ No newline at end of file diff --git a/usability/search-replace/main.js b/usability/search-replace/main.js new file mode 100755 index 0000000..7f665e0 --- /dev/null +++ b/usability/search-replace/main.js @@ -0,0 +1,217 @@ +// Simple search&replace extension based on a codemirror addon. +// Adds a search box to the notebook toolbar and selects search word if found. + +define([ + 'base/js/namespace', + 'jquery', + 'require', + 'codemirror/addon/search/search', + 'codemirror/addon/search/searchcursor' +], function(IPython, $, require) { + "use strict"; + + /** + * Search for a string within the complete notebook, starting at current cell or + * CodeMirror selection + * @param hotkey + * @returns {boolean} + */ + var search = function(hotkey) { + /* execute search operation only after pressing return key or button click */ + if (hotkey != 0 && hotkey != 13) { + return false; + } + + var cell = IPython.notebook.get_selected_cell(); + if (cell.rendered == true && cell.cell_type == "markdown" ) cell.unrender(); + var findString = $('#searchbar_search_text').val(); + var cur = cell.code_mirror.getCursor(); + + var case_sensitive = !$('#searchbar_case_sensitive').hasClass('active'); + var find = cell.code_mirror.getSearchCursor(findString,cur, case_sensitive); + if (find.find() == true) { + cell.code_mirror.setSelection(find.pos.from,find.pos.to); + cell.code_mirror.focus(); + } else { + var ncells = IPython.notebook.ncells(); + if ( IPython.notebook.get_selected_index()+1 == ncells) { + cell.code_mirror.setCursor({line:0, ch:0}); + cell.code_mirror.focus(); + } else { + IPython.notebook.select_next(); + IPython.notebook.edit_mode(); + cell.code_mirror.setCursor({line:0, ch:0}); + return search(hotkey); + } + } + }; + + /** + * Replace a string within the complete notebook, starting at current cell or + * CodeMirror selection + * @param hotkey + * @returns {boolean} + */ + var replace = function(hotkey) { + /* execute replace operation only after pressing return key or button click */ + if (hotkey != 0 && hotkey != 13) { + return false; + } + + var cell = IPython.notebook.get_selected_cell(); + if (cell.rendered == true && cell.cell_type == "markdown" ) cell.unrender(); + + var findString = $('#searchbar_search_text').val(); + var replaceString = $('#searchbar_replace_text').val(); + var cur = cell.code_mirror.getCursor(); + + var case_sensitive = !$('#searchbar_case_sensitive').hasClass('active'); + var find = cell.code_mirror.getSearchCursor(findString,cur, case_sensitive); + if (find.find() == true) { + cell.code_mirror.setSelection(find.pos.from,find.pos.to); + cell.code_mirror.replaceSelection(replaceString); + cell.code_mirror.focus(); + } else { + var ncells = IPython.notebook.ncells(); + if ( IPython.notebook.get_selected_index()+1 == ncells) { + cell.code_mirror.setCursor({line:0, ch:0}); + cell.code_mirror.focus(); + } else { + IPython.notebook.select_next(); + IPython.notebook.edit_mode(); + cell.code_mirror.setCursor({line:0, ch:0}); + return replace(hotkey); + } + } + }; + + /** + * Create floating toolbar + * + */ + var create_searchbar_div = function () { + var btn = '
\ +
\ + \ + \ + \ + \ +
\ +
\ + \ + \ + \ +
\ +
'; + + var searchbar_wrapper = $('
') + .text("Searchbar") + .append(btn) + .draggable() + .append("
"); + + $("#header").append(searchbar_wrapper); + $("#searchbar-wrapper").css({'position' : 'absolute'}); + + $('#searchbar_search').on('click', function() { search(0); this.blur(); }) + .tooltip({ title : 'Search text' , delay: {show: 500, hide: 100}}); + $('#searchbar_case_sensitive').on('click', function() { this.blur(); }) + .tooltip({ title : 'Case sensitive' , delay: {show: 500, hide: 100}}); + $('#searchbar_replace').on('click', function() { replace(0); this.blur(); }) + .tooltip({ title : 'Replace text' , delay: {show: 500, hide: 100}}); + $('#searchbar_search_text').on('keyup', function(event) { search(event.keyCode);}); + $('#searchbar_replace_text').on('keyup', function(event) { replace(event.keyCode);}); + IPython.notebook.keyboard_manager.register_events($('#searchbar_search_text')); + IPython.notebook.keyboard_manager.register_events($('#searchbar_replace_text')); + }; + + /** + * Show/hide toolbar + * + */ + var toggle_toolbar = function() { + var dom = $("#searchbar-wrapper"); + + if (dom.is(':visible')) { + $('#toggle_searchbar').removeClass('active').blur(); + dom.hide(); + } else { + $('#toggle_searchbar').addClass('active'); + dom.show(); + } + + if (dom.length === 0) { + create_searchbar_div() + } + + }; + + IPython.toolbar.add_buttons_group([ + { + id : 'toggle_searchbar', + label : 'Toggle Search Toolbar', + icon : 'fa-search', + callback : function () { + toggle_toolbar(); + $('#searchbar_search_text') + } + } + ]); + + $("#toggle_searchbar").css({'outline' : 'none'}); + + /** + * Add CSS file + * + * @param name filename + */ + var load_css = function (name) { + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = require.toUrl(name); + document.getElementsByTagName("head")[0].appendChild(link); + }; + + /** + * Initialize extension + */ + var load_ipython_extension = function() { + load_css('./main.css'); + + /* Add keyboard shortcuts for search and replace. + * Hardcoded for now, should be configurable + */ + var add_shortcuts = { + 'f' : { + help : 'search', + help_index : 'se', + handler : function() { + if (!$('#toggle_searchbar').hasClass('active')) { + toggle_toolbar(); + } + $('#searchbar_search_text').focus(); + return false; + } + }, + 'r' : { + help : 'replace', + help_index : 're', + handler : function() { + if (!$('#toggle_searchbar').hasClass('active')) { + toggle_toolbar(); + } + $('#searchbar_replace_text').focus(); + return false; + } + } + }; + + IPython.keyboard_manager.command_shortcuts.add_shortcuts(add_shortcuts); + }; + + var extension = { + load_ipython_extension : load_ipython_extension + }; + return extension; +}); diff --git a/usability/search-replace/search-replace.yaml b/usability/search-replace/search-replace.yaml new file mode 100644 index 0000000..3ee8054 --- /dev/null +++ b/usability/search-replace/search-replace.yaml @@ -0,0 +1,7 @@ +Type: IPython Notebook Extension +Name: Search and Replace +Description: Search and replace +Link: https://github.com/ipython-contrib/IPython-notebook-extensions/wiki +Icon: icon.png +Main: main.js +Compatibility: 3.x