diff --git a/notebook/static/base/js/dialog.js b/notebook/static/base/js/dialog.js index aba7bd4b9..5cb7a9c75 100644 --- a/notebook/static/base/js/dialog.js +++ b/notebook/static/base/js/dialog.js @@ -49,7 +49,7 @@ define(function(require) { .addClass("modal-content") .appendTo(dialog); if(typeof(options.body) === 'string' && options.sanitize !== false){ - options.body = $("

").text(options.body) + options.body = $("

").text(options.body); } dialog_content.append( $("

") diff --git a/notebook/static/notebook/js/actions.js b/notebook/static/notebook/js/actions.js index fe3d21ba5..85994da25 100644 --- a/notebook/static/notebook/js/actions.js +++ b/notebook/static/notebook/js/actions.js @@ -337,9 +337,169 @@ define(function(require){ handler : function(env){ env.notebook.show_command_palette(); } + }, + 'search-and-replace-dialog': { + help: 'search and replace', + handler: function(env){ + var search = $("") + .addClass('form-control') + .attr('placeholder','Search'); + var repl = $("") + .addClass('form-control') + .attr('placeholder','replace'); + //var submit = $("").attr('type', 'submit'); + var body = $('
').css('max-height','60vh').css('overflow','auto'); + var form = $('
') + .append($('
').addClass('form-group').append(search)) + .append($('
').addClass('form-group').append(repl)) + //.append(submit) + .append(body); + + var onsubmit = function(event){ + var sre = search.val(); + var replace = repl.val(); + if(!sre){ + return false; + } + var cells = env.notebook.get_cells(); + var arr = []; + for(var c=0; c < cells.length; c++){ + //console.log("looping through cell", c); + var oldvalue = cells[c].code_mirror.getValue(); + var newvalue = oldvalue.replace(new RegExp(sre, 'g'), replace); + cells[c].code_mirror.setValue(newvalue); + if(cells[c].cell_type === 'markdown'){ + //cells[c].unrender(); + cells[c].rendered = false; + cells[c].render(); + } + + } + + }; + var ontype = function(){ + + var sre = search.val(); + // abort on invalid RE + if(!sre){ + body.empty(); + body.append($('

').text('No matches, invalid or empty regular expression')); + return; + } + + try { + new RegExp(sre); + } catch (e){ + body.empty(); + body.append($('

').text('No matches, invalid or empty regular expression')); + return; + } + var replace = repl.val(); + var cells = env.notebook.get_cells(); + var arr = []; + for(var c=0; c < cells.length; c++){ + //console.log("looping through cell", c); + arr = arr.concat(cells[c].code_mirror.getValue().split('\n')); + } + + var html = []; + for(var r=0; r < arr.length; r++){ + var matches = getMatches(sre, arr[r]); + //console.log("looping through line", r, "matches", matches); + for(var mindex=0; mindex < matches.length ; mindex++){ + var start = matches[mindex][0]; + var stop = matches[mindex][1]; + //console.log(matches[mindex], arr[r].slice(start, stop)); + var init = arr[r].slice(start, stop); + var replaced = init.replace( new RegExp(sre), replace); + html.push([cutBefore(arr[r].slice(0, start)), arr[r].slice(start, stop), replaced, cutAfter(arr[r].slice(stop))]); + } + } + body.empty(); + for(var rindex=0; rindex').addClass('replace-preview') + .append(html[rindex][0]) + .append($('').addClass('match').text(html[rindex][1])); + if(replace){ + pre.append($('').addClass('replace').text(html[rindex][2])); + pre.addClass('replace'); + } + pre.append(html[rindex][3]); + body.append(pre); + } + return false; + }; + + + search.keypress(function (e) { + if (e.which == 13) {//enter + repl.focus(); + } + }); + search.on('input', ontype); + repl.on('input', ontype); + var mod = IPython.dialog.modal({ + show: false, + title: "Search and Replace", + body:form, + keyboard_manager: env.notebook.keyboard_manager, + buttons:{ + 'Do it':{ class: "btn-primary", + click: function(event){onsubmit(event); return true;} + } + }, + open: function(){ + search.focus(); + } + }); + + repl.keypress(function (e) { + if (e.which == 13) {//enter + onsubmit(); + mod.modal('hide'); + } + }); + mod.modal('show'); + } } }; + + var cutAfter = function(string){ + if(string.length > 13){ + return string.slice(0, 10)+'...'; + } + return string; + }; + + var cutBefore = function(string){ + if(string.length > 13){ + return '...'+string.slice(-10); + } + return string; + }; + + var getMatches = function(re, string){ + try { + re = new RegExp(re, 'g');// have to global or infinite loop + } catch (e){ + return []; + } + //debugger; + var res = []; + var match; + // yes this is a castin != + var escape_hatch = 0; + while((match = re.exec(string)) !== null) { + res.push([match.index, match.index+match[0].length]); + escape_hatch++; + if(escape_hatch > 1000){ + console.warn("More than 1000 matches, aborting"); + break; + } + } + return res; + }; /** diff --git a/notebook/static/notebook/js/commandpalette.js b/notebook/static/notebook/js/commandpalette.js index 1319c0e28..0507d6c67 100644 --- a/notebook/static/notebook/js/commandpalette.js +++ b/notebook/static/notebook/js/commandpalette.js @@ -81,8 +81,7 @@ define(function(require){ // click on button trigger de-focus on mouse up. // or somethign like that. setTimeout(function(){input.focus();}, 100); - }) - .on("hide.bs.modal", before_close); + }); notebook.keyboard_manager.disable(); @@ -103,6 +102,9 @@ define(function(require){ } before_close.ok = true; // avoid double call. }; + + mod.on("hide.bs.modal", before_close) + .on("hidden.bs.modal", before_close); // will be trigger when user select action var onSubmit = function(node, query, result, resultCount) { @@ -152,7 +154,6 @@ define(function(require){ // now src is the right structure for typeahead - input.typeahead({ emptyTemplate: "No results found for

{{query}}
", maxItem: 1e3, diff --git a/notebook/static/notebook/less/notebook.less b/notebook/static/notebook/less/notebook.less index a935cc055..afa700582 100644 --- a/notebook/static/notebook/less/notebook.less +++ b/notebook/static/notebook/less/notebook.less @@ -99,3 +99,27 @@ kbd { padding-top: 1px; padding-bottom: 1px; } + +.replace-preview .match, .replace-preview .replace { + background-color:lightblue; + border-color: darken(lightblue, 20%); + border-style: solid; + border-width: 1px; + border-radius: @border-radius-base +} + +.replace-preview.replace { + + & .match{ + //display: none; + background-color:salmon; + text-decoration: line-through; + border-color: darken(salmon, 20%); + } + + & .replace{ + background-color: green; + background-color:lightGreen; + border-color: darken(lightGreen, 20%); + } +} diff --git a/notebook/templates/notebook.html b/notebook/templates/notebook.html index 268b50672..4a3bbfa9c 100644 --- a/notebook/templates/notebook.html +++ b/notebook/templates/notebook.html @@ -328,6 +328,6 @@ data-notebook-path="{{notebook_path}}" - + {% endblock %} diff --git a/notebook/templates/page.html b/notebook/templates/page.html index 95dee7186..42c136dfe 100644 --- a/notebook/templates/page.html +++ b/notebook/templates/page.html @@ -24,7 +24,7 @@ {% endif %} baseUrl: '{{static_url("", include_version=False)}}', paths: { - 'auth/js/main': 'auth/js/main.min', + 'auth/js/main': 'auth/js/main', custom : '{{ base_url }}custom', nbextensions : '{{ base_url }}nbextensions', widgets : '{{ base_url }}deprecatedwidgets',