From 10bd54bd30ab5f6ee8bf7cfcefa91d6f706b61a6 Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Tue, 21 Jan 2014 16:49:35 -0800 Subject: [PATCH] Move js *RangeWidget code into *Widget --- .../html/static/notebook/js/widgets/init.js | 2 - .../notebook/js/widgets/widget_float.js | 263 +++++++++++++++++- .../static/notebook/js/widgets/widget_int.js | 216 +++++++++++++- 3 files changed, 473 insertions(+), 8 deletions(-) diff --git a/IPython/html/static/notebook/js/widgets/init.js b/IPython/html/static/notebook/js/widgets/init.js index 41034fece..9d3148d35 100644 --- a/IPython/html/static/notebook/js/widgets/init.js +++ b/IPython/html/static/notebook/js/widgets/init.js @@ -14,10 +14,8 @@ define([ "notebook/js/widgets/widget_button", "notebook/js/widgets/widget_container", "notebook/js/widgets/widget_float", - "notebook/js/widgets/widget_float_range", "notebook/js/widgets/widget_image", "notebook/js/widgets/widget_int", - "notebook/js/widgets/widget_int_range", "notebook/js/widgets/widget_selection", "notebook/js/widgets/widget_selectioncontainer", "notebook/js/widgets/widget_string", diff --git a/IPython/html/static/notebook/js/widgets/widget_float.js b/IPython/html/static/notebook/js/widgets/widget_float.js index aa06f72c3..848533670 100644 --- a/IPython/html/static/notebook/js/widgets/widget_float.js +++ b/IPython/html/static/notebook/js/widgets/widget_float.js @@ -5,6 +5,263 @@ // the file COPYING, distributed as part of this software. //---------------------------------------------------------------------------- -// This file is a place holder to maintain a one to one mapping of widget_*.py -// files and widget_*.js widget and test files. The Views for this model are -// shared with the bounded float, and can be found in widget_float_range.js. +//============================================================================ +// FloatWidget +//============================================================================ + +/** + * @module IPython + * @namespace IPython + **/ + +define(["notebook/js/widgets/widget"], function(WidgetManager){ + + var FloatSliderView = IPython.DOMWidgetView.extend({ + render : function(){ + // Called when view is rendered. + this.$el + .addClass('widget-hbox-single'); + this.$label = $('
') + .appendTo(this.$el) + .addClass('widget-hlabel') + .hide(); + this.$slider = $('
') + .slider({}) + .addClass('slider'); + + // Put the slider in a container + this.$slider_container = $('
') + .addClass('widget-hslider') + .append(this.$slider); + this.$el_to_style = this.$slider_container; // Set default element to style + this.$el.append(this.$slider_container); + + // Set defaults. + this.update(); + }, + + update : function(options){ + // Update the contents of this view + // + // Called when the model is changed. The model may have been + // changed by another view or by a state update from the back-end. + + if (options === undefined || options.updated_view != this) { + // JQuery slider option keys. These keys happen to have a + // one-to-one mapping with the corrosponding keys of the model. + var jquery_slider_keys = ['step', 'max', 'min', 'disabled']; + _.each(jquery_slider_keys, function(key, i) { + var model_value = this.model.get(key); + if (model_value !== undefined) { + this.$slider.slider("option", key, model_value); + } + }); + + // WORKAROUND FOR JQUERY SLIDER BUG. + // The horizontal position of the slider handle + // depends on the value of the slider at the time + // of orientation change. Before applying the new + // workaround, we set the value to the minimum to + // make sure that the horizontal placement of the + // handle in the vertical slider is always + // consistent. + var orientation = this.model.get('orientation'); + var value = this.model.get('min'); + this.$slider.slider('option', 'value', value); + this.$slider.slider('option', 'orientation', orientation); + value = this.model.get('value'); + this.$slider.slider('option', 'value', value); + + // Use the right CSS classes for vertical & horizontal sliders + if (orientation=='vertical') { + this.$slider_container + .removeClass('widget-hslider') + .addClass('widget-vslider'); + this.$el + .removeClass('widget-hbox-single') + .addClass('widget-vbox-single'); + this.$label + .removeClass('widget-hlabel') + .addClass('widget-vlabel'); + + } else { + this.$slider_container + .removeClass('widget-vslider') + .addClass('widget-hslider'); + this.$el + .removeClass('widget-vbox-single') + .addClass('widget-hbox-single'); + this.$label + .removeClass('widget-vlabel') + .addClass('widget-hlabel'); + } + + var description = this.model.get('description'); + if (description.length === 0) { + this.$label.hide(); + } else { + this.$label.text(description); + this.$label.show(); + } + } + return FloatSliderView.__super__.update.apply(this); + }, + + events: { + // Dictionary of events and their handlers. + "slide" : "handleSliderChange" + }, + + handleSliderChange: function(e, ui) { + // Handle when the slider value is changed. + + // Calling model.set will trigger all of the other views of the + // model to update. + this.model.set('value', ui.value, {updated_view: this}); + this.touch(); + }, + }); + WidgetManager.register_widget_view('FloatSliderView', FloatSliderView); + + + var FloatTextView = IPython.DOMWidgetView.extend({ + render : function(){ + // Called when view is rendered. + this.$el + .addClass('widget-hbox-single'); + this.$label = $('
') + .appendTo(this.$el) + .addClass('widget-hlabel') + .hide(); + this.$textbox = $('') + .addClass('input') + .addClass('widget-numeric-text') + .appendTo(this.$el); + this.$el_to_style = this.$textbox; // Set default element to style + this.update(); // Set defaults. + }, + + update : function(options){ + // Update the contents of this view + // + // Called when the model is changed. The model may have been + // changed by another view or by a state update from the back-end. + if (options === undefined || options.updated_view != this) { + var value = this.model.get('value'); + if (parseFloat(this.$textbox.val()) != value) { + this.$textbox.val(value); + } + + if (this.model.get('disabled')) { + this.$textbox.attr('disabled','disabled'); + } else { + this.$textbox.removeAttr('disabled'); + } + + var description = this.model.get('description'); + if (description.length === 0) { + this.$label.hide(); + } else { + this.$label.text(description); + this.$label.show(); + } + } + return FloatTextView.__super__.update.apply(this); + }, + + events: { + // Dictionary of events and their handlers. + + "keyup input" : "handleChanging", + "paste input" : "handleChanging", + "cut input" : "handleChanging", + + // Fires only when control is validated or looses focus. + "change input" : "handleChanged" + }, + + handleChanging: function(e) { + // Handles and validates user input. + + // Try to parse value as a float. + var numericalValue = 0.0; + if (e.target.value !== '') { + numericalValue = parseFloat(e.target.value); + } + + // If parse failed, reset value to value stored in model. + if (isNaN(numericalValue)) { + e.target.value = this.model.get('value'); + } else if (!isNaN(numericalValue)) { + if (this.model.get('max') !== undefined) { + numericalValue = Math.min(this.model.get('max'), numericalValue); + } + if (this.model.get('min') !== undefined) { + numericalValue = Math.max(this.model.get('min'), numericalValue); + } + + // Apply the value if it has changed. + if (numericalValue != this.model.get('value')) { + + // Calling model.set will trigger all of the other views of the + // model to update. + this.model.set('value', numericalValue, {updated_view: this}); + this.touch(); + } + } + }, + + handleChanged: function(e) { + // Applies validated input. + if (this.model.get('value') != e.target.value) { + e.target.value = this.model.get('value'); + } + } + }); + WidgetManager.register_widget_view('FloatTextView', FloatTextView); + + + var ProgressView = IPython.DOMWidgetView.extend({ + render : function(){ + // Called when view is rendered. + this.$el + .addClass('widget-hbox-single'); + this.$label = $('
') + .appendTo(this.$el) + .addClass('widget-hlabel') + .hide(); + this.$progress = $('
') + .addClass('progress') + .addClass('widget-progress') + .appendTo(this.$el); + this.$el_to_style = this.$progress; // Set default element to style + this.$bar = $('
') + .addClass('bar') + .css('width', '50%') + .appendTo(this.$progress); + this.update(); // Set defaults. + }, + + update : function(){ + // Update the contents of this view + // + // Called when the model is changed. The model may have been + // changed by another view or by a state update from the back-end. + var value = this.model.get('value'); + var max = this.model.get('max'); + var min = this.model.get('min'); + var percent = 100.0 * (value - min) / (max - min); + this.$bar.css('width', percent + '%'); + + var description = this.model.get('description'); + if (description.length === 0) { + this.$label.hide(); + } else { + this.$label.text(description); + this.$label.show(); + } + return ProgressView.__super__.update.apply(this); + }, + }); + WidgetManager.register_widget_view('ProgressView', ProgressView); +}); diff --git a/IPython/html/static/notebook/js/widgets/widget_int.js b/IPython/html/static/notebook/js/widgets/widget_int.js index 164323864..af0c3c7ab 100644 --- a/IPython/html/static/notebook/js/widgets/widget_int.js +++ b/IPython/html/static/notebook/js/widgets/widget_int.js @@ -5,6 +5,216 @@ // the file COPYING, distributed as part of this software. //---------------------------------------------------------------------------- -// This file is a place holder to maintain a one to one mapping of widget_*.py -// files and widget_*.js widget and test files. The Views for this model are -// shared with the bounded int, and can be found in widget_int_range.js. +//============================================================================ +// IntWidget +//============================================================================ + +/** + * @module IPython + * @namespace IPython + **/ + +define(["notebook/js/widgets/widget"], function(WidgetManager){ + + var IntSliderView = IPython.DOMWidgetView.extend({ + render : function(){ + // Called when view is rendered. + this.$el + .addClass('widget-hbox-single'); + this.$label = $('
') + .appendTo(this.$el) + .addClass('widget-hlabel') + .hide(); + this.$slider = $('
') + .slider({}) + .addClass('slider'); + + // Put the slider in a container + this.$slider_container = $('
') + .addClass('widget-hslider') + .append(this.$slider); + this.$el_to_style = this.$slider_container; // Set default element to style + this.$el.append(this.$slider_container); + + // Set defaults. + this.update(); + }, + + update : function(options){ + // Update the contents of this view + // + // Called when the model is changed. The model may have been + // changed by another view or by a state update from the back-end. + if (options === undefined || options.updated_view != this) { + // JQuery slider option keys. These keys happen to have a + // one-to-one mapping with the corrosponding keys of the model. + var jquery_slider_keys = ['step', 'max', 'min', 'disabled']; + _.each(jquery_slider_keys, function(key, i) { + var model_value = this.model.get(key); + if (model_value !== undefined) { + this.$slider.slider("option", key, model_value); + } + }); + + // WORKAROUND FOR JQUERY SLIDER BUG. + // The horizontal position of the slider handle + // depends on the value of the slider at the time + // of orientation change. Before applying the new + // workaround, we set the value to the minimum to + // make sure that the horizontal placement of the + // handle in the vertical slider is always + // consistent. + var orientation = this.model.get('orientation'); + var value = this.model.get('min'); + this.$slider.slider('option', 'value', value); + this.$slider.slider('option', 'orientation', orientation); + value = this.model.get('value'); + this.$slider.slider('option', 'value', value); + + // Use the right CSS classes for vertical & horizontal sliders + if (orientation=='vertical') { + this.$slider_container + .removeClass('widget-hslider') + .addClass('widget-vslider'); + this.$el + .removeClass('widget-hbox-single') + .addClass('widget-vbox-single'); + this.$label + .removeClass('widget-hlabel') + .addClass('widget-vlabel'); + + } else { + this.$slider_container + .removeClass('widget-vslider') + .addClass('widget-hslider'); + this.$el + .removeClass('widget-vbox-single') + .addClass('widget-hbox-single'); + this.$label + .removeClass('widget-vlabel') + .addClass('widget-hlabel'); + } + + var description = this.model.get('description'); + if (description.length === 0) { + this.$label.hide(); + } else { + this.$label.text(description); + this.$label.show(); + } + } + return IntSliderView.__super__.update.apply(this); + }, + + events: { + // Dictionary of events and their handlers. + "slide" : "handleSliderChange" + }, + + handleSliderChange: function(e, ui) { + // Called when the slider value is changed. + + // Calling model.set will trigger all of the other views of the + // model to update. + this.model.set('value', ~~ui.value, {updated_view: this}); // Double bit-wise not to truncate decimel + this.touch(); + }, + }); + WidgetManager.register_widget_view('IntSliderView', IntSliderView); + + + var IntTextView = IPython.DOMWidgetView.extend({ + render : function(){ + // Called when view is rendered. + this.$el + .addClass('widget-hbox-single'); + this.$label = $('
') + .appendTo(this.$el) + .addClass('widget-hlabel') + .hide(); + this.$textbox = $('') + .addClass('input') + .addClass('widget-numeric-text') + .appendTo(this.$el); + this.$el_to_style = this.$textbox; // Set default element to style + this.update(); // Set defaults. + }, + + update : function(options){ + // Update the contents of this view + // + // Called when the model is changed. The model may have been + // changed by another view or by a state update from the back-end. + if (options === undefined || options.updated_view != this) { + var value = this.model.get('value'); + if (parseInt(this.$textbox.val()) != value) { + this.$textbox.val(value); + } + + if (this.model.get('disabled')) { + this.$textbox.attr('disabled','disabled'); + } else { + this.$textbox.removeAttr('disabled'); + } + + var description = this.model.get('description'); + if (description.length === 0) { + this.$label.hide(); + } else { + this.$label.text(description); + this.$label.show(); + } + } + return IntTextView.__super__.update.apply(this); + }, + + events: { + // Dictionary of events and their handlers. + "keyup input" : "handleChanging", + "paste input" : "handleChanging", + "cut input" : "handleChanging", + + // Fires only when control is validated or looses focus. + "change input" : "handleChanged" + }, + + handleChanging: function(e) { + // Handles and validates user input. + + // Try to parse value as a float. + var numericalValue = 0; + if (e.target.value !== '') { + numericalValue = parseInt(e.target.value); + } + + // If parse failed, reset value to value stored in model. + if (isNaN(numericalValue)) { + e.target.value = this.model.get('value'); + } else if (!isNaN(numericalValue)) { + if (this.model.get('max') !== undefined) { + numericalValue = Math.min(this.model.get('max'), numericalValue); + } + if (this.model.get('min') !== undefined) { + numericalValue = Math.max(this.model.get('min'), numericalValue); + } + + // Apply the value if it has changed. + if (numericalValue != this.model.get('value')) { + + // Calling model.set will trigger all of the other views of the + // model to update. + this.model.set('value', numericalValue, {updated_view: this}); + this.touch(); + } + } + }, + + handleChanged: function(e) { + // Applies validated input. + if (this.model.get('value') != e.target.value) { + e.target.value = this.model.get('value'); + } + } + }); + WidgetManager.register_widget_view('IntTextView', IntTextView); +});