We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Change iframe call for rendering in Jupyter Book
<!--
Hi! 
If you have: 
 -1 Questions about how to use Nilearn or 
 -2 Need analysis suggestions & recommendations?

A bunch of fMRI researchers hang out at Neurostars (http://neurostars.org/). 
Post those questions there. 
Add the tag `nilearn` (we get an email from Neurostars if you do).

Posting them here makes life more complicated for the Nilearn developers.  
-->

<!-- 
For the Feature Request,
Include the following:
------------------------
What would you like changed/added and why?
What would be the benefit? Does the change make something easier to use?
Clarifies something?
If it is a new feature, what is the benefit? 
-->

I would like to render interactive brainsprites generated from `view_img` in [Jupyter Book](https://github.com/jupyter/jupyter-book). Jupyter Book is built using Jekyll, and converts executed notebooks to markdown using nbconvert. The nbconvert is then converted to stylized HTML using kramdown.

My difficulty comes that kramdown [cannot currently handle unquoted attributes](https://github.com/jupyter/jupyter-book/issues/72) (even though these are valid HTML!). When we generate the iframe in which the brainsprite viewer is embedded here in nilearn, [we don't quote the `width` and `height` attributes](https://github.com/nilearn/nilearn/blob/ed86f29305a264693156826f0e429b2d6281eeaf/nilearn/plotting/js_plotting_utils.py#L126).

I'd like to open a simple fix to quote these two attributes, and smooth nilearn use with Jupyter Book. Would that be alright?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `nilearn/plotting/js_plotting_utils.py`
Content:
```
1 """
2 Helper functions for views, i.e. interactive plots from html_surface and
3 html_connectome.
4 """
5 
6 import os
7 import base64
8 import webbrowser
9 import tempfile
10 import warnings
11 import subprocess
12 from string import Template
13 import weakref
14 try:
15     from html import escape  # Unavailable in Py2
16 except ImportError:  # Can be removed once we EOL Py2 support for NiLearn
17     from cgi import escape  # Deprecated in Py3, necessary for Py2
18 
19 import matplotlib as mpl
20 import numpy as np
21 from matplotlib import cm as mpl_cm
22 
23 from .._utils.extmath import fast_abs_percentile
24 from .._utils.param_validation import check_threshold
25 from .. import surface
26 
27 
28 def add_js_lib(html, embed_js=True):
29     """
30     Add javascript libraries to html template.
31 
32     if embed_js is True, jquery and plotly are embedded in resulting page.
33     otherwise, they are loaded via CDNs.
34     """
35     js_dir = os.path.join(os.path.dirname(__file__), 'data', 'js')
36     with open(os.path.join(js_dir, 'surface-plot-utils.js')) as f:
37         js_utils = f.read()
38     if not embed_js:
39         js_lib = """
40         <script
41         src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js">
42         </script>
43         <script src="https://cdn.plot.ly/plotly-gl3d-latest.min.js"></script>
44         <script>
45         {}
46         </script>
47         """.format(js_utils)
48     else:
49         with open(os.path.join(js_dir, 'jquery.min.js')) as f:
50             jquery = f.read()
51         with open(os.path.join(js_dir, 'plotly-gl3d-latest.min.js')) as f:
52             plotly = f.read()
53         js_lib = """
54         <script>{}</script>
55         <script>{}</script>
56         <script>
57         {}
58         </script>
59         """.format(jquery, plotly, js_utils)
60     if not isinstance(html, Template):
61         html = Template(html)
62     return html.safe_substitute({'INSERT_JS_LIBRARIES_HERE': js_lib})
63 
64 
65 def get_html_template(template_name):
66     """Get an HTML file from package data"""
67     template_path = os.path.join(
68         os.path.dirname(__file__), 'data', 'html', template_name)
69     with open(template_path, 'rb') as f:
70         return Template(f.read().decode('utf-8'))
71 
72 
73 def _remove_after_n_seconds(file_name, n_seconds):
74     script = os.path.join(os.path.dirname(__file__), 'rm_file.py')
75     subprocess.Popen(['python', script, file_name, str(n_seconds)])
76 
77 
78 class HTMLDocument(object):
79     """
80     Embeds a plot in a web page.
81 
82     If you are running a Jupyter notebook, the plot will be displayed
83     inline if this object is the output of a cell.
84     Otherwise, use open_in_browser() to open it in a web browser (or
85     save_as_html("filename.html") to save it as an html file).
86 
87     use str(document) or document.html to get the content of the web page,
88     and document.get_iframe() to have it wrapped in an iframe.
89 
90     """
91     _all_open_html_repr = weakref.WeakSet()
92 
93     def __init__(self, html, width=600, height=400):
94         self.html = html
95         self.width = width
96         self.height = height
97         self._temp_file = None
98         self._check_n_open()
99 
100     def _check_n_open(self):
101         HTMLDocument._all_open_html_repr.add(self)
102         if len(HTMLDocument._all_open_html_repr) > 9:
103             warnings.warn('It seems you have created more than 10 '
104                           'nilearn views. As each view uses dozens '
105                           'of megabytes of RAM, you might want to '
106                           'delete some of them.')
107 
108     def resize(self, width, height):
109         """Resize the plot displayed in a Jupyter notebook."""
110         self.width, self.height = width, height
111         return self
112 
113     def get_iframe(self, width=None, height=None):
114         """
115         Get the document wrapped in an inline frame.
116 
117         For inserting in another HTML page of for display in a Jupyter
118         notebook.
119 
120         """
121         if width is None:
122             width = self.width
123         if height is None:
124             height = self.height
125         escaped = escape(self.html, quote=True)
126         wrapped = ('<iframe srcdoc="{}" width={} height={} '
127                    'frameBorder="0"></iframe>').format(escaped, width, height)
128         return wrapped
129 
130     def get_standalone(self):
131         """ Get the plot in an HTML page."""
132         return self.html
133 
134     def _repr_html_(self):
135         """
136         Used by the Jupyter notebook.
137 
138         Users normally won't call this method explicitely.
139         """
140         return self.get_iframe()
141 
142     def __str__(self):
143         return self.html
144 
145     def save_as_html(self, file_name):
146         """
147         Save the plot in an HTML file, that can later be opened in a browser.
148         """
149         with open(file_name, 'wb') as f:
150             f.write(self.html.encode('utf-8'))
151 
152     def open_in_browser(self, file_name=None, temp_file_lifetime=30):
153         """
154         Save the plot to a temporary HTML file and open it in a browser.
155 
156         Parameters
157         ----------
158 
159         file_name : str, optional
160             .html file to use as temporary file
161 
162         temp_file_lifetime : float, optional (default=30.)
163             Time, in seconds, after which the temporary file is removed.
164             If None, it is never removed.
165 
166         """
167         if file_name is None:
168             fd, file_name = tempfile.mkstemp('.html', 'nilearn_surface_plot_')
169             os.close(fd)
170         self.save_as_html(file_name)
171         self._temp_file = file_name
172         file_size = os.path.getsize(file_name) / 1e6
173         if temp_file_lifetime is None:
174             print(("Saved HTML in temporary file: {}\n"
175                    "file size is {:.1f}M, delete it when you're done, "
176                    "for example by calling this.remove_temp_file").format(
177                        file_name, file_size))
178         else:
179             _remove_after_n_seconds(self._temp_file, temp_file_lifetime)
180         webbrowser.open('file://{}'.format(file_name))
181 
182     def remove_temp_file(self):
183         """
184         Remove the temporary file created by `open_in_browser`, if necessary.
185         """
186         if self._temp_file is None:
187             return
188         if not os.path.isfile(self._temp_file):
189             return
190         os.remove(self._temp_file)
191         print('removed {}'.format(self._temp_file))
192         self._temp_file = None
193 
194 
195 def colorscale(cmap, values, threshold=None, symmetric_cmap=True,
196                vmax=None, vmin=None):
197     """Normalize a cmap, put it in plotly format, get threshold and range."""
198     cmap = mpl_cm.get_cmap(cmap)
199     abs_values = np.abs(values)
200     if not symmetric_cmap and (values.min() < 0):
201         warnings.warn('you have specified symmetric_cmap=False '
202                       'but the map contains negative values; '
203                       'setting symmetric_cmap to True')
204         symmetric_cmap = True
205     if symmetric_cmap and vmin is not None:
206         warnings.warn('vmin cannot be chosen when cmap is symmetric')
207         vmin = None
208     if threshold is not None:
209         if vmin is not None:
210             warnings.warn('choosing both vmin and a threshold is not allowed; '
211                           'setting vmin to 0')
212         vmin = 0
213     if vmax is None:
214         vmax = abs_values.max()
215     if symmetric_cmap:
216         vmin = - vmax
217     if vmin is None:
218         vmin = values.min()
219     norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
220     cmaplist = [cmap(i) for i in range(cmap.N)]
221     abs_threshold = None
222     if threshold is not None:
223         abs_threshold = check_threshold(threshold, values, fast_abs_percentile)
224         istart = int(norm(-abs_threshold, clip=True) * (cmap.N - 1))
225         istop = int(norm(abs_threshold, clip=True) * (cmap.N - 1))
226         for i in range(istart, istop):
227             cmaplist[i] = (0.5, 0.5, 0.5, 1.)  # just an average gray color
228     our_cmap = mpl.colors.LinearSegmentedColormap.from_list(
229         'Custom cmap', cmaplist, cmap.N)
230     x = np.linspace(0, 1, 100)
231     rgb = our_cmap(x, bytes=True)[:, :3]
232     rgb = np.array(rgb, dtype=int)
233     colors = []
234     for i, col in zip(x, rgb):
235         colors.append([np.round(i, 3), "rgb({}, {}, {})".format(*col)])
236     return {
237         'colors': colors, 'vmin': vmin, 'vmax': vmax, 'cmap': our_cmap,
238         'norm': norm, 'abs_threshold': abs_threshold,
239         'symmetric_cmap': symmetric_cmap
240     }
241 
242 
243 def encode(a):
244     """Base64 encode a numpy array"""
245     try:
246         data = a.tobytes()
247     except AttributeError:
248         # np < 1.9
249         data = a.tostring()
250     return base64.b64encode(data).decode('utf-8')
251 
252 
253 def decode(b, dtype):
254     """Decode a numpy array encoded as Base64"""
255     return np.frombuffer(base64.b64decode(b.encode('utf-8')), dtype)
256 
257 
258 def mesh_to_plotly(mesh):
259     mesh = surface.load_surf_mesh(mesh)
260     x, y, z = map(encode, np.asarray(mesh[0].T, dtype='<f4'))
261     i, j, k = map(encode, np.asarray(mesh[1].T, dtype='<i4'))
262     info = {
263         "_x": x,
264         "_y": y,
265         "_z": z,
266         "_i": i,
267         "_j": j,
268         "_k": k,
269     }
270     return info
271 
272 
273 def to_color_strings(colors):
274     cmap = mpl.colors.ListedColormap(colors)
275     colors = cmap(np.arange(cmap.N))[:, :3]
276     colors = np.asarray(colors * 255, dtype='uint8')
277     colors = ['#{:02x}{:02x}{:02x}'.format(*row) for row in colors]
278     return colors
279
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/nilearn/plotting/js_plotting_utils.py b/nilearn/plotting/js_plotting_utils.py
--- a/nilearn/plotting/js_plotting_utils.py
+++ b/nilearn/plotting/js_plotting_utils.py
@@ -123,7 +123,7 @@
         if height is None:
             height = self.height
         escaped = escape(self.html, quote=True)
-        wrapped = ('<iframe srcdoc="{}" width={} height={} '
+        wrapped = ('<iframe srcdoc="{}" width="{}" height="{}" '
                    'frameBorder="0"></iframe>').format(escaped, width, height)
         return wrapped

{"golden_diff": "diff --git a/nilearn/plotting/js_plotting_utils.py b/nilearn/plotting/js_plotting_utils.py\n--- a/nilearn/plotting/js_plotting_utils.py\n+++ b/nilearn/plotting/js_plotting_utils.py\n@@ -123,7 +123,7 @@\n         if height is None:\n             height = self.height\n         escaped = escape(self.html, quote=True)\n-        wrapped = ('<iframe srcdoc=\"{}\" width={} height={} '\n+        wrapped = ('<iframe srcdoc=\"{}\" width=\"{}\" height=\"{}\" '\n                    'frameBorder=\"0\"></iframe>').format(escaped, width, height)\n         return wrapped\n", "issue": "Change iframe call for rendering in Jupyter Book\n<!--\r\nHi! \r\nIf you have: \r\n -1 Questions about how to use Nilearn or \r\n -2 Need analysis suggestions & recommendations?\r\n\r\nA bunch of fMRI researchers hang out at Neurostars (http://neurostars.org/). \r\nPost those questions there. \r\nAdd the tag `nilearn` (we get an email from Neurostars if you do).\r\n\r\nPosting them here makes life more complicated for the Nilearn developers.  \r\n-->\r\n\r\n<!-- \r\nFor the Feature Request,\r\nInclude the following:\r\n------------------------\r\nWhat would you like changed/added and why?\r\nWhat would be the benefit? Does the change make something easier to use?\r\nClarifies something?\r\nIf it is a new feature, what is the benefit? \r\n-->\r\n\r\nI would like to render interactive brainsprites generated from `view_img` in [Jupyter Book](https://github.com/jupyter/jupyter-book). Jupyter Book is built using Jekyll, and converts executed notebooks to markdown using nbconvert. The nbconvert is then converted to stylized HTML using kramdown.\r\n\r\nMy difficulty comes that kramdown [cannot currently handle unquoted attributes](https://github.com/jupyter/jupyter-book/issues/72) (even though these are valid HTML!). When we generate the iframe in which the brainsprite viewer is embedded here in nilearn, [we don't quote the `width` and `height` attributes](https://github.com/nilearn/nilearn/blob/ed86f29305a264693156826f0e429b2d6281eeaf/nilearn/plotting/js_plotting_utils.py#L126).\r\n\r\nI'd like to open a simple fix to quote these two attributes, and smooth nilearn use with Jupyter Book. Would that be alright?\r\n\n", "before_files": [{"content": "\"\"\"\nHelper functions for views, i.e. interactive plots from html_surface and\nhtml_connectome.\n\"\"\"\n\nimport os\nimport base64\nimport webbrowser\nimport tempfile\nimport warnings\nimport subprocess\nfrom string import Template\nimport weakref\ntry:\n    from html import escape  # Unavailable in Py2\nexcept ImportError:  # Can be removed once we EOL Py2 support for NiLearn\n    from cgi import escape  # Deprecated in Py3, necessary for Py2\n\nimport matplotlib as mpl\nimport numpy as np\nfrom matplotlib import cm as mpl_cm\n\nfrom .._utils.extmath import fast_abs_percentile\nfrom .._utils.param_validation import check_threshold\nfrom .. import surface\n\n\ndef add_js_lib(html, embed_js=True):\n    \"\"\"\n    Add javascript libraries to html template.\n\n    if embed_js is True, jquery and plotly are embedded in resulting page.\n    otherwise, they are loaded via CDNs.\n    \"\"\"\n    js_dir = os.path.join(os.path.dirname(__file__), 'data', 'js')\n    with open(os.path.join(js_dir, 'surface-plot-utils.js')) as f:\n        js_utils = f.read()\n    if not embed_js:\n        js_lib = \"\"\"\n        <script\n        src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\">\n        </script>\n        <script src=\"https://cdn.plot.ly/plotly-gl3d-latest.min.js\"></script>\n        <script>\n        {}\n        </script>\n        \"\"\".format(js_utils)\n    else:\n        with open(os.path.join(js_dir, 'jquery.min.js')) as f:\n            jquery = f.read()\n        with open(os.path.join(js_dir, 'plotly-gl3d-latest.min.js')) as f:\n            plotly = f.read()\n        js_lib = \"\"\"\n        <script>{}</script>\n        <script>{}</script>\n        <script>\n        {}\n        </script>\n        \"\"\".format(jquery, plotly, js_utils)\n    if not isinstance(html, Template):\n        html = Template(html)\n    return html.safe_substitute({'INSERT_JS_LIBRARIES_HERE': js_lib})\n\n\ndef get_html_template(template_name):\n    \"\"\"Get an HTML file from package data\"\"\"\n    template_path = os.path.join(\n        os.path.dirname(__file__), 'data', 'html', template_name)\n    with open(template_path, 'rb') as f:\n        return Template(f.read().decode('utf-8'))\n\n\ndef _remove_after_n_seconds(file_name, n_seconds):\n    script = os.path.join(os.path.dirname(__file__), 'rm_file.py')\n    subprocess.Popen(['python', script, file_name, str(n_seconds)])\n\n\nclass HTMLDocument(object):\n    \"\"\"\n    Embeds a plot in a web page.\n\n    If you are running a Jupyter notebook, the plot will be displayed\n    inline if this object is the output of a cell.\n    Otherwise, use open_in_browser() to open it in a web browser (or\n    save_as_html(\"filename.html\") to save it as an html file).\n\n    use str(document) or document.html to get the content of the web page,\n    and document.get_iframe() to have it wrapped in an iframe.\n\n    \"\"\"\n    _all_open_html_repr = weakref.WeakSet()\n\n    def __init__(self, html, width=600, height=400):\n        self.html = html\n        self.width = width\n        self.height = height\n        self._temp_file = None\n        self._check_n_open()\n\n    def _check_n_open(self):\n        HTMLDocument._all_open_html_repr.add(self)\n        if len(HTMLDocument._all_open_html_repr) > 9:\n            warnings.warn('It seems you have created more than 10 '\n                          'nilearn views. As each view uses dozens '\n                          'of megabytes of RAM, you might want to '\n                          'delete some of them.')\n\n    def resize(self, width, height):\n        \"\"\"Resize the plot displayed in a Jupyter notebook.\"\"\"\n        self.width, self.height = width, height\n        return self\n\n    def get_iframe(self, width=None, height=None):\n        \"\"\"\n        Get the document wrapped in an inline frame.\n\n        For inserting in another HTML page of for display in a Jupyter\n        notebook.\n\n        \"\"\"\n        if width is None:\n            width = self.width\n        if height is None:\n            height = self.height\n        escaped = escape(self.html, quote=True)\n        wrapped = ('<iframe srcdoc=\"{}\" width={} height={} '\n                   'frameBorder=\"0\"></iframe>').format(escaped, width, height)\n        return wrapped\n\n    def get_standalone(self):\n        \"\"\" Get the plot in an HTML page.\"\"\"\n        return self.html\n\n    def _repr_html_(self):\n        \"\"\"\n        Used by the Jupyter notebook.\n\n        Users normally won't call this method explicitely.\n        \"\"\"\n        return self.get_iframe()\n\n    def __str__(self):\n        return self.html\n\n    def save_as_html(self, file_name):\n        \"\"\"\n        Save the plot in an HTML file, that can later be opened in a browser.\n        \"\"\"\n        with open(file_name, 'wb') as f:\n            f.write(self.html.encode('utf-8'))\n\n    def open_in_browser(self, file_name=None, temp_file_lifetime=30):\n        \"\"\"\n        Save the plot to a temporary HTML file and open it in a browser.\n\n        Parameters\n        ----------\n\n        file_name : str, optional\n            .html file to use as temporary file\n\n        temp_file_lifetime : float, optional (default=30.)\n            Time, in seconds, after which the temporary file is removed.\n            If None, it is never removed.\n\n        \"\"\"\n        if file_name is None:\n            fd, file_name = tempfile.mkstemp('.html', 'nilearn_surface_plot_')\n            os.close(fd)\n        self.save_as_html(file_name)\n        self._temp_file = file_name\n        file_size = os.path.getsize(file_name) / 1e6\n        if temp_file_lifetime is None:\n            print((\"Saved HTML in temporary file: {}\\n\"\n                   \"file size is {:.1f}M, delete it when you're done, \"\n                   \"for example by calling this.remove_temp_file\").format(\n                       file_name, file_size))\n        else:\n            _remove_after_n_seconds(self._temp_file, temp_file_lifetime)\n        webbrowser.open('file://{}'.format(file_name))\n\n    def remove_temp_file(self):\n        \"\"\"\n        Remove the temporary file created by `open_in_browser`, if necessary.\n        \"\"\"\n        if self._temp_file is None:\n            return\n        if not os.path.isfile(self._temp_file):\n            return\n        os.remove(self._temp_file)\n        print('removed {}'.format(self._temp_file))\n        self._temp_file = None\n\n\ndef colorscale(cmap, values, threshold=None, symmetric_cmap=True,\n               vmax=None, vmin=None):\n    \"\"\"Normalize a cmap, put it in plotly format, get threshold and range.\"\"\"\n    cmap = mpl_cm.get_cmap(cmap)\n    abs_values = np.abs(values)\n    if not symmetric_cmap and (values.min() < 0):\n        warnings.warn('you have specified symmetric_cmap=False '\n                      'but the map contains negative values; '\n                      'setting symmetric_cmap to True')\n        symmetric_cmap = True\n    if symmetric_cmap and vmin is not None:\n        warnings.warn('vmin cannot be chosen when cmap is symmetric')\n        vmin = None\n    if threshold is not None:\n        if vmin is not None:\n            warnings.warn('choosing both vmin and a threshold is not allowed; '\n                          'setting vmin to 0')\n        vmin = 0\n    if vmax is None:\n        vmax = abs_values.max()\n    if symmetric_cmap:\n        vmin = - vmax\n    if vmin is None:\n        vmin = values.min()\n    norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)\n    cmaplist = [cmap(i) for i in range(cmap.N)]\n    abs_threshold = None\n    if threshold is not None:\n        abs_threshold = check_threshold(threshold, values, fast_abs_percentile)\n        istart = int(norm(-abs_threshold, clip=True) * (cmap.N - 1))\n        istop = int(norm(abs_threshold, clip=True) * (cmap.N - 1))\n        for i in range(istart, istop):\n            cmaplist[i] = (0.5, 0.5, 0.5, 1.)  # just an average gray color\n    our_cmap = mpl.colors.LinearSegmentedColormap.from_list(\n        'Custom cmap', cmaplist, cmap.N)\n    x = np.linspace(0, 1, 100)\n    rgb = our_cmap(x, bytes=True)[:, :3]\n    rgb = np.array(rgb, dtype=int)\n    colors = []\n    for i, col in zip(x, rgb):\n        colors.append([np.round(i, 3), \"rgb({}, {}, {})\".format(*col)])\n    return {\n        'colors': colors, 'vmin': vmin, 'vmax': vmax, 'cmap': our_cmap,\n        'norm': norm, 'abs_threshold': abs_threshold,\n        'symmetric_cmap': symmetric_cmap\n    }\n\n\ndef encode(a):\n    \"\"\"Base64 encode a numpy array\"\"\"\n    try:\n        data = a.tobytes()\n    except AttributeError:\n        # np < 1.9\n        data = a.tostring()\n    return base64.b64encode(data).decode('utf-8')\n\n\ndef decode(b, dtype):\n    \"\"\"Decode a numpy array encoded as Base64\"\"\"\n    return np.frombuffer(base64.b64decode(b.encode('utf-8')), dtype)\n\n\ndef mesh_to_plotly(mesh):\n    mesh = surface.load_surf_mesh(mesh)\n    x, y, z = map(encode, np.asarray(mesh[0].T, dtype='<f4'))\n    i, j, k = map(encode, np.asarray(mesh[1].T, dtype='<i4'))\n    info = {\n        \"_x\": x,\n        \"_y\": y,\n        \"_z\": z,\n        \"_i\": i,\n        \"_j\": j,\n        \"_k\": k,\n    }\n    return info\n\n\ndef to_color_strings(colors):\n    cmap = mpl.colors.ListedColormap(colors)\n    colors = cmap(np.arange(cmap.N))[:, :3]\n    colors = np.asarray(colors * 255, dtype='uint8')\n    colors = ['#{:02x}{:02x}{:02x}'.format(*row) for row in colors]\n    return colors\n", "path": "nilearn/plotting/js_plotting_utils.py"}], "after_files": [{"content": "\"\"\"\nHelper functions for views, i.e. interactive plots from html_surface and\nhtml_connectome.\n\"\"\"\n\nimport os\nimport base64\nimport webbrowser\nimport tempfile\nimport warnings\nimport subprocess\nfrom string import Template\nimport weakref\ntry:\n    from html import escape  # Unavailable in Py2\nexcept ImportError:  # Can be removed once we EOL Py2 support for NiLearn\n    from cgi import escape  # Deprecated in Py3, necessary for Py2\n\nimport matplotlib as mpl\nimport numpy as np\nfrom matplotlib import cm as mpl_cm\n\nfrom .._utils.extmath import fast_abs_percentile\nfrom .._utils.param_validation import check_threshold\nfrom .. import surface\n\n\ndef add_js_lib(html, embed_js=True):\n    \"\"\"\n    Add javascript libraries to html template.\n\n    if embed_js is True, jquery and plotly are embedded in resulting page.\n    otherwise, they are loaded via CDNs.\n    \"\"\"\n    js_dir = os.path.join(os.path.dirname(__file__), 'data', 'js')\n    with open(os.path.join(js_dir, 'surface-plot-utils.js')) as f:\n        js_utils = f.read()\n    if not embed_js:\n        js_lib = \"\"\"\n        <script\n        src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\">\n        </script>\n        <script src=\"https://cdn.plot.ly/plotly-gl3d-latest.min.js\"></script>\n        <script>\n        {}\n        </script>\n        \"\"\".format(js_utils)\n    else:\n        with open(os.path.join(js_dir, 'jquery.min.js')) as f:\n            jquery = f.read()\n        with open(os.path.join(js_dir, 'plotly-gl3d-latest.min.js')) as f:\n            plotly = f.read()\n        js_lib = \"\"\"\n        <script>{}</script>\n        <script>{}</script>\n        <script>\n        {}\n        </script>\n        \"\"\".format(jquery, plotly, js_utils)\n    if not isinstance(html, Template):\n        html = Template(html)\n    return html.safe_substitute({'INSERT_JS_LIBRARIES_HERE': js_lib})\n\n\ndef get_html_template(template_name):\n    \"\"\"Get an HTML file from package data\"\"\"\n    template_path = os.path.join(\n        os.path.dirname(__file__), 'data', 'html', template_name)\n    with open(template_path, 'rb') as f:\n        return Template(f.read().decode('utf-8'))\n\n\ndef _remove_after_n_seconds(file_name, n_seconds):\n    script = os.path.join(os.path.dirname(__file__), 'rm_file.py')\n    subprocess.Popen(['python', script, file_name, str(n_seconds)])\n\n\nclass HTMLDocument(object):\n    \"\"\"\n    Embeds a plot in a web page.\n\n    If you are running a Jupyter notebook, the plot will be displayed\n    inline if this object is the output of a cell.\n    Otherwise, use open_in_browser() to open it in a web browser (or\n    save_as_html(\"filename.html\") to save it as an html file).\n\n    use str(document) or document.html to get the content of the web page,\n    and document.get_iframe() to have it wrapped in an iframe.\n\n    \"\"\"\n    _all_open_html_repr = weakref.WeakSet()\n\n    def __init__(self, html, width=600, height=400):\n        self.html = html\n        self.width = width\n        self.height = height\n        self._temp_file = None\n        self._check_n_open()\n\n    def _check_n_open(self):\n        HTMLDocument._all_open_html_repr.add(self)\n        if len(HTMLDocument._all_open_html_repr) > 9:\n            warnings.warn('It seems you have created more than 10 '\n                          'nilearn views. As each view uses dozens '\n                          'of megabytes of RAM, you might want to '\n                          'delete some of them.')\n\n    def resize(self, width, height):\n        \"\"\"Resize the plot displayed in a Jupyter notebook.\"\"\"\n        self.width, self.height = width, height\n        return self\n\n    def get_iframe(self, width=None, height=None):\n        \"\"\"\n        Get the document wrapped in an inline frame.\n\n        For inserting in another HTML page of for display in a Jupyter\n        notebook.\n\n        \"\"\"\n        if width is None:\n            width = self.width\n        if height is None:\n            height = self.height\n        escaped = escape(self.html, quote=True)\n        wrapped = ('<iframe srcdoc=\"{}\" width=\"{}\" height=\"{}\" '\n                   'frameBorder=\"0\"></iframe>').format(escaped, width, height)\n        return wrapped\n\n    def get_standalone(self):\n        \"\"\" Get the plot in an HTML page.\"\"\"\n        return self.html\n\n    def _repr_html_(self):\n        \"\"\"\n        Used by the Jupyter notebook.\n\n        Users normally won't call this method explicitely.\n        \"\"\"\n        return self.get_iframe()\n\n    def __str__(self):\n        return self.html\n\n    def save_as_html(self, file_name):\n        \"\"\"\n        Save the plot in an HTML file, that can later be opened in a browser.\n        \"\"\"\n        with open(file_name, 'wb') as f:\n            f.write(self.html.encode('utf-8'))\n\n    def open_in_browser(self, file_name=None, temp_file_lifetime=30):\n        \"\"\"\n        Save the plot to a temporary HTML file and open it in a browser.\n\n        Parameters\n        ----------\n\n        file_name : str, optional\n            .html file to use as temporary file\n\n        temp_file_lifetime : float, optional (default=30.)\n            Time, in seconds, after which the temporary file is removed.\n            If None, it is never removed.\n\n        \"\"\"\n        if file_name is None:\n            fd, file_name = tempfile.mkstemp('.html', 'nilearn_surface_plot_')\n            os.close(fd)\n        self.save_as_html(file_name)\n        self._temp_file = file_name\n        file_size = os.path.getsize(file_name) / 1e6\n        if temp_file_lifetime is None:\n            print((\"Saved HTML in temporary file: {}\\n\"\n                   \"file size is {:.1f}M, delete it when you're done, \"\n                   \"for example by calling this.remove_temp_file\").format(\n                       file_name, file_size))\n        else:\n            _remove_after_n_seconds(self._temp_file, temp_file_lifetime)\n        webbrowser.open('file://{}'.format(file_name))\n\n    def remove_temp_file(self):\n        \"\"\"\n        Remove the temporary file created by `open_in_browser`, if necessary.\n        \"\"\"\n        if self._temp_file is None:\n            return\n        if not os.path.isfile(self._temp_file):\n            return\n        os.remove(self._temp_file)\n        print('removed {}'.format(self._temp_file))\n        self._temp_file = None\n\n\ndef colorscale(cmap, values, threshold=None, symmetric_cmap=True,\n               vmax=None, vmin=None):\n    \"\"\"Normalize a cmap, put it in plotly format, get threshold and range.\"\"\"\n    cmap = mpl_cm.get_cmap(cmap)\n    abs_values = np.abs(values)\n    if not symmetric_cmap and (values.min() < 0):\n        warnings.warn('you have specified symmetric_cmap=False '\n                      'but the map contains negative values; '\n                      'setting symmetric_cmap to True')\n        symmetric_cmap = True\n    if symmetric_cmap and vmin is not None:\n        warnings.warn('vmin cannot be chosen when cmap is symmetric')\n        vmin = None\n    if threshold is not None:\n        if vmin is not None:\n            warnings.warn('choosing both vmin and a threshold is not allowed; '\n                          'setting vmin to 0')\n        vmin = 0\n    if vmax is None:\n        vmax = abs_values.max()\n    if symmetric_cmap:\n        vmin = - vmax\n    if vmin is None:\n        vmin = values.min()\n    norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)\n    cmaplist = [cmap(i) for i in range(cmap.N)]\n    abs_threshold = None\n    if threshold is not None:\n        abs_threshold = check_threshold(threshold, values, fast_abs_percentile)\n        istart = int(norm(-abs_threshold, clip=True) * (cmap.N - 1))\n        istop = int(norm(abs_threshold, clip=True) * (cmap.N - 1))\n        for i in range(istart, istop):\n            cmaplist[i] = (0.5, 0.5, 0.5, 1.)  # just an average gray color\n    our_cmap = mpl.colors.LinearSegmentedColormap.from_list(\n        'Custom cmap', cmaplist, cmap.N)\n    x = np.linspace(0, 1, 100)\n    rgb = our_cmap(x, bytes=True)[:, :3]\n    rgb = np.array(rgb, dtype=int)\n    colors = []\n    for i, col in zip(x, rgb):\n        colors.append([np.round(i, 3), \"rgb({}, {}, {})\".format(*col)])\n    return {\n        'colors': colors, 'vmin': vmin, 'vmax': vmax, 'cmap': our_cmap,\n        'norm': norm, 'abs_threshold': abs_threshold,\n        'symmetric_cmap': symmetric_cmap\n    }\n\n\ndef encode(a):\n    \"\"\"Base64 encode a numpy array\"\"\"\n    try:\n        data = a.tobytes()\n    except AttributeError:\n        # np < 1.9\n        data = a.tostring()\n    return base64.b64encode(data).decode('utf-8')\n\n\ndef decode(b, dtype):\n    \"\"\"Decode a numpy array encoded as Base64\"\"\"\n    return np.frombuffer(base64.b64decode(b.encode('utf-8')), dtype)\n\n\ndef mesh_to_plotly(mesh):\n    mesh = surface.load_surf_mesh(mesh)\n    x, y, z = map(encode, np.asarray(mesh[0].T, dtype='<f4'))\n    i, j, k = map(encode, np.asarray(mesh[1].T, dtype='<i4'))\n    info = {\n        \"_x\": x,\n        \"_y\": y,\n        \"_z\": z,\n        \"_i\": i,\n        \"_j\": j,\n        \"_k\": k,\n    }\n    return info\n\n\ndef to_color_strings(colors):\n    cmap = mpl.colors.ListedColormap(colors)\n    colors = cmap(np.arange(cmap.N))[:, :3]\n    colors = np.asarray(colors * 255, dtype='uint8')\n    colors = ['#{:02x}{:02x}{:02x}'.format(*row) for row in colors]\n    return colors\n", "path": "nilearn/plotting/js_plotting_utils.py"}]}