diff --git a/IPython/html/static/notebook/js/outputarea.js b/IPython/html/static/notebook/js/outputarea.js index fe6422c..12f219f 100644 --- a/IPython/html/static/notebook/js/outputarea.js +++ b/IPython/html/static/notebook/js/outputarea.js @@ -326,7 +326,51 @@ var IPython = (function (IPython) { } return oa; }; - + + + OutputArea.prototype.create_output_subarea = function(md, classes) { + var subarea = $('
').addClass('output_subarea').addClass(classes); + if (md['isolated']) { + // Create an iframe to isolate the subarea from the rest of the + // document + var iframe = $('').addClass('box-flex1'); + iframe.css({'height':1, 'width':'100%', 'display':'block'}); + iframe.attr('frameborder', 0); + iframe.attr('scrolling', 'auto'); + + // Once the iframe is loaded, the subarea is dynamically inserted + iframe.on('load', function() { + // Workaround needed by Firefox, to properly render svg inside + // iframes, see http://stackoverflow.com/questions/10177190/ + // svg-dynamically-added-to-iframe-does-not-render-correctly + this.contentDocument.open(); + + // Insert the subarea into the iframe + // We must directly write the html. When using Jquery's append + // method, javascript is evaluated in the parent document and + // not in the iframe document. + this.contentDocument.write(subarea.html()); + + this.contentDocument.close(); + + var body = this.contentDocument.body; + // Adjust the iframe height automatically + iframe.height(body.scrollHeight + 'px'); + }); + + // Elements should be appended to the inner subarea and not to the + // iframe + iframe.append = function(that) { + subarea.append(that); + }; + + return iframe; + } else { + return subarea; + } + } + + OutputArea.prototype._append_javascript_error = function (err, container) { // display a message when a javascript error occurs in display output var msg = "Javascript error adding output!" @@ -460,7 +504,7 @@ var IPython = (function (IPython) { OutputArea.prototype.append_html = function (html, md, element) { - var toinsert = $("").addClass("output_subarea output_html rendered_html"); + var toinsert = this.create_output_subarea(md, "output_html rendered_html"); toinsert.append(html); element.append(toinsert); }; @@ -468,7 +512,7 @@ var IPython = (function (IPython) { OutputArea.prototype.append_javascript = function (js, md, container) { // We just eval the JS code, element appears in the local scope. - var element = $("").addClass("output_subarea"); + var element = this.create_output_subarea(md, ""); container.append(element); // Div for js shouldn't be drawn, as it will add empty height to the area. container.hide(); @@ -483,7 +527,7 @@ var IPython = (function (IPython) { OutputArea.prototype.append_text = function (data, md, element, extra_class) { - var toinsert = $("").addClass("output_subarea output_text"); + var toinsert = this.create_output_subarea(md, "output_text"); // escape ANSI & HTML specials in plaintext: data = utils.fixConsole(data); data = utils.fixCarriageReturn(data); @@ -497,7 +541,7 @@ var IPython = (function (IPython) { OutputArea.prototype.append_svg = function (svg, md, element) { - var toinsert = $("").addClass("output_subarea output_svg"); + var toinsert = this.create_output_subarea(md, "output_svg"); toinsert.append(svg); element.append(toinsert); }; @@ -531,7 +575,7 @@ var IPython = (function (IPython) { OutputArea.prototype.append_png = function (png, md, element) { - var toinsert = $("").addClass("output_subarea output_png"); + var toinsert = this.create_output_subarea(md, "output_png"); var img = $("").attr('src','data:image/png;base64,'+png); if (md['height']) { img.attr('height', md['height']); @@ -546,7 +590,7 @@ var IPython = (function (IPython) { OutputArea.prototype.append_jpeg = function (jpeg, md, element) { - var toinsert = $("").addClass("output_subarea output_jpeg"); + var toinsert = this.create_output_subarea(md, "output_jpeg"); var img = $("").attr('src','data:image/jpeg;base64,'+jpeg); if (md['height']) { img.attr('height', md['height']); @@ -563,7 +607,7 @@ var IPython = (function (IPython) { OutputArea.prototype.append_latex = function (latex, md, element) { // This method cannot do the typesetting because the latex first has to // be on the page. - var toinsert = $("").addClass("output_subarea output_latex"); + var toinsert = this.create_output_subarea(md, "output_latex"); toinsert.append(latex); element.append(toinsert); }; diff --git a/IPython/html/tests/casperjs/test_cases/isolated_svg.js b/IPython/html/tests/casperjs/test_cases/isolated_svg.js new file mode 100644 index 0000000..eb0156c --- /dev/null +++ b/IPython/html/tests/casperjs/test_cases/isolated_svg.js @@ -0,0 +1,42 @@ +// +// Test svg isolation +// An object whose metadata contains an "isolated" tag must be isolated +// from the rest of the document. In the case of inline SVGs, this means +// that multiple SVGs have different scopes. This test checks that there +// are no CSS leaks between two isolated SVGs. +// + +casper.notebook_test(function () { + this.evaluate(function () { + var cell = IPython.notebook.get_cell(0); + cell.set_text( "from IPython.core.display import SVG, display_svg\n" + + "s1 = '''" + + "'''\n" + + "s2 = '''" + + "'''\n" + + "display_svg(SVG(s1), metadata=dict(isolated=True))\n" + + "display_svg(SVG(s2), metadata=dict(isolated=True))\n" + ); + cell.execute(); + }); + + this.wait_for_output(0); + + this.then(function () { + var colors = this.evaluate(function () { + var colors = []; + var ifr = __utils__.findAll("iframe"); + var svg1 = ifr[0].contentWindow.document.getElementById('r1'); + colors[0] = window.getComputedStyle(svg1)["fill"]; + var svg2 = ifr[1].contentWindow.document.getElementById('r2'); + colors[1] = window.getComputedStyle(svg2)["fill"]; + return colors; + }); + + this.test.assertEquals(colors[0], '#ff0000', 'First svg should be red'); + this.test.assertEquals(colors[1], '#000000', 'Second svg should be black'); + }); +});