{
 "metadata": {
  "cell_tags": [
   [
    "<None>",
    null
   ]
  ],
  "name": ""
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import base64\n",
      "from __future__ import print_function # py 2.7 compat.\n",
      "from IPython.html import widgets # Widget definitions.\n",
      "from IPython.utils.traitlets import Unicode # Traitlet needed to add synced attributes to the widget."
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 1
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "This is a custom widget that allows the user to upload file data to the notebook server.  The file data is sent via a statefull `value` attribute of the widget.  The widget has an upload failed event that fires in the front-end and is echoed to the back-end using a custom msg."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "class FileWidget(widgets.DOMWidget):\n",
      "    _view_name = Unicode('FilePickerView', sync=True)\n",
      "    value = Unicode(sync=True)\n",
      "    filename = Unicode(sync=True)\n",
      "    \n",
      "    def __init__(self, **kwargs):\n",
      "        \"\"\"Constructor\"\"\"\n",
      "        widgets.DOMWidget.__init__(self, **kwargs) # Call the base.\n",
      "        \n",
      "        # Allow the user to register error callbacks with the following signatures:\n",
      "        #    callback()\n",
      "        #    callback(sender)\n",
      "        self.errors = widgets.CallbackDispatcher(accepted_nargs=[0, 1])\n",
      "        \n",
      "        # Listen for custom msgs\n",
      "        self.on_msg(self._handle_custom_msg)\n",
      "\n",
      "    def _handle_custom_msg(self, content):\n",
      "        \"\"\"Handle a msg from the front-end.\n",
      "\n",
      "        Parameters\n",
      "        ----------\n",
      "        content: dict\n",
      "            Content of the msg.\"\"\"\n",
      "        if 'event' in content and content['event'] == 'error':\n",
      "            self.errors()\n",
      "            self.errors(self)\n",
      "    "
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 2
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%javascript\n",
      "\n",
      "require([\"widgets/js/widget\"], function(WidgetManager){\n",
      "\n",
      "    var FilePickerView = IPython.WidgetView.extend({\n",
      "        render: function(){\n",
      "            // Render the view.\n",
      "            this.setElement($('<input />')\n",
      "                .attr('type', 'file'));\n",
      "        },\n",
      "        \n",
      "        events: {\n",
      "            // List of events and their handlers.\n",
      "            'change': 'handle_file_change',\n",
      "        },\n",
      "       \n",
      "        handle_file_change: function(evt) { \n",
      "            // Handle when the user has changed the file.\n",
      "            \n",
      "            // Retrieve the first (and only!) File from the FileList object\n",
      "            var file = evt.target.files[0];\n",
      "            if (file) {\n",
      "\n",
      "                // Read the file's textual content and set value to those contents.\n",
      "                var that = this;\n",
      "                var file_reader = new FileReader();\n",
      "                file_reader.onload = function(e) {\n",
      "                    that.model.set('value', e.target.result);\n",
      "                    that.touch();\n",
      "                }\n",
      "                file_reader.readAsText(file);\n",
      "            } else {\n",
      "\n",
      "                // The file couldn't be opened.  Send an error msg to the\n",
      "                // back-end.\n",
      "                this.send({ 'event': 'error' });\n",
      "            }\n",
      "\n",
      "            // Set the filename of the file.\n",
      "            this.model.set('filename', file.name);\n",
      "            this.touch();\n",
      "        },\n",
      "    });\n",
      "        \n",
      "    // Register the DatePickerView with the widget manager.\n",
      "    WidgetManager.register_widget_view('FilePickerView', FilePickerView);\n",
      "});"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "javascript": [
        "\n",
        "require([\"widgets/js/widget\"], function(WidgetManager){\n",
        "\n",
        "    var FilePickerView = IPython.WidgetView.extend({\n",
        "        render: function(){\n",
        "            // Render the view.\n",
        "            this.setElement($('<input />')\n",
        "                .attr('type', 'file'));\n",
        "        },\n",
        "        \n",
        "        events: {\n",
        "            // List of events and their handlers.\n",
        "            'change': 'handle_file_change',\n",
        "        },\n",
        "       \n",
        "        handle_file_change: function(evt) { \n",
        "            // Handle when the user has changed the file.\n",
        "            \n",
        "            // Retrieve the first (and only!) File from the FileList object\n",
        "            var file = evt.target.files[0];\n",
        "            if (file) {\n",
        "\n",
        "                // Read the file's textual content and set value to those contents.\n",
        "                var that = this;\n",
        "                var file_reader = new FileReader();\n",
        "                file_reader.onload = function(e) {\n",
        "                    that.model.set('value', e.target.result);\n",
        "                    that.touch();\n",
        "                }\n",
        "                file_reader.readAsText(file);\n",
        "            } else {\n",
        "\n",
        "                // The file couldn't be opened.  Send an error msg to the\n",
        "                // back-end.\n",
        "                this.send({ 'event': 'error' });\n",
        "            }\n",
        "\n",
        "            // Set the filename of the file.\n",
        "            this.model.set('filename', file.name);\n",
        "            this.touch();\n",
        "        },\n",
        "    });\n",
        "        \n",
        "    // Register the DatePickerView with the widget manager.\n",
        "    WidgetManager.register_widget_view('FilePickerView', FilePickerView);\n",
        "});"
       ],
       "metadata": {},
       "output_type": "display_data",
       "text": [
        "<IPython.core.display.Javascript at 0x36df2d0>"
       ]
      }
     ],
     "prompt_number": 3
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "The following shows how the file widget can be used."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "file_widget = FileWidget()\n",
      "\n",
      "# Register an event to echo the filename when it has been changed.\n",
      "def file_loading():\n",
      "    print(\"Loading %s\" % file_widget.filename)\n",
      "file_widget.on_trait_change(file_loading, 'filename')\n",
      "\n",
      "# Register an event to echo the filename and contents when a file\n",
      "# has been uploaded.\n",
      "def file_loaded():\n",
      "    print(\"Loaded, file contents: %s\" % file_widget.value)\n",
      "file_widget.on_trait_change(file_loaded, 'value')\n",
      "\n",
      "# Register an event to print an error message when a file could not\n",
      "# be opened.  Since the error messages are not handled through\n",
      "# traitlets but instead handled through custom msgs, the registration\n",
      "# of the handler is different than the two examples above.  Instead\n",
      "# the API provided by the CallbackDispatcher must be used.\n",
      "def file_failed():\n",
      "    print(\"Could not load file contents of %s\" % file_widget.filename)\n",
      "file_widget.errors.register_callback(file_failed)\n",
      "\n",
      "file_widget"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Loading test.txt\n"
       ]
      },
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Loaded, file contents: Hello World!\n",
        "\n"
       ]
      }
     ],
     "prompt_number": 4
    }
   ],
   "metadata": {}
  }
 ]
}
