##// END OF EJS Templates
remove heading cells in v4
MinRK -
Show More
@@ -1,131 +1,131 b''
1 1 # coding: utf-8
2 2 import base64
3 3 import io
4 4 import json
5 5 import os
6 6 from os.path import join as pjoin
7 7 import shutil
8 8
9 9 import requests
10 10
11 11 from IPython.html.utils import url_path_join
12 12 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
13 13 from IPython.nbformat.current import (new_notebook, write,
14 new_heading_cell, new_code_cell,
14 new_markdown_cell, new_code_cell,
15 15 new_output)
16 16
17 17 from IPython.testing.decorators import onlyif_cmds_exist
18 18
19 19
20 20 class NbconvertAPI(object):
21 21 """Wrapper for nbconvert API calls."""
22 22 def __init__(self, base_url):
23 23 self.base_url = base_url
24 24
25 25 def _req(self, verb, path, body=None, params=None):
26 26 response = requests.request(verb,
27 27 url_path_join(self.base_url, 'nbconvert', path),
28 28 data=body, params=params,
29 29 )
30 30 response.raise_for_status()
31 31 return response
32 32
33 33 def from_file(self, format, path, name, download=False):
34 34 return self._req('GET', url_path_join(format, path, name),
35 35 params={'download':download})
36 36
37 37 def from_post(self, format, nbmodel):
38 38 body = json.dumps(nbmodel)
39 39 return self._req('POST', format, body)
40 40
41 41 def list_formats(self):
42 42 return self._req('GET', '')
43 43
44 44 png_green_pixel = base64.encodestring(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00'
45 45 b'\x00\x00\x01\x00\x00x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDAT'
46 46 b'\x08\xd7c\x90\xfb\xcf\x00\x00\x02\\\x01\x1e.~d\x87\x00\x00\x00\x00IEND\xaeB`\x82'
47 47 ).decode('ascii')
48 48
49 49 class APITest(NotebookTestBase):
50 50 def setUp(self):
51 51 nbdir = self.notebook_dir.name
52 52
53 53 if not os.path.isdir(pjoin(nbdir, 'foo')):
54 54 os.mkdir(pjoin(nbdir, 'foo'))
55 55
56 56 nb = new_notebook()
57 57
58 nb.cells.append(new_heading_cell(u'Created by test ³'))
58 nb.cells.append(new_markdown_cell(u'Created by test ³'))
59 59 cc1 = new_code_cell(source=u'print(2*6)')
60 60 cc1.outputs.append(new_output(output_type="stream", text=u'12'))
61 61 cc1.outputs.append(new_output(output_type="execute_result",
62 62 data={'image/png' : png_green_pixel},
63 63 execution_count=1,
64 64 ))
65 65 nb.cells.append(cc1)
66 66
67 67 with io.open(pjoin(nbdir, 'foo', 'testnb.ipynb'), 'w',
68 68 encoding='utf-8') as f:
69 69 write(nb, f)
70 70
71 71 self.nbconvert_api = NbconvertAPI(self.base_url())
72 72
73 73 def tearDown(self):
74 74 nbdir = self.notebook_dir.name
75 75
76 76 for dname in ['foo']:
77 77 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
78 78
79 79 @onlyif_cmds_exist('pandoc')
80 80 def test_from_file(self):
81 81 r = self.nbconvert_api.from_file('html', 'foo', 'testnb.ipynb')
82 82 self.assertEqual(r.status_code, 200)
83 83 self.assertIn(u'text/html', r.headers['Content-Type'])
84 84 self.assertIn(u'Created by test', r.text)
85 85 self.assertIn(u'print', r.text)
86 86
87 87 r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb')
88 88 self.assertIn(u'text/x-python', r.headers['Content-Type'])
89 89 self.assertIn(u'print(2*6)', r.text)
90 90
91 91 @onlyif_cmds_exist('pandoc')
92 92 def test_from_file_404(self):
93 93 with assert_http_error(404):
94 94 self.nbconvert_api.from_file('html', 'foo', 'thisdoesntexist.ipynb')
95 95
96 96 @onlyif_cmds_exist('pandoc')
97 97 def test_from_file_download(self):
98 98 r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb', download=True)
99 99 content_disposition = r.headers['Content-Disposition']
100 100 self.assertIn('attachment', content_disposition)
101 101 self.assertIn('testnb.py', content_disposition)
102 102
103 103 @onlyif_cmds_exist('pandoc')
104 104 def test_from_file_zip(self):
105 105 r = self.nbconvert_api.from_file('latex', 'foo', 'testnb.ipynb', download=True)
106 106 self.assertIn(u'application/zip', r.headers['Content-Type'])
107 107 self.assertIn(u'.zip', r.headers['Content-Disposition'])
108 108
109 109 @onlyif_cmds_exist('pandoc')
110 110 def test_from_post(self):
111 111 nbmodel_url = url_path_join(self.base_url(), 'api/contents/foo/testnb.ipynb')
112 112 nbmodel = requests.get(nbmodel_url).json()
113 113
114 114 r = self.nbconvert_api.from_post(format='html', nbmodel=nbmodel)
115 115 self.assertEqual(r.status_code, 200)
116 116 self.assertIn(u'text/html', r.headers['Content-Type'])
117 117 self.assertIn(u'Created by test', r.text)
118 118 self.assertIn(u'print', r.text)
119 119
120 120 r = self.nbconvert_api.from_post(format='python', nbmodel=nbmodel)
121 121 self.assertIn(u'text/x-python', r.headers['Content-Type'])
122 122 self.assertIn(u'print(2*6)', r.text)
123 123
124 124 @onlyif_cmds_exist('pandoc')
125 125 def test_from_post_zip(self):
126 126 nbmodel_url = url_path_join(self.base_url(), 'api/contents/foo/testnb.ipynb')
127 127 nbmodel = requests.get(nbmodel_url).json()
128 128
129 129 r = self.nbconvert_api.from_post(format='latex', nbmodel=nbmodel)
130 130 self.assertIn(u'application/zip', r.headers['Content-Type'])
131 131 self.assertIn(u'.zip', r.headers['Content-Disposition'])
@@ -1,545 +1,545 b''
1 1 """A contents manager that uses the local file system for storage."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import base64
7 7 import io
8 8 import os
9 9 import glob
10 10 import shutil
11 11
12 12 from tornado import web
13 13
14 14 from .manager import ContentsManager
15 15 from IPython.nbformat import current
16 16 from IPython.utils.io import atomic_writing
17 17 from IPython.utils.path import ensure_dir_exists
18 18 from IPython.utils.traitlets import Unicode, Bool, TraitError
19 19 from IPython.utils.py3compat import getcwd
20 20 from IPython.utils import tz
21 21 from IPython.html.utils import is_hidden, to_os_path, url_path_join
22 22
23 23
24 24 class FileContentsManager(ContentsManager):
25 25
26 26 root_dir = Unicode(getcwd(), config=True)
27 27
28 28 save_script = Bool(False, config=True, help='DEPRECATED, IGNORED')
29 29 def _save_script_changed(self):
30 30 self.log.warn("""
31 31 Automatically saving notebooks as scripts has been removed.
32 32 Use `ipython nbconvert --to python [notebook]` instead.
33 33 """)
34 34
35 35 def _root_dir_changed(self, name, old, new):
36 36 """Do a bit of validation of the root_dir."""
37 37 if not os.path.isabs(new):
38 38 # If we receive a non-absolute path, make it absolute.
39 39 self.root_dir = os.path.abspath(new)
40 40 return
41 41 if not os.path.isdir(new):
42 42 raise TraitError("%r is not a directory" % new)
43 43
44 44 checkpoint_dir = Unicode('.ipynb_checkpoints', config=True,
45 45 help="""The directory name in which to keep file checkpoints
46 46
47 47 This is a path relative to the file's own directory.
48 48
49 49 By default, it is .ipynb_checkpoints
50 50 """
51 51 )
52 52
53 53 def _copy(self, src, dest):
54 54 """copy src to dest
55 55
56 56 like shutil.copy2, but log errors in copystat
57 57 """
58 58 shutil.copyfile(src, dest)
59 59 try:
60 60 shutil.copystat(src, dest)
61 61 except OSError as e:
62 62 self.log.debug("copystat on %s failed", dest, exc_info=True)
63 63
64 64 def _get_os_path(self, name=None, path=''):
65 65 """Given a filename and API path, return its file system
66 66 path.
67 67
68 68 Parameters
69 69 ----------
70 70 name : string
71 71 A filename
72 72 path : string
73 73 The relative API path to the named file.
74 74
75 75 Returns
76 76 -------
77 77 path : string
78 78 API path to be evaluated relative to root_dir.
79 79 """
80 80 if name is not None:
81 81 path = url_path_join(path, name)
82 82 return to_os_path(path, self.root_dir)
83 83
84 84 def path_exists(self, path):
85 85 """Does the API-style path refer to an extant directory?
86 86
87 87 API-style wrapper for os.path.isdir
88 88
89 89 Parameters
90 90 ----------
91 91 path : string
92 92 The path to check. This is an API path (`/` separated,
93 93 relative to root_dir).
94 94
95 95 Returns
96 96 -------
97 97 exists : bool
98 98 Whether the path is indeed a directory.
99 99 """
100 100 path = path.strip('/')
101 101 os_path = self._get_os_path(path=path)
102 102 return os.path.isdir(os_path)
103 103
104 104 def is_hidden(self, path):
105 105 """Does the API style path correspond to a hidden directory or file?
106 106
107 107 Parameters
108 108 ----------
109 109 path : string
110 110 The path to check. This is an API path (`/` separated,
111 111 relative to root_dir).
112 112
113 113 Returns
114 114 -------
115 115 exists : bool
116 116 Whether the path is hidden.
117 117
118 118 """
119 119 path = path.strip('/')
120 120 os_path = self._get_os_path(path=path)
121 121 return is_hidden(os_path, self.root_dir)
122 122
123 123 def file_exists(self, name, path=''):
124 124 """Returns True if the file exists, else returns False.
125 125
126 126 API-style wrapper for os.path.isfile
127 127
128 128 Parameters
129 129 ----------
130 130 name : string
131 131 The name of the file you are checking.
132 132 path : string
133 133 The relative path to the file's directory (with '/' as separator)
134 134
135 135 Returns
136 136 -------
137 137 exists : bool
138 138 Whether the file exists.
139 139 """
140 140 path = path.strip('/')
141 141 nbpath = self._get_os_path(name, path=path)
142 142 return os.path.isfile(nbpath)
143 143
144 144 def exists(self, name=None, path=''):
145 145 """Returns True if the path [and name] exists, else returns False.
146 146
147 147 API-style wrapper for os.path.exists
148 148
149 149 Parameters
150 150 ----------
151 151 name : string
152 152 The name of the file you are checking.
153 153 path : string
154 154 The relative path to the file's directory (with '/' as separator)
155 155
156 156 Returns
157 157 -------
158 158 exists : bool
159 159 Whether the target exists.
160 160 """
161 161 path = path.strip('/')
162 162 os_path = self._get_os_path(name, path=path)
163 163 return os.path.exists(os_path)
164 164
165 165 def _base_model(self, name, path=''):
166 166 """Build the common base of a contents model"""
167 167 os_path = self._get_os_path(name, path)
168 168 info = os.stat(os_path)
169 169 last_modified = tz.utcfromtimestamp(info.st_mtime)
170 170 created = tz.utcfromtimestamp(info.st_ctime)
171 171 # Create the base model.
172 172 model = {}
173 173 model['name'] = name
174 174 model['path'] = path
175 175 model['last_modified'] = last_modified
176 176 model['created'] = created
177 177 model['content'] = None
178 178 model['format'] = None
179 179 model['message'] = None
180 180 return model
181 181
182 182 def _dir_model(self, name, path='', content=True):
183 183 """Build a model for a directory
184 184
185 185 if content is requested, will include a listing of the directory
186 186 """
187 187 os_path = self._get_os_path(name, path)
188 188
189 189 four_o_four = u'directory does not exist: %r' % os_path
190 190
191 191 if not os.path.isdir(os_path):
192 192 raise web.HTTPError(404, four_o_four)
193 193 elif is_hidden(os_path, self.root_dir):
194 194 self.log.info("Refusing to serve hidden directory %r, via 404 Error",
195 195 os_path
196 196 )
197 197 raise web.HTTPError(404, four_o_four)
198 198
199 199 if name is None:
200 200 if '/' in path:
201 201 path, name = path.rsplit('/', 1)
202 202 else:
203 203 name = ''
204 204 model = self._base_model(name, path)
205 205 model['type'] = 'directory'
206 206 dir_path = u'{}/{}'.format(path, name)
207 207 if content:
208 208 model['content'] = contents = []
209 209 for os_path in glob.glob(self._get_os_path('*', dir_path)):
210 210 name = os.path.basename(os_path)
211 211 # skip over broken symlinks in listing
212 212 if not os.path.exists(os_path):
213 213 self.log.warn("%s doesn't exist", os_path)
214 214 continue
215 215 if self.should_list(name) and not is_hidden(os_path, self.root_dir):
216 216 contents.append(self.get_model(name=name, path=dir_path, content=False))
217 217
218 218 model['format'] = 'json'
219 219
220 220 return model
221 221
222 222 def _file_model(self, name, path='', content=True):
223 223 """Build a model for a file
224 224
225 225 if content is requested, include the file contents.
226 226 UTF-8 text files will be unicode, binary files will be base64-encoded.
227 227 """
228 228 model = self._base_model(name, path)
229 229 model['type'] = 'file'
230 230 if content:
231 231 os_path = self._get_os_path(name, path)
232 232 with io.open(os_path, 'rb') as f:
233 233 bcontent = f.read()
234 234 try:
235 235 model['content'] = bcontent.decode('utf8')
236 236 except UnicodeError as e:
237 237 model['content'] = base64.encodestring(bcontent).decode('ascii')
238 238 model['format'] = 'base64'
239 239 else:
240 240 model['format'] = 'text'
241 241 return model
242 242
243 243
244 244 def _notebook_model(self, name, path='', content=True):
245 245 """Build a notebook model
246 246
247 247 if content is requested, the notebook content will be populated
248 248 as a JSON structure (not double-serialized)
249 249 """
250 250 model = self._base_model(name, path)
251 251 model['type'] = 'notebook'
252 252 if content:
253 253 os_path = self._get_os_path(name, path)
254 254 with io.open(os_path, 'r', encoding='utf-8') as f:
255 255 try:
256 256 nb = current.read(f, u'json')
257 257 except Exception as e:
258 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
258 raise web.HTTPError(400, u"Unreadable Notebook: %s %r" % (os_path, e))
259 259 self.mark_trusted_cells(nb, name, path)
260 260 model['content'] = nb
261 261 model['format'] = 'json'
262 262 self.validate_notebook_model(model)
263 263 return model
264 264
265 265 def get_model(self, name, path='', content=True):
266 266 """ Takes a path and name for an entity and returns its model
267 267
268 268 Parameters
269 269 ----------
270 270 name : str
271 271 the name of the target
272 272 path : str
273 273 the API path that describes the relative path for the target
274 274
275 275 Returns
276 276 -------
277 277 model : dict
278 278 the contents model. If content=True, returns the contents
279 279 of the file or directory as well.
280 280 """
281 281 path = path.strip('/')
282 282
283 283 if not self.exists(name=name, path=path):
284 284 raise web.HTTPError(404, u'No such file or directory: %s/%s' % (path, name))
285 285
286 286 os_path = self._get_os_path(name, path)
287 287 if os.path.isdir(os_path):
288 288 model = self._dir_model(name, path, content)
289 289 elif name.endswith('.ipynb'):
290 290 model = self._notebook_model(name, path, content)
291 291 else:
292 292 model = self._file_model(name, path, content)
293 293 return model
294 294
295 295 def _save_notebook(self, os_path, model, name='', path=''):
296 296 """save a notebook file"""
297 297 # Save the notebook file
298 298 nb = current.to_notebook_json(model['content'])
299 299
300 300 self.check_and_sign(nb, name, path)
301 301
302 302 if 'name' in nb['metadata']:
303 303 nb['metadata']['name'] = u''
304 304
305 305 with atomic_writing(os_path, encoding='utf-8') as f:
306 306 current.write(nb, f, version=nb.nbformat)
307 307
308 308 def _save_file(self, os_path, model, name='', path=''):
309 309 """save a non-notebook file"""
310 310 fmt = model.get('format', None)
311 311 if fmt not in {'text', 'base64'}:
312 312 raise web.HTTPError(400, "Must specify format of file contents as 'text' or 'base64'")
313 313 try:
314 314 content = model['content']
315 315 if fmt == 'text':
316 316 bcontent = content.encode('utf8')
317 317 else:
318 318 b64_bytes = content.encode('ascii')
319 319 bcontent = base64.decodestring(b64_bytes)
320 320 except Exception as e:
321 321 raise web.HTTPError(400, u'Encoding error saving %s: %s' % (os_path, e))
322 322 with atomic_writing(os_path, text=False) as f:
323 323 f.write(bcontent)
324 324
325 325 def _save_directory(self, os_path, model, name='', path=''):
326 326 """create a directory"""
327 327 if is_hidden(os_path, self.root_dir):
328 328 raise web.HTTPError(400, u'Cannot create hidden directory %r' % os_path)
329 329 if not os.path.exists(os_path):
330 330 os.mkdir(os_path)
331 331 elif not os.path.isdir(os_path):
332 332 raise web.HTTPError(400, u'Not a directory: %s' % (os_path))
333 333 else:
334 334 self.log.debug("Directory %r already exists", os_path)
335 335
336 336 def save(self, model, name='', path=''):
337 337 """Save the file model and return the model with no content."""
338 338 path = path.strip('/')
339 339
340 340 if 'type' not in model:
341 341 raise web.HTTPError(400, u'No file type provided')
342 342 if 'content' not in model and model['type'] != 'directory':
343 343 raise web.HTTPError(400, u'No file content provided')
344 344
345 345 # One checkpoint should always exist
346 346 if self.file_exists(name, path) and not self.list_checkpoints(name, path):
347 347 self.create_checkpoint(name, path)
348 348
349 349 new_path = model.get('path', path).strip('/')
350 350 new_name = model.get('name', name)
351 351
352 352 if path != new_path or name != new_name:
353 353 self.rename(name, path, new_name, new_path)
354 354
355 355 os_path = self._get_os_path(new_name, new_path)
356 356 self.log.debug("Saving %s", os_path)
357 357 try:
358 358 if model['type'] == 'notebook':
359 359 self._save_notebook(os_path, model, new_name, new_path)
360 360 elif model['type'] == 'file':
361 361 self._save_file(os_path, model, new_name, new_path)
362 362 elif model['type'] == 'directory':
363 363 self._save_directory(os_path, model, new_name, new_path)
364 364 else:
365 365 raise web.HTTPError(400, "Unhandled contents type: %s" % model['type'])
366 366 except web.HTTPError:
367 367 raise
368 368 except Exception as e:
369 369 raise web.HTTPError(400, u'Unexpected error while saving file: %s %s' % (os_path, e))
370 370
371 371 validation_message = None
372 372 if model['type'] == 'notebook':
373 373 self.validate_notebook_model(model)
374 374 validation_message = model.get('message', None)
375 375
376 376 model = self.get_model(new_name, new_path, content=False)
377 377 if validation_message:
378 378 model['message'] = validation_message
379 379 return model
380 380
381 381 def update(self, model, name, path=''):
382 382 """Update the file's path and/or name
383 383
384 384 For use in PATCH requests, to enable renaming a file without
385 385 re-uploading its contents. Only used for renaming at the moment.
386 386 """
387 387 path = path.strip('/')
388 388 new_name = model.get('name', name)
389 389 new_path = model.get('path', path).strip('/')
390 390 if path != new_path or name != new_name:
391 391 self.rename(name, path, new_name, new_path)
392 392 model = self.get_model(new_name, new_path, content=False)
393 393 return model
394 394
395 395 def delete(self, name, path=''):
396 396 """Delete file by name and path."""
397 397 path = path.strip('/')
398 398 os_path = self._get_os_path(name, path)
399 399 rm = os.unlink
400 400 if os.path.isdir(os_path):
401 401 listing = os.listdir(os_path)
402 402 # don't delete non-empty directories (checkpoints dir doesn't count)
403 403 if listing and listing != [self.checkpoint_dir]:
404 404 raise web.HTTPError(400, u'Directory %s not empty' % os_path)
405 405 elif not os.path.isfile(os_path):
406 406 raise web.HTTPError(404, u'File does not exist: %s' % os_path)
407 407
408 408 # clear checkpoints
409 409 for checkpoint in self.list_checkpoints(name, path):
410 410 checkpoint_id = checkpoint['id']
411 411 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
412 412 if os.path.isfile(cp_path):
413 413 self.log.debug("Unlinking checkpoint %s", cp_path)
414 414 os.unlink(cp_path)
415 415
416 416 if os.path.isdir(os_path):
417 417 self.log.debug("Removing directory %s", os_path)
418 418 shutil.rmtree(os_path)
419 419 else:
420 420 self.log.debug("Unlinking file %s", os_path)
421 421 rm(os_path)
422 422
423 423 def rename(self, old_name, old_path, new_name, new_path):
424 424 """Rename a file."""
425 425 old_path = old_path.strip('/')
426 426 new_path = new_path.strip('/')
427 427 if new_name == old_name and new_path == old_path:
428 428 return
429 429
430 430 new_os_path = self._get_os_path(new_name, new_path)
431 431 old_os_path = self._get_os_path(old_name, old_path)
432 432
433 433 # Should we proceed with the move?
434 434 if os.path.isfile(new_os_path):
435 435 raise web.HTTPError(409, u'File with name already exists: %s' % new_os_path)
436 436
437 437 # Move the file
438 438 try:
439 439 shutil.move(old_os_path, new_os_path)
440 440 except Exception as e:
441 441 raise web.HTTPError(500, u'Unknown error renaming file: %s %s' % (old_os_path, e))
442 442
443 443 # Move the checkpoints
444 444 old_checkpoints = self.list_checkpoints(old_name, old_path)
445 445 for cp in old_checkpoints:
446 446 checkpoint_id = cp['id']
447 447 old_cp_path = self.get_checkpoint_path(checkpoint_id, old_name, old_path)
448 448 new_cp_path = self.get_checkpoint_path(checkpoint_id, new_name, new_path)
449 449 if os.path.isfile(old_cp_path):
450 450 self.log.debug("Renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
451 451 shutil.move(old_cp_path, new_cp_path)
452 452
453 453 # Checkpoint-related utilities
454 454
455 455 def get_checkpoint_path(self, checkpoint_id, name, path=''):
456 456 """find the path to a checkpoint"""
457 457 path = path.strip('/')
458 458 basename, ext = os.path.splitext(name)
459 459 filename = u"{name}-{checkpoint_id}{ext}".format(
460 460 name=basename,
461 461 checkpoint_id=checkpoint_id,
462 462 ext=ext,
463 463 )
464 464 os_path = self._get_os_path(path=path)
465 465 cp_dir = os.path.join(os_path, self.checkpoint_dir)
466 466 ensure_dir_exists(cp_dir)
467 467 cp_path = os.path.join(cp_dir, filename)
468 468 return cp_path
469 469
470 470 def get_checkpoint_model(self, checkpoint_id, name, path=''):
471 471 """construct the info dict for a given checkpoint"""
472 472 path = path.strip('/')
473 473 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
474 474 stats = os.stat(cp_path)
475 475 last_modified = tz.utcfromtimestamp(stats.st_mtime)
476 476 info = dict(
477 477 id = checkpoint_id,
478 478 last_modified = last_modified,
479 479 )
480 480 return info
481 481
482 482 # public checkpoint API
483 483
484 484 def create_checkpoint(self, name, path=''):
485 485 """Create a checkpoint from the current state of a file"""
486 486 path = path.strip('/')
487 487 src_path = self._get_os_path(name, path)
488 488 # only the one checkpoint ID:
489 489 checkpoint_id = u"checkpoint"
490 490 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
491 491 self.log.debug("creating checkpoint for %s", name)
492 492 self._copy(src_path, cp_path)
493 493
494 494 # return the checkpoint info
495 495 return self.get_checkpoint_model(checkpoint_id, name, path)
496 496
497 497 def list_checkpoints(self, name, path=''):
498 498 """list the checkpoints for a given file
499 499
500 500 This contents manager currently only supports one checkpoint per file.
501 501 """
502 502 path = path.strip('/')
503 503 checkpoint_id = "checkpoint"
504 504 os_path = self.get_checkpoint_path(checkpoint_id, name, path)
505 505 if not os.path.exists(os_path):
506 506 return []
507 507 else:
508 508 return [self.get_checkpoint_model(checkpoint_id, name, path)]
509 509
510 510
511 511 def restore_checkpoint(self, checkpoint_id, name, path=''):
512 512 """restore a file to a checkpointed state"""
513 513 path = path.strip('/')
514 514 self.log.info("restoring %s from checkpoint %s", name, checkpoint_id)
515 515 nb_path = self._get_os_path(name, path)
516 516 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
517 517 if not os.path.isfile(cp_path):
518 518 self.log.debug("checkpoint file does not exist: %s", cp_path)
519 519 raise web.HTTPError(404,
520 520 u'checkpoint does not exist: %s-%s' % (name, checkpoint_id)
521 521 )
522 522 # ensure notebook is readable (never restore from an unreadable notebook)
523 523 if cp_path.endswith('.ipynb'):
524 524 with io.open(cp_path, 'r', encoding='utf-8') as f:
525 525 current.read(f, u'json')
526 526 self._copy(cp_path, nb_path)
527 527 self.log.debug("copying %s -> %s", cp_path, nb_path)
528 528
529 529 def delete_checkpoint(self, checkpoint_id, name, path=''):
530 530 """delete a file's checkpoint"""
531 531 path = path.strip('/')
532 532 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
533 533 if not os.path.isfile(cp_path):
534 534 raise web.HTTPError(404,
535 535 u'Checkpoint does not exist: %s%s-%s' % (path, name, checkpoint_id)
536 536 )
537 537 self.log.debug("unlinking %s", cp_path)
538 538 os.unlink(cp_path)
539 539
540 540 def info_string(self):
541 541 return "Serving notebooks from local directory: %s" % self.root_dir
542 542
543 543 def get_kernel_path(self, name, path='', model=None):
544 544 """Return the initial working dir a kernel associated with a given notebook"""
545 545 return os.path.join(self.root_dir, path)
@@ -1,480 +1,480 b''
1 1 # coding: utf-8
2 2 """Test the contents webservice API."""
3 3
4 4 import base64
5 5 import io
6 6 import json
7 7 import os
8 8 import shutil
9 9 from unicodedata import normalize
10 10
11 11 pjoin = os.path.join
12 12
13 13 import requests
14 14
15 15 from IPython.html.utils import url_path_join, url_escape
16 16 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
17 17 from IPython.nbformat import current
18 18 from IPython.nbformat.current import (new_notebook, write, read,
19 new_heading_cell, to_notebook_json)
19 new_markdown_cell, to_notebook_json)
20 20 from IPython.nbformat import v2
21 21 from IPython.utils import py3compat
22 22 from IPython.utils.data import uniq_stable
23 23
24 24
25 25 def notebooks_only(dir_model):
26 26 return [nb for nb in dir_model['content'] if nb['type']=='notebook']
27 27
28 28 def dirs_only(dir_model):
29 29 return [x for x in dir_model['content'] if x['type']=='directory']
30 30
31 31
32 32 class API(object):
33 33 """Wrapper for contents API calls."""
34 34 def __init__(self, base_url):
35 35 self.base_url = base_url
36 36
37 37 def _req(self, verb, path, body=None):
38 38 response = requests.request(verb,
39 39 url_path_join(self.base_url, 'api/contents', path),
40 40 data=body,
41 41 )
42 42 response.raise_for_status()
43 43 return response
44 44
45 45 def list(self, path='/'):
46 46 return self._req('GET', path)
47 47
48 48 def read(self, name, path='/'):
49 49 return self._req('GET', url_path_join(path, name))
50 50
51 51 def create_untitled(self, path='/', ext=None):
52 52 body = None
53 53 if ext:
54 54 body = json.dumps({'ext': ext})
55 55 return self._req('POST', path, body)
56 56
57 57 def upload_untitled(self, body, path='/'):
58 58 return self._req('POST', path, body)
59 59
60 60 def copy_untitled(self, copy_from, path='/'):
61 61 body = json.dumps({'copy_from':copy_from})
62 62 return self._req('POST', path, body)
63 63
64 64 def create(self, name, path='/'):
65 65 return self._req('PUT', url_path_join(path, name))
66 66
67 67 def upload(self, name, body, path='/'):
68 68 return self._req('PUT', url_path_join(path, name), body)
69 69
70 70 def mkdir(self, name, path='/'):
71 71 return self._req('PUT', url_path_join(path, name), json.dumps({'type': 'directory'}))
72 72
73 73 def copy(self, copy_from, copy_to, path='/'):
74 74 body = json.dumps({'copy_from':copy_from})
75 75 return self._req('PUT', url_path_join(path, copy_to), body)
76 76
77 77 def save(self, name, body, path='/'):
78 78 return self._req('PUT', url_path_join(path, name), body)
79 79
80 80 def delete(self, name, path='/'):
81 81 return self._req('DELETE', url_path_join(path, name))
82 82
83 83 def rename(self, name, path, new_name):
84 84 body = json.dumps({'name': new_name})
85 85 return self._req('PATCH', url_path_join(path, name), body)
86 86
87 87 def get_checkpoints(self, name, path):
88 88 return self._req('GET', url_path_join(path, name, 'checkpoints'))
89 89
90 90 def new_checkpoint(self, name, path):
91 91 return self._req('POST', url_path_join(path, name, 'checkpoints'))
92 92
93 93 def restore_checkpoint(self, name, path, checkpoint_id):
94 94 return self._req('POST', url_path_join(path, name, 'checkpoints', checkpoint_id))
95 95
96 96 def delete_checkpoint(self, name, path, checkpoint_id):
97 97 return self._req('DELETE', url_path_join(path, name, 'checkpoints', checkpoint_id))
98 98
99 99 class APITest(NotebookTestBase):
100 100 """Test the kernels web service API"""
101 101 dirs_nbs = [('', 'inroot'),
102 102 ('Directory with spaces in', 'inspace'),
103 103 (u'unicodé', 'innonascii'),
104 104 ('foo', 'a'),
105 105 ('foo', 'b'),
106 106 ('foo', 'name with spaces'),
107 107 ('foo', u'unicodé'),
108 108 ('foo/bar', 'baz'),
109 109 ('ordering', 'A'),
110 110 ('ordering', 'b'),
111 111 ('ordering', 'C'),
112 112 (u'å b', u'ç d'),
113 113 ]
114 114 hidden_dirs = ['.hidden', '__pycache__']
115 115
116 116 dirs = uniq_stable([py3compat.cast_unicode(d) for (d,n) in dirs_nbs])
117 117 del dirs[0] # remove ''
118 118 top_level_dirs = {normalize('NFC', d.split('/')[0]) for d in dirs}
119 119
120 120 @staticmethod
121 121 def _blob_for_name(name):
122 122 return name.encode('utf-8') + b'\xFF'
123 123
124 124 @staticmethod
125 125 def _txt_for_name(name):
126 126 return u'%s text file' % name
127 127
128 128 def setUp(self):
129 129 nbdir = self.notebook_dir.name
130 130 self.blob = os.urandom(100)
131 131 self.b64_blob = base64.encodestring(self.blob).decode('ascii')
132 132
133 133
134 134
135 135 for d in (self.dirs + self.hidden_dirs):
136 136 d.replace('/', os.sep)
137 137 if not os.path.isdir(pjoin(nbdir, d)):
138 138 os.mkdir(pjoin(nbdir, d))
139 139
140 140 for d, name in self.dirs_nbs:
141 141 d = d.replace('/', os.sep)
142 142 # create a notebook
143 143 with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w',
144 144 encoding='utf-8') as f:
145 145 nb = new_notebook()
146 146 write(nb, f, format='ipynb')
147 147
148 148 # create a text file
149 149 with io.open(pjoin(nbdir, d, '%s.txt' % name), 'w',
150 150 encoding='utf-8') as f:
151 151 f.write(self._txt_for_name(name))
152 152
153 153 # create a binary file
154 154 with io.open(pjoin(nbdir, d, '%s.blob' % name), 'wb') as f:
155 155 f.write(self._blob_for_name(name))
156 156
157 157 self.api = API(self.base_url())
158 158
159 159 def tearDown(self):
160 160 nbdir = self.notebook_dir.name
161 161
162 162 for dname in (list(self.top_level_dirs) + self.hidden_dirs):
163 163 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
164 164
165 165 if os.path.isfile(pjoin(nbdir, 'inroot.ipynb')):
166 166 os.unlink(pjoin(nbdir, 'inroot.ipynb'))
167 167
168 168 def test_list_notebooks(self):
169 169 nbs = notebooks_only(self.api.list().json())
170 170 self.assertEqual(len(nbs), 1)
171 171 self.assertEqual(nbs[0]['name'], 'inroot.ipynb')
172 172
173 173 nbs = notebooks_only(self.api.list('/Directory with spaces in/').json())
174 174 self.assertEqual(len(nbs), 1)
175 175 self.assertEqual(nbs[0]['name'], 'inspace.ipynb')
176 176
177 177 nbs = notebooks_only(self.api.list(u'/unicodé/').json())
178 178 self.assertEqual(len(nbs), 1)
179 179 self.assertEqual(nbs[0]['name'], 'innonascii.ipynb')
180 180 self.assertEqual(nbs[0]['path'], u'unicodé')
181 181
182 182 nbs = notebooks_only(self.api.list('/foo/bar/').json())
183 183 self.assertEqual(len(nbs), 1)
184 184 self.assertEqual(nbs[0]['name'], 'baz.ipynb')
185 185 self.assertEqual(nbs[0]['path'], 'foo/bar')
186 186
187 187 nbs = notebooks_only(self.api.list('foo').json())
188 188 self.assertEqual(len(nbs), 4)
189 189 nbnames = { normalize('NFC', n['name']) for n in nbs }
190 190 expected = [ u'a.ipynb', u'b.ipynb', u'name with spaces.ipynb', u'unicodé.ipynb']
191 191 expected = { normalize('NFC', name) for name in expected }
192 192 self.assertEqual(nbnames, expected)
193 193
194 194 nbs = notebooks_only(self.api.list('ordering').json())
195 195 nbnames = [n['name'] for n in nbs]
196 196 expected = ['A.ipynb', 'b.ipynb', 'C.ipynb']
197 197 self.assertEqual(nbnames, expected)
198 198
199 199 def test_list_dirs(self):
200 200 dirs = dirs_only(self.api.list().json())
201 201 dir_names = {normalize('NFC', d['name']) for d in dirs}
202 202 self.assertEqual(dir_names, self.top_level_dirs) # Excluding hidden dirs
203 203
204 204 def test_list_nonexistant_dir(self):
205 205 with assert_http_error(404):
206 206 self.api.list('nonexistant')
207 207
208 208 def test_get_nb_contents(self):
209 209 for d, name in self.dirs_nbs:
210 210 nb = self.api.read('%s.ipynb' % name, d+'/').json()
211 211 self.assertEqual(nb['name'], u'%s.ipynb' % name)
212 212 self.assertEqual(nb['type'], 'notebook')
213 213 self.assertIn('content', nb)
214 214 self.assertEqual(nb['format'], 'json')
215 215 self.assertIn('content', nb)
216 216 self.assertIn('metadata', nb['content'])
217 217 self.assertIsInstance(nb['content']['metadata'], dict)
218 218
219 219 def test_get_contents_no_such_file(self):
220 220 # Name that doesn't exist - should be a 404
221 221 with assert_http_error(404):
222 222 self.api.read('q.ipynb', 'foo')
223 223
224 224 def test_get_text_file_contents(self):
225 225 for d, name in self.dirs_nbs:
226 226 model = self.api.read(u'%s.txt' % name, d+'/').json()
227 227 self.assertEqual(model['name'], u'%s.txt' % name)
228 228 self.assertIn('content', model)
229 229 self.assertEqual(model['format'], 'text')
230 230 self.assertEqual(model['type'], 'file')
231 231 self.assertEqual(model['content'], self._txt_for_name(name))
232 232
233 233 # Name that doesn't exist - should be a 404
234 234 with assert_http_error(404):
235 235 self.api.read('q.txt', 'foo')
236 236
237 237 def test_get_binary_file_contents(self):
238 238 for d, name in self.dirs_nbs:
239 239 model = self.api.read(u'%s.blob' % name, d+'/').json()
240 240 self.assertEqual(model['name'], u'%s.blob' % name)
241 241 self.assertIn('content', model)
242 242 self.assertEqual(model['format'], 'base64')
243 243 self.assertEqual(model['type'], 'file')
244 244 b64_data = base64.encodestring(self._blob_for_name(name)).decode('ascii')
245 245 self.assertEqual(model['content'], b64_data)
246 246
247 247 # Name that doesn't exist - should be a 404
248 248 with assert_http_error(404):
249 249 self.api.read('q.txt', 'foo')
250 250
251 251 def _check_created(self, resp, name, path, type='notebook'):
252 252 self.assertEqual(resp.status_code, 201)
253 253 location_header = py3compat.str_to_unicode(resp.headers['Location'])
254 254 self.assertEqual(location_header, url_escape(url_path_join(u'/api/contents', path, name)))
255 255 rjson = resp.json()
256 256 self.assertEqual(rjson['name'], name)
257 257 self.assertEqual(rjson['path'], path)
258 258 self.assertEqual(rjson['type'], type)
259 259 isright = os.path.isdir if type == 'directory' else os.path.isfile
260 260 assert isright(pjoin(
261 261 self.notebook_dir.name,
262 262 path.replace('/', os.sep),
263 263 name,
264 264 ))
265 265
266 266 def test_create_untitled(self):
267 267 resp = self.api.create_untitled(path=u'Ã¥ b')
268 268 self._check_created(resp, 'Untitled0.ipynb', u'Ã¥ b')
269 269
270 270 # Second time
271 271 resp = self.api.create_untitled(path=u'Ã¥ b')
272 272 self._check_created(resp, 'Untitled1.ipynb', u'Ã¥ b')
273 273
274 274 # And two directories down
275 275 resp = self.api.create_untitled(path='foo/bar')
276 276 self._check_created(resp, 'Untitled0.ipynb', 'foo/bar')
277 277
278 278 def test_create_untitled_txt(self):
279 279 resp = self.api.create_untitled(path='foo/bar', ext='.txt')
280 280 self._check_created(resp, 'untitled0.txt', 'foo/bar', type='file')
281 281
282 282 resp = self.api.read(path='foo/bar', name='untitled0.txt')
283 283 model = resp.json()
284 284 self.assertEqual(model['type'], 'file')
285 285 self.assertEqual(model['format'], 'text')
286 286 self.assertEqual(model['content'], '')
287 287
288 288 def test_upload_untitled(self):
289 289 nb = new_notebook()
290 290 nbmodel = {'content': nb, 'type': 'notebook'}
291 291 resp = self.api.upload_untitled(path=u'Ã¥ b',
292 292 body=json.dumps(nbmodel))
293 293 self._check_created(resp, 'Untitled0.ipynb', u'Ã¥ b')
294 294
295 295 def test_upload(self):
296 296 nb = new_notebook()
297 297 nbmodel = {'content': nb, 'type': 'notebook'}
298 298 resp = self.api.upload(u'Upload tést.ipynb', path=u'å b',
299 299 body=json.dumps(nbmodel))
300 300 self._check_created(resp, u'Upload tést.ipynb', u'å b')
301 301
302 302 def test_mkdir(self):
303 303 resp = self.api.mkdir(u'New ∂ir', path=u'å b')
304 304 self._check_created(resp, u'New ∂ir', u'å b', type='directory')
305 305
306 306 def test_mkdir_hidden_400(self):
307 307 with assert_http_error(400):
308 308 resp = self.api.mkdir(u'.hidden', path=u'Ã¥ b')
309 309
310 310 def test_upload_txt(self):
311 311 body = u'ünicode téxt'
312 312 model = {
313 313 'content' : body,
314 314 'format' : 'text',
315 315 'type' : 'file',
316 316 }
317 317 resp = self.api.upload(u'Upload tést.txt', path=u'å b',
318 318 body=json.dumps(model))
319 319
320 320 # check roundtrip
321 321 resp = self.api.read(path=u'å b', name=u'Upload tést.txt')
322 322 model = resp.json()
323 323 self.assertEqual(model['type'], 'file')
324 324 self.assertEqual(model['format'], 'text')
325 325 self.assertEqual(model['content'], body)
326 326
327 327 def test_upload_b64(self):
328 328 body = b'\xFFblob'
329 329 b64body = base64.encodestring(body).decode('ascii')
330 330 model = {
331 331 'content' : b64body,
332 332 'format' : 'base64',
333 333 'type' : 'file',
334 334 }
335 335 resp = self.api.upload(u'Upload tést.blob', path=u'å b',
336 336 body=json.dumps(model))
337 337
338 338 # check roundtrip
339 339 resp = self.api.read(path=u'å b', name=u'Upload tést.blob')
340 340 model = resp.json()
341 341 self.assertEqual(model['type'], 'file')
342 342 self.assertEqual(model['format'], 'base64')
343 343 decoded = base64.decodestring(model['content'].encode('ascii'))
344 344 self.assertEqual(decoded, body)
345 345
346 346 def test_upload_v2(self):
347 347 nb = v2.new_notebook()
348 348 ws = v2.new_worksheet()
349 349 nb.worksheets.append(ws)
350 350 ws.cells.append(v2.new_code_cell(input='print("hi")'))
351 351 nbmodel = {'content': nb, 'type': 'notebook'}
352 352 resp = self.api.upload(u'Upload tést.ipynb', path=u'å b',
353 353 body=json.dumps(nbmodel))
354 354 self._check_created(resp, u'Upload tést.ipynb', u'å b')
355 355 resp = self.api.read(u'Upload tést.ipynb', u'å b')
356 356 data = resp.json()
357 357 self.assertEqual(data['content']['nbformat'], current.nbformat)
358 358
359 359 def test_copy_untitled(self):
360 360 resp = self.api.copy_untitled(u'ç d.ipynb', path=u'å b')
361 361 self._check_created(resp, u'ç d-Copy0.ipynb', u'å b')
362 362
363 363 def test_copy(self):
364 364 resp = self.api.copy(u'ç d.ipynb', u'cøpy.ipynb', path=u'å b')
365 365 self._check_created(resp, u'cøpy.ipynb', u'å b')
366 366
367 367 def test_copy_path(self):
368 368 resp = self.api.copy(u'foo/a.ipynb', u'cøpyfoo.ipynb', path=u'å b')
369 369 self._check_created(resp, u'cøpyfoo.ipynb', u'å b')
370 370
371 371 def test_copy_dir_400(self):
372 372 # can't copy directories
373 373 with assert_http_error(400):
374 374 resp = self.api.copy(u'Ã¥ b', u'Ã¥ c')
375 375
376 376 def test_delete(self):
377 377 for d, name in self.dirs_nbs:
378 378 resp = self.api.delete('%s.ipynb' % name, d)
379 379 self.assertEqual(resp.status_code, 204)
380 380
381 381 for d in self.dirs + ['/']:
382 382 nbs = notebooks_only(self.api.list(d).json())
383 383 self.assertEqual(len(nbs), 0)
384 384
385 385 def test_delete_dirs(self):
386 386 # depth-first delete everything, so we don't try to delete empty directories
387 387 for name in sorted(self.dirs + ['/'], key=len, reverse=True):
388 388 listing = self.api.list(name).json()['content']
389 389 for model in listing:
390 390 self.api.delete(model['name'], model['path'])
391 391 listing = self.api.list('/').json()['content']
392 392 self.assertEqual(listing, [])
393 393
394 394 def test_delete_non_empty_dir(self):
395 395 """delete non-empty dir raises 400"""
396 396 with assert_http_error(400):
397 397 self.api.delete(u'Ã¥ b')
398 398
399 399 def test_rename(self):
400 400 resp = self.api.rename('a.ipynb', 'foo', 'z.ipynb')
401 401 self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb')
402 402 self.assertEqual(resp.json()['name'], 'z.ipynb')
403 403 assert os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'z.ipynb'))
404 404
405 405 nbs = notebooks_only(self.api.list('foo').json())
406 406 nbnames = set(n['name'] for n in nbs)
407 407 self.assertIn('z.ipynb', nbnames)
408 408 self.assertNotIn('a.ipynb', nbnames)
409 409
410 410 def test_rename_existing(self):
411 411 with assert_http_error(409):
412 412 self.api.rename('a.ipynb', 'foo', 'b.ipynb')
413 413
414 414 def test_save(self):
415 415 resp = self.api.read('a.ipynb', 'foo')
416 416 nbcontent = json.loads(resp.text)['content']
417 417 nb = to_notebook_json(nbcontent)
418 nb.cells.append(new_heading_cell(u'Created by test ³'))
418 nb.cells.append(new_markdown_cell(u'Created by test ³'))
419 419
420 420 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'}
421 421 resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
422 422
423 423 nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb')
424 424 with io.open(nbfile, 'r', encoding='utf-8') as f:
425 425 newnb = read(f, format='ipynb')
426 426 self.assertEqual(newnb.cells[0].source,
427 427 u'Created by test ³')
428 428 nbcontent = self.api.read('a.ipynb', 'foo').json()['content']
429 429 newnb = to_notebook_json(nbcontent)
430 430 self.assertEqual(newnb.cells[0].source,
431 431 u'Created by test ³')
432 432
433 433 # Save and rename
434 434 nbmodel= {'name': 'a2.ipynb', 'path':'foo/bar', 'content': nb, 'type': 'notebook'}
435 435 resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
436 436 saved = resp.json()
437 437 self.assertEqual(saved['name'], 'a2.ipynb')
438 438 self.assertEqual(saved['path'], 'foo/bar')
439 439 assert os.path.isfile(pjoin(self.notebook_dir.name,'foo','bar','a2.ipynb'))
440 440 assert not os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'a.ipynb'))
441 441 with assert_http_error(404):
442 442 self.api.read('a.ipynb', 'foo')
443 443
444 444 def test_checkpoints(self):
445 445 resp = self.api.read('a.ipynb', 'foo')
446 446 r = self.api.new_checkpoint('a.ipynb', 'foo')
447 447 self.assertEqual(r.status_code, 201)
448 448 cp1 = r.json()
449 449 self.assertEqual(set(cp1), {'id', 'last_modified'})
450 450 self.assertEqual(r.headers['Location'].split('/')[-1], cp1['id'])
451 451
452 452 # Modify it
453 453 nbcontent = json.loads(resp.text)['content']
454 454 nb = to_notebook_json(nbcontent)
455 hcell = new_heading_cell('Created by test')
455 hcell = new_markdown_cell('Created by test')
456 456 nb.cells.append(hcell)
457 457 # Save
458 458 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb, 'type': 'notebook'}
459 459 resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
460 460
461 461 # List checkpoints
462 462 cps = self.api.get_checkpoints('a.ipynb', 'foo').json()
463 463 self.assertEqual(cps, [cp1])
464 464
465 465 nbcontent = self.api.read('a.ipynb', 'foo').json()['content']
466 466 nb = to_notebook_json(nbcontent)
467 467 self.assertEqual(nb.cells[0].source, 'Created by test')
468 468
469 469 # Restore cp1
470 470 r = self.api.restore_checkpoint('a.ipynb', 'foo', cp1['id'])
471 471 self.assertEqual(r.status_code, 204)
472 472 nbcontent = self.api.read('a.ipynb', 'foo').json()['content']
473 473 nb = to_notebook_json(nbcontent)
474 474 self.assertEqual(nb.cells, [])
475 475
476 476 # Delete cp1
477 477 r = self.api.delete_checkpoint('a.ipynb', 'foo', cp1['id'])
478 478 self.assertEqual(r.status_code, 204)
479 479 cps = self.api.get_checkpoints('a.ipynb', 'foo').json()
480 480 self.assertEqual(cps, [])
@@ -1,226 +1,214 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'notebook/js/toolbar',
8 8 'notebook/js/celltoolbar',
9 9 ], function(IPython, $, toolbar, celltoolbar) {
10 10 "use strict";
11 11
12 12 var MainToolBar = function (selector, options) {
13 13 // Constructor
14 14 //
15 15 // Parameters:
16 16 // selector: string
17 17 // options: dictionary
18 18 // Dictionary of keyword arguments.
19 19 // events: $(Events) instance
20 20 // notebook: Notebook instance
21 21 toolbar.ToolBar.apply(this, arguments);
22 22 this.events = options.events;
23 23 this.notebook = options.notebook;
24 24 this.construct();
25 25 this.add_celltype_list();
26 26 this.add_celltoolbar_list();
27 27 this.bind_events();
28 28 };
29 29
30 30 MainToolBar.prototype = Object.create(toolbar.ToolBar.prototype);
31 31
32 32 MainToolBar.prototype.construct = function () {
33 33 var that = this;
34 34 this.add_buttons_group([
35 35 {
36 36 id : 'save_b',
37 37 label : 'Save and Checkpoint',
38 38 icon : 'fa-save',
39 39 callback : function () {
40 40 that.notebook.save_checkpoint();
41 41 }
42 42 }
43 43 ]);
44 44
45 45 this.add_buttons_group([
46 46 {
47 47 id : 'insert_below_b',
48 48 label : 'Insert Cell Below',
49 49 icon : 'fa-plus',
50 50 callback : function () {
51 51 that.notebook.insert_cell_below('code');
52 52 that.notebook.select_next();
53 53 that.notebook.focus_cell();
54 54 }
55 55 }
56 56 ],'insert_above_below');
57 57
58 58 this.add_buttons_group([
59 59 {
60 60 id : 'cut_b',
61 61 label : 'Cut Cell',
62 62 icon : 'fa-cut',
63 63 callback : function () {
64 64 that.notebook.cut_cell();
65 65 }
66 66 },
67 67 {
68 68 id : 'copy_b',
69 69 label : 'Copy Cell',
70 70 icon : 'fa-copy',
71 71 callback : function () {
72 72 that.notebook.copy_cell();
73 73 }
74 74 },
75 75 {
76 76 id : 'paste_b',
77 77 label : 'Paste Cell Below',
78 78 icon : 'fa-paste',
79 79 callback : function () {
80 80 that.notebook.paste_cell_below();
81 81 }
82 82 }
83 83 ],'cut_copy_paste');
84 84
85 85 this.add_buttons_group([
86 86 {
87 87 id : 'move_up_b',
88 88 label : 'Move Cell Up',
89 89 icon : 'fa-arrow-up',
90 90 callback : function () {
91 91 that.notebook.move_cell_up();
92 92 }
93 93 },
94 94 {
95 95 id : 'move_down_b',
96 96 label : 'Move Cell Down',
97 97 icon : 'fa-arrow-down',
98 98 callback : function () {
99 99 that.notebook.move_cell_down();
100 100 }
101 101 }
102 102 ],'move_up_down');
103 103
104 104
105 105 this.add_buttons_group([
106 106 {
107 107 id : 'run_b',
108 108 label : 'Run Cell',
109 109 icon : 'fa-play',
110 110 callback : function () {
111 111 // emulate default shift-enter behavior
112 112 that.notebook.execute_cell_and_select_below();
113 113 }
114 114 },
115 115 {
116 116 id : 'interrupt_b',
117 117 label : 'Interrupt',
118 118 icon : 'fa-stop',
119 119 callback : function () {
120 120 that.notebook.kernel.interrupt();
121 121 }
122 122 },
123 123 {
124 124 id : 'repeat_b',
125 125 label : 'Restart Kernel',
126 126 icon : 'fa-repeat',
127 127 callback : function () {
128 128 that.notebook.restart_kernel();
129 129 }
130 130 }
131 131 ],'run_int');
132 132 };
133 133
134 134 MainToolBar.prototype.add_celltype_list = function () {
135 135 this.element
136 136 .append($('<select/>')
137 137 .attr('id','cell_type')
138 138 .addClass('form-control select-xs')
139 139 .append($('<option/>').attr('value','code').text('Code'))
140 140 .append($('<option/>').attr('value','markdown').text('Markdown'))
141 141 .append($('<option/>').attr('value','raw').text('Raw NBConvert'))
142 .append($('<option/>').attr('value','heading1').text('Heading 1'))
143 .append($('<option/>').attr('value','heading2').text('Heading 2'))
144 .append($('<option/>').attr('value','heading3').text('Heading 3'))
145 .append($('<option/>').attr('value','heading4').text('Heading 4'))
146 .append($('<option/>').attr('value','heading5').text('Heading 5'))
147 .append($('<option/>').attr('value','heading6').text('Heading 6'))
148 142 );
149 143 };
150 144
151 145 MainToolBar.prototype.add_celltoolbar_list = function () {
152 146 var label = $('<span/>').addClass("navbar-text").text('Cell Toolbar:');
153 147 var select = $('<select/>')
154 148 .attr('id', 'ctb_select')
155 149 .addClass('form-control select-xs')
156 150 .append($('<option/>').attr('value', '').text('None'));
157 151 this.element.append(label).append(select);
158 152 var that = this;
159 153 select.change(function() {
160 154 var val = $(this).val();
161 155 if (val ==='') {
162 156 celltoolbar.CellToolbar.global_hide();
163 157 delete that.notebook.metadata.celltoolbar;
164 158 } else {
165 159 celltoolbar.CellToolbar.global_show();
166 160 celltoolbar.CellToolbar.activate_preset(val, that.events);
167 161 that.notebook.metadata.celltoolbar = val;
168 162 }
169 163 });
170 164 // Setup the currently registered presets.
171 165 var presets = celltoolbar.CellToolbar.list_presets();
172 166 for (var i=0; i<presets.length; i++) {
173 167 var name = presets[i];
174 168 select.append($('<option/>').attr('value', name).text(name));
175 169 }
176 170 // Setup future preset registrations.
177 171 this.events.on('preset_added.CellToolbar', function (event, data) {
178 172 var name = data.name;
179 173 select.append($('<option/>').attr('value', name).text(name));
180 174 });
181 175 // Update select value when a preset is activated.
182 176 this.events.on('preset_activated.CellToolbar', function (event, data) {
183 177 if (select.val() !== data.name)
184 178 select.val(data.name);
185 179 });
186 180 };
187 181
188 182 MainToolBar.prototype.bind_events = function () {
189 183 var that = this;
190 184
191 185 this.element.find('#cell_type').change(function () {
192 186 var cell_type = $(this).val();
193 if (cell_type === 'code') {
187 switch (cell_type) {
188 case 'code':
194 189 that.notebook.to_code();
195 } else if (cell_type === 'markdown') {
190 break;
191 case 'markdown':
196 192 that.notebook.to_markdown();
197 } else if (cell_type === 'raw') {
193 break;
194 case 'raw':
198 195 that.notebook.to_raw();
199 } else if (cell_type === 'heading1') {
200 that.notebook.to_heading(undefined, 1);
201 } else if (cell_type === 'heading2') {
202 that.notebook.to_heading(undefined, 2);
203 } else if (cell_type === 'heading3') {
204 that.notebook.to_heading(undefined, 3);
205 } else if (cell_type === 'heading4') {
206 that.notebook.to_heading(undefined, 4);
207 } else if (cell_type === 'heading5') {
208 that.notebook.to_heading(undefined, 5);
209 } else if (cell_type === 'heading6') {
210 that.notebook.to_heading(undefined, 6);
196 break;
197 default:
198 console.log("unrecognized cell type:", cell_type);
211 199 }
212 200 });
213 201 this.events.on('selected_cell_type_changed.Notebook', function (event, data) {
214 202 if (data.cell_type === 'heading') {
215 203 that.element.find('#cell_type').val(data.cell_type+data.level);
216 204 } else {
217 205 that.element.find('#cell_type').val(data.cell_type);
218 206 }
219 207 });
220 208 };
221 209
222 210 // Backwards compatability.
223 211 IPython.MainToolBar = MainToolBar;
224 212
225 213 return {'MainToolBar': MainToolBar};
226 214 });
@@ -1,2678 +1,2682 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/utils',
8 8 'base/js/dialog',
9 9 'notebook/js/textcell',
10 10 'notebook/js/codecell',
11 11 'services/sessions/session',
12 12 'notebook/js/celltoolbar',
13 13 'components/marked/lib/marked',
14 14 'highlight',
15 15 'notebook/js/mathjaxutils',
16 16 'base/js/keyboard',
17 17 'notebook/js/tooltip',
18 18 'notebook/js/celltoolbarpresets/default',
19 19 'notebook/js/celltoolbarpresets/rawcell',
20 20 'notebook/js/celltoolbarpresets/slideshow',
21 21 'notebook/js/scrollmanager'
22 22 ], function (
23 23 IPython,
24 24 $,
25 25 utils,
26 26 dialog,
27 27 textcell,
28 28 codecell,
29 29 session,
30 30 celltoolbar,
31 31 marked,
32 32 hljs,
33 33 mathjaxutils,
34 34 keyboard,
35 35 tooltip,
36 36 default_celltoolbar,
37 37 rawcell_celltoolbar,
38 38 slideshow_celltoolbar,
39 39 scrollmanager
40 40 ) {
41 41
42 42 var Notebook = function (selector, options) {
43 43 // Constructor
44 44 //
45 45 // A notebook contains and manages cells.
46 46 //
47 47 // Parameters:
48 48 // selector: string
49 49 // options: dictionary
50 50 // Dictionary of keyword arguments.
51 51 // events: $(Events) instance
52 52 // keyboard_manager: KeyboardManager instance
53 53 // save_widget: SaveWidget instance
54 54 // config: dictionary
55 55 // base_url : string
56 56 // notebook_path : string
57 57 // notebook_name : string
58 58 this.config = utils.mergeopt(Notebook, options.config);
59 59 this.base_url = options.base_url;
60 60 this.notebook_path = options.notebook_path;
61 61 this.notebook_name = options.notebook_name;
62 62 this.events = options.events;
63 63 this.keyboard_manager = options.keyboard_manager;
64 64 this.save_widget = options.save_widget;
65 65 this.tooltip = new tooltip.Tooltip(this.events);
66 66 this.ws_url = options.ws_url;
67 67 this._session_starting = false;
68 68 this.default_cell_type = this.config.default_cell_type || 'code';
69 69
70 70 // Create default scroll manager.
71 71 this.scroll_manager = new scrollmanager.ScrollManager(this);
72 72
73 73 // TODO: This code smells (and the other `= this` line a couple lines down)
74 74 // We need a better way to deal with circular instance references.
75 75 this.keyboard_manager.notebook = this;
76 76 this.save_widget.notebook = this;
77 77
78 78 mathjaxutils.init();
79 79
80 80 if (marked) {
81 81 marked.setOptions({
82 82 gfm : true,
83 83 tables: true,
84 84 langPrefix: "language-",
85 85 highlight: function(code, lang) {
86 86 if (!lang) {
87 87 // no language, no highlight
88 88 return code;
89 89 }
90 90 var highlighted;
91 91 try {
92 92 highlighted = hljs.highlight(lang, code, false);
93 93 } catch(err) {
94 94 highlighted = hljs.highlightAuto(code);
95 95 }
96 96 return highlighted.value;
97 97 }
98 98 });
99 99 }
100 100
101 101 this.element = $(selector);
102 102 this.element.scroll();
103 103 this.element.data("notebook", this);
104 104 this.next_prompt_number = 1;
105 105 this.session = null;
106 106 this.kernel = null;
107 107 this.clipboard = null;
108 108 this.undelete_backup = null;
109 109 this.undelete_index = null;
110 110 this.undelete_below = false;
111 111 this.paste_enabled = false;
112 112 // It is important to start out in command mode to match the intial mode
113 113 // of the KeyboardManager.
114 114 this.mode = 'command';
115 115 this.set_dirty(false);
116 116 this.metadata = {};
117 117 this._checkpoint_after_save = false;
118 118 this.last_checkpoint = null;
119 119 this.checkpoints = [];
120 120 this.autosave_interval = 0;
121 121 this.autosave_timer = null;
122 122 // autosave *at most* every two minutes
123 123 this.minimum_autosave_interval = 120000;
124 124 this.notebook_name_blacklist_re = /[\/\\:]/;
125 125 this.nbformat = 4; // Increment this when changing the nbformat
126 126 this.nbformat_minor = 0; // Increment this when changing the nbformat
127 127 this.codemirror_mode = 'ipython';
128 128 this.create_elements();
129 129 this.bind_events();
130 130 this.save_notebook = function() { // don't allow save until notebook_loaded
131 131 this.save_notebook_error(null, null, "Load failed, save is disabled");
132 132 };
133 133
134 134 // Trigger cell toolbar registration.
135 135 default_celltoolbar.register(this);
136 136 rawcell_celltoolbar.register(this);
137 137 slideshow_celltoolbar.register(this);
138 138 };
139 139
140 140 Notebook.options_default = {
141 141 // can be any cell type, or the special values of
142 142 // 'above', 'below', or 'selected' to get the value from another cell.
143 143 Notebook: {
144 144 default_cell_type: 'code',
145 145 }
146 146 };
147 147
148 148
149 149 /**
150 150 * Create an HTML and CSS representation of the notebook.
151 151 *
152 152 * @method create_elements
153 153 */
154 154 Notebook.prototype.create_elements = function () {
155 155 var that = this;
156 156 this.element.attr('tabindex','-1');
157 157 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
158 158 // We add this end_space div to the end of the notebook div to:
159 159 // i) provide a margin between the last cell and the end of the notebook
160 160 // ii) to prevent the div from scrolling up when the last cell is being
161 161 // edited, but is too low on the page, which browsers will do automatically.
162 162 var end_space = $('<div/>').addClass('end_space');
163 163 end_space.dblclick(function (e) {
164 164 var ncells = that.ncells();
165 165 that.insert_cell_below('code',ncells-1);
166 166 });
167 167 this.element.append(this.container);
168 168 this.container.append(end_space);
169 169 };
170 170
171 171 /**
172 172 * Bind JavaScript events: key presses and custom IPython events.
173 173 *
174 174 * @method bind_events
175 175 */
176 176 Notebook.prototype.bind_events = function () {
177 177 var that = this;
178 178
179 179 this.events.on('set_next_input.Notebook', function (event, data) {
180 180 var index = that.find_cell_index(data.cell);
181 181 var new_cell = that.insert_cell_below('code',index);
182 182 new_cell.set_text(data.text);
183 183 that.dirty = true;
184 184 });
185 185
186 186 this.events.on('set_dirty.Notebook', function (event, data) {
187 187 that.dirty = data.value;
188 188 });
189 189
190 190 this.events.on('trust_changed.Notebook', function (event, trusted) {
191 191 that.trusted = trusted;
192 192 });
193 193
194 194 this.events.on('select.Cell', function (event, data) {
195 195 var index = that.find_cell_index(data.cell);
196 196 that.select(index);
197 197 });
198 198
199 199 this.events.on('edit_mode.Cell', function (event, data) {
200 200 that.handle_edit_mode(data.cell);
201 201 });
202 202
203 203 this.events.on('command_mode.Cell', function (event, data) {
204 204 that.handle_command_mode(data.cell);
205 205 });
206 206
207 207 this.events.on('spec_changed.Kernel', function(event, data) {
208 208 that.metadata.kernelspec =
209 209 {name: data.name, display_name: data.display_name};
210 210 });
211 211
212 212 this.events.on('kernel_ready.Kernel', function(event, data) {
213 213 var kinfo = data.kernel.info_reply
214 214 var langinfo = kinfo.language_info || {};
215 215 if (!langinfo.name) langinfo.name = kinfo.language;
216 216
217 217 that.metadata.language_info = langinfo;
218 218 // Mode 'null' should be plain, unhighlighted text.
219 219 var cm_mode = langinfo.codemirror_mode || langinfo.language || 'null'
220 220 that.set_codemirror_mode(cm_mode);
221 221 });
222 222
223 223 var collapse_time = function (time) {
224 224 var app_height = $('#ipython-main-app').height(); // content height
225 225 var splitter_height = $('div#pager_splitter').outerHeight(true);
226 226 var new_height = app_height - splitter_height;
227 227 that.element.animate({height : new_height + 'px'}, time);
228 228 };
229 229
230 230 this.element.bind('collapse_pager', function (event, extrap) {
231 231 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
232 232 collapse_time(time);
233 233 });
234 234
235 235 var expand_time = function (time) {
236 236 var app_height = $('#ipython-main-app').height(); // content height
237 237 var splitter_height = $('div#pager_splitter').outerHeight(true);
238 238 var pager_height = $('div#pager').outerHeight(true);
239 239 var new_height = app_height - pager_height - splitter_height;
240 240 that.element.animate({height : new_height + 'px'}, time);
241 241 };
242 242
243 243 this.element.bind('expand_pager', function (event, extrap) {
244 244 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
245 245 expand_time(time);
246 246 });
247 247
248 248 // Firefox 22 broke $(window).on("beforeunload")
249 249 // I'm not sure why or how.
250 250 window.onbeforeunload = function (e) {
251 251 // TODO: Make killing the kernel configurable.
252 252 var kill_kernel = false;
253 253 if (kill_kernel) {
254 254 that.session.delete();
255 255 }
256 256 // if we are autosaving, trigger an autosave on nav-away.
257 257 // still warn, because if we don't the autosave may fail.
258 258 if (that.dirty) {
259 259 if ( that.autosave_interval ) {
260 260 // schedule autosave in a timeout
261 261 // this gives you a chance to forcefully discard changes
262 262 // by reloading the page if you *really* want to.
263 263 // the timer doesn't start until you *dismiss* the dialog.
264 264 setTimeout(function () {
265 265 if (that.dirty) {
266 266 that.save_notebook();
267 267 }
268 268 }, 1000);
269 269 return "Autosave in progress, latest changes may be lost.";
270 270 } else {
271 271 return "Unsaved changes will be lost.";
272 272 }
273 273 }
274 274 // Null is the *only* return value that will make the browser not
275 275 // pop up the "don't leave" dialog.
276 276 return null;
277 277 };
278 278 };
279 279
280 280 /**
281 281 * Set the dirty flag, and trigger the set_dirty.Notebook event
282 282 *
283 283 * @method set_dirty
284 284 */
285 285 Notebook.prototype.set_dirty = function (value) {
286 286 if (value === undefined) {
287 287 value = true;
288 288 }
289 289 if (this.dirty == value) {
290 290 return;
291 291 }
292 292 this.events.trigger('set_dirty.Notebook', {value: value});
293 293 };
294 294
295 295 /**
296 296 * Scroll the top of the page to a given cell.
297 297 *
298 298 * @method scroll_to_cell
299 299 * @param {Number} cell_number An index of the cell to view
300 300 * @param {Number} time Animation time in milliseconds
301 301 * @return {Number} Pixel offset from the top of the container
302 302 */
303 303 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
304 304 var cells = this.get_cells();
305 305 time = time || 0;
306 306 cell_number = Math.min(cells.length-1,cell_number);
307 307 cell_number = Math.max(0 ,cell_number);
308 308 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
309 309 this.element.animate({scrollTop:scroll_value}, time);
310 310 return scroll_value;
311 311 };
312 312
313 313 /**
314 314 * Scroll to the bottom of the page.
315 315 *
316 316 * @method scroll_to_bottom
317 317 */
318 318 Notebook.prototype.scroll_to_bottom = function () {
319 319 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
320 320 };
321 321
322 322 /**
323 323 * Scroll to the top of the page.
324 324 *
325 325 * @method scroll_to_top
326 326 */
327 327 Notebook.prototype.scroll_to_top = function () {
328 328 this.element.animate({scrollTop:0}, 0);
329 329 };
330 330
331 331 // Edit Notebook metadata
332 332
333 333 Notebook.prototype.edit_metadata = function () {
334 334 var that = this;
335 335 dialog.edit_metadata({
336 336 md: this.metadata,
337 337 callback: function (md) {
338 338 that.metadata = md;
339 339 },
340 340 name: 'Notebook',
341 341 notebook: this,
342 342 keyboard_manager: this.keyboard_manager});
343 343 };
344 344
345 345 // Cell indexing, retrieval, etc.
346 346
347 347 /**
348 348 * Get all cell elements in the notebook.
349 349 *
350 350 * @method get_cell_elements
351 351 * @return {jQuery} A selector of all cell elements
352 352 */
353 353 Notebook.prototype.get_cell_elements = function () {
354 354 return this.container.children("div.cell");
355 355 };
356 356
357 357 /**
358 358 * Get a particular cell element.
359 359 *
360 360 * @method get_cell_element
361 361 * @param {Number} index An index of a cell to select
362 362 * @return {jQuery} A selector of the given cell.
363 363 */
364 364 Notebook.prototype.get_cell_element = function (index) {
365 365 var result = null;
366 366 var e = this.get_cell_elements().eq(index);
367 367 if (e.length !== 0) {
368 368 result = e;
369 369 }
370 370 return result;
371 371 };
372 372
373 373 /**
374 374 * Try to get a particular cell by msg_id.
375 375 *
376 376 * @method get_msg_cell
377 377 * @param {String} msg_id A message UUID
378 378 * @return {Cell} Cell or null if no cell was found.
379 379 */
380 380 Notebook.prototype.get_msg_cell = function (msg_id) {
381 381 return codecell.CodeCell.msg_cells[msg_id] || null;
382 382 };
383 383
384 384 /**
385 385 * Count the cells in this notebook.
386 386 *
387 387 * @method ncells
388 388 * @return {Number} The number of cells in this notebook
389 389 */
390 390 Notebook.prototype.ncells = function () {
391 391 return this.get_cell_elements().length;
392 392 };
393 393
394 394 /**
395 395 * Get all Cell objects in this notebook.
396 396 *
397 397 * @method get_cells
398 398 * @return {Array} This notebook's Cell objects
399 399 */
400 400 // TODO: we are often calling cells as cells()[i], which we should optimize
401 401 // to cells(i) or a new method.
402 402 Notebook.prototype.get_cells = function () {
403 403 return this.get_cell_elements().toArray().map(function (e) {
404 404 return $(e).data("cell");
405 405 });
406 406 };
407 407
408 408 /**
409 409 * Get a Cell object from this notebook.
410 410 *
411 411 * @method get_cell
412 412 * @param {Number} index An index of a cell to retrieve
413 413 * @return {Cell} Cell or null if no cell was found.
414 414 */
415 415 Notebook.prototype.get_cell = function (index) {
416 416 var result = null;
417 417 var ce = this.get_cell_element(index);
418 418 if (ce !== null) {
419 419 result = ce.data('cell');
420 420 }
421 421 return result;
422 422 };
423 423
424 424 /**
425 425 * Get the cell below a given cell.
426 426 *
427 427 * @method get_next_cell
428 428 * @param {Cell} cell The provided cell
429 429 * @return {Cell} the next cell or null if no cell was found.
430 430 */
431 431 Notebook.prototype.get_next_cell = function (cell) {
432 432 var result = null;
433 433 var index = this.find_cell_index(cell);
434 434 if (this.is_valid_cell_index(index+1)) {
435 435 result = this.get_cell(index+1);
436 436 }
437 437 return result;
438 438 };
439 439
440 440 /**
441 441 * Get the cell above a given cell.
442 442 *
443 443 * @method get_prev_cell
444 444 * @param {Cell} cell The provided cell
445 445 * @return {Cell} The previous cell or null if no cell was found.
446 446 */
447 447 Notebook.prototype.get_prev_cell = function (cell) {
448 448 var result = null;
449 449 var index = this.find_cell_index(cell);
450 450 if (index !== null && index > 0) {
451 451 result = this.get_cell(index-1);
452 452 }
453 453 return result;
454 454 };
455 455
456 456 /**
457 457 * Get the numeric index of a given cell.
458 458 *
459 459 * @method find_cell_index
460 460 * @param {Cell} cell The provided cell
461 461 * @return {Number} The cell's numeric index or null if no cell was found.
462 462 */
463 463 Notebook.prototype.find_cell_index = function (cell) {
464 464 var result = null;
465 465 this.get_cell_elements().filter(function (index) {
466 466 if ($(this).data("cell") === cell) {
467 467 result = index;
468 468 }
469 469 });
470 470 return result;
471 471 };
472 472
473 473 /**
474 474 * Get a given index , or the selected index if none is provided.
475 475 *
476 476 * @method index_or_selected
477 477 * @param {Number} index A cell's index
478 478 * @return {Number} The given index, or selected index if none is provided.
479 479 */
480 480 Notebook.prototype.index_or_selected = function (index) {
481 481 var i;
482 482 if (index === undefined || index === null) {
483 483 i = this.get_selected_index();
484 484 if (i === null) {
485 485 i = 0;
486 486 }
487 487 } else {
488 488 i = index;
489 489 }
490 490 return i;
491 491 };
492 492
493 493 /**
494 494 * Get the currently selected cell.
495 495 * @method get_selected_cell
496 496 * @return {Cell} The selected cell
497 497 */
498 498 Notebook.prototype.get_selected_cell = function () {
499 499 var index = this.get_selected_index();
500 500 return this.get_cell(index);
501 501 };
502 502
503 503 /**
504 504 * Check whether a cell index is valid.
505 505 *
506 506 * @method is_valid_cell_index
507 507 * @param {Number} index A cell index
508 508 * @return True if the index is valid, false otherwise
509 509 */
510 510 Notebook.prototype.is_valid_cell_index = function (index) {
511 511 if (index !== null && index >= 0 && index < this.ncells()) {
512 512 return true;
513 513 } else {
514 514 return false;
515 515 }
516 516 };
517 517
518 518 /**
519 519 * Get the index of the currently selected cell.
520 520
521 521 * @method get_selected_index
522 522 * @return {Number} The selected cell's numeric index
523 523 */
524 524 Notebook.prototype.get_selected_index = function () {
525 525 var result = null;
526 526 this.get_cell_elements().filter(function (index) {
527 527 if ($(this).data("cell").selected === true) {
528 528 result = index;
529 529 }
530 530 });
531 531 return result;
532 532 };
533 533
534 534
535 535 // Cell selection.
536 536
537 537 /**
538 538 * Programmatically select a cell.
539 539 *
540 540 * @method select
541 541 * @param {Number} index A cell's index
542 542 * @return {Notebook} This notebook
543 543 */
544 544 Notebook.prototype.select = function (index) {
545 545 if (this.is_valid_cell_index(index)) {
546 546 var sindex = this.get_selected_index();
547 547 if (sindex !== null && index !== sindex) {
548 548 // If we are about to select a different cell, make sure we are
549 549 // first in command mode.
550 550 if (this.mode !== 'command') {
551 551 this.command_mode();
552 552 }
553 553 this.get_cell(sindex).unselect();
554 554 }
555 555 var cell = this.get_cell(index);
556 556 cell.select();
557 557 if (cell.cell_type === 'heading') {
558 558 this.events.trigger('selected_cell_type_changed.Notebook',
559 559 {'cell_type':cell.cell_type,level:cell.level}
560 560 );
561 561 } else {
562 562 this.events.trigger('selected_cell_type_changed.Notebook',
563 563 {'cell_type':cell.cell_type}
564 564 );
565 565 }
566 566 }
567 567 return this;
568 568 };
569 569
570 570 /**
571 571 * Programmatically select the next cell.
572 572 *
573 573 * @method select_next
574 574 * @return {Notebook} This notebook
575 575 */
576 576 Notebook.prototype.select_next = function () {
577 577 var index = this.get_selected_index();
578 578 this.select(index+1);
579 579 return this;
580 580 };
581 581
582 582 /**
583 583 * Programmatically select the previous cell.
584 584 *
585 585 * @method select_prev
586 586 * @return {Notebook} This notebook
587 587 */
588 588 Notebook.prototype.select_prev = function () {
589 589 var index = this.get_selected_index();
590 590 this.select(index-1);
591 591 return this;
592 592 };
593 593
594 594
595 595 // Edit/Command mode
596 596
597 597 /**
598 598 * Gets the index of the cell that is in edit mode.
599 599 *
600 600 * @method get_edit_index
601 601 *
602 602 * @return index {int}
603 603 **/
604 604 Notebook.prototype.get_edit_index = function () {
605 605 var result = null;
606 606 this.get_cell_elements().filter(function (index) {
607 607 if ($(this).data("cell").mode === 'edit') {
608 608 result = index;
609 609 }
610 610 });
611 611 return result;
612 612 };
613 613
614 614 /**
615 615 * Handle when a a cell blurs and the notebook should enter command mode.
616 616 *
617 617 * @method handle_command_mode
618 618 * @param [cell] {Cell} Cell to enter command mode on.
619 619 **/
620 620 Notebook.prototype.handle_command_mode = function (cell) {
621 621 if (this.mode !== 'command') {
622 622 cell.command_mode();
623 623 this.mode = 'command';
624 624 this.events.trigger('command_mode.Notebook');
625 625 this.keyboard_manager.command_mode();
626 626 }
627 627 };
628 628
629 629 /**
630 630 * Make the notebook enter command mode.
631 631 *
632 632 * @method command_mode
633 633 **/
634 634 Notebook.prototype.command_mode = function () {
635 635 var cell = this.get_cell(this.get_edit_index());
636 636 if (cell && this.mode !== 'command') {
637 637 // We don't call cell.command_mode, but rather call cell.focus_cell()
638 638 // which will blur and CM editor and trigger the call to
639 639 // handle_command_mode.
640 640 cell.focus_cell();
641 641 }
642 642 };
643 643
644 644 /**
645 645 * Handle when a cell fires it's edit_mode event.
646 646 *
647 647 * @method handle_edit_mode
648 648 * @param [cell] {Cell} Cell to enter edit mode on.
649 649 **/
650 650 Notebook.prototype.handle_edit_mode = function (cell) {
651 651 if (cell && this.mode !== 'edit') {
652 652 cell.edit_mode();
653 653 this.mode = 'edit';
654 654 this.events.trigger('edit_mode.Notebook');
655 655 this.keyboard_manager.edit_mode();
656 656 }
657 657 };
658 658
659 659 /**
660 660 * Make a cell enter edit mode.
661 661 *
662 662 * @method edit_mode
663 663 **/
664 664 Notebook.prototype.edit_mode = function () {
665 665 var cell = this.get_selected_cell();
666 666 if (cell && this.mode !== 'edit') {
667 667 cell.unrender();
668 668 cell.focus_editor();
669 669 }
670 670 };
671 671
672 672 /**
673 673 * Focus the currently selected cell.
674 674 *
675 675 * @method focus_cell
676 676 **/
677 677 Notebook.prototype.focus_cell = function () {
678 678 var cell = this.get_selected_cell();
679 679 if (cell === null) {return;} // No cell is selected
680 680 cell.focus_cell();
681 681 };
682 682
683 683 // Cell movement
684 684
685 685 /**
686 686 * Move given (or selected) cell up and select it.
687 687 *
688 688 * @method move_cell_up
689 689 * @param [index] {integer} cell index
690 690 * @return {Notebook} This notebook
691 691 **/
692 692 Notebook.prototype.move_cell_up = function (index) {
693 693 var i = this.index_or_selected(index);
694 694 if (this.is_valid_cell_index(i) && i > 0) {
695 695 var pivot = this.get_cell_element(i-1);
696 696 var tomove = this.get_cell_element(i);
697 697 if (pivot !== null && tomove !== null) {
698 698 tomove.detach();
699 699 pivot.before(tomove);
700 700 this.select(i-1);
701 701 var cell = this.get_selected_cell();
702 702 cell.focus_cell();
703 703 }
704 704 this.set_dirty(true);
705 705 }
706 706 return this;
707 707 };
708 708
709 709
710 710 /**
711 711 * Move given (or selected) cell down and select it
712 712 *
713 713 * @method move_cell_down
714 714 * @param [index] {integer} cell index
715 715 * @return {Notebook} This notebook
716 716 **/
717 717 Notebook.prototype.move_cell_down = function (index) {
718 718 var i = this.index_or_selected(index);
719 719 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
720 720 var pivot = this.get_cell_element(i+1);
721 721 var tomove = this.get_cell_element(i);
722 722 if (pivot !== null && tomove !== null) {
723 723 tomove.detach();
724 724 pivot.after(tomove);
725 725 this.select(i+1);
726 726 var cell = this.get_selected_cell();
727 727 cell.focus_cell();
728 728 }
729 729 }
730 730 this.set_dirty();
731 731 return this;
732 732 };
733 733
734 734
735 735 // Insertion, deletion.
736 736
737 737 /**
738 738 * Delete a cell from the notebook.
739 739 *
740 740 * @method delete_cell
741 741 * @param [index] A cell's numeric index
742 742 * @return {Notebook} This notebook
743 743 */
744 744 Notebook.prototype.delete_cell = function (index) {
745 745 var i = this.index_or_selected(index);
746 746 var cell = this.get_cell(i);
747 747 if (!cell.is_deletable()) {
748 748 return this;
749 749 }
750 750
751 751 this.undelete_backup = cell.toJSON();
752 752 $('#undelete_cell').removeClass('disabled');
753 753 if (this.is_valid_cell_index(i)) {
754 754 var old_ncells = this.ncells();
755 755 var ce = this.get_cell_element(i);
756 756 ce.remove();
757 757 if (i === 0) {
758 758 // Always make sure we have at least one cell.
759 759 if (old_ncells === 1) {
760 760 this.insert_cell_below('code');
761 761 }
762 762 this.select(0);
763 763 this.undelete_index = 0;
764 764 this.undelete_below = false;
765 765 } else if (i === old_ncells-1 && i !== 0) {
766 766 this.select(i-1);
767 767 this.undelete_index = i - 1;
768 768 this.undelete_below = true;
769 769 } else {
770 770 this.select(i);
771 771 this.undelete_index = i;
772 772 this.undelete_below = false;
773 773 }
774 774 this.events.trigger('delete.Cell', {'cell': cell, 'index': i});
775 775 this.set_dirty(true);
776 776 }
777 777 return this;
778 778 };
779 779
780 780 /**
781 781 * Restore the most recently deleted cell.
782 782 *
783 783 * @method undelete
784 784 */
785 785 Notebook.prototype.undelete_cell = function() {
786 786 if (this.undelete_backup !== null && this.undelete_index !== null) {
787 787 var current_index = this.get_selected_index();
788 788 if (this.undelete_index < current_index) {
789 789 current_index = current_index + 1;
790 790 }
791 791 if (this.undelete_index >= this.ncells()) {
792 792 this.select(this.ncells() - 1);
793 793 }
794 794 else {
795 795 this.select(this.undelete_index);
796 796 }
797 797 var cell_data = this.undelete_backup;
798 798 var new_cell = null;
799 799 if (this.undelete_below) {
800 800 new_cell = this.insert_cell_below(cell_data.cell_type);
801 801 } else {
802 802 new_cell = this.insert_cell_above(cell_data.cell_type);
803 803 }
804 804 new_cell.fromJSON(cell_data);
805 805 if (this.undelete_below) {
806 806 this.select(current_index+1);
807 807 } else {
808 808 this.select(current_index);
809 809 }
810 810 this.undelete_backup = null;
811 811 this.undelete_index = null;
812 812 }
813 813 $('#undelete_cell').addClass('disabled');
814 814 };
815 815
816 816 /**
817 817 * Insert a cell so that after insertion the cell is at given index.
818 818 *
819 819 * If cell type is not provided, it will default to the type of the
820 820 * currently active cell.
821 821 *
822 822 * Similar to insert_above, but index parameter is mandatory
823 823 *
824 824 * Index will be brought back into the accessible range [0,n]
825 825 *
826 826 * @method insert_cell_at_index
827 * @param [type] {string} in ['code','markdown','heading'], defaults to 'code'
827 * @param [type] {string} in ['code','markdown', 'raw'], defaults to 'code'
828 828 * @param [index] {int} a valid index where to insert cell
829 829 *
830 830 * @return cell {cell|null} created cell or null
831 831 **/
832 832 Notebook.prototype.insert_cell_at_index = function(type, index){
833 833
834 834 var ncells = this.ncells();
835 835 index = Math.min(index, ncells);
836 836 index = Math.max(index, 0);
837 837 var cell = null;
838 838 type = type || this.default_cell_type;
839 839 if (type === 'above') {
840 840 if (index > 0) {
841 841 type = this.get_cell(index-1).cell_type;
842 842 } else {
843 843 type = 'code';
844 844 }
845 845 } else if (type === 'below') {
846 846 if (index < ncells) {
847 847 type = this.get_cell(index).cell_type;
848 848 } else {
849 849 type = 'code';
850 850 }
851 851 } else if (type === 'selected') {
852 852 type = this.get_selected_cell().cell_type;
853 853 }
854 854
855 855 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
856 856 var cell_options = {
857 857 events: this.events,
858 858 config: this.config,
859 859 keyboard_manager: this.keyboard_manager,
860 860 notebook: this,
861 861 tooltip: this.tooltip,
862 862 };
863 if (type === 'code') {
863 switch(type) {
864 case 'code':
864 865 cell = new codecell.CodeCell(this.kernel, cell_options);
865 866 cell.set_input_prompt();
866 } else if (type === 'markdown') {
867 break;
868 case 'markdown':
867 869 cell = new textcell.MarkdownCell(cell_options);
868 } else if (type === 'raw') {
870 break;
871 case 'raw':
869 872 cell = new textcell.RawCell(cell_options);
870 } else if (type === 'heading') {
871 cell = new textcell.HeadingCell(cell_options);
873 break;
874 default:
875 console.log("invalid cell type: ", type);
872 876 }
873 877
874 878 if(this._insert_element_at_index(cell.element,index)) {
875 879 cell.render();
876 880 this.events.trigger('create.Cell', {'cell': cell, 'index': index});
877 881 cell.refresh();
878 882 // We used to select the cell after we refresh it, but there
879 883 // are now cases were this method is called where select is
880 884 // not appropriate. The selection logic should be handled by the
881 885 // caller of the the top level insert_cell methods.
882 886 this.set_dirty(true);
883 887 }
884 888 }
885 889 return cell;
886 890
887 891 };
888 892
889 893 /**
890 894 * Insert an element at given cell index.
891 895 *
892 896 * @method _insert_element_at_index
893 897 * @param element {dom_element} a cell element
894 898 * @param [index] {int} a valid index where to inser cell
895 899 * @private
896 900 *
897 901 * return true if everything whent fine.
898 902 **/
899 903 Notebook.prototype._insert_element_at_index = function(element, index){
900 904 if (element === undefined){
901 905 return false;
902 906 }
903 907
904 908 var ncells = this.ncells();
905 909
906 910 if (ncells === 0) {
907 911 // special case append if empty
908 912 this.element.find('div.end_space').before(element);
909 913 } else if ( ncells === index ) {
910 914 // special case append it the end, but not empty
911 915 this.get_cell_element(index-1).after(element);
912 916 } else if (this.is_valid_cell_index(index)) {
913 917 // otherwise always somewhere to append to
914 918 this.get_cell_element(index).before(element);
915 919 } else {
916 920 return false;
917 921 }
918 922
919 923 if (this.undelete_index !== null && index <= this.undelete_index) {
920 924 this.undelete_index = this.undelete_index + 1;
921 925 this.set_dirty(true);
922 926 }
923 927 return true;
924 928 };
925 929
926 930 /**
927 931 * Insert a cell of given type above given index, or at top
928 932 * of notebook if index smaller than 0.
929 933 *
930 934 * default index value is the one of currently selected cell
931 935 *
932 936 * @method insert_cell_above
933 937 * @param [type] {string} cell type
934 938 * @param [index] {integer}
935 939 *
936 940 * @return handle to created cell or null
937 941 **/
938 942 Notebook.prototype.insert_cell_above = function (type, index) {
939 943 index = this.index_or_selected(index);
940 944 return this.insert_cell_at_index(type, index);
941 945 };
942 946
943 947 /**
944 948 * Insert a cell of given type below given index, or at bottom
945 949 * of notebook if index greater than number of cells
946 950 *
947 951 * default index value is the one of currently selected cell
948 952 *
949 953 * @method insert_cell_below
950 954 * @param [type] {string} cell type
951 955 * @param [index] {integer}
952 956 *
953 957 * @return handle to created cell or null
954 958 *
955 959 **/
956 960 Notebook.prototype.insert_cell_below = function (type, index) {
957 961 index = this.index_or_selected(index);
958 962 return this.insert_cell_at_index(type, index+1);
959 963 };
960 964
961 965
962 966 /**
963 967 * Insert cell at end of notebook
964 968 *
965 969 * @method insert_cell_at_bottom
966 970 * @param {String} type cell type
967 971 *
968 972 * @return the added cell; or null
969 973 **/
970 974 Notebook.prototype.insert_cell_at_bottom = function (type){
971 975 var len = this.ncells();
972 976 return this.insert_cell_below(type,len-1);
973 977 };
974 978
975 979 /**
976 980 * Turn a cell into a code cell.
977 981 *
978 982 * @method to_code
979 983 * @param {Number} [index] A cell's index
980 984 */
981 985 Notebook.prototype.to_code = function (index) {
982 986 var i = this.index_or_selected(index);
983 987 if (this.is_valid_cell_index(i)) {
984 988 var source_cell = this.get_cell(i);
985 989 if (!(source_cell instanceof codecell.CodeCell)) {
986 990 var target_cell = this.insert_cell_below('code',i);
987 991 var text = source_cell.get_text();
988 992 if (text === source_cell.placeholder) {
989 993 text = '';
990 994 }
991 995 //metadata
992 996 target_cell.metadata = source_cell.metadata;
993 997
994 998 target_cell.set_text(text);
995 999 // make this value the starting point, so that we can only undo
996 1000 // to this state, instead of a blank cell
997 1001 target_cell.code_mirror.clearHistory();
998 1002 source_cell.element.remove();
999 1003 this.select(i);
1000 1004 var cursor = source_cell.code_mirror.getCursor();
1001 1005 target_cell.code_mirror.setCursor(cursor);
1002 1006 this.set_dirty(true);
1003 1007 }
1004 1008 }
1005 1009 };
1006 1010
1007 1011 /**
1008 1012 * Turn a cell into a Markdown cell.
1009 1013 *
1010 1014 * @method to_markdown
1011 1015 * @param {Number} [index] A cell's index
1012 1016 */
1013 1017 Notebook.prototype.to_markdown = function (index) {
1014 1018 var i = this.index_or_selected(index);
1015 1019 if (this.is_valid_cell_index(i)) {
1016 1020 var source_cell = this.get_cell(i);
1017 1021
1018 1022 if (!(source_cell instanceof textcell.MarkdownCell)) {
1019 1023 var target_cell = this.insert_cell_below('markdown',i);
1020 1024 var text = source_cell.get_text();
1021 1025
1022 1026 if (text === source_cell.placeholder) {
1023 1027 text = '';
1024 1028 }
1025 1029 // metadata
1026 1030 target_cell.metadata = source_cell.metadata
1027 1031 // We must show the editor before setting its contents
1028 1032 target_cell.unrender();
1029 1033 target_cell.set_text(text);
1030 1034 // make this value the starting point, so that we can only undo
1031 1035 // to this state, instead of a blank cell
1032 1036 target_cell.code_mirror.clearHistory();
1033 1037 source_cell.element.remove();
1034 1038 this.select(i);
1035 1039 if ((source_cell instanceof textcell.TextCell) && source_cell.rendered) {
1036 1040 target_cell.render();
1037 1041 }
1038 1042 var cursor = source_cell.code_mirror.getCursor();
1039 1043 target_cell.code_mirror.setCursor(cursor);
1040 1044 this.set_dirty(true);
1041 1045 }
1042 1046 }
1043 1047 };
1044 1048
1045 1049 /**
1046 1050 * Turn a cell into a raw text cell.
1047 1051 *
1048 1052 * @method to_raw
1049 1053 * @param {Number} [index] A cell's index
1050 1054 */
1051 1055 Notebook.prototype.to_raw = function (index) {
1052 1056 var i = this.index_or_selected(index);
1053 1057 if (this.is_valid_cell_index(i)) {
1054 1058 var target_cell = null;
1055 1059 var source_cell = this.get_cell(i);
1056 1060
1057 1061 if (!(source_cell instanceof textcell.RawCell)) {
1058 1062 target_cell = this.insert_cell_below('raw',i);
1059 1063 var text = source_cell.get_text();
1060 1064 if (text === source_cell.placeholder) {
1061 1065 text = '';
1062 1066 }
1063 1067 //metadata
1064 1068 target_cell.metadata = source_cell.metadata;
1065 1069 // We must show the editor before setting its contents
1066 1070 target_cell.unrender();
1067 1071 target_cell.set_text(text);
1068 1072 // make this value the starting point, so that we can only undo
1069 1073 // to this state, instead of a blank cell
1070 1074 target_cell.code_mirror.clearHistory();
1071 1075 source_cell.element.remove();
1072 1076 this.select(i);
1073 1077 var cursor = source_cell.code_mirror.getCursor();
1074 1078 target_cell.code_mirror.setCursor(cursor);
1075 1079 this.set_dirty(true);
1076 1080 }
1077 1081 }
1078 1082 };
1079 1083
1080 1084 /**
1081 1085 * Turn a cell into a heading cell.
1082 1086 *
1083 1087 * @method to_heading
1084 1088 * @param {Number} [index] A cell's index
1085 1089 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
1086 1090 */
1087 1091 Notebook.prototype.to_heading = function (index, level) {
1088 1092 level = level || 1;
1089 1093 var i = this.index_or_selected(index);
1090 1094 if (this.is_valid_cell_index(i)) {
1091 1095 var source_cell = this.get_cell(i);
1092 1096 var target_cell = null;
1093 if (source_cell instanceof textcell.HeadingCell) {
1094 source_cell.set_level(level);
1097 if (source_cell instanceof textcell.MarkdownCell) {
1098 source_cell.set_heading_level(level);
1095 1099 } else {
1096 target_cell = this.insert_cell_below('heading',i);
1100 target_cell = this.insert_cell_below('markdown',i);
1097 1101 var text = source_cell.get_text();
1098 1102 if (text === source_cell.placeholder) {
1099 1103 text = '';
1100 1104 }
1101 1105 //metadata
1102 1106 target_cell.metadata = source_cell.metadata;
1103 1107 // We must show the editor before setting its contents
1104 target_cell.set_level(level);
1105 1108 target_cell.unrender();
1106 1109 target_cell.set_text(text);
1110 target_cell.set_heading_level(level);
1107 1111 // make this value the starting point, so that we can only undo
1108 1112 // to this state, instead of a blank cell
1109 1113 target_cell.code_mirror.clearHistory();
1110 1114 source_cell.element.remove();
1111 1115 this.select(i);
1112 1116 var cursor = source_cell.code_mirror.getCursor();
1113 1117 target_cell.code_mirror.setCursor(cursor);
1114 1118 if ((source_cell instanceof textcell.TextCell) && source_cell.rendered) {
1115 1119 target_cell.render();
1116 1120 }
1117 1121 }
1118 1122 this.set_dirty(true);
1119 1123 this.events.trigger('selected_cell_type_changed.Notebook',
1120 {'cell_type':'heading',level:level}
1124 {'cell_type':'markdown',level:level}
1121 1125 );
1122 1126 }
1123 1127 };
1124 1128
1125 1129
1126 1130 // Cut/Copy/Paste
1127 1131
1128 1132 /**
1129 1133 * Enable UI elements for pasting cells.
1130 1134 *
1131 1135 * @method enable_paste
1132 1136 */
1133 1137 Notebook.prototype.enable_paste = function () {
1134 1138 var that = this;
1135 1139 if (!this.paste_enabled) {
1136 1140 $('#paste_cell_replace').removeClass('disabled')
1137 1141 .on('click', function () {that.paste_cell_replace();});
1138 1142 $('#paste_cell_above').removeClass('disabled')
1139 1143 .on('click', function () {that.paste_cell_above();});
1140 1144 $('#paste_cell_below').removeClass('disabled')
1141 1145 .on('click', function () {that.paste_cell_below();});
1142 1146 this.paste_enabled = true;
1143 1147 }
1144 1148 };
1145 1149
1146 1150 /**
1147 1151 * Disable UI elements for pasting cells.
1148 1152 *
1149 1153 * @method disable_paste
1150 1154 */
1151 1155 Notebook.prototype.disable_paste = function () {
1152 1156 if (this.paste_enabled) {
1153 1157 $('#paste_cell_replace').addClass('disabled').off('click');
1154 1158 $('#paste_cell_above').addClass('disabled').off('click');
1155 1159 $('#paste_cell_below').addClass('disabled').off('click');
1156 1160 this.paste_enabled = false;
1157 1161 }
1158 1162 };
1159 1163
1160 1164 /**
1161 1165 * Cut a cell.
1162 1166 *
1163 1167 * @method cut_cell
1164 1168 */
1165 1169 Notebook.prototype.cut_cell = function () {
1166 1170 this.copy_cell();
1167 1171 this.delete_cell();
1168 1172 };
1169 1173
1170 1174 /**
1171 1175 * Copy a cell.
1172 1176 *
1173 1177 * @method copy_cell
1174 1178 */
1175 1179 Notebook.prototype.copy_cell = function () {
1176 1180 var cell = this.get_selected_cell();
1177 1181 this.clipboard = cell.toJSON();
1178 1182 // remove undeletable status from the copied cell
1179 1183 if (this.clipboard.metadata.deletable !== undefined) {
1180 1184 delete this.clipboard.metadata.deletable;
1181 1185 }
1182 1186 this.enable_paste();
1183 1187 };
1184 1188
1185 1189 /**
1186 1190 * Replace the selected cell with a cell in the clipboard.
1187 1191 *
1188 1192 * @method paste_cell_replace
1189 1193 */
1190 1194 Notebook.prototype.paste_cell_replace = function () {
1191 1195 if (this.clipboard !== null && this.paste_enabled) {
1192 1196 var cell_data = this.clipboard;
1193 1197 var new_cell = this.insert_cell_above(cell_data.cell_type);
1194 1198 new_cell.fromJSON(cell_data);
1195 1199 var old_cell = this.get_next_cell(new_cell);
1196 1200 this.delete_cell(this.find_cell_index(old_cell));
1197 1201 this.select(this.find_cell_index(new_cell));
1198 1202 }
1199 1203 };
1200 1204
1201 1205 /**
1202 1206 * Paste a cell from the clipboard above the selected cell.
1203 1207 *
1204 1208 * @method paste_cell_above
1205 1209 */
1206 1210 Notebook.prototype.paste_cell_above = function () {
1207 1211 if (this.clipboard !== null && this.paste_enabled) {
1208 1212 var cell_data = this.clipboard;
1209 1213 var new_cell = this.insert_cell_above(cell_data.cell_type);
1210 1214 new_cell.fromJSON(cell_data);
1211 1215 new_cell.focus_cell();
1212 1216 }
1213 1217 };
1214 1218
1215 1219 /**
1216 1220 * Paste a cell from the clipboard below the selected cell.
1217 1221 *
1218 1222 * @method paste_cell_below
1219 1223 */
1220 1224 Notebook.prototype.paste_cell_below = function () {
1221 1225 if (this.clipboard !== null && this.paste_enabled) {
1222 1226 var cell_data = this.clipboard;
1223 1227 var new_cell = this.insert_cell_below(cell_data.cell_type);
1224 1228 new_cell.fromJSON(cell_data);
1225 1229 new_cell.focus_cell();
1226 1230 }
1227 1231 };
1228 1232
1229 1233 // Split/merge
1230 1234
1231 1235 /**
1232 1236 * Split the selected cell into two, at the cursor.
1233 1237 *
1234 1238 * @method split_cell
1235 1239 */
1236 1240 Notebook.prototype.split_cell = function () {
1237 1241 var mdc = textcell.MarkdownCell;
1238 1242 var rc = textcell.RawCell;
1239 1243 var cell = this.get_selected_cell();
1240 1244 if (cell.is_splittable()) {
1241 1245 var texta = cell.get_pre_cursor();
1242 1246 var textb = cell.get_post_cursor();
1243 1247 cell.set_text(textb);
1244 1248 var new_cell = this.insert_cell_above(cell.cell_type);
1245 1249 // Unrender the new cell so we can call set_text.
1246 1250 new_cell.unrender();
1247 1251 new_cell.set_text(texta);
1248 1252 }
1249 1253 };
1250 1254
1251 1255 /**
1252 1256 * Combine the selected cell into the cell above it.
1253 1257 *
1254 1258 * @method merge_cell_above
1255 1259 */
1256 1260 Notebook.prototype.merge_cell_above = function () {
1257 1261 var mdc = textcell.MarkdownCell;
1258 1262 var rc = textcell.RawCell;
1259 1263 var index = this.get_selected_index();
1260 1264 var cell = this.get_cell(index);
1261 1265 var render = cell.rendered;
1262 1266 if (!cell.is_mergeable()) {
1263 1267 return;
1264 1268 }
1265 1269 if (index > 0) {
1266 1270 var upper_cell = this.get_cell(index-1);
1267 1271 if (!upper_cell.is_mergeable()) {
1268 1272 return;
1269 1273 }
1270 1274 var upper_text = upper_cell.get_text();
1271 1275 var text = cell.get_text();
1272 1276 if (cell instanceof codecell.CodeCell) {
1273 1277 cell.set_text(upper_text+'\n'+text);
1274 1278 } else {
1275 1279 cell.unrender(); // Must unrender before we set_text.
1276 1280 cell.set_text(upper_text+'\n\n'+text);
1277 1281 if (render) {
1278 1282 // The rendered state of the final cell should match
1279 1283 // that of the original selected cell;
1280 1284 cell.render();
1281 1285 }
1282 1286 }
1283 1287 this.delete_cell(index-1);
1284 1288 this.select(this.find_cell_index(cell));
1285 1289 }
1286 1290 };
1287 1291
1288 1292 /**
1289 1293 * Combine the selected cell into the cell below it.
1290 1294 *
1291 1295 * @method merge_cell_below
1292 1296 */
1293 1297 Notebook.prototype.merge_cell_below = function () {
1294 1298 var mdc = textcell.MarkdownCell;
1295 1299 var rc = textcell.RawCell;
1296 1300 var index = this.get_selected_index();
1297 1301 var cell = this.get_cell(index);
1298 1302 var render = cell.rendered;
1299 1303 if (!cell.is_mergeable()) {
1300 1304 return;
1301 1305 }
1302 1306 if (index < this.ncells()-1) {
1303 1307 var lower_cell = this.get_cell(index+1);
1304 1308 if (!lower_cell.is_mergeable()) {
1305 1309 return;
1306 1310 }
1307 1311 var lower_text = lower_cell.get_text();
1308 1312 var text = cell.get_text();
1309 1313 if (cell instanceof codecell.CodeCell) {
1310 1314 cell.set_text(text+'\n'+lower_text);
1311 1315 } else {
1312 1316 cell.unrender(); // Must unrender before we set_text.
1313 1317 cell.set_text(text+'\n\n'+lower_text);
1314 1318 if (render) {
1315 1319 // The rendered state of the final cell should match
1316 1320 // that of the original selected cell;
1317 1321 cell.render();
1318 1322 }
1319 1323 }
1320 1324 this.delete_cell(index+1);
1321 1325 this.select(this.find_cell_index(cell));
1322 1326 }
1323 1327 };
1324 1328
1325 1329
1326 1330 // Cell collapsing and output clearing
1327 1331
1328 1332 /**
1329 1333 * Hide a cell's output.
1330 1334 *
1331 1335 * @method collapse_output
1332 1336 * @param {Number} index A cell's numeric index
1333 1337 */
1334 1338 Notebook.prototype.collapse_output = function (index) {
1335 1339 var i = this.index_or_selected(index);
1336 1340 var cell = this.get_cell(i);
1337 1341 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1338 1342 cell.collapse_output();
1339 1343 this.set_dirty(true);
1340 1344 }
1341 1345 };
1342 1346
1343 1347 /**
1344 1348 * Hide each code cell's output area.
1345 1349 *
1346 1350 * @method collapse_all_output
1347 1351 */
1348 1352 Notebook.prototype.collapse_all_output = function () {
1349 1353 $.map(this.get_cells(), function (cell, i) {
1350 1354 if (cell instanceof codecell.CodeCell) {
1351 1355 cell.collapse_output();
1352 1356 }
1353 1357 });
1354 1358 // this should not be set if the `collapse` key is removed from nbformat
1355 1359 this.set_dirty(true);
1356 1360 };
1357 1361
1358 1362 /**
1359 1363 * Show a cell's output.
1360 1364 *
1361 1365 * @method expand_output
1362 1366 * @param {Number} index A cell's numeric index
1363 1367 */
1364 1368 Notebook.prototype.expand_output = function (index) {
1365 1369 var i = this.index_or_selected(index);
1366 1370 var cell = this.get_cell(i);
1367 1371 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1368 1372 cell.expand_output();
1369 1373 this.set_dirty(true);
1370 1374 }
1371 1375 };
1372 1376
1373 1377 /**
1374 1378 * Expand each code cell's output area, and remove scrollbars.
1375 1379 *
1376 1380 * @method expand_all_output
1377 1381 */
1378 1382 Notebook.prototype.expand_all_output = function () {
1379 1383 $.map(this.get_cells(), function (cell, i) {
1380 1384 if (cell instanceof codecell.CodeCell) {
1381 1385 cell.expand_output();
1382 1386 }
1383 1387 });
1384 1388 // this should not be set if the `collapse` key is removed from nbformat
1385 1389 this.set_dirty(true);
1386 1390 };
1387 1391
1388 1392 /**
1389 1393 * Clear the selected CodeCell's output area.
1390 1394 *
1391 1395 * @method clear_output
1392 1396 * @param {Number} index A cell's numeric index
1393 1397 */
1394 1398 Notebook.prototype.clear_output = function (index) {
1395 1399 var i = this.index_or_selected(index);
1396 1400 var cell = this.get_cell(i);
1397 1401 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1398 1402 cell.clear_output();
1399 1403 this.set_dirty(true);
1400 1404 }
1401 1405 };
1402 1406
1403 1407 /**
1404 1408 * Clear each code cell's output area.
1405 1409 *
1406 1410 * @method clear_all_output
1407 1411 */
1408 1412 Notebook.prototype.clear_all_output = function () {
1409 1413 $.map(this.get_cells(), function (cell, i) {
1410 1414 if (cell instanceof codecell.CodeCell) {
1411 1415 cell.clear_output();
1412 1416 }
1413 1417 });
1414 1418 this.set_dirty(true);
1415 1419 };
1416 1420
1417 1421 /**
1418 1422 * Scroll the selected CodeCell's output area.
1419 1423 *
1420 1424 * @method scroll_output
1421 1425 * @param {Number} index A cell's numeric index
1422 1426 */
1423 1427 Notebook.prototype.scroll_output = function (index) {
1424 1428 var i = this.index_or_selected(index);
1425 1429 var cell = this.get_cell(i);
1426 1430 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1427 1431 cell.scroll_output();
1428 1432 this.set_dirty(true);
1429 1433 }
1430 1434 };
1431 1435
1432 1436 /**
1433 1437 * Expand each code cell's output area, and add a scrollbar for long output.
1434 1438 *
1435 1439 * @method scroll_all_output
1436 1440 */
1437 1441 Notebook.prototype.scroll_all_output = function () {
1438 1442 $.map(this.get_cells(), function (cell, i) {
1439 1443 if (cell instanceof codecell.CodeCell) {
1440 1444 cell.scroll_output();
1441 1445 }
1442 1446 });
1443 1447 // this should not be set if the `collapse` key is removed from nbformat
1444 1448 this.set_dirty(true);
1445 1449 };
1446 1450
1447 1451 /** Toggle whether a cell's output is collapsed or expanded.
1448 1452 *
1449 1453 * @method toggle_output
1450 1454 * @param {Number} index A cell's numeric index
1451 1455 */
1452 1456 Notebook.prototype.toggle_output = function (index) {
1453 1457 var i = this.index_or_selected(index);
1454 1458 var cell = this.get_cell(i);
1455 1459 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1456 1460 cell.toggle_output();
1457 1461 this.set_dirty(true);
1458 1462 }
1459 1463 };
1460 1464
1461 1465 /**
1462 1466 * Hide/show the output of all cells.
1463 1467 *
1464 1468 * @method toggle_all_output
1465 1469 */
1466 1470 Notebook.prototype.toggle_all_output = function () {
1467 1471 $.map(this.get_cells(), function (cell, i) {
1468 1472 if (cell instanceof codecell.CodeCell) {
1469 1473 cell.toggle_output();
1470 1474 }
1471 1475 });
1472 1476 // this should not be set if the `collapse` key is removed from nbformat
1473 1477 this.set_dirty(true);
1474 1478 };
1475 1479
1476 1480 /**
1477 1481 * Toggle a scrollbar for long cell outputs.
1478 1482 *
1479 1483 * @method toggle_output_scroll
1480 1484 * @param {Number} index A cell's numeric index
1481 1485 */
1482 1486 Notebook.prototype.toggle_output_scroll = function (index) {
1483 1487 var i = this.index_or_selected(index);
1484 1488 var cell = this.get_cell(i);
1485 1489 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1486 1490 cell.toggle_output_scroll();
1487 1491 this.set_dirty(true);
1488 1492 }
1489 1493 };
1490 1494
1491 1495 /**
1492 1496 * Toggle the scrolling of long output on all cells.
1493 1497 *
1494 1498 * @method toggle_all_output_scrolling
1495 1499 */
1496 1500 Notebook.prototype.toggle_all_output_scroll = function () {
1497 1501 $.map(this.get_cells(), function (cell, i) {
1498 1502 if (cell instanceof codecell.CodeCell) {
1499 1503 cell.toggle_output_scroll();
1500 1504 }
1501 1505 });
1502 1506 // this should not be set if the `collapse` key is removed from nbformat
1503 1507 this.set_dirty(true);
1504 1508 };
1505 1509
1506 1510 // Other cell functions: line numbers, ...
1507 1511
1508 1512 /**
1509 1513 * Toggle line numbers in the selected cell's input area.
1510 1514 *
1511 1515 * @method cell_toggle_line_numbers
1512 1516 */
1513 1517 Notebook.prototype.cell_toggle_line_numbers = function() {
1514 1518 this.get_selected_cell().toggle_line_numbers();
1515 1519 };
1516 1520
1517 1521 /**
1518 1522 * Set the codemirror mode for all code cells, including the default for
1519 1523 * new code cells.
1520 1524 *
1521 1525 * @method set_codemirror_mode
1522 1526 */
1523 1527 Notebook.prototype.set_codemirror_mode = function(newmode){
1524 1528 if (newmode === this.codemirror_mode) {
1525 1529 return;
1526 1530 }
1527 1531 this.codemirror_mode = newmode;
1528 1532 codecell.CodeCell.options_default.cm_config.mode = newmode;
1529 modename = newmode.mode || newmode.name || newmode
1530
1533 modename = newmode.mode || newmode.name || newmode;
1534
1531 1535 that = this;
1532 1536 utils.requireCodeMirrorMode(modename, function () {
1533 1537 $.map(that.get_cells(), function(cell, i) {
1534 1538 if (cell.cell_type === 'code'){
1535 1539 cell.code_mirror.setOption('mode', newmode);
1536 1540 // This is currently redundant, because cm_config ends up as
1537 1541 // codemirror's own .options object, but I don't want to
1538 1542 // rely on that.
1539 1543 cell.cm_config.mode = newmode;
1540 1544 }
1541 1545 });
1542 })
1546 });
1543 1547 };
1544 1548
1545 1549 // Session related things
1546 1550
1547 1551 /**
1548 1552 * Start a new session and set it on each code cell.
1549 1553 *
1550 1554 * @method start_session
1551 1555 */
1552 1556 Notebook.prototype.start_session = function (kernel_name) {
1553 1557 var that = this;
1554 1558 if (this._session_starting) {
1555 1559 throw new session.SessionAlreadyStarting();
1556 1560 }
1557 1561 this._session_starting = true;
1558 1562
1559 1563 var options = {
1560 1564 base_url: this.base_url,
1561 1565 ws_url: this.ws_url,
1562 1566 notebook_path: this.notebook_path,
1563 1567 notebook_name: this.notebook_name,
1564 1568 kernel_name: kernel_name,
1565 1569 notebook: this
1566 1570 };
1567 1571
1568 1572 var success = $.proxy(this._session_started, this);
1569 1573 var failure = $.proxy(this._session_start_failed, this);
1570 1574
1571 1575 if (this.session !== null) {
1572 1576 this.session.restart(options, success, failure);
1573 1577 } else {
1574 1578 this.session = new session.Session(options);
1575 1579 this.session.start(success, failure);
1576 1580 }
1577 1581 };
1578 1582
1579 1583
1580 1584 /**
1581 1585 * Once a session is started, link the code cells to the kernel and pass the
1582 1586 * comm manager to the widget manager
1583 1587 *
1584 1588 */
1585 1589 Notebook.prototype._session_started = function (){
1586 1590 this._session_starting = false;
1587 1591 this.kernel = this.session.kernel;
1588 1592 var ncells = this.ncells();
1589 1593 for (var i=0; i<ncells; i++) {
1590 1594 var cell = this.get_cell(i);
1591 1595 if (cell instanceof codecell.CodeCell) {
1592 1596 cell.set_kernel(this.session.kernel);
1593 1597 }
1594 1598 }
1595 1599 };
1596 1600 Notebook.prototype._session_start_failed = function (jqxhr, status, error){
1597 1601 this._session_starting = false;
1598 1602 utils.log_ajax_error(jqxhr, status, error);
1599 1603 };
1600 1604
1601 1605 /**
1602 1606 * Prompt the user to restart the IPython kernel.
1603 1607 *
1604 1608 * @method restart_kernel
1605 1609 */
1606 1610 Notebook.prototype.restart_kernel = function () {
1607 1611 var that = this;
1608 1612 dialog.modal({
1609 1613 notebook: this,
1610 1614 keyboard_manager: this.keyboard_manager,
1611 1615 title : "Restart kernel or continue running?",
1612 1616 body : $("<p/>").text(
1613 1617 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1614 1618 ),
1615 1619 buttons : {
1616 1620 "Continue running" : {},
1617 1621 "Restart" : {
1618 1622 "class" : "btn-danger",
1619 1623 "click" : function() {
1620 1624 that.kernel.restart();
1621 1625 }
1622 1626 }
1623 1627 }
1624 1628 });
1625 1629 };
1626 1630
1627 1631 /**
1628 1632 * Execute or render cell outputs and go into command mode.
1629 1633 *
1630 1634 * @method execute_cell
1631 1635 */
1632 1636 Notebook.prototype.execute_cell = function () {
1633 1637 // mode = shift, ctrl, alt
1634 1638 var cell = this.get_selected_cell();
1635 1639 var cell_index = this.find_cell_index(cell);
1636 1640
1637 1641 cell.execute();
1638 1642 this.command_mode();
1639 1643 this.set_dirty(true);
1640 1644 };
1641 1645
1642 1646 /**
1643 1647 * Execute or render cell outputs and insert a new cell below.
1644 1648 *
1645 1649 * @method execute_cell_and_insert_below
1646 1650 */
1647 1651 Notebook.prototype.execute_cell_and_insert_below = function () {
1648 1652 var cell = this.get_selected_cell();
1649 1653 var cell_index = this.find_cell_index(cell);
1650 1654
1651 1655 cell.execute();
1652 1656
1653 1657 // If we are at the end always insert a new cell and return
1654 1658 if (cell_index === (this.ncells()-1)) {
1655 1659 this.command_mode();
1656 1660 this.insert_cell_below();
1657 1661 this.select(cell_index+1);
1658 1662 this.edit_mode();
1659 1663 this.scroll_to_bottom();
1660 1664 this.set_dirty(true);
1661 1665 return;
1662 1666 }
1663 1667
1664 1668 this.command_mode();
1665 1669 this.insert_cell_below();
1666 1670 this.select(cell_index+1);
1667 1671 this.edit_mode();
1668 1672 this.set_dirty(true);
1669 1673 };
1670 1674
1671 1675 /**
1672 1676 * Execute or render cell outputs and select the next cell.
1673 1677 *
1674 1678 * @method execute_cell_and_select_below
1675 1679 */
1676 1680 Notebook.prototype.execute_cell_and_select_below = function () {
1677 1681
1678 1682 var cell = this.get_selected_cell();
1679 1683 var cell_index = this.find_cell_index(cell);
1680 1684
1681 1685 cell.execute();
1682 1686
1683 1687 // If we are at the end always insert a new cell and return
1684 1688 if (cell_index === (this.ncells()-1)) {
1685 1689 this.command_mode();
1686 1690 this.insert_cell_below();
1687 1691 this.select(cell_index+1);
1688 1692 this.edit_mode();
1689 1693 this.scroll_to_bottom();
1690 1694 this.set_dirty(true);
1691 1695 return;
1692 1696 }
1693 1697
1694 1698 this.command_mode();
1695 1699 this.select(cell_index+1);
1696 1700 this.focus_cell();
1697 1701 this.set_dirty(true);
1698 1702 };
1699 1703
1700 1704 /**
1701 1705 * Execute all cells below the selected cell.
1702 1706 *
1703 1707 * @method execute_cells_below
1704 1708 */
1705 1709 Notebook.prototype.execute_cells_below = function () {
1706 1710 this.execute_cell_range(this.get_selected_index(), this.ncells());
1707 1711 this.scroll_to_bottom();
1708 1712 };
1709 1713
1710 1714 /**
1711 1715 * Execute all cells above the selected cell.
1712 1716 *
1713 1717 * @method execute_cells_above
1714 1718 */
1715 1719 Notebook.prototype.execute_cells_above = function () {
1716 1720 this.execute_cell_range(0, this.get_selected_index());
1717 1721 };
1718 1722
1719 1723 /**
1720 1724 * Execute all cells.
1721 1725 *
1722 1726 * @method execute_all_cells
1723 1727 */
1724 1728 Notebook.prototype.execute_all_cells = function () {
1725 1729 this.execute_cell_range(0, this.ncells());
1726 1730 this.scroll_to_bottom();
1727 1731 };
1728 1732
1729 1733 /**
1730 1734 * Execute a contiguous range of cells.
1731 1735 *
1732 1736 * @method execute_cell_range
1733 1737 * @param {Number} start Index of the first cell to execute (inclusive)
1734 1738 * @param {Number} end Index of the last cell to execute (exclusive)
1735 1739 */
1736 1740 Notebook.prototype.execute_cell_range = function (start, end) {
1737 1741 this.command_mode();
1738 1742 for (var i=start; i<end; i++) {
1739 1743 this.select(i);
1740 1744 this.execute_cell();
1741 1745 }
1742 1746 };
1743 1747
1744 1748 // Persistance and loading
1745 1749
1746 1750 /**
1747 1751 * Getter method for this notebook's name.
1748 1752 *
1749 1753 * @method get_notebook_name
1750 1754 * @return {String} This notebook's name (excluding file extension)
1751 1755 */
1752 1756 Notebook.prototype.get_notebook_name = function () {
1753 1757 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1754 1758 return nbname;
1755 1759 };
1756 1760
1757 1761 /**
1758 1762 * Setter method for this notebook's name.
1759 1763 *
1760 1764 * @method set_notebook_name
1761 1765 * @param {String} name A new name for this notebook
1762 1766 */
1763 1767 Notebook.prototype.set_notebook_name = function (name) {
1764 1768 this.notebook_name = name;
1765 1769 };
1766 1770
1767 1771 /**
1768 1772 * Check that a notebook's name is valid.
1769 1773 *
1770 1774 * @method test_notebook_name
1771 1775 * @param {String} nbname A name for this notebook
1772 1776 * @return {Boolean} True if the name is valid, false if invalid
1773 1777 */
1774 1778 Notebook.prototype.test_notebook_name = function (nbname) {
1775 1779 nbname = nbname || '';
1776 1780 if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) {
1777 1781 return true;
1778 1782 } else {
1779 1783 return false;
1780 1784 }
1781 1785 };
1782 1786
1783 1787 /**
1784 1788 * Load a notebook from JSON (.ipynb).
1785 1789 *
1786 1790 * @method fromJSON
1787 1791 * @param {Object} data JSON representation of a notebook
1788 1792 */
1789 1793 Notebook.prototype.fromJSON = function (data) {
1790 1794
1791 1795 var content = data.content;
1792 1796 var ncells = this.ncells();
1793 1797 var i;
1794 1798 for (i=0; i<ncells; i++) {
1795 1799 // Always delete cell 0 as they get renumbered as they are deleted.
1796 1800 this.delete_cell(0);
1797 1801 }
1798 1802 // Save the metadata and name.
1799 1803 this.metadata = content.metadata;
1800 1804 this.notebook_name = data.name;
1801 1805 var trusted = true;
1802 1806
1803 1807 // Trigger an event changing the kernel spec - this will set the default
1804 1808 // codemirror mode
1805 1809 if (this.metadata.kernelspec !== undefined) {
1806 1810 this.events.trigger('spec_changed.Kernel', this.metadata.kernelspec);
1807 1811 }
1808 1812
1809 1813 // Set the codemirror mode from language_info metadata
1810 1814 if (this.metadata.language_info !== undefined) {
1811 1815 var langinfo = this.metadata.language_info;
1812 1816 // Mode 'null' should be plain, unhighlighted text.
1813 1817 var cm_mode = langinfo.codemirror_mode || langinfo.language || 'null'
1814 1818 this.set_codemirror_mode(cm_mode);
1815 1819 }
1816 1820
1817 1821 var new_cells = content.cells;
1818 1822 ncells = new_cells.length;
1819 1823 var cell_data = null;
1820 1824 var new_cell = null;
1821 1825 for (i=0; i<ncells; i++) {
1822 1826 cell_data = new_cells[i];
1823 1827 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1824 1828 new_cell.fromJSON(cell_data);
1825 1829 if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) {
1826 1830 trusted = false;
1827 1831 }
1828 1832 }
1829 1833 if (trusted !== this.trusted) {
1830 1834 this.trusted = trusted;
1831 1835 this.events.trigger("trust_changed.Notebook", trusted);
1832 1836 }
1833 1837 };
1834 1838
1835 1839 /**
1836 1840 * Dump this notebook into a JSON-friendly object.
1837 1841 *
1838 1842 * @method toJSON
1839 1843 * @return {Object} A JSON-friendly representation of this notebook.
1840 1844 */
1841 1845 Notebook.prototype.toJSON = function () {
1842 1846 // remove the conversion indicator, which only belongs in-memory
1843 1847 delete this.metadata.orig_nbformat;
1844 1848 delete this.metadata.orig_nbformat_minor;
1845 1849
1846 1850 var cells = this.get_cells();
1847 1851 var ncells = cells.length;
1848 1852 var cell_array = new Array(ncells);
1849 1853 var trusted = true;
1850 1854 for (var i=0; i<ncells; i++) {
1851 1855 var cell = cells[i];
1852 1856 if (cell.cell_type == 'code' && !cell.output_area.trusted) {
1853 1857 trusted = false;
1854 1858 }
1855 1859 cell_array[i] = cell.toJSON();
1856 1860 }
1857 1861 var data = {
1858 1862 cells: cell_array,
1859 1863 metadata : this.metadata
1860 1864 };
1861 1865 if (trusted != this.trusted) {
1862 1866 this.trusted = trusted;
1863 1867 this.events.trigger("trust_changed.Notebook", trusted);
1864 1868 }
1865 1869 return data;
1866 1870 };
1867 1871
1868 1872 /**
1869 1873 * Start an autosave timer, for periodically saving the notebook.
1870 1874 *
1871 1875 * @method set_autosave_interval
1872 1876 * @param {Integer} interval the autosave interval in milliseconds
1873 1877 */
1874 1878 Notebook.prototype.set_autosave_interval = function (interval) {
1875 1879 var that = this;
1876 1880 // clear previous interval, so we don't get simultaneous timers
1877 1881 if (this.autosave_timer) {
1878 1882 clearInterval(this.autosave_timer);
1879 1883 }
1880 1884
1881 1885 this.autosave_interval = this.minimum_autosave_interval = interval;
1882 1886 if (interval) {
1883 1887 this.autosave_timer = setInterval(function() {
1884 1888 if (that.dirty) {
1885 1889 that.save_notebook();
1886 1890 }
1887 1891 }, interval);
1888 1892 this.events.trigger("autosave_enabled.Notebook", interval);
1889 1893 } else {
1890 1894 this.autosave_timer = null;
1891 1895 this.events.trigger("autosave_disabled.Notebook");
1892 1896 }
1893 1897 };
1894 1898
1895 1899 /**
1896 1900 * Save this notebook on the server. This becomes a notebook instance's
1897 1901 * .save_notebook method *after* the entire notebook has been loaded.
1898 1902 *
1899 1903 * @method save_notebook
1900 1904 */
1901 1905 Notebook.prototype.save_notebook = function (extra_settings) {
1902 1906 // Create a JSON model to be sent to the server.
1903 1907 var model = {};
1904 1908 model.name = this.notebook_name;
1905 1909 model.path = this.notebook_path;
1906 1910 model.type = 'notebook';
1907 1911 model.format = 'json';
1908 1912 model.content = this.toJSON();
1909 1913 model.content.nbformat = this.nbformat;
1910 1914 model.content.nbformat_minor = this.nbformat_minor;
1911 1915 // time the ajax call for autosave tuning purposes.
1912 1916 var start = new Date().getTime();
1913 1917 // We do the call with settings so we can set cache to false.
1914 1918 var settings = {
1915 1919 processData : false,
1916 1920 cache : false,
1917 1921 type : "PUT",
1918 1922 data : JSON.stringify(model),
1919 1923 contentType: 'application/json',
1920 1924 dataType : "json",
1921 1925 success : $.proxy(this.save_notebook_success, this, start),
1922 1926 error : $.proxy(this.save_notebook_error, this)
1923 1927 };
1924 1928 if (extra_settings) {
1925 1929 for (var key in extra_settings) {
1926 1930 settings[key] = extra_settings[key];
1927 1931 }
1928 1932 }
1929 1933 this.events.trigger('notebook_saving.Notebook');
1930 1934 var url = utils.url_join_encode(
1931 1935 this.base_url,
1932 1936 'api/contents',
1933 1937 this.notebook_path,
1934 1938 this.notebook_name
1935 1939 );
1936 1940 $.ajax(url, settings);
1937 1941 };
1938 1942
1939 1943 /**
1940 1944 * Success callback for saving a notebook.
1941 1945 *
1942 1946 * @method save_notebook_success
1943 1947 * @param {Integer} start the time when the save request started
1944 1948 * @param {Object} data JSON representation of a notebook
1945 1949 * @param {String} status Description of response status
1946 1950 * @param {jqXHR} xhr jQuery Ajax object
1947 1951 */
1948 1952 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1949 1953 this.set_dirty(false);
1950 1954 if (data.message) {
1951 1955 // save succeeded, but validation failed.
1952 1956 var body = $("<div>");
1953 1957 var title = "Notebook validation failed";
1954 1958
1955 1959 body.append($("<p>").text(
1956 1960 "The save operation succeeded," +
1957 1961 " but the notebook does not appear to be valid." +
1958 1962 " The validation error was:"
1959 1963 )).append($("<div>").addClass("validation-error").append(
1960 1964 $("<pre>").text(data.message)
1961 1965 ));
1962 1966 dialog.modal({
1963 1967 notebook: this,
1964 1968 keyboard_manager: this.keyboard_manager,
1965 1969 title: title,
1966 1970 body: body,
1967 1971 buttons : {
1968 1972 OK : {
1969 1973 "class" : "btn-primary"
1970 1974 }
1971 1975 }
1972 1976 });
1973 1977 }
1974 1978 this.events.trigger('notebook_saved.Notebook');
1975 1979 this._update_autosave_interval(start);
1976 1980 if (this._checkpoint_after_save) {
1977 1981 this.create_checkpoint();
1978 1982 this._checkpoint_after_save = false;
1979 1983 }
1980 1984 };
1981 1985
1982 1986 /**
1983 1987 * update the autosave interval based on how long the last save took
1984 1988 *
1985 1989 * @method _update_autosave_interval
1986 1990 * @param {Integer} timestamp when the save request started
1987 1991 */
1988 1992 Notebook.prototype._update_autosave_interval = function (start) {
1989 1993 var duration = (new Date().getTime() - start);
1990 1994 if (this.autosave_interval) {
1991 1995 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1992 1996 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1993 1997 // round to 10 seconds, otherwise we will be setting a new interval too often
1994 1998 interval = 10000 * Math.round(interval / 10000);
1995 1999 // set new interval, if it's changed
1996 2000 if (interval != this.autosave_interval) {
1997 2001 this.set_autosave_interval(interval);
1998 2002 }
1999 2003 }
2000 2004 };
2001 2005
2002 2006 /**
2003 2007 * Failure callback for saving a notebook.
2004 2008 *
2005 2009 * @method save_notebook_error
2006 2010 * @param {jqXHR} xhr jQuery Ajax object
2007 2011 * @param {String} status Description of response status
2008 2012 * @param {String} error HTTP error message
2009 2013 */
2010 2014 Notebook.prototype.save_notebook_error = function (xhr, status, error) {
2011 2015 this.events.trigger('notebook_save_failed.Notebook', [xhr, status, error]);
2012 2016 };
2013 2017
2014 2018 /**
2015 2019 * Explicitly trust the output of this notebook.
2016 2020 *
2017 2021 * @method trust_notebook
2018 2022 */
2019 2023 Notebook.prototype.trust_notebook = function (extra_settings) {
2020 2024 var body = $("<div>").append($("<p>")
2021 2025 .text("A trusted IPython notebook may execute hidden malicious code ")
2022 2026 .append($("<strong>")
2023 2027 .append(
2024 2028 $("<em>").text("when you open it")
2025 2029 )
2026 2030 ).append(".").append(
2027 2031 " Selecting trust will immediately reload this notebook in a trusted state."
2028 2032 ).append(
2029 2033 " For more information, see the "
2030 2034 ).append($("<a>").attr("href", "http://ipython.org/ipython-doc/2/notebook/security.html")
2031 2035 .text("IPython security documentation")
2032 2036 ).append(".")
2033 2037 );
2034 2038
2035 2039 var nb = this;
2036 2040 dialog.modal({
2037 2041 notebook: this,
2038 2042 keyboard_manager: this.keyboard_manager,
2039 2043 title: "Trust this notebook?",
2040 2044 body: body,
2041 2045
2042 2046 buttons: {
2043 2047 Cancel : {},
2044 2048 Trust : {
2045 2049 class : "btn-danger",
2046 2050 click : function () {
2047 2051 var cells = nb.get_cells();
2048 2052 for (var i = 0; i < cells.length; i++) {
2049 2053 var cell = cells[i];
2050 2054 if (cell.cell_type == 'code') {
2051 2055 cell.output_area.trusted = true;
2052 2056 }
2053 2057 }
2054 2058 nb.events.on('notebook_saved.Notebook', function () {
2055 2059 window.location.reload();
2056 2060 });
2057 2061 nb.save_notebook();
2058 2062 }
2059 2063 }
2060 2064 }
2061 2065 });
2062 2066 };
2063 2067
2064 2068 Notebook.prototype.new_notebook = function(){
2065 2069 var path = this.notebook_path;
2066 2070 var base_url = this.base_url;
2067 2071 var settings = {
2068 2072 processData : false,
2069 2073 cache : false,
2070 2074 type : "POST",
2071 2075 dataType : "json",
2072 2076 async : false,
2073 2077 success : function (data, status, xhr){
2074 2078 var notebook_name = data.name;
2075 2079 window.open(
2076 2080 utils.url_join_encode(
2077 2081 base_url,
2078 2082 'notebooks',
2079 2083 path,
2080 2084 notebook_name
2081 2085 ),
2082 2086 '_blank'
2083 2087 );
2084 2088 },
2085 2089 error : utils.log_ajax_error,
2086 2090 };
2087 2091 var url = utils.url_join_encode(
2088 2092 base_url,
2089 2093 'api/contents',
2090 2094 path
2091 2095 );
2092 2096 $.ajax(url,settings);
2093 2097 };
2094 2098
2095 2099
2096 2100 Notebook.prototype.copy_notebook = function(){
2097 2101 var path = this.notebook_path;
2098 2102 var base_url = this.base_url;
2099 2103 var settings = {
2100 2104 processData : false,
2101 2105 cache : false,
2102 2106 type : "POST",
2103 2107 dataType : "json",
2104 2108 data : JSON.stringify({copy_from : this.notebook_name}),
2105 2109 async : false,
2106 2110 success : function (data, status, xhr) {
2107 2111 window.open(utils.url_join_encode(
2108 2112 base_url,
2109 2113 'notebooks',
2110 2114 data.path,
2111 2115 data.name
2112 2116 ), '_blank');
2113 2117 },
2114 2118 error : utils.log_ajax_error,
2115 2119 };
2116 2120 var url = utils.url_join_encode(
2117 2121 base_url,
2118 2122 'api/contents',
2119 2123 path
2120 2124 );
2121 2125 $.ajax(url,settings);
2122 2126 };
2123 2127
2124 2128 Notebook.prototype.rename = function (nbname) {
2125 2129 var that = this;
2126 2130 if (!nbname.match(/\.ipynb$/)) {
2127 2131 nbname = nbname + ".ipynb";
2128 2132 }
2129 2133 var data = {name: nbname};
2130 2134 var settings = {
2131 2135 processData : false,
2132 2136 cache : false,
2133 2137 type : "PATCH",
2134 2138 data : JSON.stringify(data),
2135 2139 dataType: "json",
2136 2140 contentType: 'application/json',
2137 2141 success : $.proxy(that.rename_success, this),
2138 2142 error : $.proxy(that.rename_error, this)
2139 2143 };
2140 2144 this.events.trigger('rename_notebook.Notebook', data);
2141 2145 var url = utils.url_join_encode(
2142 2146 this.base_url,
2143 2147 'api/contents',
2144 2148 this.notebook_path,
2145 2149 this.notebook_name
2146 2150 );
2147 2151 $.ajax(url, settings);
2148 2152 };
2149 2153
2150 2154 Notebook.prototype.delete = function () {
2151 2155 var that = this;
2152 2156 var settings = {
2153 2157 processData : false,
2154 2158 cache : false,
2155 2159 type : "DELETE",
2156 2160 dataType: "json",
2157 2161 error : utils.log_ajax_error,
2158 2162 };
2159 2163 var url = utils.url_join_encode(
2160 2164 this.base_url,
2161 2165 'api/contents',
2162 2166 this.notebook_path,
2163 2167 this.notebook_name
2164 2168 );
2165 2169 $.ajax(url, settings);
2166 2170 };
2167 2171
2168 2172
2169 2173 Notebook.prototype.rename_success = function (json, status, xhr) {
2170 2174 var name = this.notebook_name = json.name;
2171 2175 var path = json.path;
2172 2176 this.session.rename_notebook(name, path);
2173 2177 this.events.trigger('notebook_renamed.Notebook', json);
2174 2178 };
2175 2179
2176 2180 Notebook.prototype.rename_error = function (xhr, status, error) {
2177 2181 var that = this;
2178 2182 var dialog_body = $('<div/>').append(
2179 2183 $("<p/>").text('This notebook name already exists.')
2180 2184 );
2181 2185 this.events.trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
2182 2186 dialog.modal({
2183 2187 notebook: this,
2184 2188 keyboard_manager: this.keyboard_manager,
2185 2189 title: "Notebook Rename Error!",
2186 2190 body: dialog_body,
2187 2191 buttons : {
2188 2192 "Cancel": {},
2189 2193 "OK": {
2190 2194 class: "btn-primary",
2191 2195 click: function () {
2192 2196 this.save_widget.rename_notebook({notebook:that});
2193 2197 }}
2194 2198 },
2195 2199 open : function (event, ui) {
2196 2200 var that = $(this);
2197 2201 // Upon ENTER, click the OK button.
2198 2202 that.find('input[type="text"]').keydown(function (event, ui) {
2199 2203 if (event.which === this.keyboard.keycodes.enter) {
2200 2204 that.find('.btn-primary').first().click();
2201 2205 }
2202 2206 });
2203 2207 that.find('input[type="text"]').focus();
2204 2208 }
2205 2209 });
2206 2210 };
2207 2211
2208 2212 /**
2209 2213 * Request a notebook's data from the server.
2210 2214 *
2211 2215 * @method load_notebook
2212 2216 * @param {String} notebook_name and path A notebook to load
2213 2217 */
2214 2218 Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
2215 2219 var that = this;
2216 2220 this.notebook_name = notebook_name;
2217 2221 this.notebook_path = notebook_path;
2218 2222 // We do the call with settings so we can set cache to false.
2219 2223 var settings = {
2220 2224 processData : false,
2221 2225 cache : false,
2222 2226 type : "GET",
2223 2227 dataType : "json",
2224 2228 success : $.proxy(this.load_notebook_success,this),
2225 2229 error : $.proxy(this.load_notebook_error,this),
2226 2230 };
2227 2231 this.events.trigger('notebook_loading.Notebook');
2228 2232 var url = utils.url_join_encode(
2229 2233 this.base_url,
2230 2234 'api/contents',
2231 2235 this.notebook_path,
2232 2236 this.notebook_name
2233 2237 );
2234 2238 $.ajax(url, settings);
2235 2239 };
2236 2240
2237 2241 /**
2238 2242 * Success callback for loading a notebook from the server.
2239 2243 *
2240 2244 * Load notebook data from the JSON response.
2241 2245 *
2242 2246 * @method load_notebook_success
2243 2247 * @param {Object} data JSON representation of a notebook
2244 2248 * @param {String} status Description of response status
2245 2249 * @param {jqXHR} xhr jQuery Ajax object
2246 2250 */
2247 2251 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
2248 2252 var failed;
2249 2253 try {
2250 2254 this.fromJSON(data);
2251 2255 } catch (e) {
2252 2256 failed = e;
2253 2257 console.log("Notebook failed to load from JSON:", e);
2254 2258 }
2255 2259 if (failed || data.message) {
2256 2260 // *either* fromJSON failed or validation failed
2257 2261 var body = $("<div>");
2258 2262 var title;
2259 2263 if (failed) {
2260 2264 title = "Notebook failed to load";
2261 2265 body.append($("<p>").text(
2262 2266 "The error was: "
2263 2267 )).append($("<div>").addClass("js-error").text(
2264 2268 failed.toString()
2265 2269 )).append($("<p>").text(
2266 2270 "See the error console for details."
2267 2271 ));
2268 2272 } else {
2269 2273 title = "Notebook validation failed";
2270 2274 }
2271 2275
2272 2276 if (data.message) {
2273 2277 var msg;
2274 2278 if (failed) {
2275 2279 msg = "The notebook also failed validation:"
2276 2280 } else {
2277 2281 msg = "An invalid notebook may not function properly." +
2278 2282 " The validation error was:"
2279 2283 }
2280 2284 body.append($("<p>").text(
2281 2285 msg
2282 2286 )).append($("<div>").addClass("validation-error").append(
2283 2287 $("<pre>").text(data.message)
2284 2288 ));
2285 2289 }
2286 2290
2287 2291 dialog.modal({
2288 2292 notebook: this,
2289 2293 keyboard_manager: this.keyboard_manager,
2290 2294 title: title,
2291 2295 body: body,
2292 2296 buttons : {
2293 2297 OK : {
2294 2298 "class" : "btn-primary"
2295 2299 }
2296 2300 }
2297 2301 });
2298 2302 }
2299 2303 if (this.ncells() === 0) {
2300 2304 this.insert_cell_below('code');
2301 2305 this.edit_mode(0);
2302 2306 } else {
2303 2307 this.select(0);
2304 2308 this.handle_command_mode(this.get_cell(0));
2305 2309 }
2306 2310 this.set_dirty(false);
2307 2311 this.scroll_to_top();
2308 2312 var nbmodel = data.content;
2309 2313 var orig_nbformat = nbmodel.metadata.orig_nbformat;
2310 2314 var orig_nbformat_minor = nbmodel.metadata.orig_nbformat_minor;
2311 2315 if (orig_nbformat !== undefined && nbmodel.nbformat !== orig_nbformat) {
2312 2316 var msg = "This notebook has been converted from an older " +
2313 2317 "notebook format (v"+orig_nbformat+") to the current notebook " +
2314 2318 "format (v"+nbmodel.nbformat+"). The next time you save this notebook, the " +
2315 2319 "newer notebook format will be used and older versions of IPython " +
2316 2320 "may not be able to read it. To keep the older version, close the " +
2317 2321 "notebook without saving it.";
2318 2322 dialog.modal({
2319 2323 notebook: this,
2320 2324 keyboard_manager: this.keyboard_manager,
2321 2325 title : "Notebook converted",
2322 2326 body : msg,
2323 2327 buttons : {
2324 2328 OK : {
2325 2329 class : "btn-primary"
2326 2330 }
2327 2331 }
2328 2332 });
2329 2333 } else if (orig_nbformat_minor !== undefined && nbmodel.nbformat_minor !== orig_nbformat_minor) {
2330 2334 var that = this;
2331 2335 var orig_vs = 'v' + nbmodel.nbformat + '.' + orig_nbformat_minor;
2332 2336 var this_vs = 'v' + nbmodel.nbformat + '.' + this.nbformat_minor;
2333 2337 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
2334 2338 this_vs + ". You can still work with this notebook, but some features " +
2335 2339 "introduced in later notebook versions may not be available.";
2336 2340
2337 2341 dialog.modal({
2338 2342 notebook: this,
2339 2343 keyboard_manager: this.keyboard_manager,
2340 2344 title : "Newer Notebook",
2341 2345 body : msg,
2342 2346 buttons : {
2343 2347 OK : {
2344 2348 class : "btn-danger"
2345 2349 }
2346 2350 }
2347 2351 });
2348 2352
2349 2353 }
2350 2354
2351 2355 // Create the session after the notebook is completely loaded to prevent
2352 2356 // code execution upon loading, which is a security risk.
2353 2357 if (this.session === null) {
2354 2358 var kernelspec = this.metadata.kernelspec || {};
2355 2359 var kernel_name = kernelspec.name;
2356 2360
2357 2361 this.start_session(kernel_name);
2358 2362 }
2359 2363 // load our checkpoint list
2360 2364 this.list_checkpoints();
2361 2365
2362 2366 // load toolbar state
2363 2367 if (this.metadata.celltoolbar) {
2364 2368 celltoolbar.CellToolbar.global_show();
2365 2369 celltoolbar.CellToolbar.activate_preset(this.metadata.celltoolbar);
2366 2370 } else {
2367 2371 celltoolbar.CellToolbar.global_hide();
2368 2372 }
2369 2373
2370 2374 // now that we're fully loaded, it is safe to restore save functionality
2371 2375 delete(this.save_notebook);
2372 2376 this.events.trigger('notebook_loaded.Notebook');
2373 2377 };
2374 2378
2375 2379 /**
2376 2380 * Failure callback for loading a notebook from the server.
2377 2381 *
2378 2382 * @method load_notebook_error
2379 2383 * @param {jqXHR} xhr jQuery Ajax object
2380 2384 * @param {String} status Description of response status
2381 2385 * @param {String} error HTTP error message
2382 2386 */
2383 2387 Notebook.prototype.load_notebook_error = function (xhr, status, error) {
2384 2388 this.events.trigger('notebook_load_failed.Notebook', [xhr, status, error]);
2385 2389 utils.log_ajax_error(xhr, status, error);
2386 var msg;
2390 var msg = $("<div>");
2387 2391 if (xhr.status === 400) {
2388 msg = escape(utils.ajax_error_msg(xhr));
2392 msg.text(utils.ajax_error_msg(xhr));
2389 2393 } else if (xhr.status === 500) {
2390 msg = "An unknown error occurred while loading this notebook. " +
2394 msg.text("An unknown error occurred while loading this notebook. " +
2391 2395 "This version can load notebook formats " +
2392 "v" + this.nbformat + " or earlier. See the server log for details.";
2396 "v" + this.nbformat + " or earlier. See the server log for details.");
2393 2397 }
2394 2398 dialog.modal({
2395 2399 notebook: this,
2396 2400 keyboard_manager: this.keyboard_manager,
2397 2401 title: "Error loading notebook",
2398 2402 body : msg,
2399 2403 buttons : {
2400 2404 "OK": {}
2401 2405 }
2402 2406 });
2403 2407 };
2404 2408
2405 2409 /********************* checkpoint-related *********************/
2406 2410
2407 2411 /**
2408 2412 * Save the notebook then immediately create a checkpoint.
2409 2413 *
2410 2414 * @method save_checkpoint
2411 2415 */
2412 2416 Notebook.prototype.save_checkpoint = function () {
2413 2417 this._checkpoint_after_save = true;
2414 2418 this.save_notebook();
2415 2419 };
2416 2420
2417 2421 /**
2418 2422 * Add a checkpoint for this notebook.
2419 2423 * for use as a callback from checkpoint creation.
2420 2424 *
2421 2425 * @method add_checkpoint
2422 2426 */
2423 2427 Notebook.prototype.add_checkpoint = function (checkpoint) {
2424 2428 var found = false;
2425 2429 for (var i = 0; i < this.checkpoints.length; i++) {
2426 2430 var existing = this.checkpoints[i];
2427 2431 if (existing.id == checkpoint.id) {
2428 2432 found = true;
2429 2433 this.checkpoints[i] = checkpoint;
2430 2434 break;
2431 2435 }
2432 2436 }
2433 2437 if (!found) {
2434 2438 this.checkpoints.push(checkpoint);
2435 2439 }
2436 2440 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2437 2441 };
2438 2442
2439 2443 /**
2440 2444 * List checkpoints for this notebook.
2441 2445 *
2442 2446 * @method list_checkpoints
2443 2447 */
2444 2448 Notebook.prototype.list_checkpoints = function () {
2445 2449 var url = utils.url_join_encode(
2446 2450 this.base_url,
2447 2451 'api/contents',
2448 2452 this.notebook_path,
2449 2453 this.notebook_name,
2450 2454 'checkpoints'
2451 2455 );
2452 2456 $.get(url).done(
2453 2457 $.proxy(this.list_checkpoints_success, this)
2454 2458 ).fail(
2455 2459 $.proxy(this.list_checkpoints_error, this)
2456 2460 );
2457 2461 };
2458 2462
2459 2463 /**
2460 2464 * Success callback for listing checkpoints.
2461 2465 *
2462 2466 * @method list_checkpoint_success
2463 2467 * @param {Object} data JSON representation of a checkpoint
2464 2468 * @param {String} status Description of response status
2465 2469 * @param {jqXHR} xhr jQuery Ajax object
2466 2470 */
2467 2471 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
2468 2472 data = $.parseJSON(data);
2469 2473 this.checkpoints = data;
2470 2474 if (data.length) {
2471 2475 this.last_checkpoint = data[data.length - 1];
2472 2476 } else {
2473 2477 this.last_checkpoint = null;
2474 2478 }
2475 2479 this.events.trigger('checkpoints_listed.Notebook', [data]);
2476 2480 };
2477 2481
2478 2482 /**
2479 2483 * Failure callback for listing a checkpoint.
2480 2484 *
2481 2485 * @method list_checkpoint_error
2482 2486 * @param {jqXHR} xhr jQuery Ajax object
2483 2487 * @param {String} status Description of response status
2484 2488 * @param {String} error_msg HTTP error message
2485 2489 */
2486 2490 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
2487 2491 this.events.trigger('list_checkpoints_failed.Notebook');
2488 2492 };
2489 2493
2490 2494 /**
2491 2495 * Create a checkpoint of this notebook on the server from the most recent save.
2492 2496 *
2493 2497 * @method create_checkpoint
2494 2498 */
2495 2499 Notebook.prototype.create_checkpoint = function () {
2496 2500 var url = utils.url_join_encode(
2497 2501 this.base_url,
2498 2502 'api/contents',
2499 2503 this.notebook_path,
2500 2504 this.notebook_name,
2501 2505 'checkpoints'
2502 2506 );
2503 2507 $.post(url).done(
2504 2508 $.proxy(this.create_checkpoint_success, this)
2505 2509 ).fail(
2506 2510 $.proxy(this.create_checkpoint_error, this)
2507 2511 );
2508 2512 };
2509 2513
2510 2514 /**
2511 2515 * Success callback for creating a checkpoint.
2512 2516 *
2513 2517 * @method create_checkpoint_success
2514 2518 * @param {Object} data JSON representation of a checkpoint
2515 2519 * @param {String} status Description of response status
2516 2520 * @param {jqXHR} xhr jQuery Ajax object
2517 2521 */
2518 2522 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
2519 2523 data = $.parseJSON(data);
2520 2524 this.add_checkpoint(data);
2521 2525 this.events.trigger('checkpoint_created.Notebook', data);
2522 2526 };
2523 2527
2524 2528 /**
2525 2529 * Failure callback for creating a checkpoint.
2526 2530 *
2527 2531 * @method create_checkpoint_error
2528 2532 * @param {jqXHR} xhr jQuery Ajax object
2529 2533 * @param {String} status Description of response status
2530 2534 * @param {String} error_msg HTTP error message
2531 2535 */
2532 2536 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
2533 2537 this.events.trigger('checkpoint_failed.Notebook');
2534 2538 };
2535 2539
2536 2540 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2537 2541 var that = this;
2538 2542 checkpoint = checkpoint || this.last_checkpoint;
2539 2543 if ( ! checkpoint ) {
2540 2544 console.log("restore dialog, but no checkpoint to restore to!");
2541 2545 return;
2542 2546 }
2543 2547 var body = $('<div/>').append(
2544 2548 $('<p/>').addClass("p-space").text(
2545 2549 "Are you sure you want to revert the notebook to " +
2546 2550 "the latest checkpoint?"
2547 2551 ).append(
2548 2552 $("<strong/>").text(
2549 2553 " This cannot be undone."
2550 2554 )
2551 2555 )
2552 2556 ).append(
2553 2557 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2554 2558 ).append(
2555 2559 $('<p/>').addClass("p-space").text(
2556 2560 Date(checkpoint.last_modified)
2557 2561 ).css("text-align", "center")
2558 2562 );
2559 2563
2560 2564 dialog.modal({
2561 2565 notebook: this,
2562 2566 keyboard_manager: this.keyboard_manager,
2563 2567 title : "Revert notebook to checkpoint",
2564 2568 body : body,
2565 2569 buttons : {
2566 2570 Revert : {
2567 2571 class : "btn-danger",
2568 2572 click : function () {
2569 2573 that.restore_checkpoint(checkpoint.id);
2570 2574 }
2571 2575 },
2572 2576 Cancel : {}
2573 2577 }
2574 2578 });
2575 2579 };
2576 2580
2577 2581 /**
2578 2582 * Restore the notebook to a checkpoint state.
2579 2583 *
2580 2584 * @method restore_checkpoint
2581 2585 * @param {String} checkpoint ID
2582 2586 */
2583 2587 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2584 2588 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2585 2589 var url = utils.url_join_encode(
2586 2590 this.base_url,
2587 2591 'api/contents',
2588 2592 this.notebook_path,
2589 2593 this.notebook_name,
2590 2594 'checkpoints',
2591 2595 checkpoint
2592 2596 );
2593 2597 $.post(url).done(
2594 2598 $.proxy(this.restore_checkpoint_success, this)
2595 2599 ).fail(
2596 2600 $.proxy(this.restore_checkpoint_error, this)
2597 2601 );
2598 2602 };
2599 2603
2600 2604 /**
2601 2605 * Success callback for restoring a notebook to a checkpoint.
2602 2606 *
2603 2607 * @method restore_checkpoint_success
2604 2608 * @param {Object} data (ignored, should be empty)
2605 2609 * @param {String} status Description of response status
2606 2610 * @param {jqXHR} xhr jQuery Ajax object
2607 2611 */
2608 2612 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
2609 2613 this.events.trigger('checkpoint_restored.Notebook');
2610 2614 this.load_notebook(this.notebook_name, this.notebook_path);
2611 2615 };
2612 2616
2613 2617 /**
2614 2618 * Failure callback for restoring a notebook to a checkpoint.
2615 2619 *
2616 2620 * @method restore_checkpoint_error
2617 2621 * @param {jqXHR} xhr jQuery Ajax object
2618 2622 * @param {String} status Description of response status
2619 2623 * @param {String} error_msg HTTP error message
2620 2624 */
2621 2625 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
2622 2626 this.events.trigger('checkpoint_restore_failed.Notebook');
2623 2627 };
2624 2628
2625 2629 /**
2626 2630 * Delete a notebook checkpoint.
2627 2631 *
2628 2632 * @method delete_checkpoint
2629 2633 * @param {String} checkpoint ID
2630 2634 */
2631 2635 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2632 2636 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2633 2637 var url = utils.url_join_encode(
2634 2638 this.base_url,
2635 2639 'api/contents',
2636 2640 this.notebook_path,
2637 2641 this.notebook_name,
2638 2642 'checkpoints',
2639 2643 checkpoint
2640 2644 );
2641 2645 $.ajax(url, {
2642 2646 type: 'DELETE',
2643 2647 success: $.proxy(this.delete_checkpoint_success, this),
2644 2648 error: $.proxy(this.delete_checkpoint_error, this)
2645 2649 });
2646 2650 };
2647 2651
2648 2652 /**
2649 2653 * Success callback for deleting a notebook checkpoint
2650 2654 *
2651 2655 * @method delete_checkpoint_success
2652 2656 * @param {Object} data (ignored, should be empty)
2653 2657 * @param {String} status Description of response status
2654 2658 * @param {jqXHR} xhr jQuery Ajax object
2655 2659 */
2656 2660 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2657 2661 this.events.trigger('checkpoint_deleted.Notebook', data);
2658 2662 this.load_notebook(this.notebook_name, this.notebook_path);
2659 2663 };
2660 2664
2661 2665 /**
2662 2666 * Failure callback for deleting a notebook checkpoint.
2663 2667 *
2664 2668 * @method delete_checkpoint_error
2665 2669 * @param {jqXHR} xhr jQuery Ajax object
2666 2670 * @param {String} status Description of response status
2667 2671 * @param {String} error HTTP error message
2668 2672 */
2669 2673 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error) {
2670 2674 this.events.trigger('checkpoint_delete_failed.Notebook', [xhr, status, error]);
2671 2675 };
2672 2676
2673 2677
2674 2678 // For backwards compatability.
2675 2679 IPython.Notebook = Notebook;
2676 2680
2677 2681 return {'Notebook': Notebook};
2678 2682 });
@@ -1,425 +1,352 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'base/js/utils',
7 7 'jquery',
8 8 'notebook/js/cell',
9 9 'base/js/security',
10 10 'notebook/js/mathjaxutils',
11 11 'notebook/js/celltoolbar',
12 12 'components/marked/lib/marked',
13 13 'codemirror/lib/codemirror',
14 14 'codemirror/mode/gfm/gfm',
15 15 'notebook/js/codemirror-ipythongfm'
16 16 ], function(IPython,utils , $, cell, security, mathjaxutils, celltoolbar, marked, CodeMirror, gfm, ipgfm) {
17 17 "use strict";
18 18 var Cell = cell.Cell;
19 19
20 20 var TextCell = function (options) {
21 21 // Constructor
22 22 //
23 23 // Construct a new TextCell, codemirror mode is by default 'htmlmixed',
24 24 // and cell type is 'text' cell start as not redered.
25 25 //
26 26 // Parameters:
27 27 // options: dictionary
28 28 // Dictionary of keyword arguments.
29 29 // events: $(Events) instance
30 30 // config: dictionary
31 31 // keyboard_manager: KeyboardManager instance
32 32 // notebook: Notebook instance
33 33 options = options || {};
34 34
35 35 // in all TextCell/Cell subclasses
36 36 // do not assign most of members here, just pass it down
37 37 // in the options dict potentially overwriting what you wish.
38 38 // they will be assigned in the base class.
39 39 this.notebook = options.notebook;
40 40 this.events = options.events;
41 41 this.config = options.config;
42 42
43 43 // we cannot put this as a class key as it has handle to "this".
44 44 var cm_overwrite_options = {
45 45 onKeyEvent: $.proxy(this.handle_keyevent,this)
46 46 };
47 47 var config = utils.mergeopt(TextCell, this.config, {cm_config:cm_overwrite_options});
48 48 Cell.apply(this, [{
49 49 config: config,
50 50 keyboard_manager: options.keyboard_manager,
51 51 events: this.events}]);
52 52
53 53 this.cell_type = this.cell_type || 'text';
54 54 mathjaxutils = mathjaxutils;
55 55 this.rendered = false;
56 56 };
57 57
58 58 TextCell.prototype = Object.create(Cell.prototype);
59 59
60 60 TextCell.options_default = {
61 61 cm_config : {
62 62 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
63 63 mode: 'htmlmixed',
64 64 lineWrapping : true,
65 65 }
66 66 };
67 67
68 68
69 69 /**
70 70 * Create the DOM element of the TextCell
71 71 * @method create_element
72 72 * @private
73 73 */
74 74 TextCell.prototype.create_element = function () {
75 75 Cell.prototype.create_element.apply(this, arguments);
76 76
77 77 var cell = $("<div>").addClass('cell text_cell');
78 78 cell.attr('tabindex','2');
79 79
80 80 var prompt = $('<div/>').addClass('prompt input_prompt');
81 81 cell.append(prompt);
82 82 var inner_cell = $('<div/>').addClass('inner_cell');
83 83 this.celltoolbar = new celltoolbar.CellToolbar({
84 84 cell: this,
85 85 notebook: this.notebook});
86 86 inner_cell.append(this.celltoolbar.element);
87 87 var input_area = $('<div/>').addClass('input_area');
88 88 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
89 89 // The tabindex=-1 makes this div focusable.
90 90 var render_area = $('<div/>').addClass('text_cell_render rendered_html')
91 91 .attr('tabindex','-1');
92 92 inner_cell.append(input_area).append(render_area);
93 93 cell.append(inner_cell);
94 94 this.element = cell;
95 95 };
96 96
97 97
98 98 // Cell level actions
99 99
100 100 TextCell.prototype.select = function () {
101 101 var cont = Cell.prototype.select.apply(this);
102 102 if (cont) {
103 103 if (this.mode === 'edit') {
104 104 this.code_mirror.refresh();
105 105 }
106 106 }
107 107 return cont;
108 108 };
109 109
110 110 TextCell.prototype.unrender = function () {
111 111 if (this.read_only) return;
112 112 var cont = Cell.prototype.unrender.apply(this);
113 113 if (cont) {
114 114 var text_cell = this.element;
115 115 var output = text_cell.find("div.text_cell_render");
116 116 if (this.get_text() === this.placeholder) {
117 117 this.set_text('');
118 118 }
119 119 this.refresh();
120 120 }
121 121 return cont;
122 122 };
123 123
124 124 TextCell.prototype.execute = function () {
125 125 this.render();
126 126 };
127 127
128 128 /**
129 129 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
130 130 * @method get_text
131 131 * @retrun {string} CodeMirror current text value
132 132 */
133 133 TextCell.prototype.get_text = function() {
134 134 return this.code_mirror.getValue();
135 135 };
136 136
137 137 /**
138 138 * @param {string} text - Codemiror text value
139 139 * @see TextCell#get_text
140 140 * @method set_text
141 141 * */
142 142 TextCell.prototype.set_text = function(text) {
143 143 this.code_mirror.setValue(text);
144 144 this.unrender();
145 145 this.code_mirror.refresh();
146 146 };
147 147
148 148 /**
149 149 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
150 150 * @method get_rendered
151 151 * */
152 152 TextCell.prototype.get_rendered = function() {
153 153 return this.element.find('div.text_cell_render').html();
154 154 };
155 155
156 156 /**
157 157 * @method set_rendered
158 158 */
159 159 TextCell.prototype.set_rendered = function(text) {
160 160 this.element.find('div.text_cell_render').html(text);
161 161 };
162 162
163 163
164 164 /**
165 165 * Create Text cell from JSON
166 166 * @param {json} data - JSON serialized text-cell
167 167 * @method fromJSON
168 168 */
169 169 TextCell.prototype.fromJSON = function (data) {
170 170 Cell.prototype.fromJSON.apply(this, arguments);
171 171 if (data.cell_type === this.cell_type) {
172 172 if (data.source !== undefined) {
173 173 this.set_text(data.source);
174 174 // make this value the starting point, so that we can only undo
175 175 // to this state, instead of a blank cell
176 176 this.code_mirror.clearHistory();
177 177 // TODO: This HTML needs to be treated as potentially dangerous
178 178 // user input and should be handled before set_rendered.
179 179 this.set_rendered(data.rendered || '');
180 180 this.rendered = false;
181 181 this.render();
182 182 }
183 183 }
184 184 };
185 185
186 186 /** Generate JSON from cell
187 187 * @return {object} cell data serialised to json
188 188 */
189 189 TextCell.prototype.toJSON = function () {
190 190 var data = Cell.prototype.toJSON.apply(this);
191 191 data.source = this.get_text();
192 192 if (data.source == this.placeholder) {
193 193 data.source = "";
194 194 }
195 195 return data;
196 196 };
197 197
198 198
199 199 var MarkdownCell = function (options) {
200 200 // Constructor
201 201 //
202 202 // Parameters:
203 203 // options: dictionary
204 204 // Dictionary of keyword arguments.
205 205 // events: $(Events) instance
206 206 // config: dictionary
207 207 // keyboard_manager: KeyboardManager instance
208 208 // notebook: Notebook instance
209 209 options = options || {};
210 210 var config = utils.mergeopt(MarkdownCell, options.config);
211 211 TextCell.apply(this, [$.extend({}, options, {config: config})]);
212 212
213 213 this.cell_type = 'markdown';
214 214 };
215 215
216 216 MarkdownCell.options_default = {
217 217 cm_config: {
218 218 mode: 'ipythongfm'
219 219 },
220 220 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
221 221 };
222 222
223 223 MarkdownCell.prototype = Object.create(TextCell.prototype);
224 224
225 MarkdownCell.prototype.set_heading_level = function (level) {
226 // make a markdown cell a heading
227 level = level || 1;
228 var source = this.get_text();
229 // \s\S appears to be the js version of multi-line dot-all
230 var match = source.match(/(#*)\s*([\s\S]*)/);
231 // strip the leading `#` if it's already there
232 if (match) {
233 source = match[2];
234 }
235 // add `#` markdown heading prefix
236 var new_text = new Array(level + 1).join('#') + ' ' + source;
237
238 this.set_text(new_text);
239 this.refresh();
240 if (this.rendered) {
241 this.render();
242 }
243 };
244
225 245 /**
226 246 * @method render
227 247 */
228 248 MarkdownCell.prototype.render = function () {
229 249 var cont = TextCell.prototype.render.apply(this);
230 250 if (cont) {
231 251 var text = this.get_text();
232 252 var math = null;
233 253 if (text === "") { text = this.placeholder; }
234 254 var text_and_math = mathjaxutils.remove_math(text);
235 255 text = text_and_math[0];
236 256 math = text_and_math[1];
237 257 var html = marked.parser(marked.lexer(text));
238 258 html = mathjaxutils.replace_math(html, math);
239 259 html = security.sanitize_html(html);
240 260 html = $($.parseHTML(html));
261 // add anchors to headings
262 // console.log(html);
263 html.find(":header").addBack(":header").each(function (i, h) {
264 h = $(h);
265 var hash = h.text().replace(/ /g, '-');
266 h.attr('id', hash);
267 h.append(
268 $('<a/>')
269 .addClass('anchor-link')
270 .attr('href', '#' + hash)
271 .text('¶')
272 );
273 })
241 274 // links in markdown cells should open in new tabs
242 275 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
243 276 this.set_rendered(html);
244 277 this.typeset();
245 278 this.events.trigger("rendered.MarkdownCell", {cell: this})
246 279 }
247 280 return cont;
248 281 };
249 282
250 283
251 284 var RawCell = function (options) {
252 285 // Constructor
253 286 //
254 287 // Parameters:
255 288 // options: dictionary
256 289 // Dictionary of keyword arguments.
257 290 // events: $(Events) instance
258 291 // config: dictionary
259 292 // keyboard_manager: KeyboardManager instance
260 293 // notebook: Notebook instance
261 294 options = options || {};
262 295 var config = utils.mergeopt(RawCell, options.config);
263 296 TextCell.apply(this, [$.extend({}, options, {config: config})]);
264 297
265 298 this.cell_type = 'raw';
266 299 };
267 300
268 301 RawCell.options_default = {
269 302 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert. " +
270 303 "It will not be rendered in the notebook. " +
271 304 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
272 305 };
273 306
274 307 RawCell.prototype = Object.create(TextCell.prototype);
275 308
276 309 /** @method bind_events **/
277 310 RawCell.prototype.bind_events = function () {
278 311 TextCell.prototype.bind_events.apply(this);
279 312 var that = this;
280 313 this.element.focusout(function() {
281 314 that.auto_highlight();
282 315 that.render();
283 316 });
284 317
285 318 this.code_mirror.on('focus', function() { that.unrender(); });
286 319 };
287 320
288 321 /**
289 322 * Trigger autodetection of highlight scheme for current cell
290 323 * @method auto_highlight
291 324 */
292 325 RawCell.prototype.auto_highlight = function () {
293 326 this._auto_highlight(this.config.raw_cell_highlight);
294 327 };
295 328
296 329 /** @method render **/
297 330 RawCell.prototype.render = function () {
298 331 var cont = TextCell.prototype.render.apply(this);
299 332 if (cont){
300 333 var text = this.get_text();
301 334 if (text === "") { text = this.placeholder; }
302 335 this.set_text(text);
303 336 this.element.removeClass('rendered');
304 337 }
305 338 return cont;
306 339 };
307 340
308
309 var HeadingCell = function (options) {
310 // Constructor
311 //
312 // Parameters:
313 // options: dictionary
314 // Dictionary of keyword arguments.
315 // events: $(Events) instance
316 // config: dictionary
317 // keyboard_manager: KeyboardManager instance
318 // notebook: Notebook instance
319 options = options || {};
320 var config = utils.mergeopt(HeadingCell, options.config);
321 TextCell.apply(this, [$.extend({}, options, {config: config})]);
322
323 this.level = 1;
324 this.cell_type = 'heading';
325 };
326
327 HeadingCell.options_default = {
328 cm_config: {
329 theme: 'heading-1'
330 },
331 placeholder: "Type Heading Here"
332 };
333
334 HeadingCell.prototype = Object.create(TextCell.prototype);
335
336 /** @method fromJSON */
337 HeadingCell.prototype.fromJSON = function (data) {
338 if (data.level !== undefined){
339 this.level = data.level;
340 }
341 TextCell.prototype.fromJSON.apply(this, arguments);
342 this.code_mirror.setOption("theme", "heading-"+this.level);
343 };
344
345
346 /** @method toJSON */
347 HeadingCell.prototype.toJSON = function () {
348 var data = TextCell.prototype.toJSON.apply(this);
349 data.level = this.get_level();
350 return data;
351 };
352
353 /**
354 * Change heading level of cell, and re-render
355 * @method set_level
356 */
357 HeadingCell.prototype.set_level = function (level) {
358 this.level = level;
359 this.code_mirror.setOption("theme", "heading-"+level);
360
361 if (this.rendered) {
362 this.rendered = false;
363 this.render();
364 }
365 };
366
367 /** The depth of header cell, based on html (h1 to h6)
368 * @method get_level
369 * @return {integer} level - for 1 to 6
370 */
371 HeadingCell.prototype.get_level = function () {
372 return this.level;
373 };
374
375
376 HeadingCell.prototype.get_rendered = function () {
377 var r = this.element.find("div.text_cell_render");
378 return r.children().first().html();
379 };
380
381 HeadingCell.prototype.render = function () {
382 var cont = TextCell.prototype.render.apply(this);
383 if (cont) {
384 var text = this.get_text();
385 var math = null;
386 // Markdown headings must be a single line
387 text = text.replace(/\n/g, ' ');
388 if (text === "") { text = this.placeholder; }
389 text = new Array(this.level + 1).join("#") + " " + text;
390 var text_and_math = mathjaxutils.remove_math(text);
391 text = text_and_math[0];
392 math = text_and_math[1];
393 var html = marked.parser(marked.lexer(text));
394 html = mathjaxutils.replace_math(html, math);
395 html = security.sanitize_html(html);
396 var h = $($.parseHTML(html));
397 // add id and linkback anchor
398 var hash = h.text().trim().replace(/ /g, '-');
399 h.attr('id', hash);
400 h.append(
401 $('<a/>')
402 .addClass('anchor-link')
403 .attr('href', '#' + hash)
404 .text('¶')
405 );
406 this.set_rendered(h);
407 this.typeset();
408 }
409 return cont;
410 };
411
412 341 // Backwards compatability.
413 342 IPython.TextCell = TextCell;
414 343 IPython.MarkdownCell = MarkdownCell;
415 344 IPython.RawCell = RawCell;
416 IPython.HeadingCell = HeadingCell;
417 345
418 346 var textcell = {
419 'TextCell': TextCell,
420 'MarkdownCell': MarkdownCell,
421 'RawCell': RawCell,
422 'HeadingCell': HeadingCell,
347 TextCell: TextCell,
348 MarkdownCell: MarkdownCell,
349 RawCell: RawCell,
423 350 };
424 351 return textcell;
425 352 });
@@ -1,328 +1,322 b''
1 1 {% extends "page.html" %}
2 2
3 3 {% block stylesheet %}
4 4
5 5 {% if mathjax_url %}
6 6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 7 {% endif %}
8 8 <script type="text/javascript">
9 9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 10 // where it will be undefined, and should prompt a dialog later.
11 11 window.mathjax_url = "{{mathjax_url}}";
12 12 </script>
13 13
14 14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
15 15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
16 16
17 17 {{super()}}
18 18
19 19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
20 20
21 21 {% endblock %}
22 22
23 23 {% block params %}
24 24
25 25 data-project="{{project}}"
26 26 data-base-url="{{base_url}}"
27 27 data-ws-url="{{ws_url}}"
28 28 data-notebook-name="{{notebook_name}}"
29 29 data-notebook-path="{{notebook_path}}"
30 30 class="notebook_app"
31 31
32 32 {% endblock %}
33 33
34 34
35 35 {% block header %}
36 36
37 37
38 38 <span id="save_widget" class="nav pull-left">
39 39 <span id="notebook_name"></span>
40 40 <span id="checkpoint_status"></span>
41 41 <span id="autosave_status"></span>
42 42 </span>
43 43
44 44 <span id="kernel_selector_widget" class="pull-right dropdown">
45 45 <button class="dropdown-toggle" data-toggle="dropdown" type='button' id="current_kernel_spec">
46 46 <span class='kernel_name'>Python</span>
47 47 <span class="caret"></span>
48 48 </button>
49 49 <ul id="kernel_selector" class="dropdown-menu">
50 50 </ul>
51 51 </span>
52 52
53 53 {% endblock %}
54 54
55 55
56 56 {% block site %}
57 57
58 58 <div id="menubar-container" class="container">
59 59 <div id="menubar">
60 60 <div id="menus" class="navbar navbar-default" role="navigation">
61 61 <div class="container-fluid">
62 62 <button type="button" class="btn btn-default navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
63 63 <i class="fa fa-bars"></i>
64 64 <span class="navbar-text">Menu</span>
65 65 </button>
66 66 <ul class="nav navbar-nav navbar-right">
67 67 <li id="kernel_indicator">
68 68 <i id="kernel_indicator_icon"></i>
69 69 </li>
70 70 <li id="modal_indicator">
71 71 <i id="modal_indicator_icon"></i>
72 72 </li>
73 73 <li id="notification_area"></li>
74 74 </ul>
75 75 <div class="navbar-collapse collapse">
76 76 <ul class="nav navbar-nav">
77 77 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
78 78 <ul id="file_menu" class="dropdown-menu">
79 79 <li id="new_notebook"
80 80 title="Make a new notebook (Opens a new window)">
81 81 <a href="#">New</a></li>
82 82 <li id="open_notebook"
83 83 title="Opens a new window with the Dashboard view">
84 84 <a href="#">Open...</a></li>
85 85 <!-- <hr/> -->
86 86 <li class="divider"></li>
87 87 <li id="copy_notebook"
88 88 title="Open a copy of this notebook's contents and start a new kernel">
89 89 <a href="#">Make a Copy...</a></li>
90 90 <li id="rename_notebook"><a href="#">Rename...</a></li>
91 91 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
92 92 <!-- <hr/> -->
93 93 <li class="divider"></li>
94 94 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
95 95 <ul class="dropdown-menu">
96 96 <li><a href="#"></a></li>
97 97 <li><a href="#"></a></li>
98 98 <li><a href="#"></a></li>
99 99 <li><a href="#"></a></li>
100 100 <li><a href="#"></a></li>
101 101 </ul>
102 102 </li>
103 103 <li class="divider"></li>
104 104 <li id="print_preview"><a href="#">Print Preview</a></li>
105 105 <li class="dropdown-submenu"><a href="#">Download as</a>
106 106 <ul class="dropdown-menu">
107 107 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
108 108 <li id="download_py"><a href="#">Python (.py)</a></li>
109 109 <li id="download_html"><a href="#">HTML (.html)</a></li>
110 110 <li id="download_rst"><a href="#">reST (.rst)</a></li>
111 111 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
112 112 </ul>
113 113 </li>
114 114 <li class="divider"></li>
115 115 <li id="trust_notebook"
116 116 title="Trust the output of this notebook">
117 117 <a href="#" >Trust Notebook</a></li>
118 118 <li class="divider"></li>
119 119 <li id="kill_and_exit"
120 120 title="Shutdown this notebook's kernel, and close this window">
121 121 <a href="#" >Close and halt</a></li>
122 122 </ul>
123 123 </li>
124 124 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
125 125 <ul id="edit_menu" class="dropdown-menu">
126 126 <li id="cut_cell"><a href="#">Cut Cell</a></li>
127 127 <li id="copy_cell"><a href="#">Copy Cell</a></li>
128 128 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
129 129 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
130 130 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
131 131 <li id="delete_cell"><a href="#">Delete Cell</a></li>
132 132 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
133 133 <li class="divider"></li>
134 134 <li id="split_cell"><a href="#">Split Cell</a></li>
135 135 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
136 136 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
137 137 <li class="divider"></li>
138 138 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
139 139 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
140 140 <li class="divider"></li>
141 141 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
142 142 </ul>
143 143 </li>
144 144 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
145 145 <ul id="view_menu" class="dropdown-menu">
146 146 <li id="toggle_header"
147 147 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
148 148 <a href="#">Toggle Header</a></li>
149 149 <li id="toggle_toolbar"
150 150 title="Show/Hide the action icons (below menu bar)">
151 151 <a href="#">Toggle Toolbar</a></li>
152 152 </ul>
153 153 </li>
154 154 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
155 155 <ul id="insert_menu" class="dropdown-menu">
156 156 <li id="insert_cell_above"
157 157 title="Insert an empty Code cell above the currently active cell">
158 158 <a href="#">Insert Cell Above</a></li>
159 159 <li id="insert_cell_below"
160 160 title="Insert an empty Code cell below the currently active cell">
161 161 <a href="#">Insert Cell Below</a></li>
162 162 </ul>
163 163 </li>
164 164 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
165 165 <ul id="cell_menu" class="dropdown-menu">
166 166 <li id="run_cell" title="Run this cell, and move cursor to the next one">
167 167 <a href="#">Run</a></li>
168 168 <li id="run_cell_select_below" title="Run this cell, select below">
169 169 <a href="#">Run and Select Below</a></li>
170 170 <li id="run_cell_insert_below" title="Run this cell, insert below">
171 171 <a href="#">Run and Insert Below</a></li>
172 172 <li id="run_all_cells" title="Run all cells in the notebook">
173 173 <a href="#">Run All</a></li>
174 174 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
175 175 <a href="#">Run All Above</a></li>
176 176 <li id="run_all_cells_below" title="Run this cell and all cells below it">
177 177 <a href="#">Run All Below</a></li>
178 178 <li class="divider"></li>
179 179 <li id="change_cell_type" class="dropdown-submenu"
180 180 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
181 181 <a href="#">Cell Type</a>
182 182 <ul class="dropdown-menu">
183 183 <li id="to_code"
184 184 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
185 185 <a href="#">Code</a></li>
186 186 <li id="to_markdown"
187 187 title="Contents will be rendered as HTML and serve as explanatory text">
188 188 <a href="#">Markdown</a></li>
189 189 <li id="to_raw"
190 190 title="Contents will pass through nbconvert unmodified">
191 191 <a href="#">Raw NBConvert</a></li>
192 <li id="to_heading1"><a href="#">Heading 1</a></li>
193 <li id="to_heading2"><a href="#">Heading 2</a></li>
194 <li id="to_heading3"><a href="#">Heading 3</a></li>
195 <li id="to_heading4"><a href="#">Heading 4</a></li>
196 <li id="to_heading5"><a href="#">Heading 5</a></li>
197 <li id="to_heading6"><a href="#">Heading 6</a></li>
198 192 </ul>
199 193 </li>
200 194 <li class="divider"></li>
201 195 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
202 196 <ul class="dropdown-menu">
203 197 <li id="toggle_current_output"
204 198 title="Hide/Show the output of the current cell">
205 199 <a href="#">Toggle</a>
206 200 </li>
207 201 <li id="toggle_current_output_scroll"
208 202 title="Scroll the output of the current cell">
209 203 <a href="#">Toggle Scrolling</a>
210 204 </li>
211 205 <li id="clear_current_output"
212 206 title="Clear the output of the current cell">
213 207 <a href="#">Clear</a>
214 208 </li>
215 209 </ul>
216 210 </li>
217 211 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
218 212 <ul class="dropdown-menu">
219 213 <li id="toggle_all_output"
220 214 title="Hide/Show the output of all cells">
221 215 <a href="#">Toggle</a>
222 216 </li>
223 217 <li id="toggle_all_output_scroll"
224 218 title="Scroll the output of all cells">
225 219 <a href="#">Toggle Scrolling</a>
226 220 </li>
227 221 <li id="clear_all_output"
228 222 title="Clear the output of all cells">
229 223 <a href="#">Clear</a>
230 224 </li>
231 225 </ul>
232 226 </li>
233 227 </ul>
234 228 </li>
235 229 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
236 230 <ul id="kernel_menu" class="dropdown-menu">
237 231 <li id="int_kernel"
238 232 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
239 233 <a href="#">Interrupt</a></li>
240 234 <li id="restart_kernel"
241 235 title="Restart the Kernel">
242 236 <a href="#">Restart</a></li>
243 237 <li class="divider"></li>
244 238 <li id="menu-change-kernel" class="dropdown-submenu">
245 239 <a href="#">Change kernel</a>
246 240 <ul class="dropdown-menu" id="menu-change-kernel-submenu"></ul>
247 241 </li>
248 242 </ul>
249 243 </li>
250 244 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
251 245 <ul id="help_menu" class="dropdown-menu">
252 246 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
253 247 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
254 248 <li class="divider"></li>
255 249 {% set
256 250 sections = (
257 251 (
258 252 ("http://ipython.org/documentation.html","IPython Help",True),
259 253 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
260 254 ),(
261 255 ("http://docs.python.org","Python",True),
262 256 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
263 257 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
264 258 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
265 259 ("http://matplotlib.org/contents.html","Matplotlib",True),
266 260 ("http://docs.sympy.org/latest/index.html","SymPy",True),
267 261 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
268 262 )
269 263 )
270 264 %}
271 265
272 266 {% for helplinks in sections %}
273 267 {% for link in helplinks %}
274 268 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
275 269 {{'<i class="fa fa-external-link menu-icon pull-right"></i>' if link[2]}}
276 270 {{link[1]}}
277 271 </a></li>
278 272 {% endfor %}
279 273 {% if not loop.last %}
280 274 <li class="divider"></li>
281 275 {% endif %}
282 276 {% endfor %}
283 277 <li class="divider"></li>
284 278 <li title="About IPython Notebook"><a id="notebook_about" href="#">About</a></li>
285 279 </ul>
286 280 </li>
287 281 </ul>
288 282 </div>
289 283 </div>
290 284 </div>
291 285 </div>
292 286 <div id="maintoolbar" class="navbar">
293 287 <div class="toolbar-inner navbar-inner navbar-nobg">
294 288 <div id="maintoolbar-container" class="container"></div>
295 289 </div>
296 290 </div>
297 291 </div>
298 292
299 293 <div id="ipython-main-app">
300 294
301 295 <div id="notebook_panel">
302 296 <div id="notebook"></div>
303 297 <div id="pager_splitter"></div>
304 298 <div id="pager">
305 299 <div id='pager_button_area'>
306 300 </div>
307 301 <div id="pager-container" class="container"></div>
308 302 </div>
309 303 </div>
310 304
311 305 </div>
312 306 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
313 307
314 308
315 309 {% endblock %}
316 310
317 311
318 312 {% block script %}
319 313 {{super()}}
320 314 <script type="text/javascript">
321 315 sys_info = {{sys_info}};
322 316 </script>
323 317
324 318 <script src="{{ static_url("components/text-encoding/lib/encoding.js") }}" charset="utf-8"></script>
325 319
326 320 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
327 321
328 322 {% endblock %}
@@ -1,77 +1,77 b''
1 1
2 2 // Test
3 3 casper.notebook_test(function () {
4 4 var a = 'print("a")';
5 5 var index = this.append_cell(a);
6 6 this.execute_cell_then(index);
7 7
8 8 var b = 'print("b")';
9 9 index = this.append_cell(b);
10 10 this.execute_cell_then(index);
11 11
12 12 var c = 'print("c")';
13 13 index = this.append_cell(c);
14 14 this.execute_cell_then(index);
15 15
16 16 this.thenEvaluate(function() {
17 17 IPython.notebook.default_cell_type = 'code';
18 18 });
19 19
20 20 this.then(function () {
21 21 // Cell insertion
22 22 this.select_cell(2);
23 23 this.trigger_keydown('m'); // Make it markdown
24 24 this.trigger_keydown('a'); // Creates one cell
25 25 this.test.assertEquals(this.get_cell_text(2), '', 'a; New cell 2 text is empty');
26 26 this.test.assertEquals(this.get_cell(2).cell_type, 'code', 'a; inserts a code cell');
27 27 this.validate_notebook_state('a', 'command', 2);
28 28 this.trigger_keydown('b'); // Creates one cell
29 29 this.test.assertEquals(this.get_cell_text(2), '', 'b; Cell 2 text is still empty');
30 30 this.test.assertEquals(this.get_cell_text(3), '', 'b; New cell 3 text is empty');
31 31 this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'b; inserts a code cell');
32 32 this.validate_notebook_state('b', 'command', 3);
33 33 });
34 34
35 35 this.thenEvaluate(function() {
36 36 IPython.notebook.default_cell_type = 'selected';
37 37 });
38 38
39 39 this.then(function () {
40 40 this.select_cell(2);
41 41 this.trigger_keydown('m'); // switch it to markdown for the next test
42 42 this.test.assertEquals(this.get_cell(2).cell_type, 'markdown', 'test cell is markdown');
43 43 this.trigger_keydown('a'); // new cell above
44 44 this.test.assertEquals(this.get_cell(2).cell_type, 'markdown', 'a; inserts a markdown cell when markdown selected');
45 45 this.trigger_keydown('b'); // new cell below
46 46 this.test.assertEquals(this.get_cell(3).cell_type, 'markdown', 'b; inserts a markdown cell when markdown selected');
47 47 });
48 48
49 49 this.thenEvaluate(function() {
50 50 IPython.notebook.default_cell_type = 'above';
51 51 });
52 52
53 53 this.then(function () {
54 54 this.select_cell(2);
55 this.trigger_keydown('1'); // switch it to heading for the next test
56 this.test.assertEquals(this.get_cell(2).cell_type, 'heading', 'test cell is heading');
55 this.trigger_keydown('y'); // switch it to code for the next test
56 this.test.assertEquals(this.get_cell(2).cell_type, 'code', 'test cell is code');
57 57 this.trigger_keydown('b'); // new cell below
58 this.test.assertEquals(this.get_cell(3).cell_type, 'heading', 'b; inserts a heading cell below heading cell');
58 this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'b; inserts a code cell below code cell');
59 59 this.trigger_keydown('a'); // new cell above
60 this.test.assertEquals(this.get_cell(3).cell_type, 'heading', 'a; inserts a heading cell below heading cell');
60 this.test.assertEquals(this.get_cell(3).cell_type, 'code', 'a; inserts a code cell below code cell');
61 61 });
62 62
63 63 this.thenEvaluate(function() {
64 64 IPython.notebook.default_cell_type = 'below';
65 65 });
66 66
67 67 this.then(function () {
68 68 this.select_cell(2);
69 69 this.trigger_keydown('r'); // switch it to markdown for the next test
70 70 this.test.assertEquals(this.get_cell(2).cell_type, 'raw', 'test cell is raw');
71 71 this.trigger_keydown('a'); // new cell above
72 72 this.test.assertEquals(this.get_cell(2).cell_type, 'raw', 'a; inserts a raw cell above raw cell');
73 73 this.trigger_keydown('y'); // switch it to code for the next test
74 74 this.trigger_keydown('b'); // new cell below
75 75 this.test.assertEquals(this.get_cell(3).cell_type, 'raw', 'b; inserts a raw cell above raw cell');
76 76 });
77 77 });
@@ -1,28 +1,41 b''
1 1 // Test keyboard shortcuts that change the cell's mode.
2 2
3 3 // Test
4 4 casper.notebook_test(function () {
5 5 this.then(function () {
6 6 // Cell mode change
7 this.select_cell(0);
7 var index = 0;
8 this.select_cell(index);
9 var a = 'hello\nmulti\nline';
10 this.set_cell_text(index, a);
8 11 this.trigger_keydown('esc','r');
9 this.test.assertEquals(this.get_cell(0).cell_type, 'raw', 'r; cell is raw');
12 this.test.assertEquals(this.get_cell(index).cell_type, 'raw', 'r; cell is raw');
10 13 this.trigger_keydown('1');
11 this.test.assertEquals(this.get_cell(0).cell_type, 'heading', '1; cell is heading');
12 this.test.assertEquals(this.get_cell(0).level, 1, '1; cell is level 1 heading');
14 this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '1; cell is markdown');
15 this.test.assertEquals(this.get_cell_text(index), '# ' + a, '1; markdown heading');
13 16 this.trigger_keydown('2');
14 this.test.assertEquals(this.get_cell(0).level, 2, '2; cell is level 2 heading');
17 this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '2; cell is markdown');
18 this.test.assertEquals(this.get_cell_text(index), '## ' + a, '2; markdown heading');
15 19 this.trigger_keydown('3');
16 this.test.assertEquals(this.get_cell(0).level, 3, '3; cell is level 3 heading');
20 this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '3; cell is markdown');
21 this.test.assertEquals(this.get_cell_text(index), '### ' + a, '3; markdown heading');
17 22 this.trigger_keydown('4');
18 this.test.assertEquals(this.get_cell(0).level, 4, '4; cell is level 4 heading');
23 this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '4; cell is markdown');
24 this.test.assertEquals(this.get_cell_text(index), '#### ' + a, '4; markdown heading');
19 25 this.trigger_keydown('5');
20 this.test.assertEquals(this.get_cell(0).level, 5, '5; cell is level 5 heading');
26 this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '5; cell is markdown');
27 this.test.assertEquals(this.get_cell_text(index), '##### ' + a, '5; markdown heading');
21 28 this.trigger_keydown('6');
22 this.test.assertEquals(this.get_cell(0).level, 6, '6; cell is level 6 heading');
29 this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '6; cell is markdown');
30 this.test.assertEquals(this.get_cell_text(index), '###### ' + a, '6; markdown heading');
23 31 this.trigger_keydown('m');
24 this.test.assertEquals(this.get_cell(0).cell_type, 'markdown', 'm; cell is markdown');
32 this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', 'm; cell is markdown');
33 this.test.assertEquals(this.get_cell_text(index), '###### ' + a, 'm; still markdown heading');
25 34 this.trigger_keydown('y');
26 this.test.assertEquals(this.get_cell(0).cell_type, 'code', 'y; cell is code');
35 this.test.assertEquals(this.get_cell(index).cell_type, 'code', 'y; cell is code');
36 this.test.assertEquals(this.get_cell_text(index), '###### ' + a, 'y; still has hashes');
37 this.trigger_keydown('1');
38 this.test.assertEquals(this.get_cell(index).cell_type, 'markdown', '1; cell is markdown');
39 this.test.assertEquals(this.get_cell_text(index), '# ' + a, '1; markdown heading');
27 40 });
28 41 }); No newline at end of file
@@ -1,47 +1,62 b''
1 1 //
2 2 // Test that a Markdown cell is rendered to HTML.
3 3 //
4 4 casper.notebook_test(function () {
5 5 // Test JavaScript models.
6 6 var output = this.evaluate(function () {
7 7 IPython.notebook.to_markdown();
8 8 var cell = IPython.notebook.get_selected_cell();
9 9 cell.set_text('# Foo');
10 10 cell.render();
11 11 return cell.get_rendered();
12 12 });
13 this.test.assertEquals(output.trim(), '<h1 id=\"foo\">Foo</h1>', 'Markdown JS API works.');
13 this.test.assertEquals(output.trim(), '<h1 id=\"Foo\">Foo<a class=\"anchor-link\" href=\"#Foo\">¶</a></h1>', 'Markdown JS API works.');
14 14
15 15 // Test menubar entries.
16 16 output = this.evaluate(function () {
17 17 $('#to_code').mouseenter().click();
18 18 $('#to_markdown').mouseenter().click();
19 19 var cell = IPython.notebook.get_selected_cell();
20 cell.set_text('# Bar');
20 cell.set_text('**Bar**');
21 21 $('#run_cell').mouseenter().click();
22 22 return cell.get_rendered();
23 23 });
24 this.test.assertEquals(output.trim(), '<h1 id=\"bar\">Bar</h1>', 'Markdown menubar items work.');
24 this.test.assertEquals(output.trim(), '<p><strong>Bar</strong></p>', 'Markdown menubar items work.');
25 25
26 26 // Test toolbar buttons.
27 27 output = this.evaluate(function () {
28 28 $('#cell_type').val('code').change();
29 29 $('#cell_type').val('markdown').change();
30 30 var cell = IPython.notebook.get_selected_cell();
31 cell.set_text('# Baz');
31 cell.set_text('*Baz*');
32 32 $('#run_b').click();
33 33 return cell.get_rendered();
34 34 });
35 this.test.assertEquals(output.trim(), '<h1 id=\"baz\">Baz</h1>', 'Markdown toolbar items work.');
35 this.test.assertEquals(output.trim(), '<p><em>Baz</em></p>', 'Markdown toolbar items work.');
36 36
37 // Test JavaScript models.
38 var output = this.evaluate(function () {
37 // Test markdown headings
39 38
39 var text = 'multi\nline';
40
41 this.evaluate(function (text) {
40 42 var cell = IPython.notebook.insert_cell_at_index('markdown', 0);
41 cell.set_text('# Qux');
42 cell.render();
43 return cell.get_rendered();
44 });
45 this.test.assertEquals(output.trim(), '<h1 id=\"qux\">Qux</h1>', 'Markdown JS API works.');
43 cell.set_text(text);
44 }, {text: text});
45
46 var set_level = function (level) {
47 return casper.evaluate(function (level) {
48 var cell = IPython.notebook.get_cell(0);
49 cell.set_heading_level(level);
50 return cell.get_text();
51 }, {level: level});
52 };
46 53
54 var level_text;
55 var levels = [ 1, 2, 3, 4, 5, 6, 2, 1 ];
56 for (var idx=0; idx < levels.length; idx++) {
57 var level = levels[idx];
58 level_text = set_level(level);
59 hashes = new Array(level + 1).join('#');
60 this.test.assertEquals(level_text, hashes + ' ' + text, 'markdown set_heading_level ' + level);
61 }
47 62 });
@@ -1,263 +1,262 b''
1 1 {
2 2 "cells": [
3 3 {
4 "cell_type": "heading",
5 "level": 1,
4 "cell_type": "markdown",
6 5 "metadata": {},
7 6 "source": [
8 "NumPy and Matplotlib examples"
7 "# NumPy and Matplotlib examples"
9 8 ]
10 9 },
11 10 {
12 11 "cell_type": "markdown",
13 12 "metadata": {},
14 13 "source": [
15 14 "First import NumPy and Matplotlib:"
16 15 ]
17 16 },
18 17 {
19 18 "cell_type": "code",
20 19 "execution_count": 1,
21 20 "metadata": {
22 21 "collapsed": false
23 22 },
24 23 "outputs": [
25 24 {
26 25 "name": "stdout",
27 26 "output_type": "stream",
28 27 "text": [
29 28 "\n",
30 29 "Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.kernel.zmq.pylab.backend_inline].\n",
31 30 "For more information, type 'help(pylab)'.\n"
32 31 ]
33 32 }
34 33 ],
35 34 "source": [
36 35 "%pylab inline"
37 36 ]
38 37 },
39 38 {
40 39 "cell_type": "code",
41 40 "execution_count": 2,
42 41 "metadata": {
43 42 "collapsed": false
44 43 },
45 44 "outputs": [],
46 45 "source": [
47 46 "import numpy as np"
48 47 ]
49 48 },
50 49 {
51 50 "cell_type": "markdown",
52 51 "metadata": {},
53 52 "source": [
54 53 "Now we show some very basic examples of how they can be used."
55 54 ]
56 55 },
57 56 {
58 57 "cell_type": "code",
59 58 "execution_count": 6,
60 59 "metadata": {
61 60 "collapsed": false
62 61 },
63 62 "outputs": [],
64 63 "source": [
65 64 "a = np.random.uniform(size=(100,100))"
66 65 ]
67 66 },
68 67 {
69 68 "cell_type": "code",
70 69 "execution_count": 7,
71 70 "metadata": {
72 71 "collapsed": false
73 72 },
74 73 "outputs": [
75 74 {
76 75 "data": {
77 76 "text/plain": [
78 77 "(100, 100)"
79 78 ]
80 79 },
81 80 "execution_count": 7,
82 81 "metadata": {},
83 82 "output_type": "execute_result"
84 83 }
85 84 ],
86 85 "source": [
87 86 "a.shape"
88 87 ]
89 88 },
90 89 {
91 90 "cell_type": "code",
92 91 "execution_count": 8,
93 92 "metadata": {
94 93 "collapsed": false
95 94 },
96 95 "outputs": [],
97 96 "source": [
98 97 "evs = np.linalg.eigvals(a)"
99 98 ]
100 99 },
101 100 {
102 101 "cell_type": "code",
103 102 "execution_count": 10,
104 103 "metadata": {
105 104 "collapsed": false
106 105 },
107 106 "outputs": [
108 107 {
109 108 "data": {
110 109 "text/plain": [
111 110 "(100,)"
112 111 ]
113 112 },
114 113 "execution_count": 10,
115 114 "metadata": {},
116 115 "output_type": "execute_result"
117 116 }
118 117 ],
119 118 "source": [
120 119 "evs.shape"
121 120 ]
122 121 },
123 122 {
124 123 "cell_type": "markdown",
125 124 "metadata": {},
126 125 "source": [
127 126 "Here is a cell that has both text and PNG output:"
128 127 ]
129 128 },
130 129 {
131 130 "cell_type": "code",
132 131 "execution_count": 14,
133 132 "metadata": {
134 133 "collapsed": false
135 134 },
136 135 "outputs": [
137 136 {
138 137 "data": {
139 138 "text/plain": [
140 139 "(array([95, 4, 0, 0, 0, 0, 0, 0, 0, 1]),\n",
141 140 " array([ -2.93566063, 2.35937011, 7.65440086, 12.9494316 ,\n",
142 141 " 18.24446235, 23.53949309, 28.83452384, 34.12955458,\n",
143 142 " 39.42458533, 44.71961607, 50.01464682]),\n",
144 143 " <a list of 10 Patch objects>)"
145 144 ]
146 145 },
147 146 "execution_count": 14,
148 147 "metadata": {},
149 148 "output_type": "execute_result"
150 149 },
151 150 {
152 151 "data": {
153 152 "image/png": [
154 153 "iVBORw0KGgoAAAANSUhEUgAAAXgAAAD9CAYAAAC2l2x5AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\n",
155 154 "AAALEgAACxIB0t1+/AAAEhdJREFUeJzt3X1olfX/x/HXtVbT8CZDmsK6KmrubEu3U2xnZOpxLBnG\n",
156 155 "OqsIE7RoE3QRZkT/yEAjcIh/LIs6i/BEGSU1CkxT0+pkFp1zMmsxZ5uUTIXoxm95lmdlef3+8Nep\n",
157 156 "dbtz7exs16fnAw7sXNs5n/c14nmurl3naDmO4wgAYJy8sR4AADA6CDwAGIrAA4ChCDwAGIrAA4Ch\n",
158 157 "CDwAGOofA9/U1KTCwkLNnj07vS2ZTCoUCsm2bTU2NmpgYCD9vccee0zFxcUqKyvTgQMHRm9qAMC/\n",
159 158 "+sfA33PPPdq9e/eQbeFwWLZtq6+vT0VFRero6JAkffXVV3ryySf15ptvKhwOa/Xq1aM3NQDgX/1j\n",
160 159 "4OfNm6dp06YN2RaPx9Xc3KyCggI1NTUpFotJkmKxmOrr62XbthYsWCDHcZRMJkdvcgDAP8r4HHwi\n",
161 160 "kZDP55Mk+Xw+xeNxSecDX1pamv65kpKS9PcAALmXn+kDMvlkA8uyhrUNAPDvMv1kmYyP4KuqqtTT\n",
162 161 "0yNJ6unpUVVVlSQpEAjo8OHD6Z87cuRI+nt/NaRXb+vWrRvzGZh/7Odgfu/dvDy747j7yLCMAx8I\n",
163 162 "BBSJRJRKpRSJRFRTUyNJqq6u1p49e9Tf369oNKq8vDxNnjzZ1VAAgJH7x8AvXbpUN9xwg3p7e3X5\n",
164 163 "5ZfrmWeeUUtLi/r7+1VSUqKTJ09q1apVkqTCwkK1tLSotrZW9957rzZv3pyTHQAA/DXLcXvs73ZB\n",
165 164 "y3L9vxvjQTQaVTAYHOsxXGP+scX8Y8fLs0vu2kngAcAD3LSTjyoAAEMReAAwFIEHAEMReAAwFIEH\n",
166 165 "AEP9ZwM/Zcqlsixr1G9Tplw61rsK4D/qP3uZ5PnPxMnFHONjfwF4G5dJAgDSCDwAGIrAA4ChCDwA\n",
167 166 "GIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrA\n",
168 167 "A4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChXAf+6aef1g03\n",
169 168 "3KDrr79ea9askSQlk0mFQiHZtq3GxkYNDAxkbVAAQGZcBf7UqVPasGGD9u7dq0Qiod7eXu3Zs0fh\n",
170 169 "cFi2bauvr09FRUXq6OjI9rwAgGFyFfiJEyfKcRx9//33SqVSOnPmjC655BLF43E1NzeroKBATU1N\n",
171 170 "isVi2Z4XADBMrgMfDod15ZVXasaMGZo7d64CgYASiYR8Pp8kyefzKR6PZ3VYAMDw5bt50Ndff62W\n",
172 171 "lhYdPnxY06ZN0x133KEdO3bIcZxhPX79+vXpr4PBoILBoJsxAMBY0WhU0Wh0RM9hOcOt8u/s3LlT\n",
173 172 "W7du1bZt2yRJ4XBYx44d09GjR9Xa2iq/36+DBw+qra1NnZ2dQxe0rGG/EIwmy7Ik5WKO8bG/ALzN\n",
174 173 "TTtdnaKZN2+ePvzwQ506dUo//vijdu3apUWLFikQCCgSiSiVSikSiaimpsbN0wMAssBV4KdMmaLW\n",
175 174 "1lbdeuutuvHGG1VRUaGFCxeqpaVF/f39Kikp0cmTJ7Vq1apszwsAGCZXp2hGtCCnaAAgYzk7RQMA\n",
176 175 "GP8IPAAYisADgKEIPAAYisADgKEIPAAYisADgKEIPAAYisADgKEIPAAYisADgKEIPAAYisADgKEI\n",
177 176 "PAAYisADgKEIPAAYisADgKEIPAAYisADgKEIPAAYisADgKEIPAAYisADgKEIPAAYisADgKEIPAAY\n",
178 177 "isADgKEIPAAYisADgKEIPAAYisADgKEIPAAYisADgKEIPAAYisADgKEIPAAYynXgf/jhB919992a\n",
179 178 "NWuWysrKFIvFlEwmFQqFZNu2GhsbNTAwkM1ZAQAZcB34devWybZtdXV1qaurSz6fT+FwWLZtq6+v\n",
180 179 "T0VFRero6MjmrACADLgO/L59+7R27VpNmDBB+fn5mjp1quLxuJqbm1VQUKCmpibFYrFszgoAyICr\n",
181 180 "wJ84cUKDg4NqaWlRIBDQxo0blUqllEgk5PP5JEk+n0/xeDyrwwIAhi/fzYMGBwfV29urTZs2qa6u\n",
182 181 "TitXrtRLL70kx3GG9fj169envw4GgwoGg27GAABjRaNRRaPRET2H5Qy3yn9QWlqqnp4eSdKuXbv0\n",
183 182 "3HPP6aefflJra6v8fr8OHjyotrY2dXZ2Dl3Qsob9QjCaLMuSlIs5xsf+AvA2N+10fQ6+uLhYsVhM\n",
184 183 "586d086dO1VXV6dAIKBIJKJUKqVIJKKamhq3Tw8AGCHXR/C9vb266667NDg4qLq6Oj388MM6d+6c\n",
185 184 "li1bpkOHDum6667T888/r0mTJg1dkCN4AMiYm3a6DrxbBB4AMpfTUzQAgPGNwAOAoQg8ABiKwAOA\n",
186 185 "oQg8ABiKwAOAoQg8ABiKwAOAoQg8ABiKwAOAoQg8ABiKwAOAoQg8ABiKwAOAoQg8ABiKwAOAoQg8\n",
187 186 "ABiKwAOAoQg8ABiKwAOAoQg8ABiKwAOAoQg8ABiKwAOAoQg8ABiKwAOAoQg8ABiKwAOAoQg8ABiK\n",
188 187 "wAOAoQg8ABiKwAOAoQg8ABiKwAOAoQg8ABiKwAOAoVwH/pdffpHf71dDQ4MkKZlMKhQKybZtNTY2\n",
189 188 "amBgIGtDAgAy5zrwmzdvVllZmSzLkiSFw2HZtq2+vj4VFRWpo6Mja0MCADLnKvAnTpzQ66+/rhUr\n",
190 189 "VshxHElSPB5Xc3OzCgoK1NTUpFgsltVBAQCZcRX4Bx54QJs2bVJe3m8PTyQS8vl8kiSfz6d4PJ6d\n",
191 190 "CQEAruRn+oAdO3bosssuk9/vVzQaTW//9Uh+ONavX5/+OhgMKhgMZjoGABgtGo0OaawblpNJmSWt\n",
192 191 "XbtWW7duVX5+vgYHB3X69GnddtttOnPmjFpbW+X3+3Xw4EG1tbWps7PzzwtaVkYvBqPl/N8OcjHH\n",
193 192 "+NhfAN7mpp0Zn6LZsGGDjh8/ri+++ELbtm1TbW2ttm7dqkAgoEgkolQqpUgkopqamkyfGgCQRSO+\n",
194 193 "Dv7Xq2haWlrU39+vkpISnTx5UqtWrRrxcAAA9zI+RTPiBTlFAwAZy8kpGgCANxB4ADAUgQcAQxF4\n",
195 194 "ADAUgQcAQxF4ADAUgQcAQxF4ADAUgQcAQxF4ADAUgQcAQxF4ADAUgQcAQxF4ADAUgQcAQxF4ADAU\n",
196 195 "gQcAQxF4ADAUgQcAQxF4ADAUgQcAQxF4ADAUgQcAQxF4ADAUgQcAQxF4ADAUgQcAQxF4ADAUgQcA\n",
197 196 "QxF4ADAUgQcAQxF4ADAUgQcAQxF4ADAUgQcAQ7kK/PHjx7Vw4UKVl5crGAzqhRdekCQlk0mFQiHZ\n",
198 197 "tq3GxkYNDAxkdVgAwPC5CvyFF16o9vZ2dXd3q7OzU62trUomkwqHw7JtW319fSoqKlJHR0e25wUA\n",
199 198 "DJOrwM+YMUOVlZWSpOnTp6u8vFyJRELxeFzNzc0qKChQU1OTYrFYVocFAAzfiM/BHz16VN3d3aqu\n",
200 199 "rlYikZDP55Mk+Xw+xePxEQ8IAHAnfyQPTiaTWrJkidrb2zVp0iQ5jjOsx61fvz79dTAYVDAYHMkY\n",
201 200 "AGCcaDSqaDQ6ouewnOFW+Q/Onj2rm2++WYsXL9aaNWskSbfffrtaW1vl9/t18OBBtbW1qbOzc+iC\n",
202 201 "ljXsF4LRZFmWpFzMMT72F4C3uWmnq1M0juOoublZ1157bTrukhQIBBSJRJRKpRSJRFRTU+Pm6QEA\n",
203 202 "WeDqCP7AgQOaP3++5syZ8/9HwlJbW5vmzp2rZcuW6dChQ7ruuuv0/PPPa9KkSUMX5AgeADLmpp2u\n",
204 203 "T9G4ReABIHM5O0UDABj/CDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrA\n",
205 204 "A4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4Ch\n",
206 205 "CDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4ChCDwAGIrAA4Ch8sd6APPly7KsUV1h8uRpOn361Kiu\n",
207 206 "AcB7LMdxnJwuaFnK8ZJ/O4eUizlysc74+J0CGD1u2skpGgAwFIEHAEMReAAwVNYDv3//fpWWlqq4\n",
208 207 "uFiPP/54tp9+HIiO9QAjEo1Gx3qEEWH+seXl+b08u1tZD/z999+vp556Svv27dMTTzyhb775JttL\n",
209 208 "jLHoWA8wIl7/j5z5x5aX5/fy7G5lNfDff/+9JGn+/Pm64oortGjRIsVisWwuAcBAU6ZcKsuyRvXW\n",
210 209 "1rZxrHcz57Ia+EQiIZ/Pl75fVlamDz74IJtLADBQMvk/nb+cePRuP/00mLsdGieyeh38vn37tGXL\n",
211 210 "Fr344ouSpI6ODp08eVKPPPLIbwuO8pt+AMBUmeY6q+9kraqq0kMPPZS+393drfr6+iE/wxtyACA3\n",
212 211 "snqKZurUqZLOX0lz7Ngx7d27V4FAIJtLAACGKeufRfPoo49q5cqVOnv2rFavXq3p06dnewkAwDBk\n",
213 212 "/TLJBQsWqKenR0ePHtXq1aslSS+//LLKy8t1wQUX6KOPPhry84899piKi4tVVlamAwcOZHucrPHa\n",
214 213 "9f1NTU0qLCzU7Nmz09uSyaRCoZBs21ZjY6MGBgbGcMJ/dvz4cS1cuFDl5eUKBoN64YUXJHlnHwYH\n",
215 214 "BxUIBFRZWamamhq1t7dL8s78kvTLL7/I7/eroaFBkrdmv/LKKzVnzhz5/X5VV1dL8tb8P/zwg+6+\n",
216 215 "+27NmjVLZWVlisVirubPyTtZZ8+erVdffVXz588fsv2rr77Sk08+qTfffFPhcDj9gjAeee36/nvu\n",
217 216 "uUe7d+8esi0cDsu2bfX19amoqEgdHR1jNN2/u/DCC9Xe3q7u7m51dnaqtbVVyWTSM/swYcIEvf32\n",
218 217 "2/r444/1zjvvaMuWLerr6/PM/JK0efNmlZWVpS+M8NLslmUpGo3q0KFDisfjkrw1/7p162Tbtrq6\n",
219 218 "utTV1SWfz+dq/pwE3ufzadasWX/aHovFVF9fL9u2tWDBAjmOo2QymYuRMuLF6/vnzZunadOmDdkW\n",
220 219 "j8fV3NysgoICNTU1jet9mDFjhiorKyVJ06dPV3l5uRKJhKf24eKLL5YkDQwM6Oeff1ZBQYFn5j9x\n",
221 220 "4oRef/11rVixIn1hhFdm/9UfL+jw0vz79u3T2rVrNWHCBOXn52vq1Kmu5h/Tz6KJx+MqLS1N3y8p\n",
222 221 "KUm/2o4nplzf//v98Pl84/J3/VeOHj2q7u5uVVdXe2ofzp07p4qKChUWFuq+++6Tbduemf+BBx7Q\n",
223 222 "pk2blJf3WyK8Mrt0/gi+trZWjY2N2r59uyTvzH/ixAkNDg6qpaVFgUBAGzduVCqVcjV/1v7IetNN\n",
224 223 "N+nLL7/80/YNGzakz+H90V9dMsl18qPHi5eoJpNJLVmyRO3t7Zo0aZKn9iEvL0+ffPKJjh07psWL\n",
225 224 "F2vu3LmemH/Hjh267LLL5Pf7h7y93wuz/+q9997TzJkz1dPTo4aGBlVXV3tm/sHBQfX29mrTpk2q\n",
226 225 "q6vTypUr9dJLL7maP2tH8Hv37tWnn376p9vfxV2SAoGADh8+nL5/5MgRVVVVZWukrKmqqtKRI0fS\n",
227 226 "97u7u1VTUzOGE7lTVVWlnp4eSVJPT8+4/F3/3tmzZ3X77bdr+fLlCoVCkry3D9L5P/gtXrxYsVjM\n",
228 227 "E/O///772r59u6666iotXbpUb731lpYvX+6J2X81c+ZMSVJpaaluueUWvfbaa56Z/5prrlFJSYka\n",
229 228 "Gho0ceJELV26VLt373Y1f85P0fz+Vai6ulp79uxRf3+/otGo8vLyNHny5FyP9K9Mub4/EAgoEoko\n",
230 229 "lUopEomM6xcpx3HU3Nysa6+9VmvWrElv98o+fPPNN/ruu+8kSd9++63eeOMNhUIhT8y/YcMGHT9+\n",
231 230 "XF988YW2bdum2tpabd261ROzS9KZM2fSf8v7+uuvtWfPHtXX13tmfkkqLi5WLBbTuXPntHPnTtXV\n",
232 231 "1bmb38mBV155xSkqKnImTJjgFBYWOvX19envPfroo87VV1/tlJaWOvv378/FOK5Eo1HH5/M5V199\n",
233 232 "tbN58+axHudf3Xnnnc7MmTOdiy66yCkqKnIikYhz+vRp55ZbbnEuv/xyJxQKOclkcqzH/Fvvvvuu\n",
234 233 "Y1mWU1FR4VRWVjqVlZXOrl27PLMPXV1djt/vd+bMmeMsWrTIefbZZx3HcTwz/6+i0ajT0NDgOI53\n",
235 234 "Zv/888+diooKp6KiwqmtrXW2bNniOI535nccx/nss8+cQCDgVFRUOA8++KAzMDDgav6c/5usAIDc\n",
236 235 "4F90AgBDEXgAMBSBBwBDEXgAMBSBBwBDEXgAMNT/AQKseNIf7mhWAAAAAElFTkSuQmCC\n"
237 236 ],
238 237 "text/plain": [
239 238 "<matplotlib.figure.Figure at 0x108c8f1d0>"
240 239 ]
241 240 },
242 241 "metadata": {},
243 242 "output_type": "display_data"
244 243 }
245 244 ],
246 245 "source": [
247 246 "hist(evs.real)"
248 247 ]
249 248 },
250 249 {
251 250 "cell_type": "code",
252 251 "execution_count": null,
253 252 "metadata": {
254 253 "collapsed": false
255 254 },
256 255 "outputs": [],
257 256 "source": []
258 257 }
259 258 ],
260 259 "metadata": {},
261 260 "nbformat": 4,
262 261 "nbformat_minor": 0
263 262 } No newline at end of file
@@ -1,38 +1,39 b''
1 1 """Tests for notebook.py"""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import json
7 7
8 8 from .base import ExportersTestsBase
9 9 from ..notebook import NotebookExporter
10 10
11 from IPython.nbformat.current import validate
11 12 from IPython.testing.tools import assert_big_text_equal
12 13
13 14 class TestNotebookExporter(ExportersTestsBase):
14 15 """Contains test functions for notebook.py"""
15 16
16 17 exporter_class = NotebookExporter
17 18
18 19 def test_export(self):
19 20 """
20 21 Does the NotebookExporter return the file unchanged?
21 22 """
22 23 with open(self._get_notebook()) as f:
23 24 file_contents = f.read()
24 25 (output, resources) = self.exporter_class().from_filename(self._get_notebook())
25 26 assert len(output) > 0
26 27 assert_big_text_equal(output, file_contents)
27 28
28 29 def test_downgrade_3(self):
29 30 exporter = self.exporter_class(nbformat_version=3)
30 31 (output, resources) = exporter.from_filename(self._get_notebook())
31 32 nb = json.loads(output)
32 self.assertEqual(nb['nbformat'], 3)
33 validate(nb)
33 34
34 35 def test_downgrade_2(self):
35 36 exporter = self.exporter_class(nbformat_version=2)
36 37 (output, resources) = exporter.from_filename(self._get_notebook())
37 38 nb = json.loads(output)
38 39 self.assertEqual(nb['nbformat'], 2)
@@ -1,231 +1,236 b''
1 1 """Markdown filters
2 2
3 3 This file contains a collection of utility filters for dealing with
4 4 markdown within Jinja templates.
5 5 """
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 from __future__ import print_function
10 10
11 11 # Stdlib imports
12 12 import os
13 13 import subprocess
14 14 from io import TextIOWrapper, BytesIO
15 15 import re
16 16
17 17 import mistune
18 18 from pygments import highlight
19 19 from pygments.lexers import get_lexer_by_name
20 20 from pygments.formatters import HtmlFormatter
21 21 from pygments.util import ClassNotFound
22 22
23 23 # IPython imports
24 from IPython.nbconvert.filters.strings import add_anchor
24 25 from IPython.nbconvert.utils.pandoc import pandoc
25 26 from IPython.nbconvert.utils.exceptions import ConversionException
26 27 from IPython.utils.decorators import undoc
27 28 from IPython.utils.process import get_output_error_code
28 29 from IPython.utils.py3compat import cast_bytes
29 30 from IPython.utils.version import check_version
30 31
31 32
32 33 marked = os.path.join(os.path.dirname(__file__), "marked.js")
33 34 _node = None
34 35
35 36 __all__ = [
36 37 'markdown2html',
37 38 'markdown2html_pandoc',
38 39 'markdown2html_marked',
39 40 'markdown2html_mistune',
40 41 'markdown2latex',
41 42 'markdown2rst',
42 43 ]
43 44
44 45 class NodeJSMissing(ConversionException):
45 46 """Exception raised when node.js is missing."""
46 47 pass
47 48
48 49 def markdown2latex(source, markup='markdown', extra_args=None):
49 50 """Convert a markdown string to LaTeX via pandoc.
50 51
51 52 This function will raise an error if pandoc is not installed.
52 53 Any error messages generated by pandoc are printed to stderr.
53 54
54 55 Parameters
55 56 ----------
56 57 source : string
57 58 Input string, assumed to be valid markdown.
58 59 markup : string
59 60 Markup used by pandoc's reader
60 61 default : pandoc extended markdown
61 62 (see http://johnmacfarlane.net/pandoc/README.html#pandocs-markdown)
62 63
63 64 Returns
64 65 -------
65 66 out : string
66 67 Output as returned by pandoc.
67 68 """
68 69 return pandoc(source, markup, 'latex', extra_args=extra_args)
69 70
70 71
71 72 @undoc
72 73 class MathBlockGrammar(mistune.BlockGrammar):
73 74 block_math = re.compile("^\$\$(.*?)\$\$", re.DOTALL)
74 75 latex_environment = re.compile(r"^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}",
75 76 re.DOTALL)
76 77
77 78 @undoc
78 79 class MathBlockLexer(mistune.BlockLexer):
79 80 default_features = ['block_math', 'latex_environment'] + mistune.BlockLexer.default_features
80 81
81 82 def __init__(self, rules=None, **kwargs):
82 83 if rules is None:
83 84 rules = MathBlockGrammar()
84 85 super(MathBlockLexer, self).__init__(rules, **kwargs)
85 86
86 87 def parse_block_math(self, m):
87 88 """Parse a $$math$$ block"""
88 89 self.tokens.append({
89 90 'type': 'block_math',
90 91 'text': m.group(1)
91 92 })
92 93
93 94 def parse_latex_environment(self, m):
94 95 self.tokens.append({
95 96 'type': 'latex_environment',
96 97 'name': m.group(1),
97 98 'text': m.group(2)
98 99 })
99 100
100 101 @undoc
101 102 class MathInlineGrammar(mistune.InlineGrammar):
102 103 math = re.compile("^\$(.+?)\$")
103 104 text = re.compile(r'^[\s\S]+?(?=[\\<!\[_*`~$]|https?://| {2,}\n|$)')
104 105
105 106 @undoc
106 107 class MathInlineLexer(mistune.InlineLexer):
107 108 default_features = ['math'] + mistune.InlineLexer.default_features
108 109
109 110 def __init__(self, renderer, rules=None, **kwargs):
110 111 if rules is None:
111 112 rules = MathInlineGrammar()
112 113 super(MathInlineLexer, self).__init__(renderer, rules, **kwargs)
113 114
114 115 def output_math(self, m):
115 116 return self.renderer.inline_math(m.group(1))
116 117
117 118 @undoc
118 119 class MarkdownWithMath(mistune.Markdown):
119 120 def __init__(self, renderer, **kwargs):
120 121 if 'inline' not in kwargs:
121 122 kwargs['inline'] = MathInlineLexer
122 123 if 'block' not in kwargs:
123 124 kwargs['block'] = MathBlockLexer
124 125 super(MarkdownWithMath, self).__init__(renderer, **kwargs)
125 126
126 127 def parse_block_math(self):
127 128 return self.renderer.block_math(self.token['text'])
128 129
129 130 def parse_latex_environment(self):
130 131 return self.renderer.latex_environment(self.token['name'], self.token['text'])
131 132
132 133 @undoc
133 134 class IPythonRenderer(mistune.Renderer):
134 135 def block_code(self, code, lang):
135 136 if lang:
136 137 try:
137 138 lexer = get_lexer_by_name(lang, stripall=True)
138 139 except ClassNotFound:
139 140 code = lang + '\n' + code
140 141 lang = None
141 142
142 143 if not lang:
143 144 return '\n<pre><code>%s</code></pre>\n' % \
144 145 mistune.escape(code)
145 146
146 147 formatter = HtmlFormatter()
147 148 return highlight(code, lexer, formatter)
148 149
150 def header(self, text, level, raw=None):
151 html = super(IPythonRenderer, self).header(text, level, raw=raw)
152 return add_anchor(html)
153
149 154 # Pass math through unaltered - mathjax does the rendering in the browser
150 155 def block_math(self, text):
151 156 return '$$%s$$' % text
152 157
153 158 def latex_environment(self, name, text):
154 159 return r'\begin{%s}%s\end{%s}' % (name, text, name)
155 160
156 161 def inline_math(self, text):
157 162 return '$%s$' % text
158 163
159 164 def markdown2html_mistune(source):
160 165 """Convert a markdown string to HTML using mistune"""
161 166 return MarkdownWithMath(renderer=IPythonRenderer()).render(source)
162 167
163 168 def markdown2html_pandoc(source, extra_args=None):
164 169 """Convert a markdown string to HTML via pandoc"""
165 170 extra_args = extra_args or ['--mathjax']
166 171 return pandoc(source, 'markdown', 'html', extra_args=extra_args)
167 172
168 173 def _find_nodejs():
169 174 global _node
170 175 if _node is None:
171 176 # prefer md2html via marked if node.js >= 0.9.12 is available
172 177 # node is called nodejs on debian, so try that first
173 178 _node = 'nodejs'
174 179 if not _verify_node(_node):
175 180 _node = 'node'
176 181 return _node
177 182
178 183 def markdown2html_marked(source, encoding='utf-8'):
179 184 """Convert a markdown string to HTML via marked"""
180 185 command = [_find_nodejs(), marked]
181 186 try:
182 187 p = subprocess.Popen(command,
183 188 stdin=subprocess.PIPE, stdout=subprocess.PIPE
184 189 )
185 190 except OSError as e:
186 191 raise NodeJSMissing(
187 192 "The command '%s' returned an error: %s.\n" % (" ".join(command), e) +
188 193 "Please check that Node.js is installed."
189 194 )
190 195 out, _ = p.communicate(cast_bytes(source, encoding))
191 196 out = TextIOWrapper(BytesIO(out), encoding, 'replace').read()
192 197 return out.rstrip('\n')
193 198
194 199 # The mistune renderer is the default, because it's simple to depend on it
195 200 markdown2html = markdown2html_mistune
196 201
197 202 def markdown2rst(source, extra_args=None):
198 203 """Convert a markdown string to ReST via pandoc.
199 204
200 205 This function will raise an error if pandoc is not installed.
201 206 Any error messages generated by pandoc are printed to stderr.
202 207
203 208 Parameters
204 209 ----------
205 210 source : string
206 211 Input string, assumed to be valid markdown.
207 212
208 213 Returns
209 214 -------
210 215 out : string
211 216 Output as returned by pandoc.
212 217 """
213 218 return pandoc(source, 'markdown', 'rst', extra_args=extra_args)
214 219
215 220 def _verify_node(cmd):
216 221 """Verify that the node command exists and is at least the minimum supported
217 222 version of node.
218 223
219 224 Parameters
220 225 ----------
221 226 cmd : string
222 227 Node command to verify (i.e 'node')."""
223 228 try:
224 229 out, err, return_code = get_output_error_code([cmd, '--version'])
225 230 except OSError:
226 231 # Command not found
227 232 return False
228 233 if return_code:
229 234 # Command error
230 235 return False
231 236 return check_version(out.lstrip('v'), '0.9.12')
@@ -1,232 +1,221 b''
1 1 # coding: utf-8
2 2 """String filters.
3 3
4 4 Contains a collection of useful string manipulation filters for use in Jinja
5 5 templates.
6 6 """
7 #-----------------------------------------------------------------------------
8 # Copyright (c) 2013, the IPython Development Team.
9 #
10 # Distributed under the terms of the Modified BSD License.
11 #
12 # The full license is in the file COPYING.txt, distributed with this software.
13 #-----------------------------------------------------------------------------
14 7
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
8 # Copyright (c) IPython Development Team.
9 # Distributed under the terms of the Modified BSD License.
18 10
19 11 import os
20 12 import re
21 13 import textwrap
22 14 try:
23 15 from urllib.parse import quote # Py 3
24 16 except ImportError:
25 17 from urllib2 import quote # Py 2
26 18 from xml.etree import ElementTree
27 19
28 20 from IPython.core.interactiveshell import InteractiveShell
29 21 from IPython.utils import py3compat
30 22
31 #-----------------------------------------------------------------------------
32 # Functions
33 #-----------------------------------------------------------------------------
34 23
35 24 __all__ = [
36 25 'wrap_text',
37 26 'html2text',
38 27 'add_anchor',
39 28 'strip_dollars',
40 29 'strip_files_prefix',
41 30 'comment_lines',
42 31 'get_lines',
43 32 'ipython2python',
44 33 'posix_path',
45 34 'path2url',
46 35 'add_prompts',
47 36 'ascii_only',
48 37 'prevent_list_blocks',
49 38 ]
50 39
51 40
52 41 def wrap_text(text, width=100):
53 42 """
54 43 Intelligently wrap text.
55 44 Wrap text without breaking words if possible.
56 45
57 46 Parameters
58 47 ----------
59 48 text : str
60 49 Text to wrap.
61 50 width : int, optional
62 51 Number of characters to wrap to, default 100.
63 52 """
64 53
65 54 split_text = text.split('\n')
66 55 wrp = map(lambda x:textwrap.wrap(x,width), split_text)
67 56 wrpd = map('\n'.join, wrp)
68 57 return '\n'.join(wrpd)
69 58
70 59
71 60 def html2text(element):
72 61 """extract inner text from html
73 62
74 63 Analog of jQuery's $(element).text()
75 64 """
76 65 if isinstance(element, py3compat.string_types):
77 66 try:
78 67 element = ElementTree.fromstring(element)
79 68 except Exception:
80 69 # failed to parse, just return it unmodified
81 70 return element
82 71
83 72 text = element.text or ""
84 73 for child in element:
85 74 text += html2text(child)
86 75 text += (element.tail or "")
87 76 return text
88 77
89 78
90 79 def add_anchor(html):
91 """Add an anchor-link to an html header tag
80 """Add an anchor-link to an html header
92 81
93 For use in heading cells
82 For use on markdown headings
94 83 """
95 84 try:
96 85 h = ElementTree.fromstring(py3compat.cast_bytes_py2(html, encoding='utf-8'))
97 86 except Exception:
98 87 # failed to parse, just return it unmodified
99 88 return html
100 89 link = html2text(h).replace(' ', '-')
101 90 h.set('id', link)
102 91 a = ElementTree.Element("a", {"class" : "anchor-link", "href" : "#" + link})
103 92 a.text = u'¶'
104 93 h.append(a)
105 94
106 95 # Known issue of Python3.x, ElementTree.tostring() returns a byte string
107 96 # instead of a text string. See issue http://bugs.python.org/issue10942
108 97 # Workaround is to make sure the bytes are casted to a string.
109 98 return py3compat.decode(ElementTree.tostring(h), 'utf-8')
110 99
111 100
112 101 def add_prompts(code, first='>>> ', cont='... '):
113 102 """Add prompts to code snippets"""
114 103 new_code = []
115 104 code_list = code.split('\n')
116 105 new_code.append(first + code_list[0])
117 106 for line in code_list[1:]:
118 107 new_code.append(cont + line)
119 108 return '\n'.join(new_code)
120 109
121 110
122 111 def strip_dollars(text):
123 112 """
124 113 Remove all dollar symbols from text
125 114
126 115 Parameters
127 116 ----------
128 117 text : str
129 118 Text to remove dollars from
130 119 """
131 120
132 121 return text.strip('$')
133 122
134 123
135 124 files_url_pattern = re.compile(r'(src|href)\=([\'"]?)/?files/')
136 125 markdown_url_pattern = re.compile(r'(!?)\[(?P<caption>.*?)\]\(/?files/(?P<location>.*?)\)')
137 126
138 127 def strip_files_prefix(text):
139 128 """
140 129 Fix all fake URLs that start with `files/`, stripping out the `files/` prefix.
141 130 Applies to both urls (for html) and relative paths (for markdown paths).
142 131
143 132 Parameters
144 133 ----------
145 134 text : str
146 135 Text in which to replace 'src="files/real...' with 'src="real...'
147 136 """
148 137 cleaned_text = files_url_pattern.sub(r"\1=\2", text)
149 138 cleaned_text = markdown_url_pattern.sub(r'\1[\2](\3)', cleaned_text)
150 139 return cleaned_text
151 140
152 141
153 142 def comment_lines(text, prefix='# '):
154 143 """
155 144 Build a Python comment line from input text.
156 145
157 146 Parameters
158 147 ----------
159 148 text : str
160 149 Text to comment out.
161 150 prefix : str
162 151 Character to append to the start of each line.
163 152 """
164 153
165 154 #Replace line breaks with line breaks and comment symbols.
166 155 #Also add a comment symbol at the beginning to comment out
167 156 #the first line.
168 157 return prefix + ('\n'+prefix).join(text.split('\n'))
169 158
170 159
171 160 def get_lines(text, start=None,end=None):
172 161 """
173 162 Split the input text into separate lines and then return the
174 163 lines that the caller is interested in.
175 164
176 165 Parameters
177 166 ----------
178 167 text : str
179 168 Text to parse lines from.
180 169 start : int, optional
181 170 First line to grab from.
182 171 end : int, optional
183 172 Last line to grab from.
184 173 """
185 174
186 175 # Split the input into lines.
187 176 lines = text.split("\n")
188 177
189 178 # Return the right lines.
190 179 return "\n".join(lines[start:end]) #re-join
191 180
192 181 def ipython2python(code):
193 182 """Transform IPython syntax to pure Python syntax
194 183
195 184 Parameters
196 185 ----------
197 186
198 187 code : str
199 188 IPython code, to be transformed to pure Python
200 189 """
201 190 shell = InteractiveShell.instance()
202 191 return shell.input_transformer_manager.transform_cell(code)
203 192
204 193 def posix_path(path):
205 194 """Turn a path into posix-style path/to/etc
206 195
207 196 Mainly for use in latex on Windows,
208 197 where native Windows paths are not allowed.
209 198 """
210 199 if os.path.sep != '/':
211 200 return path.replace(os.path.sep, '/')
212 201 return path
213 202
214 203 def path2url(path):
215 204 """Turn a file path into a URL"""
216 205 parts = path.split(os.path.sep)
217 206 return '/'.join(quote(part) for part in parts)
218 207
219 208 def ascii_only(s):
220 209 """ensure a string is ascii"""
221 210 s = py3compat.cast_unicode(s)
222 211 return s.encode('ascii', 'replace').decode('ascii')
223 212
224 213 def prevent_list_blocks(s):
225 214 """
226 215 Prevent presence of enumerate or itemize blocks in latex headings cells
227 216 """
228 217 out = re.sub('(^\s*\d*)\.', '\\1\.', s)
229 218 out = re.sub('(^\s*)\-', '\\1\-', out)
230 219 out = re.sub('(^\s*)\+', '\\1\+', out)
231 220 out = re.sub('(^\s*)\*', '\\1\*', out)
232 221 return out
@@ -1,129 +1,143 b''
1 # coding: utf-8
1 2 """Tests for conversions from markdown to other formats"""
2 3
3 4 # Copyright (c) IPython Development Team.
4 5 # Distributed under the terms of the Modified BSD License.
5 6
6 7 from copy import copy
7 8
8 9 from IPython.utils.py3compat import string_types
9 10 from IPython.testing import decorators as dec
10 11
11 12 from ...tests.base import TestsBase
12 13 from ..markdown import markdown2latex, markdown2html, markdown2rst
13 14
14 15 from jinja2 import Environment
15 16
16 17 class TestMarkdown(TestsBase):
17 18
18 19 tests = [
19 20 '*test',
20 21 '**test',
21 22 '*test*',
22 23 '_test_',
23 24 '__test__',
24 25 '__*test*__',
25 26 '**test**',
26 27 '#test',
27 28 '##test',
28 29 'test\n----',
29 'test [link](https://google.com/)']
30 'test [link](https://google.com/)',
31 ]
30 32
31 33 tokens = [
32 34 '*test',
33 35 '**test',
34 36 'test',
35 37 'test',
36 38 'test',
37 39 'test',
38 40 'test',
39 41 'test',
40 42 'test',
41 43 'test',
42 ('test', 'https://google.com/')]
44 ('test', 'https://google.com/'),
45 ]
43 46
44 47
45 48 @dec.onlyif_cmds_exist('pandoc')
46 49 def test_markdown2latex(self):
47 50 """markdown2latex test"""
48 51 for index, test in enumerate(self.tests):
49 52 self._try_markdown(markdown2latex, test, self.tokens[index])
50 53
51 54 @dec.onlyif_cmds_exist('pandoc')
52 55 def test_markdown2latex_markup(self):
53 56 """markdown2latex with markup kwarg test"""
54 57 # This string should be passed through unaltered with pandoc's
55 58 # markdown_strict reader
56 59 s = '1) arabic number with parenthesis'
57 60 self.assertEqual(markdown2latex(s, markup='markdown_strict'), s)
58 61 # This string should be passed through unaltered with pandoc's
59 62 # markdown_strict+tex_math_dollars reader
60 63 s = '$\\alpha$ latex math'
61 64 self.assertEqual(
62 65 markdown2latex(s, markup='markdown_strict+tex_math_dollars'),
63 66 s)
64 67
65 68 @dec.onlyif_cmds_exist('pandoc')
66 69 def test_pandoc_extra_args(self):
67 70 # pass --no-wrap
68 71 s = '\n'.join([
69 72 "#latex {{long_line | md2l('markdown', ['--no-wrap'])}}",
70 73 "#rst {{long_line | md2r(['--columns', '5'])}}",
71 74 ])
72 75 long_line = ' '.join(['long'] * 30)
73 76 env = Environment()
74 77 env.filters.update({
75 78 'md2l': markdown2latex,
76 79 'md2r': markdown2rst,
77 80 })
78 81 tpl = env.from_string(s)
79 82 rendered = tpl.render(long_line=long_line)
80 83 _, latex, rst = rendered.split('#')
81 84
82 85 self.assertEqual(latex.strip(), 'latex %s' % long_line)
83 86 self.assertEqual(rst.strip(), 'rst %s' % long_line.replace(' ', '\n'))
84 87
85 88 def test_markdown2html(self):
86 89 """markdown2html test"""
87 90 for index, test in enumerate(self.tests):
88 91 self._try_markdown(markdown2html, test, self.tokens[index])
89 92
93 def test_markdown2html_heading_anchors(self):
94 for md, tokens in [
95 ('# test',
96 ('<h1', '>test', 'id="test"', u'&#182;</a>', "anchor-link")
97 ),
98 ('###test head space',
99 ('<h3', '>test head space', 'id="test-head-space"', u'&#182;</a>', "anchor-link")
100 )
101 ]:
102 self._try_markdown(markdown2html, md, tokens)
103
90 104 def test_markdown2html_math(self):
91 105 # Mathematical expressions should be passed through unaltered
92 106 cases = [("\\begin{equation*}\n"
93 107 "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n"
94 108 "\\end{equation*}"),
95 109 ("$$\n"
96 110 "a = 1 *3* 5\n"
97 111 "$$"),
98 112 "$ a = 1 *3* 5 $",
99 113 ]
100 114 for case in cases:
101 115 self.assertIn(case, markdown2html(case))
102 116
103 117 def test_markdown2html_math_paragraph(self):
104 118 # https://github.com/ipython/ipython/issues/6724
105 119 a = """Water that is stored in $t$, $s_t$, must equal the storage content of the previous stage,
106 120 $s_{t-1}$, plus a stochastic inflow, $I_t$, minus what is being released in $t$, $r_t$.
107 121 With $s_0$ defined as the initial storage content in $t=1$, we have"""
108 122 self.assertIn(a, markdown2html(a))
109 123
110 124 @dec.onlyif_cmds_exist('pandoc')
111 125 def test_markdown2rst(self):
112 126 """markdown2rst test"""
113 127
114 128 #Modify token array for rst, escape asterik
115 129 tokens = copy(self.tokens)
116 130 tokens[0] = r'\*test'
117 131 tokens[1] = r'\*\*test'
118 132
119 133 for index, test in enumerate(self.tests):
120 134 self._try_markdown(markdown2rst, test, tokens[index])
121 135
122 136
123 137 def _try_markdown(self, method, test, tokens):
124 138 results = method(test)
125 139 if isinstance(tokens, string_types):
126 140 assert tokens in results
127 141 else:
128 142 for token in tokens:
129 143 assert token in results
@@ -1,203 +1,192 b''
1 1 {%- extends 'display_priority.tpl' -%}
2 2
3 3
4 4 {% block codecell %}
5 5 <div class="cell border-box-sizing code_cell rendered">
6 6 {{ super() }}
7 7 </div>
8 8 {%- endblock codecell %}
9 9
10 10 {% block input_group -%}
11 11 <div class="input">
12 12 {{ super() }}
13 13 </div>
14 14 {% endblock input_group %}
15 15
16 16 {% block output_group %}
17 17 <div class="output_wrapper">
18 18 <div class="output">
19 19 {{ super() }}
20 20 </div>
21 21 </div>
22 22 {% endblock output_group %}
23 23
24 24 {% block in_prompt -%}
25 25 <div class="prompt input_prompt">
26 26 {%- if cell.execution_count is defined -%}
27 27 In&nbsp;[{{ cell.execution_count|replace(None, "&nbsp;") }}]:
28 28 {%- else -%}
29 29 In&nbsp;[&nbsp;]:
30 30 {%- endif -%}
31 31 </div>
32 32 {%- endblock in_prompt %}
33 33
34 34 {% block empty_in_prompt -%}
35 35 <div class="prompt input_prompt">
36 36 </div>
37 37 {%- endblock empty_in_prompt %}
38 38
39 39 {#
40 40 output_prompt doesn't do anything in HTML,
41 41 because there is a prompt div in each output area (see output block)
42 42 #}
43 43 {% block output_prompt %}
44 44 {% endblock output_prompt %}
45 45
46 46 {% block input %}
47 47 <div class="inner_cell">
48 48 <div class="input_area">
49 49 {{ cell.source | highlight_code(metadata=cell.metadata) }}
50 50 </div>
51 51 </div>
52 52 {%- endblock input %}
53 53
54 54 {% block output %}
55 55 <div class="output_area">
56 56 {%- if output.output_type == 'execute_result' -%}
57 57 <div class="prompt output_prompt">
58 58 {%- if cell.execution_count is defined -%}
59 59 Out[{{ cell.execution_count|replace(None, "&nbsp;") }}]:
60 60 {%- else -%}
61 61 Out[&nbsp;]:
62 62 {%- endif -%}
63 63 {%- else -%}
64 64 <div class="prompt">
65 65 {%- endif -%}
66 66 </div>
67 67 {{ super() }}
68 68 </div>
69 69 {% endblock output %}
70 70
71 71 {% block markdowncell scoped %}
72 72 <div class="cell border-box-sizing text_cell rendered">
73 73 {{ self.empty_in_prompt() }}
74 74 <div class="inner_cell">
75 75 <div class="text_cell_render border-box-sizing rendered_html">
76 76 {{ cell.source | markdown2html | strip_files_prefix }}
77 77 </div>
78 78 </div>
79 79 </div>
80 80 {%- endblock markdowncell %}
81 81
82 {% block headingcell scoped %}
83 <div class="cell border-box-sizing text_cell rendered">
84 {{ self.empty_in_prompt() }}
85 <div class="inner_cell">
86 <div class="text_cell_render border-box-sizing rendered_html">
87 {{ ("#" * cell.level + cell.source) | replace('\n', ' ') | markdown2html | strip_files_prefix | add_anchor }}
88 </div>
89 </div>
90 </div>
91 {% endblock headingcell %}
92
93 82 {% block unknowncell scoped %}
94 83 unknown type {{ cell.type }}
95 84 {% endblock unknowncell %}
96 85
97 86 {% block execute_result -%}
98 87 {%- set extra_class="output_execute_result" -%}
99 88 {% block data_priority scoped %}
100 89 {{ super() }}
101 90 {% endblock %}
102 91 {%- set extra_class="" -%}
103 92 {%- endblock execute_result %}
104 93
105 94 {% block stream_stdout -%}
106 95 <div class="output_subarea output_stream output_stdout output_text">
107 96 <pre>
108 97 {{- output.text | ansi2html -}}
109 98 </pre>
110 99 </div>
111 100 {%- endblock stream_stdout %}
112 101
113 102 {% block stream_stderr -%}
114 103 <div class="output_subarea output_stream output_stderr output_text">
115 104 <pre>
116 105 {{- output.text | ansi2html -}}
117 106 </pre>
118 107 </div>
119 108 {%- endblock stream_stderr %}
120 109
121 110 {% block data_svg scoped -%}
122 111 <div class="output_svg output_subarea {{extra_class}}">
123 112 {%- if output.svg_filename %}
124 113 <img src="{{output.svg_filename | posix_path}}"
125 114 {%- else %}
126 115 {{ output.data['image/svg+xml'] }}
127 116 {%- endif %}
128 117 </div>
129 118 {%- endblock data_svg %}
130 119
131 120 {% block data_html scoped -%}
132 121 <div class="output_html rendered_html output_subarea {{extra_class}}">
133 122 {{ output.data['text/html'] }}
134 123 </div>
135 124 {%- endblock data_html %}
136 125
137 126 {% block data_png scoped %}
138 127 <div class="output_png output_subarea {{extra_class}}">
139 128 {%- if 'image/png' in output.metadata.get('filenames', {}) %}
140 129 <img src="{{output.metadata.filenames['image/png'] | posix_path}}"
141 130 {%- else %}
142 131 <img src="data:image/png;base64,{{ output.data['image/png'] }}"
143 132 {%- endif %}
144 133 {%- if 'width' in output.metadata.get('image/png', {}) %}
145 134 width={{output.metadata['png']['width']}}
146 135 {%- endif %}
147 136 {%- if 'height' in output.metadata.get('image/png', {}) %}
148 137 height={{output.metadata['png']['height']}}
149 138 {%- endif %}
150 139 >
151 140 </div>
152 141 {%- endblock data_png %}
153 142
154 143 {% block data_jpg scoped %}
155 144 <div class="output_jpeg output_subarea {{extra_class}}">
156 145 {%- if 'image/jpeg' in output.metadata.get('filenames', {}) %}
157 146 <img src="{{output.metadata.filenames['image/jpeg'] | posix_path}}"
158 147 {%- else %}
159 148 <img src="data:image/jpeg;base64,{{ output.data['image/jpeg'] }}"
160 149 {%- endif %}
161 150 {%- if 'width' in output.metadata.get('image/jpeg', {}) %}
162 151 width={{output.metadata['image/jpeg']['width']}}
163 152 {%- endif %}
164 153 {%- if 'height' in output.metadata.get('image/jpeg', {}) %}
165 154 height={{output.metadata['image/jpeg']['height']}}
166 155 {%- endif %}
167 156 >
168 157 </div>
169 158 {%- endblock data_jpg %}
170 159
171 160 {% block data_latex scoped %}
172 161 <div class="output_latex output_subarea {{extra_class}}">
173 162 {{ output.data['text/latex'] }}
174 163 </div>
175 164 {%- endblock data_latex %}
176 165
177 166 {% block error -%}
178 167 <div class="output_subarea output_text output_error">
179 168 <pre>
180 169 {{- super() -}}
181 170 </pre>
182 171 </div>
183 172 {%- endblock error %}
184 173
185 174 {%- block traceback_line %}
186 175 {{ line | ansi2html }}
187 176 {%- endblock traceback_line %}
188 177
189 178 {%- block data_text scoped %}
190 179 <div class="output_text output_subarea {{extra_class}}">
191 180 <pre>
192 181 {{- output.data['text/plain'] | ansi2html -}}
193 182 </pre>
194 183 </div>
195 184 {%- endblock -%}
196 185
197 186 {%- block data_javascript scoped %}
198 187 <div class="output_subarea output_javascript {{extra_class}}">
199 188 <script type="text/javascript">
200 189 {{ output.data['text/javascript'] }}
201 190 </script>
202 191 </div>
203 192 {%- endblock -%}
@@ -1,223 +1,202 b''
1 1 ((= Latex base template (must inherit)
2 2 This template builds upon the abstract template, adding common latex output
3 3 functions. Figures, data_text,
4 4 This template does not define a docclass, the inheriting class must define this.=))
5 5
6 6 ((*- extends 'display_priority.tplx' -*))
7 7
8 8 %===============================================================================
9 9 % Abstract overrides
10 10 %===============================================================================
11 11
12 12 ((* block header *))
13 13 ((* block docclass *))((* endblock docclass *))
14 14
15 15 ((* block packages *))
16 16 \usepackage{graphicx} % Used to insert images
17 17 \usepackage{adjustbox} % Used to constrain images to a maximum size
18 18 \usepackage{color} % Allow colors to be defined
19 19 \usepackage{enumerate} % Needed for markdown enumerations to work
20 20 \usepackage{geometry} % Used to adjust the document margins
21 21 \usepackage{amsmath} % Equations
22 22 \usepackage{amssymb} % Equations
23 23 \usepackage[mathletters]{ucs} % Extended unicode (utf-8) support
24 24 \usepackage[utf8x]{inputenc} % Allow utf-8 characters in the tex document
25 25 \usepackage{fancyvrb} % verbatim replacement that allows latex
26 26 \usepackage{grffile} % extends the file name processing of package graphics
27 27 % to support a larger range
28 28 % The hyperref package gives us a pdf with properly built
29 29 % internal navigation ('pdf bookmarks' for the table of contents,
30 30 % internal cross-reference links, web links for URLs, etc.)
31 31 \usepackage{hyperref}
32 32 \usepackage{longtable} % longtable support required by pandoc >1.10
33 33 \usepackage{booktabs} % table support for pandoc > 1.12.2
34 34 ((* endblock packages *))
35 35
36 36 ((* block definitions *))
37 37 \definecolor{orange}{cmyk}{0,0.4,0.8,0.2}
38 38 \definecolor{darkorange}{rgb}{.71,0.21,0.01}
39 39 \definecolor{darkgreen}{rgb}{.12,.54,.11}
40 40 \definecolor{myteal}{rgb}{.26, .44, .56}
41 41 \definecolor{gray}{gray}{0.45}
42 42 \definecolor{lightgray}{gray}{.95}
43 43 \definecolor{mediumgray}{gray}{.8}
44 44 \definecolor{inputbackground}{rgb}{.95, .95, .85}
45 45 \definecolor{outputbackground}{rgb}{.95, .95, .95}
46 46 \definecolor{traceback}{rgb}{1, .95, .95}
47 47 % ansi colors
48 48 \definecolor{red}{rgb}{.6,0,0}
49 49 \definecolor{green}{rgb}{0,.65,0}
50 50 \definecolor{brown}{rgb}{0.6,0.6,0}
51 51 \definecolor{blue}{rgb}{0,.145,.698}
52 52 \definecolor{purple}{rgb}{.698,.145,.698}
53 53 \definecolor{cyan}{rgb}{0,.698,.698}
54 54 \definecolor{lightgray}{gray}{0.5}
55 55
56 56 % bright ansi colors
57 57 \definecolor{darkgray}{gray}{0.25}
58 58 \definecolor{lightred}{rgb}{1.0,0.39,0.28}
59 59 \definecolor{lightgreen}{rgb}{0.48,0.99,0.0}
60 60 \definecolor{lightblue}{rgb}{0.53,0.81,0.92}
61 61 \definecolor{lightpurple}{rgb}{0.87,0.63,0.87}
62 62 \definecolor{lightcyan}{rgb}{0.5,1.0,0.83}
63 63
64 64 % commands and environments needed by pandoc snippets
65 65 % extracted from the output of `pandoc -s`
66 66 \DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
67 67 % Add ',fontsize=\small' for more characters per line
68 68 \newenvironment{Shaded}{}{}
69 69 \newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{{#1}}}}
70 70 \newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{{#1}}}
71 71 \newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
72 72 \newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
73 73 \newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
74 74 \newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
75 75 \newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
76 76 \newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{{#1}}}}
77 77 \newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{{#1}}}
78 78 \newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
79 79 \newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{{#1}}}
80 80 \newcommand{\RegionMarkerTok}[1]{{#1}}
81 81 \newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
82 82 \newcommand{\NormalTok}[1]{{#1}}
83 83
84 84 % Define a nice break command that doesn't care if a line doesn't already
85 85 % exist.
86 86 \def\br{\hspace*{\fill} \\* }
87 87 % Math Jax compatability definitions
88 88 \def\gt{>}
89 89 \def\lt{<}
90 90 % Document parameters
91 91 ((* block title *))\title{((( resources.metadata.name | ascii_only | escape_latex )))}((* endblock title *))
92 92 ((* block date *))((* endblock date *))
93 93 ((* block author *))((* endblock author *))
94 94 ((* endblock definitions *))
95 95
96 96 ((* block commands *))
97 97 % Prevent overflowing lines due to hard-to-break entities
98 98 \sloppy
99 99 % Setup hyperref package
100 100 \hypersetup{
101 101 breaklinks=true, % so long urls are correctly broken across lines
102 102 colorlinks=true,
103 103 urlcolor=blue,
104 104 linkcolor=darkorange,
105 105 citecolor=darkgreen,
106 106 }
107 107 % Slightly bigger margins than the latex defaults
108 108 ((* block margins *))
109 109 \geometry{verbose,tmargin=1in,bmargin=1in,lmargin=1in,rmargin=1in}
110 110 ((* endblock margins *))
111 111 ((* endblock commands *))
112 112 ((* endblock header *))
113 113
114 114 ((* block body *))
115 115 \begin{document}
116 116
117 117 ((* block predoc *))
118 118 ((* block maketitle *))\maketitle((* endblock maketitle *))
119 119 ((* block abstract *))((* endblock abstract *))
120 120 ((* endblock predoc *))
121 121
122 122 ((( super() )))
123 123
124 124 % Add a bibliography block to the postdoc
125 125 ((* block postdoc *))
126 126 ((* block bibliography *))((* endblock bibliography *))
127 127 ((* endblock postdoc *))
128 128 \end{document}
129 129 ((* endblock body *))
130 130
131 131 %===============================================================================
132 132 % Support blocks
133 133 %===============================================================================
134 134
135 135 % Displaying simple data text
136 136 ((* block data_text *))
137 137 \begin{verbatim}
138 138 ((( output.data['text/plain'] )))
139 139 \end{verbatim}
140 140 ((* endblock data_text *))
141 141
142 142 % Display python error text as-is
143 143 ((* block error *))
144 144 \begin{Verbatim}[commandchars=\\\{\}]
145 145 ((( super() )))
146 146 \end{Verbatim}
147 147 ((* endblock error *))
148 148 ((* block traceback_line *))
149 149 ((( line | indent | strip_ansi | escape_latex )))
150 150 ((* endblock traceback_line *))
151 151
152 152 % Display stream ouput with coloring
153 153 ((* block stream *))
154 154 \begin{Verbatim}[commandchars=\\\{\}]
155 155 ((( output.text | escape_latex | ansi2latex )))
156 156 \end{Verbatim}
157 157 ((* endblock stream *))
158 158
159 159 % Display latex
160 160 ((* block data_latex -*))
161 161 ((*- if output.data['text/latex'].startswith('$'): -*))
162 162 ((= Replace $ symbols with more explicit, equation block. =))
163 163 \begin{equation*}\adjustbox{max width=\hsize}{$
164 164 ((( output.data['text/latex'] | strip_dollars )))
165 165 $}\end{equation*}
166 166 ((*- else -*))
167 167 ((( output.data['text/latex'] )))
168 168 ((*- endif *))
169 169 ((* endblock data_latex *))
170 170
171 171 % Default mechanism for rendering figures
172 172 ((*- block data_png -*))((( draw_figure(output.metadata.filenames['image/png']) )))((*- endblock -*))
173 173 ((*- block data_jpg -*))((( draw_figure(output.metadata.filenames['image/jpeg']) )))((*- endblock -*))
174 174 ((*- block data_svg -*))((( draw_figure(output.metadata.filenames['image/svg+xml']) )))((*- endblock -*))
175 175 ((*- block data_pdf -*))((( draw_figure(output.metadata.filenames['application/pdf']) )))((*- endblock -*))
176 176
177 177 % Draw a figure using the graphicx package.
178 178 ((* macro draw_figure(filename) -*))
179 179 ((* set filename = filename | posix_path *))
180 180 ((*- block figure scoped -*))
181 181 \begin{center}
182 182 \adjustimage{max size={0.9\linewidth}{0.9\paperheight}}{((( filename )))}
183 183 \end{center}
184 184 { \hspace*{\fill} \\}
185 185 ((*- endblock figure -*))
186 186 ((*- endmacro *))
187 187
188 % Draw heading cell. Explicitly map different cell levels.
189 ((* block headingcell scoped *))
190
191 ((* if cell.level == 1 -*))
192 ((* block h1 -*))\section((* endblock h1 -*))
193 ((* elif cell.level == 2 -*))
194 ((* block h2 -*))\subsection((* endblock h2 -*))
195 ((* elif cell.level == 3 -*))
196 ((* block h3 -*))\subsubsection((* endblock h3 -*))
197 ((* elif cell.level == 4 -*))
198 ((* block h4 -*))\paragraph((* endblock h4 -*))
199 ((* elif cell.level == 5 -*))
200 ((* block h5 -*))\subparagraph((* endblock h5 -*))
201 ((* elif cell.level == 6 -*))
202 ((* block h6 -*))\\*\textit((* endblock h6 -*))
203 ((*- endif -*))
204 {((( cell.source | replace('\n', ' ') | citation2latex | strip_files_prefix | prevent_list_blocks | markdown2latex(markup='markdown_strict+tex_math_dollars') )))}
205
206
207 ((* endblock headingcell *))
208
209 188 % Redirect execute_result to display data priority.
210 189 ((* block execute_result scoped *))
211 190 ((* block data_priority scoped *))
212 191 ((( super() )))
213 192 ((* endblock *))
214 193 ((* endblock execute_result *))
215 194
216 195 % Render markdown
217 196 ((* block markdowncell scoped *))
218 197 ((( cell.source | citation2latex | strip_files_prefix | markdown2latex )))
219 198 ((* endblock markdowncell *))
220 199
221 200 % Don't display unknown types
222 201 ((* block unknowncell scoped *))
223 202 ((* endblock unknowncell *))
@@ -1,96 +1,93 b''
1 1 ((= Auto-generated template file, DO NOT edit directly!
2 2 To edit this file, please refer to ../../skeleton/README.md =))
3 3
4 4
5 5 ((=
6 6
7 7 DO NOT USE THIS AS A BASE,
8 8 IF YOU ARE COPY AND PASTING THIS FILE
9 9 YOU ARE PROBABLY DOING THINGS INCORRECTLY.
10 10
11 11 Null template, does nothing except defining a basic structure
12 12 To layout the different blocks of a notebook.
13 13
14 14 Subtemplates can override blocks to define their custom representation.
15 15
16 16 If one of the block you do overwrite is not a leave block, consider
17 17 calling super.
18 18
19 19 ((*- block nonLeaveBlock -*))
20 20 #add stuff at beginning
21 21 ((( super() )))
22 22 #add stuff at end
23 23 ((*- endblock nonLeaveBlock -*))
24 24
25 25 consider calling super even if it is a leave block, we might insert more blocks later.
26 26
27 27 =))
28 28 ((*- block header -*))
29 29 ((*- endblock header -*))
30 30 ((*- block body -*))
31 31 ((*- for cell in nb.cells -*))
32 32 ((*- block any_cell scoped -*))
33 33 ((*- if cell.cell_type == 'code' -*))
34 34 ((*- block codecell scoped -*))
35 35 ((*- block input_group -*))
36 36 ((*- block in_prompt -*))((*- endblock in_prompt -*))
37 37 ((*- block input -*))((*- endblock input -*))
38 38 ((*- endblock input_group -*))
39 39 ((*- if cell.outputs -*))
40 40 ((*- block output_group -*))
41 41 ((*- block output_prompt -*))((*- endblock output_prompt -*))
42 42 ((*- block outputs scoped -*))
43 43 ((*- for output in cell.outputs -*))
44 44 ((*- block output scoped -*))
45 45 ((*- if output.output_type == 'execute_result' -*))
46 46 ((*- block execute_result scoped -*))((*- endblock execute_result -*))
47 47 ((*- elif output.output_type == 'stream' -*))
48 48 ((*- block stream scoped -*))
49 49 ((*- if output.name == 'stdout' -*))
50 50 ((*- block stream_stdout scoped -*))
51 51 ((*- endblock stream_stdout -*))
52 52 ((*- elif output.name == 'stderr' -*))
53 53 ((*- block stream_stderr scoped -*))
54 54 ((*- endblock stream_stderr -*))
55 55 ((*- endif -*))
56 56 ((*- endblock stream -*))
57 57 ((*- elif output.output_type == 'display_data' -*))
58 58 ((*- block display_data scoped -*))
59 59 ((*- block data_priority scoped -*))
60 60 ((*- endblock data_priority -*))
61 61 ((*- endblock display_data -*))
62 62 ((*- elif output.output_type == 'error' -*))
63 63 ((*- block error scoped -*))
64 64 ((*- for line in output.traceback -*))
65 65 ((*- block traceback_line scoped -*))((*- endblock traceback_line -*))
66 66 ((*- endfor -*))
67 67 ((*- endblock error -*))
68 68 ((*- endif -*))
69 69 ((*- endblock output -*))
70 70 ((*- endfor -*))
71 71 ((*- endblock outputs -*))
72 72 ((*- endblock output_group -*))
73 73 ((*- endif -*))
74 74 ((*- endblock codecell -*))
75 75 ((*- elif cell.cell_type in ['markdown'] -*))
76 76 ((*- block markdowncell scoped-*))
77 77 ((*- endblock markdowncell -*))
78 ((*- elif cell.cell_type in ['heading'] -*))
79 ((*- block headingcell scoped-*))
80 ((*- endblock headingcell -*))
81 78 ((*- elif cell.cell_type in ['raw'] -*))
82 79 ((*- block rawcell scoped -*))
83 80 ((* if cell.metadata.get('raw_mimetype', '').lower() in resources.get('raw_mimetypes', ['']) *))
84 81 ((( cell.source )))
85 82 ((* endif *))
86 83 ((*- endblock rawcell -*))
87 84 ((*- else -*))
88 85 ((*- block unknowncell scoped-*))
89 86 ((*- endblock unknowncell -*))
90 87 ((*- endif -*))
91 88 ((*- endblock any_cell -*))
92 89 ((*- endfor -*))
93 90 ((*- endblock body -*))
94 91
95 92 ((*- block footer -*))
96 93 ((*- endblock footer -*))
@@ -1,68 +1,63 b''
1 1 {% extends 'display_priority.tpl' %}
2 2
3 3
4 4 {% block in_prompt %}
5 5 {% endblock in_prompt %}
6 6
7 7 {% block output_prompt %}
8 8 {%- endblock output_prompt %}
9 9
10 10 {% block input %}
11 11 {{ cell.source | indent(4)}}
12 12 {% endblock input %}
13 13
14 14 {% block error %}
15 15 {{ super() }}
16 16 {% endblock error %}
17 17
18 18 {% block traceback_line %}
19 19 {{ line | indent | strip_ansi }}
20 20 {% endblock traceback_line %}
21 21
22 22 {% block execute_result %}
23 23
24 24 {% block data_priority scoped %}
25 25 {{ super() }}
26 26 {% endblock %}
27 27 {% endblock execute_result %}
28 28
29 29 {% block stream %}
30 30 {{ output.text | indent }}
31 31 {% endblock stream %}
32 32
33 33 {% block data_svg %}
34 34 ![svg]({{ output.svg_filename | path2url }})
35 35 {% endblock data_svg %}
36 36
37 37 {% block data_png %}
38 38 ![png]({{ output.metadata.filenames['image/png'] | path2url }})
39 39 {% endblock data_png %}
40 40
41 41 {% block data_jpg %}
42 42 ![jpeg]({{ output.metadata.filenames['image/jpeg'] | path2url }})
43 43 {% endblock data_jpg %}
44 44
45 45 {% block data_latex %}
46 46 {{ output.data['text/latex'] }}
47 47 {% endblock data_latex %}
48 48
49 49 {% block data_html scoped %}
50 50 {{ output.data['text/html'] }}
51 51 {% endblock data_html %}
52 52
53 53 {% block data_text scoped %}
54 54 {{ output.data['text/plain'] | indent }}
55 55 {% endblock data_text %}
56 56
57 57 {% block markdowncell scoped %}
58 58 {{ cell.source }}
59 59 {% endblock markdowncell %}
60 60
61
62 {% block headingcell scoped %}
63 {{ '#' * cell.level }} {{ cell.source | replace('\n', ' ') }}
64 {% endblock headingcell %}
65
66 61 {% block unknowncell scoped %}
67 62 unknown type {{ cell.type }}
68 63 {% endblock unknowncell %} No newline at end of file
@@ -1,21 +1,17 b''
1 1 {%- extends 'null.tpl' -%}
2 2
3 3 {% block header %}
4 4 # coding: utf-8
5 5 {% endblock header %}
6 6
7 7 {% block in_prompt %}
8 8 # In[{{ cell.execution_count if cell.execution_count else ' ' }}]:
9 9 {% endblock in_prompt %}
10 10
11 11 {% block input %}
12 12 {{ cell.source | ipython2python }}
13 13 {% endblock input %}
14 14
15 15 {% block markdowncell scoped %}
16 16 {{ cell.source | comment_lines }}
17 17 {% endblock markdowncell %}
18
19 {% block headingcell scoped %}
20 {{ '#' * cell.level }}{{ cell.source | replace('\n', ' ') | comment_lines }}
21 {% endblock headingcell %}
@@ -1,92 +1,89 b''
1 1 {#
2 2
3 3 DO NOT USE THIS AS A BASE,
4 4 IF YOU ARE COPY AND PASTING THIS FILE
5 5 YOU ARE PROBABLY DOING THINGS INCORRECTLY.
6 6
7 7 Null template, does nothing except defining a basic structure
8 8 To layout the different blocks of a notebook.
9 9
10 10 Subtemplates can override blocks to define their custom representation.
11 11
12 12 If one of the block you do overwrite is not a leave block, consider
13 13 calling super.
14 14
15 15 {%- block nonLeaveBlock -%}
16 16 #add stuff at beginning
17 17 {{ super() }}
18 18 #add stuff at end
19 19 {%- endblock nonLeaveBlock -%}
20 20
21 21 consider calling super even if it is a leave block, we might insert more blocks later.
22 22
23 23 #}
24 24 {%- block header -%}
25 25 {%- endblock header -%}
26 26 {%- block body -%}
27 27 {%- for cell in nb.cells -%}
28 28 {%- block any_cell scoped -%}
29 29 {%- if cell.cell_type == 'code' -%}
30 30 {%- block codecell scoped -%}
31 31 {%- block input_group -%}
32 32 {%- block in_prompt -%}{%- endblock in_prompt -%}
33 33 {%- block input -%}{%- endblock input -%}
34 34 {%- endblock input_group -%}
35 35 {%- if cell.outputs -%}
36 36 {%- block output_group -%}
37 37 {%- block output_prompt -%}{%- endblock output_prompt -%}
38 38 {%- block outputs scoped -%}
39 39 {%- for output in cell.outputs -%}
40 40 {%- block output scoped -%}
41 41 {%- if output.output_type == 'execute_result' -%}
42 42 {%- block execute_result scoped -%}{%- endblock execute_result -%}
43 43 {%- elif output.output_type == 'stream' -%}
44 44 {%- block stream scoped -%}
45 45 {%- if output.name == 'stdout' -%}
46 46 {%- block stream_stdout scoped -%}
47 47 {%- endblock stream_stdout -%}
48 48 {%- elif output.name == 'stderr' -%}
49 49 {%- block stream_stderr scoped -%}
50 50 {%- endblock stream_stderr -%}
51 51 {%- endif -%}
52 52 {%- endblock stream -%}
53 53 {%- elif output.output_type == 'display_data' -%}
54 54 {%- block display_data scoped -%}
55 55 {%- block data_priority scoped -%}
56 56 {%- endblock data_priority -%}
57 57 {%- endblock display_data -%}
58 58 {%- elif output.output_type == 'error' -%}
59 59 {%- block error scoped -%}
60 60 {%- for line in output.traceback -%}
61 61 {%- block traceback_line scoped -%}{%- endblock traceback_line -%}
62 62 {%- endfor -%}
63 63 {%- endblock error -%}
64 64 {%- endif -%}
65 65 {%- endblock output -%}
66 66 {%- endfor -%}
67 67 {%- endblock outputs -%}
68 68 {%- endblock output_group -%}
69 69 {%- endif -%}
70 70 {%- endblock codecell -%}
71 71 {%- elif cell.cell_type in ['markdown'] -%}
72 72 {%- block markdowncell scoped-%}
73 73 {%- endblock markdowncell -%}
74 {%- elif cell.cell_type in ['heading'] -%}
75 {%- block headingcell scoped-%}
76 {%- endblock headingcell -%}
77 74 {%- elif cell.cell_type in ['raw'] -%}
78 75 {%- block rawcell scoped -%}
79 76 {% if cell.metadata.get('raw_mimetype', '').lower() in resources.get('raw_mimetypes', ['']) %}
80 77 {{ cell.source }}
81 78 {% endif %}
82 79 {%- endblock rawcell -%}
83 80 {%- else -%}
84 81 {%- block unknowncell scoped-%}
85 82 {%- endblock unknowncell -%}
86 83 {%- endif -%}
87 84 {%- endblock any_cell -%}
88 85 {%- endfor -%}
89 86 {%- endblock body -%}
90 87
91 88 {%- block footer -%}
92 89 {%- endblock footer -%}
@@ -1,190 +1,189 b''
1 1 {
2 2 "cells": [
3 3 {
4 "cell_type": "heading",
5 "level": 1,
4 "cell_type": "markdown",
6 5 "metadata": {},
7 6 "source": [
8 "A simple SymPy example"
7 "# A simple SymPy example"
9 8 ]
10 9 },
11 10 {
12 11 "cell_type": "markdown",
13 12 "metadata": {},
14 13 "source": [
15 14 "First we import SymPy and initialize printing:"
16 15 ]
17 16 },
18 17 {
19 18 "cell_type": "code",
20 19 "execution_count": 2,
21 20 "metadata": {
22 21 "collapsed": false
23 22 },
24 23 "outputs": [],
25 24 "source": [
26 25 "from sympy import init_printing\n",
27 26 "from sympy import *\n",
28 27 " init_printing()"
29 28 ]
30 29 },
31 30 {
32 31 "cell_type": "markdown",
33 32 "metadata": {},
34 33 "source": [
35 34 "Create a few symbols:"
36 35 ]
37 36 },
38 37 {
39 38 "cell_type": "code",
40 39 "execution_count": 4,
41 40 "metadata": {
42 41 "collapsed": false
43 42 },
44 43 "outputs": [],
45 44 "source": [
46 45 "x,y,z = symbols('x y z')"
47 46 ]
48 47 },
49 48 {
50 49 "cell_type": "markdown",
51 50 "metadata": {},
52 51 "source": [
53 52 "Here is a basic expression:"
54 53 ]
55 54 },
56 55 {
57 56 "cell_type": "code",
58 57 "execution_count": 6,
59 58 "metadata": {
60 59 "collapsed": false
61 60 },
62 61 "outputs": [
63 62 {
64 63 "data": {
65 64 "image/png": [
66 65 "iVBORw0KGgoAAAANSUhEUgAAAKMAAAAZBAMAAACvE4OgAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\n",
67 66 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHarIkSJZt3NVLsy\n",
68 67 "me8Q6PJIAAACz0lEQVRIDa1UTWjUQBT+ZpvdzW7TGlrxItjYSg/C6vbiDwjmoCgUpHioPYhdqig9\n",
69 68 "FJYiPYmW4klB14NgFGnw4EHpj7UgUtTFXhSEBgVBxIOFggWVrrUqiMY3mZkkLNIK7oN575vvvfky\n",
70 69 "8yYJIGzgkSlRrULKrivVSkvq6LbxtcaSjV3aSo0lgWyl5pK69V+SRlEsPxNTGYhhDrV3M2Ue2etc\n",
71 70 "EDmuMmM+IjolrCuHXNoLoQDNSAXdzbjsfFVKTY1vCgFXFIxenG4cFSSzRewAPnN0FugXjPDr45MQ\n",
72 71 "JwoKtitgXL9zT+CsJeIHYG+Z4H1gwhRU4G/FcAQbbYU3KdDo+0sCK8lRU0guA72uKqMYk9RehHxP\n",
73 72 "iDIu0NS2v90KGShJYi7T7tgvkrQ2vIT2XtRISWNra6lzGc8/PW3ji4PL7Vmge095YIX0iB71NCaZ\n",
74 73 "5N3XyM0VCuNIyFNIyY3AMG/KDUvjn90DGmwq9wpIl5AyU5WsTYy0aJf6JFGB5An3Der5jExKHjNR\n",
75 74 "4JKPge/EXqDBoOXpkxkmkJHFfAFRVhDIveWA0S57N2Me6yw+DSX1n1uCq3sIfCF2IcjNkjeWyKli\n",
76 75 "ginHubboOB4vSNAjyaiXE26ygrkyTfod55Lj3CTE+n2P73ImJpnk6wJJKjYJSwt3OQbNJu4icM5s\n",
77 76 "KGGbzMuD70N6JSbJD44x7pLDyJrbkfiLpOEhYVMJSVEj83x5YFLyNrAzJsmvJ+uhLrieXvcJDshy\n",
78 77 "HtQuD54c2IWWEnSXfUTDZJJfAjcpOW5imp9aHvw4ZZ4NDV4FGjw0tzadKgbFwinJUd//AT0P1tdW\n",
79 78 "BtuRU39oKdk9ONQ163fM+nvu/s4D/FX30otdQIZGlSnJKpq6KUxKVqV1WxGHFIhishjhEO1Gi3r4\n",
80 79 "kZCMg+hH1henV8EjmFoly1PTMs/Uadaox+FceY2STpmvt9co/Pe0Jvt1GvgDK/Osw/4jQ4wAAAAA\n",
81 80 "SUVORK5CYII=\n"
82 81 ],
83 82 "text/latex": [
84 83 "$$x^{2} + 2.0 y + \\sin{\\left (z \\right )}$$"
85 84 ],
86 85 "text/plain": [
87 86 " 2 \n",
88 87 "x + 2.0\u22c5y + sin(z)"
89 88 ]
90 89 },
91 90 "execution_count": 6,
92 91 "metadata": {},
93 92 "output_type": "execute_result"
94 93 }
95 94 ],
96 95 "source": [
97 96 "e = x**2 + 2.0*y + sin(z); e"
98 97 ]
99 98 },
100 99 {
101 100 "cell_type": "code",
102 101 "execution_count": 7,
103 102 "metadata": {
104 103 "collapsed": false
105 104 },
106 105 "outputs": [
107 106 {
108 107 "data": {
109 108 "image/png": [
110 109 "iVBORw0KGgoAAAANSUhEUgAAABQAAAAOBAMAAADd6iHDAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\n",
111 110 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIpm7MhCriUTv3c12\n",
112 111 "VGZoascqAAAAgElEQVQIHWNgVDJ2YICAMAb2H1BmKgPDTChzFgNDvgOEvT8AzgQKrA9gPZPYUwNk\n",
113 112 "cXxnCGd4dWA1kMllwFDKUB9wEchUZmAIYNgMZDDwJIDIPyDiEgOjAAPLFwZWBhYFBh6BqzwfGI4y\n",
114 113 "SJUXZXH8Zf7A+IBh////v1hzjh5/xwAAW80hUDE8HYkAAAAASUVORK5CYII=\n"
115 114 ],
116 115 "text/latex": [
117 116 "$$2 x$$"
118 117 ],
119 118 "text/plain": [
120 119 "2\u22c5x"
121 120 ]
122 121 },
123 122 "execution_count": 7,
124 123 "metadata": {},
125 124 "output_type": "execute_result"
126 125 }
127 126 ],
128 127 "source": [
129 128 "diff(e, x)"
130 129 ]
131 130 },
132 131 {
133 132 "cell_type": "code",
134 133 "execution_count": 8,
135 134 "metadata": {
136 135 "collapsed": false
137 136 },
138 137 "outputs": [
139 138 {
140 139 "data": {
141 140 "image/png": [
142 141 "iVBORw0KGgoAAAANSUhEUgAAALsAAAAZBAMAAACbakK8AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\n",
143 142 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAEHarIkSJZt3NVLsy\n",
144 143 "me8Q6PJIAAADAklEQVRIDbVVS2gTURQ90/wmk0k6tCJCsR1SKShIsxE3CgNWBKUxq9qFmqFqShfF\n",
145 144 "UKQrkaDiF0pcCKYgBBcuBLV+wIWKARe6kQ4UhNKKWdiF4KIptmA/xPvmzZuMxdYUzIPcd+655568\n",
146 145 "vLlJAL6G32oOasQWNHz5Rvg6nrKh/mygfSzlX2ygPaBUGmov6//NXs1yq4sex2EPrsHemTd2snNg\n",
147 146 "tkb+Cx1zBL6SqwxZLvQAKYHzKZaPY4fh4TeHd0S5Nox9OClItm/jiU9DrEwwVEawpiVis9VkimqX\n",
148 147 "AOr4o2cCs/0BT2I5+FYJRhJbePQxgzcD7QLEqtV5gdnu2Icr3L45gcCyt74Z7neL4SLQ0nm4S+dM\n",
149 148 "YCz1gSPHnhKZDWyHhcCCNKwjqaF/TkwGl0L6nClie/wc1D1xdoNsSLhT0IJkhi7Lzr22xb8keE/N\n",
150 149 "Pm0Sc9yEuhRUyuiG9HzvFNeImCyq39SriOhtQI7IV/TiTqE8glqwohjE0NJwiANxOZTdZoxtfzSa\n",
151 150 "x2tI8DtHcKQoQFmV6f1XT2swibxFL+6k5EgenhBCqKLTPX3ULnaYdDlaTMcCSd8zuXTvBq2bJUJr\n",
152 151 "lE4WgSV5ZRdBzLFgO6nzhJp1ltvrlB2HCoWxQuG+jTvt2GxBWUZaU2mMApZNuSHA3vJpCliRhqqs\n",
153 152 "ZtvbTrb9ZIk+i70Ut1OcnpgeKskTCFUwjaYy8Jhr3eiefq0HIfa7yC6HOwVyULRuNDn21JngbcL+\n",
154 153 "E8A+MNnSxb+w59+Cj2tELJBbjEZr8SGwn0j2aLkTPdp08R2OcKV6fXB3ikPH3n8tM5WTfrETtZcw\n",
155 154 "g3QWH0dH7nKNiMkszqo/EDafaHhJ5Bm6ee4UtdAabxnMcmUUl0SnYx+uVqs5XAGN9QGgdeCrASv0\n",
156 155 "3TmCsJcOdhnozexD38goK9HXynEKr1OKDs9guhQD039kGySyIQpJAdbvJ9YTlPvyUl3/aLUf34G/\n",
157 156 "uGxIyXpE37DoLbAHwJaU53t9MRCfrU8o/k4iRn36Lar8Wd5wAfgN4R6xelyy/ssAAAAASUVORK5C\n",
158 157 "YII=\n"
159 158 ],
160 159 "text/latex": [
161 160 "$$x^{2} z + 2.0 y z - \\cos{\\left (z \\right )}$$"
162 161 ],
163 162 "text/plain": [
164 163 " 2 \n",
165 164 "x \u22c5z + 2.0\u22c5y\u22c5z - cos(z)"
166 165 ]
167 166 },
168 167 "execution_count": 8,
169 168 "metadata": {},
170 169 "output_type": "execute_result"
171 170 }
172 171 ],
173 172 "source": [
174 173 "integrate(e, z)"
175 174 ]
176 175 },
177 176 {
178 177 "cell_type": "code",
179 178 "execution_count": null,
180 179 "metadata": {
181 180 "collapsed": false
182 181 },
183 182 "outputs": [],
184 183 "source": []
185 184 }
186 185 ],
187 186 "metadata": {},
188 187 "nbformat": 4,
189 188 "nbformat_minor": 0
190 189 } No newline at end of file
@@ -1,1586 +1,1584 b''
1 1 {
2 2 "cells": [
3 3 {
4 "cell_type": "heading",
5 "level": 1,
4 "cell_type": "markdown",
6 5 "metadata": {},
7 6 "source": [
8 "NumPy and Matplotlib examples"
7 "# NumPy and Matplotlib examples"
9 8 ]
10 9 },
11 10 {
12 11 "cell_type": "markdown",
13 12 "metadata": {},
14 13 "source": [
15 14 "First import NumPy and Matplotlib:"
16 15 ]
17 16 },
18 17 {
19 18 "cell_type": "code",
20 19 "execution_count": 1,
21 20 "metadata": {
22 21 "collapsed": false
23 22 },
24 23 "outputs": [
25 24 {
26 25 "name": "stdout",
27 26 "output_type": "stream",
28 27 "text": [
29 28 "module://IPython.kernel.zmq.pylab.backend_inline\n"
30 29 ]
31 30 }
32 31 ],
33 32 "source": [
34 33 "%matplotlib inline\n",
35 34 "import matplotlib\n",
36 35 "import matplotlib.pyplot as plt\n",
37 36 "print(matplotlib.backends.backend)"
38 37 ]
39 38 },
40 39 {
41 40 "cell_type": "code",
42 41 "execution_count": 2,
43 42 "metadata": {
44 43 "collapsed": false
45 44 },
46 45 "outputs": [],
47 46 "source": [
48 47 "from IPython.display import set_matplotlib_formats\n",
49 48 "set_matplotlib_formats('png', 'pdf')\n",
50 49 "matplotlib.rcParams['figure.figsize'] = (2,1)"
51 50 ]
52 51 },
53 52 {
54 53 "cell_type": "code",
55 54 "execution_count": 3,
56 55 "metadata": {
57 56 "collapsed": false
58 57 },
59 58 "outputs": [
60 59 {
61 60 "data": {
62 61 "text/plain": [
63 62 "{matplotlib.figure.Figure: <function IPython.core.pylabtools.<lambda>>}"
64 63 ]
65 64 },
66 65 "execution_count": 3,
67 66 "metadata": {},
68 67 "output_type": "execute_result"
69 68 }
70 69 ],
71 70 "source": [
72 71 "ip.display_formatter.formatters['application/pdf'].type_printers"
73 72 ]
74 73 },
75 74 {
76 75 "cell_type": "code",
77 76 "execution_count": 4,
78 77 "metadata": {
79 78 "collapsed": false
80 79 },
81 80 "outputs": [],
82 81 "source": [
83 82 "import numpy as np"
84 83 ]
85 84 },
86 85 {
87 86 "cell_type": "markdown",
88 87 "metadata": {},
89 88 "source": [
90 89 "Now we show some very basic examples of how they can be used."
91 90 ]
92 91 },
93 92 {
94 93 "cell_type": "code",
95 94 "execution_count": 5,
96 95 "metadata": {
97 96 "collapsed": false
98 97 },
99 98 "outputs": [],
100 99 "source": [
101 100 "a = np.random.uniform(size=(100,100))"
102 101 ]
103 102 },
104 103 {
105 104 "cell_type": "code",
106 105 "execution_count": 6,
107 106 "metadata": {
108 107 "collapsed": false
109 108 },
110 109 "outputs": [
111 110 {
112 111 "data": {
113 112 "text/plain": [
114 113 "(100, 100)"
115 114 ]
116 115 },
117 116 "execution_count": 6,
118 117 "metadata": {},
119 118 "output_type": "execute_result"
120 119 }
121 120 ],
122 121 "source": [
123 122 "a.shape"
124 123 ]
125 124 },
126 125 {
127 126 "cell_type": "code",
128 127 "execution_count": 7,
129 128 "metadata": {
130 129 "collapsed": false
131 130 },
132 131 "outputs": [],
133 132 "source": [
134 133 "evs = np.linalg.eigvals(a)"
135 134 ]
136 135 },
137 136 {
138 137 "cell_type": "code",
139 138 "execution_count": 8,
140 139 "metadata": {
141 140 "collapsed": false
142 141 },
143 142 "outputs": [
144 143 {
145 144 "data": {
146 145 "text/plain": [
147 146 "(100,)"
148 147 ]
149 148 },
150 149 "execution_count": 8,
151 150 "metadata": {},
152 151 "output_type": "execute_result"
153 152 }
154 153 ],
155 154 "source": [
156 155 "evs.shape"
157 156 ]
158 157 },
159 158 {
160 "cell_type": "heading",
161 "level": 2,
159 "cell_type": "markdown",
162 160 "metadata": {},
163 161 "source": [
164 "Here is a very long heading that pandoc will wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap"
162 "## Here is a very long heading that pandoc will wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap"
165 163 ]
166 164 },
167 165 {
168 166 "cell_type": "markdown",
169 167 "metadata": {},
170 168 "source": [
171 169 "Here is a cell that has both text and PNG output:"
172 170 ]
173 171 },
174 172 {
175 173 "cell_type": "code",
176 174 "execution_count": 9,
177 175 "metadata": {
178 176 "collapsed": false
179 177 },
180 178 "outputs": [
181 179 {
182 180 "data": {
183 181 "text/plain": [
184 182 "(array([97, 2, 0, 0, 0, 0, 0, 0, 0, 1]),\n",
185 183 " array([ -2.59479443, 2.67371141, 7.94221725, 13.21072308,\n",
186 184 " 18.47922892, 23.74773476, 29.0162406 , 34.28474644,\n",
187 185 " 39.55325228, 44.82175812, 50.09026395]),\n",
188 186 " <a list of 10 Patch objects>)"
189 187 ]
190 188 },
191 189 "execution_count": 9,
192 190 "metadata": {},
193 191 "output_type": "execute_result"
194 192 },
195 193 {
196 194 "data": {
197 195 "application/pdf": [
198 196 "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1R5cGUgL0NhdGFsb2cgL1BhZ2VzIDIgMCBSID4+\n",
199 197 "CmVuZG9iago4IDAgb2JqCjw8IC9YT2JqZWN0IDcgMCBSIC9QYXR0ZXJuIDUgMCBSCi9Qcm9jU2V0\n",
200 198 "IFsgL1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdIC9FeHRHU3RhdGUgNCAwIFIK\n",
201 199 "L1NoYWRpbmcgNiAwIFIgL0ZvbnQgMyAwIFIgPj4KZW5kb2JqCjEwIDAgb2JqCjw8IC9Hcm91cCA8\n",
202 200 "PCAvQ1MgL0RldmljZVJHQiAvUyAvVHJhbnNwYXJlbmN5IC9UeXBlIC9Hcm91cCA+PiAvUGFyZW50\n",
203 201 "IDIgMCBSCi9NZWRpYUJveCBbIDAgMCAxNTIuMzk4NDM3NSA4Ny4xOTIxODc1IF0gL1Jlc291cmNl\n",
204 202 "cyA4IDAgUiAvVHlwZSAvUGFnZQovQ29udGVudHMgOSAwIFIgPj4KZW5kb2JqCjkgMCBvYmoKPDwg\n",
205 203 "L0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxMSAwIFIgPj4Kc3RyZWFtCnicxZi/btwwDMYz\n",
206 204 "a+wTCJ3aRSEpiZLGHtAG6FbkgL5AmwTBXYCmQ16/dJycRcWq7SV3k81Pf76fzyZpo703l1/Q3v61\n",
207 205 "ZL9bsE8W7ZUBOTpajOR8ycGnKOcHdZ6Tw0KY5fAgojq9Mw+yKA2Lgiv+9WdvZeCVoewCR6ZoCVyk\n",
208 206 "jHIkewVw0IYPdTix8y/hao0qKvveiHOhcF5t6qJgqWDPRRXlkwmzm92vHp1g8rYzzf5on8xuby+/\n",
209 207 "oUWw+xsjO2L0w+gk06Ld/zKfPn64wAv4bPf39uu+XeGZwITgAuSQCQMVZbtRlHWt1fYbZRNCIAdA\n",
210 208 "5F/mnjAWEFiiEb0vlGNSCI2iELRWIzTKJoQYXSglJU7PcyeEpf9BDFAJWDhQZgXRKApCazVEo2yC\n",
211 209 "SOiQ0efhCnANQQsQhRz5BNHHhEFBNIqC0FoN0SibICR5QCjxde4E4RcgELLDVAqCTPQ6nzSSwmjE\n",
212 210 "mqOVNoEgkCuFwuvkiSQskVBwSDTejKhJGkmTaFGRNNI2Esyu5PLyp9QkcYlkLpHr/K4J5jK8Gr/R\n",
213 211 "uQ9Sc8bxlW1esL1YD6BjermIrTGdHI8VAXm47sDr8unkz6Pj/Mb1FG1c18Nnw6tcyzM/bIahMryU\n",
214 212 "eCZzUkOk+rSWp2hjuR4+G15pGUazvjgPle+lJ3RyGIPUvje+p2jjux4+G159qSPIZpXl9fc0RykT\n",
215 213 "byxP0cZyPXw2vNby8yy5p6hynVe77vRaXKeDFemDNxVUuc6JXKqfQWkIJs9/ZpMColBaaSmyffxt\n",
216 214 "f9qHsZ12BFKZMbIUZxkbEBOHBCw20unEPk49atUtXxlhoITscwhNv5cdJ5TWC1TVO2ghBUkqYQRX\n",
217 215 "S1WC9Mw788O+J9S896ON0gXIxBDZqwp4aBUxFQb3puE9CefA6rk/Dk+NzJQcSZLgFZdSzH+IK+Xd\n",
218 216 "wXr2pW/1LnNhOaeowZRiusjnBevZP9o8ZK4i60pTrp8vpZgu8nnBevalSQfHsiYDSJekTCrFdJHP\n",
219 217 "C9azL2BFsn2W/MaQGrBaMV3kM4N17A+vI0k8JOZEgM2nESWZLvR50boAwoaylaTvBEneMzSbkkwf\n",
220 218 "+8xwPYLx7YtYXAafC2s4JRkpW5B5jtvW0gg3mk4+UZSmm9SHrBX9z/WKNxc9fsvXuu7w+ebt2ph/\n",
221 219 "ACMXFgplbmRzdHJlYW0KZW5kb2JqCjExIDAgb2JqCjg3MAplbmRvYmoKMTYgMCBvYmoKPDwgL0Zp\n",
222 220 "bHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aDEgMTkgMCBSIC9MZW5ndGggMjAgMCBSID4+CnN0cmVh\n",
223 221 "bQp4nNS9eYAdVZ0vfurudavq3rr7vvftvdOdTtIJi6QTsnQATdhCgsYECRAWIVFAwAUGWQL6JjDj\n",
224 222 "AEEQRJYAvsnNnbZZxjfkzVMExhl7HImgeZBRCIn6BnUcRudB8vt8zqm66QScef/+DqnvreXUWb6f\n",
225 223 "73pO2QpNCBEH8Ynq8iVLlwleawdAhLl81cozz//y80lcm0LoZyw/8+zFIiXCQvvNBj5feebw6M3v\n",
226 224 "9H4RL+zA9YbzP3ne5v959xPrhTj9QSEC2fOvvrIaXKRVhTj3RDyvXrj5ok9etDjUI8Q5T+Od0y+6\n",
227 225 "7NoLR5ee8b+EWLdJaGdt2LTxk9fkfn3RLCFCWSFWbN10wXkbn8ze+XPUHcT7Y5twIzjoXYPrjbju\n",
228 226 "2vTJK68pvnny6UJ4QujPvvSCT12+7NTTbxHarheEqC+57Irzz1v629+uRv8fE8Ib/uR512z23hfG\n",
229 227 "2LWtHM/l533ygq985Ml/EdqPMD/jbzdf8ekr77zla3cJ7fVpjGHW5k9dsPmkX106T4i1eK4NKd6I\n",
230 228 "xZenf/fv66Mn/psoeCWjPtds3cDf7994/87Daw/t8x7wfhOXuvAIVfCe98ChPyM9vPbwWryliaOK\n",
231 229 "dhPvgG4UQXE2sFDPw5iZ0B8B0YTXE9XuEH7h89zuAQfEqepX+7gY1bKs5/d6+OPFUHauEtVT3LZP\n",
232 230 "W/GRlWJcVP+vB2P4a45BW4rbX5X9/sh7j6jiPw+Hqv3OGU0aDf3lBxxXiw2e3WJEHm+K1Z7TcX3s\n",
233 231 "8aawPINigzeMOj8UG7SQPJZrocNv4/gNzj3az0SEbaE/HhHvdbK+OhaLEW9LLJS/H3B4TlJ1ZF+D\n",
234 232 "YlQeJ6E93McYz3rfcf7h99Aexx7wnHT4t51jUGTkEURbziF+LU7gr/cs595u5/desQrHWu3bouKt\n",
235 233 "igF573wxoX0KeLwmTtd8wtR8h/8Vv4tw1MSvD78p7/9OrMBc0e7hn6tfjHNQfERep8XZOBahvRzu\n",
236 234 "ldFeL377OB/gPIdtoM+P4HoA7y3FcQ7ORz7gGIM8baEkEj/5zulixGn/bK0XfeZkvyPOr8DRLXYc\n",
237 235 "Xos+ycez0Nc7fA9jnIvjQ9oJ4gQcS1FvAs8mtQ+j/q95Lk7g2OX88A6Of0b9CTk29nsv5yYC4N/J\n",
238 236 "ePYbZ/wT+L0Jvyc74+Vvg2OceXjvlfwpy+tBsQkY9+AY1y4WY9rpoqL9iajgWa98Dt5D9kfA6zEc\n",
239 237 "Ie8i0e3OF3zIuWMhTzG+YRyDwOx+HOvdA+8tmHFs1E4TpxILjCGrPSFqPDCOk4gRecyxE2+pB4Pi\n",
240 238 "LNSPyHGAlxjnIMZZx5z3oP8JWRfXcu7HHMRIyv8PRQS/PLfdwzN4+BD6zfBgu5RPxecO33GI1ezH\n",
241 239 "weA3zvEmx43fFxxZIS83eL4movI4IIblwTHMEVWMYcLzMfDkTdGn/Rp8rYgLtP9z+Pe4l/N8TywH\n",
242 240 "j9fTHoCHZ+BY7vyeQZkhj4+yCa49mGkXaAM+wDbQJsw8aB+UjYA+hA6/C9twBo7EMfahc3RshGsn\n",
243 241 "XJswsx/XJhxtG44cH2QjcEgb8UfsAnV35iF1Venr+/Xx/GMOhbsJbF7B8VvgEqLezNQd58jDy53j\n",
244 242 "6Id7xGfqCGUURxJH2ZXRzgF5nHl8kOzJQ8md3ZGzFnj7U8jlh6UOSUzJC5QR+kld9/uFN+CUoJ8l\n",
245 243 "6A8GPSy+gN8fCgZDekgWf0g+VlX1gN8pHtwP4K4v6A94vfJFr9/n8Xn8Hq8X9/y4CPg9HlUbHQb9\n",
246 244 "Xq9XvYr7QZ8PV6iuuvSg75BHvqZKCC+GQvINtoIzHd37fPJ99hX04AJPvQH+C3k9ul+17vehbTxj\n",
247 245 "XVnT4xQvJhyUcw3iKYYu5+Rx2RB02CCLywd5TSYcKQ4jnL5QAoHOfdWjnJUzGE6albzu5P0cHSbi\n",
248 246 "DegIBcLEQfYY4CHHEQgFvbI6miMGDg6BkDNMVtTVKCQOuoTOBzhkL152DRQwM698BcwNeDzqBXQY\n",
249 247 "DKi2MXX8C3KEAY8aN36CIY+O+kFXNnRUCoV8Xh8v0KDXG0b3nanhDnAIeCkHpCGfJxzwOWOTDA2Q\n",
250 248 "QcfgQJaS/ayAriTPPYoNQZcNnHIw6PKBwiCZcKSounogEDgKGKc40/F04J2Bgyv1Ph9nHwQOhhEI\n",
251 249 "CK/sISj7JeoBPaRwgFiGdV3ngRLQg+74QsFwh1HAEwhBiPRAUOHgpWhCOL2UXdTzecl6VRsdSpTV\n",
252 250 "WDHPkMTBq3iKH8i+DviC7kzDeFnXMQFeoEGv1whSYp2peX06NYU4hAiF7vMaDg4BpcAB1vUeXcA+\n",
253 251 "8i9ITcZ8A5y8R7Eh5LKBU5aK6FV4Kia4KLmMCIU7Aul35VjWcabjTBW3fEqHleLI9wkuZh9CXGxa\n",
254 252 "xEH2EFK9YADBsK64iREaum6YhiyhMAcYUsMxJGK84cU5Xgz5wzhcHMAYL7jmM4Kogq5wrmZomgE5\n",
255 253 "uQD1z4P7IRqZoItDgDgYXsUJSkaIF1AiH+URrYS9XgtjVTOhdPkMWhlw1qdDJYNhv9cM+h0F1/1S\n",
256 254 "vQNKrGcU6LguK3AYEC925PEqHrhsCKPoussHKCY4ASY4KDlA6SFDckExNujKs6NIUhR9zkyU4Qr4\n",
257 255 "HJFSmhSCxOqGEJYVDAqfahH9KoRDR3AIGuGwYTk4yEE4Q7X0o3EAa4wjOODuDBwCXr2DAzrUQ+5Q\n",
258 256 "PBT+4NE4YOoGbuouDhZwMAyFAwA0vD6Jg+QzrBVxkIOAPhIHw++1HBwoUR+MAyYcDrP9MI2wrljq\n",
259 257 "IRvCsig2HIMDLhUOZEIHB90KdpAJhf4LHOSYj8IBbtanW0JEoh0cdLcXMDysHAlGCBzMiClLyJS+\n",
260 258 "Qo4UOLiS4w2asFI4NSF/jgnkXZpyn0nnAubSvUqwo5EgJ+eTPXl04gCtD3kdyxEkDhbqS5/ENyK4\n",
261 259 "ME1lFzAXy+uNuBKr3LBJa4/24Mrwzwx4IyHHdjpKSy5I7s/EgXMFDkGSkMIBXYcdiZNsQKH7hLvX\n",
262 260 "Hf0gE1Rx5BbF6ozGASZ0xKBRFNVUdQZENFzBI66Go4N59+kRIaJ2KCR8snPioPAImR0cQtAFK2rJ\n",
263 261 "4vBe4RB1OgXOIQvQhYFDSA9IHHyQfcQqIQQjlsTBBw66OERRFXOTI8E9X1ji4OvgEA57LV94Bg54\n",
264 262 "2bJg43kBHPx+G91L5hIHr9/y0SiiFyPEqgFf1MEByuHIphTGIwXWLSyZrVNdMV/JUq8ffFc4yKFS\n",
265 263 "9MLGERxwn0zosNrRnajigjvcGThQ8OkjjsaBouPgEFI4GFEh7FgHB4P9spi6lD9wBwpKDBwcwpZ0\n",
266 264 "2lI39KghRyJxiEgVClpQcxcHHSjoxIFOPugzdAcH3bZDUshCHC/4AZbDV+kuDiEHB91wZxrFRSQC\n",
267 265 "28ILhQP4pWYi9cECpjq0Ba4MUUUk4IvpAcWQoOVEe9LPzyh+TpYSTXUNozUcEgdDab6UNHVKHEKy\n",
268 266 "O4cJYSdqUUCZRlRxQQ23g4/UeZ9PxtvOPakHDAoCjsu3JBbAwRYiFtN14XdxUI4Agm+pWB/MAAaR\n",
269 267 "WESWcASj1cNyOEbMcIfjg26YcOahiN7BQXdw8Ec6OPiVHqFDafXCfOANGz7DwcFJLzAWfxT1OzjY\n",
270 268 "R+MQ8ftjEgelHuzij+FAJzYTB/9MHCxKSJjqGjalKTC8fgcGS7JBzVPioAaDKzBBGisZQToVYnqn\n",
271 269 "KAVxcJCRke4GWkfhEDiCgwlHbcSEiMeJg2It+pE4gOEKhzD6BwbRWFSWcJRO23BxcDXYp9uYsGmF\n",
272 270 "olBzmaJIHGBxwKQoJQ04hB0cwujQoLLLC68BHBhBhn0qd/KHXRxMsok9xPByNAqF5gUa9PvjkEJH\n",
273 271 "BNBDIAocwmgvaIUDqBr0xcPBDg66jLoJ4FE4kNl4YChiyJAQFs4yLcltxQYUJZBh2R0uwQSJg/TY\n",
274 272 "Lg6uHaPPCTvPpGNBR2EXB4MJQsBNQBRqHJ2pB/0WcEgkwmEHBxMdSXmIGNGIEk4oKDCw47Yshm26\n",
275 273 "44sYCUtqEHEO27BYFnAImw43wRVKNphkc4Yhv4VzBRw6NPkgTFvsxalJHAx/0NUHmIKY33RjNMOI\n",
276 274 "40Xbho1Hd3jR9vsTGKtKaqQTsyFmBvKSoGUETMMO+hOGxMxwgglG3ob/qBJQzA4bVFcjYkqWEgdL\n",
277 275 "ab4yOCiRiMsHGEhwwrb5xCmyqpUwHHNON+7ad9PpkSMMONWdgEolUcruyLDTbyWESCaBQ0CZPuAt\n",
278 276 "cYi6OIQdHBIKB1PiYKqRzsQhFo6ErYgOOGbgEHZwsI7GAR1GTEaBEgfofSSMTMj0d/QBU4/5I6Zl\n",
279 277 "gu0cTRygxGKYA2QYViLm9yeVmVaqThzAaIUD/sWC/qQRVFZep+0Od3AIzMQhGjWNsEl1RTgIAxCJ\n",
280 278 "EAc5t6jDhg4O4f8Eh8gMHCz1SAq14UwHnSlHYzoBFV2+41kgShE4auKQShmGCEizg6E5AzDtqGrF\n",
281 279 "ChvAIJaMyWLGaDxNVTkhe2PHfiNuRDEoPWZY7vKLQRxMZFExvGACB9PBwUSHUeIguezD/Sgj+Rk4\n",
282 280 "QAYS/qgbK5tmAhoSj4ckDrAScb8/BSlU2i9xiAEHE5MNRc2gZcZD/hQibDlR4MAuKajH4KCYbVg2\n",
283 281 "cLCACBnq84PvygLL1yl6UTIiEJaw45JMMB0f4jAiklBckDhIJTYVZ/5LHEyaeeIQTQqRTndwwBhs\n",
284 282 "qRe2GVM4GNBd4BBPxWWx4hYHoHBIKR1iRGEmAB3cR9yMhJxFLqIjcYiToTr46verztFh1GIUKHGA\n",
285 283 "I4pSYy1nDRFvYerAATGaqax1CiBKHCCtSP4TgWAaY1USKZPYOLTPxSFCHNKmim0MI2r8ERwo2hZx\n",
286 284 "MEgsaYN8QQUCH7FQ9KK2NCyyO+BAJsg5y5Gp2ilXHqEglrLrNFemMx0IiuKUJVNDnemjuiNxiIZD\n",
287 285 "gWhKiEzGNF0cgLfUtJgVt2Vegv7NRCwWTzs4JCISJhYrFZWWDN0GgIPt4KCrtS4/7wYsJKtxMM/S\n",
288 286 "A1EroHCw0KFNHCSXfQiEbIlDoIMDdDERsN1YGTjAiCQSiHnYHXAIBjMKB0xe4YBM18LUENqFYK5D\n",
289 287 "gYylcDBVMGFRUI9agnNwgEeMAYcIELFxTRzk3GKKDZyyHWPernAALLgjnbcqylqnrIiru+rEch08\n",
290 288 "tf5YHMIzQuAYcLARuEbTQmSzwCEoG4yiHzUAKxGT44WYm9CFZCYpSyQZIS+kjYpkbOWmMEUracUw\n",
291 289 "KLiMqLMKG3BxCCbI0DD4GnCcEDq0I65I+KLAgTlqxFnLxVu2HUgG7IgNky1nlA5ErGQS2QZsCXBI\n",
292 290 "BoNZjFVNXOKQcHGwESpYST2QtXTH28Ypp8xEj8WBoo0K0TjtbCwibZAvqCxPLK7YkECJxVw+0DjG\n",
293 291 "yQSFg5RXFjsj7zgexXL1JGI500HArfxIZAYOinVxgotkzs4IkctZlghKcafWScWIR1wcbNNKJRLJ\n",
294 292 "rItD9Ej32Rk4pGCxYraRJA5qsRF3zUAEF0kwLwIcIg4OEXQYkziQy75oFB7kaBww9XQg5uYskUgG\n",
295 293 "xjyVQphPmx4MpoLBHOyFsgxcOdKT8NERTFaPR0LRSEoP5CIKB9gzFf4ZVKSZOAQVs61owiQYEWmD\n",
296 294 "fGBDTGm+ZIPEIU5GmrZ0VOBEMimdtyrKa2aPaIjCIaIcfAeHkFNdRa9GB4cIpSRmhoLxnBD5/Ewc\n",
297 295 "EmoAkWRcpqsWeJRJJFK5lCzRNIcQVZVzMenGcCMQSUfiGJSZitjhIzhYEgfcIw4xDEt1jg5jUXco\n",
298 296 "Phs4UGMj7n6GFcDUOzhwNNlANJJOI06l1QgG08FgHmKgJFIu6qQcHMJxhGyRtB7Iz8DBkknRMTgE\n",
299 297 "gAPEKmrZCYiDHY92cJAlodhA0Ysn5NpkzMGBTOjgoBgRzykuKEMlH1C5nB4tFwe85uDgpCLS7iAq\n",
300 298 "RwJBHAqFSESEZIMx4C0NVDKaSjo4RCLZVCqdS8tiZ2zy4oNwSETiMTMdibk4RIhDFHlLmkmgEYhH\n",
301 299 "A8oJRdFhXOLAuMAPIxTnmk30CA5x4hCPxh2nGc3hxUwGDs5WOIRCBXQ/A4c0rGA0RP8U1WPRTDhQ\n",
302 300 "iIaVw5OGFOEQDEZALbw6MIDj5GjEhgoDjJj0Bf4QIZBSrww/SjKpcGBnuCQTOGvlSBRoM3CIxaLu\n",
303 301 "M9WjwsGZiLMSQhzUjSSsWQIpP3EoFomDNDuwfimpaSk7rXCAmEegC5l8RhY7A97HbDnWeF4ld2gu\n",
304 302 "GM1Gk9FE3ATPwwG5CAzZD0aCNvKWDF6wjWACHFRuDR3GY/C6UbLVj8QgTo21g86aZSQYTwRzwbjK\n",
305 303 "WTiafBA2GDjAT8bhNTOhUBH2QumKxCGDMNAO2XY4aeuoGg4W7bCtZDIlxZMhaPCoEqJoo0IsBXFA\n",
306 304 "WB6JJ4hDMknLk0wpNlD0FA6RuIMDmECbrEamGJHIKy4oQ2W7o3Z6pKSEnHszcHA8fEThkMwLUSpF\n",
307 305 "o+/HIZNycIhGgUGukEPJ5mLZOBtUlQszcMgRh6SVicYNtZsIU9LBAfVM4hBQnaPDhA0c5IU/Hg8m\n",
308 306 "HByUqEYCiUQgh/oJV+IKACWbRb4A2w0As6FQCWLg4gDf5+JgJO0wqoaDJQeHaCRp/VEcKNnROM0m\n",
309 307 "wnLpC/yKDR0cKHqplMsHKmUGbKAtcHitaheO4KAeyVFD0WkaaJ10555aNzedVIR2BzgkI8ChIES5\n",
310 308 "TByk+U+kUypATcdcHGAoC+i9mJMllotTJhUOpQRNGQUgaOftlJ1MWVk70cEB6ARj4FI2hpjDDCYh\n",
311 309 "ycqtocNEnDjEMBd/wsEhFgy7+pBIBvPBhModOZpiMB7L5ZD2wgbgxVwoVEb3ygpzoSCcRawUC8Vi\n",
312 310 "RioWRlUjWI4ZsqtoNK3CcIv5eDAYOoJDMknJjsbTUYIBHJLEQVkeFMkGiUPa5cMMHJyRKUakSooL\n",
313 311 "0rHKRzEVaHX0QXfqy0RPrQQoVqRhzFKRcChVFKJSsW2hSxyS6XRGDSCWld2HIOZ2MZvNl/KywBax\n",
314 312 "QQVaKTkThzSsaSQHHJwdKdwlDrqeUzikOjigw2QcqikviAMiEAzMxSEaTBKHZCzl4lACDvm8wgFe\n",
315 313 "M6/rFXSvrLDcNssRB13hkIjljWDFwcFGYIHyfhxCIZoYWpYM3FcinZC+wK87OGQUG7Io6bTDBwYM\n",
316 314 "4ASY0MFBMcLFgYXsPxqH6H+KA8KrVDSsp0pCVKvEQQoBRCQrLV42nss4OMCe53KFckGWeCEh1UVW\n",
317 315 "LieVc49BFAuxTCyVjuRjSXe7CiyKhuIhXc/HUc8KpSDJKrxAh+kEcWCM6E8m4MmBQzzk7ChEQ5h6\n",
318 316 "IZRWOTxHUw4l4oUCsm84UOBQ0PUqZAbsZddcwc8jIInrcfinuAF7aYaqcVPFEtGMCsNhMEJHl1Qq\n",
319 317 "m0WFRJb+LpOwYQrSfl1iAC4oNtAEZDIuH2wbl2ACfaNTlO6UFRekoVE8kfYi7kwHguLMxFmRkmuX\n",
320 318 "St5hzNLAIV0WolaLxYQu3TBERCUK2Xg+q/ZsUnYMulCsFGVJFJNsUFZOVlJyRBKHInBIp6MSB7UZ\n",
321 319 "FYPBnolDuoMDOswkEYXKiwD8YAYe9ggOdghTL4YyKofnaMqhZLxYNA2LF7oOHGoQA2Wh5XJqB4cM\n",
322 320 "cSiaoZqDA5McmuYPwCGdBg4wflm4r2QmIX1yQLGBOEg20ARksw4fUHAJJiQVswmUql1J/Cc42C4O\n",
323 321 "uPU+HJiCZeywnqkIUa8fwSGbzckB5BL5nIMD7Dl6ryocksUkgzpVuYqYLillIxQvxbOwptFCPGXN\n",
324 322 "xCGBi0ICeEVCGUiyCi/QocIhgbkEUhIHw0iEjBk4lFA/oyLIZLISSiYcHFJ4sajrdXSvvKHEoQAc\n",
325 323 "EnoiYWUSRioBHOoJU3YVs3PSWjA1DoU6n2HxjKYeFVI5qGUqm5Q+WeJAY5TLdXDI5Vw+IHBzcXBH\n",
326 324 "pnCoKi6oSFf2K+124ggOTv0ZOChW5CQOhp6pCtFoxOMinJW2MJfLS4uXTxQUDrF0LF4pFEq1kizJ\n",
327 325 "EpPrpKycqqWP4FCO54CDXTyCAyKbGHAIh4vH4oAOsymYSHkRQDyShWU/gkMslM2GyqGswoGjqYbg\n",
328 326 "jEoWMnsUXS+Fww3goCy03J0sIkBPoD0rmzBR1Qo1EpaKrWM5aa6ZGh+Fgw6O5/OJZDyVjxGMZDwL\n",
329 327 "AAJhiQG4oNhAU6xwAB9QcAkmpBSzpd2Qpaa4IAPN9+FAy2k495yldCclJA6IJLIxI5ytCdHVRRxk\n",
330 328 "OAQRKagBJIt5uZ4OcxOHLpTrZVlS5RSDOlk5Xc/INAfN6YlKIpfIZu1SIq1wQGoCx6kn9XC4lARe\n",
331 329 "ET0LSVZhHjokDkaCsXogndIlDknd2dOK6TAFFdTPqggylarpqWS5bJkROlFdL4fDXZAZ5Q0lDiWk\n",
332 330 "RclwMmnlkib8lqV3JS0V08XyMrxkaqwfXehyUSFdgNlM51PSJwcUG+gFJBtoAvJ5hw8ouCQTOGs1\n",
333 331 "MlW7rrigMg7Zr0o8nOlAYZ36M3BQrMgDhxxwyNWFaDYTCReHfL4gPU8hVSqoPUwIJXSh0qjIkqqk\n",
334 332 "O91nGhkVNxGHaiKfyGXtciITUV/9wLXqcT0FHMop1IvqOUiyCi/QYS4NHGBkUqlAJq0jEjTNlG4q\n",
335 333 "BsV1iGAV9eVaCkdT19OpSgU4wInixUo43ET3ykLLTbAyAvRUmPFaysykKpbeTFlqbEhyGNbQYBwN\n",
336 334 "AyZcLDJSVzikpU+WONAYFRQbaAIKBZcPCShlCUzAg5RTFA4NxQUV6cr7KvFwpgMcnJk4WxrEQb3P\n",
337 335 "FCwXN8O5hhDd3cDBkGFptlAoSotXTJUdHBA4QBeqXVVZ0tUMG5SVM11ZOSL6zmQ1WUgivSgnj8XB\n",
338 336 "MN6HAzrMZbgvp3DI/Kc4cDQNHc6oGrEYv2fC4aphdEscKFFy80XigMlG8ikLVSN6dyry/4hDhmYz\n",
339 337 "U8goHAyJAbig2EAT4OAgAyhcggkZxWwCpaL5LsWF/wIH3HJWyuVafgeHfNw08l1C9PQkk8KQYSn0\n",
340 338 "sSQHUEqXixKHBBxWo1yuNWuypGsZqS4s2aZadJI41IBDPherJLMuDkloiZ4GDpU08LL1PCyKCvPQ\n",
341 339 "YT7DfTmGisFsRs8nEqaZdnFI6Pm8XkP9fNqJIrv0TLpWkzhkFQ49kBlloSUOFQToaSOdjhTSVjZd\n",
342 340 "i+g96YgaW6JI/qVpMI7BIZ8vlVAhWwIO2WJW+uSgYgO9QAeHYtHlAwI3cKJWw4O0U1TtpuKCDPiz\n",
343 341 "8r5KPJzpwHA6M3G2loiDer8IY1ZImEahKURv7xEcYP+k5ymnKyW1p4/+uyqVWrfCIVPLskGFQ3dO\n",
344 342 "jgiwhlP1VDFVKMarqWxUfeQGHMKJcDpsGFWJQ7gAi6I6R4f57BEcsmHggIGFI2qLNwEWhevhfLrg\n",
345 343 "zqgZzioceBEO1wyjF90rbyhxqIYVDlEHh3BvB4dSB4fwzKKHC4VyOZ1JZstwXxKHgoNDUXoByQaa\n",
346 344 "4lLJ5QMCN3CiVssqZhMohUO34sL7cHCmA8Pp3HsfDiXgUExEjEK3EH19qZQwZFgKEXFwyFTLak8/\n",
347 345 "n0zBJtV76rJk61kGdrJyrgexdVYqYjjVSJWIQy2Vi6qvEUMpOM5wBjjUMsALOECSVZiHDgvEAcY+\n",
348 346 "kwnmsuECPKyVCUcVi5LhQj7cQP1Cxokiu8PZTL0ejdgMZgyjbhh9kBnlKeVmZA04ZAzEzcVMJJep\n",
349 347 "R8N9maiKrZFsMrzkUtEH4IAKOeKQK+VkbBQkG+gUHBxoistlhw8ouCQTOGs1MhXN9yguyIA/J/tV\n",
350 348 "CaAzHeDg1Hd2LOSeilK4JHGIGsUeIfr70ylhyrA0Xy5XpOepZGouDvCrtVqjtyFLtpFjg6pyr8IB\n",
351 349 "sIbTXelyulhK1NN5W1cfrDg4mGadi7WxcLGDQ39/qpCDq5IXwXzu/TgUw13AoejOqCeMoKABHOBE\n",
352 350 "gUPDNPshMyoqkTjUkShl0J5dykTymUY03H8MDjTcR+EQBscrlUw2laukCEYuXSgCB1NiAC4oNtAE\n",
353 351 "uDjIwA2caDRyitnEQUXzvYoLKuOQ/Ur/eQQHy5mJs9U6E4d4vJSMmqVeIQYG0mlhyrAUIlKVnqea\n",
354 352 "rVUkDqlCKg2b1NXXJUuuK0+ZlJXzfVyElYoYTjeBQ+koHNLQknCWOGTxTixcgkVR4TY6LOa5T82Q\n",
355 353 "PQgjBH8ZiWRdHFJhmORmuJgtZdV6Tr43nM92ddkR5lF5w+gyzQGIgfKUHRyyJuLmcjaCqnZ4IGur\n",
356 354 "hChVIf+yXCqS3O/AYJRKtDDpfBU45Cs5GRsFFRvojSUbiEOl4vBB4QAmyCRb5Rmqdp/igky85KOs\n",
357 355 "SsSd6QAH594MHBQrKql4vAwcyn1CDA5mXBwgIjU1gGy9KvdbU0X41Xq92d+UJdc8gkOhvyhxAKxG\n",
358 356 "ppmpZJDmNTIF2/m0J40AxsgaptnIFrLZuFGCKVKdDw4qHORFsJA3FA7q0wpM2SiWjKYxA4c+I59t\n",
359 357 "Nu0ocEBto2mag4gnVVTCN6INJEoODlHED7Yx2MGhKnGg4TaOLhIHqEEN7qtQyWfKUASJQ0V6AckG\n",
360 358 "muJq1eUDAmhwotl8Hw79igsq85P9Sv+ZdaZjGC4Ozlars2RIwwenUk4Bh34hhoYyGWHJ9KBYrdak\n",
361 359 "56nlGgqHdDGdgU3q7u+WJd9dYICtKvcXZdovcejOVBFdJLsyxZjzMVsGjtPIGZbVxUXzuFGGRVHh\n",
362 360 "NjqsFPi9QA5zCRULiKjSkUjOcBBMGxDBbqMCY6HW1Qp9RiHX3Q0c4EQLptm0rCHIjPKUcnO+Czjk\n",
363 361 "LMTNlVy0mOu2jaGcrXKcdJX8y3HJ7hgcyuV6HRUUDtW8jI1CZAOdQk2xgaZY4ZCWOOASTJBJtsoz\n",
364 362 "VFbVr7ggAyC1QSD9Z86ZDhTWmckMHBQrqulEopKyrQpwmDUrCxxkelCCH5IDqOe7agqHUjoDm9Q9\n",
365 363 "0MFBmi2W4oBaDAesRrYnW81Wyslm9mgc8sChmQdecaMCSVbh9qxZEocIjH0+38EhPwOHqtGD+pW8\n",
366 364 "E833G4V8d3fMjjOYMc1uy5oFMVBRicShiYQ1b+XzsWo+Wsx3x4xZ+ZjKcdI1GebTcB+DQ6VSr6NC\n",
367 365 "sY4wolgryNgopNhAbyzZQFNcqzl8QMGlwiHvFFV7QHFB4SDvq0T8CA7OTJwtb+Kg3ufSUDVtW9UB\n",
368 366 "IYaHs1kHB0THDRkBNPLNusQhg8ABNqlnsEeWQk+RDSrQBstyRBKH3mwNM0l1Z0suDlloicShm5sX\n",
369 367 "iRk4oMNqsYNDqWggM8LAXBwyRrVq9BpVhQNHM2AU8z09wAHBDHDosaxhxJPKU8rP7rqP4GCX8j0x\n",
370 368 "Y9jBAXGWdJvvx8GsVBoNVCg2EEYQhyoUQeJQk95YsoGmuF53+YAAGpzo6SkqZhMohcOg4oJMvJRs\n",
371 369 "yjgm70zHwQGvyQVZtfetWFHPODgMCjEykgMOMj2AqnbJAXQVmg35HQj7H2g2e4d6ZSn2FimTsnJp\n",
372 370 "CDlOUeqomevL1XNV4JArxdWX68DBzJgFEzgUSgXkKlVYFBVuj4woHGDsCwXgYEocCurTFEzZrNbM\n",
373 371 "PtSvFpxoftAsFnp7FQ4l0+y1rBGIgYqlFA5mMlmwCoV4rWCXCr0xc6QQUzlOpiHDfDwuyFG5n2oa\n",
374 372 "ZrXa1YUKpS6EEaV6KUeGhsgGOoWurg4OjYbLB4kDmcBZq5GprGpIcUHh4OSBPHGmY5oRp/4MHBQr\n",
375 373 "GsChlrGt2pAQs2fnciIi0wOISJeMxLoK3QqHLAK4we7uvll9shT7SmxQVi7PqhzBoT/XyNWq6Z5c\n",
376 374 "Oe58ZJuD4wQOkUgPN5GSZq2DAzqslSKRqMKhXDIRtxAH58PRrAlT0I/6NXdGQ2ap0NcXjyUQVJYs\n",
377 375 "qy8SmQ2ZUZ5SfqzSY8JDYrLxesEuF/ri5uxCXCWm2YZ0m3Sg5tGFoU+hmCt3IYwoN8oyRg2RDXQK\n",
378 376 "XYoNNMUKh6wMZHEJJpQUs4mDyqpmKS7IxEttmKkFEWc6MJzOTJxPQGbgkE2l6plYpD5LiNHRvItD\n",
379 377 "tdFoygigWezpUjhU4Vd7evqH+2Up9Zcpkwq04Sqaljpq5gfyjTxw6M1XEjNxKAKHXm4iAQdYFJX2\n",
380 378 "jI7m6mXgAKdbLIYqZUS2WdsuHsEBpmDArBdrKrMtl2eZ5WJ/P3BAMFO2rP5IZBTxpIql5McqvcCh\n",
381 379 "GCkWE/ViDPFD3BwtxlWume2S6RaXTo+GwWLogwrlZq4AMMr5o3BoKjbQFHd1uXxAIgNO9PfLxQ6V\n",
382 380 "7ykchhUXVAYu+5VxTPEIDs5M5MK4+gZBsaKLOGSBw7AQc+bk8yIi0zSoarccQHextylxyCGQhk0a\n",
383 381 "GBmQpTQgcZCVKyNH4dCVrzcyfUdwyENLJA59Cod6Bwd0eAQHnTjkcjNwyLk41F0choHDwEA8lmRQ\n",
384 382 "KXGYAzFQEYvEoc+EhyQODeIwEDfnODjkc00ZvtCBvg+H7u5iKV/pRjhX6arIGFWPSF2gN5ZsoClu\n",
385 383 "Nl0+IJEBJwYGZuCgstsRxQWZAL8Ph9zRONCZH8GBS3QN4NAYEWLu3AJwkGlaDX5IRmI9pd5u+cFZ\n",
386 384 "DoH0LPQ+W+FQHqgw4ZSVq7ORa5aljpqFwUITUV6mv1BNyC0nB4cScOgvAa+U2YBlV+nn3Ln5RgU4\n",
387 385 "wOmWSnq1YjaIQ+kIDo26OYj6jZJaZ66MmJXSwEAinkRQiRcHIpG5kBkV08rPMfqBQymCPLKrFKuW\n",
388 386 "BhLm3FJC5fy5bnKnxCXsY3BgCIoKlR6Ec5VmpVBvEAfMqym9sWQDcejudvng4iAXnVTerbLb2YoL\n",
389 387 "MhCtyn5lHFPq4GA7M5mBg2JFN3DoysUiXbOFmDevUBDRY3HoUzjka/nCcF/f4OigLOXBGTiMKhwA\n",
390 388 "q1UcKjaLja5sf7GaVP9rJoSaVt4qWdGowsFqwLKrztFhoxKN2i4OFuIWDMyKq09E82CRNWTNwGG2\n",
391 389 "VSkNDiocqpHIYDQ6DzioWEp+NNRvwUNGkUd2leKI4xLWPAcHxLsyfKEDtY4uXV29vaVyodqDcK7a\n",
392 390 "XZG5gh49Gge6RIVDXuKASzDhfTiMKi6oQHYGDs504MCcmTif4shvUBQO+UymKxePdo0KMTZWBA4y\n",
393 391 "XYaq9spIrLfc36NwQCA90t8/NGdIlspQlbZBVq7NqaNpqaNWcVaxu9jVlR0o1o7CoQwcBsrAK211\n",
394 392 "wbKr9HNsrNBVBQ4lprB6rWp1EYfyERy6GtYs1O9SKwzV6qhVLQ8NJeMpBJXAYSgaHYPMzMBhADiU\n",
395 393 "o8gjm+V4rTyUtMbKSZXz53vInTKXsD8AB1So9SKcq3VXIUXEAfOic+5VbKBL7Olx+YBEBpwYGpKL\n",
396 394 "fyrvVqsMcxQX1EqI7FctTB3BwZnJDBwUK3qAQzMfjzbnCDF/frEoojJNa/T09MlIrK8y0Ku+RW0U\n",
397 395 "irBJs+bOkqU6q0aZlJXrc5FrVqVsWKXhUk+p2cwOluouDkUEklYFOAxWgFfaasKyq/QTHXbVgAOc\n",
398 396 "bqWi12sW4sdYrOLiUACLrGGrC8ZCrTPX5li1yqxZyUQKQWUtEpkVjc6HzKhYSuIwCBwq0Uol2V2J\n",
399 397 "1yuzktb8SlLl/IVe8q+Cx5VjcGg2+/pQodaHcK7WU5Uxqk420Dn3KTbQFPf2OnxAwSWYIBedVN6t\n",
400 398 "stu5igsyAa7LfmUcU3GmAwfmzGQGDooVvYVMphs4dM8VYsGCUlHYLg79cgD9lUEXB8Q3g4PD84Zl\n",
401 399 "qQ7PwGHeDBxGJA65oVI9JT/+tywHB9seOhaHBQuKzZptxzo4NBFxxmfg0OyyRlC/g8Nc4DA8nEyk\n",
402 400 "GdxHo8O2vaCDg/yIbshCpGIzj6wkEMclrQXH4MBA5mgYIgxBK9Vivb9YLtZ7a6VuKIJuOzj0KzbQ\n",
403 401 "FLs4yEQGnBgefh8O8/5fcZAbFOrbKBeHbLanELe758m/w+R1jqL6q4C+j+BKk9c+HyyXWCPGhV9k\n",
404 402 "xRe0AW2+tkL7gvZnnoLne54XPf/b+xXv495ve3dXk9V8tVytV7urI9Xjq0uq36wlIL3dtVl1Tz1Q\n",
405 403 "j9bj9VQdcVJ9oD5R31C/oPnSvj0/t35/+P96Dh/mX0oUD2qztOO009By1vM8Wn6l03KiiryPk0bL\n",
406 404 "x31AyzG0nOu0vFG2LNCyJlt2yiH5dxQPNYV47xPvrXzvQ+8dL8S+h3lv34p9N+47Zd+Cfce9fuD1\n",
407 405 "1uv/+Po/vPbua2+/9q9CvPY7HK+/9qPX/ua1R177xk+Pq94sRMgv/9riGi3p6fec6PmYEJ6/8XwH\n",
408 406 "9DtuT57neXheEn+keKbUcdS9J3D8rUThSvG0+Kq4UOwUS8Vz4r+LvxQrxF+JdeJW0RIvijfEN8Q/\n",
409 407 "iX8QnxXXiBfE98TlYrv4mHhe3CE+JW4QU+IU7Uahi7AwhSUSIilSIi0KwLEkKuBxXfSLATEohsSw\n",
410 408 "mC3GxHyxQBwnThSniw+LM8Ry8RNxvThJnCZWiw3iYnG1+Ly4WWwV/038qdgm/lzcI+4TT4pvid3i\n",
411 409 "f4nvi5fFT8Ve8b/Fa+JnYqX4iFglJrQ/EVeJc8XfifXin8VZ4rtio/i4eEz8ibhTu0H8T/EVcb6Y\n",
412 410 "FD8S02KRuEt8U+wSa8WjYoe4UTwiHhY/EE+JgHhJ+EQIshbUvigMERMRERW2yIsMpC8n4qImekSX\n",
413 411 "aIo+0S3uFr1inhgVc8RccbwYEV8QS8RCSOqpYrFYJk6G1H5SXCIuFZeJL4vbxO3iS+LT4l7xkLhf\n",
414 412 "fE08IR4QF4jHxatij/ixeEW8Lv5e7NMMbYlmaks1S1umRbTlWlSb0GLaKZoNmY9rp2oJyGdK+4iW\n",
415 413 "1lZqGW2VltVO15Lah7WcdoaW187UCtpZWlE7Wytpq7Wydo5W0dZoVW2tVtfO1WpiE/Tmeq2hfUzr\n",
416 414 "0tZpTe3jWo+2QevW1mu92nmiLP5M69M+AQ3bqPVr52sXaIPahdqQdhH0YpNoQD+GtYu12dol2oh2\n",
417 415 "qTaqXabN0T6pzdUuF7PEd6CVW7QxbTM06NPa8dqV2gnaVdqJ2tXah7TPaCdp12gLtWu1ce06bZH2\n",
418 416 "WXGCtlj7nHay9nnxIfFzbZ52hbZA+5T4qDhHnCkuEn8rvi7+UdwitojN4lnx1+IZcZ74hDhb08Rf\n",
419 417 "aGEYhf3iLXFAHBS/FL8S/0e8LX4tfiN+K34h/lX8TnxGXCsC2ktSn/f9/1uWMQdIIWQwApnrhoQt\n",
420 418 "hHSNQ7JOhWytgVx9UkrWbZAtSta9kKqHIFcPQLL2QKooU+dB3qkN3xVnQ9q/AA34uPghZH+j5od0\n",
421 419 "94r3xKQW1ELQlbvEIU3TPOI/xGHoyw7x75Dex6EPV0FzhLhOC4h/gxbdKK6AhgWgH32Uhw5C3xb/\n",
422 420 "Q1ygecHxE8TnxJvii+ImicQnoGF/A/za0KkoNMuGPik9ylOHNB90idozT2wC+v8I/VT4rwX6Xxfn\n",
423 421 "tMTgqS191Zpdmvana5/WDt/cWlLapXvXf3yopQ1Wq0svXtLSNgy1PIMtrb821PIOVpe1vM1lZ6xp\n",
424 422 "rK3eXr19xcbbq8uqm87b2PI15S8eXHD72uFqS5y55mLQs9bUWuNrC53TC9auPX6o5WMzPtnM7WvR\n",
425 423 "wCVOA5fIBvD+e0Mt/+Cp1Za3e9Wa09e0blhSaI0vWVuo1apLW7tXrWntXlKorV071Ap0xojfz1+c\n",
426 424 "VaMNDrYC/UOtkGrhTLyP19fefru6atRau2+/vXA7ZuBcP62JY2+Mz7yBGS99WrthlXxyQ6NW4I1G\n",
427 425 "rVHDiNYuGWrpg6eeuWYphlRbOwSRakFk25rHM6C1vV7QKe/yRfOa2dDAlO/Dzon/LHUipjTnbMrz\n",
428 426 "sWVj8lY74A8OtISaT3tDSPt0+/owyAMkz5G8TnKYpBLWrmwvJFlPso1kJ8kPSN4miYa1q9rDJCtJ\n",
429 427 "riDZaeLdt0kqJqoMk6wn2UbyAMlzJD8gOUwSNdkKyUKSlSRXkLztVPk0q2BovLQjOBsnWUWyk+Rt\n",
430 428 "kuEIhxvBa4d5eQUu6RhT0LgToZET+BUifrgoTO1HIg5vG/f8DpZBQLvVf1+Gb3kFVnuWp9/7qP+l\n",
431 429 "wNbADwO/DWaDZwWvCV0e+qb+2/Bp4S+EHwq/ZAij23jUfNictiYin4rmo2P2JbGJ2ObY12J/EzuQ\n",
432 430 "/I9UOvX99O8ze3KX5LbmXshfXjilcGHh9sJkYbq4oXRLRVS+VvmbyhvVrdUdtQOIQOr1c+vXdd3e\n",
433 431 "9WhPqOfcnlbvpr5Ng/cOTg1tHTowy5z1w+EHh58f/tXIZSMPjbw02jV65ZzpudV5l8z79lh17KwF\n",
434 432 "Xz7u9uM3H986IXLCwyf8/sSNJ3lOGj3pEwu/sPDBhd9euH9cLJpYNL3oXxaHFy9Y8u2l6aVXLn13\n",
435 433 "2e7lkxNLVixbsXvFj05dctrVp019OPThlz5yw0cOrFy18oerxlatXrV71U9W/fr09OmDp0+ecd8Z\n",
436 434 "3z+zfOaSM58/69dnB86unv3s6jWrD63pXjO+dvTckXMnzr3w3C987NmPHVqXXHf1+lkbzt2w47w1\n",
437 435 "5x3aGN7YfUHgglUXvHHhZZs8myY23XjJpksevuS3lz592QuXhy5fdvm9l//HFR+/4r4r3tic3Lxg\n",
438 436 "c+tTOz5911Weq+KfyX7mK9f4ru2/9t5rv3Pdjs+e9Lmxz09d/+ANX/uTxTce/8W5N+Vv+sub/vam\n",
439 437 "vTcdutm+uf/m3bc8sXXktk/dduNtk7d/4kuXfelzX/7Uf3vov/3sT/N/etmffvtPX9n2uW3T2/Zv\n",
440 438 "e/eO+p32nVfe+fCdu+985c5Df9b1Zz/88xu+kv/K1r84/i+uu2vwbnH3GXdfdvef3f303b+455R7\n",
441 439 "7tpe3X7a9s9tv2P7S9v3bP/ZvcvuPePej997yb1X33vjV6/56k1fveOr9311x32R+/L3dd83et9J\n",
442 440 "951y30P3/eV9z973/P3x+8v3998/dv/i+z9y/7n3X3j/p+7/wv2333/X/Q/d/5f3P3v/8/f/8P7X\n",
443 441 "vrb7a9//2itfe+Nrv35g9IGTHjjlgdUPfOKByx+MPJh/sPvB0QdPevCUB1d/fcnXV339Y1/f9PUr\n",
444 442 "v37DQ2se2vjQ5oc+99DWb4x848RvTHzjrG9s+MZl3/jdw+Jh8+GtD08/vPfhAw//7hHxiPlI9pGu\n",
445 443 "R0YeOfGRiUfOemTDI5c9cs0jNz1yxyP3PbLj0Tseve/RHY9OPrr7sfHHTntszWMbH9v82Od29O6Y\n",
446 444 "u2N8x2k71uzYuGPzjjt23PH4xidGGLbLSFJ4/gGetwHbPiBGW4PDraHh1qDd6p5udQ/vSvvebQ3Z\n",
447 445 "rebeXUXfu+KvvFqXb+CvmloO1KdpvoGR2fPnzUn19MydPzY2/yTvvLndjXog2DM2Nmc0nUryLwIG\n",
448 446 "UplYLabheG3BPI8VTMfsZNg3VKkMBUaDp4yNLct1NwOB5w5t1P7hkLjq5JOvii3IWaVYNJOI6V2z\n",
449 447 "B+eEJhYtP7E6r1FLJOc+7bn4vbs99703iiELoaJ1z196P+3phucXWhBe6XXEDh9qX2Fo6yYXGisN\n",
450 448 "zxbajS2TO6PPRT3rpnZHp6P7ot51grZmHe3UOhqhdbQruETNkdlH2r240+5G2e6YbLe90NC2jFsP\n",
451 449 "GDuN54wfGK8bbxuBde0rSnxSkk9KO0vPlX5Qer30dimwbmT2B4zzetneQPsKC29xgKJ9mKdvcxgL\n",
452 450 "3bF0RoVGZs73nk47fyvbWdleFUPtK2KofQPIlIjZsWrMu2Vqd2w6ti/mxZ2aXavWvKhUY481Dc9q\n",
453 451 "07V9NS9ewS3RXp9kX8Vj+/pop69/lH012wvz6Atk3dQD+Z355/Jo4Yq8bKGCFg73qha8sLDCczzs\n",
454 452 "bhgxxID22XZzwDvQHhnAyzZIa8DeJXzvtu9o4qWRJu+CtJr2roD33ZawW8Y0Llrl6VZ5uL2qrK3b\n",
455 453 "FfO+264ZzVj8uPbmQfTXEkvXtHLDhV0586S18qIfF/2Bk9YiAHm3beT6UbVlDO8Ke95t5exdaY3v\n",
456 454 "h/n+wRoHqU3u8D7t9axrT3jR/R+8GIgewtm9CQjPNcmtSTw6QK5cTHIvyYEUL0nGiqh5C2Y8eWH1\n",
457 455 "6ipqrq7ixltVPHqzAbKH5EddvASZWt11YdfVXQBidc+FPVf3gGcv95JnZ7Hrb7HrpTx7lGfvkAR4\n",
458 456 "GeZw9kdxuYm93wMyeW3yNg7sIG9sSrlj2s+ON5G83HD6bP+Y5BV0M3/O6EkeqmZjnjqb5Wk0euaM\n",
459 457 "lj1U0VQamhrBnR239ywZLZ17zp/fmZ/Tl9crJ865feKfBs8Y71504qlnx8YuPPuVxXZttLFsyYlW\n",
460 458 "ebhu9PaWFsf7l4wef2bEE1j3kdjiRSMy0x05/C+exzx7RI/nlnY87B1oxe2WmG6J4fa4wHSmhbau\n",
461 459 "FbZ3lQDnql6qXh8Frw9KurBvZR+mdn2fi24UgEZddP248APddiLqJ7KJ4VbU3pXV3m357V11gNvj\n",
462 460 "j/J+z3B7ugdNbuiRILff1HBxWxBkbYhnJG9S59eS3Ebysk2wqD5XkXyX5FaSt0im43wAMnlbYnsC\n",
463 461 "A3wGAtJ+ieQXJE9SXp5JvkhYniQsv+xgs5ZkO8mLJM+S/ILkSfkgDfIMyRMkvyJJ58CjN/KUMJK7\n",
464 462 "SPaQvAMyGcin8zBpy+XzAm48WpgqoNv9Bb5awKvLebacZ29SOMcqyyp4flfVlZE0xXQ5zw5SPFZ0\n",
465 463 "4XIHdXAtyXaSHLVxBcmzvHy8G+082/1SN9r5aTdvkLu/6qH4HiRvD5CFK2i+9pCPL4Nnk1fHbolx\n",
466 464 "XOTfleTfGyQvk3yPPJN69aLLp8lnUi+mUH1H+iiuHCS5hnM9QHIKiZzpAVoaOQmpcPs5kwuaZEdz\n",
467 465 "qol2pjjMPSQHMMxmxNuoz4LYn+SBV8oEZ+EyAtEve6gN8z2PxectWTm48tbzFyw4/9aVi28aXmh0\n",
468 466 "z15QXHLZqb29p162pDh/3nByc2GkkVxw/tbTT996/oK5Jw7H6nl79KwrPvShK84aDWf7KlLue6Tc\n",
469 467 "r/B+rj3YBVsX7BqkPAYhj5C+VmRvexvt+esgraDdSu5t/5oMKEeCqDZufLX8ZPmvy39X/mnZD7GE\n",
470 468 "xWv3gbS67F3zYRHhkRdPtxYPT44vXrUYIjC9GM/KdmvF3vaqU6hFp9LjnbryVCrQqa4C9UFn+lwF\n",
471 469 "WoGLFVCglrm3PQLMWrG9rRU0se2hvhUcKRx/n71rFBq1wt51IjRq0Yo+3l803N6wiIqwSGnU3Vnq\n",
472 470 "Rw4dXly6toQOl9Op7KhTikjWkPPLwfnJH/e/1Y/nPxogSiS3knyP5J8GiTDI1L1DTww9OwST+HdD\n",
473 471 "FCySF2bh5SdmPTsLL/9yFqVkmD2QvECylmQ7yTMk15H8gmT/XLw4f+7yuXD2n5l769y756Ldb82j\n",
474 472 "JsxbNg+tPTqPtUhuJfkxydhxHC/JrSS3HM8qJMtJ7ia5ZyGaWLvw4oU0vQupwCTf5t0Xx18dp2qQ\n",
475 473 "Q78iufpkvk9yN8ktSyinJHuWcvJLqTiPgIWT382+nPWsm7wlexd+2reAoe0pcvXu3GM5j6P73yO5\n",
476 474 "Dm65fTEjjM+SHMPuZ0jOIc9vo2aeQsbv6d9Pxr9BTr9MckuH8W+S8XtIXiCrd5DVz856iaz+RYfV\n",
477 475 "B0iu7XD5RZJzhwnK8LPDqLlgLplCcoBkBdl+7dzbwPb2VrL0aZIVJK+SvLWAsBy3/DiPw+M3XfZO\n",
478 476 "3nr83cfj7pvk510kU2TqOQsvIqsf442Xxzl0krfI3mvI1AMk20m2krMHlxCIpa8u9azzZ5zgEy6t\n",
479 477 "3jM/PWd0TDo8hqXzu3v+uB3I0Ez0aJd6QjGjPJyq9SXnDtn13nSvL5yMRtLh+Nyh/pP/MxtxgrQj\n",
480 478 "hnGi5vX1dGXqqXC2K57M+SKWHvDHlgdixT9uP/ppXzQZjq8G+RbiJlPktMF2IgdLspOicT0I4xhG\n",
481 479 "TbthQ1sJGSu119MiPkAiaPZ3F1zlj0DfI67yZ3GRdWKjVsTeZWjvUpWfZqxxHMnjJK/KIIihx708\n",
482 480 "e9pHFEEmt/se9wGPp+k8V4R4I/R4CDde5Q0ZNT2l42yCZAfJcSTbSV4l0XVWoXtYTZcg/cKt9Aa3\n",
483 481 "0gSuliEMyRRN/kSaKiKjomUkMipaJsfCtp4g+WuSFWzwGZJl6U6YMyO4YVTztS+f/pmV3d0rP3P6\n",
484 482 "lyd+ufjmSxcvvvTmxb9cPPv0i+bPv+j02YsH1968Zu1Nawdl7MKYtQHeG+LClne4bYMT5HfLa7e0\n",
485 483 "va3AdCswzNg0NN3S7FZ4b3snvN7k9dY2y+PwXAYpLs89uPC4PEeQEoBJ1f0emlQdYSkRmF9L1WI4\n",
486 484 "+N8O7bOHPqQ9dehO7ZxDjy9e7PnO4t8sRhwux8TcQVgyDl8kdiEOb3DhZZ2YfNs8bHqcJAbpzBZ5\n",
487 485 "WyY2bhx/7Psfle/Pbm8D36aG7YX2StuLBMl+zvYwiKcf57TaCxl3ro+iybejsrVOWxd32too21o3\n",
488 486 "rg+bC82V5nrTt2Vc32Y+YO40nzN966ZeNzk+mOAoh7YBo5p6Pft29nCWt7JIlvTh7MLsyuz6rG/L\n",
489 487 "5APZnbSDG+hdttH/PVc+ut8jc7he9jugHOqGKMVFZk0yf5JjXs/RHybZGXV4Ad2yHN2yRU27vV2o\n",
490 488 "UbdoTK8HadUc3YICtQqdPMSaJo3sxeNWXNLkdHs9g+wHSEQDyZtEOw200y70VVxUXejTUt3aVrpK\n",
491 489 "6K3hXTYykth0y2Jb1MIc5GzyOO8Kr8dRvJ+SfJNkAcX+HpJnSHI+1FwQmoDuTd4T2kEVfIYqmDta\n",
492 490 "BaXi/ZTkmyRPZ6hkWSYTtCTdjDHP4dmt9C6r6VP2k3Qjf23fyrPVZbZHEHY3yN3JjjLOl8kKx3IX\n",
493 491 "yQL2vp3kFJInSBawz1NIniV5hr2fxd5XEFrZ8RSbXk7yUfQ0Q3OpCMxTYh+owZrn0KGJFSs+QI9/\n",
494 492 "MzamzXmfLhvAWhdntzTosuboMjTXu5eYQp2h18G97ZWGazK9gM3rYihwIRgvee1dPuAX9AoVz+0K\n",
495 493 "UXXnNDqKO6E9eeh+7ZRDU1JnnRzovyMWbHpK7WQJOVDJ3hWCrR5OgguHafJaIK2kvSuGESXtVhPZ\n",
496 494 "7nB7hE681eOOpo4B1I814G1Rz3IYYrhVt3d5IV5Ze5eF4SXU/cRwex+8w664MvAvIt2a/Kp4UjBE\n",
497 495 "wXn72yQXMW5fQ3INyNRt2nbtcQ06+SJv/YHkpyTfJnncA/J3JHf5Qd4hCfghhvP9y/0Qw7HAsgAa\n",
498 496 "fyfA+wHMbxnJVJgeP7w8TC8e5stcOtlD8g5JwEALy4zVhgcNGcsNtkDLtZqG4i2KZR/XGQ7Qr/XR\n",
499 497 "r/VVQNaSrGHM38c8Zi2JTLffYArwsky3mQfc1XyUecDzzF+ealKA7+a0d5C8Kty57yB5gQy4WLtW\n",
500 498 "u40MeIa3fiIfcsYvgkwe51+BebaXcRbzScY6U1lOspajPSjTFI52dSfXeqOThf+MA/kuyaMke9xx\n",
501 499 "ad3dR2KQsfnzKPiBwMxsxXNp18bFbpgx/4Lex39z4rZFbq5y9mdHPIu7B91AopD5P4sPPdJounnK\n",
502 500 "8IirB5+SPu3mlgE9AOPbq0hGQHaFIX+e6ZYhFWE98xTvNLKTXQHfu5PCsi1YpG10ctPWPji59iqL\n",
503 501 "dtxyJVSDUGquhIZxEaaEejSussD9Udd808j2W8HplsfepVMmE3NitdicGLQn1tgwod04MXHoCxOe\n",
504 502 "7xz6e23OeydqKw/tUmMWOzBmrzgNfUizvI9hyCrfBoQhk9f7twGRqYp/2L/QD+e12z9NhITftcVH\n",
505 503 "6bEcJMYlVXbOjgl0Rp+yHMwJSJ+SdvyqCZ8ya/IB/86ZjcPF+g4z9llIc7feRxfrnMm1vcNv4+3c\n",
506 504 "jHY+KtuZO3l9cFsQrz0HiziuV0LDoYWhlSHfFngqatBOkvUkb3NpApzn0iPGdfg3aCUjfWza8bGm\n",
507 505 "XOO7Hh2ORx/w7fQ95/uB73UfhxVcN25EfRXfsG+hb6XPv6W90sb4rk/Lmumd6efSP0i/nn47fTiN\n",
508 506 "mno0XUkPpxemfVvc2ID/l2r/c8bYr5d9DbtjFxhVezMImBA4TC1fSC1fTwXf6Zw5vlUTkcP/on0Z\n",
509 507 "rE2Iv2mFhuW8J18PvQ0X1UrslZY3BD/IJT/av20pV4ak2LhY2biwpZWzw8rKyQWj9g1cNbKlvaNV\n",
510 508 "6wXS7YvJvetINnEsB0CmQoFsoDfg3dK+hSHMLQwH7kGYOfVM/MX4q3Eo+BNxWoML+daFfGsr693m\n",
511 509 "1ms/DTI/nZb+CEIqk4junnPCYyM9swvhicTwacet+IR559DcyuyFNe2N9w51nfqh3lNXuHp2HeYf\n",
512 510 "0f5HyxymOp1MZP+K5DDJn5NsJhkHS6lnCDHPhDud/Ib3r7gO+T261rWMvu/xUmhS3qZ3nnepF7HR\n",
513 511 "P3l/zhpfZI1fkwhUa/n2Tp7v+zQ1IuVr+qCqf6BkjlFGl5J4QcajN/ju8D3oa/l2+6Z9+yAyLVPi\n",
514 512 "wDWIwN72z8i28wOfDnwxAPb8L/LkX0m8AHnqjsCDgRbvT/OW3whw9XQZjX+vf4EfpvtW/93UvEfJ\n",
515 513 "z2+B0JBEptsVimHL/sBM5CjdNHBhBJyLIC6CEnyv4bg4fW97KVMHv9DZdQ9xf4qdcRDtFEkiRNXR\n",
516 514 "2j+hwf4Vyd+TfIdc+g758Sbrf48kxPovMkTZG2Lw/LLnTQ9G/12/W4vpTGLOHG2OpjW0RiNGe6Hp\n",
517 515 "2ocvOvRV7dpLDj1saBMT2p3anEPPHfq89slDf64+4pHYa7/FhV8cd2ze4NlL4xcc/oD8QHKCQaLo\n",
518 516 "pALo7pRDl08w+pc65bRrib9GE60omqbowD770LqYbvnYQdtGxNuK2k6CAlz1vaTGtIDxg4LJ3iz0\n",
519 517 "ZpnHsnobfV1FY5QP1zZurA9fEb4+vC38QNgPSQurtVrY7bbQiMDU82KP2C+862Dad3lwO6RrBMYr\n",
520 518 "QjI2UpGSIa8mbzHugl+HxZ+fCjJOmhecNx8zfOUVzvGMMya0S55d9uzPFu9ftmyZdo/LR2+eOuTJ\n",
521 519 "t4MhROcrGdeupBkSoaDqAdFbex/BvYFkFUkLRHJE29teyAmtJ5G+/FUQGB6ueT3H+ELu+2xzcySp\n",
522 520 "B5G97T00AFMkZ5KMkEQ7aQTzHynN7etJHiCZJhn/YAkP4SIk5ThCPrQ/RQv2TyRnMxobE8sQjU2e\n",
523 521 "LM4UUNm/4v19JLtp4yLINKc5YN902x+K8PXvQzInv+y/j4r2WUrpkyR7qZG67GDyy/p9Olr8vr5X\n",
524 522 "Zx0G3U/qat7GdHuNrZbtnmInF5Mp2zWuuWovalzN4o0nSG5Ci5M/CrwBOz95QeAq/Ew9EvhW4LvU\n",
525 523 "/7uCNEhTweeDe4L7gz5EkLRjj5Fsoj7tcNOAcf2Z0IuhV0MHQ6j0uM5lGf1VjGrqEv06/Xbd62Tu\n",
526 524 "vyR5hGy+kOQukudJ9pOcE6GRfpDjvZPkRyRTJPtBmglGDw1qKBVU+x9vXz7hOSy64c4mLr/JM/7e\n",
527 525 "iZ6N790nj+84dvkJxj9aue3VIVNVWthVTLlu8N5BqzrtyhAjfQ8kwj8NRR2PrfSv91/hZ7DBmOA5\n",
528 526 "f2hLS0ccjwoGIqbh8cSIMW6sMjYYm40bjDuMB42WoW9hKr6F30JsmVxlbWDMdD1czBTjp19bmH6r\n",
529 527 "EzxJ2/e+3Q3hp+2bukBcJW6GnrX/gBmPW7rIiT5xnFgh1orAlvZXeNN4EO3sFtNin/BLw+uZRljN\n",
530 528 "vZGpHwReD7wN3MatSwLXBW4P3Bt4IvBsILCu/W8BtuYPpALNwLzA0sDZgcCWlp8rGiOzm3MYlimm\n",
531 529 "ejZ4Js49dC7IZZ41YOjH33tI2jonF3+C+4KiqvYFNQsxQ7l9PfRpknk/pjxuyg1B7uwc7pMhgoc5\n",
532 530 "kTeOnCgherTftMowSnvbgjtCmwnsuJDr2bvCMuvelUSyNN4LHrb6dnNPaCGb2tfHvc0+u6/a53UC\n",
533 531 "vQbY1nB5KDcD6Uwa9i6/Jjf9oipemKBHANnSPo5n20kOMrbXPTkPlHABJeAAyQqS7Z21MGbm7f+Q\n",
534 532 "Z3SoaUY+1IUpqsE7QeCTDjIVCi5nvDRGLXiUZD+jn0AoHeJuDW88RbKc1mwZjc+jJPtJ0hSXp3i2\n",
535 533 "nDIzZi2jzNxFmdlj7bfegcxMBqw0A/F3qBtpbgzn6F8XcBFtBcl2bvq+GjsY+0MMtfVYLobaE4xn\n",
536 534 "QDBnnh0kyfFyNdfYbiVZzcz8VpK1zFy2cpl5LclWpi9rJeHyyGrmK7eSrGaGems30SVPJxd4J6BB\n",
537 535 "kwuCE5j/5Hxzuckfa7nFn9jyGH/iy+OedZpcg52x3CpDKzfPKXs832GC4yY6/P0IMxw30+Gv9gkm\n",
538 536 "OW6yw18mOW6yg1+1xrMQPuRG+hDR0FbBMLcKeyd3Fp7jNtnbmKVc14nYuxJQ4+g07W1yr9r83paU\n",
539 537 "eyLt62MyNW/Vp5GBj+sLmyub65tXNGHPtjVd3a1B1Gqu3KVwkaLc1eRGJP1yCiLMhdbptl1L0W/Z\n",
540 538 "w7tiShb3UJqWM+y7yPsZMm++bznzi7tl7Ea7KDPNu0lkfvkYV03WSMKlmR1yC4BLJ0+XicM1bO8V\n",
541 539 "kt+ThCixUoov7qwfbSc5hWQZ25+vu+0vZfty/+6AlAJ28jjJQRK5JrTf7am9AkRzMPTNmZNwli5d\n",
542 540 "EHHqCR239OrVs8O5gaUbFi2f0PKH9mujhxZd+ufr+k7+5JeWa9869BPPjc0Vn1yeOmHhwtkVbeXi\n",
543 541 "Q/+6eMmmLxy39uY1A1zrlDZC5iN1lZNpfcyl2pt7wa71vVf00rb0chdZ7p7v5P7xepqJt3sP89l6\n",
544 542 "3ljofILwAe19VLY33r6C5mQVv0XYPICW7YHqgGfL1O6B6YF9A9Dp8UF+kaE2pllpIfdN3h5Ql0e3\n",
545 543 "fXGn7Y2y7S6Ob93keO+qXuggx90e5+bI+NxVc3nj+D86tuvl+wva4+hx8sGB1gCcphjg0JDOTA/I\n",
546 544 "+TpDwHwHDg941HDk6GQS5u3YWENkxBz/1nahjzGUnGwfQsQ+Z6GpNbq3PTLKmG+U4yMRvNwsz5qj\n",
547 545 "jCleEj/hmtFlNM7XkjxDC92n1rEMWaf9Y4rPfpKrQPi9Rtfe1qjdmrO3bXbNYY2rXDs3+V3zZXqG\n",
548 546 "T/DGPm5dby7cQJ1s0fKMcIVnFQgXXmn7M+r1VyiVnyWRS5j7SbLq0XXcQZvKPs8l43+mtDbU/a1c\n",
549 547 "DjpAspGkW430Rdqtq0hmqxuXzGZ7s+lQ5tnzqvPA44XcJmzN281twn3zXE2fBeWe5Wr6KC5Gj13j\n",
550 548 "lRdduOjixSy1KFeVvgeZDL9JmHxavEBmXkI+XkPyBokR9XIoL5OBt5BcSfIzElM9+h65tdV0lfRq\n",
551 549 "ko0kGdlu+1Uy5DqSZ47wZxaXmSefyn6PrPm5ZE11Fmt/iQw5SHKBZI26+8IR1sg3FWt+PlsFjQs4\n",
552 550 "0BUMW7eLxzmHAwwVT2EA2a8dr0Gk5zGpkkuDexioOquCQX8G+WH7akapexhYzg8s5wLCvDBqd4NM\n",
553 551 "PhZ+iouDTS5FPcU5L+XE/oJkzFkJXDf5qDlFqemmk1xAMs864iSXcTHqMSbwb5EEeXm86xknD8R+\n",
554 552 "z08Qsvw0SnrJV0mO4yWz/PbvSbJxrrzHV8Q9joc8TjrMBMhWesjHSf5A0p/m0mPGtcdvkfyeJMSV\n",
555 553 "7WVcUv8FV7abXEOXS+pyIb3JhfRz6hjPssbqBnr5PVnfz88u3uFKZaCZboJPY/zcYjlJoBvkGcLx\n",
556 554 "JMkabulme/iBEs/WUOG3khwg6ad1WsO93P5BJv0jnPwIxYkYZudws2DOxByuDMzBDR032v1zaNxu\n",
557 555 "Jq5yUXSC5DgivECbYEbwOME9qP2B57rGJvwTzD10Ap1n8r8gMEEsZ8I4RQR7ieXZBE8GORK1+YTx\n",
558 556 "bvMxwng2YVpKBB+1pojgft4I8IaKGdoX8UaaGP2YaP07yV8Ql38jSREtFU+0byMoa4mHnwA8SnPw\n",
559 557 "VvbfKfMSjK3k/lb6rbVlcr++uo5H87lX/pbcMCcOEoztIJOhZpY4/IFBzg4yPtTB4fdk/FqSHHE4\n",
560 558 "p/Mdw1skq8n9/SS3cjt9WQeCU0Amb579F7M9R5Z50/yjMs7XWD3znQ+v6t093Jrudpd852c867o+\n",
561 559 "fjxjoEat0TkbnTXKSGjpNcOnNU9zzj8zclrTc3q1wWDouE19Tefsov6x2Td+mFHRQPfp9TOc8/6e\n",
562 560 "0+sdv/PRjt/5R+l3CuOGvbS6dGTp+NJVS/0QkPX8WOHw0iN7Z4z7M3JfOqPV2rEMPIudgcXO2PIr\n",
563 561 "UTHdXsjAKaZ2pZ/jhwwLcyv5IQN3rj8gTZYbZkx5UpE0Q6TUcPt17gHfAMIlkRnb1Ecim16SFzuh\n",
564 562 "+SWd3bJNDG96GaRfy7OD7m6Zyk97GXkfZFB+QP8909GQntV7dVj9axkMvUhyK1P6W+T6YDfb/rFX\n",
565 563 "GlqcXc3GLiK5lo31sbFLeHYN3+vTIZU/0X/B3LvAxapbbK4izdyM5rewXE7aceXIyb3xeO/JI1dO\n",
566 564 "/MNln//8ZbdMaF9K1obyuaF6YvHH169ff+gp8noEznwAfjwl+rTX2nYAvJYrr6uYfxzmOIdp5xIB\n",
567 565 "W8aWdiu1txUADghtbXtX1fvuZGtgNwMEMeAuiR+VbbpL4rsKYHOCy/+7morTa+UaJKe6lROUn8xt\n",
568 566 "5SzXkqymet5Kspq5yEEujDxPHf2Wq57tR3nWzTO5v7+C5FF+ITeWXUbl3E+bmabKnuMYTmQ7OWY7\n",
569 567 "uf25d3LMdnLpnGfL5PLcOfhpp/Pg7qvFg0W8+2Pq9Aska2hlV/Bse4nWqvQHfoek4+7kRHlNGa8f\n",
570 568 "V15Rhh96tXwQP+013DiaqPMl2uKD9T/QGuTkjY4NeLVxEBZ6Um/kGug4RxudphVYRt2/q4dj7Nnf\n",
571 569 "804Px9iT7kGdc2gQljHI20+SZii6vPcchH6i/T3yRXqa/UypfkzyFY74RyRvkjwPUqvNyILk95hz\n",
572 570 "NCdHmufkTN6BQ5d++IsbxsY2fPHDp+F35Tl73juze2LT4kWbJrrxu2gxfsfOu3nlypvPG+Pvhu2z\n",
573 571 "PFrfCZeeMXv2GZee4Py6Ony81OG8diG0kf/zii27LLWWGJHrwc9RzihsuxKQpoDdyk63ssPtP9C+\n",
574 572 "hrLZrGdLK7e3naeJ/SZ1fGVuPXRc/Wxpv0s4TT48nqQPREpl1m5l9rZvgLkej41nVmU2ZDZnbsjc\n",
575 573 "kXkw08qEtrRX8mvaneTQwiK/qChuK7pfVMgdAVdu5Rqm2h6wnBViWxoJrhnkudUud9Wzclc9T9U9\n",
576 574 "wG2ji0PXcqf8dgpznhoql5+66WbGKKkX8MPaq5O38AvO5SkqPHdQLDW83ZnpzL5MAAbAzZZF+0DI\n",
577 575 "tQWyzUtJDrDNiwj6VjQ31Z86PnVKyrtlhiHgwor6LD7R8DZ23ODaghsmfnLZ59et+f6av73miDV4\n",
578 576 "7wue73x8/SkXhA61NLm/MHr4X7T/gE0Y8TzQjvR6B1q9dmtkb3snfc1hkitIXifZNkIL6n8XVXYF\n",
579 577 "YYz5HTnhTe9t/wAz25XxS+NawaOVDPmvINk26lrpIXB5yGV5AhcJyfKhhMPyIbX0G1Y3wsPtTGKI\n",
580 578 "Z5lhfpNEGMJ2q7F33NjZeK7xg8brjbcbfhlMbu0kE9vlAtaT4q/F34mfil8K/7r2e7y/yV2elOuS\n",
581 579 "k89qLzEKeZWB5TJ9NS2sTF5Xd9ZQHqExesPmd9j21bbH+QZ1D2G4h87k8RTXJJ9NvZT6SeoXKd+6\n",
582 580 "qQuzV2dvyXJp7NEs04c92f3Zd7KA10eBZYY9uaA2UfNwJbP2Yu3V2sGab93kpu5r+P3rq4wHXqE5\n",
583 581 "+AmTqq39FK7+a/mp3cF+eiKSixkWHJRf3vGzuX8HmXpk5Fsj3x1BavnmCOXndwy3psTzDKi5pKk4\n",
584 582 "w53pqae072k/5lb0L8mFPXKvQ3dnew7JDhnxcsqb7Gsw5XFjR+rp1AupV1IHUn7nc+Pf0cpenSWT\n",
585 583 "j0wTDy+Sn26SBHNunLqgRhY9XXuh9krtACbbXsE1n2s41YtJftKZ9B5O+mXO8UKSt+QZJ/pjd6Lt\n",
586 584 "x0a4q+2uErjxjbulNndWwI1ytJ3D5xbHBvJjq9avGus7+cz+RVf1LcqcsaAwNlCszFm0dNGcSs+i\n",
587 585 "MwZO/ES/Z+XSaGV2bWRevTB0yvjIh+cWZy8Y7hmO1UYqzdm1dLo4sGj2nNNGs90jMq+WeiLz6iEZ\n",
588 586 "35zz/zH2JvBtXdeZ+FuwcwMJYuUCkCAAghQJkCABQqJIiBJJUCtta7cZsrUtWf05lZjEizLJSP80\n",
589 587 "sSVPMtK0iSylnUj/NNqcTgU+w9DSzkitLWpJfyMktTYbrdjakiU5E6mNrcWVybnfeXggKSnTOtHB\n",
590 588 "e49vue++e88963e4nzD5poqUW6ZCQzC+BdKJQT8ItZcRknLI3yIEGX/Ucs+8rVUJqvqEQNEisqvs\n",
591 589 "HL7DIEgnmMju7O6wtBlbRr2y1KrYlFE9EkWikhVDLeNNVYwVtFaZ3fzc+P8Rqv+P8J2urodlrAru\n",
592 590 "TqKC8We2oiH1Bvy5Asu81InP62RMSglQKkpLuxEPvQnkOIK+O51LnMLjJK5ytlOOuVxWDs9Coiyg\n",
593 591 "WMYoiDBQBrcVm9lFheX4c1FA2sZW+JF8WTS4kbUnDUvrFAkr+YLuVXBW4odyPJ8pO8A46RrOfznn\n",
594 592 "kST56YUc47wJMlOnyAl03VbbQ1F85skoIDMTnso7mquqmjvKvxGP75mxMOJyRRbO2BO/pbE319c3\n",
595 593 "2TX/+GfWQG8w2Bu0/g/2LZdO/EZYzfqygh9E3MMSJe5hJB8GfGOiPEPpLtK57LIzLN3CFq1Cg9jF\n",
596 594 "UjRSoXqQ3FC5GaLEbuchxNJ3UpoLyDmQfkgWLqfy7fNZF+c/bNQnW2A+eyTWpeQ+IQWvY0QxQqf2\n",
597 595 "iSlxVGRSRUTsnbSmpg5qj2rPwMRMxuSDuqPo6R3sgySvFd1BNpOmyFLkLWIy7PIiJne8ZtwB/ncV\n",
598 596 "rG8HFGBLsRd2YAtWuFomqUu7sIDdALFjdwvYwC6s63abn63ryRv2+2wpT+ntdrvfzm67yg5N0Qs/\n",
599 597 "GMnGtGBSSoQH139izX5qXrHlwmNeqWIfUMU2hdXmmc8v6X9upjleWuX3ewsLgBpSGud1M55dGoks\n",
600 598 "fXYG/9fjC0N9zRVGtdpY0dwX4pOYv/TdaP42ZeMykHdUySVvVU5AlOuETjcItfpQdkvOsXr4OjkX\n",
601 599 "rCF5qPI4LluCKzZU5xKVboEcAukEuxusUnK/5Pusy91Hzv2aGSvaXYlbnau8UomWaJVbohFL6rBV\n",
602 600 "R2EXwUPB48FzwSvBW8GJoPbxbduUfSfpkCv7bGpF8lbVRJVAB8CQ+IkvGT9YQ/zo7tsoIl8vcWoV\n",
603 601 "pqaK3KAxWJET4gk4scgbGoTIk4sPGBFExJ2CdXVCcSgCWQL5bhvIOQh5EA+CmIUxzEcXuFkRyDaw\n",
604 602 "tD0gG+DEG9Pf1isshXzHyhAnXkfigQr+2eRT3LNY1JbBsdML8jJWtu9ivftj7qccllXFaaWiNAiB\n",
605 603 "ZDXSOJar8XWu4oLlIMjc8YRMId7Nh/hG4fri8V/Hx3/bL3ukyD8Oo2eCbRYJ+ZLI5GTpCibVcUbA\n",
606 604 "uAsyUhFsC3KArnSOoloLwNDYS3MF+TB23aNoMJDXQUbJEYsOhIs5uV3cw/oWbnxdBt00kNyg24xp\n",
607 605 "eIJtj+jZbVR6cvGeZu1PtaniqpUqNmG34G3ugmjB+XyMwBerZUsIPKa9WtkNIRkEWPlSKw0vGF41\n",
608 606 "iAMQXQszUhyq1QlGRorYOXsw8wYx85Zg5lGS3zaQEyBjxY8NXSQuRF8ln+yI31NsPNJ7IP8MopL/\n",
609 607 "9F00cxYImilHTPyzinyhiCdgXEuflgwiddZN+Ax+QSFwjKTMBo+h1SAO49Qi+ox3cWMrPjtZleIQ\n",
610 608 "YXQILriD76KBA+4TdO79XJx5BA8lJ0UbRJ17uTANCvEhh9o9WlwwFGeB3MPzdTAj3sUHjYNEQWxF\n",
611 609 "YFd4eoQNGvxPdNP/Ql8I0nJJOND/y6WSIC39Zf8a8md+ZYpfk40lDZtrG+AnFo6Qnzh5SH8cAudx\n",
612 610 "TIdbmASb9Ntw4E204r9heJwQ05h6nB49KQd7yGEob4HsBAmyd07wmVjRNn43f4g/zp/jr/C3eO1A\n",
613 611 "zNTJL+EH+fX8Jl75m569E7pMy/PUqaqM1I+uuYTQrwJOZVS5VEFVTNWv0gxL2/GR1JlY8aSD+pz6\n",
614 612 "ivqWWjcQMyN27WH3tWFY0sgt1WM8x/KUoLRBnZpJM+jlQZCLOiQoZWKmoDam7dcOaTdoN2u3a/do\n",
615 613 "E1o9womy2ye0ae2YVjuAuxkysj+pE5/lArb2InQkj7Vu0jd+Ii+dN5bHWmc15rnyHvWd5w3Lgefw\n",
616 614 "l0tLEHR4q2ACE9cJw24aR4OPd55PBg7lgQlJpRiB58Fz1nAvgx3tx2i8A6LBn1pBvpezw8ObDomA\n",
617 615 "T0saLbzvUh/jlMkPNZ/CyvlzWK+Pac5i2w8OuoLCozhNNhxFnZb0tIMJ0IvB3oOBfXj6OF+Bca5H\n",
618 616 "9HavegUs42RN1cB5HNeu1LIDGnDhVSB3MMA0GHZtIEcp1QFb5G+J52GYezjEz3LLEUJiQ6ueZoRn\n",
619 617 "Q553i6KbF0N8++jq27xq8NLl1bx46/f4g3zZeGL8+/yc8eP8N/h+GvMT/8rG/EI25nV8YUIISOfA\n",
620 618 "ONm6sg2xECqMABpsQ6oNKiWITP+4gDKBoiIktSAHATFFH4GkGnaDTs0SzaBmvWaTZptmt+aQ5rgG\n",
621 619 "N1C25TAFtpLuxpAZBDmX3RoGHgVblUA2G5Sv/ljJWlKrEJgtdaNHl6mxCH9P/SP1XvU76pPq8+qr\n",
622 620 "avaApWqEy4Gj4St9gkmFeEGOqUfqlHpUfUF9Ta2W45qlN9ktmITOh0xs6WEi+vjd1X//96vH7/JN\n",
623 621 "/J+MS/zi8a9SnCb1Ha3p0aycsp/yMvAaTFjRT4BXdOZ0hEOK8pCNvcxevy53/XN0fTDLdlJX9LgF\n",
624 622 "EwqL9E49E84OmY+bcdh8yzxhxmGz0yzIgZw8Z2X3MiPWnbcwCYGtg+vBRwN4myXYWoLXVyOJhX1T\n",
625 623 "Psb380P8Bn4zv53fwyd4+qbZ7RN8mh9j7AmahZBGDC8i5f/DXxIPoXA3aQM+ZD9IEGQMC8Z2wx5D\n",
626 624 "gi100hUM5AkQJ8RySqufjMKn+KmHg+QQPaol57qkzQkXPhDMa4rcQZZNWl447CBxECwm7JvziD/j\n",
627 625 "q1qrhKLxZ/k/Hxf4n4y/wP+rsOXLY13tQk+XHNtC/UjftH2K7uiltkkb8kh4xIjszI1XRM0BJkWJ\n",
628 626 "cfmM/1/8GGfkWvh/kSoDYn0iYBxRs7kxATHRyQRGBLmUqB8kjGn2p4Q1nbAGpFtMkkaatDstBZEu\n",
629 627 "dyWM7glPS3I3PJTkPhn+ginQIme7twSS6ZaxFmFghBMeJOqMSu57qfBAssinWNjjLIyjBS0xC3w4\n",
630 628 "dS0Q2z70fuoVyEbzEha2i+InUETe0Z5EWMsdCC3Eqi4WfAKGvAXK2veQuPuO+SQblckd5n3I36UU\n",
631 629 "rosgv4V35im4zE7ClJansVgsXkvY0mNRs7tYPsGj4dxN7ijbB/1zH7SuN8lOCkPqZbhSboLUBvnh\n",
632 630 "1NbgruBBWE/s8K7YmnCoaVfTwSZ2yM/2kkeaTzezyeFvjjb3NYvD2aCI67kwHnqDD7CCfApC7f8c\n",
633 631 "7YSNKAnDCRJpcGCFBeZmyw008IJVaRSy6KUbcOxfbrjRIGT9a1Y8OtU8yh4tWZr5Yc+U+JbZYou7\n",
634 632 "WusLkwILx4/b6211T4nt55+0L+huMrl8pTWtNcWvPjm7tuu71Z2N5WoxJopCxZKoc2ZD2axVz9fd\n",
635 633 "UJtq3dYqs97mjzirw/kbI4F5pf6O+l+WzTIV15oC/mJ3qDo6x6Uj3w4bf8J7bPx2ZHnS99nAtklc\n",
636 634 "mPVbMAxtIXwizJq7PTwlDmH6NU/TNW6Ji+IaJGv2Y+tEFPJwdE8UWWK3WyfjvmcJLdwXFKu/FGtI\n",
637 635 "ERN4kseFc0zFBbvRK1KOajglizpMzuUysl2FsR0RIjZUmLQ4JmoHuG8xtm5jclSySHAKFDzqjoRe\n",
638 636 "nvXc00LLdm5Ke4/l2vsctbdG2gb3/ZXWW3Df0ywKIlt0Wy+O9t7qFSZjJD4Ta9gcNXBl3FKxTXoq\n",
639 637 "zPhl7Ck2A58ywmgqDWL2tYefwmxpD4wY2GQNG0eMqgeJdmOijNIZytPs5ERtOlEbSPgyUmM7TpZW\n",
640 638 "NbKbhI2J7nSiOxAr7u8e6t7Qvbl7e/ee7kT3iW7dQGJeJrlnXmIea+GC7CULMAhB9iOBe9vy3cux\n",
641 639 "aiyHLrac9frYcoUJLGZTfbHCBOJsJ67sNLGdJoUj1LOdeuwsZnyHzfr2xU3yeyQ725e0s6nqbA+0\n",
642 640 "C8MJQzrRZBwxC3ipkTL2w16p3jhSzS7xLa7P4ig01i9GI3+NtOAP8XJx40gXO2NenG46L5AMzovR\n",
643 641 "29Al0s/wIssYSb234P0FHy9g0/OPF8hxBCshDK+FeHkS5p4UyDHYHg47T8GqsgaK8CjIVgIdANkI\n",
644 642 "w+MukKRfYQpkabyEnONPQW5E2MPeiPw48laEPexSG3jN1rZdbQfbjradaWMr+gEM4NMgx0BOImWY\n",
645 643 "ErTXgrwOchIZxDtAXge5BMfnpR7IsEisPwxyeiHYCchlkAv9uAHIJ/3glk988gRmBb3jOiJ40STe\n",
646 644 "cRQkCUYyCnISNpfDICmYHk4RwVvvxQu/BvIjvPArIEf8kwznBt77Q2RgX2q5juz21yI7Ivvw0p+w\n",
647 645 "LpB+xt5ceh/kHaRav9a2ow35TziQxHt/D2Qv9QDIVuRVv8BePrll3k72AZPnu692s58LPdd62HXv\n",
648 646 "4J3fw5tepdddiIzy/mv9MC89gQOM5AL6tFO4XmtraDKfGszO65vC8kKtSLnOogNRmjZ4o/DPpd7W\n",
649 647 "qsqQ1/ILc6PXThxxpt/SZXKHXJUN1Y68Fu/aSOuQw/3sfLDEuq4nfOv5cm9twVOz/NWD0fDSYutA\n",
650 648 "qGVJuIwvrIrUWqy1EVdYZ3LaiGXWzXTr9a6Z9Y58c3mRv6mxJdreBH4ZiDoNFZ5AmaEt6Kpp8dYF\n",
651 649 "azqXN1U+lh9uIv5SnnS1BduYsB1En3JtSI5o29OWaBOnxF4JCeECV8Q5uW7x39hMTIQyieqMdALh\n",
652 650 "BlyoGjPkc+gbH0FgYZNNzdT7Ivn4n8KTehNkKyOJkDExOyNtmM0PEN8JkYtdOuBgf6o2JqoysYKv\n",
653 651 "V/1R1Z9U/XnV21XvVmmYOgIT0hcwZH0E8iuQIZAySxXu/yrsuztB1oK46Ghy1HUB0DVuuQ2RHG5N\n",
654 652 "UD7wFnxJoyBd8l1OdmFpB5krH7iE+bMT5A6IZi6Tb2YzBpihyAGpn5GReewtO3sVNtbG+FObItt5\n",
655 653 "2Y5X4VyU7UhKnLcNjUOwMzAJ0GNkV4DKk/AakTgtaSGFuelEKYomXwaZ2+bFgffRls9B1KxB0kvY\n",
656 654 "+iHI34IUuefipDfR2VfR2W3GERO75V5k8JXJd3gOnfQyyA9B3kW82q/K/hmSimuumyKvMGlfRw8H\n",
657 655 "uTIc6CojJeQueof6aR7dijFJsL/XoNYfJr8WwmbXaF+GYNUKNvG5DvhAujW6l3VsNpMW2Kuo4VNi\n",
658 656 "v85QKBikl1UgFL30Y5A7iF19E7LLXZAtIF641ntBTsIhexiEhsANkJsQaXqRl3gdfPUF8NWDIGcU\n",
659 657 "sSv5QuOrAIQ4CM5/BuQGyLFJKA4ssEdBDgPJYe8sjBSQwyDaTsjmRGJoCIhvDiM350KE3pLLzvgE\n",
660 658 "zPIlCGcX8LZ3QLTogdFcfPTrIO9QNhfIfQrdgr92q3kXpLYvcu/bA3IKr/ox3vIlkI/wquTh78P7\n",
661 659 "3iA5Di+9F6+6BmQU5BO89JrGl9lLp/Y1phpHG2F3x+uuIa8W3nTfLCxXs07NQqQBXs5PBC/XB+LH\n",
662 660 "G37E3tBUKOa4oEiIaNi1dogmq3sKP8yF/MguDq01Ehp3N1UW2mpbKioC/ppiX9TfMLvYateXNvrs\n",
663 661 "m3aveW7Gc/EZT8yucTTMdLlCZcGY190RKCtvaCv78997gn+m1O2bUVbZ7C41lnst/O66UFuwxFdd\n",
664 662 "oc23u+rGt/zsheicspYFQV8sVFtU/VRTbUe9xVw7y+eONLgLDxyYKlvdyPG+vyXe94bEzYIsiE+7\n",
665 663 "CSSI3RPoju2z9qA7NkHU2oNVI7hAJsMSB3ICu9uBRyO5OnCPDrAFtpUc67jdAdsZtrd37GHbybGe\n",
666 664 "21h9uB54akBcPfhjzx4cDfbINnGunzcJ85m8qeFek3iOaVg87G5JlxCEyClmpN9C4ldxPHmrobwm\n",
667 665 "A5pOjQBjmfQA2jAMudykCQHcSJ1JiGl2EbInYvq/Vv2dKqP6tUo1IH1GWZdTTW9DKi3lm93F2L2n\n",
668 666 "YSthyEQ2l9bg1Y7jxzuE98aX8OvG34RsvIr7VJjDf53zcF8yZpcwZSD6mgAkIMfJILQNbfRkpB8r\n",
669 667 "Yg/2fRkuoQ4kCjLQp0vSiRK2bRyxCQQj4RIonoIieVlDYvp3hJMCUtlUAzH9YfGUCO2NNX0nLA47\n",
670 668 "DeTczbqHccYRy2nLJct1CzsD0W6xAoPH4anzzPTM96z2aIa5mP6ocEa4zEYBzj4qnhEvizdwP4AK\n",
671 669 "JHcaDhhgxE+ZR80XzNfoju9YTlrOW67ijvdwxzwE09V62jxxj3oYwFLDvAzB5JPj4LS+DhGjPhsL\n",
672 670 "FxHm5JVV+W1Gi8VRYg+XF1c6TNq6Rw/xcWNVWTGvyTcUVhVZ7Yba6bvobyc/xP93ysEchem7nqwY\n",
673 671 "mVRQiAn9gjic3CBshuvtNtCP8mG2TCLXjc35TsMSw6BBHJZEA/A5krPEBQBjQAgY7qHKSFdkc6vU\n",
674 672 "DKPCv2IY1WBrN7b+AWQudp8EKQHhyL6XT4vCZQ3l0cHoqu/XDengRGCd1a9DLl9GqkN+2wNwwdlQ\n",
675 673 "sou0TtgDF+Lo0yC/wp+2g+ipddI/5DAeWmBauk1IHY/momkzSiLvElz8JghHaFNs4CfhgoGOlkaO\n",
676 674 "oJCRzLDOU2bMN0Ei2P0cWypsQY3OWjt76c3oPtKPYQv9TyB4R2mZhr0RU+0MBJCSR9AZ+WRyO0wW\n",
677 675 "cZhSj2hOayAtU4L9aZBjlJODPFRkKZ3ViAOpg4ajhjMGccAUsWqtWsRH+iLW1tCD75t/sDr4zDPB\n",
678 676 "1T8wf79daG1oa9hY861v1WxkG9s4gatnMtjnTAZbyH1FvU5qqWMjoM6YmJuRtmHln5iLr5mRbisJ\n",
679 677 "R9J2SGI8Ywu8/J3bM9LediazqJkaGGhhr7mHKRmJFuNIgZrMKQicaTEmXOmEKyBthwgwxMiIk52+\n",
680 678 "kGtHhyxqJ5CIucaRFezgAB1MLhkaHGJfYfeQ8okC7BMFFCmIiaEjHkhBAWMinpHScdwW5HYcrQJ0\n",
681 679 "HkLrY5lEND2iY5M/QAzkNvKauRJjiatEBHAiHNoTbLlLFZU7ywPlbCw7PQG0xRmQhqBntMfiaOGC\n",
682 680 "eAxHmSr3JLtXzDiyWpDzUmDJuk8E1q7TcDQfLDpahLAXDLD7IKvgDF6NLQO2PsUW8gslHcVdg9zH\n",
683 681 "7o9BzoKU4ZgVAdWfQ1DZh6X7ldLXSxEjXmotZQOdsA1PYhknk8wamGT2mVNY3DUIxgqDvKKYlpIn\n",
684 682 "7efhpVbbzXYPvNRrEIW2z55CpOkrWP3fBDkJae5zkDUg+xxgg4DP8zrCDtVw8nXHmw5EFOKPZmQf\n",
685 683 "7IO40IOtNrh1v4C97h1I7oCQk/oAqtVWG6+Fe35GfAY6BTG/KyE8XAfpAyEVdSZIaTObyWuaX4Zt\n",
686 684 "6HVEmphb2V3fh2lhB8h+kPNQ3faDLO9At3S8jiXxNBbTPpDLIFEIk2dAbnYBjaK7rxtiDxOtkwd6\n",
687 685 "jmBptGGZjPb0YfsLKK5tvbgDSLgPEg7IfkZS5+dfnf/5fCbajGKt7gXZD3JsCZq8DF6TZWghyCiI\n",
688 686 "dSU79tkqtpUCWf4MlMtndjyDvn4Gr/4MlvYPMGpWYixcBzkKosf4oOFyFruUsRYFWZ0bHwbs7sLX\n",
689 687 "95cyfeoPSr9Z+kYpa5wDKCkzQfJBCEnxMkgUI+Gb2PpTkHzs/ghf+3OQl0HWKvJuSmu32n0YID/K\n",
690 688 "DYVuxJK+7HgNX55AFTXAGAmDrKWTMAa6MQZaMQa8IEcwBrq96GFfnw8KCYZCtLYPQyEyoxdD4SKG\n",
691 689 "wnmIkOGGHmjr1zAEVoC8jG8P46C0Fls0FKwYCltgjftx+C1Y4z7AYDiIcfAByCoISRtB3uhQhsJZ\n",
692 690 "EMKYO92lDIobIL1QtLrx1d8B2QdyB8QCCSrSg0b19vSyp/wMw6AV5Cq+eQ/IPpALIHdAjmIc3FwK\n",
693 691 "Vo2vb8OIMGMI3MXX/whffAvIRpBdIKufgQAkapiIa5kas6pAmoSmgi9mY3mqvT6yhOYi3kNaXAWp\n",
694 692 "me4hBNuWqtT5eY5wfXlwyfMtc762rLl52dfmLNjq77TNe3KgadGWNbNmrdmyqPtbA+FAd391ZY1K\n",
695 693 "sM6cEVtc1bG81TPbyO5lqTDWdDTYW/1VbXU2wTt+raAsT5fvmb24LvqVed7WlV/v6Pj6ytZY1D+n\n",
696 694 "wRZ9fsvixVuejzY8OTx3zgvzfeVOm/upuS3P9zfVNC42e8qKBH/nAndduD42Pycj/0VORv4lyciV\n",
697 695 "EoevGoOicAKWyO2te1qxzAX7sr6B+MRnwizhM87ENfIVUlUjW5tkxCmkDDWwhYUtmg3GhDktJaBy\n",
698 696 "UCBnJ8hgUAkuokTDwqnBXIoNkEI2NQ+r1eVkA4TOXAQHQDnpthdg/YfIBmsOzPT1XkR7JY/Vn0XE\n",
699 697 "+E3E812ulwFydwkHmbwkQ1MNJHeJB/GzQ7sPyZ47dPuAQrUjfx9yPncU7EPOZ7S4Dzmfu4oPIvdm\n",
700 698 "FcIWkH2a3FVyEEkbgH1KRip7EaCdco462akp9yiCriM1vTWImkYmruehXFE+VBySJUZzNtLI5za7\n",
701 699 "+d/aG+f4/fOay8ub5/n9cxrt48u6BOOMYMgaebrL4+l6OmINBWcYha6bwLWtaJ7ny/4KTeN/bKu2\n",
702 700 "GDxdq1tbV3d5DJZq22KyZasnfsN/lckPIvfThIgMZQgHE4hELeKdvDCc9fFPQGCBoZuwXB4DhaM4\n",
703 701 "rZQMquQqbh2MGVsp7PUgd5Q7w13mbiDs9U3owW0CjvcJq4R1wkZhq6BmS69wlHV9zI6+XyWuEzeK\n",
704 702 "W0V8AEUgz2eyEa9VR/nATOFvS7+sN6H9T0z8hvsBG2Mi15hQBRC5mG0tk1ynNFMZQgpAHYHyuJ9o\n",
705 703 "bxc+e/AF7pM/8RxfLGS4AJ+XmBFIDs3YMEMgZKAZlOKXh42RCjZmS+hQgMm12wK7A4cCxwNMrrUF\n",
706 704 "KjDQOpswbg0BIFtxBk8WzmUwi/+bJ1DQdoANTBtHcorHOFJDokhMr+ftvJ+P8iq2EBMkFfpHj2Rt\n",
707 705 "vxAV2NF1kDa/aMBRQ4Ojoa5hZgOg5/SN9kZ/Y7QR1zViZUI4RUyv5a28j4/wOEUjWASvEMZNSIMj\n",
708 706 "FJh1MAncpdvpG+wN/oYou510r5EO5O5pag63yoZMMw1P0trNlaKZBioOMkYHnOX86khJpc8caysx\n",
709 707 "6wVzRaVeX1lhFvTmkraY2VdZEqnmO2a2Su5gRUF7ka2y8C9qmioKeIEvqGiq+YvCSltRe0FF0C21\n",
710 708 "ziQf/W8nnuPu0bdwsHGVHBTXK+FebHzG9Ju57RyAAFQUsWPIwBNawkRPK1eCTq0gf0UNQZ3NSE/7\n",
711 709 "JgEPQewE2DcJ8LLHUP4u7ANy0z6I9PlD3SjVCNmgniT6EwlsOLAWfblmsi+1DdYGX0OEPo260dzo\n",
712 710 "aWzFp7mDTzPtI9/DnXSCDXfy404zQQjWZ60fd1I3mBs8Da34Krg13YLdv9Ha6GuM4NNUa8xynKgM\n",
713 711 "cU32ldZGsVWOJG1V3HChe//upxH+o5+GzZM57Nt8xL6NlvNBXweuXBoquqB+wAGwRQU0FupZRP+i\n",
714 712 "L03uYjZQikMfDQ399KfCvgefhcVX5XtVsXu9S/fyJzQBKYboPF5NKRhaWCc4ultg2t2sbNq2sn9V\n",
715 713 "uNsaMT/8YAuNmatCjM8j3JgzEqdBLBN3HJgjJzRpGELkWwLcMMN9iw0qG7RQYBRldc8UfgJaMTuk\n",
716 714 "UB+EqRUGpyGAaC8tQBFpmqb4Uf4Cf41nc34/RkjefuGwcEq4KHzCOFhMf0R1WnVJdV2lQoi6+oj6\n",
717 715 "tPqS+rpaDd2PzgaLky0N08+WDqrIaJG9AAcYm+Vlf0IIRoNwhM9TPdHR8YRqhnam1ztTK7zX2d3d\n",
718 716 "iYpoFEc9yQe1XE9CBzQmREFOaGSFSksgKxrg5EwJnlO4oobtaGTcP6wA7DR1Rv5wIuvqH7TjP/bi\n",
719 717 "jeO1479i362PG+F/LpjYul4gGTixnpOK1GI9u2CayNMo9JUHYx5PLFiu/PK/P3WP/XJZ/PTf8n/P\n",
720 718 "/xNXJXRLVUB/XW9nSrVdDrFZDzt/Ech2GbIyUZxOrXdvcm9zM8H5Fgydg25Em2BrCcimHEBlJXuv\n",
721 719 "SkVgKGU7pWRtryyVOXPKxQW5GKB9Kml0AZ2wgDEDY2mlDGCQBFSoHCRwDMrzv4H8GmQ9wjAITe8Y\n",
722 720 "yM0cgOBNzOuwugfpo/thJNmHKY3YMKkVSmYPyD6QA1ASjuW8IF8gveFD46eIaF4PNeFdRDRfKL4G\n",
723 721 "6aK3eAUCm9+FBW0popuvYes6ZJljIF+AGBB8MB/e/LPWD6zsNn0Q8+/Dkn+s7Cws+QYI+kdhIL4M\n",
724 722 "ch/EgMDv+SCE50+uuXdh5V+BPv8AW0Xo3/XoVUqJly4p2Soyig6GqnQAr0ngDfvwNgThj4wNKY53\n",
725 723 "6StGwmDxTYhI+RCRKLxzJd7kJoSlG5b7EM7oXQ5DJblLuiladTiXSnYXiVInXedztQeeQgtPooVX\n",
726 724 "QdTYvVglYwx6ZWEqwiaOVaPRamSB3GqxWPn/Xjzbx4Qnm40JU77ZxWH3ymD0mTnV1XOeiQZXuvk1\n",
727 725 "jhq+vLm7tra7uZyvcfj9Pp5nolVrKxOxeN7nl+PIPhJa+AKyyW6SOJ6N2U0E2garCllnpVu0gA8K\n",
728 726 "64VNwjZBNZADcVLxXM5OK40BnUbfrxnSbNBs1qjI3nYLEta3mNxiI0EMF04gRKVIdGId3IDgOhVP\n",
729 727 "YY3qNKapj8l/oT39X+166qkuoWX7iy/K2Ow0p7r4L2Rs9hPQ2LYzAvGlIy1pazpkbE8Fpn0KQjsy\n",
730 728 "pyoyWZx26U9hGHgLcuzRyjOVAgG0tzIhvtKY6GKthQd5cJ4y5Qh/XZlyXWyn6yH89a4s/nqXjL+e\n",
731 729 "DDbEmP4ImIUgm4FdxpGZbAbO7iIY9tlsBs5eMptmYBLpOuz1oZ0mz1RfRjbjEfiMzzKSOuI77buE\n",
732 730 "IhPIS05+2PApu2fyQOORHMb3LwD6HW7ugXViPzLDW6F39uTMEfvg5OwFOdKOMQ1yE+QXtDUb8xvk\n",
733 731 "r0GuQkm92AFxi5Txk1MMMkcxFM+CTG0p4fOfAklRWhHIKZCPJ7HJ0czraObBxqONsLo0x9HaA1Cg\n",
734 732 "D4SUhvagjfvJ742WXQXZBzIK8gmadxnN+5QR638EGRxzQvvojHE/+J2w4JFKR2NuvoQfnU26x+OB\n",
735 733 "L1AZTFPmkf/ReUaYij7un9kMc3KdkraQaYy/Y1QCTsmZmcLm5WGWKMrAzF3Mlq//58uH1v/Ot+vw\n",
736 734 "P775S2mdXTxxhP8XoZyr5PyCSVIVAU+Z8bqUHC/KBiBQO6UTIPoilQx8nUzrx4BWFtTH8LNdv4f9\n",
737 735 "JHQkOumNCX86UUp2Yyuh2znSqPJRo3ogv5yVvZzV8FDYL4lEMjDciayZWpchGHJsMrXaCqBlUEsG\n",
738 736 "tCwjnQPIs9U4UsmudNLj/GSmpqULgdvJ08IlqL6yGpb8QLwJbrMSgIr6I/mn8y/lX8+HB4V8kojE\n",
739 737 "14OpXyq5ztTcJNAm2c8R02mUC7mHaDM9LKB3YYUs0DqsDp8j4uh1rHBohqVdWJI+LPu0DFeUn0ae\n",
740 738 "80Ew+BvllEQF5RPuHWEgtUNA6hBCPrJJ2my9QNc+gLiUl68qV81QtasWAgcUGZPSDeBtH8s/m/8B\n",
741 739 "ILiPUlMJKIM1NXWy5HzJVSBiUijOSbRRjTamsHUX5O9hEvstNVl0mBxuR8gx1/EUmoz6JMlLZdex\n",
742 740 "iv4Ya9MBNHhyKT0LAi+zJyxPH7MbsjjJ5CGzPMN8WtlLauX7a9qKwq6lDcGmxeGKivDipmDDUle4\n",
743 741 "qK0m6ArVmEw1IVeT4Lb5vbXC7E61f87SYHDpHL+6c7ZQ6/Xb3IKgcQU73O6OoEvz0Fr0HUmDWNhN\n",
744 742 "kPo0kOUIZ48tMNwQt4HbDJWJqfe38Mk3o78ENcV0q8nfc4tk0EHVetUm1TbV5IolC8y0BkGIFCEd\n",
745 743 "JovUTog4E7jLBrhHBFEtR4S7fVp3JMQXYDX6ar/Q8uKL29m8WTbxBfc3/GXC6fkWSehSP1ZMeCaS\n",
746 744 "HGfkhOFYnqzXIVdGPZCSA28h42lkV0U+CbAlaekW2O16+yaw2wS2Y/Z+u5KrQxYjw8OhtPpMwmxk\n",
747 745 "zCHYFCmV7XDgDcVTtv+m0marnPovaK6qMrN//FeyG1n95xvceW6QydmLCO43IwMr3wOmso638bW8\n",
748 746 "OJys5dt4Nnrl2GJRTkwWmIiJuBAFCpejSOMMYbWxi62sz747d+4T3/+UUnw4+8RvRK2MHcctEH8i\n",
749 747 "VZaK9alNpdtKd8Ne7CxlU+9c6RX4FJaUDiKLe0yG18Y3nyBvD0y5Y1i6E5Vy4RSm1hC8ZSmyqJND\n",
750 748 "wQ1BBHcE2Z1OBNPYjrHtxMyM1I8qFRtmbp7JuFRtOjHTmJjFLoXzm5tlnCUMJ4LkYSLn0ixjYkFa\n",
751 749 "GkLQVBBBU0sWDS5C5uCiQ4sQ1rdIkQumla1Sgngh7WAEpTCaAmoRQj/7SOTeS3FFxiJXETvmNibq\n",
752 750 "mYgCK91EPVWESLRmkmOttxH3OKuVQvdmBRKtTKxhl3bg0g5jh6tDRMkt+Jcm4nhE3BkPxMVhaUF9\n",
753 751 "K65YAE/yJIO5AMtIL8jnkLUARAsdn22ndohIV8Q5uTJVADOT4kDF26VF0iL742X4Gr+ARunQ1jGN\n",
754 752 "MotZn0LZiKOIc6HCEV8g9MWgc+jqdKwpUbCrXfkH84+CXV1G3Md8pbRQalfBwYKjwE+8DA72BQ4Z\n",
755 753 "ChwFdQXsQhXE58+hIJiLPUwzSO0o3lecKhazeeAREDWk6zBcZjtK9pWkwPgugPH1gqzMJUYuB+ci\n",
756 754 "vAaC8V4OyeUayA0C44GJ/3TDJZj4VzLZJHWp8XrjPYSKUIzMq5BNVoFcBjkLchNkJeSVLSFcHLoU\n",
757 755 "wpKAAxtBboCsQCjja5BnXmtD8kBY6IGbWoXOJdD9HiUCW4qgTyO6Xphe2wriiMYmHEBdDuaIcP90\n",
758 756 "eN2Z2CKMvxUE5UdlnvAKZxou4xUowHpVLsTnPgiJhyvQ6FcYSZ5qvgip63shxCiF1oReDsF5hDbf\n",
759 757 "wetcDH2C11mWa//rrP2mh+ArtA8pwp6pCOdsAXBPB0Ff1rT8a11dX1vepPy2t6/Zsmjh1jXsd+vC\n",
760 758 "RVvWtAtzip/vDj0501UZfTJUE2uuVrPR1eJt91us9e2elh4dPzT368vY9S/Nm/uNZcGmZS919299\n",
761 759 "Lhp99vUl2V99fKl79lNNTUvbq03eqLeurayxo8bd2VQxq57kmkpunVAoPM34TYTvl6xAUUNVMmkI\n",
762 760 "JOaXgdAYH0sCw4J1QBqdnAAZAolZZenCoH7ASa5m5CWyDwBMMITkJpohITVvbt7ezJTtZjLOQ6SP\n",
763 761 "lPaCg8muz2TE3IufsL0HjH2f4taSredZcTrq7oPtvM0bBzgBwIoUJ9Vb5K+sj8OifxS84gA0gbZA\n",
764 762 "PACZJnAUNXxScFNEWnvhplAeHjb3wO96GO61I3hcFkblrcpjylOlg3h0mzs+5dGkehzAMyP1vXjm\n",
765 763 "fjwuEujFc/ZNPkc63Pro+PBYLFqLBm5+2cJDYEgRbxgufzlUNGL9vfLmLq+nq6m8vKnL4+1iMvIr\n",
766 764 "jnKeL3e0zmitWlRbu6iKbTxyhP99X3eosjLU7cv+DlctqquTzy0rk8+cto9vXzvxmVCW9dPEpdKq\n",
767 765 "ST9NqXHELPtp2OrZkJaCOT8NXDTS8aDiCyAHzDTXzDSnjeKnyeFGPs5PE9Mfs5y1fGC5ibAa2UUT\n",
768 766 "079Tf7L+fP3VehXpgD8S9kI2/ZG4F26ZsNhDThrGggWlPk8S7BSnFO+F4SRc3IOfH5XshTMGaDmS\n",
769 767 "vpLPYuUMJ486zyAUW4+MdiBjJY+6zwCyTA+LRxQkDkyco4ikuw+ix24UBHE/kt4DpHhwaoUrzcQj\n",
770 768 "Tjv5gchD35x3t7qnewatoeLQBnh0oAFBE4JHh//5f8yjM35r8cMOHf4PmXzin/iOIFLtgyffFtUa\n",
771 769 "VT3Z9NJZN85tgc/aQsn4SQZB2cw6NcNQ4vJUsoWMrGIa0hb00BZgyAWSQ6g1xEf+7u+62P/5e+1f\n",
772 770 "jgoz2/9OtuU1T3yH/4Iw8f8ze37h456vlquQEPyOgShb8lG4BhJeIex7HAyYOSgWapFJN9kiSaUj\n",
773 771 "HBZVYEQr0OhUUdUaEUjsaGux0lb2jwniZDCW2/wu+6+LiKBqH0+1nzjRzs9vPyFjUYT4ffyAkCaZ\n",
774 772 "KyWVVkJGZYJWVqpaIqcyQYwifMnJoo05SF51BsKKPZPFBiJ5k6ewnyTsvuAMWNZGQQgelyBOTiLx\n",
775 773 "MwsPdxRj6CBWsTOA5coqY3IxkgO47DQICuAkDxecwhWHccVeKjNYwv+7oLN8g7HMYzZ7yozKb9BS\n",
776 774 "C1iJWovyK8ya+mf8Tv0r+0VfTVwVlvJ5wjGS5weA/g+7cSZmkg1ZMoo+bO9jGj0TGzVGDeuOfETx\n",
777 775 "JBHAw4R901DJhpLNJdtL9pQkSk6UpEvGSvRQNcwBG5R4kVgOBp2pJVtaF5xyyvbecqu1HP9OKBvC\n",
778 776 "66VOZ+mUf0yOXsytEBYKWmpnGbdZ1jygFCWhEwmK7pGE2sH24FtinXoF1kzYzuWiD5se0kCwbSM7\n",
779 777 "uZ0p4ECUGQLhMPe3V0CJtTsYE7UTkgpTPRwEV4bR4Pkd2oe1qrWK/6OHVZAz4wI//gNFDwlkN863\n",
780 778 "s/eqn/gL/r5o4xq4MDdH+LZUDr1vDKhB6fIxaNQxiHSbQYZA3GoCOHEHpH7G5xgvT8xJY+aw8Vqd\n",
781 779 "STSmYXxgU6g8G3w5YlITkIIvnfAFRmrZDuPYIRVFY7Yrpolp+PqEDwzz+pwCBMqjhOLEnKzH7jh3\n",
782 780 "jrvC3YL6WUBzvgDw9KAmUBSqTcwhvKw6K1PUB63rrZusItMirIeYrJE1Zyf7rKusbBRVUYuryMXH\n",
783 781 "aG0mubl2e61A1awa2Y0ayO4RxmPD58JXwrfC7LEWYyICppCS3criQGqddqN2KwT3F3I1UrWI3dHA\n",
784 782 "Xr0flcJSxaPFFyBTixAtyWhAZve7sFifNX1gYm36uemvYPDYSYFekFgoGhuxV9LH0L1+BPI5bKcv\n",
785 783 "V74GseJl52tYcQhw8TmAKb1WtwNgSheALPQKDhyuO4UDVyHDrGwCE0CbEX6EqU8JRRo0dC+s69Ra\n",
786 784 "qt5Kgj8xg9+ikQRPdxVEi9ZTCdLLILty9Vr3o6n7qYoPWvkqIuZQ+lQ6yZqRerkOLYO1BC2j5q0F\n",
787 785 "oaKWK5AM+A5SakYZMU1aOrCsab1ebXGlKPuK3a1ery9SKYYiWAvDXp+YNYKMV3dH3FXhbk+wpm2G\n",
788 786 "u7DNHK/xxGfWOCPz64M96yvbjbW1PmPQ1Ogr4xdEg2UzXCWmqhl2Pm5wNnY11vc0V4g98wptTmPQ\n",
789 787 "XcWPf15Q1dTdVNfdXCnO61J11tebXZY8Pr+yyROaW8KvEMzVDQ6Ht8KipzWqW/gK93NaI5slDcf0\n",
790 788 "6t0cvIVsUMjcS1Q4VkrmYCL4kp7xJTEznR115zjPf1M4Du6/gtFd/PtslvYwJT+JSFxheERUP5A2\n",
791 789 "w3gVU6Jok0PqDTCijKmnQVooYpSS7S1X8dnV1cVuSesU4nEe8GOcjvPwp6QaB2Y+hJMTIBwTTmCh\n",
792 790 "RwWhfqTPOGQAf32aoLbZn+QZLg2ycQHwbyrB1Q+yHgbpbSBjICdyZbmcrCVOw8MynJOcZkx4Mstm\n",
793 791 "Tuk24nM5PT9MMZtm40gh+3Ox2Yk/Fwek7cVyYhyVTqGQ2v1grqug5Mmw3yCEDt8Dsh+Low9bvYBD\n",
794 792 "IOjF1cD9WWl8wcim0hbjTjjLVmIKbMRkiFiRGGs9DWYRsfUC4Odo+ZlyWBkrTgMtMQoGTcot3EQS\n",
795 793 "ByzDJVTW9wChjuH5kVxLsDgnw/k9+ew+1nwf+5Hxdz9BQ1ZgCu4g5DWq8WiEmXSFca3xFePrRlW2\n",
796 794 "LUetZ6ArUZoG+bEAfygdBtGiKRGgN16oulYFtHhZJdSyqSJb4KcFkZn5ZH/c1d5QJojlje3u1kWl\n",
797 795 "obrnZ7asinlqYsubW5dGK/nCuU/Z68IVYXdHg6PJVdfaqMiG3s5lwUol1+EB5Xmty+bK/oSNJx8+\n",
798 796 "/rC0HiNgCUgAuzQqOAQBbvBu9gq52ooP3+NpukclXZ4MeDu9rJe2g0+M1aFfb/uzWEXydcdy1z1H\n",
799 797 "17XRc2L6Q97j3nPeK17WcRtywzEInRwYZuzvbcfbzrVdaUMIedu0PN6pbdlE93RI27M663ByzH/b\n",
800 798 "jzXeb/QTcoDIhdk1d7Iy3iL+r6XYfDZ79qDUahEyWjZjawgkxghihaEGzTdS1bAYFpFEJDBSSsew\n",
801 799 "0jQEpD0N/MBIJZtt7fNjlDIGP8wiefvsImUKTcPR7mY73Vgr3c3d8qpM2lA3IWhDP7bzD6RaNaVd\n",
802 800 "tbuzdlVJi1D5FflrgSsMmOzkioK1kACBAp1cWfwCnKuwyUtnYRhaWfICVB6IkdILYOjrAhsDWwNY\n",
803 801 "8KC/rW1hh9a0vtz6Wis7tBYpTadms8vOd1xFYsqKjrUIzD0FT9L5yfK0yfNdV7vY8ZcRh3keEeYf\n",
804 802 "IOZ2Y5yd8UH8ZpydsXLBC6jI/AGSe1cufGEh2gDD4AcLby5EdCBhg+gx50/m8P2AhyydgplrBRYw\n",
805 803 "WOvlNNIVIGuU9kqj8G6Nok0nFz4aNCc+tK+Vl6VctpFS9p3mVTjC/6XFF612t/ksFl+buzrqszhM\n",
806 804 "1Y1lZY3VJuX3A6uv3OgKx2tr42GXsdxnbY97Zj/R0PDEbE+8/ZSzFZe2OrO//LqyxiqTqQqX0u+3\n",
807 805 "dc665rK67qDDEeyuK2uuc+oE43M9jfNby8tb5zf2PGdk43F44gvxFW4Nyadu7qdwa5VAkobcZFXM\n",
808 806 "9pN28THuNpenCK0AUMjHaflD+RvyN+dvz9+Tn8g/kZ/OH8u/nY/T8o2Ma0FpseE025Btg22zbbtt\n",
809 807 "jy1hO2FL28Zst204zWZkvFIazNV6KGPjswwsvigNQbwMCZgJc5rE8akSrPp3bIuvKBLs+NuPbimG\n",
810 808 "9Yf/cTK+1TnxOl/MesaJ+CKmmaqN2bJPfJqTBLjaxwSwAA8TmcXrD2zi9XNdnHIt/xldW04XBqZc\n",
811 809 "yAvTLuQ/G8/nP6MLBaYjjAoL+Z0UO9MC1VNAFCT0VIEQoXg5bEarhBeSFisDJN6mcnC8uarVw/4J\n",
812 810 "CyGws39Ce/uO9vbsWi1Ui3mcn5utegKJCeoisT65ybmNSYFsBU34MxLn9FOUKpSS+dxq4Nu8hu23\n",
813 811 "uXehi3ydEKaK1FQYCba2X4P8HORDEJd8+acweD6NsIgPsFXhojwc4Acn91WkKuSnudPQABAvkwRG\n",
814 812 "Kbu7Tz6PfNGvMQLfY50s57cwnrYEKWwBkEFIe50gm0A4ZAKOdWLE1AQACuAMTEbZKKJCrj6irxZC\n",
815 813 "gPQynvIeyB+BVDhrcTSD9fAmGvpWxTEs05yPoikJnsxaipA96T16U/n4dbzfJibrIJQSJqqoHLL3\n",
816 814 "PM6PIDBnK9Tlb0Pt7hGXQ3TeAZMyme+1YD+ElbEV5CbIUkhlpJQTasZbIK061kejuguAPqMkzR6q\n",
817 815 "N4stNTKQWkEAW5SM5vXlAUcdjEyPgjbR/D7ICmdy3sczYGo5l6mMkEx8mVI79ZDP78D8eAfhLyhL\n",
818 816 "kvym7Q0gR6+0s9tttG+FPfQgpPU/gIFyl+Mgwvmp/iZCfJJtFfEK9sCVkOJ3onPeAtkFEecuAqK0\n",
819 817 "MGT5yJoFkoSY+DxVDgXpqUF9AW8PrJphLP1hiEURfKBaFB5ZhQzSd7GkvjljMt7/TcoAgRqwC+St\n",
820 818 "JvREc18zLHDNB/HTFo4j0v8AIv13gRwFOQCb+S4KZEBeZQRepTdn7Z/FLnizfX87Voi1GPyvCltg\n",
821 819 "+CBHC8GSrwShD0c1uI7hSxyjNGF0MWlnyxEAdZWcGkBrfBVgC6/at9izPSnFEQ91Jle7NF422Xe7\n",
822 820 "siWAWetdBxFgdAC992MQB/rs39CP9dhaANIO8gxIIUgvDIJhyCzUgb205QNyv68Xtuk69OPT6MfT\n",
823 821 "6Mc+JppIBpB9CPzYj67cj15shcSzn+I90FfRXK/Nz3XdAfRaFOQAkzP4SlE7zcJjsUYaRdmeDAha\n",
824 822 "KxNDH7ZDDjXGmx28LToUD8smxvCMnhqh1OP1W/+w5kVHwNLuCFrX1I6P2BvmUJA5BZ3PabALP2z8\n",
825 823 "yrMvzmr4yuImRbQsrxQ80WBdabSmzVZb5DH7za3ucddDpkriseT/JZ2riPvm2/mqAlU99H+q3xfT\n",
826 824 "K0BLquH/yLqXvFJ8C8KOEwEI64s3Ybs/BwtYxJhO0SNYSvoMWyqne399TKvKeX1L4nFlXfpPgorL\n",
827 825 "trmc+xv+GrX5/3u7QJXP2pxvlPPTkku4QSzBGjRX86jV67YmDwB2TI10aYKamKZfo/l32j1N96OX\n",
828 826 "0MkRmdmmT1U8EQW7bErT+YTS9i8fZGuTMtIrcpydO/E2r1ap6pNAwEImKNzVHEDbTmSD1WDbJFc8\n",
829 827 "2CAHwDa56CxFsWD9VGdAtXJZ8HSikH2w/sKhwg2FmwtVdAdbWupEaGGwbBpYneICoELOhoci8FHE\n",
830 828 "mUdgbxqpvVpC5C+RGXlcQd/npOV6VGDK2pxlS5k26zwzh/jq9V3PPru8t7CiMD/fUVBWXaJZz+8c\n",
831 829 "f4Hf2b55YIlKbBdVJVUz7K/Cn47++CXrD3xLP58HDKrb4BQcCjoUZlAAcVgpdw8Mv4dGn459TCMn\n",
832 830 "x4tidGqGASdepH4ApxfMY8WPxNxUZpInKtPwIQXhgK+ltd6F8iXkykYZJipyJCXqp8ldSq/Vsp1a\n",
833 831 "pddK2E4Jeq0M4yF1Tg+wNHEgltepX6If1K/Xb9KrEbABkbAgjYcVgo4UY4WkfSvkOKDdwI8St69E\n",
834 832 "wITUaweW7RTBjU0J7e/s7l9WlpRU4l9RV9eeR7te2GIuKzOzf18+y7/f/rjPQHGJn/E3GR/w8F+X\n",
835 833 "DCViPRpck2YyxAjPpI6arIiCmoTSEIJmgYQHTDyghVEFM4NxpIx+shXEEzmplbJK86dOIHSYx0gA\n",
836 834 "z2pPEUW6BCRBTYWEPoKMYCKxSqkgDtkHpkhknLNL3HQiaw3G5HksOstz1nsqBnkY5B3Y4gFmwT70\n",
837 835 "KIHgYFFCSZjkaP6FXA2YNSCjBMUFQeBrMMWdx6L/CQiViZ8JcgDLvB6r1UEsSVGs6fNBrhOQ7ztw\n",
838 836 "hi/XrkGKM61+W3G3V1Ft4qL5Ezgy1VkgjAFpL26lwa3ewq1QA4av0mZBaB6BKafAOFMV/8vxf1RZ\n",
839 837 "g7bmJ2dV1cRWhOa+WNFbGg9Uz6y3OxpjXu+Cai2/TvjOeb2uevZTTaFls6tjHc66JnvdzCpPR4PN\n",
840 838 "XMKvJ96J+fa3ImZeBfceqiPfRt9wOsIGlYJUml5NBZLV7EeVAVqsPiPX7wQtYSfBzgrPgEAge+Vp\n",
841 839 "aQNMlNtAEiDAoJbSTuX7E2cpnPr9H5s4lgt259OKG1JHhu88CryzZHmQgjvCJXsLVjA1uwrR8L9z\n",
842 840 "avzt42fEZ+M/+x3zgJt4SdAIaa6O/w4bfkifyAIl2DLSEL4ex74jOAvTXGppjJLnqdRky+4wdcyM\n",
843 841 "OgS0zzioMyON5TrFT9dwCT/yJ+itK9hbV2By+GW2qyXrHJg3Yw9+Yg8VxhE3vXwqwvfyK3hxCoht\n",
844 842 "8hXhdXherYJPEIZTO40HjEeM4oBky+U/67F1D5YHXbENweorEVt3D4KWDkJqraPNgRojbDt1pOx0\n",
845 843 "2aUydvlBAmcBISCotSBvgkRQGGqFB+Ch3hVeYZhL7TDuM6bwSAsedBeP1LKtpKbYgqdR3SSKNtQr\n",
846 844 "0YaqYekd3HoU5BrIfpCLBAyEh6wDAfgW7xbhms1Nh4iJfeQIEvCUb6zVhvj9r8xVl7ojtZb6Ylde\n",
847 845 "eYG10pQndr74SpmmtKbF21JfXVBRZHeVGkTLi/xXx/eUhWeU5RUENRpTpdfEPxne7myb4agI6vS2\n",
848 846 "mlrjt2mOeBnREI5cIfc/UVViCfhcfkbajZm9HqQTJXsxOBi/E5HYNKJV0ZpJ6ieOMm5fEJDEAiXQ\n",
849 847 "6vF1ebWFMlBpIAuHIPmziJIDKF+MW8txo6KCXnAeX/4aSA8Sk1ZQJVHYYslW0wZih92pr2BVgTDM\n",
850 848 "OnCWQL7WqlY+xK8UPrsTX7MmPv6/+cpnhG+N/2HXgQNP8mcRq7hqYoxfInzM3llPpZpFxl3dvJXf\n",
851 849 "z78bGu8QPnZ+WZa1cRsFKz/Gmbh2ISK1eFHVzyujIcGyvaSFbbcYR8rl6AQ2e5vT7G+J9nSinSm4\n",
852 850 "UFivwE7UCXIcBGAs3CRgnMINyC5neMiHRTtRthPVyTUuwSTq5ViF0igCzpInS88j6i4FR8rHyPDf\n",
853 851 "XrqHHUhEjSNWdlZFVb2i2ib/quIX0Go/BRMO1sM5lkoG3wu+HxQppgEhaOzPrRT1BELK6n5Eoe3U\n",
854 852 "HgCj34klZmf+AcQ17Cw4AJtfK4b7PpBekDcx7xDmJWTju17DqkIwAmuxFceiEMdS0EaaDxShI1D+\n",
855 853 "zoCchmYC6DUpDqUkCpJCsvaK8FqocKewfT5yNcKefzhyiv2kzrRfbr/RLiKMRkbwf1PcD0WbdLMd\n",
856 854 "VBoZwzdO7myQH4N8A215H+R5kKsgrWgkmpsEwApeDS3sATmNxh0DSaFxp0BWQBO6BE3oDMgxkKNQ\n",
857 855 "hy7B7HoG5AbIsfbJEAyfUlUou9hNQ6WkQIzWXOH7V8sCnZ6aOc1lvK2xy+/pDJTlVwxEm3vqTYLr\n",
858 856 "mVBoVZe3pnNFizkQaCwVukyR31/S84cNtz1zmiocAXY6+61omjP+A1+gonmud2mwtqZzZSi0ssuj\n",
859 857 "K3WXLR4PepfHg41Bjmo1fCZUsPWxhb8olcKodUiWxgmPEbGc0olsQGd+Gu71Usac2bj3cjAKJ7wB\n",
860 858 "qdBbStZmQKdXyEcrAih0PwP1G1o3I/ZoSVgZ8xQ4XviQY2laCWHJp7XLIIgjXjaAi3yE5WWXwXe0\n",
861 859 "lLaK0bpOtRGqwavwl6wDATZ48k3DfpTEewmLJvlDoiDkl+yGsiOnMUlhmD96cobdHghB78PoQZYP\n",
862 860 "DdT3s9haicoA66o2It8DqRTJldUvVCvBWN8EOQuyEjUI5oNshAp9LYh6VU3Xmu4AJdUCzNQLzdea\n",
863 861 "7zSjelWzpZmtIsm1qlfQ/OfR8lcoPQu6xsYCpcko7SWX8R1G4UTWfII0RKNGs0lRA8lk9XtozUto\n",
864 862 "wxqqrIf4sO66ZXVCtilvMBJyF+esz5Qzmx1hrRY5FcHrbpXL4pEMsaPL3PREe21PqPIbM+bUlVZ3\n",
865 863 "rmgtrCzMt+X3D744UBn2WeOh2nBVoeCa8USHx9bQVfdSnaCqifb6Wld0VInqqCg8s/Tppe0FFYHq\n",
866 864 "9q7KxjYH47V3hBbuHVpfngOiCjDZHwd6LGZieZOIpOoBqUjkh2MFQTEm9otD4gZxs6hhmoZLVI6o\n",
867 865 "hxOCkkbJZ+REccbKxJDVbXj6uVnCe9tlHXQxVycsZG3QcN+eNJ6qRQD6xIr+i/pP1T9X/5X6F+oP\n",
868 866 "1Z+qtewBZrVH3aruVi9Tq4elz9Q5QH4ByQ2yBVdWV7MlalQZaQIqq5oXJhOqZINs8hR3EeEcp8VL\n",
869 867 "jCuZ1GZ1q8ezmEyz/2P8KaHlN42/aT9zButMC+fk9/J/w6m5PH7R2xqVXlX/NqcSmb5MCv5Acok4\n",
870 868 "mC2IkA1WhtyoZ9JihuCcmeZKyO2cpDOgZh2shLfA/4AuD0FWSKeMBpchaBCHUycMacMYyh4YUO0e\n",
871 869 "kpZc4ZwN1+XQyO9T0rGcI9yHLGEDHaVMMjkRvA9Z3AYsxvcpWt+u8quiqj4VjiI74r6ajqr96qi6\n",
872 870 "T42jajYN/Jqopg+h9Mvhd7lPle3tWr82qu3T4hw6SqXs7Tq/Lqrr0+EoXuU+gWnZDX5D1NBnwFHA\n",
873 871 "EK4EhDUwc9if8vx5sIDiTwTT/jQcu0+zTY9V64tY1UT55q8Hvv/9wPhF+rmdqPrlL6sSRPENZrNv\n",
874 872 "8JfZb9D6tl6lYd9AVHGqemkIdok9RLAoDskV66FBwIwBaCYq3uYEHLwTFtnjcHHrKcDtNgLc8tIc\n",
875 873 "exGxPnVCl9aN6cSBlFHn0gV14rD8bWT8foK0BiJ77sNIdyhfV8NbeC8f5nt4NiRXKmUdpqXU36Hv\n",
876 874 "oFFZVF5VGN/hjpquVFvUXjUSQdmVMCuzQ1qL1qsNa3u0uBn6/A71OYqme3Vh9Pkd6m6NwWLwGsLo\n",
877 875 "7jt5dGWeJc+bF87ryUOdytVoxzLqZtxkGetrBI5qfR6iCepj3v+YrgZ2Mefk7lBf/+XbaqXeU3Ym\n",
878 876 "AWl8UIuxzIsYy3gO5YrsRnaDzB7QgyJYgUiQcPcpvcTOyeMYg4DLjuOUPIxFZRAjkt4u+sWoiEO5\n",
879 877 "AgGPHcYpefgy8UIuq7KaHYtgHGnv4KW+HqOXkmM52Njh6H0OvS1QvZiJbC2ULOwV+55ylor8ZiKP\n",
880 878 "wUDcD6/Cu/gg2rgbw6sTrbolylEKd+jNLExID3M9eDPCYFfTGMDryQODXarGVXfwehbRK4bxemq8\n",
881 879 "mTww5GHRw96MTQ3cAZUupOXsBAB9+SLd9EL8HvmNeC7OXuqy8Bln445I+TbIBUwZhK0MQFxsvG6S\n",
882 880 "gdwAzyWloeAjAUaacEwD5SicKv4rwi0lkmGhF3RWSA4k3JOY/y7I25hbBdC9YTRCDyzHhDpFxecw\n",
883 881 "SbYU4X02GrcadxkPGlVM9DusPwXEf/xB2gjwrF1FB4sYu82Cl1jMTFvOLm9md7xLyKuuC1hXL+8K\n",
884 882 "96kbQ7VdTDv+kcllLRhcOX6RFxc/pauJx24jtjAptPDttG5puZ8CGWCTUqExCex9xo2FTHJMuC0D\n",
885 883 "budAtqcib/8/ALfl9PaUXCeErsrWDWFXqTJydOyjNT/YcqfCckcORQ1b9DROakhaxutWs39HZj33\n",
886 884 "9Pjbs54HcPf27exberlzvI/8kW74I3nCQlRnsl7MTsysTnT9FLekb3yC589la6OxscAdEI6QzXIX\n",
887 885 "U+ySscL+QuHfMVMWTzVT4u86xVQpbUaK+VDxBohiu5VaOlKsGEKiPjBpNlFGjFJTJwljo5At7UBV\n",
888 886 "HdZn2SwFRD1kPzwwxUo4zSRIMRuz2Dt9QTEbL7CRruHmUMTGblRWQwqlINdwSMbU/Wq51sPExMRH\n",
889 887 "7MyCKdc8za4pTR5SHcfp/SqsOrfVcpzJxFV2Rh7FmcjnPsfObYkV7Vbh9HOqK6pbqgmVdiBZpHKq\n",
890 888 "hGH2B8shy3HLOcsVyy3LhAV/sDgteDK71x127TtTnruJ7ZekOK1R69KKrI3afm22HoUw8U8Txuy3\n",
891 889 "cnJ/kbCwb2Xpt0z9VnmT30U9/Lgvl7DQJJc2u/CVqjZA+N0NmTOGvPaxKszw7FealhNKlh2mp9KX\n",
892 890 "UT7V+mzNvuxXgm3HKAvwPWXLy4QBLhUvW1n2Qpk4MO3zKbWai7P6kC/3Mf+XqDPpbBWFqi7BGmkp\n",
893 891 "t+W+7IMUL6hKKqqLBe+X5wxNzV4D8WXwsY8YHzPwf87YUPKQ7rhOGBjRqqmuH1u/RbDBFkyBHhCC\n",
894 892 "FLDRwgbyR9h9F+RrWOeZ2FjCZJ6UWwgJcwXGYJdBAgpj7swD+SlqygHpcruACZ0QTghpYQxJ9zoS\n",
895 893 "294C/8cikPxAfRMRd3+MA7+vppov0tcgRH5BGaWiyqRyq0KqucgoHSIg1IdLv0gih+D4JArYCXJi\n",
896 894 "anKt+Aq2qXLyXqwkP8RRVPlRshLJE66wZeLRyudTKuY9VNaHh2E6eZH7BCEAq7FwWECACM36joIB\n",
897 895 "1uJJlBi7EYRKRB/Bg7O6OJV0NoNo5Auu51ArCa3BA6mpVINiIaZISKt2+xAx/8Y/PHuDF9b8tquL\n",
898 896 "5//+wvidO+wjKjGNhC8SZ+2XtiFJF1VVBLLjUkxjEmWXhN8V1fiw8pmLapTvP6krfItgZMArJZ7K\n",
899 897 "3MT0dfxMfj6/GhLxv+HBwNHB5P0u/0P+Z3ySf49/n/8Y2oxAPipoC2oV0xaoElwO/5KfUkhOQYli\n",
900 898 "qoKsIyRPaS4yhs6r1WZPq0dYOP4U0xQEfpy/d+ZMO1MX5DZ+h+JE1FwDJvWUYBBFNdBmuCynl8vV\n",
901 899 "3SZhwgOzWJWwsH28gx//zgft2dpb3E326nnceXavJByEcoxoLlkii1lDKRrSZlhTYrC67YauOoaU\n",
902 900 "gaGCDQUYX3xASZeYNsqUOnWxoun1b5iqpYfUg2OsQ1EAK4mKWEjAPa47p7uiu6Wb0KkHUkU6lMti\n",
903 901 "assm3TbdbiY7ZwvSsMFJGfi8XBRXowXucLY8VK4wlCli1rLlX9saYR/5iSe62P/5s0v7nhr/z/zs\n",
904 902 "p/qe5NezPpjLcaLAj3Ez+PcTMwLS8Rn8wEi9msybMxByRFUBpW0gt0FcjBGOuFDJlyK/LewEKObb\n",
905 903 "QG6DuCzsBDMbjojJkzYgsa+zkfXZOWwNgmwDKcKxTmy5sDXWqAxUPesyvTIxi9lOsWFqZyo7SsUJ\n",
906 904 "Sa2XbYHq8xjwaojk/wTOUlhM1j91IZsU7xd+XIjg9DQqNRcbRxzCA8aTRzyyjEXpHCl83Esw5MRz\n",
907 905 "iJKXc4iScdglKL6BylceRFTDLvtBRDXEYcq+BBIFiSPEVFc+WT38OkyOB5HXtKvmYA0iWWohA49O\n",
908 906 "ZlRK5/HYHghvF4quAfmUguDDeGYvyBt45k7ImK1ZuNAB6QLIVTzsAuGqg2iB1XINz/svCJXYWcMP\n",
909 907 "mFq8qGcqO5yyYAxTJcIpsqEo9Aq2ZXWm+lp3fumKAJMRm+d5mp2F7EdT3+jp4l90tDh6/XU6s7ey\n",
910 908 "oYUExuLehRXB2VX3sZGVHGFf/hdhJTch/FfGp4yEy5Itr0KCCpuEE+P/yhcKK8OyfYIvZvN5EbKt\n",
911 909 "p8tnFCwGDZN9Ipq5xAmK27GuTZTz/8hkmXLuH6R8DvI5R7khAK7KCed55BCWLpbIDnZHWkqjZxIg\n",
912 910 "VBZpe+U0ed0w1VGumTr+lB3yDOtkr7la9tvkpaUSG029DAYHcHQkh40Y/bdhPf2vIH/moKrTjr9y\n",
913 911 "/MLxoeNTB1WlTh7VndFRFWX7Eftp+yX7dbsahcHesh+zn7V/YL/JdpkQn4W3hG3UYs1mr/kgyEfi\n",
914 912 "lS1eSxc+Gon03f4u/v3xL5cvsDXOrb89/ieOkGNhictWWPG5bAf6Lve8YOHvM+1eQ3UG5fJgV3Jl\n",
915 913 "hQkWpx8LlVxXULok17cCE2Ta22QJP0MmoUtTSTxpiGwuiN5PQqQShlOIDXFxTC7LwjxzsNGgwt3E\n",
916 914 "lOp1Mk9FATsNlw2rnQmB42mQXkgVVMQyDgVuFqnYIGQ2uQurTi+MIndhLlhO1o4VWI/bFCuNtBB3\n",
917 915 "mIeLI7huFVbYz3Bdt2JMkeYzXYqfgvHML/pJ/e7d9T/ZXbd7dx2/enfdnt3Y372njvpuLvRBxiMr\n",
918 916 "uNtSAbCxDtkpBX7ERksFlQ5ECk0h9EEl218u1rvd+Vi+RgvwwyNOUunhSExeUF2DUH0SnZCESaAQ\n",
919 917 "xi4lMOM0mFs3lMGUQVELT2GrGwYh9IjkA9mZNQGzLWgYG0HeAHkL5LQZn+4U7jWKi7fgbArgJ9f4\n",
920 918 "LpBRdo7JYjE/RpFkW77XBf5hbZKtJO+bPKZHFEr+q6aZjmzeBOvLBxT7PYP/O8lXwXpzM8LcYiB7\n",
921 919 "EPzok71LnWyiQmRG0LfPmKjLSPY6trKUig+kg6V0GhAJBhG8sZuRkRliNiOKKnrkP7RMJKrZnGUL\n",
922 920 "pa2+mny1TBq2HbcxabiIHTTbcDCJYu3CZNkgCnZbgbVhBUYPBUmvKFB6cSWm+laQI3AUIIIgqbVY\n",
923 921 "mboh3QOnvo+ZfwZM+gzGwWkoFDdc9xHWdj8XFHgBfpWT8IBeALkKH9AoyH4Qqkl2EtV49vlTfnbh\n",
924 922 "NThfLoKcnsErlX9WaZV2vYZ2LSfPEzEjeHYc8Oxcxrp8H8QO8z5hlFEjLzumtXQULT2PuMU7IARD\n",
925 923 "9gntosWfocUX0eJRNPYayAEUJzvgP+IXHsl61PoeSp8o1WhFN5+01s10u2fWWZXfuFActTUumVlV\n",
926 924 "3bG0uXlpR7XbcXwZ/3l1W63VWttWXd3mt1j8bX8u5OdXzewPNvXPdLlm9jfNWlk/fga6LM1NwsXd\n",
927 925 "mcXFRf6BFdAbWfyN/llgEsFmpXaYwLkZ0ZNtw8l/F/HAKKmd7HcOISvsNrY5p9GJoO0MLRWI2s3P\n",
928 926 "SBuwshSwQ+hsrrKAUM4J1+cWem83wH2M7DTMIr7MCCYqGKlKy0Z8qBdAxALA2kkqZ372aia1avMp\n",
929 927 "MFcn/1CZcuTDMZETCT158uFC+cIiumHCCHglaYJ9cAQjOTJkfJJKih00uDOIJygNSGbalyyOYvzY\n",
930 928 "5Z9yuWUV1P7H5BAqQE4SR08EeGonJHe5AYlCtlsoA6LdzpYpqGCbFWTkw56WRGcdZRHnUZahkbZL\n",
931 929 "6LiZzrFQWXl7GlHxZbDehCJuLdz9+BfS0j+zm/658Zf//T3L955e84eWF1+qmF3xPfZv9bOWwbWV\n",
932 930 "syu/VzmbL/npP7b/tP199h/7uXDhQi4/S6gi37WPf13OI3bJecTEXVzZmogGbMPXugSE0BYGQTpB\n",
933 931 "xkA4v+K/oxRjw0Muu4RbFgbsOUAFGb1YioN9RKEKTgcwjmh7H8UxDhf0FEzCGcs4xsm2kjjwnGQQ\n",
934 932 "YxkjYTiZsowCpS/nrEumrKPIb7qDQDoNJTyVnQFCEjAHJT3Ca+X8KxlpUA/JNFrRV8FuFXX2OZGe\n",
935 933 "5TpDXAkTnSkl7EDVmSqFTemr8AjPqAePwIzXIB4k5R1FpPIdxNhqkJ8U9vX44NprE+OTpeSTkYJe\n",
936 934 "+MnDCuLJw8kijyRGX1bCXJWw1ziy75UQWWTjP5yC70MErBIJy36VnCShimxGf5bNsRphY8IkDYJb\n",
937 935 "7a49VAuTlZ8fnpJPNe38p+l8F50vLanlh1Mn6tP1Y/XigGSsJ0tX/dT8J3btsdy1z9G1FukQrnUh\n",
938 936 "5vgQ8npcM3k59wl1AP+A6gBWcgvFvdKCWcDpQL2aACrXUCGbBCOJBbIbOm/WAky7vIB0Ow81H1QU\n",
939 937 "azHLOGJmWwvYGphGyS+2OjblEZ84jRjmuXkI2E/GFvUDAOjKYkUGibFBG1NGcA/b6VF2wmwnrAi6\n",
940 938 "jWynETs9MnSIs6cxW9EBzFFiXHOYSnzGyKCFMoVQqBpJoZLq5JPrAtIhpKA1hZGElbzSdKuJNWVx\n",
941 939 "EzwJ1MRYGM1divyluXPZEErPHZs7ZfWVC9phDq3BAncKxAcRcA3442mwYAqAIXyxA1Cg+kDOgBwD\n",
942 940 "oUo3vfBu78PU2A8dqg9z4ihIH3AsD5YfxcT4BSbGAegFbcgFPFpxBqEiJ3Oe5RQ8y5HqXvib12AR\n",
943 941 "7IU2t7F2ay2CMGov196oZSNjJ5gFFXk6C/IhyK/RA/8AMgo5ZS8izVMgd0FsAbjxQM4gsUrbhoAQ\n",
944 942 "gAreo4KCR6Kno5ei16P3ouqB5JHZp2cDwQfJV/rZ7MTTiKc5CnIfRI9om/tICrkDtP6VqNNwMf5J\n",
945 943 "PFcgEEpuEv5GNuOPFJzGvDyYrWE+TF2XOlh4tPBMoZiF8jkAEkf3HQDZiz7sAdmLjiR1cz/IKZCT\n",
946 944 "6DwkUlJfSRGQk0pPST/CtDtfexXTjqSbH6JvroKM5iSaS+ilX9Tjq8w4iuoGX1ChC2Cd3Ef9QXvA\n",
947 945 "H2Cy1Sp01yl0132qWRBB76HP7Oi9Y9j6AsSA3XsoOniUkdTZ6AfRm1H2bnfQgZrZ4GEdo8iuu4Pe\n",
948 946 "06D37uZ6bzmKG0S6ETMRvxafItpoH2ZgD5cdnAouMllrUKxVag3GLXUdtTWzZ9hsM2bX1HbUWeLT\n",
949 947 "6g1Gf6+8tKmlxRJe0VFdE1vRHH2xquqpzulVBms880KVCgesDM0tnVZoMFxRa89X+GFTWygsVxjk\n",
950 948 "ucoJH/crTsNZuVlvF6t1qvpYGaL2vMVAZ1levKb45eLXimUEq9HiAva9iq8Ww7AlBhDaZhULSFOb\n",
951 949 "khIbCWWrL4QjvyoO2ktc9iKjw1UU9NRWlhaWGd4pNuVbnSazz+3M90QqzGV5eY/ltZuIXxopADvF\n",
952 950 "1RvrXfWiUl8aFv9nCFPv/4e98xBB5vEADVeR9q3KSAHoeeeYkpSSEfNEUvbVaW4yzDR/qsWXfG8a\n",
953 951 "2VhGPxT8xlNJuOT74sciqky9qdqvOqw6pVKxyQMsYdLCku+rPmZqWVKlKlWxgfg5Du1Q7YOmNqqi\n",
954 952 "0t2AtOSfiW9qSMaEP9gQ/iFhyPkmxsQy4WPG7T2cX/gEUUNOF2P5Vpg3gE+b3GNNsAUc0erZ0Ak4\n",
955 953 "zYrSiSKgwmK7Qg5lZ9JSgJTKhMuYcGdiBYNuGYJ5t/uQWzOQvOWecAv0x5q0VKTkDyMgz5eRzkG9\n",
956 954 "upVTtIagaNVm8Ed/RtpU/9jw7VyIUlYMThVVOisDleIwfKAISJWWo/2jwgWUDNDDF98jLBdUwwpE\n",
957 955 "0MfCZ4JsBCjJIFYDK1iKyzPmufLEYamwhJMFSlQRcmakGIJmKXK2ykNLWTX9MH0OJxAIhoyYmfDT\n",
958 956 "J7uBxaEPFosD0JR3gXwKYkPaGCG/DUu1sG18Con8KtQ0NSq79GBrBchdEKr8c818x4yX0JgtZq85\n",
959 957 "bFYNSyuoNhDkLC0UpjYIO69SgVSQdci13uU96MUa4L3sveFl3GUVen0+SBt6eQvIEZB1AKna5Tvo\n",
960 958 "w9m+y74bgMldhRyv5WCIr9XCrZuqHa29UHutVjWAo8NSL/70Sk4dvAc2qqu11bLX6vYj1T3lH/Vf\n",
961 959 "8F/z4wJkWvf6VyDT+hX/637w/ecxdK+il3rRS/up/DzIVaj9UVhDHCDzQerQUVcReXW19HNEVU72\n",
962 960 "khadRslxFjOyn81rESJIMfNaqJc+kKu5EDL0VlVVayQ8CfU6Ge+u1YY88kwReSsvlo1/+PNSb6G5\n",
963 961 "vKAqoGszLAqXN3utRkd18Xc++vJw+5MVL4WfpJjU6M9KAxX5lmJ9lVXf1FJYVldmq6txGV/6lP/q\n",
964 962 "XO+qL8NKzKqKi7H5JlBsayFn5uz8Fyg9rkVYUJERBsVsgAlT55YgknZJvuzagh8iheLPAYGNbznQ\n",
965 963 "V2t8TJAv4owIbZbQkqRDRih9wFBJ3jJNmNgQKhg0rTdtMm0z7TYdMmnoj6VpaT16ZjNIP+seQKFY\n",
966 964 "2aTEenprOn4bU94sBGmzKZfHQ2APyqQkMxImZSF7g0L2BgEQrlCPGTMXippWX5gFlc6GlKTgZw2o\n",
967 965 "2HtNDzGW0TglF4zNMZBiM2VllNAPqv+Zs6XiYfdj2pqd5l0bKrm8atkCNeQI3mgdDuyyHLRgcFtQ\n",
968 966 "xRyDm+YMXutVkCMg65BWuct60IoTrZetN6w4EWmCyyFUvGajWWAbtV2wXbNhUOfSKF6xKSLcPZje\n",
969 967 "dTYbkqVfQyZmyj4KM/w12DOWI7oW+TTSRSTVuLXZgGh3qymknhx2whvCljeAp3/i7b5xz5Rhxn/1\n",
970 968 "cvgrX+ngC7Y8NK54LsxXcf8knGVr0atw9mQRPuVVKAl/tjDZz2wMZAFc4fLWZJekFAKRlqsZ5+vM\n",
971 969 "VYKcXKAyKA3H+B+IQHZRyQUWK9I2rVIm+Q3+iRrMV1ETSdd18gd4j5DmCrgm7n2p1iTWJzEI4btG\n",
972 970 "Ro0pFzYnpWvlLCgHY8YOOdbElC0sB4CkhrS0CSiLh0KPwGqx4WZzF8q2M9balBzbICKuIaHLpOC8\n",
973 971 "Wq+TgULL2V/L+8uHqBicfI0zgPNkrIbGQgTKJj9ovAlgcMA/Qmu+WPkJ0rR2IJMoeb3yHnY+gA0H\n",
974 972 "qQgmkrlkU7h2sq5zy1TmQtXuUeHpYkFdbYXH4nAa1c+UeitKSmrnNltiZTNKXI7ZLk9TRZ4ohEW+\n",
975 973 "sJlNdX1pSWlJscNZ8LrB7DRbPY5CXdRY6LEbTWWu/K3FVQU1pZZs//4+698izs2NSUZgvfTDsHoC\n",
976 974 "60p/lgMoHs7dsMSsl0FfAAg9AX3lEFTnTbksKcJykcHKUoiyGALsqpMAqvJYX+YN5q3Po9iu5AHh\n",
977 975 "CIwJZwh7LOdhHoU1nYBb3sllQ1HU9yjs+e/AoLsPBKbd5NGCM5D2oSNlyx8ezd2NfNAH6XK6qFBB\n",
978 976 "L1MKaz2ionuLyryWhra2Bou3rKjdVj+rxj2r/v+y9ybgbVVn/vA9V6styZKszZYX2ZYsyUss2Yol\n",
979 977 "WzKxvMXOQhYSHCclk/wLZGEZyAwkAdqGYUqWzrSkLc3CAGHakm06g3yjOEthyMyQhbRT3CkJCbhN\n",
980 978 "uiQkYWhoSymlwf6f33uvZDsLMDPf9z3P9zxTmp+vjq7OOfe9Z3vPed/fW8D/en38r5hw+Iotieqq\n",
981 979 "uKXIZy9rBFdZ4xjOMpUQHPmVWMXeE4r4eiisekxSWVU1KasyuqJdLoJdfCuHpDnsTrpnuRe773c/\n",
982 980 "6t7o1i3EVtt/QHs7y2HguaJU0aEi1UKZyEoSirClJkeGeBOgRVcyGLFflv6W4XkDl8E6LEBihm7D\n",
983 981 "PAP4rEEwsxJwwgCLYsMruCfPRJtmr0OH/Dmg0G0iBv7CUU/mJgwxxfKND0MBegZAeuRBwIZi8OzL\n",
984 982 "1uKV8m3kUbO9EtxglWexm+KXa/ak/7vYSlmJNcXjWFNI/n/lCekf+3/uJ5PX/jqeSZhuln4KFo5f\n",
985 983 "AyhI9hYK1gVX5TWARYCzAKEhs2fl5C3NmVHys54oghOeKNJpiOnCqKz0TjrpbzL0GPoMywx8BL4A\n",
986 984 "6ayHdAYMR7h0ZEN4KY/ulM5BPD8hGckZzoNcKDKiTCCKqcw9mHKHYFcD0nO9pb9KvCIVlxJdwlNQ\n",
987 985 "wlcXry8e5SCvpG/S36p8HjL6JYQ2CPBX0fbnWkjoDf/bEBkFq9+OhL3+w0igEPZhOYOfht8Fm/JW\n",
988 986 "SGhnWB5iGoUu8DpQVO8BjIZHsyRIH6AfTNZidaNdquU/jOG6z74M7KT77cewIKIwgB9g+omVdZfB\n",
989 987 "3aJsBzbOTsAcfVP5duyZEa/RFuj6u6DKKjEbt0CV3QXH/dO1F3lCemK4M4ywY10Q+wcZyoQ0Qs/w\n",
990 988 "2ykA0TFAD86u/oirZlz1YP+jGQCXl/Q8+1LU6yjWaORmgrlRairLOtCnN9fsAC3qZJQfIzd3wAVA\n",
991 989 "F7hDYxxYhpFFoUDN+rC7dApBritjkTQxwN4u8mBLLjyztKOsvbgU23KhWWXtZRsndGLfrqLU2VSB\n",
992 990 "jbsK9lXPnHpyTy/o9HSWzQnjuqiw0zM3AOW1s64i346LtnF7cN/L6oU/Jr2wSGqFmr+Iw4AlVhYL\n",
993 991 "x1TYy28Z3cvvGT4rJmift441fXp8u1Y8NsW3G2VQLaNIdxNC17opjuMx/+T4diewEjoP2Av4VwAF\n",
994 992 "tTsIqJWD3R2vfbNW/G+FuJOJTcG0nIly14QNmilXR7mTw9tJkz3Y4aAYd8QtIe3H9sp+6HkU606K\n",
995 993 "0clVJvHa0HeOa0LfWf8Hoe+Y9Rqi1OFvZOIOPSYyruObhT+DY/AsdMmNZOE/lH6CbYO3/ExS+YkL\n",
996 994 "sV8tUyIa+JeGbWTZpfCmZl3uiGsG1pbgRdPmkbmRlq+eNOQ3apWJhyIRkbV/ob3u5pjn4bve/ZLK\n",
997 995 "eOV9dhAV/jzitkRHFgtXxLOCVbiA8w64pafDeUmYkOQOkY26bLLeiivYreMQuFlgZOSpkR0FDUPE\n",
998 996 "oWoNpQy0aj4HW3bzSraWbWLbmRzCSqe4HDxPihpgE/GlcCVaJAruFaDg5t9jCQGHrvSA/giaxmR9\n",
999 997 "L5Tb53PAEZizNmcT3NBzBnKO5JzMOZ+j5jfk9ObwG87T/G981Qh1cBW2T7qR33msErqh8L2OJziX\n",
1000 998 "mw0eD1dL8DJdKauvsFrLwp7GjqlTesR/jiy+/a5Jk5Yt/rOJwT+78PADlz5P707P5fQjktN/ZORE\n",
1001 999 "h0PSWcCjeXLc4ykY5MICxQ4h6QhQQaQpXILEEztE7knwEuCi4gKDS8YGtpXtYqqF6VfZaTSB+RDT\n",
1002 1000 "aJQnihc9H8vj3coR6AppK4Q0H6rsbrLofyhnQ87WnF1cGgOv5pzOuZijIj4Dng2e/iIkM2A8At/w\n",
1003 1001 "XhDGDFiOWCClt5H/BX6La5IK+ivcUXF0qR8nkS9dRyAawT/yc1WR+Eu+ynEJNUKrMEdsk7nNCipA\n",
1004 1002 "5Y1QpzMBjwKSGKzvB7Cp5D+sWPRJScD9TBYeSIikXkgQc4US1uhRPHQKMAhYLNJ5XLpAFQRf/lS0\n",
1005 1003 "m17AWhWZrUMPBK9EqsKCZfwIvRyZWS1Vy79CPKKpYOuURuDYNwuBWM/cmlmrzuQda2ZG+yQ7Vzom\n",
1006 1004 "oPjQM+G+gRgKJbTJdH9mfymVM0REhEi38WyhhfB763lqvXzZxRVc7LLO7EL8An5LQrb4P4LnPa/M\n",
1007 1005 "iHyKE5bC9nIzEt4AyL4OOYLs7aBeIT1E7YF8dg4wtJaLiAuHmFjpPnEZ1s50+LqVXEZGQ8zLJqJH\n",
1008 1006 "AOdpPMxy3MBJgpegkv0k1CvS67Vb+IogvdawiY836XWWzeA9XGfdjCO6Dc6tTnwX2RTBn4mbJuK7\n",
1009 1007 "5s3N+BTfFMctk7ZOAtX5Bu1WZLPBsPXabNY6NyGbr9Q9hZhDa5s30e8nbeI/dAWYy/Zp52af8v3p\n",
1010 1008 "iVy77bR5qgsKqj22zN+WIjkGXFHm73DoU2/xFIlvF338VmG1x2r1VBdm/lYmQ0VFIdxCf9n8T7mB\n",
1011 1009 "jx3ERUOca4v+65FIpMuwvbzPtAanpSnTIfxJmmaZxLHGrjLjzHi6mX+5mv+MxrFbmVr4F3Y/r0vP\n",
1012 1010 "aF2SOZny1aOlp89oL+MtnjFdRpnjaiDNInMoGFeNN8jPFsrimVJ1QiEfK16jsQJaZUjoEGYKt4t7\n",
1013 1011 "pFkLePnhOAfSM7fh8JAtiCsDBLkDnUGTNzMMn0PpoNCERe08SG+tkI18Njo6PCqPDtK0zIggPQG1\n",
1014 1012 "tRVwH2CjosAqo8SAoLaoy9Rcd4cSC02M/0JRZ68JyhhGYFbpPR9+5rP4ynw8LU7GOiOgOzRXe6pD\n",
1015 1013 "1TyrjbN4BrMsqU5+cydu7rR0lnXymxdYUlP5zVNx81TP1NBUlHsnyr0zMwAt4i9zUWYAauQfGkcH\n",
1016 1014 "oEWZAajRkmodkja2wjCIlxFAGQFLoCyAyJGD+HoCL2YCipngmRCawJNNlOwYTLVaEIyJZ9XNf9mN\n",
1017 1015 "X3Zbusu6+S087VY+4fOxMHUz//nN+PnNnptDN8uOV0sg9PEj1lVDlZSDoXs1Xtjy6w5UU/BCKYIf\n",
1018 1016 "TunSiMMrXme0moK57pOHLENmyJqi0qwY2K09qD3OG+zA8/q9+sPwuXveuNd42MgvducdzDuehwv/\n",
1019 1017 "Qf9xP74K7g0eDuKidm/t4Vp8VXew7ngdLiYfnHx8Mr7q2dtzuAcX0/dOPzwdX804OOP4DBWNUMSc\n",
1020 1018 "mjkOumr00XzCd5/0uy9i1GILmqurm/EvWlDVVFbWVFWQ+Tt8z42/+u4Nv5JHsCPV8Xg1/pU1YaRr\n",
1021 1019 "KvM0YbBq8twgnW24wReKj21YnC7W8fFjuqTC+DGi+Ehe14y8lYYJAd597yn3qYcUN1vi+JetJmX2\n",
1022 1020 "fJk5vzHC7v75z1v4/9nNwJ8LCmciyo3QGLJEMiBSGxH5YzlKfrEGMtek5TCYFA2wy5FareQpa+Dl\n",
1023 1021 "kw/USHbRkRRld1kNsb/o5AMb/Mo4BMwb5INpuaPcyuvjUOpG/rxUs5/f08KWPv10y+bNVLcg18uK\n",
1024 1022 "FL1M8+nxLJI41yWVDPEsKAqSopc5QjcIbpH5kAlu8ZmCWRB19oAc0GIs9UMmjgXiDN0onIUcYiIT\n",
1025 1023 "zgJnpWKW8GEKAlookSyaoXwpQSwQtSLd7JviE1dcG4zCek0wisb/fjCKj19jf3698OL8XYRGPmID\n",
1026 1024 "4iBvnxPYc/IM99zoRCuHJzPI5G9PaLdpNTLt/sDL2te0Z/gYorDuG0MDZ4yXjSN8DEmbjR6juKLf\n",
1027 1025 "pKbVvJ+PjdiHMfs9fp4KyxQ/toGlVmzwnCm+jA2eJ4q38T/9Raor0n2hzNhOZB/X54lDcylgV6Si\n",
1028 1026 "cjKVKyItClQ3GW4bInMkCsdXYaEwH1Q379hGzyZwLCE9hXOHO7EttQlgB/PNEfdJ93k3NKal7lXu\n",
1029 1027 "de7Nbt48tsOw8XEoxEuyLPAna87XQCcgthsHeBO0AKK5OUzRHlDw24BlKGw94CHAFpx0rC/cgs3C\n",
1030 1028 "ZZhBTyF23Qb3VpBGPoyEHZgzl1Wvrl4PfvchFHeq5gIvrnIcOx5vGmqiJ9DqAlmNXO1gtZk1RUFP\n",
1031 1029 "oa/aW9A2MdKRM03rbewMBCdHSgp81b6CLJfenKIJAV9VXWkk2VKfE5hU43TVtvPBrKYi0OBD+6gd\n",
1032 1030 "sbA9xLE3gQ2TPTeZ/KUvCyNYfZuxNwV6dn6NOOpZhnZyB5UsWi5TmXJvTFtJ5mQ4cNUZDtwxzeW9\n",
1033 1031 "DLP1mMYSRmN5tHgjGoscP4Eay8wQux45H7WcseR8snlmptEYy0cbjQNnWw4i6cdL25eNPEhBB4mw\n",
1034 1032 "cjNe2uN4aUtcK2FOt2nMCwThp/QIgF7lesB+wFa81Ffdp/FSwakkXUIqfD2knWhOu3FsuwsGvHjR\n",
1035 1033 "2INH0yJLlw2AS/Qxs1soXaTtmQLU1o3ankI7I6LSTajeOjTl11HCYcASwFrAAOA8YPP48unYeDus\n",
1036 1034 "a17PNuztaGmrqtehOm8j4QSK3g7A0Die4NCRp1HmZld2pFI3Doy2PVZwo8Y3yoY4p7iWt75QybWt\n",
1037 1035 "r75SHp+e4auhuxRf41tT6pC0EcYVZ9XvwbjifrWsCcvHbUNyOF716JHWYqjzZ3MyjYSchIzyAlGt\n",
1038 1036 "xDCXp9IA/8fqWlq+3tIivvLOO+/IZS8aeYDtpFiCOuGv5WiC14sZSCzEcEwZSGkOaQY1/Es1RUnv\n",
1039 1037 "1/E2+lxONpZglrGYQpUjCGEP68NKb1x0Qv6NpknTo+lDmPEPEX1PrynQBDWqFYhBiH/f7uiYfQfF\n",
1040 1038 "IkRNeT25jHYqMtrCnzKNo0VoIurLathYDFHUdmISkV5QgbBKRQHbidmNJvMXdDxVy2ubhMSeu5HE\n",
1041 1039 "tIrEeBVVMOfsU6GKcK3Xq7DDQJXXwcKzT4dvEIBPryvQBXWqFSyi42LWedmdSu0zgh5f/+e4qAZe\n",
1042 1040 "Fl8Tz4iqhf9tgdNLp9c/hnpOPkv9n74E21UvgZ1W2gsTmthp4Yf0HLeDgRQRVfhAda3saRMzDbd2\n",
1043 1041 "6KbkSS0JmsyxDJnWoK7i0CeK2saFuUGW47iyV/KnJb/1NCwWwMYqXsYq5gXxZcUh/3pxL1ElOfgl\n",
1044 1042 "akL+oteI7hMlBrk8IsuDZDHygPBD6js9qM/LIrth0TPHRdu8cQkupYS/xfPewZ/3OXreEGQNV3ox\n",
1045 1043 "OwZIT1Dvzzq4ZkVKbma8r99B/Rz1nMbhVr4WdQnbU65Qv1N2b3RZYMx/CPYcZzC4m2E8AM8e6dHC\n",
1046 1044 "cRSq1/U/pIM1nKVpDXQWdh5T4U+0tF7utzCZvxBbkk1opR9gFNeCZJqMK5vpow2mxJNxT6OyIbsw\n",
1047 1045 "3ZjfxZeTiteeQn5HWztW70wVaxN17sp6T7HDZCr0R33WdtbuSUbm2bzFVm2bpmTCxILhDwX1uLil\n",
1048 1046 "E4V2VSNFLpUDlsqRSrNRTJUApv+1yKXtQ+nF7fcjBoGlnd+WaucVD7cn23kTf49fp9qG5LBDwUFE\n",
1049 1047 "Gq8bkmbV8U/NPLWZ/61D6PPUpJA0axJbAa19BLENZrYuasX6opWntfC0Fn7nJEvqpiEpBnPXXtht\n",
1050 1048 "ngWEYTG8uAM/6VzUCUaETixPOl/ozOz+/A/CnBZiGzS9JvpElDc0rwVPchYWocloG9YTyRCM5qKW\n",
1051 1049 "VIT/EMEphYglUhZR8TVhs7yf2jKIIEqTiL4GvvE3DG6qUTlU8ib/1cFNodcrFvTXi2yaq3PfMLIp\n",
1052 1050 "+IPSOfpCHA40URDTa4Ob3iCuaRpxTUUloKnG6rg2oClI7saHMU3H8ruh/NwwkOl5QB/Oby+SyxDO\n",
1053 1051 "8jpgntsIOAfoBdyKXa5zgCigG9AFQrlzgCigN4HFEeSXYbuTHSmkGB68RzFl5rqWdcqo74QShhT1\n",
1054 1052 "oTCk52JKsenupnlNXHxN8Z54X5w/zgUU2YSCLgCmJD5DQNHsl3zOozWTDgYoDu+nhxJlwanO6nKb\n",
1055 1053 "tdhrXdjy7Iyp7eEvtK28ueVTA4guUeUVu51Feepooi0e1fzzgQM0txaINcIp8SJ4L9gjktGJ9bvs\n",
1056 1054 "BUu80ljgbuQrSJzGa8EZZg0V0C6AtAjqTAjkcJetIzhUNON6m/UFfg1jC7g0pVuLZsKfJFnEe6Zq\n",
1057 1055 "CLYaORRSj85fQKIj9eLUZX6GlCppiuV1583LW5q3Km9dnnYh/2zuNs8zLzWvMq8z43OTrcfWZ1tm\n",
1058 1056 "W21bb6PP9h47ztpX29fb6XNBT0FfwbKC1QXrC+hzIbiSlxXC+oLyK+kumVeytGRVyboSfI6WTi7t\n",
1059 1057 "LV1SivBeWt5K6LSoj2qGk6F5qNk8qllTXk9eX96yvNV566lmTeYec595mXm1ef11axYr7C6cV7i0\n",
1060 1058 "cFXhuuuWjLCo80qXlq4qXcdLtn3CblbaW1joxT+fuchrs1cUmc1FFXabt8gsfqWwoqIQ/+xed16e\n",
1061 1059 "22u3ewvN5kIvrTfKR95nf8PnrnqxWzIjIix5KyEYLLmwgEpWr+YLIjNFh5XN9aRFINQ2W/rLuHJl\n",
1062 1060 "tqTqB1P1ISkJy5BWmHediVCQ8ch9kTWRJyJqZdgkr4/M9EYhJMhUpM6ueIHBPg2H6jhXNciphpBU\n",
1063 1061 "YK9THCoRzkTE/NBfJF7BfpFvSHrfJx/r+LEN+n3olEPCf0KPBI9X0vAQkw/6DjCNfLKX/gF7i8GE\n",
1064 1062 "1bQOWy0P5j2ex/8M5B3hf6RfocmewPi0z3oUrRXBFtOnnBeg7VPIhp8U/BIhG45kwtZK57L60UnA\n",
1065 1063 "G3BM2AnoqcCRIa4uUlzZQA9iBFzCAVYTzCNexy7U24BGuKycphhw4I7hQ+Q+TJCrAafoCvXewnYi\n",
1066 1064 "2vgxdopd4EsfaQNWKAcAYApL42g2+1jSqjw8Q95RPNSr2ILfn9X0aD+D/DUvEbjGWDjSE1CoOFR5\n",
1067 1065 "a8UuOKRMwYO8OfoMaRgXw+kKT3EOD3AScAKxISrrG+vFFSwT+NgJw45rSKj9gUAmZo/TxZLmBp+V\n",
1068 1066 "Ly/q6jrq2zyR9orowvJm+03V9qDHNrEyaawKFJfUTyqvn+tid5ZU6K1uW3mpwWLoaPDHvJaqhkCZ\n",
1069 1067 "P8dWavd6csx86JpQ7otWWP11sPegdk1+AP9GvD7zFgqCXpDek900RWHSyIPsP8RBwS7MVXkkfye8\n",
1070 1068 "pDp5G+/kDZC397OyaXt/Hr/OUcHGULLTH2wmzB1MzQ3RJmOnJTV9MDU9lH5i+rbpIrlPSVWdfuW+\n",
1071 1069 "2sFUbSj9aO3GWhEZpyYOpiaG+hv572ot/TfxW7vpVukRxFd6B7ChGyvDenlLcjbvI7MRGHUbHJqS\n",
1072 1070 "9bPlJUJ/DrsiVcyul23XpTX83fTb4WUsJzlCqdnkMCVVJclot1H+YWMoVW/pb+Lp3RXEKroOBT7Q\n",
1073 1071 "LXcgcofaTFyfmO/ID35n1lP7EjYyDsIg+yAmvFdghQSzJGkTYB1gB+Aoeedga2AfIIo5cQlgAABi\n",
1074 1072 "ivS64Gb46dA2AbZJRXlzYOD5ur11h+v4gnFLZCfOOk9FLuDPgcirCMW9C4ukJqwDd7bvx8JwPoJT\n",
1075 1073 "3YUT3j9N5rAcJ94HAKdm4AqwG3B8Bjx+Zh6YyX9zeiZP2DULTXYOKgvQz+XNOzgXK+WlEMG3III7\n",
1076 1074 "8PTk0v8t/ahBzEEIYT+EsB3PehJmWevKN8Ms6ygJBA9/hMNA1DfZ1+tDWEVfRgD7AFsyZvbSBsDz\n",
1077 1075 "kMFAzRGKLE3RrEFXcgEj6G6EIT8eeRPP/ndI+D7gTkiBREGPPxXy2N1+EPJIIGEp5PEB5LEMorgI\n",
1078 1076 "ODgVLhQzTs7gwtwxY98MGJRBDgOAHYDtkMg5COMI4A2ACmKxAyo52OCJUyq6MiYLgVidalwX1+qu\n",
1079 1077 "39FlY4/R7v7zkgkOj8/SbnI7TNaiCmvzFo21yOfyVrv0oYo/r46FA36Pp7HT13F3Qbt1SrW9qsLh\n",
1080 1078 "L9sQiCQm5BYVWErCrd6OBWb2VWOwzOFx5evKNPmuYrOz3G3Tuw/lOm0mZ2mpvrLK2uRsm1iXdLib\n",
1081 1079 "aqpvqrI1JcpCwdyCQImnytJhaw03JO06W3GgKJAI2mMBrG/auJ55guuC4GB7ERxsijGOHLjENbpz\n",
1082 1080 "/YJW3rO+rB3RGhbK/HCfKZoJDkhMXHM1HTIN8mVxvxEEN5Z+M9dcX8CEowQKuaxwtfExIC80ntWB\n",
1083 1081 "r4zyLBQjBNqtqcnUY+ozQbvFcltvKjAFTVx/HkfTFoscz+ykTYZS/R1lx+zjF5W9hiiHN8n/aV9G\n",
1084 1082 "3yclu5+pwFQvxTH9vAYAAYq0FYBIVZn9E67kqkPpVvVMdUY1IqKrqx2iyK6KgckCIw2ZR+oxX0eJ\n",
1085 1083 "fnPUZInP0WBdlLRYXkG54V+Ka0VZw8GXGaJ66Q/QY/QqtoIxL8hb2JvDv5zNvMM72HfFxz7+kvhY\n",
1086 1084 "+2gMTYpTXct+IJUEsG4dwwCRhFfho4DnruaCIBqIN2H4SNaP20cJIWaho24cRwhBHBCZhyZ2CDot\n",
1087 1085 "GscGgWhsChuEndggpPPI+BW7PPz2UvQjsjDKMi0QycIxABGQn8J8/SGgAAvuHMAHhZmVx2GYLR+G\n",
1088 1086 "9+x5wBuAw9CO3gD8AYDY59IpjEgfAo4DSG06DtiPEelVwMUsUzi2lgderTpddbEKZMvVfKFxtPaN\n",
1089 1087 "WhxZ3Iq69mVDXRzNslWczPjppLVOJ9gqfl+oLCvSpzwXcHx1CpUhb3DyAP8I1TqdDb5+AZU5TUNm\n",
1090 1088 "hp5C3mLeh33uN6rerhKvCbsec40NeCFH/rNFVn8mJojXFAKICoUQ4jpEECzG+4pm5NfsXvGkoBL+\n",
1091 1089 "VfHPuIKGbBy1mhJEUmCkK9Cvjapirn5fvfs1jhqNJQvBOtorLBFWCmuFTcJ2YUA4IpwUzgtG3vQz\n",
1092 1090 "rKS9oBI0gZ62ly1h6C1aPpKzfTBbewPdZQu2AYJik9gjqviaF9HE5XN93mWoXcEoI92tmscrlN6p\n",
1093 1091 "2q+SjX5a4yzUnG8TT9jJPhYd5p9Ufyn62WOKT/sZ4QWhWXoBFtqHALPA+oKABWnEIUQjoO8WGaDe\n",
1094 1092 "GUZg274ICa0G2XqWCXN5nlV8jeVhd+1hav1ofKKz2ahEENhZTLKz9Iv14kIlQohMqJiNE2Ifkhaj\n",
1095 1093 "CyTt8s9KFfdLYk8yjt09g0ZRqgYPRao0JIceIoUCNhqMWCNKiVTJYVUrayWrpb9AJr5ZD7s14jm9\n",
1096 1094 "BKqsw7YT8HA5j1n/DxTJBVU4AnDBYe0Cri5iOfAWYCfa/YNo5ofdJ3Bg83t0Sw1ObZwIjTHPvdQN\n",
1097 1095 "ma1DKedRyjrAryhnwO8BGhwyHrGBidc+aD9r593OhsIoisz5bDlbAQc4DPS5l7lXu/ltFISjgJfE\n",
1098 1096 "vNaIo7wxIscwkefdMUFMdu/8ePjWJYsX28O3TKqdXRqyN3nroiX6nWzm8LstLczWMi/c2xYoLou4\n",
1099 1097 "iv3NycLZFGeKj6OTxD7BLZzewzQ2dc1ogKnMGx0TZwqmmJJFIJNLaHDb0Cjew9MJNvzAthhyPaR4\n",
1100 1098 "DtkpIIqTXk/hoLQmE8xcID+gzG5bRm0kJhFMKYVyhBOTpV8nM2XZBrG55hykyEeWPgQNXg3VZzlg\n",
1101 1099 "A2C+ha/yBqK5k3N7wdONm5ZZ+IITbLciAlDRqIKwvDSGkPwiXGLxdbn5hWarz6yqnGirqnDefnv7\n",
1102 1100 "OrZl+D/d5fm6XP1N1tzicIAFWr78ZdlefKRYTIjvk13Ccq7zpl+oe7kO3HBD1zUaHzUVh13CGH+8\n",
1103 1101 "64a1IbsE/WeyF1fMES7VsDH24LJt9hTALsB27Pptxq6fbBMubcKIvoM2u7JRHaZmQzvsgj68v+BY\n",
1104 1102 "AXTm0iNwoSJKnyMYwrHiTx+rPoWDv4MZw2/pVTpz3IyZu4m8gshYklyDUPZTKHuLfiesIDabdkB5\n",
1105 1103 "3YGSyHx8B4qDu55ifC4bO+yvPoYiTlcruZMB/LUG5tZPNDB313fVZAzMneHQBPunGD+I9cMbSgNO\n",
1106 1104 "/TgL8x+hb/h533ibj3Ex8XapLKiqgX80he3p1/Ilxn0Kw8dC4uuAZxz5IWH9J8WCZRiDYiHJHaOr\n",
1107 1105 "oKW/ii+syiz99WplaWHh79iSGeHIeRojHLNUyvZ6KYuFtEGjnGAMga8xyhNyo3zQoKiCbkY8GFWV\n",
1108 1106 "YOVJVYWkxXweRcCsWvkM4QQcdrXYy3odqg8ZbDuxofU2rh5E34VXU3pr3i5sJ2hwxrAUb8fBO/PA\n",
1109 1107 "EttK21qbSj5sSC+xr8Re/0+gI96KnQWdy+XiSwAKV+XikD5Rcq6Ev+rekiWgwDiBl7oSgJAL0o+z\n",
1110 1108 "jBjnAVrsPCDqgrSK4i+gka0CXMDh/kVspbwFOAatSQ+Sx1dh6XMMmtIxMDh8OBFt7xQeUI8HfAMP\n",
1111 1109 "eAGPdRrgxlOuxgNuwANuztuBB1ySOTpJr7Ktw0BVgCF5Nfxh9K4CPM18PN4QoADPlQMYgp31CSwl\n",
1112 1110 "5wH6UOHVgOWA04AcPE4f6r8asBxwkQCPcyqQeaYjeBwdHucoHuc4Hof4Oz7iYItkwkmM93p2jYae\n",
1113 1111 "GBvty/vDSk9zdWHXpAmdztay29uqpzaV28prC1SlDT67b9ItdTMeKJme350sjVa5ShvavB72ZJ6n\n",
1114 1112 "wdvcGvSXNccKQl21JRMR0VhbHm4pr58+sahzmitSb6+c6AnGKszkD107clblVPyhk0KnyphqC0l6\n",
1115 1113 "EABW1fPekPNprtCgLh4kJ0bM9Gqa77VDSB7jD01ESRZsTKaq5H3IikH47duG5InlBWyQtML0uz7j\n",
1116 1114 "EFbIC8LKM1lfJW+bINg3V2eT9JHf1jCEfOI8hzhIEtpSbXyUbsPhk3QZyvRrgMWAJM6cOobwZeeQ\n",
1117 1115 "tGZyZpzOhtsa6/t2td80+a0lyW9a/1n9pkvoUOs+NIQzAEsJ7YFaQgMzLYss92HSMls8Fq5WlhBD\n",
1118 1116 "KihRpcK6EgrmgkEBp16tCN5baWlF+a9iPd0g59IQwglZ49CAudHTGGpUUSbtg6l2WnB1DqY6yail\n",
1119 1117 "sYx378llvWVLynj39iPm8FyEQT1HvHIAJ0VFBZxEu+0F28DbuOoGucB56oY4fDkNuAQ4hvO1Juxf\n",
1120 1118 "rAbsByxvw+DStqsNPtttp9sutsGPog3O3ZD9euxx7G8/hj2O5bje2r6rHbuQ7ReVbaAVUi9O7NZ2\n",
1121 1119 "kHN3x5GOkx3nO+Dc3YHq4atVgB10Uyfd1Hmk82Tn+U7chLO97s55OOfb0YnhAk+a7i6bB8+7GK5P\n",
1122 1120 "eS/A0K4ADzqfJIBnpEeeh0c+GRz7yANHmk42nW9SyQ873kO8UgkJM7bvakcjll/VdW1wJH921JH8\n",
1123 1121 "4/6CqkSVp6TSHnInq4vClY6Qt67eVdVYGplmixhrq80lBeacgurykuh4X/P7KyoLKmxF5f4Sc3GV\n",
1124 1122 "29toEPXNgZI6j6VqQmlpmdFWaDIWuczDJaO+6BWsiHXzOa2e3Su54VpyFotLC9ayKcVSmy+3bKGM\n",
1125 1123 "Sbf0Aga41grZBYRWfRlTbqm+gjjj6kOSs56uKkAugiy4Mg2eqzTOCUT55OCq6GYe3gmwvjgDYHke\n",
1126 1124 "edKTY9GsGJDDAKjg9gPzkzIcxeTKN+ViIkyFh2R3PCcLUyfAotnroU7oDcEd2s87gd/jD/llX+hK\n",
1127 1125 "ayNOJWW6GpXC6HEyw+OR1tlddj78v42hfh4G/RMYX96G0noK49OlKrSfydjOCVhjsCA8RbOGvQA/\n",
1128 1126 "+yPyIcIBygBRmdMnqs6BdpFIil6to9H9v9BAHrtxe7BPHNsexMLyiiLvp7YAmVv3pJhkbvF7Qolw\n",
1129 1127 "STILfAQ3w6lHpuETvpCy42gPjzILuph76Npjb3s2bM6A2eAxhAw8TaPElMWXJXx8C9EulmySsgxG\n",
1130 1128 "KQ+RU0yTukfdp16mVisu529TSBUcAs/LX5q/Kl/D74nZum3zbEttapDZ4qR2ngrM7PSVuls9T70U\n",
1131 1129 "Pz+a+fkAwnufxwH26nwU0Zw/JX9+/vJ8NYpTDuPUC1XEQEpuqNFYRA4T6Kf3UK0yOT2u/Mr8/Aqz\n",
1132 1130 "y6Gp4R/LnPyjtcLCP4pJm78kPy9Hb/bY7JUl+WZ9Tp7HJusBLKiCbUme4GTbUvkhSa0BFakSbZer\n",
1133 1131 "SSFsgW0kWwjF8oyUqkfhm6iBFizNhNXGNkBZDtemFF9/RasyykTnTJknFaJwaQ2WLwiqKc0CMXH+\n",
1134 1132 "KI+jmeLGclU6hNe3puAa9g/rEGzDNYOw53aQC52YC0164Lj4pngJJgbwdEo7xEp4D2qV77Rvai/B\n",
1135 1133 "ccWhrdTy5IcheKP8Xdw41bjAqFqRThinwQ/uTzAWMBiLjDU8UXoYxgLfMcokEQ6q2Xt2mDzYLfYy\n",
1136 1134 "O280FrKuMjyv2qs6rDqhOoeoVRQ2bx2gFxPoETW4bDFhoAm+qjmtuajBq0YUEdkfKmmQPRVQS/77\n",
1137 1135 "+Ub0UURvTb9u/BUqhRRYSqu8FPgzYiMmD0QALVc91yL+TeBvxJaFf/VXCz7+zeMt69kUFmQLhrfT\n",
1138 1136 "v6eHX2Et0eGn2JIo/Ex55/ktHzPL2FelQnAvLabVR8bcBwqrXk27ptbBlDVE72TAbPVYQ1bVin4L\n",
1139 1137 "baj2e1RXpD956G6ZNEMYPSy9egMJ4WtBlPQ+0X7aidHjACTzvoqsaPpNXA+wUDpfOUil8h25sLt5\n",
1140 1138 "GEvu7wOIAeMpAALLY95HjARpOdE2QIqIKsVf6ztI6KOlMxbMR7O7JG/mX4IdxDKswuiUaBVG2T8A\n",
1141 1139 "KkG8shbrZOKOjOLjKlytxADYDObIquLmYvipH0b25wDbAUsBy1DafMBFApT7JuA4CgfttrQEBb5B\n",
1142 1140 "myfOTNEoK73KtQ5GtmB1KS9vZPKxxTVMQeXst3yadFvK3BaHv6E4Ot0w3fyXt9TcnPAWBiNFZ9gD\n",
1143 1141 "c5jH4gvWFpTUlVlbIvqZC1x1XXVVnS1R90+VebKc5slSdpfMt/UCOU7iGc+Ca+ezUW7NwuzWiv3Q\n",
1144 1142 "+8oynZJCQSu2htdjx3o+G7QzhsCtMkUWefdKGqT+SpQ7/41JsTJEiOmt6l0wg72E934RGtIlizLj\n",
1145 1143 "SYux4WSH5//vcaXB1eTMTlQaNFbiGBorhRAKnF2SX2Hv4jXJ8kPtHM8PVQijjrdR1nlo/KP0WR/g\n",
1146 1144 "isJGUlkghkrm6BwuR8ARc6hXsE+gfmLdN+Z7YuU3InmSY5lwPR6c7x7hB1KRCe60RUR+2++WObhf\n",
1147 1145 "GMPBncLLGgSMQG/dWD5u5ZLZrMkyKY3ttZKK2JOkl7An+wPVW4jhcUlFIVz67SIOhbj+gsXI1NwF\n",
1148 1146 "ueKK9FTDAgMX8iraFIHxjOIPsg7tvw9dqo+vKYQ02PXQArBlsip/HW7B9km53y9zuvsp3IuybS6z\n",
1149 1147 "vbOnxhBsG0vqKxWK7SzBNqsa/jjQUuUgiu3hb9ri7pERWU50rn5C2ScGH3Je+v6yR7FkTpYxiqtz\n",
1150 1148 "zX2fo/sK0ovK7uP3Ddzve9S30YfgX4j6oHCdyr85mP3NHfSboPQCF/XAa2Vnyi5DFyHquRdgbGJu\n",
1151 1149 "4DrKaw1nGi43qMbnMVruGspDJy2qyHA6IC7zX7P3R/ZynbUI7v2aEE3Jg5h2GbaWQ2Tj6Sh3lLP3\n",
1152 1150 "h43TGmgPlMPrvH3omGaPoGbqGmXXPweNHCqt7EOHNj8gx43jwzosU9WMepxAi1S42w/IUfl4ZyS/\n",
1153 1151 "ci3gvYzt9Gs4VwkD7sfETyT6tA54AhDCxxSuLDkZ29pxXgvZZqYWiGQf8cmko7B2364egDn3CfU5\n",
1154 1152 "tagEXaStUqEPvPqyu7NqxWjEsz/A6gQRonkjRHQz/kenduEPFlniinKvKmLj/zHn1H+e+S3xW9PY\n",
1155 1153 "68Mr2VcERVbiK1xWZvbkHr7s4bJS0SLSPCTdj8F7FlwQzprBusAfmeVRVbdj9UMcApsBJwC/Umzx\n",
1156 1154 "+WJIoyZjhjcxdLwD0+etml0wfV6tWa/BuQFXzCG1RZnYjDDjRqcCAbNBRWulPAs8RGT+7EF0KMzB\n",
1157 1155 "0hOKodpC6WVAK0zWnsOVBVdy4KxrKPUz+9ASM9G0+jqkNZ8tx1b4g7A3lqPW8Nb6CyaTWKsGJY18\n",
1158 1156 "73fxCA/iEeZrloOPZS1tvAEoZNEvKMgMMVvzNZhRIHagJViyDBn/k2LN43qisdMo09lSeIJ5ZJIG\n",
1159 1157 "EhcKOhiEGIib8BQmzzewiRLAhkSAi95mi8j/qWjVo7rtHvGLnV8U75nx5NQviF+Y+iR/k19mX6B/\n",
1160 1158 "tezR4Uf5m2TUcX7BrwziT2UPHqxf008I22BtdQbL2FmCTLjAV7J78fLuA/wQQOGeLHAoFoeSprCY\n",
1161 1159 "FGeJi0XEctKuSJoR0ek5MRPPScfnTkz/r+FZZsH15zktnH9UY8M7aIbS0Db4wLgAMYkOao6jDaiH\n",
1162 1160 "MiSdK5KG0VBgmoWye2T6rO49GHQKENAcwJtYrCZ1oDrMBIJTLxxAyK/FOkTCGUqaERQHca3kkDm6\n",
1163 1161 "hUlTq17mIkPAHO0KJRaO1JyjCFx2rpRm4rAMSpBk5lcDLxheNrxmUC3McG8tlG7DsdkJwzkDkX4k\n",
1164 1162 "TRtzn8tN5crBQ7V8wpuVuzj3/txHER8SjkeSBTy6IcATeJm0LYZYQRnncATMGHPcmGmoFC0IDTUn\n",
1165 1163 "FwFUpd9j8IgCenIQQybnIiKJxVB7J6ALX6lxpRLB8SzHlqoCgCw5jbU473URVQesi+VQHrIpgKRV\n",
1166 1164 "5ZDujWM2tEsX2mUfbPJOCRfQSpqIjQgNwo8GgUhaA7IGp5KDmkpVcBt4A68f/EhSAQAOIFIVXteU\n",
1167 1165 "bISOPjTvD2HP3pPTByISP8Q9GfAHyPzDXCxHTqLsVcI6lP02WigqI92KZ6HYCyexwF2lXQc+JkhD\n",
1168 1166 "WpCDKJsRVywCS1ud9192L3jsC7d855UFz2ye+9GbL7zw5kf//u8Y3ywjFnaC94V8MYklHW/wa9Dn\n",
1169 1167 "t7EX2Mvo84vwjPdm2UT4OEgDXNZ9u4/iZ+JqX9bD+wTgV4LiWGO2IEyUtAUrAzPGv/QTOdvwpkZo\n",
1170 1168 "8Jcz7IMT1y4K4AP4KBtio4micljkEzmu/1EM8UuAKwAjBrZ1GWNHaRaH/nw+EDdhb/81rL7uA0Ab\n",
1171 1169 "G7MazbQoohREKDpBm49aoM7S3vHPcSfgAcA3BHkhynVKraXfKF6RT+V+byFtY2C/cEw4hdC41Dam\n",
1172 1170 "4BcXs27tH2JWpfmVGqEOAFd0ya7mTXev+jAmtL2aw5gGTmrOYyh1ogF9iCPWXbCBbLL04CCQgo9c\n",
1173 1171 "yJ4GHgCcBvwRkANpLcBVDwRDUsjJh0XYL1CV1wGHAT2AQpwknNJkFstFxO6VJwudKy2AUwA9cp2S\n",
1174 1172 "LWRB9jUdB3wfANrYGB+FZWrnGG0+xFSRD56YdcnRdpPZY9Y5SrxBc8WhObPZX38sRZOiJmmoDS5g\n",
1175 1173 "b/J1DrVBWuf8TI6x+BBWOa02WuVkvz+Y/f4O/v1UPizaXra9Zjtju2zj2jDuTntsIRvvQUmoURww\n",
1176 1174 "dFa+XPla5ZnKy5W4B5RynspQJe6pZATyQoqNDPN5vk58RShglj2W0Vi+g3K0JOg+96Evi/K4zVMK\n",
1177 1175 "hmgelCknFVerxTQdk8gEixJ7IndI5gtczKE/h99kyKWgEltxUINIxOJCOURFyjQo5VlylZ/ZlOAP\n",
1178 1176 "Y8KhG6+2N7baKa6ZNYRFvVFnV87NTEMgejDCN13SU2r6cf23cCQKPWXgsP6E/pxetTD9Hf0emCno\n",
1179 1177 "yI/eSsTDdOYs7aMh5wRGp/McYo0Bb2NEhe0EFcJMZPzZG3+re/oL/37rv/3brT985Gndd77T850z\n",
1180 1178 "NxujrHf4e2zu8O6o8eborl0wyxJcHL7Jx5gcdk4SsHV0H5rbZQB8nSQPb3jyglX6BgYZiqb4AOBO\n",
1181 1179 "rEVgLSIq9lSIZ5u2Mx+WchHWwf9ItyDp71k/bIozgXIhO5nsAu9uUMoFRV2Rtobr/+kW7XRsAyxE\n",
1182 1180 "0j9p/5mPmAOyHRzvus/gHd8D+FvAIxhVp2jnY1QFtRwqyVczulAaUxxoTPBm4eojhQGHAPcDzmDo\n",
1183 1181 "fgJwnyEz9IyLbpt14NbLx/78DX4HY/yXhScxxr8P32G1YBd8fCUrD0m9gJWAc4BXAD8B3I57I0KH\n",
1184 1182 "MAccoypLv8jz4++eokj+ADCELaUWzXTNbRrVijQimfNh5h3Nn/ifAYOmSFMDptWHcOcUjbx1gkUg\n",
1185 1183 "8XDBso0vtSBjZQVdoA1CjD3aPojxdJZx75IWrcalgSUbVzcayZhtw/CvWTFffuUPX2F3sO8Ob2kJ\n",
1186 1184 "s2UtvE9Tm6A+/4txNs25ik2zf+R9UUuxjwJsZcobklq9vP9o1FfgasQX33zeQMx3c9I8y7zYfL/5\n",
1187 1185 "UbN2ITko9RdmDpzHbTVlQrVQUBN2heKZyKfG7ytPSZsQimXeOvI9QgdYB8BKIx3NmYxZ6yO81B4s\n",
1188 1186 "Y5oBGwAfAXqydmMfkd8aBqGPyGsaOwwgy+ZS0zgdTv5nu3MAJEaaYkcxPhUPgM+RfG+W4MxVi/AA\n",
1189 1187 "fdhLyQE/wq6yA2AozC1zl/G7d/kPgMGxEKpjLgKK9AWWwZg9J1CIuCKI7rWQIlpDi6MIYLK/nBRF\n",
1190 1188 "LbuzVV0P/brXsQQE1X0oehmKno+QPr2BJTxHNsYIgWxlx5mlTRKZM95RkZNT0RHP/J3+ZxPz8yf+\n",
1191 1189 "2XTlr/i1Sbctq65edtukzN/2yff8VSLxV/dMzvwlHculvOsw+/dUZUh6lI/K/bkqtOH0Ws0mLIdz\n",
1192 1190 "SeeSuaUtg9JKDLFLER34pOU8ZsVbLdhYedzyLcvzlr0WNYwCQBpN21cbOZCBO6jTB9Pf8jzvEemG\n",
1193 1191 "siF43fGLanJwDA+mwsomJdmmZFqOm39wk3vHBLfs3pGawJc07IqkkRO4zs1Vtvdg+SXoLfAnM8lf\n",
1194 1192 "mELSIqxtKQQmIiEh5NF7FuxhWSyWMgvvrW5Lvw2GLu4J+IEzlD7kHHTKM4KH1KYKOkUOkEJfTaPz\n",
1195 1193 "KujBWwAxLHmex7uksDHnAX/gT5s0wVgh4Iq5ul3zXHxhfw67lLfiwCZGERYBsKYZWBVeF94cxtbF\n",
1196 1194 "Q8gwigw3WTN5YddF+hNlaHAVuWpcCdc01+eQYV8mr/TukoMwf4A/vHRnmCesDK8Fm+hmfk3eE7py\n",
1197 1195 "XTn8fFwRnVc+lFACIwS8sQiMqmP8LwVbdLHfuLqqN/lYkXX4XRVj7+WfMs/pCXZaQ8W3R6Ozm336\n",
1198 1196 "afZ6xlRd9ubCr/6f6nmF4l1W50CTL5hXlBeb9/nKooq6Wl/bgmgsr8Ts9zZ9aVWBRT4H+kD8UNir\n",
1199 1197 "Wi+ohDvw9hjCZbON7Dkme9+eRSRW8khOGkYDb/P1gllFY4wqqZqlWqy6X/WoimszBgSTl1M0UAKF\n",
1200 1198 "L/CxvAAUjHSWx0cvVcTlzf3cHQnV+o3yHk2Cl/8RlT8XtslmbHPCpZscdMdGAc+GBv+EcOBKgEiR\n",
1201 1199 "lyd6RJQHT+OViTs+J34olzfyS/FDZuLlaYXHJC3mW8Qjh+IoDMkEJjkZE281HdZczhzWSKKGljca\n",
1202 1200 "Yri6TAHhM/TcamLGQuhD+Yll4h5G5/xch8WZmSgHLZbux0JSVGkoDAaD9hGLMFP7nDnt984SP7zn\n",
1203 1201 "no3j6riG4l5KazCVPcqyrFyXiddpkXifuEZ8QhwtfcwmFF+UQb/mj6OVaTbUFNkWCwteR4Y6MpnQ\n",
1204 1202 "C3VNm1UebFHej7W3mpG2p6Fw6KQmPTfrXtSQC/Ee8Fr2igbhLTnu2R6Visn7Ppqh6/iAQ/xPNE/q\n",
1205 1203 "Fg0HcObIb3ar7vvfM8fPcuaoUt3ozHHkHJejQbVcMPCVwkKsagWM/0mb/KI3auWdlEHtWW0OH3a1\n",
1206 1204 "FjgvGLGGTQv5lnxxBb8z//78R/M35j+Xn8o/lD+YfzY/B83CwTuPGWt3HNTl4P2PofEIjLl+vtjl\n",
1207 1205 "Ksa/Q5kLVdTu8djH/EP8VpVLGFHd/InxW1Wu6H811uvIOXGYP/9jgklwCnt0DBuPOoUkC/GkeAOQ\n",
1208 1206 "Va2ILHRmUM+eNGm2ulYX9/vjOtVjrV1drQH+P9nn9VfCKdW3/9fn9f+XPq+qjhv7vIojx/g4Oovm\n",
1209 1207 "Fp3w7ZQ2RGM9kQ2l4eMjwkwtfVZ8T6G6yEwwY2edT5hs5A3Kgfs0azRPaOhXmpmaRZr7NPxXaoWq\n",
1210 1208 "jf9Kl9lyHNSd1dEUpcaUqHCkyL5FCHYuD5Ya/u+HfL4a3pO4E5PWRj4fCF3isPAPqjt4G22gPdkB\n",
1211 1209 "kCi9LKhGGZSUXj4g93oVSsnJ9J4x3bYr21vrM71U5k52C3tHfnL98Zz2w/UKxQhoNZbz8fz8Afxu\n",
1212 1210 "sThROCe+y39n38NUKvmsBDGXBZG4K/mq5nsY/N89IHPkmDl8XTzG79cK5WNCJl9z5K1BJ46wCF/Q\n",
1213 1211 "lJtZ4VTmGf7+L9hR8djHTeLjHx/4fzYvJoR4Xv8o/lioF97YY9F41DV7nJpCjiGNwLFKo1XXpGFY\n",
1214 1212 "IC5MFuVoC7U4LIfmu1z7kBbmB7u0B7Svak38W63FafFbopbJll7LEstKy1rLJst2CziBEQrLco6v\n",
1215 1213 "x2GQ5BqETWKYMKLI2ccr7LsmZNJYipCUz9JvEK+kTxsuGlAVvaHAEDRkWPZXG9Ybthh2GvYbjhlM\n",
1216 1214 "GK/g75AK8YU7b1rljZmzcVltcTlg9Z5x2FPOVR0RB5MmdIVcgc7bGhtv6wy4Ql0T/nQ4NCXijvQ9\n",
1217 1215 "0Nb2l/Mi7oYp4cN79VWTZkyILmjzVbYtaKydOalatzd+iyk6pS/ccf/sCRNm398enj8larol/r+y\n",
1218 1216 "/X9Vtv8bv/y/Hr+8bOQjMZ/LrFD4CsyHi2Bc3K/nMnIrhHMQmCwOM39O8zV7Ulk6H3wwW/pz4J4y\n",
1219 1217 "iDWodVAOTtvvFK8kixDEOeCMObud85xLnauc65ybnTuc+5xHnby5nnSeB9lAgcpMekClNuOLIlsk\n",
1220 1218 "Obz0wDGHmG8tyS8pN/P5ZzL79XRDkd1p0OfYyqtdrHp4KduaSAy/5izLzcQwGPkt28b7WrlQI/w8\n",
1221 1219 "VRtCLIJaouHkarjsimIbgpemiz9vNl6PzdJfobmSfrPiUgV1worCiqqK5oopFfMrllc8VLGhAgwB\n",
1222 1220 "ByperTCRpUtwUKrS2KyZmL/jPBdo7U9kRiL2cfuLSRDFruJAcay4u3he8dLiVcXrijcX7yjeV3y0\n",
1223 1221 "GIIoPl9MKj1ifqZPV16spN5XWVAZrGyq7Knsq1xWubpyfeWWyp2V+yuPVZoWSlWUN4QGc4dGnTcQ\n",
1224 1222 "s8r+T2RFqXj3RFwqeUXhiEbZtkR3rCcuunvt3lBReNK8uXlOt8k+wSUWTCt1ufxN8apYa9tfTKqc\n",
1225 1223 "y1TVbRNcnS1Tn+3d5K6w6fPtXtHrDzW1/Kztr2UZ145cFutFl1AkVAgFUnEF12IqsPZUmFg9FTTz\n",
1226 1224 "Oa4iu5MHgkAjdH7HM4ne1tv8XlcolgwkFnX6fJ2LEvFFnf4VrXPntjDV3kRzq8MXKTfXTLsjHr9z\n",
1227 1225 "ak3VlDtv8n29peXriHtTJSwTX2RfEwJCTJgibE5NDaWmkQIw1SJHa0b/D+J9Bi8F6X0GC4NVwebg\n",
1228 1226 "lOD84PIgoo1uDe4KHgi+GjSR+Vi7hr8jbbuz3d8ebZ/c3tu+pH1l+9r2Te3b2wfaj7RjbG0/1y4u\n",
1229 1227 "FPY0iho+ZHvFGj7Ra8jOyauE80rVWFI3DaZuIv7kLj4mVma8ca4iJpV9dXRy/HddKf8Urcz6mKOH\n",
1230 1228 "iy/afRPLSibW1dic1RMipWWRSrvNFykraQzV2O1VSJnotzdXVzmqAl6zxRuocvmrh4+bfVXVDlup\n",
1231 1229 "3VAbtFcHvVc80aDL5vHb8v0euzPQ6IHjg6M8YLMFyx3OQLTCH7UVl5vySt2WhkqTp9hmLizL9zdY\n",
1232 1230 "3R45DtbI+3yB8GMu42amTsVDqQRJOE6m+7kkYQck7LjkIAk7Ch1VjmbHFMd8x3LHQ44Njq2OXY4D\n",
1233 1231 "jlcdXMJ8RMjnnc/SHyY5h51hfzganhzuDS8JYztoU3h7eCB8JAw5h8+FeV8gJRYKWHAwFQwJ/N+1\n",
1234 1232 "FKfkf8072h4z3oaUryGaynLig0ifdl90Uy9yF7iD7iZ3j1v2Hlzv3uLe6d7vPuY2Ybe+v5F6Z6Or\n",
1235 1233 "MdAYa+xunNe4tHFV47rGzY07Gvc1Hm1E72w830jGB+MW5E7XxKvsstQZx8OMW9ZRS3h+T2hqpMjd\n",
1236 1234 "0FNX3+th1qquid6I21c2uSY8NVrpzO0qmhWuiHitVm+koibus7C7Y3+9YnrlpNm1NTPiFXV+k9tU\n",
1237 1235 "veiWqNteU1zqiXbPmvMFX8xdG/eUttSXhnvm0Fh+50gN26TKFxoEB19b+NU1wp4y0cHbaJ2oV9eg\n",
1238 1236 "2jSsomZOp8MqG1eT1zyNEdGJjXzk8Mttkm1yWPK4tq0z5ulUjInOhrAjYMsz8SStKU/DRJWhaILX\n",
1239 1237 "UOgwiWetVqPZ5A7U1ZRphvVl829pdhbm5RvzjBOaI1oHu+xqaW0Jl2iMNuiqH45Usw94HfXYwdcJ\n",
1240 1238 "GB00XpUuELPZIszX/t26gjuefan1JJvLCxxOq57EcxWOVIvt/DcGwUFcd/rQnhwRyyWNqMVzuRoZ\n",
1241 1239 "a4TVEit3FLIpw4dF8/Bk1ji8n50+GWOH2IGmScM3D0++CXl187z+iuelE7w8H64VcK2ECPX0il8/\n",
1242 1240 "DqCFPVqRIWeNtZzPQOVWsXV4arsYP6nafaVXdeFKQYbj8qfst+I7gkbIEUKSBpaQWpEmeT39kSOq\n",
1243 1241 "CDBK4t8IavqG/tBD2yI2lcpmY92tu3a1/sPx9es3eNk6tm74IdY1/P3h77Mu+BlRQeKP+Ejn5f2v\n",
1244 1242 "RmgYncf8gyl/qN+pvoKJrHKov1jFK65iPi6YShFrSrX8ELHGiCMQmBiLRmNYmY3a2dMEwf9zuPgj\n",
1245 1243 "Mv7vZ02NoknntFrsueoJHs8EbYNuajQ6udDPJ+SXh+9gPxoWHuzoeNDaVGgqsZpdNmuOr742ou9p\n",
1246 1244 "624pa/SW2+wT94vLP94sPv1xA68ytclbRi6IPaJGsAm9kk3N5aBTY7KEtqcflEzyBxv2rCRr9oOD\n",
1247 1245 "r1ONoT1Wesm5oo4PsGRKhA0DPnvLQSN1GHL50MrbjhzivjzmUsLbs6rpEyd9a2/nH5n75omtTx3s\n",
1248 1246 "Glm9sejBlsdavlm8iiNE2i3ME+eybwilwk3C7XvyNGZ1jRJKz2xJ1Q0Ke5pFCNIuGjhGxGqOboz3\n",
1249 1247 "ROZHHPnXbLSlIrQR7wnxxbMckw8pLYOpFuzBjd3p8o7bA3Pc+CtxrspaWlNaNs1f2VVUEdRM4B9r\n",
1250 1248 "S8unVPq7Snw+XQ19LJtWqXyrtpTgW3/l5BKvT1fLvuFu8LtceXnOeo+7IVDgzMtz1ZXt5pfZxKCS\n",
1251 1249 "SM3sFkHL35Wfa5C5gl/S6Plcrs8YCIqEuYTGIWGPHm+Fz+yNlQ6NxqHh3e8W9sHwQvad4Vz2AQu2\n",
1252 1250 "vdT2zN+1LUkkrs53gpSTzVcS9WQ6o6d3m0PvNmdc9rHGyspGjYNR9t8dvo0XwbO/p+3vnuEFDP8M\n",
1253 1251 "2fMxZf7IWTaTfPFyBHiHIqijl7nYDnIIGvXv+f88HkWWX+BmmV+AjzrvCmfG8A48lk2/jHQuK2Ek\n",
1254 1252 "IL4oviKU8f4+g8JEY36z8fnNlpnsyNr2upoI+TRgGqxgZkyDKhvFu6Q/fMjJBJnmA0E0FijXeVmE\n",
1255 1253 "edH/5V0mVq4SX8yGnLYYjTOGN82YyyZOZhO95fl2Cj89bGeRraMxqC3GnIMHxVc+bikvLKZY1GwD\n",
1256 1254 "e0CJO/ZrcYdqPX/rufR8b7Pm66ZfYN7rpl9khWPS78im/5aVU3ovF9Zeut8o3y+cJfmZkM7lZxHK\n",
1257 1255 "2VekonJogTBWXlMus1iDAOWQYuKsVRG7rInMJ/LAjA67hnIKLLsIrn7bAIJ3XNC/zFso4x/KsKHC\n",
1258 1256 "2BVowAZ2RTI5y+SzvH6LCC0Th3J2OogrVLEVclSyFdJTOJl4C/A9QFP2jO6AWjG0UVgoFQ9x6QCO\n",
1259 1257 "ZclufJ9iwbZQ2gp4C/A9AMWBJTcrCtjqB+cqRQ1cB02zF8rteYAfp8XrcNULj4h9UHgPeck5BHWZ\n",
1260 1258 "nA2A0YW6bAI0oXSKAjUVsDtLvjoVQFGfDqD0uSh9CnmJoWDyXO8GfI6XFBv134J9sZeP2Nasm7r3\n",
1261 1259 "2b+dvWqm3z9z1ey/ZeLwcM+UKe+0P353e/vdj7e/014/e2kstnR2/W+44hKpnf943/wvz6+lNrCY\n",
1262 1260 "v2svtQGT0gZ+MCb95mz6u8KxMemPZdMvC8fHpN+RTf+t8B80XizmfbGTt6VKYd6eAjVmiAIY22R7\n",
1263 1261 "5Thdb1yvzJwD73EwJ/8ZV+fyGTEfFfA/hYNSpcpynS5Zbi130H/875h+KXaO6ZSG4TVs3/DX2bzh\n",
1264 1262 "XewRS6ZbjumRhlzxlfbftA//rj3TJ7P96B9JVnal35WPSX8sm36ZVYxJvyOb/lsWUtLd4j+KJ7Pp\n",
1265 1263 "v2OerAz/gvJ3yPlzKaI/UjqXITimHuQSgwWSRZlC+xlfu2BKH+pXqzNcJTm0E2yk67zBqziIx/Nq\n",
1266 1264 "Z6zyMlLPGH3uUYkmPl8bGa16bQotCJZymEgW97DHenqGv9TDjg9/SXzl+ec/bmEzh/vZ12+5ZWRk\n",
1267 1265 "5Jf8xQdU9/HncPL6a4V3TBRHceQKT8+lNiWnv2uQ4yvewv/sJznJ6b81yese20hAOMqfu0SI78lH\n",
1268 1266 "2/kMrYbsDbFdUKI0D9e45uG18qcYbRZHx7YKdU/+DdrC6NhM9c0b+TX7E72nIuU9/Vp+f/y9Pkw2\n",
1269 1267 "NsXyexX2Zd4r+y3dX0LP93aDMJpO95fQ/ZeEXZl8xqX/Tvj2de9fKnw8olHazW7Kv0yZJ9zZ9jQ2\n",
1270 1268 "/SIzZ+u5m/IpU/L/ONNeVfn0firkPs+qx6Q/lk2/zIJj0tdn03/DEtly45ReK9dH2H/d9IvCP2Xr\n",
1271 1269 "E6f61Cr1+QdKb+By/ojun6Dkc+i66ReEPdReGkYC7CPxpDBb+GXqllBqTih1i0KcmJpjSU0bktjE\n",
1272 1270 "aZjMS+lPaqKlv1MFBbrT2envjHZO7uztXNK5snNt56bO7Z3w9IYC3XmukyvQ8rbWNEt/jYor6TWX\n",
1273 1271 "akhJrymsqapprplSM79mec1DNRtqttbsqjlQ82oNV4Zp70Lues28TTbz1soX40a+pPAX8gmVFRLT\n",
1274 1272 "Y6GlXy1ekUrlT82W/lbGle3Wi62kbLcWtAZbm1p7Wvtal7Wubl3fuqV1Z+v+1mOtJq5Aa1yK/gEV\n",
1275 1273 "VDYBzWrQMb4UHuPxqx0l81FUajVXW6C3MIOotxpKQ47yKvvECZaKoDOozrWb85y5+fXNE+NVba5b\n",
1276 1274 "moqiNcWeSFtXW8QTaLulpuXz1a2hBcXRGnd01qJZ0aqO6uKSFqZSB3yuCkdugS/fXqjOM+VoNdYO\n",
1277 1275 "0VHYFAqErOVhT2V9udNZXNNWH5neUOAP13eZPfXl4caKoglTk+GbJ+blqATl3brxDrPv9nfCJrnP\n",
1278 1276 "8bYTpnceUtrODqUvusUwtZ2Qcv/mG6cr/iVhiuk5Y4+oVvxLdETJb1JGShoCr95+3GOGIoVFjprU\n",
1279 1277 "Gy1UqH49n4r4MGvC+kTeZ4000l6rIyKWvtXz1lvvihXvvsW+OXwv+2bLnvb2PVS3uXxeWEDjYz3V\n",
1280 1278 "7R3egkfTb86mvyv8fEz6Y9n0y+PSv51Nf0/45Zj09dn03wh/pGefy+fiBfzZY8KzqaYQb5appow3\n",
1281 1279 "fjMFSTTZArL2SBpxwNLv4yu9GCVmh95xVn5EK5P5YOQfjJlxmEy68MFo6S9h2BQqcZUESsYe2G4u\n",
1282 1280 "2VGyr+RoCTaFSs7Dqimmsl8zpfOmjF1seKCpeXNW0yW13TGr7pdGx3FV54SGuKPHXl5V5c8zBaqq\n",
1283 1281 "yu09jnjEH3WarhnbVaqC4rm317IXh6dHpjSUWDQaS0nDlAhLD3fW3j632K4pyM8M+4q+oUqRXGPK\n",
1284 1282 "/P9/aF7js7Q4ndKblfFIbpsunu6g9BZl/Hp6TPod2fTfCk9m4gyzfxa/xtMn0Tzx5JeEMemvZNPn\n",
1285 1283 "fEne0gjz8e4X7CzXvmcwdWpmKDUrlKobws67vEUo2eqIQ3CmJVU0iGOJOksqMpiKhLBw71Bj6Otw\n",
1286 1284 "dvg7oh2TO3o7lnSs7FjbsaljeweYMDD0dZzrEImW4ebR9VoVf69VudebeRv4h4arz8D2lImT+JBn\n",
1287 1285 "UxF1ShVvUdhM9F300fjmK/AFfU2+Hl+fb5lvtW+9b4tvp2+/75jPtFC6Sf4J1wp6aE+xx9UT6In1\n",
1288 1286 "dPfM61nas6pnXc/mnh09+3qO9qD59JzvwZ7i+KWgFw6MmRQ+/kUaJ3ordAHZ6RMWcmMWAqqxtpeN\n",
1289 1287 "7Bdj1wVMpRJLZjZ74hOKEvPvrC5ffctNwfYvV7TWFWtUpsyK4YuF07rqbWUBu6/RZx23fLAGbaEq\n",
1290 1288 "bEk2t5XpjQ/FQp32qkk1Py5K2LIK30qNLeh1lTtyCqpinsza8XfsJ/TOe6mNtAl7rpv+OWWuvDp9\n",
1291 1289 "ntIGr05fKnyH0oMj74s/Fd/n6YvltYcoUptC+mzxfT6jR1lBKhbCQCEOEfEYbe2lYhlv5AmZjfoJ\n",
1292 1290 "meY1QXFLxmBahlESQeZ5I/M6vX5v1DvZ2+td4l3pXevd5N3uRQRoNDLvOW+GAdTDG40nQ15G40zu\n",
1293 1291 "VaPw+CVqBYZkSZML3g7JTX8wQPPKVNB2Eh+gg9R2gq5gIBgLdgfnBZcGVwXBI7wjuC94NIi2Ezwf\n",
1294 1292 "lKnBG9A0Gy42UNNsKGgINjQ19DT0NSxrWN2wvmFLw86G/Q3HGjD1YqxvvDZAWob17SqKSXF29Lph\n",
1295 1293 "0o4ebY8Oe66OqSaGPx68JiL17okbNw6vvYpYLDs2XKH3u1x57/8wJv1r2fQnhb+/7v1zsu1kfPpS\n",
1296 1294 "rEWVMeYKH2NiQlL4ZqotBK6ftsyxULsFHuZ59lJI3i5zupda+pv4HzpokGLyV2WWVHIwlQxlR5EM\n",
1297 1295 "Md+eJtHDX2GeqhCvMEZ/sCZK4FUkLiboVSQKEsFEU6In0ZdYllidWJ/YktiZ2J84ljBd0+Vprsgc\n",
1298 1296 "jTnK6TAo5ho/WdjkkyIHWzvayTWaCcHaZKUv2dvQOLe5dPhhVXFdi7fxZnukOuLxui1yH2+b1VPW\n",
1299 1297 "MqFoTO/WqEWLTXlP/tZbw6XeSRPc9WXVja48TbFT6d1/1zGnsDpakhnLLaKL5PxFpf8OXDd9jnDw\n",
1300 1298 "uulLhe+PSX83m/6+sF32YeYT/WkVYtU/ruT/93w+YtjhYad5vy4T+lPlIRBglVtSBYOpghCd3DIy\n",
1301 1299 "yVYN4gVVYPuw38RTuapePCizDYyhrcr00HH+FeMmfxf/4JJHf/RQlbFE3qLst+K9Wi9a6b1aC6xB\n",
1302 1300 "a5O1x9pnXWZdbV1v3WLdad1vPWY1kXkwH1zK0JnR3eR4M04HX2Ipb8/hzZIsTvuXrCt6dAo5ore0\n",
1303 1301 "tP+LGNouu6IPv8FUM+aQI7oYaty4UdZFT3F5FIuQ0zKaU38sCpm5nAnUb+T0J5k81+q5LvqiuJ9r\n",
1304 1302 "XlXCQ3tKVT7YmPgu0TyW4yv0VfkQSXG+b7nvId8G31bfLt8B36uYx8w+6gLmUH8+l6hPCYMgpAKh\n",
1305 1303 "ccrsdafUMcps4NOVWdWYfdSxe5AGsT0vM02x0vz8UvwbN0Wx14drM5ORyuEoKsI/0sFHfj5iEbay\n",
1306 1304 "1wWbMH2PUZMDHTw7BueO3T/VT5oPVYaPtmqXOqDOGLeuUq9Tb1bvUO9TH1VjtFWfV2Om1rkU5QSr\n",
1307 1305 "OVJgnCq9TV9QkqduF12xicUF/8hEdX5JhVX0f/xabn2DP1d+P7w+XGfh/YF9jt7P54YFxXffzX5J\n",
1308 1306 "89nXKP13C+X0efwhnKqdPP0JZR4tFn6lpM+k+5+g+y89IN/vHnEL28ak/+7SaD438f6TyWep8I2R\n",
1309 1307 "HJ7eQf0K/XOLkv8/0v1Xp88TnpLTeT1Ps7PZ9N8JX6X0/HH38/q8mF37ieXUHp9WxvH+MemvZNPn\n",
1310 1308 "3CB9rfAmpVOcKUo/RPm3zZDzt/P63Er1kdN/9ys5vfSq++cl5PRxsRGQz6rrp39ukbyHU87nD8QI\n",
1311 1309 "uUW4CIV8Lqnisk4+15KaDp18Osw6atSfUa8mfzDo7l20tuhydvm7ol2Tu3q7lnSt7Frbtalre9dA\n",
1312 1310 "15EurC26znXxqZ2WK1zpRzlZq4IxmnnuZ9PMkxi7kheTNHYlC5LBZFOyJ9mXXJZcnVyf3JLcmdyf\n",
1313 1311 "PJbEnOT6zJr5uIAWuquCX5CS86Mb6ueN4aq6G8S7SIyPjFFuy7++gt6jsjpuEAcjcHXEDL1alN+z\n",
1314 1312 "G+8z+55/N1dQ9O332YP0/n+o7GU9ldHDwf+rpPP7m4Qbp4/hC64Qntuj1WDjV0sewE4iZ3TSClOL\n",
1315 1313 "5aWAQ8LsnDQu9GbuVec6e1SiWi3H+MWRAlnl8XHK4DIEDDFDt2GeYalhlWGdYbNhh2Gf4agB45Th\n",
1316 1314 "PEip86we5aDVRY4XhYPKArNYmZa4pj+e+rdR3gLI0P/+5V+OJwCeMUOmAF7S+NBYDuC/bVwCEuAs\n",
1317 1315 "B8n3ZA4SLscfg4Mkm/61bPqTinzHcZbQOPD0mPSL2fR/FbZm0sX8MfevFU4KModNgI3wcb5ZGFCs\n",
1318 1316 "U2qGaMaSz2fjyhq/vxTKJdmR1OCMN1UXwkeK4pNd3Y3jHb6xwkhBfTIfMqaBXFUE+XC/l16RF1Gr\n",
1319 1317 "Yt5u7zzvUu8q7zrvZu8O7z7vUWgOJ73nQa7YTPdfV/e7AXnN2OO6kXHz5PVJba5/QvAJVDfZzWJl\n",
1320 1318 "3+oVkveQsh77jTDqY634YGO8VMbRcX6aGF8/f/30pcr8thh7PeIrgrx3rhOKhB8p79nC79/P09dT\n",
1321 1319 "+leFd+g9+4XXWIBZYQsBbY4NUUjKIUGOt90KzySK5XBWhGNoJXxKA8MjjL3WLq+DAP/EyxOFXPYX\n",
1322 1320 "e7RqDR82x3kxbcN5VyuM/VtxZcbVGbUc+AFG3FjoF4D/Y59wFJ63CE8vG7bL7BphBRZKg4CNYLfR\n",
1323 1321 "g+pFJDrUi/BQ3s+OwUPZBb+CHL2okGgslHJyFG4HKVdFqX+Ab3AUoIXTQRm4ZFPwyHwNAKcMaRvA\n",
1324 1322 "jC/uU9JWSGdxNQvwqDFjyEqnJFeHg8YRuOyjKIl0lf6++AMEuxwS/xOsWn8HUd4lypHU9Rg8Ml6w\n",
1325 1323 "q+B+NR9UBb3w9V3GVuOJdoHaZYpmPuhJQGeWnqzthddvN4QC1utYuYrRkXOg/CUW2Tr8sXy2PHfG\n",
1326 1324 "8KYr7IGPN+Ks4qDynlQPkT+VjU0FqaZAMdbNQ/15XJhfAJ/LixwGvpL3VN7uPNUKSZVngMRA+C8z\n",
1327 1325 "JRyE31Yu4DwdZsKlC4R6IoWRBCGdmrLMGZJeUPiGFkpn4Lm7JueJLN+EGbwm2iHJJGd/0ARud0vK\n",
1328 1326 "MpQ0g452jeUJyzbLC5aXLboVSVvcMtWywHKX5WHLVyxPWXZbDlpyFqbftFyCGXaORdnUegHMXmYH\n",
1329 1327 "2AZmOhY57nOscTzh0Cy8Dq1/lsBEsOYqnqRW+RRRHBy4W3xE/BswE8bxjo4T/Zl8mybUr+X35BI/\n",
1330 1328 "Nl6ZkT5RjHlm9aoq5dNF3jV0XnhesxHD71kPsw//J2sYXjt88tFb2b+z54bfZHr28PDaDThGPHJE\n",
1331 1329 "fIU6ENhwKpIC0+wpEITc3AZBLYRGXuQYHfkRx9jInzg2jwxwjBMmCF8a2c3xrZFfcxwi/BnhGcKz\n",
1332 1330 "QBZFPixG2ESYRG6sDTmwh+meRzhqqEQNlaihEjVUooZK1FCJGipRQyVqqEQNlaihEjVUooZK1FCJ\n",
1333 1331 "GipRQyVqqEQNlaihErVUopZK1FKJWipRSyVqqUQtlailErVUopZK1FKJWipRSyVqqUQtlailErVU\n",
1334 1332 "opZK1FKJOipRRyXqqEQdlaijEnVUoo5K1FGJOipRRyXqqEQdlaijEnVUoo5K1FGJOipRRyXqqEQ9\n",
1335 1333 "lainEvVUop5K1FOJeipRTyXqqUQ9lainEvVUop5K1FOJeipRTyXqqUQ9lainEvVUok1Qj7zKUUOo\n",
1336 1334 "JdQR6gkXjqzhmCI8jhRmIDQSmggf41jCa/5djlHCGKU0jzyLU1fCBOFLhD8beYvjGcKzQEa/4rUF\n",
1337 1335 "NhEmkQOvLb+f1/MtoZrXc4CjhlBLqCPUEy4cWcoxRXgcKbyeQCOhifAxjrVCiLeDWiFKGBMsHJtH\n",
1338 1336 "fscxTpgg/JlQwPEM4Vkgo/tZjLCJMInf8hry+9kj/J4Qr+GLHDWEWkIdoZ5w0sjXObYSJgnbCTsJ\n",
1339 1337 "JxNOI+wl7CO8jXAh4XLCuwjvJryH8F7CdfwdhYT1I3/k+DSlPEP4LOE2wm8T7iZMER7i0g4J/0bX\n",
1340 1338 "rxAeITxOdT5B354kfIPwFOFpwiG682eEZwjPArnk+W+55IEmQnpG1kVIT8q6CXsIpxBOJbyZcAbh\n",
1341 1339 "TMJZhLMJF+Dp2BK6Xkq4jHA56sPuIrybkCTDSDLszwnvI3yAvn2QcCXhKsLVhA8RPkx3PkK4hkp8\n",
1342 1340 "jD9FlHpKlHpKlHpKlHpKlHpKlL9fYCthkrCdsJNwMuE0wtt4O4xSz4ryd4qUuwjvJryH8F7CdYTw\n",
1343 1341 "t47yd4rrZwifJdxG+G3C3YQpyvMQ7y9R/h5RynFKP0EpJwnfIDxFeJoQo0eURo8ojR5R6uNR6uNR\n",
1344 1342 "6uNRRk/B3yCQnoW/KeAMwpmEswhnEy5AnfmbwvVSwmWEy1Eif1PAuwkfIHyQcCXhKsLVhA8RPkK1\n",
1345 1343 "WkN5YrSJ8Xfxa44aQi2hjlBPOIn/KsbfBTBJ2E7YSTiZcBrhbXT/Qi6rGH8XuL6L8G7CewjvJVzP\n",
1346 1344 "+3iMvwVcP0P4LOE2wm8T7iZMUW6H6PoI4XHCE4QnCd8gPEV4GshlDjQSmgiptlzmQKozlznSZxDO\n",
1347 1345 "JJxFOJtwAWrIZY7rpYTLCOm5GD0Xo+fiMgc+SLiScBXhasKHCNdQbo/x62Yae5tp7G2msbeZxt5m\n",
1348 1346 "GnububS/y7GVMEnYTthJOJlwGuFthAtHHue4nK7vIryb8B7CewnX8d7XTKNZM5c5Up4hfJZwG+G3\n",
1349 1347 "CXcT/gPVJDXyDMe9lHKE8DilnyA8SfgG4SnC04Rv8RbVTPNFM80XzTRfNDOqP5c/kJ6Cyx84g3Am\n",
1350 1348 "4SzC2YQYnZq5/HG9lHAZ4QOU24OEKwlXEa4mfIhwDf0WM1ScpB0nacdJ2nGSdpykHSdpx0nacZJ2\n",
1351 1349 "nKQdJ2nHSdpxknacpB0nacdJ2nGSdpykHSdpx0nacZJ2nKQdJ2nHSdpxknacpB0nacdJ2nGSdpyk\n",
1352 1350 "HSdpx0nacZJ2nKQdJ2nHSdpxknacpB0nacdJ2nGSdpykHSdpx0nacZJ2nKQdJ2nHSdpxknacpB0n\n",
1353 1351 "acdJ2nGSdpykHSdpx0nacZJ2nKQdJ2nHSdpxknacpJ0gaSdI2gmSdoKknSBpJ0jaCZJ2gqSdIGkn\n",
1354 1352 "SNoJknaCpJ0gaSdI2gmSdoKknSBpJ0jaCZJ2gqSdIGknSNoJknaCpJ0gaSdI2gmSdoKknSBpJ0ja\n",
1355 1353 "CZJ2gqSdIGknSNoJknaCpJ0gaSdI2gmSdoKknSBpJ0jaCZJ2gqSdIGknSNoJknaCpJ0gaSdI2gmS\n",
1356 1354 "doKknSBpJ0jaCZJ2gqSdIGknSNoJknaCpJ0gaa8UMNqsFF4S8oUXhRdHhvjVS4RYgbxEK5CXhB/y\n",
1357 1355 "e16iGfwlmsFfohn8JZrBX2L307crCP+C4yG+XgL2ES7ktTqE/QyOywnvIryb8B7Cewm/SIgZ9pDw\n",
1358 1356 "TV6fQ8IWwqcIn6ZvnyF8lnAb4bcJdxOmqKy9uObrGWAP4RTCqYTTCG8mnEE4k3AW4WzCWwjnEM4l\n",
1359 1357 "vJXw86gJu53wDsI7CZfQt0sJMcIfJ63hOGkNx0lrOE5aw3HSGo6T1nCctIbjpDUcJ63hOM37x2ne\n",
1360 1358 "P07z/nHSGo6T1nCctIbjpDUcJ63hOGkNx2kufotmzLdophvi169yTHH8Gcn/ZySZM3R9hq7P0vVZ\n",
1361 1359 "XDMDasuR15Yjry1HXluOccIEIa8tR15bjkOEPyM8Q3gWiNpyjBE2ESaRG2rL8WG6h9eWGalEI5Vo\n",
1362 1360 "pBKNVKKRSjRSiUYq0UglGqlEI5VopBKNVKKRSjRSiUYq0UglGqlEI5VopBJNVKKJSjRRiSYq0UQl\n",
1363 1361 "mqhEE5VoohJNVKKJSjRRiSYq0UQlmqhEE5VoohJNVKKJSjRRiZXQvzhGCbn+xZHrXxzjhAnClwi5\n",
1364 1362 "/sXxDOFZIKNfQf/i2ESYRA7Qvzhy/Yv5KX8/5e+n/P2Uv5/y91P+fsrfT/n7KX8/5e+n/P2Uv5/y\n",
1365 1363 "91P+fsrfT/kHKP8A5R+g/AOUf4DyD1D+Aco/QPkHKP8A5R+g/AOUf4DyD1D+Aco/QPkHKf8g5R+k\n",
1366 1364 "/IOUf5DyD1L+Qco/SPkHKf8g5R+k/IOUf5DyD1L+Qco/SPmHoPNxjBJyvZIj1ys5xgkThFyv5HiG\n",
1367 1365 "8CyQ0f3QKzk2ESbxW+iVHLleycKUc5hyDlPOYco5TDmHKecw5RymnMOUc5hyDlPOYco5TDmHKecw\n",
1368 1366 "5VxPOddTzvWUcz3lXE8511PO9ZRzPeVcTznXU871lHM95VxPOddTzvWUM3SlFxl0JaCWUEeoJ+S6\n",
1369 1367 "MIOuBEwSthN2Ek4mnEbYS9hHeBvhQsLlhHcR3k14D+G9hFwX5shnWAa9CSnPED5LuI3w24S7CVOE\n",
1370 1368 "XBfm+G90/cr/7e1MgOwo7jPerZV2Vxe3AWMsP+MDDEKWhGBmxGGt7gvd4pAlpKe3o92Zefve8o6V\n",
1371 1369 "VoBlrxHIB5CkcscCh5CkApWEHChEoOC4HBIUJakk5iocQ27HSZzEOSqpOFH+329mtU+ywJWqVLx+\n",
1372 1370 "3+s309PT/f96ju7+PgG+CB6nzi+z9xXwVfA18HXwa+T8Ovgm+JZQY2GvkZRwJkgbNRb2GkkJV4Ar\n",
1373 1371 "wVXgavBWcB24HtwAbgS3qXUaC3uNsISDYKL6aCzsNcISEhlPZPQkNayDLfa2wRFwL7gPHAX3k/Me\n",
1374 1372 "8ABntLGwD+A3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8A\n",
1375 1373 "fgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4\n",
1376 1374 "DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3gN8AfgP4DeA3\n",
1377 1375 "gN8AfgP4DeA3gN8AfgP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8Q\n",
1378 1376 "fkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4\n",
1379 1377 "DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4DeE3\n",
1380 1378 "hN8QfkP4DeE3hN8QfkP4DeE3hN8QfkP4jeA3gt8IfiP4jeA3gt8IfiP4jeA3gt8IfiP4jeA3gt8I\n",
1381 1379 "fiP4jeA3gt8IfiP4jeA3gl9G9z6C3wh+I/iN4DeC3wh+I/iN4DeC3wh+I/iN4DeC3wh+I/iN4DeC\n",
1382 1380 "3wh+I/iN4DeC3wh+I/iN4DeC3wh+I/iN4DeC3wh+I/iN4DeC3wh+I/iN4JfZAB/BbwS/EfxG8BvB\n",
1383 1381 "bwS/EfxG8BvBbwS/EfxG8BvBbwS/EfxG8BvBbwS/zCH4CH4XaX7McArYDfaAveAt9saySPNjhovA\n",
1384 1382 "xeBScDm4BtxOfnvbN0xIp2AGVsEh8JA99xdp3GR4GHwUfAx8HHwSfJrSvkT6RfA4+DL4Cvgq+Br4\n",
1385 1383 "ulDzY4YzwJkgtdX8mCF11jjLcB24HtwAbgS3qYYaPRkOgIMg7fK0y9MuzY8ZtsERcC+4DxwFD1Da\n",
1386 1384 "mKX7NIdgOAXsBnvAXvAWY6pPcwiGi8DF4FJwObgG3A7uOHnQMCGdghlYBYfAB43xPq6gPs0hGB4G\n",
1387 1385 "HwUfAx8HnwSfoiZPnzxs+AxbXgSPs/1l8BXwVfA18HXwDXvX7dMcguEMcCZI/TWHYEgrNIdguA5c\n",
1388 1386 "D24AN4K6Ivo0h2A4AA6CLUprgyPgXnAfOAoe4NgxS69xa12/YQscccsND5I+5LYZPgQ+zJZHwCPg\n",
1389 1387 "s26+4VF3k+Fz4PPkPAa+AJ5wG/waf5vyW2+xLX4H6bvAneAusAwOk/9usAEesKN2Wg03GLbsLDut\n",
1390 1388 "hlcZHmTLIfAh8GHwEfAIOZ91FxketXrutBoKn2f7MfAF8ISb5XdaDe0oq6FwB3gXuBPcBZbBYfLf\n",
1391 1389 "DTbAA7Y90ZyJ4R2gjc0Nd5FOwBTMwCo4BN4HPmj9IdGcieGPgj8OfoG9h8FHwcfAx8Enwac51zNK\n",
1392 1390 "a87EcCW4ClwNrgHXgreC68D14AZwI7gJ3AxuAbeCu1UfzZwY9oMxuIe9A6Cu/ZQ4pMQhJQ4pcUiJ\n",
1393 1391 "Q0ocUuKQEoeUOKTEISUOKXFIiUNKHFLikBKHlDikxCElDilxSIlDShxS4pASh5Q4pMQhJQ4pcUiJ\n",
1394 1392 "Q0ocUuKQEoeUOKTEISUOKXFIiUNKHFLikBKHlDikxCElDilxyIhDRhwy4pARh4w4ZMQhIw4ZcciI\n",
1395 1393 "Q0YcMuKQEYeMOGTEISMOGXHIiENGHDLikBGHjDhkxCEjDhlxyIhDRhwy4pARh4w4ZMQhIw4ZcciI\n",
1396 1394 "Q0YcMuKQEYeMOGTEISMOGXHIiENGHDLikBGHYbtyZxm2wIPgIfAh8GHwEfCI0K5E4TZwB3gXuBPc\n",
1397 1395 "BZbBA4b7NVdm+LThPcT5HiIwxnzRGPNFY8wXjTFfNMZ80RjzRWPMF40xXzTGfNEY80VjzBeNMV80\n",
1398 1396 "xnzRGPNFY8wXjTFfNMZ80RjzRWPM4Dl3ib8s/68qGU7PNSVos3rsV57uciV3QZGe3JFniuWZX6S7\n",
1399 1397 "bXtUpKUnXFqkLyB/l/OTp9r3Z/XfWCHt3cXGRp6e5M7x+4p0l1vkHyjSkzvyTLE8Lxbpbtv+1SLd\n",
1400 1398 "46/y3yzSve6yrguK9FQ3q2t2kZ7W/Uddq4v0dDdn2uVFeoZbPW18+3luxrQfLNLnu95pX+wbiWtJ\n",
1401 1399 "o7S4Xs82xQPtarnRseXGUjhnbv918bwbS/Pnzpt/7dwF9v9iU57tWmUrjkiapXKp1Sj3x0PlRlaq\n",
1402 1400 "7ymtjJP+uLo7bgzEjdLSRruSDZWblcGkFtdKfStml+J9lWq7mYzE1dFSNanEtWbcX2oNNurtgcHS\n",
1403 1401 "2qRWb40Ox6UVQ7tXzi6Va/2lofJoaXdcasQDSbMVNyxzUitV4karbN9pu5E0+5NKK6nXmnPcEld3\n",
1404 1402 "w27UNVziBtyg9fGfM37nu7lOs0ol6/mJq1meluUZdrFtWeGG3G630s229F7+5rjqGbnmuIr9GrLv\n",
1405 1403 "krM3FPsrdZyhya/YvmP7HjHst5x9pGqWq2H7F9vxdZe5TbZtwLWthLJtP3ueGy0dWglzrZzrbP88\n",
1406 1404 "tqgN8wyvte8FBZ6eq7O0a0+Vdvo5Empbtk/Lftvz3fYNUZfMttXdHsOVti1hT9UiozYNgCXr9w2r\n",
1407 1405 "e8Xy6pimpQaJlMpXZFYQxdjtsz1Vy9m0vSOUM2rbFdUKeZvESHUYtBLrllOR/F7slG2fjtK5Vd5u\n",
1408 1406 "cjSIqNrVopZ5yQk1qrClZfnz36mdqUHefurSMqxTnznvcO4+y62jypSxnBi0YD+Gw3faWyKOTX7X\n",
1409 1407 "irqdyYiOm2f3l9D+8nbuKdpSsrrEsNM8xc6g/R7hqIEiJnkZ461XHMZLbbK/SSqmlnuIet7CPba3\n",
1410 1408 "whHq16s44szydKbYrgldGwl8fTdLs6lVXJwvoY357z1w3zpVbt3iWSUW5VOxV33qZ8Qp76HVom+V\n",
1411 1409 "icREW5LiqPwc4/046Sgxj9Qy27O7OHq87yyHnTbHzKYPtalfXoeynbNJSn0so/w2sRsvc/ys6uPD\n",
1412 1410 "RUzFZYWt42dpEptq0SvV0/L25deC7lBDHNXq4HWiPXuLfSo5j3il2KJ6j8LWliL3Xju6cZZeNUTc\n",
1413 1411 "8nhdaeWPtzq2X+MRXM7vGlfxRN0HC/abRZ3KRXzGa3d631Ht98JcicgNdcQqKUqZ6E3DnLF1FvY7\n",
1414 1412 "eZnDvTDnpW15FMecizPZO9u9Le+ZJTtX3t783qOrMa9dC84q3FMTcg5yLytRVqPgq8w9vknuOmc/\n",
1415 1413 "PR5lys63JNwR8+s1z9HZPwdhKHH7aW+r6GPj97OSu8K2X3Fa2ae3o0xbVLqupgrbKrRY99j4tDtj\n",
1416 1414 "szhbi6jkd5v8Ph2TI+ZOMtF/8p5ds0iViz6cPx2Sjntotbi/7rZPlYiNdpxxoLjDn8lFuYhrw2Je\n",
1417 1415 "Z2udK6mzrvmTIOGekF89w7S0DL/j19QeWqQrtV5cDS2uvtZppQ1yXP+pe0bnPS1/+i+gju98rx4v\n",
1418 1416 "7czeXuL+0ijin9cn7+Nv/9TQ2TKOUizWcO/Tc6sMS4nLn1z59Zt1PA/PFsu8VhWOKNP+t8+9rojO\n",
1419 1417 "ROTG823graNFjdtWyxJvS1WiP/EsnMM7Tctas9Bpve97vRP973J/lFrpOjj9uah+2dmO8beXQp/v\n",
1420 1418 "3Mln3Hx3lv/5++0zyXJ1ud+2CP+8necX3Ifch638K91VVt7vuOPud93fuI+4q901drWccL/nft/9\n",
1421 1419 "gb0hzbHa/BlvVXusbP0jwn9otflj9/3uF+2NaoG73t3g/sLeGv/enstfdS9bS1+xp/RCu3Pc5P7W\n",
1422 1420 "Pedudn9lb0S6Nz1k7f2ivV1Mtdr3WYxnug9YW5e5j7md7i63y93iFrk33Dfcg9bn/tTa9XV3v40V\n",
1423 1421 "3uvOdZ9zx6zHjLkvu0/buOtZqavdq9Z7honI3e497in3K+6Xjatv2ojhp6xHf8ld6n7T/Yy7xD3v\n",
1424 1422 "VrvPGIfvt3fcJ91vuBesl71p7/prLbojxkLb3Wq9Yb17n3vC/bnb4LvcP7gfcf/oNrrLrYd027vo\n",
1425 1423 "qLvH3et+yb1ld6eL3L+4f3X/5A67R91PuvvcZhsTTrdRRK873092L7pzbOy7xe5Uj9to5bfcr7pn\n",
1426 1424 "bGz4a+4rbprbaiOfP3G3ub90D7hZ7jL3bhsXvWTjytvdt9zF7t/cP7vX3efdu9y33R3uE+6T7lPu\n",
1427 1425 "gPF7p9vmPu62u79zR90Pux3ur92Fforv9j2+10/10/x0P8PP9Of4c/15/nx/gb/QX+Tf5S92j/lL\n",
1428 1426 "/KXuJ/y7bWT3AzYS/4L7afdjNhb/df8eG7P+rI3Ofshf7t/rZ/n3+ZL7L/9+d9Jf4T/gP+g/5D/s\n",
1429 1427 "r7QR1Uf81e7f/TV+tr/Wz/Ef9XP9PD/fX+cX+Ov9DT7woY/8Qn+jv8nf7G/xH/OLfJ9f7Je4//RL\n",
1430 1428 "/TK/3K/wK/0qv9qv8Wv9rX6dX+83+I1+k9/st/it7r/9bd752/0d/k6/zX/cb/c7/F02Uv4Pv8uX\n",
1431 1429 "/W5f8f0+9nv8gB/0iU9tTF71Q77m6zZ6vts3fNO3fNuP+L1+nx913/H7/T3+Xn+f/4Q/4D/pP2Uj\n",
1432 1430 "20/7+/1B/4B/0B/yn/Gf9Z/zn3cfdN83ZU6tXa12D5UrjXrtnOG4kdT7bXDFiGnysnajPnWgUR6J\n",
1433 1431 "51TKw1PLlXaL1DmVpFFpD+2pxvvYUSnbwR2pcrU1tZVU+8k8oz+xwppJUz+m5SdSsqddS+bOXxJN\n",
1434 1432 "3d2I8xP0NpLagBLnDbZrA+VGe6habre0YWZ/vVWuqF76Nb1SHxoq57/P7UjrvFOWxtVWmbKvixbk\n",
1435 1433 "331R/r14ydTyniS5Yd78MJoaN1vJULkV92vf8nD5cn3Pnz/v+uI76unL69rdRwV7+uoD9VqcTV8y\n",
1436 1434 "0fhpS07Vq3spTbevRr3c6l7Gr55lRRHLKGLaslPZe5YVpa3oKG3Fqd0zVnQ0a/rKiTyTV+4uN7pX\n",
1437 1435 "EdyeVXnp01ZNFLuqKHb1xCEz1nSU1b0WErvXUr8Zazt2TV5rxXSvy/evy/ev69jfs75ozHoaM3N9\n",
1438 1436 "J0ndm/LjNuXHbeo85WZ2Td/cUaXNnfu35Mds6TwXfWNe3+Qtau7WvLlbi/Nv5fxTtqq3zNzaWYue\n",
1439 1437 "rUXzb5841/Q7J9Ld26jKtG0TASsXhZZzkstFAZUOWioTJPfnJPfnJMc5yXFRRJyTHE8UHhelDXSU\n",
1440 1438 "NjBB8kAnyYMdJA+q1Une6iQvvSfJy+q1w6txs5lOTzvimXXGs5pTUc3DWu2kuCqKa/n+Wr6/1lmJ\n",
1441 1439 "Wnm43mw16sODcU+9aFY9p7t+Gt0NypjR6DxvIw9OM6e72VG9Zme2Vn7e1nfTvXhySw1v5w1vF+dv\n",
1442 1440 "53S3obt9Gt3tIr57O+ge7aB7f073/lMhn7Rq9aQk5XRz+5YW33NPzd65cQeZPZ9m2VPfL12+dos9\n",
1443 1441 "y/gXrk+eZI/P4kbNtuX5vO2bxHevfWrknN97YvzPvdK10Pd23dt11C+c/J0p39bfpKWT1k3aMmms\n",
1444 1442 "90TX3N5ne7/MH7m7FhZ/9/J3NP/Tcd3f6Lmt5yv66x3hmBP61/jsbFPsidxj577Q3guuxsFzg717\n",
1445 1443 "5O8bI/asP2ZP/uP2FvE1e3t4071VPB/Hn2n5U2z86aUn1hq/s3i+DPMMGbNnubwdcnbI1yFXhzwd\n",
1446 1444 "cmjIUXH85EtyRMgPITcEDoQZ6IalGpZmWIph6YXlQpIHSatqWi/TapnWyqR9XXzWc8g1Is+IHCPy\n",
1447 1445 "i8hnIX+FnCLbKfEAHhE5ROQPkTtE3hDpVuULkStEnhA5QuQHkRtEXhDVWz4QuUDkAZEDRP4PuT/k\n",
1448 1446 "/ZDzQ/pPqT9RU3a0z+ogp4d8HnJ5yOMhh4f8HXJ3yNshZ4d8HXJ1yNMhR4f8HHJzyMshJ4d8HHJx\n",
1449 1447 "yMOBRvGAHafYTUIFfIy5ZWmAp6MBlgJY+t8TllMr4loP12q41sKHbdvd9pHe9xr0vlL7SlMqpa90\n",
1450 1448 "vlL5SuMrha/0vVL3KkZS9krXK1WvNL1S9ErPKzWvtLxS8kqhqhUIrT9o9UFrD1p50LqD1hu02qC1\n",
1451 1449 "Bq00aJ1BqwxaY9AKg9YXtLqgtQWtLGhdQasKWlOQNncSSlnpZM/DISenm9xx8sbJGSdfnFxx8sTJ\n",
1452 1450 "ESe9odSG0hpKaSidoVSG8sCdi3dNTjT51uRak2dNjjX51eRWewvtXTfaPCnzpMuTKk/+tA340+RO\n",
1453 1451 "kzdNzjT50uRKkydNjjT50eTUkkNLTjT50ORCkwdNDjT5z+Q+k/dM6mxps+U6k5JdjjP5zeQ2k9dM\n",
1454 1452 "TjP5zOQyk8dsnF25y+Qtk7NMvjK5yuQpk6NMfjK5yeQlkzpD2gwpM6TLkCpDmgwpMqTHkBpDWgwp\n",
1455 1453 "MaTDkApDGgwpMKS/0FqztBdSXkh3odV0raVrJf3M3iWdhVQW0lhIYSF9hdQV0lZIWaG1ZznAbsZ1\n",
1456 1454 "JM+RHEfyG8ltJK+RnEbyGcllJNeO3DryF22nlx45aw+Vn+jte+QR/ENyD8k7JOeQfENyDckz9DpX\n",
1457 1455 "7LdQQ0gLISWEdBBSQZytx0r50NErUTywpmcfaR2kdJDOQSoHaRxGuVovxf1z81nvdPJFyBUhT4Qc\n",
1458 1456 "EfITyEcgL4ScEPJByAUhD4QcEPI/yP0g70PeX57A9SDPgxwP8jvI7fAUV8thfA5yOcjjIIeD/A1y\n",
1459 1457 "N8jbIGfDG9yTJ+6w0iJIiSAdglQI0iBIgSD9gdQHeX94At2BVAfSHEhxIL2B1AbSGkhpkHN9xEaX\n",
1460 1458 "a22crLH4iI1MD9r3IRuVPWSfhy39iH2O2OdZez4dtTHvc/Z53vYds88L9pGOQCoCaQikIJB+QOoB\n",
1461 1459 "aQekHJBuQKoBaQYOWH6dbYP0AlILSCsgpYB0AlIJSCMghYD0AVIHSBsgZYB0AVIFoAmQIkB6AKkB\n",
1462 1460 "0ALYRzoAqQCkAZACQOv/B6ys2f+vd9C1/wd3UY3cZ2lVVmuyWpHVeqxWY7UWy0qs1mG1Cqs1WK3A\n",
1463 1461 "av1Vq69l2jyL+/BLeBQm0WrV+EJ8E3JNyDMhx4T8EnJLyCshp4R8Ep1PSa2uam1VK6taV9WqqtZU\n",
1464 1462 "8xXV/J1Kf+5/AIGHtqIKZW5kc3RyZWFtCmVuZG9iagoyMCAwIG9iago3MDc0OQplbmRvYmoKMTkg\n",
1465 1463 "MCBvYmoKMTI3MzQwCmVuZG9iagoxNSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVu\n",
1466 1464 "Z3RoIDY3ID4+CnN0cmVhbQp4nO3NMQ0AIQAEwVNMTYKOV4AZKhosIOQxQUNmuq02uWynZ2WmpWac\n",
1467 1465 "LreHAAAAAAAAAAAAAAAAAAAAAPCY7weB+gXnCmVuZHN0cmVhbQplbmRvYmoKMTggMCBvYmoKPDwg\n",
1468 1466 "L0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyNjEgPj4Kc3RyZWFtCnicXVE9b8MgEN35FTem\n",
1469 1467 "Q0Rst5UHC6lKFw9Jq7qdogw2HBZSDQjjwf++fCRu1ZPg6T7ece+gx/a11coDfXeGd+hBKi0czmZx\n",
1470 1468 "HGHAUWlSlCAU9zcv3XzqLaGB3K2zx6nV0pCmAfoRkrN3K+xehBnwgQAAfXMCndIj7L6OXQ51i7Xf\n",
1471 1469 "OKH2cCCMgUAZ2p16e+4nBJrI+1aEvPLrPtB+Kz5Xi1Amv8gjcSNwtj1H1+sRSXMIxqCRwRhBLf7l\n",
1472 1470 "q8wa5FZexfIAzwwuf9wiQ5mhyvCY4enOuKYGdXbrW4M6hsuyiNQMl4zXOM/95Tha3OOmmy/OBclp\n",
1473 1471 "2UlrVKk0bv9hjY2seH4AHtCFLgplbmRzdHJlYW0KZW5kb2JqCjEzIDAgb2JqCjw8IC9DSURUb0dJ\n",
1474 1472 "RE1hcCAxNSAwIFIgL0ZvbnREZXNjcmlwdG9yIDEyIDAgUiAvQmFzZUZvbnQgL0F2ZW5pci1Cb29r\n",
1475 1473 "Ci9DSURTeXN0ZW1JbmZvIDw8IC9PcmRlcmluZyAoSWRlbnRpdHkpIC9TdXBwbGVtZW50IDAgL1Jl\n",
1476 1474 "Z2lzdHJ5IChBZG9iZSkgPj4KL1N1YnR5cGUgL0NJREZvbnRUeXBlMiAvVyAxNyAwIFIgL1R5cGUg\n",
1477 1475 "L0ZvbnQgPj4KZW5kb2JqCjE0IDAgb2JqCjw8IC9FbmNvZGluZyAvSWRlbnRpdHktSCAvQmFzZUZv\n",
1478 1476 "bnQgL0F2ZW5pci1Cb29rCi9EZXNjZW5kYW50Rm9udHMgWyAxMyAwIFIgXSAvU3VidHlwZSAvVHlw\n",
1479 1477 "ZTAgL1RvVW5pY29kZSAxOCAwIFIgL1R5cGUgL0ZvbnQKPj4KZW5kb2JqCjEyIDAgb2JqCjw8IC9E\n",
1480 1478 "ZXNjZW50IC0zNjYgL0ZvbnRCQm94IFsgLTE2NyAtMjg4IDEwMDAgOTQwIF0gL1N0ZW1WIDAgL0Zs\n",
1481 1479 "YWdzIDMyCi9YSGVpZ2h0IDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9Gb250RmlsZTIgMTYgMCBS\n",
1482 1480 "IC9Gb250TmFtZSAvQXZlbmlyLUJvb2sKL01heFdpZHRoIDY4MiAvQ2FwSGVpZ2h0IDAgL0l0YWxp\n",
1483 1481 "Y0FuZ2xlIDAgL0FzY2VudCAxMDAwID4+CmVuZG9iagoxNyAwIG9iagpbIDQ4ClsgNTY5LjMzMzMz\n",
1484 1482 "MzMzMzMgNTY5LjMzMzMzMzMzMzMgNTY5LjMzMzMzMzMzMzMgNTY5LjMzMzMzMzMzMzMKNTY5LjMz\n",
1485 1483 "MzMzMzMzMzMgNTY5LjMzMzMzMzMzMzMgNTY5LjMzMzMzMzMzMzMgXQo1NiBbIDU2OS4zMzMzMzMz\n",
1486 1484 "MzMzIF0gODcyMiBbIDY4MiBdIF0KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE0IDAgUiA+PgplbmRv\n",
1487 1485 "YmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMCA+PgovQTIg\n",
1488 1486 "PDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+\n",
1489 1487 "PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCA+PgplbmRvYmoKMiAwIG9i\n",
1490 1488 "ago8PCAvQ291bnQgMSAvS2lkcyBbIDEwIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMjEg\n",
1491 1489 "MCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDE0MDIyMDE3NTMyNS0wNycwMCcpCi9Qcm9kdWNl\n",
1492 1490 "ciAobWF0cGxvdGxpYiBwZGYgYmFja2VuZCkKL0NyZWF0b3IgKG1hdHBsb3RsaWIgMS4xLjEsIGh0\n",
1493 1491 "dHA6Ly9tYXRwbG90bGliLnNmLm5ldCkgPj4KZW5kb2JqCnhyZWYKMCAyMgowMDAwMDAwMDAwIDY1\n",
1494 1492 "NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDA3MzYzOCAwMDAwMCBuIAowMDAwMDczNDQ0\n",
1495 1493 "IDAwMDAwIG4gCjAwMDAwNzM0NzYgMDAwMDAgbiAKMDAwMDA3MzU3NSAwMDAwMCBuIAowMDAwMDcz\n",
1496 1494 "NTk2IDAwMDAwIG4gCjAwMDAwNzM2MTcgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAw\n",
1497 1495 "MDAwMzg4IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMTMzMyAwMDAwMCBuIAow\n",
1498 1496 "MDAwMDczMDYwIDAwMDAwIG4gCjAwMDAwNzI3MTIgMDAwMDAgbiAKMDAwMDA3MjkxOSAwMDAwMCBu\n",
1499 1497 "IAowMDAwMDcyMjM5IDAwMDAwIG4gCjAwMDAwMDEzNTMgMDAwMDAgbiAKMDAwMDA3MzI3NyAwMDAw\n",
1500 1498 "MCBuIAowMDAwMDcyMzc4IDAwMDAwIG4gCjAwMDAwNzIyMTYgMDAwMDAgbiAKMDAwMDA3MjE5NCAw\n",
1501 1499 "MDAwMCBuIAowMDAwMDczNjk4IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMjEgMCBSIC9Sb290\n",
1502 1500 "IDEgMCBSIC9TaXplIDIyID4+CnN0YXJ0eHJlZgo3Mzg0OQolJUVPRgo=\n"
1503 1501 ],
1504 1502 "image/png": [
1505 1503 "iVBORw0KGgoAAAANSUhEUgAAAJgAAABWCAYAAAAzIF/lAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\n",
1506 1504 "AAALEgAACxIB0t1+/AAACutJREFUeJzt3X1QE2ceB/BfNuHFsCExQDFECjqAhRa1DGOtAvbUuU4t\n",
1507 1505 "teDUcQbbcDiTq1d7nSLT8bBjcuBh5WxBGRDB8eWOl/MG5uwInt6LN+Xl5uxdC7aQVAJnLeYAiRJI\n",
1508 1506 "NrxE2L0/2vRiJkE27FJz/D4zzJjdfXYfnO/sbvbHs4+AYRhAiC/ED90B9P8NA4Z4hQFDvMKAIV5h\n",
1509 1507 "wBCvMGCIV6LZVtpsNllZWVl9fHx8S0ZGRrHRaEyoqqqqJghiRqlU3lSr1XsFAgFTU1NzrLe3dz3D\n",
1510 1508 "MIRKpdofGxv76UL9AujxNmvA6uvrjz777LOXJycnSQCAmpqaD/Py8l6TyWRDTU1NeW1tba8HBwcP\n",
1511 1509 "EwRBFxYWpo6Pj0uLi4svFRQUbFqY7qPH3awBU6vVe/V6/SaDwbDebrcv8ff3H5fJZEMAAGlpaTV1\n",
1512 1510 "dXXFUqn0bmpqai0AgFgsHouMjNSZTKaosLCwb1z3d+3aNXyq6+O2bNkiYLP9rAFzRlHUUpIkRxyf\n",
1513 1511 "JRLJPYqi5EKh8AFJkvddl7sLGABAUlISm/49pKmpCV555RVs/wO17+joYN1mzgEjSdJMUZTc8dli\n",
1514 1512 "sYSRJDlCkuSI1WoNlcvlAwAAVqs1VCKR3Pe8J3aGrFMwNf3tiS905dPwjXnyofViPwLCSH+uDoc4\n",
1515 1513 "NueA+fv7T9jt9iVms1mxdOnSwdbW1jcSExP/KpVKh9vb23dHRUV9abPZZEajMSE0NLSfqw623x6F\n",
1516 1514 "6k8HnJaMPbS+8McrMWCPsTkFTCAQMAAAKpUqr6SkpIEgiJnly5d/tX379g8BALq6urZqNJo2hmGI\n",
1517 1515 "7Ozsd/nsMPItjwxYQkJCS0JCQgsAgFKpvHn48OEU12127959gI/OId+3qB60xsXFYfsFtqgCtmrV\n",
1518 1516 "Kmy/wBZVwNDCm/O3SAe73R5YUVHxG4vFEkbTtHDHjh1FISEhRnclJD46jHwL64ANDw+vJEnSnJub\n",
1519 1517 "u+vu3bsrGxsbtRaLJcy1hJSWllbDR4eRb2F9iVy+fLnebrcvyc3N/Uqr1ba++uqrR11LSN3d3Zu5\n",
1520 1518 "7yryRazPYDdv3twYEBBgKy0tjTcajQnnzp0rCw8Pv+VYL5FI7js/8UeLG+szWE9Pz8b169c3AHx7\n",
1521 1519 "NgMAsFqtIY71Fosl1LlmiRY31gFTKpU39Xr9CwAAZrNZQRDEzIMHDwLNZrMCAMBRQuK4n8hHsb5E\n",
1522 1520 "JicnX+rq6tqq1WpbCIKgc3Jy3hEKhQ/clZAQYh0wAICcnJx3XJe5KyEhhA9aEa8wYIhXGDDEK6/u\n",
1523 1521 "wQAAPv/88/TBwcG49PT0Ek+jjbjsKPJNXp3BJicnSYPB8Hx6enoJwP9GGxUUFGxSKBSGtra217nt\n",
1524 1522 "JvJVXgXswoULv7p9+/bajz76qPHOnTtPY6kIecL6Ejk4OBhL07QwPz//5dHR0WUnTpz4nUKhMDjW\n",
1525 1523 "Y6kIOWN9Buvs7Hxp3bp1fwAAkMlkQxKJ5B6WipAnrAMmkUju63S6zQAAExMTktHR0WVYKkKesL5E\n",
1526 1524 "bty48cLp06crtVptKwBAVlZWvkQiuYelIuQO64ARBDHz5ptv/tR1OZaKkDv4oBXxCgOGeIUBQ7zy\n",
1527 1525 "ulRkMpmitFpt6/79+3cGBgZSWCpC7nh1BqNpmvj444/zU1JS6hmGEWCpCHniVcCam5vztm7dWuXn\n",
1528 1526 "5zfJMAyBpSLkCeuA9fX1rWMYRrBixYpOgG/PZi4vpsNSEfoe63uw7u7uzT09PRsMBsPzAwMDT3V0\n",
1529 1527 "dLzs/D4wLBUhZ6wDlpGRcdTx74aGBu3atWuvNjY2alxfTMdtN5Gv8vpbpDNPL6ZDaF4B27lzZ4Hj\n",
1530 1528 "31gqQu7gg1bEKwwY4hUGDPGK9T0YTdPEmTNnKoxG49M0TRO7du06JJPJ7mKpCLnDOmD9/f2rFQqF\n",
1531 1529 "Qa1W/2x8fFxaUlLSIBQKp/EFdMgd1pfI6OjoG+np6aUAAFNTU+KgoKDRgIAAG5aKkDte34NRFCWv\n",
1532 1530 "qqo6vW3btuNBQUFmx3IsFSFnXgVsbGzsifLy8t9mZ2fnrlixotNlDiMsFaHvsQ7YyMhIxMmTJ8/v\n",
1533 1531 "2bPnbYVC0es8hxEAjipCD2N9k9/c3JxnMpmiKisrzwEAkCQ5gqUi5AnrgKlUqjyVSpXnuhxLRcid\n",
1534 1532 "RfWgtaenB9svsEUVMIPB8OiNsD2nFlXA0MLj5O/BAABqamqO9fb2rmcYhlCpVPtjY2M/5WrfyHdx\n",
1535 1533 "cga7cePGiwRB0IWFhan5+fnbamtrf83FfpHv4+QMptPpfpSamloLACAWi8ciIyN1JpMpKiws7Bsu\n",
1536 1534 "9j+bQBEBXwxaPa5/IsgfFMEBfHfjsTBomYJhm93j+rDohZ8vkpOAURQlJ0nyvuOzRCK5R1GU3F3A\n",
1537 1535 "Ojo6WO17JQAcTfK8nh7qnbX94Hc/AABKpZL18Z35ent/YP//P1+cBIwkyRGr1Roql8sHAACsVmuo\n",
1538 1536 "RCK577rdli1bBFwcD/kOTu7BEhMTr7W3t+8GALDZbDKj0ZjgPJQNLV4ChuHm7wLr6uqKe3p6NjAM\n",
1539 1537 "Q2RnZ78bExPzL052jHwaZwFDyB180Ip4hQFDvOLsSf5c6PX6tOPHj//+2LFja6RS6TAAwOXLl9+9\n",
1540 1538 "fv36TpqmiczMzA+Sk5MvuWvrTaXAZrPJysrK6uPj41syMjKK2U55M98BLjMzM6JTp06dGRoaivH3\n",
1541 1539 "9x//bhpEAZs+zOc9bDk5OSPR0dFfAACsWbPmanJychPbwTnznjKIYZgF+TGZTJEVFRXnTpw4UWc2\n",
1542 1540 "m8MZhgGj0fhUaWnpBYZhYHp6WqTRaFqnpqYCXdt2dna+WFtbW8wwDNhsNqlGo2mZyzGrq6tPXbly\n",
1543 1541 "Zd/FixcPMAwDR44c+aPZbF7GMAxcunQpr6Wl5Y3Z2n/99ddrm5qach3HPXz48J/Z7MNms0l1Ot0m\n",
1544 1542 "x+9fVlZWw6b9zMwMUV1dfaquru4Dg8HwHNv+FxUVXXH+zLb9xMQEWV9fX+Rte4ZhFu4SGRoaeuet\n",
1545 1543 "t97KEYlEdkfqdTrdCykpKXUAAEKhcDopKam5r6/vOde2nioFjzqmWq3e++STT3YDANjt9iVs32M2\n",
1546 1544 "3wEuYrF4LCEhoQUAwGQyRUul0mE27ef7Hrb+/v5ErVbbWlhYeM1kMkWxbc/FlEGcXyIHBgZWnT9/\n",
1547 1545 "/rjzMqlUenffvn0/cd2Woih5VFTUl47PjgqAu+3mWinwhKKopd6+x8wxwCUzM/PIJ5988v3vMdd9\n",
1548 1546 "FBUV/WloaCimoKAgtaGh4Zdzae/8HrbPPvtsuzfvYSsvL18pEonsfX19606ePHmezZQ/XE0ZxHnA\n",
1549 1547 "IiIieg4ePPjSXLZ1VAAcny0WS9iyZcv6PG33qErBI45l9mZwytjY2BOVlZVns7Ozc0NCQozNzc37\n",
1550 1548 "2e7j/ffff3FgYCDu7Nmz5QKBgJ5Ley7ewyYSiewAADExMf8UiUR2NlP+cDVl0A/yLZJhGAEAwDPP\n",
1551 1549 "PPO39vb2LACA6elpv87Ozm3ubt65qBR4MzhlvgNcDAbD83q9Pg0AIDg4+N7U1JR4rtPuZGRkHD1w\n",
1552 1550 "4MD29957L3PDhg0X9uzZ83M2x+7t7X3u+vXrrwEA3Lp1K0kul/+HzZQ/XE0ZtKDfIh0c92ARERGG\n",
1553 1551 "uLi4fxw6dOjvNE0TO3bsKPLz85ty3X716tV/6erq2qrRaNoclQJvjsd2cMp8B7iEh4f/u7Ky8ux3\n",
1554 1552 "l0VBVlbWL8Ri8Zi3A2TYHFupVH518eLFg1evXn2bJMkRtVq9l6Io+VzbczVlED7JR7zCB62IVxgw\n",
1555 1553 "xCsMGOIVBgzxCgOGeIUBQ7z6LzWkj3n7AHKHAAAAAElFTkSuQmCC\n"
1556 1554 ],
1557 1555 "text/plain": [
1558 1556 "<matplotlib.figure.Figure at 0x10b0ecf10>"
1559 1557 ]
1560 1558 },
1561 1559 "metadata": {},
1562 1560 "output_type": "display_data"
1563 1561 }
1564 1562 ],
1565 1563 "source": [
1566 1564 "plt.hist(evs.real)"
1567 1565 ]
1568 1566 },
1569 1567 {
1570 1568 "cell_type": "markdown",
1571 1569 "metadata": {},
1572 1570 "source": [
1573 1571 "```python\n",
1574 1572 "def foo(bar=1):\n",
1575 1573 " \"\"\"docstring\"\"\"\n",
1576 1574 " raise Exception(\"message\")\n",
1577 1575 "```"
1578 1576 ]
1579 1577 }
1580 1578 ],
1581 1579 "metadata": {
1582 1580 "signature": "sha256:9fffd84e69e3d9b8aee7b4cde2099ca5d4158a45391698b191f94fabaf394b41"
1583 1581 },
1584 1582 "nbformat": 4,
1585 1583 "nbformat_minor": 0
1586 1584 } No newline at end of file
@@ -1,145 +1,306 b''
1 1 {
2 "metadata": {
3 "name": 0
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
2 "cells": [
8 3 {
9 "cells": [
10 {
11 "cell_type": "heading",
12 "level": 1,
13 "source": [
14 "nbconvert latex test"
15 ]
16 },
17 {
18 "cell_type": "markdown",
19 "metadata": {},
20 "source": [
21 "**Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus bibendum felis dictum sodales. Ut suscipit, orci ut interdum imperdiet, purus ligula mollis *justo*, non malesuada nisl augue eget lorem. Donec bibendum, erat sit amet porttitor aliquam, urna lorem ornare libero, in vehicula diam diam ut ante. Nam non urna rhoncus, accumsan elit sit amet, mollis tellus. Vestibulum nec tellus metus. Vestibulum tempor, ligula et vehicula rhoncus, sapien turpis faucibus lorem, id dapibus turpis mauris ac orci. Sed volutpat vestibulum venenatis."
22 ]
23 },
24 {
25 "cell_type": "heading",
26 "level": 2,
27 "metadata": {},
28 "source": [
29 "Printed Using Python"
30 ]
31 },
32 {
33 "cell_type": "code",
34 "collapsed": false,
35 "input": [
36 "print(\"hello\")"
37 ],
38 "language": "python",
39 "outputs": [
40 {
41 "output_type": "stream",
42 "stream": "stdout",
43 "text": [
44 "hello\n"
45 ]
46 }
47 ],
48 "prompt_number": 1
49 },
4 "cell_type": "markdown",
5 "metadata": {}
6 },
7 {
8 "cell_type": "markdown",
9 "metadata": {},
10 "source": [
11 "**Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus bibendum felis dictum sodales. Ut suscipit, orci ut interdum imperdiet, purus ligula mollis *justo*, non malesuada nisl augue eget lorem. Donec bibendum, erat sit amet porttitor aliquam, urna lorem ornare libero, in vehicula diam diam ut ante. Nam non urna rhoncus, accumsan elit sit amet, mollis tellus. Vestibulum nec tellus metus. Vestibulum tempor, ligula et vehicula rhoncus, sapien turpis faucibus lorem, id dapibus turpis mauris ac orci. Sed volutpat vestibulum venenatis."
12 ]
13 },
14 {
15 "cell_type": "heading",
16 "level": 2,
17 "metadata": {},
18 "source": [
19 "Printed Using Python"
20 ]
21 },
22 {
23 "cell_type": "code",
24 "execution_count": 1,
25 "metadata": {
26 "collapsed": false
27 },
28 "outputs": [
50 29 {
51 "cell_type": "heading",
52 "level": 1000,
53 "metadata": {},
54 "source": [
55 "Pyout"
30 "name": "stdout",
31 "output_type": "bad stream",
32 "text": [
33 "hello\n"
56 34 ]
57 },
35 }
36 ],
37 "source": [
38 "print(\"hello\")"
39 ]
40 },
41 {
42 "cell_type": "markdown",
43 "metadata": {},
44 "source": [
45 "## Pyout"
46 ]
47 },
48 {
49 "cell_type": "code",
50 "execution_count": 3,
51 "metadata": {
52 "collapsed": false
53 },
54 "outputs": [
58 55 {
59 "cell_type": "code",
60 "collapsed": false,
61 "input": [
62 "from IPython.display import HTML\n",
63 "HTML(\"\"\"\n",
64 "<script>\n",
65 "console.log(\"hello\");\n",
66 "</script>\n",
67 "<b>HTML</b>\n",
68 "\"\"\")"
69 ],
70 "language": "python",
56 "data": {
57 "text/html": [
58 "\n",
59 "<script>\n",
60 "console.log(\"hello\");\n",
61 "</script>\n",
62 "<b>HTML</b>\n"
63 ],
64 "text/plain": [
65 "<IPython.core.display.HTML at 0x1112757d0>"
66 ]
67 },
68 "execution_count": 3,
71 69 "metadata": {},
72 "outputs": [
73 {
74 "html": [
75 "\n",
76 "<script>\n",
77 "console.log(\"hello\");\n",
78 "</script>\n",
79 "<b>HTML</b>\n"
80 ],
81 "metadata": {},
82 "output_type": "pyout",
83 "prompt_number": 3,
84 "text": [
85 "<IPython.core.display.HTML at 0x1112757d0>"
86 ]
87 }
88 ],
89 "prompt_number": 3
90 },
70 "output_type": "execute_result"
71 }
72 ],
73 "source": [
74 "from IPython.display import HTML\n",
75 "HTML(\"\"\"\n",
76 "<script>\n",
77 "console.log(\"hello\");\n",
78 "</script>\n",
79 "<b>HTML</b>\n",
80 "\"\"\")"
81 ]
82 },
83 {
84 "cell_type": "code",
85 "execution_count": 7,
86 "metadata": {
87 "collapsed": false
88 },
89 "outputs": [
91 90 {
92 "cell_type": "code",
93 "collapsed": false,
94 "input": [
95 "%%javascript\n",
96 "console.log(\"hi\");"
97 ],
98 "language": "python",
91 "data": {
92 "application/javascript": [
93 "console.log(\"hi\");"
94 ],
95 "text/plain": [
96 "<IPython.core.display.Javascript at 0x1112b4b50>"
97 ]
98 },
99 99 "metadata": {},
100 "outputs": [
101 {
102 "javascript": [
103 "console.log(\"hi\");"
104 ],
105 "metadata": {},
106 "output_type": "display_data",
107 "text": [
108 "<IPython.core.display.Javascript at 0x1112b4b50>"
109 ]
110 }
111 ],
112 "prompt_number": 7
113 },
114 {
115 "cell_type": "heading",
116 "level": 3,
117 "metadata": {}
118 },
100 "output_type": "display_data"
101 }
102 ],
103 "source": [
104 "%%javascript\n",
105 "console.log(\"hi\");"
106 ]
107 },
108 {
109 "cell_type": "markdown",
110 "metadata": {},
111 "source": [
112 "### Image"
113 ]
114 },
115 {
116 "cell_type": "code",
117 "execution_count": 6,
118 "metadata": {
119 "collapsed": false
120 },
121 "outputs": [
119 122 {
120 "cell_type": "code",
121 "collapsed": false,
122 "input": [
123 "from IPython.display import Image\n",
124 "Image(\"http://ipython.org/_static/IPy_header.png\")"
125 ],
126 "language": "python",
123 "data": {
124 "image/png": [
125 "iVBORw0KGgoAAAANSUhEUgAAAggAAABDCAYAAAD5/P3lAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\n",
126 "AAAH3AAAB9wBYvxo6AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURB\n",
127 "VHic7Z15uBxF1bjfugkJhCWBsCSAJGACNg4QCI3RT1lEAVE+UEBNOmwCDcjHT1wQgU+WD3dFxA1o\n",
128 "CAikAZFFVlnCjizpsCUjHQjBIAkQlpCFJGS79fvjdGf69vTsc2fuza33eeaZmeqq6jM9vZw6dc4p\n",
129 "BUwC+tE+fqW1fqmRDpRSHjCggS40sBxYDCxKvL8KzNBaL21EPoPB0DPIWVY/4NlE0ffzYfhgu+Qx\n",
130 "GHoy/YFjaK+CcB3QkIIAHAWs3wRZsuhUSs0CXgQeBm7UWi/spn0Z+jA5yxpEfYruqnwYllRic5a1\n",
131 "MaWv8U5gaT4M19Sx396IAnZLfB/SLkEMhp5O/3YL0AvoAHaKXl8HLlZK3QZcpbWe0lbJDOsaHuDU\n",
132 "0e4u4JAy2wPk/C1JzrKWArOQ0fUtwH35MOysQxaDwbCO0NFuAXoh6wPjgQeUUvcqpUa0WyCDoQls\n",
133 "CIwBjgfuAV7KWdY+7RWpmJxlXZezrEdylvXxdstiMKzrGAtCYxwI/EspdZbW+g/tFsbQ67kQuBHY\n",
134 "FNgseh9FV6vCbUAeWBC9PgBeq2EfS6J2MQOBrRDTe5KdgAdzlvW1fBjeUUP/3UbOsoYBE6OvG7VT\n",
135 "FoOhL9Af+BUwFLkZpV+DaY6V4UPkRpb1+ncT+m8nGwK/V0oN01qf025hDL2XfBi+DLycLMtZVo6u\n",
136 "CsKfGnSq8/NheEpqHwOBEcDBwJnAsGhTP2ByzrJG5cPwnQb22Sy+0G4BDIa+RH+t9dmlNiqlFKIk\n",
137 "JJWGi+jq5JPmq8BbJJQArfXqpkncczlbKbVQa/3rdgtiMNRCPgxXAK8Ar+Qs63LgXmDvaPPGwPeA\n",
138 "H7VJvCRfbLcABkNfouwUg9ZaAwuj178BlFLvVejzgR4WFviM1npcuQpKqf6IyXIjxLS7GzAWuUnu\n",
139 "XsO+fqWUellr3ZBJdq/jr9+BDn1uve07O9Rz0y6f8PtGZGgWe53oT6SBkZ/q1/nHZy47aloTRTKU\n",
140 "IR+Gy3OWNR6Zxtg0Kv4KRkEwGPocxgcBiCwcsSI0F5iOhF+ilPok8C3gVGS+thK/VErdrbWuO2ys\n",
141 "s/+aLZTuOKbe9krrIUCPUBB0B+PQ1P1bdKe6EzAKQgvJh+GbOct6gkJkxM45y+qXDIWMHBhjBWJe\n",
142 "PgyDWvaRs6zPIVObAG/nw/DpEvUGAp8E9gGGJzbtl7Os7cvs4skqp0V0Yl8jgcOBjyMDhbmIZeWl\n",
143 "fBg+UUVfReQsayhwELAnsAXi6/E28BxwTz4MP6iyn92RaSCA+/NhuCwqXx9R4MYhU0MfRTK/AjyW\n",
144 "D8MFGd0ZDFVhFIQKaK3/BXxfKXUlklTq0xWafAI4Driyu2UzGLqRlygoCArYHJif2H4gcFb0+Z2c\n",
145 "ZW2bD8NV1XScs6yNgH8g/jsAPwCeTmzfFPgjYsnbiez71MUVdnMQcF8V4nyUs6whwB8QX4+0s2Ys\n",
146 "0yPAt/NhGFbRZ/wbzgO+DaxXotqqnGX9GbigCkXhf5CBCsDngYdzljURGQhsWqLN+znL+iFwdT4M\n",
147 "dYk6BkNJTJhjlWitQ2Bf4P4qqv848t8wGHor6Yd9+ruHJFkC2BI4rIa+D6egHKwmstYlGAxMQCwH\n",
148 "rRjEPI5ER5S7ZvcFXsxZ1phKneUsawSi8HyH0soB0bbvAM9Ebaplt5xlnYkct1LKAYiFZhJwSQ19\n",
149 "GwxrMRaEGtBar1RKfRX4JxIzXortou3PN1mE+YgJsSwaeoLHOQCqUy3QSr9eqZ6G/gq2aYVMhqrY\n",
150 "OfF5FeJwvJZ8GM7JWdY/gC9HRS7wtyr7Pjrx+e6MqYC3KLbU7Qhck/h+FJIKvRRVjfSREXicU8EH\n",
151 "pgAvIIqLBZwGfC7avl5Uf29KkLOsTZCMq8npj9sQx89no37HIlaAODplNPBIzrJ2z4dhNVlaT0HC\n",
152 "XwFmIkrAC4if2PaIz8/3KCgn385Z1pX5MJxeRd8Gw1qMglAjWutlSqnTgUcqVP0SzVYQtP5mcMXE\n",
153 "SvvtUUy9YsK5QEWHy7EnTB6lOtSsFohkqEDOsgYAdqJoagkT9Z8pKAj75yzr4/kwnF2h748ho/GY\n",
154 "q9J1oqiKLj4JOctKK8Yz8mH4Yrl9VcnHkXVYTsyHoZ8WJWdZNyPThbF5/3M5yzowH4alpi9+T0E5\n",
155 "WA18Nx+Gf0zVeRG4KmdZ90R9bwCMRKwyX69C5h2j91uA4/JhuCSxbTYwJWdZtwNPIFbifsAFSISZ\n",
156 "wVA1ZoqhDrTWjyIjjXIc3ApZDIZu4ELgY4nvt5Wody8wJ/qsgBOr6HsihfvOfCRrY7v5dYZyAECk\n",
157 "GP0ISEZmZYZ55yxrB8SyEXNxhnKQ7Pt64H8TRUfmLGuXKmWeC4xPKQfJvp9CLCJlZTYYymEUhPq5\n",
158 "tcL2XVsihcHQJHKWtU3Osi5GnAZj5iKWgiKitRouTxQdl7OscnPu0HV64dp8GLY7R8pyxEGxJPkw\n",
159 "fBcZ9ceUSvN8IoV76upK/UZcgawcG3NKqYopfleFU+gDic/b5SzLWIwNNWFOmPqp5CG9sVJqPa11\n",
160 "VZ7dBkOL2D1nWcmcBkOR8MFtgM/QdTXJZcCR+TBcXqa/SYj5egAFZ8VMX4ScZe2FRPnEXF2z9M3n\n",
161 "3nwYVsrtAmK6/0z0uVR4ZXLtivvzYfhGpU7zYbgkZ1k3ACdHRQdWIQsUO3ZmkUzB3Q/xjaolLbeh\n",
162 "j2MUhDrRWr+mlFpJ+eV5hyIxz4YWs98Fj/Rf8uZbozo0/ZYt7D8rf9ORK9stUw/hU9GrEnMAp1R+\n",
163 "gph8GL4bzdNPiIpOorSzYtJ68FS1IYPdTLWp3hcnPm+Q3pizrA7E+TCmFn+aZN0dcpY1LB+G5e4b\n",
164 "y6rM8bA49X39GmQyGMwUQ4NUGnkMrbDd0A3sdeLk4z6cN+89pTtDTWd+gyErF+7pTv5eu+XqJbyK\n",
165 "TDHsmg/DJ6tsc2ni8+dzljUqXSGaevhmoqjIObFNVBzlV8kQug4W5tbQNl13WGatAv+poW+DoW6M\n",
166 "BaExPgC2LrO9nHWhpSilDqI4NPMhrfXUJvS9M/DfqeJXtdY3N9p3rex50uQ9lFKT6BrTvoFCXbTX\n",
167 "yZNfmnrZxHtbLVMP4xng74nvK5DzeD7wfIWRayb5MHwiZ1kzgF0oOCuemar2ZQoK8zLgr7Xup5t4\n",
168 "s0n9DEl9b0RBSPeV5q0a+jYY6sYoCI1RacnZ91siRXUMAH6eKnsYicdulDOAY1NlpzWh35pRqG9R\n",
169 "IuGN7uw4AfG878s8nw/DX3RDv5dScGY8NmdZP86HYXJaJzm9cHMp7/s2UHdK9BTpKaxBNbRN163k\n",
170 "t9Rux05DH8FMMTTGZhW2v9sSKarjbopNk/sqpUY30qlSahCSGS/JCuD6RvqtF6UpMm/HaHTJbYaG\n",
171 "mQzED/0umRVzlrUZhXwJ0HOmF5pJOlXyxzJrZbNt6rtZP8HQIzAKQp0opTZAlsItxTKtdTnv75YS\n",
172 "LR7lpYqrjV0vx2EUH4fbtdZtucnpMqOrDjPy6jYii8DkRFHSYnAEhem22cBjrZKrVeTDcCldTf/p\n",
173 "h345ksrEGprnF2EwNIRREOrnMxW2z2uJFLVxJcXmy2OVUo34ShydUda+EaIq7T2u0SZTY/eSdFY8\n",
174 "MGdZm0efk86J6/LCQUnFp5pIkZjkcvQz8mH4YZPkMRgawigI9VNp7v7BlkhRA1rr+RQneNqC2hba\n",
175 "WYtSajiS9z3JXLomaGktq/VllLIUdKqSWe0MjZMPwxlIel8Q/6Zv5CxrGIX8AJ10XU+hFtIRQ+UW\n",
176 "KWoXyYyTu+Qsa79KDXKWNRpJyx5zZ9OlMhjqxCgIdaCU6g98o0K1npBCNotLM8rcOvuagCRgSXKN\n",
177 "1rozq3IrCCZNfFkrfRjotWsCaJinUBODK51/tkuuPkTy/DoYOIDCfeb+fBjW4t2/lqhdcmRdbUri\n",
178 "VnILXS2HZ1WRvfAcCk61K4A/dYdgBkM9GAWhPr5F6XSrIBf6Qy2SpSaidSReShV/XilV7veUIj29\n",
179 "oOkB2fGmXT7x7sCbOGpFf7VZx4A1m0/znG2nehMyc+0bms7NFJxzxwH7J7Y1OvWUPG9/mLOsLRvs\n",
180 "r6lEaaOT0TtfBB5ITLWsJWdZg3KWdRNwTKL4wnwYzu9mMQ2GqjFhjjWilBqBpJYtx51a66UV6rST\n",
181 "S+maJz52VvxRdvVilFK7UbzexGNa67Kr+bWS6X+ekPYs79HkLGt34JOI+Xyz6D2d1vfMnGUdini6\n",
182 "L0C851/Oh2HD+SyaQT4MV+YsaxJyLm1Gwf9gAXBHg93/JNHHtsArOcuajCztPBDYCkkytBXg5sOw\n",
183 "5QmF8mF4W86yLgK+HxXtC8zKWVaALMm8CslHsicS7RFzL8VhyAZDWzEKQg0opbYE7qd8prPVdF2h\n",
184 "rSdyLfALYMNE2XFKqR/XsHbEURll62L4Wiv5PuBUqPPF6JXkLuCQbpGoPi4HfohYKGMHWD9axrlu\n",
185 "8mF4Z7RuwfioaDBwaonqRemQW0U+DH+Qs6xFwHnIFNwQsv+3mMnA8dHiVwZDj8FMMVSJUuow4DkK\n",
186 "a7GX4gqt9cstEKlutNaL6boULMho5tBq2iul+lH8IFuCmJcNfZx8GM6hOCFVU5THfBhOQHxfylkH\n",
187 "3gY+asb+6iUfhhcCewC3l5BlFbJk/P75MDwqlVTKYOgRKK1rizhSSk2h67ximo1abV5XSi2n9EIk\n",
188 "z2itx5XYVqnfQcjI7DiqW2XtfeCTUbRA3ex50nWfUrqjeJEcrfcLrpj4SCN9xyilxgDPp4of0Fof\n",
189 "UEXbg4B/pIqv1FrXnVNh7AmTR3V0qIwwRH1E4E28pd5+De0hZ1m/Bb4bfX0+H4Z7dMM+hgGjkDwC\n",
190 "S5FpjFk9bR4/Z1mDkGmF4VHR20g4Y3oxJYOhR9EXphg6lFLlVjFbH0mZvDGwCTAayCFe0ntTOZ1y\n",
191 "zDLgkEaVg1ahtX5BKfUU8OlE8ReUUjtorSstCduzch8YehSR5/6ERFG3nBvRuhE9frXUfBguA6pd\n",
192 "+Mpg6DH0BQXBBro7o+Ea4Bta66e6eT/N5lK6KggKOAE4u1QDpdTGFOdNmNkLf7uh+zgYcRQEMa+3\n",
193 "Je22wWBoDOOD0DhLgYla67vaLUgd3ETxglLHRXkeSnEExQ5gbQ9tNPQokis5TsqHoVlbwGDohRgF\n",
194 "oTECYHet9Y3tFqQetNYrKDb/DqN46eYk6emF1UhUhMFAzrImUEhDvgr4VRvFMRgMDWAUhPpYAvwf\n",
195 "8Bmte31+/8uQBEdJMjMrKqW2o5A2N+YfWusePw9s6F5yltWRs6zxwKRE8RXtyEVgMBiaQ1/wQWgm\n",
196 "eWTe/jqtdU9Zz74htNavKaXuAw5KFB+glBqptZ6Tqj6RQlrYGDO90AfJWdY5wNeQFQwHIAmetk5U\n",
197 "eZFCsiCDwdALMQpCed5AphEC4NF12BHvUroqCAoJ7TwvVS+d++BdJEmPoe+xKRLnn0UeODwfhm3N\n",
198 "RWAwGBqjLygIbwN/LbNdI1MGH6ReL/eWkMUmcDeSeGa7RNlRSqnzdZQoQym1C7Bzqt11NWReNKxb\n",
199 "zEMU6GHAesBiYCaSLOviaF0Cg8HQi+kLCsLrWuvT2y1ET0ZrvUYp5SG57mO2Bz4LPB59/2ZRQ5P7\n",
200 "oM+SD8OLgYvbLYfBYOg+jJOiIeZKxOs8STJiIb28daC1/lf3imQwGAyGdmEUBAMA0XTKraniI5VS\n",
201 "A6O0zOnloI31wGAwGNZhjIJgSHJp6vtgJBNlehW65cANLZHIYDAYDG3BKAiGtWitHwVeShV/muLF\n",
202 "uW7VWi9qjVQGg8FgaAd9wUnRUBuXAn9IfN8f+FyqTo/OfbDnSX8brDpXnqEUe2ropzQvdtDx66ev\n",
203 "GN9XolIMPQDb9T8LrBd4zsPtlsXQe7Bd/0BgQeA5QbtlMQqCIc21wC+ADaPv6WWu5wAPtVKgWtjt\n",
204 "6Os2XG/9jhdQjIzTQ2rFF9bQecy4E2/I9UQlwXb9LYDDK1R7K/Cc21shj6FxbNcfDjwGKNv1Rwae\n",
205 "83q7ZWo2tusPBb6ELGW9BbAICX99Gngs8Jx0hlZDBWzXHwvcC6ywXX9o4DlL2ymPURAMXdBaL1ZK\n",
206 "+ZRItwz8Jc6N0BMZMFB9GxiZsWnzTjrPAH7QWomqYgTF/h9pngC6RUGwXf+XwC2B50ztjv57M7br\n",
207 "XwJMCjxneo1NP0SWgAfJq7LOYLv+esAFwOkUL9wWM912/d0Dz+lsnWQ9A9v1BwEXAT8PPKfWVOML\n",
208 "kPVt3kNWQm0rxgfBkEWph5UG/tJCOWqnQ40ttUkrvWcrRamWwHOmAZsguSfGAi9Hmy5AUhgPAz7f\n",
209 "Hfu2XX8k8ENgx+7ovzdju/4uwP9D/peaCDxnCbANsF3gOYubLVu7sF1/AHAHcBaiHDwI/C+ywNsE\n",
210 "4KfA68BdfVE5iNgbOBmxqtRE4Dn/BoYDnwg8Z02zBasVY0EwFKG1fkEp9RTioJjkIa11zzaVarYq\n",
211 "vVFt2TpBaiN6oCwB5tiu/2FUPCvwnLTTaLM5oJv77800dGwCz1kXHXkvRNKydwI/Cjzn1+kKtuuf\n",
212 "i2TX7Ks0et681yxBGsUoCIZSBBQrCL0h98EbdW7rddiuPwoYFJu/bdffFNgL2BZ4DZgWKR5ZbRWS\n",
213 "2+KIqGiE7fpjUtXmlrtZRdaHscBAYDowM/CckimWbdffFfgw8JzXou/9kfUccojV5MXAcz4s0XYw\n",
214 "sCsymu8PzAVmBJ7zVqn9pdoPRVKF7wSsAN4EgqzRve36HcAoZDEqgO0zjs3rged8kGo3gOJ05ADT\n",
215 "s0bTkan+k9HXGaVGjNFxykVf81nH2Hb9Ich/MRJJeT291H9fL7brj6CwANfPspQDgOi3rijRx/rI\n",
216 "b8kB7wPPBZ4zL6Ne/JvfCDzn/WhufhvgvsBzVkR1dgN2AR4JPGduom38P7wXeM7c6FzfCfgU4iMR\n",
217 "lFLebNfPIefXzMBzikz8tusPQyx676bljmTeCfhyVLST7frp//TV9Dluu/6GwOhUvTWB58zIkjFq\n",
218 "sykyNfmfwHMW2K7fLzoWeyDTFPnAc14t1T7qYwNgT+Rc/wi5ZyT/N20UBEMRSqn+wNdTxQspTqTU\n",
219 "41BaP6yVOipzGzzSYnG6m6uBz0YPv7OQm3dytc35tuuflHZutF3/BuArwEaJ4p/QNdU2wGnAH9M7\n",
220 "jRSTG5CbS5LQdv2joymTLKYBzwHjbNc/DomW2TCxfbXt+sMCz3k/sa8RwM+Qh/X6qf5W2q4/CTit\n",
221 "zMN1OPB7CopQktW2658YeM5fEvXvRKZzBiXqZaWUPha4JlW2NfB8Rt0hiANfmjWIuf5jiLPfvVm/\n",
222 "AfmvbgNmB54zKrkheuD+Bjg11Wap7fpnBJ5TybelFk4E+iE+Fb+ptbHt+scg//nGqfJbgeMDz1mY\n",
223 "KN4UOZYX2q7fSWHhuNdt198ZOBc4MypbbLv+5wPPeTb6PiJqe5ft+ichx3WXRN8rbdc/OfCcrGis\n",
224 "R4ChiHKSlSn2f4BzkOvitMRvCKJ9DEzU9TPafwGZlkkyBvExSrKUrtdnmoOBycA5tus/iCyat3li\n",
225 "u7Zd/0rk2ihS1mzXPwT4E3LulaLTKAiGLL6EaMlJbtBat91pphIjFw289t9DVh4N7Jva9EKnWnpJ\n",
226 "G0RqBXcjCa08YCqy/PJE4L8A33b9HQPPeTNR/0bgvujzGchoywPSq5U+nd6R7fp7IDfRjYDrEE99\n",
227 "DeyHrPb5lO364xI36zTb2q4/AUnt/SSyLHQHMvJZklQOIhYChyCLid2FWBoGIQrDfwGnAP8Gskzd\n",
228 "VvSbBgPvIMdpJjLHuxdikXgg1ewa4Jbo84+BHRAFI/3gT9/QQZa+/iIy9zwccVQrSeA5nbbrX4s8\n",
229 "cI6htIIQK7xdFJLIAvEEYjmYBlyP/E4LeXj92Xb94YHnnFtOjhrYJ3q/vtbpE9v1fwqcjYxUL0GO\n",
230 "51bI//g1YIzt+mNTSgJIivfNEIXgBOThfx0ySv8Nct7vgzgfj0+1HQf8E5iPKM/vI+vLHA9cZbs+\n",
231 "JZSEevgDBZ++3yIKzgVI1FeSrCnD6ci0zebAJxCfjmoZjxzXPPBL5By0gW8jCt3sqHwtkYL1N0RB\n",
232 "/R2ymOG2yHE5CLFAHAu8ahQEQxbfyijrDdML3HTTkWvUBRfsb88bPb6TzjEK+oHKL184YHL+Jmdl\n",
233 "u+XrJsYBhwaec0dcYLu+hzw0dkcu/AvjbUmLgu36DqIgPB54zuQq9nURMgI8LjnyBibZrj8z2s/l\n",
234 "tuvvVcJJbWvkXDoi8JzbKu0s8JxFtut/IqXgAPzOdv0/IiPnb5KhICAjpMGIEjAhPV1iu35HWsbA\n",
235 "c25ObD8ZURAeqibENBqpTYnark8FBSHiakRBOMx2/cHpB29kSv4KooSlLRYnIcrBHcBXk7/Fdv0b\n",
236 "gReAM23Xvz7wnJlVyFIJK3qfXUsj2/U/jiiiq4B9ktEytuv/Fhlpfx2xEnw31XxHYLfAc6bbrv8k\n",
237 "cny/Bnwz8Jy/2q6/DTLd9F8Zu94ceXAeEHhOvM7MNbbrT0UU4vNs15+c2FY3gedcm/hNP0EUhDvL\n",
238 "KMrJtkuIFPboWNWiIOSAO4HDE7/Dj67FSxEn21+m2pyOWDpuCDxn7fG2Xf8e4F1EIVsceE5oohgM\n",
239 "XVBKjURuSEke11qXMhv3OPR553VO9Sb407yJZwTexO8FnnNV/qYj11XlAOCfSeUA1s4D/y36mp7f\n",
240 "rAvb9fdGLDMzU8pBzMXIg2wsMhLKQiFhgxWVg5gM5SDm+uh9VHqD7fr7IlaNFcAJWb4UPcHLPvCc\n",
241 "2YgVZn3gyIwq30AsQg8lQ+aiefUfR1/PzlB08sD9Udusfmsi2t+Q6GutjspnIE6L16dDaSN/irMR\n",
242 "p8dTbddPOxK/nwgxTZr8747e30SsEkNL7PvXGQrAVYgvwggK/gK9mXMyfuON0fvWkY9Dkp2i97uT\n",
243 "hYHnLKNgURsDxknRUMz5FJ8XP22DHIbqSc9pxsSOW8ObtJ89ovdXbNcvpQC8j4zcdiTbnAoy4q2b\n",
244 "6Ia3CYV5/Y0zqsXOf4/WEYveaq5GQuOOQaZekhydqJNkW2BLZF2UzhL/R+xE2XAIa+A52nb9lUho\n",
245 "Y63hd7GD5d1ZGwPPmW27/iuIUrkLXc/n9xP13rZd/yNgVezoF8n1NjAyyyKETGGl97fGdv1/IlaL\n",
246 "3h7e+06WM2PgOQtt11+GTMcNo6vVJ1aWsyK+4nvFQjAKgiGBUmoshfnOmGe11vdl1Tf0GOaUKI9v\n",
247 "lqrE9lqJb6b/Hb3KsU2Zba/VslPb9bdDfA0ORLz0N62iWWxVqMkc3iZuRuawP2u7/g6JKI9RSCTR\n",
248 "YoodhOP/YgNKK2Ix2zZJzjnINMN2NbaL/4uiaIUE/0EUhB3pqiCkMwl2IscjXZZFJ/B2iW1xRtWR\n",
249 "ZWTqDcwps63U9f8Q0TSN7fp/iK0PtuvviPjmrCHyR1qrICilNkTmHjZDLsDke/JzOtwnzY1KqXcR\n",
250 "R4cFiBab9XlRT87I19dQSo1GNPz0tJOxHvR8mhrOVobB0XuAOBiWo1zmwaqdXW3X3x+4BzGVv4SM\n",
251 "pN9AnPEg21McxMIArTs2dRN4zoe26/8NOA6xGJwfbYqV9b8GnrM81Sz+Lz5A0qOXo2y4Ww3MoT4F\n",
252 "IY4+KTfNF58TaXN4VthstVNDitLKcdxvOjKmEj0tv0M953fs87E3Eul0B2JliBflOzfwnFcA+iul\n",
253 "5iEmwQFNEBaK569L0amUWggcqrXO8gg2FKHG2CdW4Uem9XvBlUflu7RUaiByU3lPa92ZKN8cSav8\n",
254 "fUQBTHKr1rrqueIsxp18/eg1azrLjSYB6NfRsY3G6Is9nDjDYxh4zundvbMotvtm5N50duA5P09t\n",
255 "T0faJIkfirU+zNrF1YiC4FBQECZE73/JqB//F+u14r+ImIVEOB1iu/6ZNfhwzEamp7YuU2e7RN1m\n",
256 "oZBnW5YVIfZ1qNWfotw51yuIph++hET0bAkcikwpTAEuCjxnSly3PzIP0a8NcnYgD6SBlSoaIhQX\n",
257 "V2UtVup24LBU6S7IyG+NUuodZP52awojrTSvIjeshlij9XdQKh2jXYRRDtpGfOCruQfEpmzbdn0V\n",
258 "dP9iPLsgjnEryI67Lzd/PCt6/5Tt+v3LJXAqQ/z7ut2ZO/Ccx23XfxUYZbt+7D8xCngl8Jwsa80s\n",
259 "ZBS8ke36O7cg4ybA5UgegJ0QE/XN5auvZRaiIMQRF12wXX8TCv9ls6eERpOtIMR+EXNS5YsRh8dS\n",
260 "To/V+CzUck21i6uR5++4wHNeKFXJRDH0PfoR5fqmtHKwDDhCa73O5JA3lCSeF04v6Z3FPRTMzBO7\n",
261 "S6AE8Q12PbomgYn5Xpm29yMPhu2RUK96iKMn9q6zfa38JXo/NHoly7oQeM5K4Iro60+jKINuJVJC\n",
262 "Yu/439uuX805A4VkWyfbrp+V/MdFnOmeCmpfFKsSRYMc2/U/DeyG3OfSjpOx5WmfVHmcuXFcFfus\n",
263 "5ZpqObbrb45EtswqpxyAcVI0FDMbOFxrXeT9a+heopvnEArzolvashT0wmbEapdgGpIU5XDb9R9F\n",
264 "YqrXQyyL8wPPeTeuGHjOMtv1T0VuqldH6W//jigNmyHOcAcBgwPPcZog20xkRLcJ8DPb9S9CRqM7\n",
265 "I7kDvoDE1hfdxwLPWWy7/plI7oCLbNffHXm4zUQeRtsjGRP/EXhOKSfcABkpj49i5+9G/putgHmB\n",
266 "5yxIN4iSF21C14V6Rtiu/yYSW15uHv4a4P8oKAedlPcvOAv4KmItfCTKKfAS8v8NR1ILHwnsl5GA\n",
267 "qF7ORdYaGA48HGWyfBqYgViDRwCfQR72PkDgOU9E2TvHI4m0TgeeRczb30DyH2iKcyA0ymrgWNv1\n",
268 "FyDK1NvIQ3tStN3LCH+9HUl29UPb9echFo8BUbtLEKfJtJ9EmgA59ifbrj8bCR3cGDlvZqdTLcPa\n",
269 "9NCbUMhs2GFLKvPFSAKxZl7/CxEL8pgoA+QMxD+kE3HenAHcHnjOGmNB6Dt8iGjHWSFKK4HHkcQr\n",
270 "OxvloLXYrr+77fqrEIejNyiE6P0WccZbabv+lFLtG+Ry5AY/BHkYfRDtR9M79QAAA3FJREFUcwYS\n",
271 "NdCFwHPuQR6a7wHfAR5GMhk+i9xcT6G6KIOKBJ6zFBn9r0GUmBlIWN9ziHf/5yjO/phsfy2yqt4i\n",
272 "xOJxF3INTI9k/Q7ZoV4xv0PC5LZCci4sQm6g08kYHdquvxy5lt4DwsSmF5EENCts1//Idv3M9LbR\n",
273 "egJTkEx4NvBA1joFifqLIjkeR6wcfwdeQfIFTEEcjHNU79RXkShvw95Ixs5+yOj/KuSh+ATiAHcq\n",
274 "xb4fxwOXRfJMQc6zlxGF6B3g4MBznmmWnBFzEUfP0xDFcCGiAG+JHKushESXIdanjRBF4l3EInAj\n",
275 "8vuOqWK/5yNRGaOQFNkfIhkOX6CQgwAA2/W3jkI3V0T7ejjatAFyXb2PXP/LbVnroWGi6bbzo697\n",
276 "IlaWk5Br93wkk+jztusP7o94Lna7eaoMZU0cVXIAped7eqGZfP2ZqmPFl+ptrVf3n19UpvVMYLRS\n",
277 "agBywxuEjLwWAe9qrTMXV2mUzs7OP/Xrp+6qt33Hmn5Zue3XNeZTOVoky5nqKiQkrNT883Qk3WvJ\n",
278 "sMLAc1bbrv9Z5AH6KWRkOB+5wRWlWo7a3Ga7/mOIomAho/GFyI30YeDREru7ELlOq07TG3jONbbr\n",
279 "T0Nu9KOQm+i/gFsDz3nTdv2fI2FbpdpfHnlpH4LcnHdAlIz5yLErqXgFnvOR7fo28lDYE7lu3kKO\n",
280 "TdZ9K52xrhTl7knnUVB6SqVeTsr4apQU6lDEbG4hCsFbROsRBE1ebjrwnNB2/XGIGf5gRBkYhPyv\n",
281 "7yDpjR9MtVkOnGK7/vWIgrFrVPcF4O8ZKbaXIuduWkH6KfL/JbkEsWClfWK2CDzHt10/jzhXjkGO\n",
282 "yzNIZEiRD00ga3ocaLv+kUh2xo8hSuVURKmIUyiXVGYCWVzKQlJD7xrJNg85b9LX8RLgF6X6SpFU\n",
283 "9Cpe28gaJgORqEEAbNffDLlvHIQoAndR8NEYilwjExD/nwuUiTQ0GAwGw7qC7fqjEUvKqsBzmhWd\n",
284 "t05gu/5pyNoifw48J9N5PForxQeeNFMMBoPBYDD0DWL/llvK1In9jt4zCoLBYDAYDH2DePo5MwrJ\n",
285 "dv0hFPwTnjBRDAaDwWAw9A3+hPgOHRPl25iK+FhsiuR4OARx0Lwf+J1REAwGg8Fg6AMEnvNklL78\n",
286 "HMRRca/E5hVINNIVwI2B56z6/3ExLRI31pXNAAAAAElFTkSuQmCC\n"
287 ],
288 "text/plain": [
289 "<IPython.core.display.Image at 0x111275490>"
290 ]
291 },
292 "execution_count": 6,
127 293 "metadata": {},
128 "outputs": [
129 {
130 "metadata": {},
131 "output_type": "pyout",
132 "png": "iVBORw0KGgoAAAANSUhEUgAAAggAAABDCAYAAAD5/P3lAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAAH3AAAB9wBYvxo6AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURB\nVHic7Z15uBxF1bjfugkJhCWBsCSAJGACNg4QCI3RT1lEAVE+UEBNOmwCDcjHT1wQgU+WD3dFxA1o\nCAikAZFFVlnCjizpsCUjHQjBIAkQlpCFJGS79fvjdGf69vTsc2fuza33eeaZmeqq6jM9vZw6dc4p\nBUwC+tE+fqW1fqmRDpRSHjCggS40sBxYDCxKvL8KzNBaL21EPoPB0DPIWVY/4NlE0ffzYfhgu+Qx\nGHoy/YFjaK+CcB3QkIIAHAWs3wRZsuhUSs0CXgQeBm7UWi/spn0Z+jA5yxpEfYruqnwYllRic5a1\nMaWv8U5gaT4M19Sx396IAnZLfB/SLkEMhp5O/3YL0AvoAHaKXl8HLlZK3QZcpbWe0lbJDOsaHuDU\n0e4u4JAy2wPk/C1JzrKWArOQ0fUtwH35MOysQxaDwbCO0NFuAXoh6wPjgQeUUvcqpUa0WyCDoQls\nCIwBjgfuAV7KWdY+7RWpmJxlXZezrEdylvXxdstiMKzrGAtCYxwI/EspdZbW+g/tFsbQ67kQuBHY\nFNgseh9FV6vCbUAeWBC9PgBeq2EfS6J2MQOBrRDTe5KdgAdzlvW1fBjeUUP/3UbOsoYBE6OvG7VT\nFoOhL9Af+BUwFLkZpV+DaY6V4UPkRpb1+ncT+m8nGwK/V0oN01qf025hDL2XfBi+DLycLMtZVo6u\nCsKfGnSq8/NheEpqHwOBEcDBwJnAsGhTP2ByzrJG5cPwnQb22Sy+0G4BDIa+RH+t9dmlNiqlFKIk\nJJWGi+jq5JPmq8BbJJQArfXqpkncczlbKbVQa/3rdgtiMNRCPgxXAK8Ar+Qs63LgXmDvaPPGwPeA\nH7VJvCRfbLcABkNfouwUg9ZaAwuj178BlFLvVejzgR4WFviM1npcuQpKqf6IyXIjxLS7GzAWuUnu\nXsO+fqWUellr3ZBJdq/jr9+BDn1uve07O9Rz0y6f8PtGZGgWe53oT6SBkZ/q1/nHZy47aloTRTKU\nIR+Gy3OWNR6Zxtg0Kv4KRkEwGPocxgcBiCwcsSI0F5iOhF+ilPok8C3gVGS+thK/VErdrbWuO2ys\ns/+aLZTuOKbe9krrIUCPUBB0B+PQ1P1bdKe6EzAKQgvJh+GbOct6gkJkxM45y+qXDIWMHBhjBWJe\nPgyDWvaRs6zPIVObAG/nw/DpEvUGAp8E9gGGJzbtl7Os7cvs4skqp0V0Yl8jgcOBjyMDhbmIZeWl\nfBg+UUVfReQsayhwELAnsAXi6/E28BxwTz4MP6iyn92RaSCA+/NhuCwqXx9R4MYhU0MfRTK/AjyW\nD8MFGd0ZDFVhFIQKaK3/BXxfKXUlklTq0xWafAI4Driyu2UzGLqRlygoCArYHJif2H4gcFb0+Z2c\nZW2bD8NV1XScs6yNgH8g/jsAPwCeTmzfFPgjYsnbiez71MUVdnMQcF8V4nyUs6whwB8QX4+0s2Ys\n0yPAt/NhGFbRZ/wbzgO+DaxXotqqnGX9GbigCkXhf5CBCsDngYdzljURGQhsWqLN+znL+iFwdT4M\ndYk6BkNJTJhjlWitQ2Bf4P4qqv848t8wGHor6Yd9+ruHJFkC2BI4rIa+D6egHKwmstYlGAxMQCwH\nrRjEPI5ER5S7ZvcFXsxZ1phKneUsawSi8HyH0soB0bbvAM9Ebaplt5xlnYkct1LKAYiFZhJwSQ19\nGwxrMRaEGtBar1RKfRX4JxIzXortou3PN1mE+YgJsSwaeoLHOQCqUy3QSr9eqZ6G/gq2aYVMhqrY\nOfF5FeJwvJZ8GM7JWdY/gC9HRS7wtyr7Pjrx+e6MqYC3KLbU7Qhck/h+FJIKvRRVjfSREXicU8EH\npgAvIIqLBZwGfC7avl5Uf29KkLOsTZCMq8npj9sQx89no37HIlaAODplNPBIzrJ2z4dhNVlaT0HC\nXwFmIkrAC4if2PaIz8/3KCgn385Z1pX5MJxeRd8Gw1qMglAjWutlSqnTgUcqVP0SzVYQtP5mcMXE\nSvvtUUy9YsK5QEWHy7EnTB6lOtSsFohkqEDOsgYAdqJoagkT9Z8pKAj75yzr4/kwnF2h748ho/GY\nq9J1oqiKLj4JOctKK8Yz8mH4Yrl9VcnHkXVYTsyHoZ8WJWdZNyPThbF5/3M5yzowH4alpi9+T0E5\nWA18Nx+Gf0zVeRG4KmdZ90R9bwCMRKwyX69C5h2j91uA4/JhuCSxbTYwJWdZtwNPIFbifsAFSISZ\nwVA1ZoqhDrTWjyIjjXIc3ApZDIZu4ELgY4nvt5Wody8wJ/qsgBOr6HsihfvOfCRrY7v5dYZyAECk\nGP0ISEZmZYZ55yxrB8SyEXNxhnKQ7Pt64H8TRUfmLGuXKmWeC4xPKQfJvp9CLCJlZTYYymEUhPq5\ntcL2XVsihcHQJHKWtU3Osi5GnAZj5iKWgiKitRouTxQdl7OscnPu0HV64dp8GLY7R8pyxEGxJPkw\nfBcZ9ceUSvN8IoV76upK/UZcgawcG3NKqYopfleFU+gDic/b5SzLWIwNNWFOmPqp5CG9sVJqPa11\nVZ7dBkOL2D1nWcmcBkOR8MFtgM/QdTXJZcCR+TBcXqa/SYj5egAFZ8VMX4ScZe2FRPnEXF2z9M3n\n3nwYVsrtAmK6/0z0uVR4ZXLtivvzYfhGpU7zYbgkZ1k3ACdHRQdWIQsUO3ZmkUzB3Q/xjaolLbeh\nj2MUhDrRWr+mlFpJ+eV5hyIxz4YWs98Fj/Rf8uZbozo0/ZYt7D8rf9ORK9stUw/hU9GrEnMAp1R+\ngph8GL4bzdNPiIpOorSzYtJ68FS1IYPdTLWp3hcnPm+Q3pizrA7E+TCmFn+aZN0dcpY1LB+G5e4b\ny6rM8bA49X39GmQyGMwUQ4NUGnkMrbDd0A3sdeLk4z6cN+89pTtDTWd+gyErF+7pTv5eu+XqJbyK\nTDHsmg/DJ6tsc2ni8+dzljUqXSGaevhmoqjIObFNVBzlV8kQug4W5tbQNl13WGatAv+poW+DoW6M\nBaExPgC2LrO9nHWhpSilDqI4NPMhrfXUJvS9M/DfqeJXtdY3N9p3rex50uQ9lFKT6BrTvoFCXbTX\nyZNfmnrZxHtbLVMP4xng74nvK5DzeD7wfIWRayb5MHwiZ1kzgF0oOCuemar2ZQoK8zLgr7Xup5t4\ns0n9DEl9b0RBSPeV5q0a+jYY6sYoCI1RacnZ91siRXUMAH6eKnsYicdulDOAY1NlpzWh35pRqG9R\nIuGN7uw4AfG878s8nw/DX3RDv5dScGY8NmdZP86HYXJaJzm9cHMp7/s2UHdK9BTpKaxBNbRN163k\nt9Rux05DH8FMMTTGZhW2v9sSKarjbopNk/sqpUY30qlSahCSGS/JCuD6RvqtF6UpMm/HaHTJbYaG\nmQzED/0umRVzlrUZhXwJ0HOmF5pJOlXyxzJrZbNt6rtZP8HQIzAKQp0opTZAlsItxTKtdTnv75YS\nLR7lpYqrjV0vx2EUH4fbtdZtucnpMqOrDjPy6jYii8DkRFHSYnAEhem22cBjrZKrVeTDcCldTf/p\nh345ksrEGprnF2EwNIRREOrnMxW2z2uJFLVxJcXmy2OVUo34ShydUda+EaIq7T2u0SZTY/eSdFY8\nMGdZm0efk86J6/LCQUnFp5pIkZjkcvQz8mH4YZPkMRgawigI9VNp7v7BlkhRA1rr+RQneNqC2hba\nWYtSajiS9z3JXLomaGktq/VllLIUdKqSWe0MjZMPwxlIel8Q/6Zv5CxrGIX8AJ10XU+hFtIRQ+UW\nKWoXyYyTu+Qsa79KDXKWNRpJyx5zZ9OlMhjqxCgIdaCU6g98o0K1npBCNotLM8rcOvuagCRgSXKN\n1rozq3IrCCZNfFkrfRjotWsCaJinUBODK51/tkuuPkTy/DoYOIDCfeb+fBjW4t2/lqhdcmRdbUri\nVnILXS2HZ1WRvfAcCk61K4A/dYdgBkM9GAWhPr5F6XSrIBf6Qy2SpSaidSReShV/XilV7veUIj29\noOkB2fGmXT7x7sCbOGpFf7VZx4A1m0/znG2nehMyc+0bms7NFJxzxwH7J7Y1OvWUPG9/mLOsLRvs\nr6lEaaOT0TtfBB5ITLWsJWdZg3KWdRNwTKL4wnwYzu9mMQ2GqjFhjjWilBqBpJYtx51a66UV6rST\nS+maJz52VvxRdvVilFK7UbzexGNa67Kr+bWS6X+ekPYs79HkLGt34JOI+Xyz6D2d1vfMnGUdini6\nL0C851/Oh2HD+SyaQT4MV+YsaxJyLm1Gwf9gAXBHg93/JNHHtsArOcuajCztPBDYCkkytBXg5sOw\n5QmF8mF4W86yLgK+HxXtC8zKWVaALMm8CslHsicS7RFzL8VhyAZDWzEKQg0opbYE7qd8prPVdF2h\nrSdyLfALYMNE2XFKqR/XsHbEURll62L4Wiv5PuBUqPPF6JXkLuCQbpGoPi4HfohYKGMHWD9axrlu\n8mF4Z7RuwfioaDBwaonqRemQW0U+DH+Qs6xFwHnIFNwQsv+3mMnA8dHiVwZDj8FMMVSJUuow4DkK\na7GX4gqt9cstEKlutNaL6boULMho5tBq2iul+lH8IFuCmJcNfZx8GM6hOCFVU5THfBhOQHxfylkH\n3gY+asb+6iUfhhcCewC3l5BlFbJk/P75MDwqlVTKYOgRKK1rizhSSk2h67ximo1abV5XSi2n9EIk\nz2itx5XYVqnfQcjI7DiqW2XtfeCTUbRA3ex50nWfUrqjeJEcrfcLrpj4SCN9xyilxgDPp4of0Fof\nUEXbg4B/pIqv1FrXnVNh7AmTR3V0qIwwRH1E4E28pd5+De0hZ1m/Bb4bfX0+H4Z7dMM+hgGjkDwC\nS5FpjFk9bR4/Z1mDkGmF4VHR20g4Y3oxJYOhR9EXphg6lFLlVjFbH0mZvDGwCTAayCFe0ntTOZ1y\nzDLgkEaVg1ahtX5BKfUU8OlE8ReUUjtorSstCduzch8YehSR5/6ERFG3nBvRuhE9frXUfBguA6pd\n+Mpg6DH0BQXBBro7o+Ea4Bta66e6eT/N5lK6KggKOAE4u1QDpdTGFOdNmNkLf7uh+zgYcRQEMa+3\nJe22wWBoDOOD0DhLgYla67vaLUgd3ETxglLHRXkeSnEExQ5gbQ9tNPQokis5TsqHoVlbwGDohRgF\noTECYHet9Y3tFqQetNYrKDb/DqN46eYk6emF1UhUhMFAzrImUEhDvgr4VRvFMRgMDWAUhPpYAvwf\n8Bmte31+/8uQBEdJMjMrKqW2o5A2N+YfWusePw9s6F5yltWRs6zxwKRE8RXtyEVgMBiaQ1/wQWgm\neWTe/jqtdU9Zz74htNavKaXuAw5KFB+glBqptZ6Tqj6RQlrYGDO90AfJWdY5wNeQFQwHIAmetk5U\neZFCsiCDwdALMQpCed5AphEC4NF12BHvUroqCAoJ7TwvVS+d++BdJEmPoe+xKRLnn0UeODwfhm3N\nRWAwGBqjLygIbwN/LbNdI1MGH6ReL/eWkMUmcDeSeGa7RNlRSqnzdZQoQym1C7Bzqt11NWReNKxb\nzEMU6GHAesBiYCaSLOviaF0Cg8HQi+kLCsLrWuvT2y1ET0ZrvUYp5SG57mO2Bz4LPB59/2ZRQ5P7\noM+SD8OLgYvbLYfBYOg+jJOiIeZKxOs8STJiIb28daC1/lf3imQwGAyGdmEUBAMA0XTKraniI5VS\nA6O0zOnloI31wGAwGNZhjIJgSHJp6vtgJBNlehW65cANLZHIYDAYDG3BKAiGtWitHwVeShV/muLF\nuW7VWi9qjVQGg8FgaAd9wUnRUBuXAn9IfN8f+FyqTo/OfbDnSX8brDpXnqEUe2ropzQvdtDx66ev\nGN9XolIMPQDb9T8LrBd4zsPtlsXQe7Bd/0BgQeA5QbtlMQqCIc21wC+ADaPv6WWu5wAPtVKgWtjt\n6Os2XG/9jhdQjIzTQ2rFF9bQecy4E2/I9UQlwXb9LYDDK1R7K/Cc21shj6FxbNcfDjwGKNv1Rwae\n83q7ZWo2tusPBb6ELGW9BbAICX99Gngs8Jx0hlZDBWzXHwvcC6ywXX9o4DlL2ymPURAMXdBaL1ZK\n+ZRItwz8Jc6N0BMZMFB9GxiZsWnzTjrPAH7QWomqYgTF/h9pngC6RUGwXf+XwC2B50ztjv57M7br\nXwJMCjxneo1NP0SWgAfJq7LOYLv+esAFwOkUL9wWM912/d0Dz+lsnWQ9A9v1BwEXAT8PPKfWVOML\nkPVt3kNWQm0rxgfBkEWph5UG/tJCOWqnQ40ttUkrvWcrRamWwHOmAZsguSfGAi9Hmy5AUhgPAz7f\nHfu2XX8k8ENgx+7ovzdju/4uwP9D/peaCDxnCbANsF3gOYubLVu7sF1/AHAHcBaiHDwI/C+ywNsE\n4KfA68BdfVE5iNgbOBmxqtRE4Dn/BoYDnwg8Z02zBasVY0EwFKG1fkEp9RTioJjkIa11zzaVarYq\nvVFt2TpBaiN6oCwB5tiu/2FUPCvwnLTTaLM5oJv77800dGwCz1kXHXkvRNKydwI/Cjzn1+kKtuuf\ni2TX7Ks0et681yxBGsUoCIZSBBQrCL0h98EbdW7rddiuPwoYFJu/bdffFNgL2BZ4DZgWKR5ZbRWS\n2+KIqGiE7fpjUtXmlrtZRdaHscBAYDowM/CckimWbdffFfgw8JzXou/9kfUccojV5MXAcz4s0XYw\nsCsymu8PzAVmBJ7zVqn9pdoPRVKF7wSsAN4EgqzRve36HcAoZDEqgO0zjs3rged8kGo3gOJ05ADT\ns0bTkan+k9HXGaVGjNFxykVf81nH2Hb9Ich/MRJJeT291H9fL7brj6CwANfPspQDgOi3rijRx/rI\nb8kB7wPPBZ4zL6Ne/JvfCDzn/WhufhvgvsBzVkR1dgN2AR4JPGduom38P7wXeM7c6FzfCfgU4iMR\nlFLebNfPIefXzMBzikz8tusPQyx676bljmTeCfhyVLST7frp//TV9Dluu/6GwOhUvTWB58zIkjFq\nsykyNfmfwHMW2K7fLzoWeyDTFPnAc14t1T7qYwNgT+Rc/wi5ZyT/N20UBEMRSqn+wNdTxQspTqTU\n41BaP6yVOipzGzzSYnG6m6uBz0YPv7OQm3dytc35tuuflHZutF3/BuArwEaJ4p/QNdU2wGnAH9M7\njRSTG5CbS5LQdv2joymTLKYBzwHjbNc/DomW2TCxfbXt+sMCz3k/sa8RwM+Qh/X6qf5W2q4/CTit\nzMN1OPB7CopQktW2658YeM5fEvXvRKZzBiXqZaWUPha4JlW2NfB8Rt0hiANfmjWIuf5jiLPfvVm/\nAfmvbgNmB54zKrkheuD+Bjg11Wap7fpnBJ5TybelFk4E+iE+Fb+ptbHt+scg//nGqfJbgeMDz1mY\nKN4UOZYX2q7fSWHhuNdt198ZOBc4MypbbLv+5wPPeTb6PiJqe5ft+ichx3WXRN8rbdc/OfCcrGis\nR4ChiHKSlSn2f4BzkOvitMRvCKJ9DEzU9TPafwGZlkkyBvExSrKUrtdnmoOBycA5tus/iCyat3li\nu7Zd/0rk2ihS1mzXPwT4E3LulaLTKAiGLL6EaMlJbtBat91pphIjFw289t9DVh4N7Jva9EKnWnpJ\nG0RqBXcjCa08YCqy/PJE4L8A33b9HQPPeTNR/0bgvujzGchoywPSq5U+nd6R7fp7IDfRjYDrEE99\nDeyHrPb5lO364xI36zTb2q4/AUnt/SSyLHQHMvJZklQOIhYChyCLid2FWBoGIQrDfwGnAP8Gskzd\nVvSbBgPvIMdpJjLHuxdikXgg1ewa4Jbo84+BHRAFI/3gT9/QQZa+/iIy9zwccVQrSeA5nbbrX4s8\ncI6htIIQK7xdFJLIAvEEYjmYBlyP/E4LeXj92Xb94YHnnFtOjhrYJ3q/vtbpE9v1fwqcjYxUL0GO\n51bI//g1YIzt+mNTSgJIivfNEIXgBOThfx0ySv8Nct7vgzgfj0+1HQf8E5iPKM/vI+vLHA9cZbs+\nJZSEevgDBZ++3yIKzgVI1FeSrCnD6ci0zebAJxCfjmoZjxzXPPBL5By0gW8jCt3sqHwtkYL1N0RB\n/R2ymOG2yHE5CLFAHAu8ahQEQxbfyijrDdML3HTTkWvUBRfsb88bPb6TzjEK+oHKL184YHL+Jmdl\nu+XrJsYBhwaec0dcYLu+hzw0dkcu/AvjbUmLgu36DqIgPB54zuQq9nURMgI8LjnyBibZrj8z2s/l\ntuvvVcJJbWvkXDoi8JzbKu0s8JxFtut/IqXgAPzOdv0/IiPnb5KhICAjpMGIEjAhPV1iu35HWsbA\nc25ObD8ZURAeqibENBqpTYnark8FBSHiakRBOMx2/cHpB29kSv4KooSlLRYnIcrBHcBXk7/Fdv0b\ngReAM23Xvz7wnJlVyFIJK3qfXUsj2/U/jiiiq4B9ktEytuv/Fhlpfx2xEnw31XxHYLfAc6bbrv8k\ncny/Bnwz8Jy/2q6/DTLd9F8Zu94ceXAeEHhOvM7MNbbrT0UU4vNs15+c2FY3gedcm/hNP0EUhDvL\nKMrJtkuIFPboWNWiIOSAO4HDE7/Dj67FSxEn21+m2pyOWDpuCDxn7fG2Xf8e4F1EIVsceE5oohgM\nXVBKjURuSEke11qXMhv3OPR553VO9Sb407yJZwTexO8FnnNV/qYj11XlAOCfSeUA1s4D/y36mp7f\nrAvb9fdGLDMzU8pBzMXIg2wsMhLKQiFhgxWVg5gM5SDm+uh9VHqD7fr7IlaNFcAJWb4UPcHLPvCc\n2YgVZn3gyIwq30AsQg8lQ+aiefUfR1/PzlB08sD9Udusfmsi2t+Q6GutjspnIE6L16dDaSN/irMR\np8dTbddPOxK/nwgxTZr8747e30SsEkNL7PvXGQrAVYgvwggK/gK9mXMyfuON0fvWkY9Dkp2i97uT\nhYHnLKNgURsDxknRUMz5FJ8XP22DHIbqSc9pxsSOW8ObtJ89ovdXbNcvpQC8j4zcdiTbnAoy4q2b\n6Ia3CYV5/Y0zqsXOf4/WEYveaq5GQuOOQaZekhydqJNkW2BLZF2UzhL/R+xE2XAIa+A52nb9lUho\nY63hd7GD5d1ZGwPPmW27/iuIUrkLXc/n9xP13rZd/yNgVezoF8n1NjAyyyKETGGl97fGdv1/IlaL\n3h7e+06WM2PgOQtt11+GTMcNo6vVJ1aWsyK+4nvFQjAKgiGBUmoshfnOmGe11vdl1Tf0GOaUKI9v\nlqrE9lqJb6b/Hb3KsU2Zba/VslPb9bdDfA0ORLz0N62iWWxVqMkc3iZuRuawP2u7/g6JKI9RSCTR\nYoodhOP/YgNKK2Ix2zZJzjnINMN2NbaL/4uiaIUE/0EUhB3pqiCkMwl2IscjXZZFJ/B2iW1xRtWR\nZWTqDcwps63U9f8Q0TSN7fp/iK0PtuvviPjmrCHyR1qrICilNkTmHjZDLsDke/JzOtwnzY1KqXcR\nR4cFiBab9XlRT87I19dQSo1GNPz0tJOxHvR8mhrOVobB0XuAOBiWo1zmwaqdXW3X3x+4BzGVv4SM\npN9AnPEg21McxMIArTs2dRN4zoe26/8NOA6xGJwfbYqV9b8GnrM81Sz+Lz5A0qOXo2y4Ww3MoT4F\nIY4+KTfNF58TaXN4VthstVNDitLKcdxvOjKmEj0tv0M953fs87E3Eul0B2JliBflOzfwnFcA+iul\n5iEmwQFNEBaK569L0amUWggcqrXO8gg2FKHG2CdW4Uem9XvBlUflu7RUaiByU3lPa92ZKN8cSav8\nfUQBTHKr1rrqueIsxp18/eg1azrLjSYB6NfRsY3G6Is9nDjDYxh4zundvbMotvtm5N50duA5P09t\nT0faJIkfirU+zNrF1YiC4FBQECZE73/JqB//F+u14r+ImIVEOB1iu/6ZNfhwzEamp7YuU2e7RN1m\noZBnW5YVIfZ1qNWfotw51yuIph++hET0bAkcikwpTAEuCjxnSly3PzIP0a8NcnYgD6SBlSoaIhQX\nV2UtVup24LBU6S7IyG+NUuodZP52awojrTSvIjeshlij9XdQKh2jXYRRDtpGfOCruQfEpmzbdn0V\ndP9iPLsgjnEryI67Lzd/PCt6/5Tt+v3LJXAqQ/z7ut2ZO/Ccx23XfxUYZbt+7D8xCngl8Jwsa80s\nZBS8ke36O7cg4ybA5UgegJ0QE/XN5auvZRaiIMQRF12wXX8TCv9ls6eERpOtIMR+EXNS5YsRh8dS\nTo/V+CzUck21i6uR5++4wHNeKFXJRDH0PfoR5fqmtHKwDDhCa73O5JA3lCSeF04v6Z3FPRTMzBO7\nS6AE8Q12PbomgYn5Xpm29yMPhu2RUK96iKMn9q6zfa38JXo/NHoly7oQeM5K4Iro60+jKINuJVJC\nYu/439uuX805A4VkWyfbrp+V/MdFnOmeCmpfFKsSRYMc2/U/DeyG3OfSjpOx5WmfVHmcuXFcFfus\n5ZpqObbrb45EtswqpxyAcVI0FDMbOFxrXeT9a+heopvnEArzolvashT0wmbEapdgGpIU5XDb9R9F\nYqrXQyyL8wPPeTeuGHjOMtv1T0VuqldH6W//jigNmyHOcAcBgwPPcZog20xkRLcJ8DPb9S9CRqM7\nI7kDvoDE1hfdxwLPWWy7/plI7oCLbNffHXm4zUQeRtsjGRP/EXhOKSfcABkpj49i5+9G/putgHmB\n5yxIN4iSF21C14V6Rtiu/yYSW15uHv4a4P8oKAedlPcvOAv4KmItfCTKKfAS8v8NR1ILHwnsl5GA\nqF7ORdYaGA48HGWyfBqYgViDRwCfQR72PkDgOU9E2TvHI4m0TgeeRczb30DyH2iKcyA0ymrgWNv1\nFyDK1NvIQ3tStN3LCH+9HUl29UPb9echFo8BUbtLEKfJtJ9EmgA59ifbrj8bCR3cGDlvZqdTLcPa\n9NCbUMhs2GFLKvPFSAKxZl7/CxEL8pgoA+QMxD+kE3HenAHcHnjOGmNB6Dt8iGjHWSFKK4HHkcQr\nOxvloLXYrr+77fqrEIejNyiE6P0WccZbabv+lFLtG+Ry5AY/BHkYfRDtR9M79QAAA3FJREFUcwYS\nNdCFwHPuQR6a7wHfAR5GMhk+i9xcT6G6KIOKBJ6zFBn9r0GUmBlIWN9ziHf/5yjO/phsfy2yqt4i\nxOJxF3INTI9k/Q7ZoV4xv0PC5LZCci4sQm6g08kYHdquvxy5lt4DwsSmF5EENCts1//Idv3M9LbR\negJTkEx4NvBA1joFifqLIjkeR6wcfwdeQfIFTEEcjHNU79RXkShvw95Ixs5+yOj/KuSh+ATiAHcq\nxb4fxwOXRfJMQc6zlxGF6B3g4MBznmmWnBFzEUfP0xDFcCGiAG+JHKushESXIdanjRBF4l3EInAj\n8vuOqWK/5yNRGaOQFNkfIhkOX6CQgwAA2/W3jkI3V0T7ejjatAFyXb2PXP/LbVnroWGi6bbzo697\nIlaWk5Br93wkk+jztusP7o94Lna7eaoMZU0cVXIAped7eqGZfP2ZqmPFl+ptrVf3n19UpvVMYLRS\nagBywxuEjLwWAe9qrTMXV2mUzs7OP/Xrp+6qt33Hmn5Zue3XNeZTOVoky5nqKiQkrNT883Qk3WvJ\nsMLAc1bbrv9Z5AH6KWRkOB+5wRWlWo7a3Ga7/mOIomAho/GFyI30YeDREru7ELlOq07TG3jONbbr\nT0Nu9KOQm+i/gFsDz3nTdv2fI2FbpdpfHnlpH4LcnHdAlIz5yLErqXgFnvOR7fo28lDYE7lu3kKO\nTdZ9K52xrhTl7knnUVB6SqVeTsr4apQU6lDEbG4hCsFbROsRBE1ebjrwnNB2/XGIGf5gRBkYhPyv\n7yDpjR9MtVkOnGK7/vWIgrFrVPcF4O8ZKbaXIuduWkH6KfL/JbkEsWClfWK2CDzHt10/jzhXjkGO\nyzNIZEiRD00ga3ocaLv+kUh2xo8hSuVURKmIUyiXVGYCWVzKQlJD7xrJNg85b9LX8RLgF6X6SpFU\n9Cpe28gaJgORqEEAbNffDLlvHIQoAndR8NEYilwjExD/nwuUiTQ0GAwGw7qC7fqjEUvKqsBzmhWd\nt05gu/5pyNoifw48J9N5PForxQeeNFMMBoPBYDD0DWL/llvK1In9jt4zCoLBYDAYDH2DePo5MwrJ\ndv0hFPwTnjBRDAaDwWAw9A3+hPgOHRPl25iK+FhsiuR4OARx0Lwf+J1REAwGg8Fg6AMEnvNklL78\nHMRRca/E5hVINNIVwI2B56z6/3ExLRI31pXNAAAAAElFTkSuQmCC\n",
133 "prompt_number": 6,
134 "text": [
135 "<IPython.core.display.Image at 0x111275490>"
136 ]
137 }
138 ],
139 "prompt_number": 6
294 "output_type": "execute_result"
140 295 }
141 296 ],
142 "metadata": {}
297 "source": [
298 "from IPython.display import Image\n",
299 "Image(\"http://ipython.org/_static/IPy_header.png\")"
300 ]
143 301 }
144 ]
145 }
302 ],
303 "metadata": {},
304 "nbformat": 4,
305 "nbformat_minor": 0
306 } No newline at end of file
@@ -1,312 +1,308 b''
1 1 {
2 2 "cells": [
3 3 {
4 "cell_type": "heading",
5 "level": 1,
4 "cell_type": "markdown",
6 5 "metadata": {},
7 6 "source": [
8 "nbconvert latex test"
7 "# nbconvert latex test"
9 8 ]
10 9 },
11 10 {
12 11 "cell_type": "markdown",
13 12 "metadata": {},
14 13 "source": [
15 14 "**Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus bibendum felis dictum sodales. Ut suscipit, orci ut interdum imperdiet, purus ligula mollis *justo*, non malesuada nisl augue eget lorem. Donec bibendum, erat sit amet porttitor aliquam, urna lorem ornare libero, in vehicula diam diam ut ante. Nam non urna rhoncus, accumsan elit sit amet, mollis tellus. Vestibulum nec tellus metus. Vestibulum tempor, ligula et vehicula rhoncus, sapien turpis faucibus lorem, id dapibus turpis mauris ac orci. Sed volutpat vestibulum venenatis."
16 15 ]
17 16 },
18 17 {
19 "cell_type": "heading",
20 "level": 2,
18 "cell_type": "markdown",
21 19 "metadata": {},
22 20 "source": [
23 "Printed Using Python"
21 "## Printed Using Python"
24 22 ]
25 23 },
26 24 {
27 25 "cell_type": "code",
28 26 "execution_count": 1,
29 27 "metadata": {
30 28 "collapsed": false
31 29 },
32 30 "outputs": [
33 31 {
34 32 "name": "stdout",
35 33 "output_type": "stream",
36 34 "text": [
37 35 "hello\n"
38 36 ]
39 37 }
40 38 ],
41 39 "source": [
42 40 "print(\"hello\")"
43 41 ]
44 42 },
45 43 {
46 "cell_type": "heading",
47 "level": 2,
44 "cell_type": "markdown",
48 45 "metadata": {},
49 46 "source": [
50 "Pyout"
47 "## Pyout"
51 48 ]
52 49 },
53 50 {
54 51 "cell_type": "code",
55 52 "execution_count": 3,
56 53 "metadata": {
57 54 "collapsed": false
58 55 },
59 56 "outputs": [
60 57 {
61 58 "data": {
62 59 "text/html": [
63 60 "\n",
64 61 "<script>\n",
65 62 "console.log(\"hello\");\n",
66 63 "</script>\n",
67 64 "<b>HTML</b>\n"
68 65 ],
69 66 "text/plain": [
70 67 "<IPython.core.display.HTML at 0x1112757d0>"
71 68 ]
72 69 },
73 70 "execution_count": 3,
74 71 "metadata": {},
75 72 "output_type": "execute_result"
76 73 }
77 74 ],
78 75 "source": [
79 76 "from IPython.display import HTML\n",
80 77 "HTML(\"\"\"\n",
81 78 "<script>\n",
82 79 "console.log(\"hello\");\n",
83 80 "</script>\n",
84 81 "<b>HTML</b>\n",
85 82 "\"\"\")"
86 83 ]
87 84 },
88 85 {
89 86 "cell_type": "code",
90 87 "execution_count": 7,
91 88 "metadata": {
92 89 "collapsed": false
93 90 },
94 91 "outputs": [
95 92 {
96 93 "data": {
97 94 "application/javascript": [
98 95 "console.log(\"hi\");"
99 96 ],
100 97 "text/plain": [
101 98 "<IPython.core.display.Javascript at 0x1112b4b50>"
102 99 ]
103 100 },
104 101 "metadata": {},
105 102 "output_type": "display_data"
106 103 }
107 104 ],
108 105 "source": [
109 106 "%%javascript\n",
110 107 "console.log(\"hi\");"
111 108 ]
112 109 },
113 110 {
114 "cell_type": "heading",
115 "level": 3,
111 "cell_type": "markdown",
116 112 "metadata": {},
117 113 "source": [
118 "Image"
114 "### Image"
119 115 ]
120 116 },
121 117 {
122 118 "cell_type": "code",
123 119 "execution_count": 6,
124 120 "metadata": {
125 121 "collapsed": false
126 122 },
127 123 "outputs": [
128 124 {
129 125 "data": {
130 126 "image/png": [
131 127 "iVBORw0KGgoAAAANSUhEUgAAAggAAABDCAYAAAD5/P3lAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\n",
132 128 "AAAH3AAAB9wBYvxo6AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURB\n",
133 129 "VHic7Z15uBxF1bjfugkJhCWBsCSAJGACNg4QCI3RT1lEAVE+UEBNOmwCDcjHT1wQgU+WD3dFxA1o\n",
134 130 "CAikAZFFVlnCjizpsCUjHQjBIAkQlpCFJGS79fvjdGf69vTsc2fuza33eeaZmeqq6jM9vZw6dc4p\n",
135 131 "BUwC+tE+fqW1fqmRDpRSHjCggS40sBxYDCxKvL8KzNBaL21EPoPB0DPIWVY/4NlE0ffzYfhgu+Qx\n",
136 132 "GHoy/YFjaK+CcB3QkIIAHAWs3wRZsuhUSs0CXgQeBm7UWi/spn0Z+jA5yxpEfYruqnwYllRic5a1\n",
137 133 "MaWv8U5gaT4M19Sx396IAnZLfB/SLkEMhp5O/3YL0AvoAHaKXl8HLlZK3QZcpbWe0lbJDOsaHuDU\n",
138 134 "0e4u4JAy2wPk/C1JzrKWArOQ0fUtwH35MOysQxaDwbCO0NFuAXoh6wPjgQeUUvcqpUa0WyCDoQls\n",
139 135 "CIwBjgfuAV7KWdY+7RWpmJxlXZezrEdylvXxdstiMKzrGAtCYxwI/EspdZbW+g/tFsbQ67kQuBHY\n",
140 136 "FNgseh9FV6vCbUAeWBC9PgBeq2EfS6J2MQOBrRDTe5KdgAdzlvW1fBjeUUP/3UbOsoYBE6OvG7VT\n",
141 137 "FoOhL9Af+BUwFLkZpV+DaY6V4UPkRpb1+ncT+m8nGwK/V0oN01qf025hDL2XfBi+DLycLMtZVo6u\n",
142 138 "CsKfGnSq8/NheEpqHwOBEcDBwJnAsGhTP2ByzrJG5cPwnQb22Sy+0G4BDIa+RH+t9dmlNiqlFKIk\n",
143 139 "JJWGi+jq5JPmq8BbJJQArfXqpkncczlbKbVQa/3rdgtiMNRCPgxXAK8Ar+Qs63LgXmDvaPPGwPeA\n",
144 140 "H7VJvCRfbLcABkNfouwUg9ZaAwuj178BlFLvVejzgR4WFviM1npcuQpKqf6IyXIjxLS7GzAWuUnu\n",
145 141 "XsO+fqWUellr3ZBJdq/jr9+BDn1uve07O9Rz0y6f8PtGZGgWe53oT6SBkZ/q1/nHZy47aloTRTKU\n",
146 142 "IR+Gy3OWNR6Zxtg0Kv4KRkEwGPocxgcBiCwcsSI0F5iOhF+ilPok8C3gVGS+thK/VErdrbWuO2ys\n",
147 143 "s/+aLZTuOKbe9krrIUCPUBB0B+PQ1P1bdKe6EzAKQgvJh+GbOct6gkJkxM45y+qXDIWMHBhjBWJe\n",
148 144 "PgyDWvaRs6zPIVObAG/nw/DpEvUGAp8E9gGGJzbtl7Os7cvs4skqp0V0Yl8jgcOBjyMDhbmIZeWl\n",
149 145 "fBg+UUVfReQsayhwELAnsAXi6/E28BxwTz4MP6iyn92RaSCA+/NhuCwqXx9R4MYhU0MfRTK/AjyW\n",
150 146 "D8MFGd0ZDFVhFIQKaK3/BXxfKXUlklTq0xWafAI4Driyu2UzGLqRlygoCArYHJif2H4gcFb0+Z2c\n",
151 147 "ZW2bD8NV1XScs6yNgH8g/jsAPwCeTmzfFPgjYsnbiez71MUVdnMQcF8V4nyUs6whwB8QX4+0s2Ys\n",
152 148 "0yPAt/NhGFbRZ/wbzgO+DaxXotqqnGX9GbigCkXhf5CBCsDngYdzljURGQhsWqLN+znL+iFwdT4M\n",
153 149 "dYk6BkNJTJhjlWitQ2Bf4P4qqv848t8wGHor6Yd9+ruHJFkC2BI4rIa+D6egHKwmstYlGAxMQCwH\n",
154 150 "rRjEPI5ER5S7ZvcFXsxZ1phKneUsawSi8HyH0soB0bbvAM9Ebaplt5xlnYkct1LKAYiFZhJwSQ19\n",
155 151 "GwxrMRaEGtBar1RKfRX4JxIzXortou3PN1mE+YgJsSwaeoLHOQCqUy3QSr9eqZ6G/gq2aYVMhqrY\n",
156 152 "OfF5FeJwvJZ8GM7JWdY/gC9HRS7wtyr7Pjrx+e6MqYC3KLbU7Qhck/h+FJIKvRRVjfSREXicU8EH\n",
157 153 "pgAvIIqLBZwGfC7avl5Uf29KkLOsTZCMq8npj9sQx89no37HIlaAODplNPBIzrJ2z4dhNVlaT0HC\n",
158 154 "XwFmIkrAC4if2PaIz8/3KCgn385Z1pX5MJxeRd8Gw1qMglAjWutlSqnTgUcqVP0SzVYQtP5mcMXE\n",
159 155 "SvvtUUy9YsK5QEWHy7EnTB6lOtSsFohkqEDOsgYAdqJoagkT9Z8pKAj75yzr4/kwnF2h748ho/GY\n",
160 156 "q9J1oqiKLj4JOctKK8Yz8mH4Yrl9VcnHkXVYTsyHoZ8WJWdZNyPThbF5/3M5yzowH4alpi9+T0E5\n",
161 157 "WA18Nx+Gf0zVeRG4KmdZ90R9bwCMRKwyX69C5h2j91uA4/JhuCSxbTYwJWdZtwNPIFbifsAFSISZ\n",
162 158 "wVA1ZoqhDrTWjyIjjXIc3ApZDIZu4ELgY4nvt5Wody8wJ/qsgBOr6HsihfvOfCRrY7v5dYZyAECk\n",
163 159 "GP0ISEZmZYZ55yxrB8SyEXNxhnKQ7Pt64H8TRUfmLGuXKmWeC4xPKQfJvp9CLCJlZTYYymEUhPq5\n",
164 160 "tcL2XVsihcHQJHKWtU3Osi5GnAZj5iKWgiKitRouTxQdl7OscnPu0HV64dp8GLY7R8pyxEGxJPkw\n",
165 161 "fBcZ9ceUSvN8IoV76upK/UZcgawcG3NKqYopfleFU+gDic/b5SzLWIwNNWFOmPqp5CG9sVJqPa11\n",
166 162 "VZ7dBkOL2D1nWcmcBkOR8MFtgM/QdTXJZcCR+TBcXqa/SYj5egAFZ8VMX4ScZe2FRPnEXF2z9M3n\n",
167 163 "3nwYVsrtAmK6/0z0uVR4ZXLtivvzYfhGpU7zYbgkZ1k3ACdHRQdWIQsUO3ZmkUzB3Q/xjaolLbeh\n",
168 164 "j2MUhDrRWr+mlFpJ+eV5hyIxz4YWs98Fj/Rf8uZbozo0/ZYt7D8rf9ORK9stUw/hU9GrEnMAp1R+\n",
169 165 "gph8GL4bzdNPiIpOorSzYtJ68FS1IYPdTLWp3hcnPm+Q3pizrA7E+TCmFn+aZN0dcpY1LB+G5e4b\n",
170 166 "y6rM8bA49X39GmQyGMwUQ4NUGnkMrbDd0A3sdeLk4z6cN+89pTtDTWd+gyErF+7pTv5eu+XqJbyK\n",
171 167 "TDHsmg/DJ6tsc2ni8+dzljUqXSGaevhmoqjIObFNVBzlV8kQug4W5tbQNl13WGatAv+poW+DoW6M\n",
172 168 "BaExPgC2LrO9nHWhpSilDqI4NPMhrfXUJvS9M/DfqeJXtdY3N9p3rex50uQ9lFKT6BrTvoFCXbTX\n",
173 169 "yZNfmnrZxHtbLVMP4xng74nvK5DzeD7wfIWRayb5MHwiZ1kzgF0oOCuemar2ZQoK8zLgr7Xup5t4\n",
174 170 "s0n9DEl9b0RBSPeV5q0a+jYY6sYoCI1RacnZ91siRXUMAH6eKnsYicdulDOAY1NlpzWh35pRqG9R\n",
175 171 "IuGN7uw4AfG878s8nw/DX3RDv5dScGY8NmdZP86HYXJaJzm9cHMp7/s2UHdK9BTpKaxBNbRN163k\n",
176 172 "t9Rux05DH8FMMTTGZhW2v9sSKarjbopNk/sqpUY30qlSahCSGS/JCuD6RvqtF6UpMm/HaHTJbYaG\n",
177 173 "mQzED/0umRVzlrUZhXwJ0HOmF5pJOlXyxzJrZbNt6rtZP8HQIzAKQp0opTZAlsItxTKtdTnv75YS\n",
178 174 "LR7lpYqrjV0vx2EUH4fbtdZtucnpMqOrDjPy6jYii8DkRFHSYnAEhem22cBjrZKrVeTDcCldTf/p\n",
179 175 "h345ksrEGprnF2EwNIRREOrnMxW2z2uJFLVxJcXmy2OVUo34ShydUda+EaIq7T2u0SZTY/eSdFY8\n",
180 176 "MGdZm0efk86J6/LCQUnFp5pIkZjkcvQz8mH4YZPkMRgawigI9VNp7v7BlkhRA1rr+RQneNqC2hba\n",
181 177 "WYtSajiS9z3JXLomaGktq/VllLIUdKqSWe0MjZMPwxlIel8Q/6Zv5CxrGIX8AJ10XU+hFtIRQ+UW\n",
182 178 "KWoXyYyTu+Qsa79KDXKWNRpJyx5zZ9OlMhjqxCgIdaCU6g98o0K1npBCNotLM8rcOvuagCRgSXKN\n",
183 179 "1rozq3IrCCZNfFkrfRjotWsCaJinUBODK51/tkuuPkTy/DoYOIDCfeb+fBjW4t2/lqhdcmRdbUri\n",
184 180 "VnILXS2HZ1WRvfAcCk61K4A/dYdgBkM9GAWhPr5F6XSrIBf6Qy2SpSaidSReShV/XilV7veUIj29\n",
185 181 "oOkB2fGmXT7x7sCbOGpFf7VZx4A1m0/znG2nehMyc+0bms7NFJxzxwH7J7Y1OvWUPG9/mLOsLRvs\n",
186 182 "r6lEaaOT0TtfBB5ITLWsJWdZg3KWdRNwTKL4wnwYzu9mMQ2GqjFhjjWilBqBpJYtx51a66UV6rST\n",
187 183 "S+maJz52VvxRdvVilFK7UbzexGNa67Kr+bWS6X+ekPYs79HkLGt34JOI+Xyz6D2d1vfMnGUdini6\n",
188 184 "L0C851/Oh2HD+SyaQT4MV+YsaxJyLm1Gwf9gAXBHg93/JNHHtsArOcuajCztPBDYCkkytBXg5sOw\n",
189 185 "5QmF8mF4W86yLgK+HxXtC8zKWVaALMm8CslHsicS7RFzL8VhyAZDWzEKQg0opbYE7qd8prPVdF2h\n",
190 186 "rSdyLfALYMNE2XFKqR/XsHbEURll62L4Wiv5PuBUqPPF6JXkLuCQbpGoPi4HfohYKGMHWD9axrlu\n",
191 187 "8mF4Z7RuwfioaDBwaonqRemQW0U+DH+Qs6xFwHnIFNwQsv+3mMnA8dHiVwZDj8FMMVSJUuow4DkK\n",
192 188 "a7GX4gqt9cstEKlutNaL6boULMho5tBq2iul+lH8IFuCmJcNfZx8GM6hOCFVU5THfBhOQHxfylkH\n",
193 189 "3gY+asb+6iUfhhcCewC3l5BlFbJk/P75MDwqlVTKYOgRKK1rizhSSk2h67ximo1abV5XSi2n9EIk\n",
194 190 "z2itx5XYVqnfQcjI7DiqW2XtfeCTUbRA3ex50nWfUrqjeJEcrfcLrpj4SCN9xyilxgDPp4of0Fof\n",
195 191 "UEXbg4B/pIqv1FrXnVNh7AmTR3V0qIwwRH1E4E28pd5+De0hZ1m/Bb4bfX0+H4Z7dMM+hgGjkDwC\n",
196 192 "S5FpjFk9bR4/Z1mDkGmF4VHR20g4Y3oxJYOhR9EXphg6lFLlVjFbH0mZvDGwCTAayCFe0ntTOZ1y\n",
197 193 "zDLgkEaVg1ahtX5BKfUU8OlE8ReUUjtorSstCduzch8YehSR5/6ERFG3nBvRuhE9frXUfBguA6pd\n",
198 194 "+Mpg6DH0BQXBBro7o+Ea4Bta66e6eT/N5lK6KggKOAE4u1QDpdTGFOdNmNkLf7uh+zgYcRQEMa+3\n",
199 195 "Je22wWBoDOOD0DhLgYla67vaLUgd3ETxglLHRXkeSnEExQ5gbQ9tNPQokis5TsqHoVlbwGDohRgF\n",
200 196 "oTECYHet9Y3tFqQetNYrKDb/DqN46eYk6emF1UhUhMFAzrImUEhDvgr4VRvFMRgMDWAUhPpYAvwf\n",
201 197 "8Bmte31+/8uQBEdJMjMrKqW2o5A2N+YfWusePw9s6F5yltWRs6zxwKRE8RXtyEVgMBiaQ1/wQWgm\n",
202 198 "eWTe/jqtdU9Zz74htNavKaXuAw5KFB+glBqptZ6Tqj6RQlrYGDO90AfJWdY5wNeQFQwHIAmetk5U\n",
203 199 "eZFCsiCDwdALMQpCed5AphEC4NF12BHvUroqCAoJ7TwvVS+d++BdJEmPoe+xKRLnn0UeODwfhm3N\n",
204 200 "RWAwGBqjLygIbwN/LbNdI1MGH6ReL/eWkMUmcDeSeGa7RNlRSqnzdZQoQym1C7Bzqt11NWReNKxb\n",
205 201 "zEMU6GHAesBiYCaSLOviaF0Cg8HQi+kLCsLrWuvT2y1ET0ZrvUYp5SG57mO2Bz4LPB59/2ZRQ5P7\n",
206 202 "oM+SD8OLgYvbLYfBYOg+jJOiIeZKxOs8STJiIb28daC1/lf3imQwGAyGdmEUBAMA0XTKraniI5VS\n",
207 203 "A6O0zOnloI31wGAwGNZhjIJgSHJp6vtgJBNlehW65cANLZHIYDAYDG3BKAiGtWitHwVeShV/muLF\n",
208 204 "uW7VWi9qjVQGg8FgaAd9wUnRUBuXAn9IfN8f+FyqTo/OfbDnSX8brDpXnqEUe2ropzQvdtDx66ev\n",
209 205 "GN9XolIMPQDb9T8LrBd4zsPtlsXQe7Bd/0BgQeA5QbtlMQqCIc21wC+ADaPv6WWu5wAPtVKgWtjt\n",
210 206 "6Os2XG/9jhdQjIzTQ2rFF9bQecy4E2/I9UQlwXb9LYDDK1R7K/Cc21shj6FxbNcfDjwGKNv1Rwae\n",
211 207 "83q7ZWo2tusPBb6ELGW9BbAICX99Gngs8Jx0hlZDBWzXHwvcC6ywXX9o4DlL2ymPURAMXdBaL1ZK\n",
212 208 "+ZRItwz8Jc6N0BMZMFB9GxiZsWnzTjrPAH7QWomqYgTF/h9pngC6RUGwXf+XwC2B50ztjv57M7br\n",
213 209 "XwJMCjxneo1NP0SWgAfJq7LOYLv+esAFwOkUL9wWM912/d0Dz+lsnWQ9A9v1BwEXAT8PPKfWVOML\n",
214 210 "kPVt3kNWQm0rxgfBkEWph5UG/tJCOWqnQ40ttUkrvWcrRamWwHOmAZsguSfGAi9Hmy5AUhgPAz7f\n",
215 211 "Hfu2XX8k8ENgx+7ovzdju/4uwP9D/peaCDxnCbANsF3gOYubLVu7sF1/AHAHcBaiHDwI/C+ywNsE\n",
216 212 "4KfA68BdfVE5iNgbOBmxqtRE4Dn/BoYDnwg8Z02zBasVY0EwFKG1fkEp9RTioJjkIa11zzaVarYq\n",
217 213 "vVFt2TpBaiN6oCwB5tiu/2FUPCvwnLTTaLM5oJv77800dGwCz1kXHXkvRNKydwI/Cjzn1+kKtuuf\n",
218 214 "i2TX7Ks0et681yxBGsUoCIZSBBQrCL0h98EbdW7rddiuPwoYFJu/bdffFNgL2BZ4DZgWKR5ZbRWS\n",
219 215 "2+KIqGiE7fpjUtXmlrtZRdaHscBAYDowM/CckimWbdffFfgw8JzXou/9kfUccojV5MXAcz4s0XYw\n",
220 216 "sCsymu8PzAVmBJ7zVqn9pdoPRVKF7wSsAN4EgqzRve36HcAoZDEqgO0zjs3rged8kGo3gOJ05ADT\n",
221 217 "s0bTkan+k9HXGaVGjNFxykVf81nH2Hb9Ich/MRJJeT291H9fL7brj6CwANfPspQDgOi3rijRx/rI\n",
222 218 "b8kB7wPPBZ4zL6Ne/JvfCDzn/WhufhvgvsBzVkR1dgN2AR4JPGduom38P7wXeM7c6FzfCfgU4iMR\n",
223 219 "lFLebNfPIefXzMBzikz8tusPQyx676bljmTeCfhyVLST7frp//TV9Dluu/6GwOhUvTWB58zIkjFq\n",
224 220 "sykyNfmfwHMW2K7fLzoWeyDTFPnAc14t1T7qYwNgT+Rc/wi5ZyT/N20UBEMRSqn+wNdTxQspTqTU\n",
225 221 "41BaP6yVOipzGzzSYnG6m6uBz0YPv7OQm3dytc35tuuflHZutF3/BuArwEaJ4p/QNdU2wGnAH9M7\n",
226 222 "jRSTG5CbS5LQdv2joymTLKYBzwHjbNc/DomW2TCxfbXt+sMCz3k/sa8RwM+Qh/X6qf5W2q4/CTit\n",
227 223 "zMN1OPB7CopQktW2658YeM5fEvXvRKZzBiXqZaWUPha4JlW2NfB8Rt0hiANfmjWIuf5jiLPfvVm/\n",
228 224 "AfmvbgNmB54zKrkheuD+Bjg11Wap7fpnBJ5TybelFk4E+iE+Fb+ptbHt+scg//nGqfJbgeMDz1mY\n",
229 225 "KN4UOZYX2q7fSWHhuNdt198ZOBc4MypbbLv+5wPPeTb6PiJqe5ft+ichx3WXRN8rbdc/OfCcrGis\n",
230 226 "R4ChiHKSlSn2f4BzkOvitMRvCKJ9DEzU9TPafwGZlkkyBvExSrKUrtdnmoOBycA5tus/iCyat3li\n",
231 227 "u7Zd/0rk2ihS1mzXPwT4E3LulaLTKAiGLL6EaMlJbtBat91pphIjFw289t9DVh4N7Jva9EKnWnpJ\n",
232 228 "G0RqBXcjCa08YCqy/PJE4L8A33b9HQPPeTNR/0bgvujzGchoywPSq5U+nd6R7fp7IDfRjYDrEE99\n",
233 229 "DeyHrPb5lO364xI36zTb2q4/AUnt/SSyLHQHMvJZklQOIhYChyCLid2FWBoGIQrDfwGnAP8Gskzd\n",
234 230 "VvSbBgPvIMdpJjLHuxdikXgg1ewa4Jbo84+BHRAFI/3gT9/QQZa+/iIy9zwccVQrSeA5nbbrX4s8\n",
235 231 "cI6htIIQK7xdFJLIAvEEYjmYBlyP/E4LeXj92Xb94YHnnFtOjhrYJ3q/vtbpE9v1fwqcjYxUL0GO\n",
236 232 "51bI//g1YIzt+mNTSgJIivfNEIXgBOThfx0ySv8Nct7vgzgfj0+1HQf8E5iPKM/vI+vLHA9cZbs+\n",
237 233 "JZSEevgDBZ++3yIKzgVI1FeSrCnD6ci0zebAJxCfjmoZjxzXPPBL5By0gW8jCt3sqHwtkYL1N0RB\n",
238 234 "/R2ymOG2yHE5CLFAHAu8ahQEQxbfyijrDdML3HTTkWvUBRfsb88bPb6TzjEK+oHKL184YHL+Jmdl\n",
239 235 "u+XrJsYBhwaec0dcYLu+hzw0dkcu/AvjbUmLgu36DqIgPB54zuQq9nURMgI8LjnyBibZrj8z2s/l\n",
240 236 "tuvvVcJJbWvkXDoi8JzbKu0s8JxFtut/IqXgAPzOdv0/IiPnb5KhICAjpMGIEjAhPV1iu35HWsbA\n",
241 237 "c25ObD8ZURAeqibENBqpTYnark8FBSHiakRBOMx2/cHpB29kSv4KooSlLRYnIcrBHcBXk7/Fdv0b\n",
242 238 "gReAM23Xvz7wnJlVyFIJK3qfXUsj2/U/jiiiq4B9ktEytuv/Fhlpfx2xEnw31XxHYLfAc6bbrv8k\n",
243 239 "cny/Bnwz8Jy/2q6/DTLd9F8Zu94ceXAeEHhOvM7MNbbrT0UU4vNs15+c2FY3gedcm/hNP0EUhDvL\n",
244 240 "KMrJtkuIFPboWNWiIOSAO4HDE7/Dj67FSxEn21+m2pyOWDpuCDxn7fG2Xf8e4F1EIVsceE5oohgM\n",
245 241 "XVBKjURuSEke11qXMhv3OPR553VO9Sb407yJZwTexO8FnnNV/qYj11XlAOCfSeUA1s4D/y36mp7f\n",
246 242 "rAvb9fdGLDMzU8pBzMXIg2wsMhLKQiFhgxWVg5gM5SDm+uh9VHqD7fr7IlaNFcAJWb4UPcHLPvCc\n",
247 243 "2YgVZn3gyIwq30AsQg8lQ+aiefUfR1/PzlB08sD9Udusfmsi2t+Q6GutjspnIE6L16dDaSN/irMR\n",
248 244 "p8dTbddPOxK/nwgxTZr8747e30SsEkNL7PvXGQrAVYgvwggK/gK9mXMyfuON0fvWkY9Dkp2i97uT\n",
249 245 "hYHnLKNgURsDxknRUMz5FJ8XP22DHIbqSc9pxsSOW8ObtJ89ovdXbNcvpQC8j4zcdiTbnAoy4q2b\n",
250 246 "6Ia3CYV5/Y0zqsXOf4/WEYveaq5GQuOOQaZekhydqJNkW2BLZF2UzhL/R+xE2XAIa+A52nb9lUho\n",
251 247 "Y63hd7GD5d1ZGwPPmW27/iuIUrkLXc/n9xP13rZd/yNgVezoF8n1NjAyyyKETGGl97fGdv1/IlaL\n",
252 248 "3h7e+06WM2PgOQtt11+GTMcNo6vVJ1aWsyK+4nvFQjAKgiGBUmoshfnOmGe11vdl1Tf0GOaUKI9v\n",
253 249 "lqrE9lqJb6b/Hb3KsU2Zba/VslPb9bdDfA0ORLz0N62iWWxVqMkc3iZuRuawP2u7/g6JKI9RSCTR\n",
254 250 "YoodhOP/YgNKK2Ix2zZJzjnINMN2NbaL/4uiaIUE/0EUhB3pqiCkMwl2IscjXZZFJ/B2iW1xRtWR\n",
255 251 "ZWTqDcwps63U9f8Q0TSN7fp/iK0PtuvviPjmrCHyR1qrICilNkTmHjZDLsDke/JzOtwnzY1KqXcR\n",
256 252 "R4cFiBab9XlRT87I19dQSo1GNPz0tJOxHvR8mhrOVobB0XuAOBiWo1zmwaqdXW3X3x+4BzGVv4SM\n",
257 253 "pN9AnPEg21McxMIArTs2dRN4zoe26/8NOA6xGJwfbYqV9b8GnrM81Sz+Lz5A0qOXo2y4Ww3MoT4F\n",
258 254 "IY4+KTfNF58TaXN4VthstVNDitLKcdxvOjKmEj0tv0M953fs87E3Eul0B2JliBflOzfwnFcA+iul\n",
259 255 "5iEmwQFNEBaK569L0amUWggcqrXO8gg2FKHG2CdW4Uem9XvBlUflu7RUaiByU3lPa92ZKN8cSav8\n",
260 256 "fUQBTHKr1rrqueIsxp18/eg1azrLjSYB6NfRsY3G6Is9nDjDYxh4zundvbMotvtm5N50duA5P09t\n",
261 257 "T0faJIkfirU+zNrF1YiC4FBQECZE73/JqB//F+u14r+ImIVEOB1iu/6ZNfhwzEamp7YuU2e7RN1m\n",
262 258 "oZBnW5YVIfZ1qNWfotw51yuIph++hET0bAkcikwpTAEuCjxnSly3PzIP0a8NcnYgD6SBlSoaIhQX\n",
263 259 "V2UtVup24LBU6S7IyG+NUuodZP52awojrTSvIjeshlij9XdQKh2jXYRRDtpGfOCruQfEpmzbdn0V\n",
264 260 "dP9iPLsgjnEryI67Lzd/PCt6/5Tt+v3LJXAqQ/z7ut2ZO/Ccx23XfxUYZbt+7D8xCngl8Jwsa80s\n",
265 261 "ZBS8ke36O7cg4ybA5UgegJ0QE/XN5auvZRaiIMQRF12wXX8TCv9ls6eERpOtIMR+EXNS5YsRh8dS\n",
266 262 "To/V+CzUck21i6uR5++4wHNeKFXJRDH0PfoR5fqmtHKwDDhCa73O5JA3lCSeF04v6Z3FPRTMzBO7\n",
267 263 "S6AE8Q12PbomgYn5Xpm29yMPhu2RUK96iKMn9q6zfa38JXo/NHoly7oQeM5K4Iro60+jKINuJVJC\n",
268 264 "Yu/439uuX805A4VkWyfbrp+V/MdFnOmeCmpfFKsSRYMc2/U/DeyG3OfSjpOx5WmfVHmcuXFcFfus\n",
269 265 "5ZpqObbrb45EtswqpxyAcVI0FDMbOFxrXeT9a+heopvnEArzolvashT0wmbEapdgGpIU5XDb9R9F\n",
270 266 "YqrXQyyL8wPPeTeuGHjOMtv1T0VuqldH6W//jigNmyHOcAcBgwPPcZog20xkRLcJ8DPb9S9CRqM7\n",
271 267 "I7kDvoDE1hfdxwLPWWy7/plI7oCLbNffHXm4zUQeRtsjGRP/EXhOKSfcABkpj49i5+9G/putgHmB\n",
272 268 "5yxIN4iSF21C14V6Rtiu/yYSW15uHv4a4P8oKAedlPcvOAv4KmItfCTKKfAS8v8NR1ILHwnsl5GA\n",
273 269 "qF7ORdYaGA48HGWyfBqYgViDRwCfQR72PkDgOU9E2TvHI4m0TgeeRczb30DyH2iKcyA0ymrgWNv1\n",
274 270 "FyDK1NvIQ3tStN3LCH+9HUl29UPb9echFo8BUbtLEKfJtJ9EmgA59ifbrj8bCR3cGDlvZqdTLcPa\n",
275 271 "9NCbUMhs2GFLKvPFSAKxZl7/CxEL8pgoA+QMxD+kE3HenAHcHnjOGmNB6Dt8iGjHWSFKK4HHkcQr\n",
276 272 "OxvloLXYrr+77fqrEIejNyiE6P0WccZbabv+lFLtG+Ry5AY/BHkYfRDtR9M79QAAA3FJREFUcwYS\n",
277 273 "NdCFwHPuQR6a7wHfAR5GMhk+i9xcT6G6KIOKBJ6zFBn9r0GUmBlIWN9ziHf/5yjO/phsfy2yqt4i\n",
278 274 "xOJxF3INTI9k/Q7ZoV4xv0PC5LZCci4sQm6g08kYHdquvxy5lt4DwsSmF5EENCts1//Idv3M9LbR\n",
279 275 "egJTkEx4NvBA1joFifqLIjkeR6wcfwdeQfIFTEEcjHNU79RXkShvw95Ixs5+yOj/KuSh+ATiAHcq\n",
280 276 "xb4fxwOXRfJMQc6zlxGF6B3g4MBznmmWnBFzEUfP0xDFcCGiAG+JHKushESXIdanjRBF4l3EInAj\n",
281 277 "8vuOqWK/5yNRGaOQFNkfIhkOX6CQgwAA2/W3jkI3V0T7ejjatAFyXb2PXP/LbVnroWGi6bbzo697\n",
282 278 "IlaWk5Br93wkk+jztusP7o94Lna7eaoMZU0cVXIAped7eqGZfP2ZqmPFl+ptrVf3n19UpvVMYLRS\n",
283 279 "agBywxuEjLwWAe9qrTMXV2mUzs7OP/Xrp+6qt33Hmn5Zue3XNeZTOVoky5nqKiQkrNT883Qk3WvJ\n",
284 280 "sMLAc1bbrv9Z5AH6KWRkOB+5wRWlWo7a3Ga7/mOIomAho/GFyI30YeDREru7ELlOq07TG3jONbbr\n",
285 281 "T0Nu9KOQm+i/gFsDz3nTdv2fI2FbpdpfHnlpH4LcnHdAlIz5yLErqXgFnvOR7fo28lDYE7lu3kKO\n",
286 282 "TdZ9K52xrhTl7knnUVB6SqVeTsr4apQU6lDEbG4hCsFbROsRBE1ebjrwnNB2/XGIGf5gRBkYhPyv\n",
287 283 "7yDpjR9MtVkOnGK7/vWIgrFrVPcF4O8ZKbaXIuduWkH6KfL/JbkEsWClfWK2CDzHt10/jzhXjkGO\n",
288 284 "yzNIZEiRD00ga3ocaLv+kUh2xo8hSuVURKmIUyiXVGYCWVzKQlJD7xrJNg85b9LX8RLgF6X6SpFU\n",
289 285 "9Cpe28gaJgORqEEAbNffDLlvHIQoAndR8NEYilwjExD/nwuUiTQ0GAwGw7qC7fqjEUvKqsBzmhWd\n",
290 286 "t05gu/5pyNoifw48J9N5PForxQeeNFMMBoPBYDD0DWL/llvK1In9jt4zCoLBYDAYDH2DePo5MwrJ\n",
291 287 "dv0hFPwTnjBRDAaDwWAw9A3+hPgOHRPl25iK+FhsiuR4OARx0Lwf+J1REAwGg8Fg6AMEnvNklL78\n",
292 288 "HMRRca/E5hVINNIVwI2B56z6/3ExLRI31pXNAAAAAElFTkSuQmCC\n"
293 289 ],
294 290 "text/plain": [
295 291 "<IPython.core.display.Image at 0x111275490>"
296 292 ]
297 293 },
298 294 "execution_count": 6,
299 295 "metadata": {},
300 296 "output_type": "execute_result"
301 297 }
302 298 ],
303 299 "source": [
304 300 "from IPython.display import Image\n",
305 301 "Image(\"http://ipython.org/_static/IPy_header.png\")"
306 302 ]
307 303 }
308 304 ],
309 305 "metadata": {},
310 306 "nbformat": 4,
311 307 "nbformat_minor": 0
312 308 } No newline at end of file
@@ -1,57 +1,57 b''
1 1 """Test nbformat.validator"""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import os
7 7
8 8 from .base import TestsBase
9 9 from jsonschema import ValidationError
10 10 from ..current import read
11 11 from ..validator import isvalid, validate
12 12
13 13
14 14 class TestValidator(TestsBase):
15 15
16 16 def test_nb2(self):
17 17 """Test that a v2 notebook converted to current passes validation"""
18 18 with self.fopen(u'test2.ipynb', u'r') as f:
19 19 nb = read(f, u'json')
20 20 validate(nb)
21 21 self.assertEqual(isvalid(nb), True)
22 22
23 23 def test_nb3(self):
24 24 """Test that a v3 notebook passes validation"""
25 25 with self.fopen(u'test3.ipynb', u'r') as f:
26 26 nb = read(f, u'json')
27 27 validate(nb)
28 28 self.assertEqual(isvalid(nb), True)
29 29
30 30 def test_nb4(self):
31 """Test that a v3 notebook passes validation"""
31 """Test that a v4 notebook passes validation"""
32 32 with self.fopen(u'test4.ipynb', u'r') as f:
33 33 nb = read(f, u'json')
34 34 validate(nb)
35 35 self.assertEqual(isvalid(nb), True)
36 36
37 37 def test_invalid(self):
38 38 """Test than an invalid notebook does not pass validation"""
39 39 # this notebook has a few different errors:
40 # - the name is an integer, rather than a string
41 40 # - one cell is missing its source
42 # - one cell has an invalid level
41 # - invalid cell type
42 # - invalid output_type
43 43 with self.fopen(u'invalid.ipynb', u'r') as f:
44 44 nb = read(f, u'json')
45 45 with self.assertRaises(ValidationError):
46 46 validate(nb)
47 47 self.assertEqual(isvalid(nb), False)
48 48
49 49 def test_future(self):
50 50 """Test than a notebook from the future with extra keys passes validation"""
51 51 with self.fopen(u'test3plus.ipynb', u'r') as f:
52 52 nb = read(f)
53 53 with self.assertRaises(ValidationError):
54 54 validate(nb, version=3)
55 55
56 56 self.assertEqual(isvalid(nb, version=3), False)
57 57 self.assertEqual(isvalid(nb), True)
@@ -1,19 +1,19 b''
1 1 """The main API for the v4 notebook format."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from .nbbase import (
7 7 NotebookNode, from_dict,
8 8 nbformat, nbformat_minor, nbformat_schema,
9 new_code_cell, new_heading_cell, new_markdown_cell, new_notebook,
9 new_code_cell, new_markdown_cell, new_notebook,
10 10 new_output, output_from_msg,
11 11 )
12 12
13 13 from .nbjson import reads as reads_json, writes as writes_json
14 14 from .nbjson import reads as read_json, writes as write_json
15 15 from .nbjson import to_notebook as to_notebook_json
16 16
17 17 from .convert import downgrade, upgrade
18 18
19 19
@@ -1,230 +1,248 b''
1 1 """Code for converting notebooks to and from v3."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import json
7 import re
7 8
8 9 from .nbbase import (
9 10 nbformat, nbformat_minor,
10 11 NotebookNode,
11 12 )
12 13
13 14 from IPython.nbformat import v3
14 15 from IPython.utils.log import get_logger
15 16
16 17 def _warn_if_invalid(nb, version):
17 18 """Log validation errors, if there are any."""
18 19 from IPython.nbformat.current import validate, ValidationError
19 20 try:
20 21 validate(nb, version=version)
21 22 except ValidationError as e:
22 23 get_logger().error("Notebook JSON is not valid v%i: %s", version, e)
23 24
24 25 def upgrade(nb, from_version=3, from_minor=0):
25 26 """Convert a notebook to v4.
26 27
27 28 Parameters
28 29 ----------
29 30 nb : NotebookNode
30 31 The Python representation of the notebook to convert.
31 32 from_version : int
32 33 The original version of the notebook to convert.
33 34 from_minor : int
34 35 The original minor version of the notebook to convert (only relevant for v >= 3).
35 36 """
36 37 if from_version == 3:
37 38 # Validate the notebook before conversion
38 39 _warn_if_invalid(nb, from_version)
39 40
40 41 # Mark the original nbformat so consumers know it has been converted
41 42 orig_nbformat = nb.pop('orig_nbformat', None)
42 43 nb.metadata.orig_nbformat = orig_nbformat or 3
43 44
44 45 # Mark the new format
45 46 nb.nbformat = nbformat
46 47 nb.nbformat_minor = nbformat_minor
47 48
48 49 # remove worksheet(s)
49 50 nb['cells'] = cells = []
50 51 # In the unlikely event of multiple worksheets,
51 52 # they will be flattened
52 53 for ws in nb.pop('worksheets', []):
53 54 # upgrade each cell
54 55 for cell in ws['cells']:
55 56 cells.append(upgrade_cell(cell))
56 57 # upgrade metadata
57 58 nb.metadata.pop('name', '')
58 59 # Validate the converted notebook before returning it
59 60 _warn_if_invalid(nb, nbformat)
60 61 return nb
61 62 elif from_version == 4:
62 63 # nothing to do
63 64 if from_minor != nbformat_minor:
64 65 nb.metadata.orig_nbformat_minor = from_minor
65 66 nb.nbformat_minor = nbformat_minor
66 67
67 68 return nb
68 69 else:
69 70 raise ValueError('Cannot convert a notebook directly from v%s to v4. ' \
70 71 'Try using the IPython.nbformat.convert module.' % from_version)
71 72
72 73 def upgrade_cell(cell):
73 74 """upgrade a cell from v3 to v4
74 75
76 heading cell -> markdown heading
75 77 code cell:
76 78 - remove language metadata
77 79 - cell.input -> cell.source
78 80 - cell.prompt_number -> cell.execution_count
79 81 - update outputs
80 82 """
81 83 cell.setdefault('metadata', NotebookNode())
82 84 if cell.cell_type == 'code':
83 85 cell.pop('language', '')
84 86 cell.metadata.collapsed = cell.pop('collapsed')
85 cell.source = cell.pop('input')
87 cell.source = cell.pop('input', '')
86 88 cell.execution_count = cell.pop('prompt_number', None)
87 89 cell.outputs = upgrade_outputs(cell.outputs)
90 elif cell.cell_type == 'heading':
91 cell.cell_type = 'markdown'
92 level = cell.pop('level', 1)
93 cell.source = '{hashes} {single_line}'.format(
94 hashes='#' * level,
95 single_line = ' '.join(cell.get('source', '').splitlines()),
96 )
88 97 elif cell.cell_type == 'html':
89 98 # Technically, this exists. It will never happen in practice.
90 99 cell.cell_type = 'markdown'
91 100 return cell
92 101
93 102 def downgrade_cell(cell):
94 103 """downgrade a cell from v4 to v3
95 104
96 105 code cell:
97 106 - set cell.language
98 107 - cell.input <- cell.source
99 108 - cell.prompt_number <- cell.execution_count
100 109 - update outputs
110 markdown cell:
111 - single-line heading -> heading cell
101 112 """
102 113 if cell.cell_type == 'code':
103 114 cell.language = 'python'
104 115 cell.input = cell.pop('source', '')
105 116 cell.prompt_number = cell.pop('execution_count', None)
106 117 cell.collapsed = cell.metadata.pop('collapsed', False)
107 118 cell.outputs = downgrade_outputs(cell.outputs)
119 elif cell.cell_type == 'markdown':
120 source = cell.get('source', '')
121 if '\n' not in source and source.startswith('#'):
122 prefix, text = re.match(r'(#+)\s*(.*)', source).groups()
123 cell.cell_type = 'heading'
124 cell.source = text
125 cell.level = len(prefix)
108 126 return cell
109 127
110 128 _mime_map = {
111 129 "text" : "text/plain",
112 130 "html" : "text/html",
113 131 "svg" : "image/svg+xml",
114 132 "png" : "image/png",
115 133 "jpeg" : "image/jpeg",
116 134 "latex" : "text/latex",
117 135 "json" : "application/json",
118 136 "javascript" : "application/javascript",
119 137 };
120 138
121 139 def to_mime_key(d):
122 140 """convert dict with v3 aliases to plain mime-type keys"""
123 141 for alias, mime in _mime_map.items():
124 142 if alias in d:
125 143 d[mime] = d.pop(alias)
126 144 return d
127 145
128 146 def from_mime_key(d):
129 147 """convert dict with mime-type keys to v3 aliases"""
130 148 for alias, mime in _mime_map.items():
131 149 if mime in d:
132 150 d[alias] = d.pop(mime)
133 151 return d
134 152
135 153 def upgrade_output(output):
136 154 """upgrade a single code cell output from v3 to v4
137 155
138 156 - pyout -> execute_result
139 157 - pyerr -> error
140 158 - output.type -> output.data.mime/type
141 159 - mime-type keys
142 160 - stream.stream -> stream.name
143 161 """
144 162 if output['output_type'] in {'pyout', 'display_data'}:
145 163 output.setdefault('metadata', NotebookNode())
146 164 if output['output_type'] == 'pyout':
147 165 output['output_type'] = 'execute_result'
148 166 output['execution_count'] = output.pop('prompt_number', None)
149 167
150 168 # move output data into data sub-dict
151 169 data = {}
152 170 for key in list(output):
153 171 if key in {'output_type', 'execution_count', 'metadata'}:
154 172 continue
155 173 data[key] = output.pop(key)
156 174 to_mime_key(data)
157 175 output['data'] = data
158 176 to_mime_key(output.metadata)
159 177 if 'application/json' in data:
160 178 data['application/json'] = json.loads(data['application/json'])
161 179 # promote ascii bytes (from v2) to unicode
162 180 for key in ('image/png', 'image/jpeg'):
163 181 if key in data and isinstance(data[key], bytes):
164 182 data[key] = data[key].decode('ascii')
165 183 elif output['output_type'] == 'pyerr':
166 184 output['output_type'] = 'error'
167 185 elif output['output_type'] == 'stream':
168 186 output['name'] = output.pop('stream')
169 187 return output
170 188
171 189 def downgrade_output(output):
172 190 """downgrade a single code cell output to v3 from v4
173 191
174 192 - pyout <- execute_result
175 193 - pyerr <- error
176 194 - output.data.mime/type -> output.type
177 195 - un-mime-type keys
178 196 - stream.stream <- stream.name
179 197 """
180 198 if output['output_type'] in {'execute_result', 'display_data'}:
181 199 if output['output_type'] == 'execute_result':
182 200 output['output_type'] = 'pyout'
183 201 output['prompt_number'] = output.pop('execution_count', None)
184 202
185 203 # promote data dict to top-level output namespace
186 204 data = output.pop('data', {})
187 205 if 'application/json' in data:
188 206 data['application/json'] = json.dumps(data['application/json'])
189 207 from_mime_key(data)
190 208 output.update(data)
191 209 from_mime_key(output.get('metadata', {}))
192 210 elif output['output_type'] == 'error':
193 211 output['output_type'] = 'pyerr'
194 212 elif output['output_type'] == 'stream':
195 213 output['stream'] = output.pop('name')
196 214 return output
197 215
198 216 def upgrade_outputs(outputs):
199 217 """upgrade outputs of a code cell from v3 to v4"""
200 218 return [upgrade_output(op) for op in outputs]
201 219
202 220 def downgrade_outputs(outputs):
203 221 """downgrade outputs of a code cell to v3 from v4"""
204 222 return [downgrade_output(op) for op in outputs]
205 223
206 224 def downgrade(nb):
207 225 """Convert a v4 notebook to v3.
208 226
209 227 Parameters
210 228 ----------
211 229 nb : NotebookNode
212 230 The Python representation of the notebook to convert.
213 231 """
214 232 if nb.nbformat != nbformat:
215 233 return nb
216 234
217 235 # Validate the notebook before conversion
218 236 _warn_if_invalid(nb, nbformat)
219 237
220 238 nb.nbformat = v3.nbformat
221 239 nb.nbformat_minor = v3.nbformat_minor
222 240 cells = [ downgrade_cell(cell) for cell in nb.pop('cells') ]
223 241 nb.worksheets = [v3.new_worksheet(cells=cells)]
224 242 nb.metadata.setdefault('name', '')
225 243 nb.metadata.pop('orig_nbformat', None)
226 244 nb.metadata.pop('orig_nbformat_minor', None)
227 245
228 246 # Validate the converted notebook before returning it
229 247 _warn_if_invalid(nb, v3.nbformat)
230 248 return nb
@@ -1,151 +1,141 b''
1 1 """Python API for composing notebook elements
2 2
3 3 The Python representation of a notebook is a nested structure of
4 4 dictionary subclasses that support attribute access
5 5 (IPython.utils.ipstruct.Struct). The functions in this module are merely
6 6 helpers to build the structs in the right form.
7 7 """
8 8
9 9 # Copyright (c) IPython Development Team.
10 10 # Distributed under the terms of the Modified BSD License.
11 11
12 12 from IPython.utils.ipstruct import Struct
13 13
14 14 # Change this when incrementing the nbformat version
15 15 nbformat = 4
16 16 nbformat_minor = 0
17 17 nbformat_schema = 'nbformat.v4.schema.json'
18 18
19 19
20 20 def validate(node, ref=None):
21 21 """validate a v4 node"""
22 22 from ..current import validate
23 23 return validate(node, ref=ref, version=nbformat)
24 24
25 25
26 26 class NotebookNode(Struct):
27 27 pass
28 28
29 29 def from_dict(d):
30 30 if isinstance(d, dict):
31 31 newd = NotebookNode()
32 32 for k,v in d.items():
33 33 newd[k] = from_dict(v)
34 34 return newd
35 35 elif isinstance(d, (tuple, list)):
36 36 return [from_dict(i) for i in d]
37 37 else:
38 38 return d
39 39
40 40
41 41 def new_output(output_type, data=None, **kwargs):
42 42 """Create a new output, to go in the ``cell.outputs`` list of a code cell."""
43 43 output = NotebookNode(output_type=output_type)
44 44 output.update(from_dict(kwargs))
45 45 if data is not None:
46 46 output.data = from_dict(data)
47 47
48 48 # populate defaults:
49 49 if output_type == 'stream':
50 50 output.setdefault('name', 'stdout')
51 51 output.setdefault('text', '')
52 52 elif output_type in {'execute_result', 'display_data'}:
53 53 output.setdefault('metadata', NotebookNode())
54 54 output.setdefault('data', NotebookNode())
55 55 validate(output, output_type)
56 56 return output
57 57
58 58
59 59 def output_from_msg(msg):
60 60 """Create a NotebookNode for an output from a kernel's IOPub message.
61 61
62 62 Returns
63 63 -------
64 64
65 65 NotebookNode: the output as a notebook node.
66 66
67 67 Raises
68 68 ------
69 69
70 70 ValueError: if the message is not an output message.
71 71
72 72 """
73 73 msg_type = msg['header']['msg_type']
74 74 content = msg['content']
75 75
76 76 if msg_type == 'execute_result':
77 77 return new_output(output_type=msg_type,
78 78 metadata=content['metadata'],
79 79 data=content['data'],
80 80 execution_count=content['execution_count'],
81 81 )
82 82
83 83 elif msg_type == 'stream':
84 84 return new_output(output_type=msg_type,
85 85 name=content['name'],
86 86 text=content['text'],
87 87 )
88 88 elif msg_type == 'display_data':
89 89 return new_output(output_type=msg_type,
90 90 metadata=content['metadata'],
91 91 data=content['data'],
92 92 )
93 93 elif msg_type == 'error':
94 94 return new_output(output_type=msg_type,
95 95 ename=content['ename'],
96 96 evalue=content['evalue'],
97 97 traceback=content['traceback'],
98 98 )
99 99 else:
100 100 raise ValueError("Unrecognized output msg type: %r" % msg_type)
101 101
102 102
103 103 def new_code_cell(source='', **kwargs):
104 104 """Create a new code cell"""
105 105 cell = NotebookNode(cell_type='code', source=source)
106 106 cell.update(from_dict(kwargs))
107 107 cell.setdefault('metadata', NotebookNode())
108 108 cell.setdefault('source', '')
109 109 cell.setdefault('execution_count', None)
110 110 cell.setdefault('outputs', [])
111 111
112 112 validate(cell, 'code_cell')
113 113 return cell
114 114
115 115 def new_markdown_cell(source='', **kwargs):
116 116 """Create a new markdown cell"""
117 117 cell = NotebookNode(cell_type='markdown', source=source)
118 118 cell.update(from_dict(kwargs))
119 119 cell.setdefault('metadata', NotebookNode())
120 120
121 121 validate(cell, 'markdown_cell')
122 122 return cell
123 123
124 def new_heading_cell(source='', **kwargs):
125 """Create a new heading cell"""
126 cell = NotebookNode(cell_type='heading', source=source)
127 cell.update(from_dict(kwargs))
128 cell.setdefault('metadata', NotebookNode())
129 cell.setdefault('level', 1)
130
131 validate(cell, 'heading_cell')
132 return cell
133
134 124 def new_raw_cell(source='', **kwargs):
135 125 """Create a new raw cell"""
136 126 cell = NotebookNode(cell_type='raw', source=source)
137 127 cell.update(from_dict(kwargs))
138 128 cell.setdefault('metadata', NotebookNode())
139 129
140 130 validate(cell, 'raw_cell')
141 131 return cell
142 132
143 133 def new_notebook(**kwargs):
144 134 """Create a new notebook"""
145 135 nb = from_dict(kwargs)
146 136 nb.nbformat = nbformat
147 137 nb.nbformat_minor = nbformat_minor
148 138 nb.setdefault('cells', [])
149 139 nb.setdefault('metadata', NotebookNode())
150 140 validate(nb)
151 141 return nb
@@ -1,337 +1,308 b''
1 1 {
2 2 "$schema": "http://json-schema.org/draft-04/schema#",
3 3 "description": "IPython Notebook v4.0 JSON schema.",
4 4 "type": "object",
5 5 "additionalProperties": false,
6 6 "required": ["metadata", "nbformat_minor", "nbformat", "cells"],
7 7 "properties": {
8 8 "metadata": {
9 9 "description": "Notebook root-level metadata.",
10 10 "type": "object",
11 11 "additionalProperties": true,
12 12 "properties": {
13 13 "kernel_info": {
14 14 "description": "Kernel information.",
15 15 "type": "object",
16 16 "required": ["name", "language"],
17 17 "properties": {
18 18 "name": {
19 19 "description": "Name of the kernel specification.",
20 20 "type": "string"
21 21 },
22 22 "language": {
23 23 "description": "The programming language which this kernel runs.",
24 24 "type": "string"
25 25 },
26 26 "codemirror_mode": {
27 27 "description": "The codemirror mode to use for code in this language.",
28 28 "type": "string"
29 29 }
30 30 }
31 31 },
32 32 "signature": {
33 33 "description": "Hash of the notebook.",
34 34 "type": "string"
35 35 },
36 36 "orig_nbformat": {
37 37 "description": "Original notebook format (major number) before converting the notebook between versions. This should never be written to a file.",
38 38 "type": "integer",
39 39 "minimum": 1
40 40 }
41 41 }
42 42 },
43 43 "nbformat_minor": {
44 44 "description": "Notebook format (minor number). Incremented for backward compatible changes to the notebook format.",
45 45 "type": "integer",
46 46 "minimum": 0
47 47 },
48 48 "nbformat": {
49 49 "description": "Notebook format (major number). Incremented between backwards incompatible changes to the notebook format.",
50 50 "type": "integer",
51 51 "minimum": 4,
52 52 "maximum": 4
53 53 },
54 54 "cells": {
55 55 "description": "Array of cells of the current notebook.",
56 56 "type": "array",
57 57 "items": {
58 58 "type": "object",
59 59 "oneOf": [
60 60 {"$ref": "#/definitions/raw_cell"},
61 61 {"$ref": "#/definitions/markdown_cell"},
62 {"$ref": "#/definitions/heading_cell"},
63 62 {"$ref": "#/definitions/code_cell"}
64 63 ]
65 64 }
66 65 }
67 66 },
68 67
69 68 "definitions": {
70 69
71 70 "raw_cell": {
72 71 "description": "Notebook raw nbconvert cell.",
73 72 "type": "object",
74 73 "additionalProperties": false,
75 74 "required": ["cell_type", "metadata", "source"],
76 75 "properties": {
77 76 "cell_type": {
78 77 "description": "String identifying the type of cell.",
79 78 "enum": ["raw"]
80 79 },
81 80 "metadata": {
82 81 "description": "Cell-level metadata.",
83 82 "type": "object",
84 83 "additionalProperties": true,
85 84 "properties": {
86 85 "format": {
87 86 "description": "Raw cell metadata format for nbconvert.",
88 87 "type": "string"
89 88 },
90 89 "name": {"$ref": "#/definitions/misc/metadata_name"},
91 90 "tags": {"$ref": "#/definitions/misc/metadata_tags"}
92 91 }
93 92 },
94 93 "source": {"$ref": "#/definitions/misc/source"}
95 94 }
96 95 },
97 96
98 97 "markdown_cell": {
99 98 "description": "Notebook markdown cell.",
100 99 "type": "object",
101 100 "additionalProperties": false,
102 101 "required": ["cell_type", "metadata", "source"],
103 102 "properties": {
104 103 "cell_type": {
105 104 "description": "String identifying the type of cell.",
106 105 "enum": ["markdown"]
107 106 },
108 107 "metadata": {
109 108 "description": "Cell-level metadata.",
110 109 "type": "object",
111 110 "properties": {
112 111 "name": {"$ref": "#/definitions/misc/metadata_name"},
113 112 "tags": {"$ref": "#/definitions/misc/metadata_tags"}
114 113 },
115 114 "additionalProperties": true
116 115 },
117 116 "source": {"$ref": "#/definitions/misc/source"}
118 117 }
119 118 },
120 119
121 "heading_cell": {
122 "description": "Notebook heading cell.",
123 "type": "object",
124 "additionalProperties": false,
125 "required": ["cell_type", "metadata", "source", "level"],
126 "properties": {
127 "cell_type": {
128 "description": "String identifying the type of cell.",
129 "enum": ["heading"]
130 },
131 "metadata": {
132 "description": "Cell-level metadata.",
133 "type": "object",
134 "properties": {
135 "name": {"$ref": "#/definitions/misc/metadata_name"},
136 "tags": {"$ref": "#/definitions/misc/metadata_tags"}
137 },
138 "additionalProperties": true
139 },
140 "source": {"$ref": "#/definitions/misc/source"},
141 "level": {
142 "description": "Level of heading cells.",
143 "type": "integer",
144 "minimum": 1
145 }
146 }
147 },
148
149 120 "code_cell": {
150 121 "description": "Notebook code cell.",
151 122 "type": "object",
152 123 "additionalProperties": false,
153 124 "required": ["cell_type", "metadata", "source", "outputs", "execution_count"],
154 125 "properties": {
155 126 "cell_type": {
156 127 "description": "String identifying the type of cell.",
157 128 "enum": ["code"]
158 129 },
159 130 "metadata": {
160 131 "description": "Cell-level metadata.",
161 132 "type": "object",
162 133 "additionalProperties": true,
163 134 "properties": {
164 135 "collapsed": {
165 136 "description": "Whether the cell is collapsed/expanded.",
166 137 "type": "boolean"
167 138 },
168 139 "autoscroll": {
169 140 "description": "Whether the cell's output is scrolled, unscrolled, or autoscrolled.",
170 141 "enum": [true, false, "auto"]
171 142 },
172 143 "name": {"$ref": "#/definitions/misc/metadata_name"},
173 144 "tags": {"$ref": "#/definitions/misc/metadata_tags"}
174 145 }
175 146 },
176 147 "source": {"$ref": "#/definitions/misc/source"},
177 148 "outputs": {
178 149 "description": "Execution, display, or stream outputs.",
179 150 "type": "array",
180 151 "items": {"$ref": "#/definitions/output"}
181 152 },
182 153 "execution_count": {
183 154 "description": "The code cell's prompt number. Will be null if the cell has not been run.",
184 155 "type": ["integer", "null"],
185 156 "minimum": 0
186 157 }
187 158 }
188 159 },
189 160 "output": {
190 161 "type": "object",
191 162 "oneOf": [
192 163 {"$ref": "#/definitions/execute_result"},
193 164 {"$ref": "#/definitions/display_data"},
194 165 {"$ref": "#/definitions/stream"},
195 166 {"$ref": "#/definitions/error"}
196 167 ]
197 168 },
198 169
199 170 "execute_result": {
200 171 "description": "Result of executing a code cell.",
201 172 "type": "object",
202 173 "additionalProperties": false,
203 174 "required": ["output_type", "data", "metadata", "execution_count"],
204 175 "properties": {
205 176 "output_type": {
206 177 "description": "Type of cell output.",
207 178 "enum": ["execute_result"]
208 179 },
209 180 "execution_count": {
210 181 "description": "A result's prompt number.",
211 182 "type": ["integer", "null"],
212 183 "minimum": 0
213 184 },
214 185 "data": {"$ref": "#/definitions/misc/mimebundle"},
215 186 "metadata": {"$ref": "#/definitions/misc/output_metadata"}
216 187 }
217 188 },
218 189
219 190 "display_data": {
220 191 "description": "Data displayed as a result of code cell execution.",
221 192 "type": "object",
222 193 "additionalProperties": false,
223 194 "required": ["output_type", "data", "metadata"],
224 195 "properties": {
225 196 "output_type": {
226 197 "description": "Type of cell output.",
227 198 "enum": ["display_data"]
228 199 },
229 200 "data": {"$ref": "#/definitions/misc/mimebundle"},
230 201 "metadata": {"$ref": "#/definitions/misc/output_metadata"}
231 202 }
232 203 },
233 204
234 205 "stream": {
235 206 "description": "Stream output from a code cell.",
236 207 "type": "object",
237 208 "additionalProperties": false,
238 209 "required": ["output_type", "name", "text"],
239 210 "properties": {
240 211 "output_type": {
241 212 "description": "Type of cell output.",
242 213 "enum": ["stream"]
243 214 },
244 215 "name": {
245 216 "description": "The name of the stream (stdout, stderr).",
246 217 "type": "string"
247 218 },
248 219 "text": {
249 220 "description": "The stream's text output, represented as an array of strings.",
250 221 "$ref": "#/definitions/misc/multiline_string"
251 222 }
252 223 }
253 224 },
254 225
255 226 "error": {
256 227 "description": "Output of an error that occurred during code cell execution.",
257 228 "type": "object",
258 229 "additionalProperties": false,
259 230 "required": ["output_type", "ename", "evalue", "traceback"],
260 231 "properties": {
261 232 "output_type": {
262 233 "description": "Type of cell output.",
263 234 "enum": ["error"]
264 235 },
265 236 "ename": {
266 237 "description": "The name of the error.",
267 238 "type": "string"
268 239 },
269 240 "evalue": {
270 241 "description": "The value, or message, of the error.",
271 242 "type": "string"
272 243 },
273 244 "traceback": {
274 245 "description": "The error's traceback, represented as an array of strings.",
275 246 "type": "array",
276 247 "items": {"type": "string"}
277 248 }
278 249 }
279 250 },
280 251
281 252 "misc": {
282 253 "metadata_name": {
283 254 "description": "The cell's name. If present, must be a non-empty string.",
284 255 "type": "string",
285 256 "pattern": "^.+$"
286 257 },
287 258 "metadata_tags": {
288 259 "description": "The cell's tags. Tags must be unique, and must not contain commas.",
289 260 "type": "array",
290 261 "uniqueItems": true,
291 262 "items": {
292 263 "type": "string",
293 264 "pattern": "^[^,]+$"
294 265 }
295 266 },
296 267 "source": {
297 268 "description": "Contents of the cell, represented as an array of lines.",
298 269 "$ref": "#/definitions/misc/multiline_string"
299 270 },
300 271 "execution_count": {
301 272 "description": "The code cell's prompt number. Will be null if the cell has not been run.",
302 273 "type": ["integer", "null"],
303 274 "minimum": 0
304 275 },
305 276 "mimebundle": {
306 277 "description": "A mime-type keyed dictionary of data",
307 278 "type": "object",
308 279 "additionalProperties": false,
309 280 "properties": {
310 281 "application/json": {
311 282 "type": "object"
312 283 }
313 284 },
314 285 "patternProperties": {
315 286 "^(?!application/json$)[a-zA-Z0-9]+/[a-zA-Z0-9\\-\\+\\.]+$": {
316 287 "description": "mimetype output (e.g. text/plain), represented as either an array of strings or a string.",
317 288 "$ref": "#/definitions/misc/multiline_string"
318 289 }
319 290 }
320 291 },
321 292 "output_metadata": {
322 293 "description": "Cell output metadata.",
323 294 "type": "object",
324 295 "additionalProperties": true
325 296 },
326 297 "multiline_string": {
327 298 "oneOf" : [
328 299 {"type": "string"},
329 300 {
330 301 "type": "array",
331 302 "items": {"type": "string"}
332 303 }
333 304 ]
334 305 }
335 306 }
336 307 }
337 308 }
@@ -1,124 +1,125 b''
1 1 """Base classes and utilities for readers and writers."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from IPython.utils import py3compat
7 7 from IPython.utils.py3compat import unicode_type, string_types
8 8
9 9 # output keys that are likely to have multiline values
10 10 _multiline_outputs = {
11 11 'text/plain',
12 12 'text/html',
13 13 'image/svg+xml',
14 14 'text/latex',
15 15 'application/javascript',
16 16 }
17 17
18 18
19 19 # FIXME: workaround for old splitlines()
20 20 def _join_lines(lines):
21 21 """join lines that have been written by splitlines()
22 22
23 23 Has logic to protect against `splitlines()`, which
24 24 should have been `splitlines(True)`
25 25 """
26 26 if lines and lines[0].endswith(('\n', '\r')):
27 27 # created by splitlines(True)
28 28 return u''.join(lines)
29 29 else:
30 30 # created by splitlines()
31 31 return u'\n'.join(lines)
32 32
33 33
34 34 def rejoin_lines(nb):
35 35 """rejoin multiline text into strings
36 36
37 37 For reversing effects of ``split_lines(nb)``.
38 38
39 39 This only rejoins lines that have been split, so if text objects were not split
40 40 they will pass through unchanged.
41 41
42 42 Used when reading JSON files that may have been passed through split_lines.
43 43 """
44 44 for cell in nb.cells:
45 45 if 'source' in cell and isinstance(cell.source, list):
46 46 cell.source = _join_lines(cell.source)
47 if cell.cell_type == 'code':
48 for output in cell.outputs:
49 if output.output_type in {'execute_result', 'display_data'}:
50 for key, value in output.data.items():
47 if cell.get('cell_type', None) == 'code':
48 for output in cell.get('outputs', []):
49 output_type = output.get('output_type', '')
50 if output_type in {'execute_result', 'display_data'}:
51 for key, value in output.get('data', {}).items():
51 52 if key != 'application/json' and isinstance(value, list):
52 53 output.data[key] = _join_lines(value)
53 elif output.output_type == 'stream':
54 if isinstance(output.text, list):
54 elif output_type:
55 if isinstance(output.get('text', ''), list):
55 56 output.text = _join_lines(output.text)
56 57 return nb
57 58
58 59
59 60 def split_lines(nb):
60 61 """split likely multiline text into lists of strings
61 62
62 63 For file output more friendly to line-based VCS. ``rejoin_lines(nb)`` will
63 64 reverse the effects of ``split_lines(nb)``.
64 65
65 66 Used when writing JSON files.
66 67 """
67 68 for cell in nb.cells:
68 69 source = cell.get('source', None)
69 70 if isinstance(source, string_types):
70 71 cell['source'] = source.splitlines(True)
71 72
72 73 if cell.cell_type == 'code':
73 74 for output in cell.outputs:
74 75 if output.output_type in {'execute_result', 'display_data'}:
75 76 for key, value in output.data.items():
76 77 if key != 'application/json' and isinstance(value, string_types):
77 78 output.data[key] = value.splitlines(True)
78 79 elif output.output_type == 'stream':
79 80 if isinstance(output.text, string_types):
80 81 output.text = output.text.splitlines(True)
81 82 return nb
82 83
83 84
84 85 def strip_transient(nb):
85 86 """Strip transient values that shouldn't be stored in files.
86 87
87 88 This should be called in *both* read and write.
88 89 """
89 90 nb.metadata.pop('orig_nbformat', None)
90 91 nb.metadata.pop('orig_nbformat_minor', None)
91 92 for cell in nb.cells:
92 93 cell.metadata.pop('trusted', None)
93 94 return nb
94 95
95 96
96 97 class NotebookReader(object):
97 98 """A class for reading notebooks."""
98 99
99 100 def reads(self, s, **kwargs):
100 101 """Read a notebook from a string."""
101 102 raise NotImplementedError("loads must be implemented in a subclass")
102 103
103 104 def read(self, fp, **kwargs):
104 105 """Read a notebook from a file like object"""
105 106 nbs = fp.read()
106 107 if not py3compat.PY3 and not isinstance(nbs, unicode_type):
107 108 nbs = py3compat.str_to_unicode(nbs)
108 109 return self.reads(nbs, **kwargs)
109 110
110 111
111 112 class NotebookWriter(object):
112 113 """A class for writing notebooks."""
113 114
114 115 def writes(self, nb, **kwargs):
115 116 """Write a notebook to a string."""
116 117 raise NotImplementedError("loads must be implemented in a subclass")
117 118
118 119 def write(self, nb, fp, **kwargs):
119 120 """Write a notebook to a file like object"""
120 121 nbs = self.writes(nb,**kwargs)
121 122 if not py3compat.PY3 and not isinstance(nbs, unicode_type):
122 123 # this branch is likely only taken for JSON on Python 2
123 124 nbs = py3compat.str_to_unicode(nbs)
124 125 return fp.write(nbs)
@@ -1,105 +1,104 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 import os
4 4 from base64 import encodestring
5 5
6 6 from ..nbbase import (
7 new_code_cell, new_heading_cell, new_markdown_cell, new_notebook,
7 new_code_cell, new_markdown_cell, new_notebook,
8 8 new_output, new_raw_cell
9 9 )
10 10
11 11 # some random base64-encoded *text*
12 12 png = encodestring(os.urandom(5)).decode('ascii')
13 13 jpeg = encodestring(os.urandom(6)).decode('ascii')
14 14
15 15 cells = []
16 16 cells.append(new_markdown_cell(
17 17 source='Some NumPy Examples',
18 18 ))
19 19
20 20
21 21 cells.append(new_code_cell(
22 22 source='import numpy',
23 23 execution_count=1,
24 24 ))
25 25
26 26 cells.append(new_markdown_cell(
27 27 source='A random array',
28 28 ))
29 29
30 30 cells.append(new_raw_cell(
31 31 source='A random array',
32 32 ))
33 33
34 cells.append(new_heading_cell(
35 source=u'My Heading',
36 level=2,
34 cells.append(new_markdown_cell(
35 source=u'## My Heading',
37 36 ))
38 37
39 38 cells.append(new_code_cell(
40 39 source='a = numpy.random.rand(100)',
41 40 execution_count=2,
42 41 ))
43 42 cells.append(new_code_cell(
44 43 source='a = 10\nb = 5\n',
45 44 execution_count=3,
46 45 ))
47 46 cells.append(new_code_cell(
48 47 source='a = 10\nb = 5',
49 48 execution_count=4,
50 49 ))
51 50
52 51 cells.append(new_code_cell(
53 52 source=u'print "ünîcødé"',
54 53 execution_count=3,
55 54 outputs=[new_output(
56 55 output_type=u'execute_result',
57 56 data={
58 57 'text/plain': u'<array a>',
59 58 'text/html': u'The HTML rep',
60 59 'text/latex': u'$a$',
61 60 'image/png': png,
62 61 'image/jpeg': jpeg,
63 62 'image/svg+xml': u'<svg>',
64 63 'application/json': {
65 64 'key': 'value'
66 65 },
67 66 'application/javascript': u'var i=0;'
68 67 },
69 68 execution_count=3
70 69 ),new_output(
71 70 output_type=u'display_data',
72 71 data={
73 72 'text/plain': u'<array a>',
74 73 'text/html': u'The HTML rep',
75 74 'text/latex': u'$a$',
76 75 'image/png': png,
77 76 'image/jpeg': jpeg,
78 77 'image/svg+xml': u'<svg>',
79 78 'application/json': {
80 79 'key': 'value'
81 80 },
82 81 'application/javascript': u'var i=0;'
83 82 },
84 83 ),new_output(
85 84 output_type=u'error',
86 85 ename=u'NameError',
87 86 evalue=u'NameError was here',
88 87 traceback=[u'frame 0', u'frame 1', u'frame 2']
89 88 ),new_output(
90 89 output_type=u'stream',
91 90 text='foo\rbar\r\n'
92 91 ),new_output(
93 92 output_type=u'stream',
94 93 name='stderr',
95 94 text='\rfoo\rbar\n'
96 95 )]
97 96 ))
98 97
99 98 nb0 = new_notebook(cells=cells,
100 99 metadata={
101 100 'language': 'python',
102 101 }
103 102 )
104 103
105 104
@@ -1,19 +1,67 b''
1 1 import copy
2 2
3 import nose.tools as nt
4
3 5 from IPython.nbformat.current import validate
4 6 from .. import convert
5 7
6 8 from . import nbexamples
7 9 from IPython.nbformat.v3.tests import nbexamples as v3examples
10 from IPython.nbformat import v3, v4
8 11
9 12 def test_upgrade_notebook():
10 13 nb03 = copy.deepcopy(v3examples.nb0)
11 14 validate(nb03)
12 15 nb04 = convert.upgrade(nb03)
13 16 validate(nb04)
14 17
15 18 def test_downgrade_notebook():
16 19 nb04 = copy.deepcopy(nbexamples.nb0)
17 20 validate(nb04)
18 21 nb03 = convert.downgrade(nb04)
19 22 validate(nb03)
23
24 def test_upgrade_heading():
25 v3h = v3.new_heading_cell
26 v4m = v4.new_markdown_cell
27 for v3cell, expected in [
28 (
29 v3h(source='foo', level=1),
30 v4m(source='# foo'),
31 ),
32 (
33 v3h(source='foo\nbar\nmulti-line\n', level=4),
34 v4m(source='#### foo bar multi-line'),
35 ),
36 ]:
37 upgraded = convert.upgrade_cell(v3cell)
38 nt.assert_equal(upgraded, expected)
39
40 def test_downgrade_heading():
41 v3h = v3.new_heading_cell
42 v4m = v4.new_markdown_cell
43 v3m = lambda source: v3.new_text_cell('markdown', source)
44 for v4cell, expected in [
45 (
46 v4m(source='# foo'),
47 v3h(source='foo', level=1),
48 ),
49 (
50 v4m(source='#foo'),
51 v3h(source='foo', level=1),
52 ),
53 (
54 v4m(source='#\tfoo'),
55 v3h(source='foo', level=1),
56 ),
57 (
58 v4m(source='# \t foo'),
59 v3h(source='foo', level=1),
60 ),
61 (
62 v4m(source='# foo\nbar'),
63 v3m(source='# foo\nbar'),
64 ),
65 ]:
66 downgraded = convert.downgrade_cell(v4cell)
67 nt.assert_equal(downgraded, expected)
@@ -1,112 +1,101 b''
1 1 # coding: utf-8
2 2 """Tests for the Python API for composing notebook elements"""
3 3
4 4 import nose.tools as nt
5 5
6 6 from IPython.nbformat.validator import isvalid, validate, ValidationError
7 7 from ..nbbase import (
8 8 NotebookNode, nbformat,
9 new_code_cell, new_heading_cell, new_markdown_cell, new_notebook,
9 new_code_cell, new_markdown_cell, new_notebook,
10 10 new_output, new_raw_cell,
11 11 )
12 12
13 13 def test_empty_notebook():
14 14 nb = new_notebook()
15 15 nt.assert_equal(nb.cells, [])
16 16 nt.assert_equal(nb.metadata, NotebookNode())
17 17 nt.assert_equal(nb.nbformat, nbformat)
18 18
19 19 def test_empty_markdown_cell():
20 20 cell = new_markdown_cell()
21 21 nt.assert_equal(cell.cell_type, 'markdown')
22 22 nt.assert_equal(cell.source, '')
23 23
24 24 def test_markdown_cell():
25 25 cell = new_markdown_cell(u'* Søme markdown')
26 26 nt.assert_equal(cell.source, u'* Søme markdown')
27 27
28 28 def test_empty_raw_cell():
29 29 cell = new_raw_cell()
30 30 nt.assert_equal(cell.cell_type, u'raw')
31 31 nt.assert_equal(cell.source, '')
32 32
33 33 def test_raw_cell():
34 34 cell = new_raw_cell('hi')
35 35 nt.assert_equal(cell.source, u'hi')
36 36
37 def test_empty_heading_cell():
38 cell = new_heading_cell()
39 nt.assert_equal(cell.cell_type, u'heading')
40 nt.assert_equal(cell.source, '')
41 nt.assert_equal(cell.level, 1)
42
43 def test_heading_cell():
44 cell = new_heading_cell(u'hi', level=2)
45 nt.assert_equal(cell.source, u'hi')
46 nt.assert_equal(cell.level, 2)
47
48 37 def test_empty_code_cell():
49 38 cell = new_code_cell('hi')
50 39 nt.assert_equal(cell.cell_type, 'code')
51 40 nt.assert_equal(cell.source, u'hi')
52 41
53 42 def test_empty_display_data():
54 43 output = new_output('display_data')
55 44 nt.assert_equal(output.output_type, 'display_data')
56 45
57 46 def test_empty_stream():
58 47 output = new_output('stream')
59 48 nt.assert_equal(output.output_type, 'stream')
60 49 nt.assert_equal(output.name, 'stdout')
61 50 nt.assert_equal(output.text, '')
62 51
63 52 def test_empty_execute_result():
64 53 output = new_output('execute_result', execution_count=1)
65 54 nt.assert_equal(output.output_type, 'execute_result')
66 55
67 56 mimebundle = {
68 57 'text/plain': "some text",
69 58 "application/json": {
70 59 "key": "value"
71 60 },
72 61 "image/svg+xml": 'ABCDEF',
73 62 "application/octet-stream": 'ABC-123',
74 63 "application/vnd.foo+bar": "Some other stuff",
75 64 }
76 65
77 66 def test_display_data():
78 67 output = new_output('display_data', mimebundle)
79 68 for key, expected in mimebundle.items():
80 69 nt.assert_equal(output.data[key], expected)
81 70
82 71 def test_execute_result():
83 72 output = new_output('execute_result', mimebundle, execution_count=10)
84 73 nt.assert_equal(output.execution_count, 10)
85 74 for key, expected in mimebundle.items():
86 75 nt.assert_equal(output.data[key], expected)
87 76
88 77 def test_error():
89 78 o = new_output(output_type=u'error', ename=u'NameError',
90 79 evalue=u'Name not found', traceback=[u'frame 0', u'frame 1', u'frame 2']
91 80 )
92 81 nt.assert_equal(o.output_type, u'error')
93 82 nt.assert_equal(o.ename, u'NameError')
94 83 nt.assert_equal(o.evalue, u'Name not found')
95 84 nt.assert_equal(o.traceback, [u'frame 0', u'frame 1', u'frame 2'])
96 85
97 86 def test_code_cell_with_outputs():
98 87 cell = new_code_cell(execution_count=10, outputs=[
99 88 new_output('display_data', mimebundle),
100 89 new_output('stream', text='hello'),
101 90 new_output('execute_result', mimebundle, execution_count=10),
102 91 ])
103 92 nt.assert_equal(cell.execution_count, 10)
104 93 nt.assert_equal(len(cell.outputs), 3)
105 94 er = cell.outputs[-1]
106 95 nt.assert_equal(er.execution_count, 10)
107 96 nt.assert_equal(er['output_type'], 'execute_result')
108 97
109 98 def test_stream():
110 99 output = new_output('stream', name='stderr', text='hello there')
111 100 nt.assert_equal(output.name, 'stderr')
112 101 nt.assert_equal(output.text, 'hello there')
@@ -1,130 +1,105 b''
1 1 """Tests for nbformat validation"""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import io
7 7 import os
8 8
9 9 import nose.tools as nt
10 10
11 11 from IPython.nbformat.validator import validate, ValidationError
12 12 from ..nbjson import reads
13 13 from ..nbbase import (
14 14 nbformat,
15 new_code_cell, new_heading_cell, new_markdown_cell, new_notebook,
15 new_code_cell, new_markdown_cell, new_notebook,
16 16 new_output, new_raw_cell,
17 17 )
18 18
19 19 def validate4(obj, ref=None):
20 20 return validate(obj, ref, version=nbformat)
21 21
22 22 def test_valid_code_cell():
23 23 cell = new_code_cell()
24 24 validate4(cell, 'code_cell')
25 25
26 26 def test_invalid_code_cell():
27 27 cell = new_code_cell()
28 28
29 29 cell['source'] = 5
30 30 with nt.assert_raises(ValidationError):
31 31 validate4(cell, 'code_cell')
32 32
33 33 cell = new_code_cell()
34 34 del cell['metadata']
35 35
36 36 with nt.assert_raises(ValidationError):
37 37 validate4(cell, 'code_cell')
38 38
39 39 cell = new_code_cell()
40 40 del cell['source']
41 41
42 42 with nt.assert_raises(ValidationError):
43 43 validate4(cell, 'code_cell')
44 44
45 45 cell = new_code_cell()
46 46 del cell['cell_type']
47 47
48 48 with nt.assert_raises(ValidationError):
49 49 validate4(cell, 'code_cell')
50 50
51 51 def test_invalid_markdown_cell():
52 52 cell = new_markdown_cell()
53 53
54 54 cell['source'] = 5
55 55 with nt.assert_raises(ValidationError):
56 56 validate4(cell, 'markdown_cell')
57 57
58 58 cell = new_markdown_cell()
59 59 del cell['metadata']
60 60
61 61 with nt.assert_raises(ValidationError):
62 62 validate4(cell, 'markdown_cell')
63 63
64 64 cell = new_markdown_cell()
65 65 del cell['source']
66 66
67 67 with nt.assert_raises(ValidationError):
68 68 validate4(cell, 'markdown_cell')
69 69
70 70 cell = new_markdown_cell()
71 71 del cell['cell_type']
72 72
73 73 with nt.assert_raises(ValidationError):
74 74 validate4(cell, 'markdown_cell')
75 75
76 def test_invalid_heading_cell():
77 cell = new_heading_cell()
78
79 cell['source'] = 5
80 with nt.assert_raises(ValidationError):
81 validate4(cell, 'heading_cell')
82
83 cell = new_heading_cell()
84 del cell['metadata']
85
86 with nt.assert_raises(ValidationError):
87 validate4(cell, 'heading_cell')
88
89 cell = new_heading_cell()
90 del cell['source']
91
92 with nt.assert_raises(ValidationError):
93 validate4(cell, 'heading_cell')
94
95 cell = new_heading_cell()
96 del cell['cell_type']
97
98 with nt.assert_raises(ValidationError):
99 validate4(cell, 'heading_cell')
100
101 76 def test_invalid_raw_cell():
102 77 cell = new_raw_cell()
103 78
104 79 cell['source'] = 5
105 80 with nt.assert_raises(ValidationError):
106 81 validate4(cell, 'raw_cell')
107 82
108 83 cell = new_raw_cell()
109 84 del cell['metadata']
110 85
111 86 with nt.assert_raises(ValidationError):
112 87 validate4(cell, 'raw_cell')
113 88
114 89 cell = new_raw_cell()
115 90 del cell['source']
116 91
117 92 with nt.assert_raises(ValidationError):
118 93 validate4(cell, 'raw_cell')
119 94
120 95 cell = new_raw_cell()
121 96 del cell['cell_type']
122 97
123 98 with nt.assert_raises(ValidationError):
124 99 validate4(cell, 'raw_cell')
125 100
126 101 def test_sample_notebook():
127 102 here = os.path.dirname(__file__)
128 103 with io.open(os.path.join(here, os.pardir, os.pardir, 'tests', "test4.ipynb"), encoding='utf-8') as f:
129 104 nb = reads(f.read())
130 105 validate4(nb)
@@ -1,291 +1,276 b''
1 1 .. _nbformat:
2 2
3 3 ===========================
4 4 The Jupyter Notebook Format
5 5 ===========================
6 6
7 7 Introduction
8 8 ============
9 9
10 10 Jupyter (née IPython) notebook files are simple JSON documents,
11 11 containing text, source code, rich media output, and metadata.
12 12 each segment of the document is stored in a cell.
13 13
14 14 Some general points about the notebook format:
15 15
16 16 .. note::
17 17
18 18 *All* metadata fields are optional.
19 19 While the type and values of some metadata are defined,
20 20 no metadata values are required to be defined.
21 21
22 22
23 23 Top-level structure
24 24 ===================
25 25
26 26 At the highest level, a Jupyter notebook is a dictionary with a few keys:
27 27
28 28 - metadata (dict)
29 29 - nbformat (int)
30 30 - nbformat_minor (int)
31 31 - cells (list)
32 32
33 33 .. sourcecode:: python
34 34
35 35 {
36 36 "metadata" : {
37 37 "signature": "hex-digest", # used for authenticating unsafe outputs on load
38 38 "kernel_info": {
39 39 # if kernel_info is defined, its name and language fields are required.
40 40 "name" : "the name of the kernel",
41 41 "language" : "the programming language of the kernel",
42 42 "codemirror_mode": "The name of the codemirror mode to use [optional]"
43 43 },
44 44 },
45 45 "nbformat": 4,
46 46 "nbformat_minor": 0,
47 47 "cells" : [
48 48 # list of cell dictionaries, see below
49 49 ],
50 50 }
51 51
52 52
53 53 Cell Types
54 54 ==========
55 55
56 56 There are a few basic cell types for encapsulating code and text.
57 57 All cells have the following basic structure:
58 58
59 59 .. sourcecode:: python
60 60
61 61 {
62 62 "cell_type" : "name",
63 63 "metadata" : {},
64 64 "source" : "single string or [list, of, strings]",
65 65 }
66 66
67 67
68 68 Markdown cells
69 69 --------------
70 70
71 71 Markdown cells are used for body-text, and contain markdown,
72 72 as defined in `GitHub-flavored markdown`_, and implemented in marked_.
73 73
74 74 .. _GitHub-flavored markdown: https://help.github.com/articles/github-flavored-markdown
75 75 .. _marked: https://github.com/chjj/marked
76 76
77 77 .. sourcecode:: python
78 78
79 79 {
80 80 "cell_type" : "markdown",
81 81 "metadata" : {},
82 82 "source" : ["some *markdown*"],
83 83 }
84 84
85 .. versionchanged:: 4.0
85 86
86 Heading cells
87 -------------
88
89 Heading cells are single lines describing a section header (mapping onto h1-h6 tags in HTML).
90 These cells indicate structure of the document,
91 and are used for things like outline-views and automatically generating HTML anchors
92 within the page for quick navigation.
93 They have a ``level`` field, with an integer value from 1-6 (inclusive).
94
95 .. sourcecode:: python
96
97 {
98 "cell_type" : "markdown",
99 "metadata" : {},
100 "level" : 1, # An integer on [1-6]
101 "source" : ["A simple heading"],
102 }
87 Heading cells have been removed, in favor of simple headings in markdown.
103 88
104 89
105 90 Code cells
106 91 ----------
107 92
108 93 Code cells are the primary content of Jupyter notebooks.
109 94 They contain source code int e language of the document's associated kernel,
110 95 and a list of outputs associated with executing.
111 96 They also have an execution_count, which must be an integer or ``null``.
112 97
113 98 .. sourcecode:: python
114 99
115 100 {
116 101 "cell_type" : "code",
117 102 "execution_count": 1, # integer or null
118 103 "metadata" : {
119 104 "collapsed" : True, # whether the output of the cell is collapsed
120 105 "autoscroll": False, # any of true, false or "auto"
121 106 },
122 107 "source" : ["some code"],
123 108 "outputs": [{
124 109 # list of output dicts (described below)
125 110 "output_type": "stream",
126 111 ...
127 112 }],
128 113 }
129 114
130 115 .. versionchanged:: 4.0
131 116
132 117 ``input`` was renamed to ``source``, for consistency among cell types.
133 118
134 119 .. versionchanged:: 4.0
135 120
136 121 ``prompt_number`` renamed to ``execution_count``
137 122
138 123 Code cell outputs
139 124 -----------------
140 125
141 126 A code cell can have a variety of outputs (stream data or rich mime-type output).
142 127 These correspond to :ref:`messages <messaging>` produced as a result of executing the cell.
143 128
144 129 All outputs have an ``output_type`` field,
145 130 which is a string defining what type of output it is.
146 131
147 132
148 133 stream output
149 134 *************
150 135
151 136 .. sourcecode:: python
152 137
153 138 {
154 139 "output_type" : "stream",
155 140 "name" : "stdout", # or stderr
156 141 "data" : ["multiline stream text"],
157 142 }
158 143
159 144 .. versionchanged:: 4.0
160 145
161 146 The keys ``stream`` and ``text`` were changed to ``name`` and ``data`` to match
162 147 the stream message specification.
163 148
164 149
165 150 display_data
166 151 ************
167 152
168 153 Rich display messages (as created by ``display_data`` messages)
169 154 contain data keyed by mime-type. All mime-type data should
170 155 The metadata of these messages may be keyed by mime-type as well.
171 156
172 157
173 158 .. sourcecode:: python
174 159
175 160 {
176 161 "output_type" : "display_data",
177 162 "data" : {
178 163 "text/plain" : ["multiline text data"],
179 164 "image/png": ["base64-encoded-png-data"],
180 165 "application/json": {
181 166 # JSON data is included as-is
182 167 "json": "data",
183 168 },
184 169 },
185 170 "metadata" : {
186 171 "image/png": {
187 172 "width": 640,
188 173 "height": 480,
189 174 },
190 175 },
191 176 }
192 177
193 178
194 179 .. versionchanged:: 4.0
195 180
196 181 ``application/json`` output is no longer double-serialized into a string.
197 182
198 183 .. versionchanged:: 4.0
199 184
200 185 mime-types are used for keys, instead of a combination of short names (``text``)
201 186 and mime-types, and are stored in a ``data`` key, rather than the top-level.
202 187 i.e. ``output.data['image/png']`` instead of ``output.png``.
203 188
204 189
205 190 execute_result
206 191 **************
207 192
208 193 Results of executing a cell (as created by ``displayhook`` in Python)
209 194 are stored in ``execute_result`` outputs.
210 195 `execute_result` outputs are identical to ``display_data``,
211 196 adding only a ``prompt_number`` field, which must be an integer.
212 197
213 198 .. sourcecode:: python
214 199
215 200 {
216 201 "output_type" : "execute_result",
217 202 "execute_result": 42,
218 203 "data" : {
219 204 "text/plain" : ["multiline text data"],
220 205 "image/png": ["base64-encoded-png-data"],
221 206 "application/json": {
222 207 # JSON data is included as-is
223 208 "json": "data",
224 209 },
225 210 },
226 211 "metadata" : {
227 212 "image/png": {
228 213 "width": 640,
229 214 "height": 480,
230 215 },
231 216 },
232 217 }
233 218
234 219 .. versionchanged:: 4.0
235 220
236 221 ``pyout`` renamed to ``execute_result``
237 222
238 223 .. versionchanged:: 4.0
239 224
240 225 ``prompt_number`` renamed to ``execution_count``
241 226
242 227
243 228 error
244 229 *****
245 230
246 231 Failed execution may show a traceback
247 232
248 233 .. sourcecode:: python
249 234
250 235 {
251 236 'ename' : str, # Exception name, as a string
252 237 'evalue' : str, # Exception value, as a string
253 238
254 239 # The traceback will contain a list of frames,
255 240 # represented each as a string.
256 241 'traceback' : list,
257 242 }
258 243
259 244 .. versionchanged:: 4.0
260 245
261 246 ``pyerr`` renamed to ``error``
262 247
263 248
264 249 Raw NBConvert cells
265 250 -------------------
266 251
267 252 A raw cell is defined as content that should be included *unmodified* in :ref:`nbconvert <nbconvert>` output.
268 253 For example, this cell could include raw LaTeX for nbconvert to pdf via latex,
269 254 or restructured text for use in Sphinx documentation.
270 255
271 256 The notebook authoring environment does not render raw cells.
272 257
273 258 The only logic in a raw cell is the `format` metadata field.
274 259 If defined, it specifies which nbconvert output format is the intended target
275 260 for the raw cell. When outputting to any other format,
276 261 the raw cell's contents will be excluded.
277 262 In the default case when this value is undefined,
278 263 a raw cell's contents will be included in any nbconvert output,
279 264 regardless of format.
280 265
281 266 .. sourcecode:: python
282 267
283 268 {
284 269 "cell_type" : "raw",
285 270 "metadata" : {
286 271 # the mime-type of the target nbconvert format.
287 272 # nbconvert to formats other than this will exclude this cell.
288 273 "format" : "mime/type"
289 274 },
290 275 "source" : ["some nbformat mime-type data"]
291 276 }
General Comments 0
You need to be logged in to leave comments. Login now