├── .gitmodules ├── LICENSE ├── README.md ├── info.yaml ├── jquery.svg.min.js ├── jquery.svgdom.min.js └── main.js /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "annodoc"] 2 | path = annodoc 3 | url = https://github.com/akoehn/annodoc.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Arne Köhn 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tired of reading dependency structures as text in your jupyter 2 | notebook? This jupyter notebook extension visualizes NLP output using 3 | brat[^1], encapsuled by annodoc[^2]. 4 | 5 | Installation: 6 | * clone this repository 7 | * run `jupyter-nbextension install jupyter-annodoc`, add `--user` or `--symlink` if you like 8 | * run `jupyter-nbextension enable jupyter-annodoc` 9 | 10 | Open a jupyter notebook with a conll-x string as output, select that 11 | cell and click on the new jupyter-annodoc icon to convert the output. 12 | 13 | This plugin tries to be smart about the output you provide. If the 14 | output is enclosed in quotes it assumes jupyter shows a variables's 15 | content and will unescape the output. Otherwise it will use the 16 | output as-is. 17 | 18 | Currently, conll-x format is hardcoded. Patches welcome for automatic 19 | or manual selection of the right backend! 20 | 21 | If you use it, drop me a short mail for motivation :-) 22 | 23 | [^1]: http://brat.nlplab.org/ 24 | [^2]: http://spyysalo.github.io/annodoc/ 25 | -------------------------------------------------------------------------------- /info.yaml: -------------------------------------------------------------------------------- 1 | Type: IPython Notebook Extension 2 | Name: annodoc integration 3 | Description: visualizes dependency structures etc. using annodoc 4 | Link: readme.md 5 | Icon: icon.png 6 | Main: main.js 7 | Compatibility: 4.x, 5.x 8 | -------------------------------------------------------------------------------- /jquery.svg.min.js: -------------------------------------------------------------------------------- 1 | /* http://keith-wood.name/svg.html 2 | SVG for jQuery v1.5.0. 3 | Written by Keith Wood (kbwood{at}iinet.com.au) August 2007. 4 | Available under the MIT (http://keith-wood.name/licence.html) license. 5 | Please attribute the author if you use it. */ 6 | (function($){function SVGManager(){this._settings=[];this._extensions=[];this.regional=[];this.regional['']={errorLoadingText:'Error loading'};this.local=this.regional[''];this._uuid=new Date().getTime();this._ie=!!window.ActiveXObject}$.extend(SVGManager.prototype,{markerClassName:'hasSVG',propertyName:'svgwrapper',svgNS:'http://www.w3.org/2000/svg',xlinkNS:'http://www.w3.org/1999/xlink',_wrapperClass:SVGWrapper,_attrNames:{class_:'class',in_:'in',alignmentBaseline:'alignment-baseline',baselineShift:'baseline-shift',clipPath:'clip-path',clipRule:'clip-rule',colorInterpolation:'color-interpolation',colorInterpolationFilters:'color-interpolation-filters',colorRendering:'color-rendering',dominantBaseline:'dominant-baseline',enableBackground:'enable-background',fillOpacity:'fill-opacity',fillRule:'fill-rule',floodColor:'flood-color',floodOpacity:'flood-opacity',fontFamily:'font-family',fontSize:'font-size',fontSizeAdjust:'font-size-adjust',fontStretch:'font-stretch',fontStyle:'font-style',fontVariant:'font-variant',fontWeight:'font-weight',glyphOrientationHorizontal:'glyph-orientation-horizontal',glyphOrientationVertical:'glyph-orientation-vertical',horizAdvX:'horiz-adv-x',horizOriginX:'horiz-origin-x',imageRendering:'image-rendering',letterSpacing:'letter-spacing',lightingColor:'lighting-color',markerEnd:'marker-end',markerMid:'marker-mid',markerStart:'marker-start',stopColor:'stop-color',stopOpacity:'stop-opacity',strikethroughPosition:'strikethrough-position',strikethroughThickness:'strikethrough-thickness',strokeDashArray:'stroke-dasharray',strokeDashOffset:'stroke-dashoffset',strokeLineCap:'stroke-linecap',strokeLineJoin:'stroke-linejoin',strokeMiterLimit:'stroke-miterlimit',strokeOpacity:'stroke-opacity',strokeWidth:'stroke-width',textAnchor:'text-anchor',textDecoration:'text-decoration',textRendering:'text-rendering',underlinePosition:'underline-position',underlineThickness:'underline-thickness',vertAdvY:'vert-adv-y',vertOriginY:'vert-origin-y',wordSpacing:'word-spacing',writingMode:'writing-mode'},_attachSVG:function(a,b){var c=(a.namespaceURI===this.svgNS?a:null);var a=(c?null:a);if($(a||c).hasClass(this.markerClassName)){return}if(typeof b==='string'){b={loadURL:b}}else if(typeof b==='function'){b={onLoad:b}}$(a||c).addClass(this.markerClassName);try{if(!c){c=document.createElementNS(this.svgNS,'svg');c.setAttribute('version','1.1');if(a.clientWidth>0){c.setAttribute('width',a.clientWidth)}if(a.clientHeight>0){c.setAttribute('height',a.clientHeight)}a.appendChild(c)}this._afterLoad(a,c,b||{})}catch(e){$(a).html('

SVG is not supported natively on this browser

')}},_afterLoad:function(a,b,c){var c=c||this._settings[a.id];this._settings[a?a.id:'']=null;var d=new this._wrapperClass(b,a);$.data(a||b,$.svg.propertyName,d);try{if(c.loadURL){d.load(c.loadURL,c)}if(c.settings){d.configure(c.settings)}if(c.onLoad&&!c.loadURL){c.onLoad.apply(a||b,[d])}}catch(e){alert(e)}},_getSVG:function(a){return $(a).data(this.propertyName)},_destroySVG:function(a){a=$(a);if(!a.hasClass(this.markerClassName)){return}a.removeClass(this.markerClassName).removeData(this.propertyName);if(a[0].namespaceURI!==this.svgNS){a.empty()}},addExtension:function(a,b){this._extensions.push([a,b])},isSVGElem:function(a){return(a.nodeType===1&&a.namespaceURI===$.svg.svgNS)}});function SVGWrapper(a,b){this._svg=a;this._container=b;for(var i=0;i<$.svg._extensions.length;i++){var c=$.svg._extensions[i];this[c[0]]=new c[1](this)}}$.extend(SVGWrapper.prototype,{width:function(){return(this._container?this._container.clientWidth:this._svg.width)},height:function(){return(this._container?this._container.clientHeight:this._svg.height)},root:function(){return this._svg},configure:function(a,b,c){if(!a.nodeName){c=b;b=a;a=this._svg}if(c){for(var i=a.attributes.length-1;i>=0;i--){var d=a.attributes.item(i);if(!(d.nodeName==='onload'||d.nodeName==='version'||d.nodeName.substring(0,5)==='xmlns')){a.attributes.removeNamedItem(d.nodeName)}}}for(var e in b){a.setAttribute($.svg._attrNames[e]||e,b[e])}return this},getElementById:function(a){return this._svg.ownerDocument.getElementById(a)},change:function(a,b){if(a){for(var c in b){if(b[c]==null){a.removeAttribute($.svg._attrNames[c]||c)}else{a.setAttribute($.svg._attrNames[c]||c,b[c])}}}return this},_args:function(b,c,d){c.splice(0,0,'parent');c.splice(c.length,0,'settings');var e={};var f=0;if(b[0]!=null&&b[0].jquery){b[0]=b[0][0]}if(b[0]!=null&&!(typeof b[0]==='object'&&b[0].nodeName)){e['parent']=null;f=1}for(var i=0;i/g,'>'))}}}return b},_checkName:function(a){a=(a.substring(0,1)>='A'&&a.substring(0,1)<='Z'?a.toLowerCase():a);return(a.substring(0,4)==='svg:'?a.substring(4):a)},load:function(l,m){m=(typeof m==='boolean'?{addTo:m}:(typeof m==='function'?{onLoad:m}:(typeof m==='string'?{parent:m}:(typeof m==='object'&&m.nodeName?{parent:m}:(typeof m==='object'&&m.jquery?{parent:m}:m||{})))));if(!m.parent&&!m.addTo){this.clear(false)}var n=[this._svg.getAttribute('width'),this._svg.getAttribute('height')];var o=this;var p=function(a){a=$.svg.local.errorLoadingText+': '+a;if(m.onLoad){m.onLoad.apply(o._container||o._svg,[o,a])}else{o.text(null,10,20,a)}};var q=function(a){var b=new ActiveXObject('Microsoft.XMLDOM');b.validateOnParse=false;b.resolveExternals=false;b.async=false;b.loadXML(a);if(b.parseError.errorCode!==0){p(b.parseError.reason);return null}return b};var r=function(b){if(!b){return}if(b.documentElement.nodeName!=='svg'){var c=b.getElementsByTagName('parsererror');var d=(c.length?c[0].getElementsByTagName('div'):[]);p(!c.length?'???':(d.length?d[0]:c[0]).firstChild.nodeValue);return}var f=(m.parent?$(m.parent)[0]:o._svg);var g={};for(var i=0;i'}else{b='<'+a.nodeName;if(a.attributes){for(var i=0;i';var d=a.firstChild;while(d){b+=this._toSVG(d);d=d.nextSibling}b+=''}else{b+='/>'}}return b}});function SVGPath(){this._path=''}$.extend(SVGPath.prototype,{reset:function(){this._path='';return this},move:function(x,y,a){a=($.isArray(x)?y:a);return this._coords((a?'m':'M'),x,y)},line:function(x,y,a){a=($.isArray(x)?y:a);return this._coords((a?'l':'L'),x,y)},horiz:function(x,a){this._path+=(a?'h':'H')+($.isArray(x)?x.join(' '):x);return this},vert:function(y,a){this._path+=(a?'v':'V')+($.isArray(y)?y.join(' '):y);return this},curveC:function(a,b,c,d,x,y,e){e=($.isArray(a)?b:e);return this._coords((e?'c':'C'),a,b,c,d,x,y)},smoothC:function(a,b,x,y,c){c=($.isArray(a)?b:c);return this._coords((c?'s':'S'),a,b,x,y)},curveQ:function(a,b,x,y,c){c=($.isArray(a)?b:c);return this._coords((c?'q':'Q'),a,b,x,y)},smoothQ:function(x,y,a){a=($.isArray(x)?y:a);return this._coords((a?'t':'T'),x,y)},_coords:function(a,b,c,d,e,f,g){if($.isArray(b)){for(var i=0;i-1)}else{b=(c.apply($(this),[a]))}return!b});return b}}($.fn.hasClass);$.fn.attr=function(h){return function(a,b,c){if(typeof a==='string'&&b===undefined){var d=h.apply(this,arguments);if(d&&d.baseVal&&d.baseVal.numberOfItems!=null){b='';d=d.baseVal;if(a==='transform'){for(var i=0;i 2 | // 3 | // Distributed under the terms of the MIT License. 4 | 5 | var annodoc_root = '/nbextensions/jupyter-annodoc/annodoc/'; 6 | 7 | var webFontURLs = [ 8 | // annodoc_root + 'static/fonts/PT_Sans-Caption-Web-Regular.ttf', 9 | annodoc_root + 'static/fonts/Liberation_Sans-Regular.ttf' 10 | ]; 11 | 12 | define([ 13 | 'base/js/namespace', 14 | 'jquery', 15 | 'require', 16 | 'notebook/js/textcell', 17 | 'base/js/utils', 18 | 19 | // we ship our own jquery-svg as the one used by annodoc is too old. 20 | '/nbextensions/jupyter-annodoc/jquery.svg.min.js', 21 | '/nbextensions/jupyter-annodoc/jquery.svgdom.min.js', 22 | 23 | annodoc_root + 'lib/ext/waypoints.min.js', 24 | 25 | // brat helper modules 26 | annodoc_root + 'lib/brat/configuration.js', 27 | annodoc_root + 'lib/brat/util.js', 28 | annodoc_root + 'lib/brat/annotation_log.js', 29 | annodoc_root + 'lib/ext/webfont.js', 30 | // brat modules 31 | annodoc_root + 'lib/brat/dispatcher.js', 32 | annodoc_root + 'lib/brat/url_monitor.js', 33 | annodoc_root + 'lib/brat/visualizer.js', 34 | 35 | // embedding configuration 36 | annodoc_root + 'lib/local/config.js', 37 | 38 | // Annodoc 39 | annodoc_root + 'lib/local/annodoc.js', 40 | 41 | // NOTE: non-local libraries 42 | 'https://spyysalo.github.io/conllu.js/conllu.js' 43 | ], function(Jupyter, $, requirejs, textcell, utils) { 44 | "use strict"; 45 | 46 | var load_ipython_extension = function() { 47 | 48 | function loadCss(url) { 49 | var link = document.createElement("link"); 50 | link.type = "text/css"; 51 | link.rel = "stylesheet"; 52 | link.href = url; 53 | document.getElementsByTagName("head")[0].appendChild(link); 54 | } 55 | loadCss("/nbextensions/jupyter-annodoc/annodoc/css/style-vis.css") 56 | 57 | var visualizeConll = function(elem) { 58 | var element = $(elem) 59 | var parseFunction = Annodoc.parseFunctionMap[".conllx-parse"], 60 | cdataCopy = jQuery.extend(true, {}, Config.bratCollData); 61 | // hide to minimize "jumping" on draw 62 | // (https://github.com/spyysalo/annodoc/issues/11) 63 | element.hide(); 64 | Annodoc.embedAnnotation(element, parseFunction, cdataCopy); 65 | } 66 | 67 | var visualizeCurrentCell = function() { 68 | var cell = Jupyter.notebook.get_cell(Jupyter.notebook.index_or_selected()) 69 | var output = cell.output_area.element.find("pre")[0] 70 | // check if the output is an escaped string representation 71 | if (output.textContent.startsWith("'") || output.textContent.startsWith("'")) { 72 | // parse the representation into a string to replace \t etc. 73 | output.textContent = JSON.parse('"' + output.textContent.slice(1,-1) + '"') 74 | } 75 | visualizeConll(output) 76 | } 77 | 78 | var action = { 79 | icon: 'fa-sitemap', 80 | help : 'render output using annodoc', 81 | help_index : 'zz', 82 | handler : visualizeCurrentCell 83 | }; 84 | var prefix = 'jupyter-annodoc'; 85 | var action_name = 'visualize'; 86 | 87 | var full_action_name = Jupyter.actions.register(action, action_name, prefix); 88 | Jupyter.toolbar.add_buttons_group([full_action_name]); 89 | }; 90 | 91 | return { 92 | load_ipython_extension : load_ipython_extension 93 | }; 94 | }); 95 | --------------------------------------------------------------------------------