Jason Grout пре 8 година
родитељ
комит
5293a4a1e4
8 измењених фајлова са 269 додато и 0 уклоњено
  1. 1 0
      jupyterlab/index.js
  2. 1 0
      package.json
  3. 49 0
      src/csvwidget/plugin.ts
  4. 21 0
      src/csvwidget/theme.css
  5. 126 0
      src/csvwidget/widget.ts
  6. 1 0
      src/theme.css
  7. 1 0
      src/typings.d.ts
  8. 69 0
      typings/d3-dsv/d3-dsv.d.ts

+ 1 - 0
jupyterlab/index.js

@@ -19,6 +19,7 @@ var app = new phosphide.Application({
     require('jupyterlab/lib/filebrowser/plugin').fileBrowserExtension,
     require('jupyterlab/lib/help/plugin').helpHandlerExtension,
     require('jupyterlab/lib/imagewidget/plugin').imageHandlerExtension,
+    require('jupyterlab/lib/csvwidget/plugin').csvHandlerExtension,
     require('jupyterlab/lib/landing/plugin').landingExtension,
     require('jupyterlab/lib/main/plugin').mainExtension,
     require('jupyterlab/lib/mainmenu/plugin').mainMenuExtension,

+ 1 - 0
package.json

@@ -8,6 +8,7 @@
     "ansi_up": "^1.3.0",
     "backbone": "^1.2.0",
     "codemirror": "^5.12.0",
+    "d3-dsv": "^1.0.0",
     "diff-match-patch": "^1.0.0",
     "es6-promise": "^3.2.1",
     "file-loader": "^0.8.5",

+ 49 - 0
src/csvwidget/plugin.ts

@@ -0,0 +1,49 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import {
+  Application
+} from 'phosphide/lib/core/application';
+
+import {
+  DocumentRegistry
+} from '../docregistry';
+
+import {
+  CSVWidget, CSVWidgetFactory
+} from './widget';
+
+
+/**
+ * The list of file extensions for csv tables.
+ */
+const EXTENSIONS = ['.csv'];
+
+
+/**
+ * The table file handler extension.
+ */
+export
+const csvHandlerExtension = {
+  id: 'jupyter.extensions.csvHandler',
+  requires: [DocumentRegistry],
+  activate: activateCSVWidget
+};
+
+
+/**
+ * Activate the table widget extension.
+ */
+function activateCSVWidget(app: Application, registry: DocumentRegistry): void {
+    let options = {
+      fileExtensions: EXTENSIONS,
+      defaultFor: EXTENSIONS,
+      displayName: 'Table',
+      modelName: 'text',
+      preferKernel: false,
+      canStartKernel: false
+    };
+
+    registry.addWidgetFactory(new CSVWidgetFactory(), options);
+
+}

+ 21 - 0
src/csvwidget/theme.css

@@ -0,0 +1,21 @@
+/*-----------------------------------------------------------------------------
+| Copyright (c) Jupyter Development Team.
+| Distributed under the terms of the Modified BSD License.
+|----------------------------------------------------------------------------*/
+
+.jp-CSVWidget {
+  padding: 1em;
+  overflow: auto;
+}
+
+
+.jp-CSVWidget th,
+.jp-CSVWidget td {
+  border: 1px solid black;
+  padding: 0.2em 0.5em;
+}
+
+.jp-CSVWidget table {
+  border: 1px solid black;
+  border-collapse: collapse;
+}

+ 126 - 0
src/csvwidget/widget.ts

@@ -0,0 +1,126 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import {
+  IKernel
+} from 'jupyter-js-services';
+
+import {
+  Message
+} from 'phosphor-messaging';
+
+import {
+  Widget
+} from 'phosphor-widget';
+
+import {
+  ABCWidgetFactory, IDocumentModel, IDocumentContext
+} from '../docregistry';
+
+import {
+  csvParse
+} from 'd3-dsv';
+
+
+/**
+ * The class name added to a csv widget.
+ */
+const CSV_CLASS = 'jp-CSVWidget';
+
+
+/**
+ * A widget for csv tables.
+ */
+export
+class CSVWidget extends Widget {
+  /**
+   * Construct a new csv table widget.
+   */
+  constructor(context: IDocumentContext<IDocumentModel>) {
+    super();
+    this._context = context;
+    this.node.tabIndex = -1;
+    this.addClass(CSV_CLASS);
+
+    if (context.model.toString()) {
+      this.update();
+    }
+    context.pathChanged.connect(() => {
+      this.update();
+    });
+    context.model.contentChanged.connect(() => {
+      this.update();
+    });
+    context.contentsModelChanged.connect(() => {
+      this.update();
+    });
+  }
+
+  /**
+   * Dispose of the resources used by the widget.
+   */
+  dispose(): void {
+    if (this.isDisposed) {
+      return;
+    }
+    this._context = null;
+    super.dispose();
+  }
+
+  /**
+   * Handle `update-request` messages for the widget.
+   */
+  protected onUpdateRequest(msg: Message): void {
+    this.title.text = this._context.path.split('/').pop();
+    let cm = this._context.contentsModel;
+    if (cm === null) {
+      return;
+    }
+    let content = this._context.model.toString();
+    this.renderTable(content);
+  }
+
+  /**
+   * Render an html table from a csv string.
+   */
+  renderTable(content: string) {
+    let parsed = csvParse(content);
+    let table = document.createElement('table');
+    let header = document.createElement('tr');
+    for (let name of parsed.columns) {
+      let th = document.createElement('th');
+      th.textContent = name;
+      header.appendChild(th);
+    }
+    table.appendChild(header);
+    for (let row of parsed) {
+      let tr = document.createElement('tr');
+      for (let col of parsed.columns) {
+        let td = document.createElement('td');
+        td.textContent = row[col]
+        tr.appendChild(td);
+      }
+      table.appendChild(tr);
+    }
+    this.node.textContent = '';
+    this.node.appendChild(table);
+  }
+
+  private _context: IDocumentContext<IDocumentModel>;
+}
+
+
+/**
+ * A widget factory for csv tables.
+ */
+export
+class CSVWidgetFactory extends ABCWidgetFactory<CSVWidget, IDocumentModel> {
+  /**
+   * Create a new widget given a context.
+   */
+  createNew(context: IDocumentContext<IDocumentModel>, kernel?: IKernel.IModel): CSVWidget {
+    let widget = new CSVWidget(context);
+    this.widgetCreated.emit(widget);
+    return widget;
+  }
+}

+ 1 - 0
src/theme.css

@@ -3,6 +3,7 @@
 |
 | Distributed under the terms of the Modified BSD License.
 |----------------------------------------------------------------------------*/
+@import './csvwidget/theme.css';
 @import './docmanager/theme.css';
 @import './dialog/theme.css';
 @import './filebrowser/theme.css';

+ 1 - 0
src/typings.d.ts

@@ -8,3 +8,4 @@
 /// <reference path="../typings/require/require.d.ts"/>
 /// <reference path="../typings/xterm/xterm.d.ts"/>
 /// <reference path="../typings/marked/marked.d.ts"/>
+/// <reference path="../typings/d3-dsv/d3-dsv.d.ts"/>

+ 69 - 0
typings/d3-dsv/d3-dsv.d.ts

@@ -0,0 +1,69 @@
+// Type definitions for d3-dsv
+// Project: https://www.npmjs.com/package/d3-dsv
+// Definitions by: Jason Swearingen <https://jasonswearingen.github.io>
+// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
+
+
+//commonjs loader
+declare module "d3-dsv" {
+
+	/** A parser and formatter for DSV (CSV and TSV) files.
+Extracted from D3. */
+export
+    var loader: (
+        /** the symbol used to seperate cells in the row.*/
+        delimiter: string,
+        /** example: "text/plain" */
+        encoding?: string) => _d3dsv.D3Dsv;
+    export
+	function csvParse(content: string): any;
+}
+declare module _d3dsv {
+	/** A parser and formatter for DSV (CSV and TSV) files.
+Extracted from D3. */
+	export class D3Dsv {
+		/** Parses the specified string, which is the contents of a CSV file, returning an array of objects representing the parsed rows. 
+		The string is assumed to be RFC4180-compliant. 
+		Unlike the parseRows method, this method requires that the first line of the CSV file contains a comma-separated list of column names; 
+		these column names become the attributes on the returned objects. 
+		For example, consider the following CSV file:
+
+Year,Make,Model,Length
+1997,Ford,E350,2.34
+2000,Mercury,Cougar,2.38
+
+The resulting JavaScript array is:
+
+[  {"Year": "1997", "Make": "Ford", "Model": "E350", "Length": "2.34"},
+  {"Year": "2000", "Make": "Mercury", "Model": "Cougar", "Length": "2.38"} ]
+		 */
+		public parse<TRow>(
+			table: string, 
+			/** coerce cells (strings) into different types or modify them. return null to strip this row from the output results. */
+			accessor?: (row: any) => TRow
+			): TRow[];
+		/** Parses the specified string, which is the contents of a CSV file, returning an array of arrays representing the parsed rows. The string is assumed to be RFC4180-compliant. Unlike the parse method, this method treats the header line as a standard row, and should be used whenever the CSV file does not contain a header. Each row is represented as an array rather than an object. Rows may have variable length. For example, consider the following CSV file:
+
+1997,Ford,E350,2.34
+2000,Mercury,Cougar,2.38
+The resulting JavaScript array is:
+
+[  ["1997", "Ford", "E350", "2.34"],
+  ["2000", "Mercury", "Cougar", "2.38"] ]
+Note that the values themselves are always strings; they will not be automatically converted to numbers. See parse for details.*/
+		public parseRows<TRow>(
+			table: string,
+			/** coerce cells (strings) into different types or modify them. return null to strip this row from the output results.*/
+			accessor?: (row: string[]) => TRow
+			): TRow[];
+		/** Converts the specified array of rows into comma-separated values format, returning a string. This operation is the reverse of parse. Each row will be separated by a newline (\n), and each column within each row will be separated by a comma (,). Values that contain either commas, double-quotes (") or newlines will be escaped using double-quotes.
+
+Each row should be an object, and all object properties will be converted into fields. For greater control over which properties are converted, convert the rows into arrays containing only the properties that should be converted and use formatRows. */
+		public format(rows: any[]): string;
+		/** Converts the specified array of rows into comma-separated values format, returning a string. This operation is the reverse of parseRows. Each row will be separated by a newline (\n), and each column within each row will be separated by a comma (,). Values that contain either commas, double-quotes (") or newlines will be escaped using double-quotes. */
+		public formatRows(rows: any[]): string;
+
+
+	}
+
+}