diff --git a/IPython/html/widgets/string/__init__.py b/IPython/html/widgets/string/__init__.py
new file mode 100644
index 0000000..c423f7a
--- /dev/null
+++ b/IPython/html/widgets/string/__init__.py
@@ -0,0 +1 @@
+from widget import StringWidget
\ No newline at end of file
diff --git a/IPython/html/widgets/string/model.js b/IPython/html/widgets/string/model.js
new file mode 100644
index 0000000..84e099e
--- /dev/null
+++ b/IPython/html/widgets/string/model.js
@@ -0,0 +1,2 @@
+var StringWidgetModel = IPython.WidgetModel.extend({});
+IPython.notebook.widget_manager.register_widget_model('StringWidgetModel', StringWidgetModel);
diff --git a/IPython/html/widgets/string/view_textarea.js b/IPython/html/widgets/string/view_textarea.js
new file mode 100644
index 0000000..29ab80d
--- /dev/null
+++ b/IPython/html/widgets/string/view_textarea.js
@@ -0,0 +1,35 @@
+var TextareaView = IPython.WidgetView.extend({
+
+ // Called when view is rendered.
+ render : function(){
+ this.$el
+ .html('')
+ .addClass(this.model.comm.comm_id);
+ this.$textbox = $('')
+ .attr('rows', 5)
+ .appendTo(this.$el);
+ this.update(); // Set defaults.
+ },
+
+ // Handles: Backend -> Frontend Sync
+ // Frontent -> Frontend Sync
+ update : function(){
+ if (!this.user_invoked_update) {
+ this.$textbox.val(this.model.get('value'));
+ }
+ },
+
+ events: {"keyup textarea" : "handleChanging",
+ "paste textarea" : "handleChanging",
+ "cut textarea" : "handleChanging"},
+
+ // Handles and validates user input.
+ handleChanging: function(e) {
+ this.user_invoked_update = true;
+ this.model.set('value', e.target.value);
+ this.model.apply(this);
+ this.user_invoked_update = false;
+ },
+});
+
+IPython.notebook.widget_manager.register_widget_view('TextareaView', TextareaView);
diff --git a/IPython/html/widgets/string/view_textbox.js b/IPython/html/widgets/string/view_textbox.js
new file mode 100644
index 0000000..0c770fd
--- /dev/null
+++ b/IPython/html/widgets/string/view_textbox.js
@@ -0,0 +1,35 @@
+var TextboxView = IPython.WidgetView.extend({
+
+ // Called when view is rendered.
+ render : function(){
+ this.$el
+ .html('')
+ .addClass(this.model.comm.comm_id);
+ this.$textbox = $('')
+ .addClass('input')
+ .appendTo(this.$el);
+ this.update(); // Set defaults.
+ },
+
+ // Handles: Backend -> Frontend Sync
+ // Frontent -> Frontend Sync
+ update : function(){
+ if (!this.user_invoked_update) {
+ this.$textbox.val(this.model.get('value'));
+ }
+ },
+
+ events: {"keyup input" : "handleChanging",
+ "paste input" : "handleChanging",
+ "cut input" : "handleChanging"},
+
+ // Handles and validates user input.
+ handleChanging: function(e) {
+ this.user_invoked_update = true;
+ this.model.set('value', e.target.value);
+ this.model.apply(this);
+ this.user_invoked_update = false;
+ },
+});
+
+IPython.notebook.widget_manager.register_widget_view('TextboxView', TextboxView);
diff --git a/IPython/html/widgets/string/widget.py b/IPython/html/widgets/string/widget.py
new file mode 100644
index 0000000..97b272e
--- /dev/null
+++ b/IPython/html/widgets/string/widget.py
@@ -0,0 +1,14 @@
+import os
+
+from ..widget import Widget
+from IPython.utils.traitlets import Unicode, Bool
+from IPython.utils.javascript import display_all_js
+
+class StringWidget(Widget):
+ target_name = Unicode('StringWidgetModel')
+ default_view_name = Unicode('TextboxView')
+ _keys = ['value', 'row_count', 'disabled']
+
+ value = Unicode()
+ disabled = Bool(False) # Enable or disable user changes
+
\ No newline at end of file