diff --git a/notebook/nbconvert/handlers.py b/notebook/nbconvert/handlers.py index 4dd5352cc6..648c9827f8 100644 --- a/notebook/nbconvert/handlers.py +++ b/notebook/nbconvert/handlers.py @@ -58,12 +58,12 @@ def get_exporter(format, **kwargs): """get an exporter, raising appropriate errors""" # if this fails, will raise 500 try: - from nbconvert.exporters.export import exporter_map + from nbconvert.exporters.base import get_exporter except ImportError as e: raise web.HTTPError(500, "Could not import nbconvert: %s" % e) try: - Exporter = exporter_map[format] + Exporter = get_exporter(format) except KeyError: # should this be 400? raise web.HTTPError(404, u"No exporter for format: %s" % format) diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py index 132aa4ddf2..97599f276f 100755 --- a/notebook/notebookapp.py +++ b/notebook/notebookapp.py @@ -389,21 +389,31 @@ class NbserverListApp(JupyterApp): description=_("List currently running notebook servers.") flags = dict( + jsonlist=({'NbserverListApp': {'jsonlist': True}}, + _("Produce machine-readable JSON list output.")), json=({'NbserverListApp': {'json': True}}, - _("Produce machine-readable JSON output.")), + _("Produce machine-readable JSON object on each line of output.")), ) + jsonlist = Bool(False, config=True, + help=_("If True, the output will be a JSON list of objects, one per " + "active notebook server, each with the details from the " + "relevant server info file.")) json = Bool(False, config=True, help=_("If True, each line of output will be a JSON object with the " - "details from the server info file.")) + "details from the server info file. For a JSON list output, " + "see the NbserverListApp.jsonlist configuration value")) def start(self): - if not self.json: - print(_("Currently running servers:")) - for serverinfo in list_running_servers(self.runtime_dir): - if self.json: + serverinfo_list = list(list_running_servers(self.runtime_dir)) + if self.jsonlist: + print(json.dumps(serverinfo_list, indent=2)) + elif self.json: + for serverinfo in serverinfo_list: print(json.dumps(serverinfo)) - else: + else: + print("Currently running servers:") + for serverinfo in serverinfo_list: url = serverinfo['url'] if serverinfo.get('token'): url = url + '?token=%s' % serverinfo['token'] diff --git a/notebook/static/auth/js/main.js b/notebook/static/auth/js/main.js index 7be82388ed..68d7871c58 100644 --- a/notebook/static/auth/js/main.js +++ b/notebook/static/auth/js/main.js @@ -1,7 +1,10 @@ // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. -define(['./loginmain', './logoutmain'], function (login_main, logout_main) { +define(['./loginmain', './logoutmain', 'bidi/bidi'], function (login_main, logout_main, bidi) { + if(bidi.isMirroringEnabled()){ + $("body").attr("dir","rtl"); + } return { login_main: login_main, logout_main: logout_main diff --git a/notebook/static/auth/less/login.less b/notebook/static/auth/less/login.less index d21a3a1c74..8a4ce3ffcb 100644 --- a/notebook/static/auth/less/login.less +++ b/notebook/static/auth/less/login.less @@ -3,4 +3,21 @@ display: inline-block; // pull the lower margin back margin-bottom: -4px; -} \ No newline at end of file +} + +[dir="rtl"] .center-nav { + form.pull-left { + .pull-right(); + } + .navbar-text { + float: right; + } +} + +[dir="rtl"] .navbar-inner { + text-align: right; +} + +[dir="rtl"] div.text-left { + .text-right(); +} diff --git a/notebook/static/base/less/page.less b/notebook/static/base/less/page.less index c300591d2e..85e7f825a9 100644 --- a/notebook/static/base/less/page.less +++ b/notebook/static/base/less/page.less @@ -63,6 +63,15 @@ body > #header { padding-bottom: (@navbar-height - @logo_height) / 2; } +[dir="rtl"] #ipython_notebook { + margin-right: 10px; + margin-left: 0; +} + +[dir="rtl"] #ipython_notebook.pull-left { + .pull-right(); +} + .flex-spacer { flex: 1; } @@ -107,7 +116,11 @@ span#kernel_logo_widget { } span#login_widget { - + float: right; +} + +[dir="rtl"] span#login_widget { + float: left; } span#login_widget > .button, diff --git a/notebook/static/bidi/bidi.js b/notebook/static/bidi/bidi.js new file mode 100644 index 0000000000..bd7118d147 --- /dev/null +++ b/notebook/static/bidi/bidi.js @@ -0,0 +1,56 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +define([ + 'bidi/numericshaping', +], function (numericshaping){ + "use strict"; + + var shaperType=""; + var textDir=""; + + var _uiLang= function (){ + return navigator.language.toLowerCase(); + }; + + var _loadLocale = function () { + if(_isMirroringEnabled()){ + $("body").attr("dir","rtl"); + } + requirejs(['components/moment/locale/'+_uiLang()], function (err){ + console.warn("Error loading the required locale"); + console.warn(err); + }); + shaperType= _uiLang()=='ar'? "national" : "defaultNumeral"; + }; + + var _isMirroringEnabled= function() { + return (new RegExp("^(ar|he)").test(_uiLang())); + }; + + /** + * NS : for digit Shaping. + * BTD : for future work in case of Base Text Direction Addition. + */ + /*var _flags= { + NS: 1, + BTD : 2 + };*/ + + /** + * @param value : the string to apply the bidi-support on it. + * @param flag :indicates the type of bidi-support (Numeric-shaping ,Base-text-dir ). + */ + var _applyBidi = function (value /*, flag*/) { + value = numericshaping.shapeNumerals(value, shaperType); + return value; + }; + + var bidi = { + applyBidi : _applyBidi, + isMirroringEnabled : _isMirroringEnabled, + loadLocale : _loadLocale, + }; + + return bidi; +}); diff --git a/notebook/static/bidi/numericshaping.js b/notebook/static/bidi/numericshaping.js new file mode 100644 index 0000000000..d78ec0d0df --- /dev/null +++ b/notebook/static/bidi/numericshaping.js @@ -0,0 +1,42 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +define([], + function(bidi) { + "use strict"; + + var regex = /([0-9])|([\u0660-\u0669])|([\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE]+)|([^0-9\u0660-\u0669\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE\u0600-\u0607\u0609-\u060A\u060C\u060E-\u061A\u064B-\u066C\u0670\u06D6-\u06E4\u06E7-\u06ED\u06F0-\u06F9\u08E4-\u08FF\uFD3E-\uFD3F\uFDD0-\uFDEF\uFDFD\uFEFF\u0000-\u0040\u005B-\u0060\u007B-\u007F\u0080-\u00A9\u00AB-\u00B4\u00B6-\u00B9\u00BB-\u00BF\u00D7\u00F7\u02B9-\u02BA\u02C2-\u02CF\u02D2-\u02DF\u02E5-\u02ED\u02EF-\u02FF\u2070\u2074-\u207E\u2080-\u208E\u2100-\u2101\u2103-\u2106\u2108-\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A-\u213B\u2140-\u2144\u214A-\u214D\u2150-\u215F\u2189\uA720-\uA721\uA788\uFF01-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\uFFE0-\uFFE6\uFFE8-\uFFEE]+)/g; + + var shape = function(text, shaperType) { + text = text.toString(); + if (!text) { + return text; + } + switch (shaperType) { + case "defaultNumeral": + return _shapeEuropean(text); + case "national": + return _shapeArabic(text); + default: + return text; + } + }; + + var _shapeEuropean = function(text) { + return text.replace(/[\u0660-\u0669]/g, function(c) { + return c.charCodeAt(0) - 1632; + }); + }; + + var _shapeArabic = function(text) { + return text.replace(/[0-9]/g, function(c) { + return String.fromCharCode(parseInt(c) + 1632); + }); + }; + + var numericshaping = { + shapeNumerals : shape + }; + + return numericshaping; +}); diff --git a/notebook/static/custom/custom.css b/notebook/static/custom/custom.css index 9f4abdaa49..b8367c9135 100644 --- a/notebook/static/custom/custom.css +++ b/notebook/static/custom/custom.css @@ -4,4 +4,18 @@ Placeholder for custom user CSS mainly to be overridden in profile/static/custom/custom.css This will always be an empty file in IPython -*/ \ No newline at end of file +*/ + +/*for the error , connecting & renaming window*/ + +[dir="rtl"] .modal-footer { + text-align : left !important; +} + +[dir="rtl"] .close { + float : left; +} + +[dir="rtl"] .fa-step-forward::before { + content: "\f048"; +} diff --git a/notebook/static/edit/js/main.js b/notebook/static/edit/js/main.js index 006d20cb6e..9b2a2cb682 100644 --- a/notebook/static/edit/js/main.js +++ b/notebook/static/edit/js/main.js @@ -13,6 +13,7 @@ require([ 'edit/js/menubar', 'edit/js/savewidget', 'edit/js/notificationarea', + 'bidi/bidi', ], function( $, contents_service, @@ -24,12 +25,14 @@ require([ editmod, menubar, savewidget, - notificationarea + notificationarea, + bidi ){ "use strict"; try { requirejs(['custom/custom'], function() {}); + bidi.loadLocale(); } catch(err) { console.log("Error loading custom.js from edition service. Continuing and logging"); console.warn(err); diff --git a/notebook/static/edit/js/savewidget.js b/notebook/static/edit/js/savewidget.js index 7df4ed4d64..6154100998 100644 --- a/notebook/static/edit/js/savewidget.js +++ b/notebook/static/edit/js/savewidget.js @@ -7,9 +7,10 @@ define([ 'base/js/dialog', 'base/js/keyboard', 'moment', -], function($, utils, dialog, keyboard, moment) { + 'bidi/bidi', +], function($, utils, dialog, keyboard, moment, bidi) { "use strict"; - + var SaveWidget = function (selector, options) { this.editor = undefined; this.selector = selector; @@ -113,6 +114,7 @@ define([ SaveWidget.prototype.update_filename = function (filename) { + filename = bidi.applyBidi(filename); this.element.find('span.filename').text(filename); }; @@ -151,7 +153,7 @@ define([ if (!this._last_modified) { el.text('').attr('title', 'never saved'); return; - } + } var chkd = moment(this._last_modified); var long_date = chkd.format('llll'); var human_date; diff --git a/notebook/static/notebook/js/main.js b/notebook/static/notebook/js/main.js index fec68fd708..2e64d3cdc8 100644 --- a/notebook/static/notebook/js/main.js +++ b/notebook/static/notebook/js/main.js @@ -42,7 +42,8 @@ require([ 'codemirror/lib/codemirror', 'notebook/js/about', 'notebook/js/searchandreplace', - 'notebook/js/clipboard' + 'notebook/js/clipboard', + 'bidi/bidi' ], function( $, contents_service, @@ -65,7 +66,8 @@ require([ CodeMirror, about, searchandreplace, - clipboard + clipboard, + bidi ) { "use strict"; @@ -74,6 +76,7 @@ require([ try{ requirejs(['custom/custom'], function() {}); + bidi.loadLocale(); } catch(err) { console.log("Error processing custom.js. Logging and continuing"); console.warn(err); diff --git a/notebook/static/notebook/js/outputarea.js b/notebook/static/notebook/js/outputarea.js index ef83fea5b6..872c1987f4 100644 --- a/notebook/static/notebook/js/outputarea.js +++ b/notebook/static/notebook/js/outputarea.js @@ -77,7 +77,6 @@ define([ OutputArea.prototype.style = function () { this.collapse_button.hide(); - this.prompt_overlay.hide(); this.wrapper.addClass('output_wrapper'); this.element.addClass('output'); @@ -263,6 +262,7 @@ define([ var MIME_SVG = 'image/svg+xml'; var MIME_PNG = 'image/png'; var MIME_JPEG = 'image/jpeg'; + var MIME_GIF = 'image/gif'; var MIME_PDF = 'application/pdf'; var MIME_TEXT = 'text/plain'; @@ -275,6 +275,7 @@ define([ MIME_SVG, MIME_PNG, MIME_JPEG, + MIME_GIF, MIME_PDF, MIME_TEXT, ]; @@ -660,6 +661,7 @@ define([ OutputArea.safe_outputs[MIME_LATEX] = true; OutputArea.safe_outputs[MIME_PNG] = true; OutputArea.safe_outputs[MIME_JPEG] = true; + OutputArea.safe_outputs[MIME_GIF] = true; OutputArea.prototype.append_mime_type = function (json, element, handle_inserted) { for (var i=0; i < OutputArea.display_order.length; i++) { @@ -683,7 +685,7 @@ define([ // callback, if the mime type is something other we must call the // inserted callback only when the element is actually inserted // into the DOM. Use a timeout of 0 to do this. - if ([MIME_PNG, MIME_JPEG].indexOf(type) < 0 && handle_inserted !== undefined) { + if ([MIME_PNG, MIME_JPEG, MIME_GIF].indexOf(type) < 0 && handle_inserted !== undefined) { setTimeout(handle_inserted, 0); } this.events.trigger('output_appended.OutputArea', [type, value, md, toinsert]); @@ -822,7 +824,7 @@ define([ }); } img[0].src = 'data:image/png;base64,'+ png; - set_width_height(img, md, MIME_PNG); + set_width_height(img, md, type); dblclick_to_reset_size(img); toinsert.append(img); element.append(toinsert); @@ -840,7 +842,24 @@ define([ }); } img[0].src = 'data:image/jpeg;base64,'+ jpeg; - set_width_height(img, md, MIME_JPEG); + set_width_height(img, md, type); + dblclick_to_reset_size(img); + toinsert.append(img); + element.append(toinsert); + return toinsert; + }; + + var append_gif = function (gif, md, element, handle_inserted) { + var type = MIME_GIF; + var toinsert = this.create_output_subarea(md, "output_gif", type); + var img = $(""); + if (handle_inserted !== undefined) { + img.on('load', function(){ + handle_inserted(img); + }); + } + img[0].src = 'data:image/gif;base64,'+ gif; + set_width_height(img, md, type); dblclick_to_reset_size(img); toinsert.append(img); element.append(toinsert); @@ -1073,6 +1092,7 @@ define([ MIME_SVG, MIME_PNG, MIME_JPEG, + MIME_GIF, MIME_PDF, MIME_TEXT ]; @@ -1084,6 +1104,7 @@ define([ OutputArea.append_map[MIME_SVG] = append_svg; OutputArea.append_map[MIME_PNG] = append_png; OutputArea.append_map[MIME_JPEG] = append_jpeg; + OutputArea.append_map[MIME_GIF] = append_gif; OutputArea.append_map[MIME_LATEX] = append_latex; OutputArea.append_map[MIME_JAVASCRIPT] = append_javascript; OutputArea.append_map[MIME_PDF] = append_pdf; diff --git a/notebook/static/notebook/js/savewidget.js b/notebook/static/notebook/js/savewidget.js index 141d624fd7..39c64a188a 100644 --- a/notebook/static/notebook/js/savewidget.js +++ b/notebook/static/notebook/js/savewidget.js @@ -8,7 +8,8 @@ define([ 'base/js/dialog', 'base/js/keyboard', 'moment', -], function($, utils, i18n, dialog, keyboard, moment) { + 'bidi/bidi', +], function($, utils, i18n, dialog, keyboard, moment, bidi) { "use strict"; var SaveWidget = function (selector, options) { @@ -133,6 +134,7 @@ define([ SaveWidget.prototype.update_notebook_name = function () { var nbname = this.notebook.get_notebook_name(); + nbname = bidi.applyBidi(nbname); this.element.find('span.filename').text(nbname); }; @@ -186,10 +188,11 @@ define([ // less than 24 hours old, use relative date human_date = chkd.fromNow(); } else { - // otherwise show calendar + // otherwise show calendar // at hh,mm,ss human_date = chkd.calendar(); } + el.text(i18n.msg.sprintf(i18n.msg._('Last Checkpoint: %s'),human_date)).attr('title', long_date); }; diff --git a/notebook/static/notebook/less/commandpalette.less b/notebook/static/notebook/less/commandpalette.less index 28e738660f..7c4069ed11 100644 --- a/notebook/static/notebook/less/commandpalette.less +++ b/notebook/static/notebook/less/commandpalette.less @@ -3,6 +3,11 @@ ul.typeahead-list i{ width: 18px; } +[dir="rtl"] ul.typeahead-list i { + margin-left: 0; + margin-right: -10px; +} + ul.typeahead-list { max-height: 80vh; overflow:auto; @@ -14,6 +19,14 @@ ul.typeahead-list { } } +ul.typeahead-list & > li > a.pull-right { + .pull-left(); +} + +[dir="rtl"] .typeahead-list { + text-align : right; +} + .cmd-palette { & .modal-body{ padding: 7px; @@ -33,6 +46,14 @@ ul.typeahead-list { color: transparent; } +[dir="rtl"] .no-shortcut.pull-right{ + .pull-left(); +} + +[dir="rtl"] .command-shortcut.pull-right{ + .pull-left(); +} + .command-shortcut:before{ content:"(command mode)"; padding-right:3px; @@ -44,3 +65,7 @@ ul.typeahead-list { padding-right:3px; color:@gray-light; } + +[dir="rtl"] .edit-shortcut.pull-right { + .pull-left(); +} diff --git a/notebook/static/notebook/less/kernelselector.less b/notebook/static/notebook/less/kernelselector.less index fab2cf7458..9dc98d2d4f 100644 --- a/notebook/static/notebook/less/kernelselector.less +++ b/notebook/static/notebook/less/kernelselector.less @@ -6,3 +6,7 @@ height: 32px; } } + +[dir="rtl"] #kernel_logo_widget { + .pull-left(); +} diff --git a/notebook/static/notebook/less/menubar.less b/notebook/static/notebook/less/menubar.less index 7f776c0355..414557aee4 100644 --- a/notebook/static/notebook/less/menubar.less +++ b/notebook/static/notebook/less/menubar.less @@ -20,6 +20,39 @@ } +[dir="rtl"] #menubar { + .navbar-toggle { + float: right; + } +} + +[dir="rtl"] #menubar { + .navbar-collapse { + clear: right; + } + .navbar-nav { + float : right; + } + .nav { + padding-right : 0px; + } + .navbar-nav > li { + float: right; + } + .navbar-right { + float : left !important; + } +} + +[dir="rtl"] ul.dropdown-menu { + text-align: right; + left : auto; +} + +[dir="rtl"] ul#new-menu.dropdown-menu { + right : auto; + left: 0; +} .nav-wrapper { border-bottom: 1px solid @navbar-default-border; } @@ -29,6 +62,10 @@ i.menu-icon { padding-top: 4px; } +[dir="rtl"] i.menu-icon.pull-right { + .pull-left(); +} + ul#help_menu li a{ overflow: hidden; padding-right: 2.2em; @@ -37,6 +74,18 @@ ul#help_menu li a{ } } +[dir="rtl"] ul#help_menu li a{ + padding-left : 2.2em; + i { + margin-right: 0; + margin-left : -1.2em; + } + + i.pull-right { + .pull-left(); + } +} + // Make sub menus work in BS3. // Credit: http://www.bootply.com/86684 .dropdown-submenu { @@ -50,6 +99,11 @@ ul#help_menu li a{ margin-left: -1px; } +[dir="rtl"] .dropdown-submenu>.dropdown-menu { + right:100%; + margin-right: -1px; +} + // arrow that indicate presence of submenu .dropdown-submenu:hover>.dropdown-menu { display: block; @@ -65,6 +119,13 @@ ul#help_menu li a{ margin-right: -10px; } +[dir="rtl"] .dropdown-submenu>a:after { + float: left; + content: @fa-var-caret-left; + margin-right : 0; + margin-left : -10px; +} + .dropdown-submenu:hover>a:after { color: @dropdown-link-hover-color; } diff --git a/notebook/static/notebook/less/notificationarea.less b/notebook/static/notebook/less/notificationarea.less index bfbf5b73f6..8f8b7566c9 100644 --- a/notebook/static/notebook/less/notificationarea.less +++ b/notebook/static/notebook/less/notificationarea.less @@ -3,6 +3,10 @@ z-index: 10; } +[dir="rtl"] #notification_area { + .pull-left(); +} + .indicator_area { .pull-right(); color: @navbar-default-link-color; @@ -14,6 +18,10 @@ width: auto; } +[dir="rtl"] .indicator_area { + .pull-left(); +} + #kernel_indicator { .indicator_area(); @@ -25,11 +33,21 @@ } } +[dir="rtl"] #kernel_indicator { + .pull-left(); + border-left: 0; + border-right : 1px solid; +} + #modal_indicator { .pull-right(); .indicator_area(); } +[dir="rtl"] #modal_indicator { + .pull-left(); +} + #readonly-indicator { .pull-right(); .indicator_area(); diff --git a/notebook/static/notebook/less/renderedhtml.less b/notebook/static/notebook/less/renderedhtml.less index 73ecd2defb..34b085dcb5 100644 --- a/notebook/static/notebook/less/renderedhtml.less +++ b/notebook/static/notebook/less/renderedhtml.less @@ -130,3 +130,10 @@ .alert {margin-bottom: initial;} * + .alert {margin-top: 1em;} } + +[dir="rtl"] .rendered_html { + p { + text-align : right; + } +} + diff --git a/notebook/static/notebook/less/savewidget.less b/notebook/static/notebook/less/savewidget.less index 13f76b4d4a..74b9d04bb0 100644 --- a/notebook/static/notebook/less/savewidget.less +++ b/notebook/static/notebook/less/savewidget.less @@ -25,6 +25,17 @@ span.save_widget { } } +[dir="rtl"] span.save_widget.pull-left { + .pull-right(); +} + +[dir="rtl"] span.save_widget { + span.filename { + margin-left : 0; + margin-right : @padding-large-horizontal; + } +} + span.checkpoint_status, span.autosave_status { font-size: small; white-space: nowrap; diff --git a/notebook/static/notebook/less/toolbar.less b/notebook/static/notebook/less/toolbar.less index af074b445c..d5e7d5b529 100644 --- a/notebook/static/notebook/less/toolbar.less +++ b/notebook/static/notebook/less/toolbar.less @@ -55,6 +55,9 @@ height: @btn_small_height; } +[dir="rtl"] .btn-group > .btn, .btn-group-vertical > .btn { + float: right; +} // highlight the new menu where celltoolbar is .pulse, .dropdown-menu > li > a.pulse, li.pulse > a.dropdown-toggle, li.pulse.open > a.dropdown-toggle { diff --git a/notebook/static/tree/js/main.js b/notebook/static/tree/js/main.js index 40580c2f32..44cb7e2008 100644 --- a/notebook/static/tree/js/main.js +++ b/notebook/static/tree/js/main.js @@ -35,6 +35,7 @@ require([ 'tree/js/terminallist', 'tree/js/newnotebook', 'auth/js/loginwidget', + 'bidi/bidi', ], function( $, contents_service, @@ -49,10 +50,13 @@ require([ kernellist, terminallist, newnotebook, - loginwidget){ + loginwidget, + bidi){ "use strict"; + try{ requirejs(['custom/custom'], function() {}); + bidi.loadLocale(); } catch(err) { console.log("Error loading custom.js from tree service. Continuing and logging"); console.warn(err); diff --git a/notebook/static/tree/js/notebooklist.js b/notebook/static/tree/js/notebooklist.js index 562ef1fe81..49e73b7206 100644 --- a/notebook/static/tree/js/notebooklist.js +++ b/notebook/static/tree/js/notebooklist.js @@ -9,8 +9,9 @@ define([ 'base/js/dialog', 'base/js/events', 'base/js/keyboard', - 'moment' -], function($, IPython, utils, i18n, dialog, events, keyboard, moment) { + 'moment', + 'bidi/bidi' +], function($, IPython, utils, i18n, dialog, events, keyboard, moment, bidi) { "use strict"; var extension = function(path){ @@ -702,6 +703,7 @@ define([ select_all.data('indeterminate', true); } // Update total counter + checked = bidi.applyBidi(checked); $('#counter-select-all').html(checked===0 ? ' ' : checked); // If at aleast on item is selected, hide the selection instructions. @@ -714,12 +716,11 @@ define([ NotebookList.prototype.add_link = function (model, item) { var running = (model.type === 'notebook' && this.sessions[model.path] !== undefined); - - item.data('name', model.name); + item.data('name',model.name); item.data('path', model.path); item.data('modified', model.last_modified); item.data('type', model.type); - item.find(".item_name").text(model.name); + item.find(".item_name").text(bidi.applyBidi(model.name)); var icon = NotebookList.icons[model.type]; if (running) { icon = 'running_' + icon; diff --git a/notebook/static/tree/js/sessionlist.js b/notebook/static/tree/js/sessionlist.js index b850ef64b0..031fa7ab08 100644 --- a/notebook/static/tree/js/sessionlist.js +++ b/notebook/static/tree/js/sessionlist.js @@ -4,9 +4,11 @@ define([ 'jquery', 'base/js/utils', -], function($, utils) { + 'bidi/bidi', +], function($, utils, bidi) { "use strict"; + var isRTL = bidi.isMirroringEnabled(); var SesssionList = function (options) { /** * Constructor @@ -36,7 +38,7 @@ define([ // to do the animation (borderSpacing). $icon.animate({ borderSpacing: 90 }, { step: function(now,fx) { - $icon.css('transform','rotate(-' + now + 'deg)'); + isRTL ? $icon.css('transform','rotate(' + now + 'deg)') : $icon.css('transform','rotate(-' + now + 'deg)'); } }, 250); } else { @@ -44,7 +46,7 @@ define([ // See comment above. $icon.animate({ borderSpacing: 0 }, { step: function(now,fx) { - $icon.css('transform','rotate(-' + now + 'deg)'); + isRTL ? $icon.css('transform','rotate(' + now + 'deg)') : $icon.css('transform','rotate(-' + now + 'deg)'); } }, 250); } diff --git a/notebook/static/tree/less/tree.less b/notebook/static/tree/less/tree.less index faebeaf623..518dc72a67 100644 --- a/notebook/static/tree/less/tree.less +++ b/notebook/static/tree/less/tree.less @@ -25,6 +25,14 @@ ul#tabs a { padding-bottom: @dashboard_tb_pad; } +[dir="rtl"] ul#tabs.nav-tabs > li { + float : right; +} + +[dir="rtl"] ul#tabs.nav.nav-tabs { + padding-right: 0; +} + ul.breadcrumb { a:focus, a:hover { text-decoration: none; @@ -48,6 +56,15 @@ ul.breadcrumb { } } +[dir="rtl"] .list_toolbar { + .tree-buttons .pull-right { + .pull-left(); + } + .col-sm-4 , .col-sm-8 { + float : right; + } +} + .dynamic-buttons { padding-top: @dashboard_tb_pad - 1px; display: inline-block; @@ -121,6 +138,13 @@ ul.breadcrumb { } } +[dir="rtl"] .list_item>div { + + input { + margin-right : 0; + } +} + .new-file input[type=checkbox] { visibility: hidden; } @@ -144,6 +168,10 @@ ul.breadcrumb { margin-left: @dashboard_lr_pad; } +[dir="rtl"] .item_modified.pull-right{ + .pull-left(); +} + .item_buttons { line-height: 1em; .btn-toolbar(); @@ -162,6 +190,17 @@ ul.breadcrumb { } } +[dir="rtl"] .item_buttons.pull-right { + .pull-left(); +} + +[dir="rtl"] .item_buttons { + .kernel-name { + margin-left : @dashboard_lr_pad; + float : right; + } +} + .toolbar_info { height: @btn_small_height; line-height: @btn_small_height; @@ -198,6 +237,10 @@ ul.breadcrumb { padding-left: @dashboard_lr_pad; } +[dir="rtl"] .sort_button.pull-right { + .pull-left(); +} + #tree-selector { padding-right: 0px; } @@ -206,11 +249,19 @@ ul.breadcrumb { min-width: 50px; } +[dir="rtl"] #button-select-all.btn { + float : right ; +} + #select-all { margin-left: @dashboard_lr_pad; margin-right: 2px; } +[dir="rtl"] #select-all.pull-left { + .pull-right(); +} + .menu_icon { margin-right: 2px; }