Karima Rafes has uploaded a new change for review. https://gerrit.wikimedia.org/r/76331
Change subject: Add tag extension lwgraph to insert a graph in the page (first graph : horizontal flow graph) ...................................................................... Add tag extension lwgraph to insert a graph in the page (first graph : horizontal flow graph) Change-Id: I87650a8b611e86e96fb849b29c69f6dd7fa69051 --- M LinkedWiki.php A js/flowchart.js A js/lwgraph.js 3 files changed, 559 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/LinkedWiki refs/changes/31/76331/1 diff --git a/LinkedWiki.php b/LinkedWiki.php index 377e969..b525f47 100644 --- a/LinkedWiki.php +++ b/LinkedWiki.php @@ -45,6 +45,15 @@ 'ext.LinkedWiki.table2CSV' => $linkedWikiTpl + array( 'scripts' => 'table2CSV.js', ), + 'ext.LinkedWiki.lwgraph' => $linkedWikiTpl + array( + 'scripts' => 'lwgraph.js', + ), + 'ext.LinkedWiki.flowchart' => $linkedWikiTpl + array( + 'scripts' => 'flowchart.js', + 'dependencies' => array( + 'ext.LinkedWiki.lwgraph', + ) + ), ); //Paths @@ -82,6 +91,7 @@ $parser->setFunctionHook( 'sparql', 'efSparqlParserFunction_Render' ); $parser->setFunctionHook( 'wsparql', 'efWsparqlParserFunction_Render' ); $parser->setFunctionHook( 'properties', 'efPropertiesParserFunction_Render' ); + $parser->setHook( 'lwgraph', 'efLwgraphRender' ); return true; } @@ -103,6 +113,42 @@ return SparqlTools::decodeURItoIRI($resolverurl).$parser->getTitle()->getPrefixedDBkey(); } +function efLwgraphRender( $input, array $args, Parser $parser, PPFrame $frame ) { + global $wgOut; + $html = ""; + $width = isset($args["width"])?$args["width"]:"100%"; + $height = isset($args["height"])?$args["height"]:"150px"; + $border = isset($args["border"]) && $args["border"]>0 ? "border:".$args["border"]."px solid #000000;" : "" ; + + if (isset($args["debug"]) && $args["debug"] == "true"){ + $attr = array(); + foreach( $args as $name => $value ) + $attr[] = $name . ' = ' . $value ; + + $html .= "<div><b>lwgraph DEBUG :</b><br/>".implode( '<br/>', $attr )."</div>"; + $html .= "<pre>".htmlspecialchars( $input )."</pre>"; + + } + + if (isset($args["type"]) == "flow"){ + $wgOut->addModules('ext.LinkedWiki.flowchart'); + } + + preg_match_all("/\[\[([^\]\|]*)(?:\|[^\]]*)?\]\]/U", $input,$out); + $arrayTitle = array_unique ($out[1]); + + $textGraph = $input; + foreach ($arrayTitle as $title) { + $titleObject = Title::newFromText( $title ); + if ( !$titleObject->exists() ) + $textGraph = str_replace("[[".$title, "~[[".$title, $textGraph); + $textGraph = str_replace("~~", "~", $textGraph); + } + $html .= "<canvas class=\"lwgraph-flow\" style=\"".$border."width: ".$width.";height:".$height."\">".$textGraph."</canvas>"; + + return array($html, 'isHTML' => true); +} + function efSparqlParserFunction_Render( $parser) { //global $wgLinkedWikiLocalEndPoint,$wgLinkedWikiEndPoint,$wgLinkedWikiGraphWiki; global $wgOut; diff --git a/js/flowchart.js b/js/flowchart.js new file mode 100644 index 0000000..f37cd9b --- /dev/null +++ b/js/flowchart.js @@ -0,0 +1,356 @@ +function lwFlowChart($canvas,graphStr){ + this.tabLines = new Array(); + this.margemin = 10; + this.margex = 10; + this.margey = 10; + this.a = $canvas[0].getContext('2d'); + this.ratio = 0; + + //BEGIN CONSTRUCTOR + var regLine = /(?:=(?:(~*)\[\[([^\]\|]*)(?:\|([^\]]*))?\]\])?=>)?(~*)\[\[([^\]\|]*)(?:\|([^\]]*))?\]\]/g; + + //Read shape arrow and rect + //console.log(graphStr); + var lines = graphStr.split("\n"); + for(i in lines){ + var line = lines[i].trim(); + //console.log(lines[i]); + if(line != ""){ + var flowChartLine = new lwFlowChartLine(); + var first = true; + while ((myArray = regLine.exec(line)) !== null) + { + //var msg = myArray[0]+"\n"; + //msg += "1 Exist property page: " + myArray[1] +"\n"; + //msg += "2 property title page: " + myArray[2] +"\n"; + //msg += "3 property label : " + myArray[3]+"\n"; + //msg += "4 Exist object page : " + myArray[4]+"\n"; + //msg += "5 object title page : " + myArray[5]+"\n"; + //msg += "6 object label : " + myArray[6]+"\n"; + //console.log(msg); + if(!first){ + if(myArray[1] != undefined){ + flowChartLine.addShape(new lwFlowChartShape(myArray[2],myArray[3],myArray[1]!="~",true)); + }else{ + flowChartLine.addShape(new lwFlowChartShape(null,null,null,true)); + } + } + flowChartLine.addShape(new lwFlowChartShape(myArray[5],myArray[6],myArray[4]!="~",false)); + first = false; + } + this.tabLines.push(flowChartLine); + } + } + + //Calculate size + //console.log(this.tabLines); + var heightLine = 60; + var nbLine = 0; + for(var i in this.tabLines){ + var line = this.tabLines[i]; + for(var j in line.shapes){ + var shape = line.shapes[j]; + shape.label = shape.label == null ? shape.title : shape.label; + if(shape.isArrow){ + shape.width = $canvas.lwDrawLineArrow(shape.label,"red",0,0,0,0,false); + }else{ + shape.width = $canvas.lwDrawRect(shape.label,"red",0,0,false); + } + shape.height = 40; + shape.centery= heightLine*nbLine + heightLine/2; + var beforex = 0; + if(shape.pointerShapesBefore.length > 0){ + beforex = shape.pointerShapesBefore[0].rightx ; + } + shape.centerx= beforex + shape.width/2; + + shape.rightx= shape.centerx + shape.width/2; + shape.righty= shape.centery; + + shape.leftx= shape.centerx - shape.width/2; + shape.lefty= shape.centery; + + shape.topx=shape.centerx; + shape.topy=shape.centery - heightLine/2; + shape.downx=shape.centerx; + shape.downy=shape.centery + heightLine/2; + + if(nbLine == 0) + shape.fixed=true; + + } + nbLine++; + } + /////////////// + + //Build array shape fixed and to link shapes + var arrayShapeFixed = this.tabLines[0].shapes; + for(var iline = 1 ; iline < (this.tabLines.length) ; iline++){ + for(var i = 0; i < arrayShapeFixed.length; i++){ + var shapeFixed = arrayShapeFixed[i]; + for(l in this.tabLines){ + var line = this.tabLines[l]; + for(j in line.shapes){ + var shape = line.shapes[j]; + if(!shape.fixed && !shape.isArrow && shape.title == shapeFixed.title){ + for(var b = 0; b < shape.pointerShapesAfter.length; b++){ + shape.pointerShapesAfter[b].leftx = shapeFixed.rightx; + shape.pointerShapesAfter[b].lefty = shapeFixed.righty; + shape.pointerShapesAfter[b].removeShapeBefore(shape); + shape.pointerShapesAfter[b].addShapeBefore(shapeFixed); + shapeFixed.addShapeAfter(shape.pointerShapesAfter[b]); + } + for(var a = 0; a < shape.pointerShapesBefore.length; a++){ + shape.pointerShapesBefore[a].rightx = shapeFixed.leftx; + shape.pointerShapesBefore[a].righty = shapeFixed.lefty; + shape.pointerShapesBefore[a].removeShapeAfter(shape); + shape.pointerShapesBefore[a].addShapeAfter(shapeFixed); + shapeFixed.addShapeBefore(shape.pointerShapesBefore[a]); + } + line.shapes.splice(j,1); + } + } + } + } + for(var s in this.tabLines[iline].shapes) + this.tabLines[iline].shapes[s].fixed = true; + arrayShapeFixed = arrayShapeFixed.concat(this.tabLines[iline].shapes); + //console.log(arrayShapeFixed); + } + + //Calculate the good position + for(var s in arrayShapeFixed){ + var shape = arrayShapeFixed[s]; + shape.checkPlace(); + } + + //calc size of the graph + var xmax = 0; + var ymax = 0; + for(var s in arrayShapeFixed){ + var shape = arrayShapeFixed[s]; + if(shape.rightx > xmax) + xmax = shape.rightx; + if(shape.downy > ymax) + ymax = shape.downy; + } + + //resize the graph in the canvas + var ratiox = $canvas[0].width / (this.margemin*2 + xmax); + var ratioy = $canvas[0].height / (this.margemin*2 +ymax ); + //console.log("ratiox"+ratiox); + //console.log("ratioy"+ratioy); + + this.a.save(); + if(ratiox<ratioy) + { + this.ratio = ratiox; + this.a.scale(this.ratio,this.ratio); + this.margex = this.margemin; + this.margey = ($canvas[0].height - ymax*ratiox)/2; + } + else + { + this.ratio = ratioy; + this.a.scale(this.ratio,this.ratio); + this.margex = ($canvas[0].width - xmax*ratioy)/2 ; + this.margey = this.margemin; + } + + this.a.translate(this.margex ,this.margey ); + + // a.beginPath(); + // a.rect(0, 0, xmax, ymax); + // a.lineWidth = 2; + // a.strokeStyle = 'red'; + // a.stroke(); + + /////////////// + + //draw the graph + for(i in this.tabLines){ + var line = this.tabLines[i]; + for(j in line.shapes){ + var shape = line.shapes[j]; + var color = shape.isPageExist ? "black" : "red"; + if(shape.isArrow){ + shape.width = $canvas.lwDrawLineArrow( + shape.label, + color, + shape.leftx, + shape.lefty, + shape.rightx, + shape.righty, + true); + }else{ + $canvas.lwDrawRect(shape.label,color,shape.centerx,shape.centery,true); + } + } + } + + this.a.restore(); + + //END CONSTRUCTOR + + //return the shape without the pointer + this.getShape= function (x,y) + { + for(i in this.tabLines){ + var line = this.tabLines[i]; + for(j in line.shapes){ + var shape = line.shapes[j]; + var xmin = shape.leftx * this.ratio + this.margex ; + var xmax = shape.rightx * this.ratio + this.margex ; + var ymin = shape.topy * this.ratio + this.margey ; + var ymax = shape.downy * this.ratio + this.margey ; + if(x > xmin && x < xmax && y > ymin && y < ymax ) + return shape; + } + } + return null; + } +} + +function lwFlowChartLine() +{ + this.shapes=new Array(); + this.addShape= function (shape) + { + if(this.shapes[this.shapes.length-1]!=undefined){ + var shapeBefore = this.shapes[this.shapes.length-1]; + shape.addShapeBefore(shapeBefore); + shapeBefore.addShapeAfter(shape); + } + this.shapes.push(shape); + } +} + +function lwFlowChartShape(title,label,isPageExist,isArrow) +{ + this.centerx=0; + this.centery=0; + this.topx=0; + this.topy=0; + this.rightx=0; + this.righty=0; + this.downx=0; + this.downy=0; + this.leftx=0; + this.lefty=0; + this.width=0; + this.height=0; + this.title=title; + this.label=label; + this.isPageExist=isPageExist; + this.isArrow=isArrow; + this.fixed=false; + this.pointerShapesBefore=new Array(); + this.pointerShapesAfter=new Array(); + + this.addShapeBefore= function (shape) + { + this.pointerShapesBefore.push(shape); + } + + this.addShapeAfter= function (shape) + { + this.pointerShapesAfter.push(shape); + } + + this.removeShapeBefore= function (shape) + { + for(var i = 0; i < this.pointerShapesBefore.length; i++) { + if(this.pointerShapesBefore[i] === shape) { + this.pointerShapesBefore.splice(i, 1); + } + } + } + + this.removeShapeAfter= function (shape) + { + for(var i = 0; i < this.pointerShapesAfter.length; i++) { + if(this.pointerShapesAfter[i] === shape) { + this.pointerShapesAfter.splice(i, 1); + } + } + } + + this.setLeftx= function (x) + { + this.leftx = x ; + this.rightx = x + this.width; + this.centerx = this.leftx + this.width/2; + this.topx=this.centerx; + this.downx=this.centerx; + } + this.setRightx= function (x) + { + this.leftx = x - this.width; + this.rightx = x ; + this.centerx = this.leftx + this.width/2; + this.topx=this.centerx; + this.downx=this.centerx; + } + + this.checkPlace= function () + { + if(this.leftx > (this.rightx - this.width) ){ + this.setLeftx(this.leftx); + + for(var a in this.pointerShapesAfter){ + this.pointerShapesAfter[a].leftx = this.rightx; + } + for(var b in this.pointerShapesBefore){ + this.pointerShapesBefore[b].rightx = this.leftx; + } + + for(var a in this.pointerShapesAfter){ + this.pointerShapesAfter[a].checkPlace(); + } + for(var b in this.pointerShapesBefore){ + this.pointerShapesBefore[b].checkPlace(); + } + } + } +} + + +$( function () { + + function getMousePos(canvas, evt) { + var rect = canvas.getBoundingClientRect(); + return { + x: evt.clientX - rect.left, + y: evt.clientY - rect.top + }; + } + + var $elsCanvas = $("canvas.lwgraph-flow"); + + $elsCanvas.each(function(index, canvas) { + canvas.width = parseInt($(this).css('width')); + canvas.height = parseInt($(this).css('height')); + + var graphFlow = new lwFlowChart($(this),$(this).text()); + canvas.addEventListener('click', function(evt) { + var mousePos = getMousePos(canvas, evt); + var shape = graphFlow.getShape(mousePos.x ,mousePos.y) + if( shape != null){ + var url = mw.config.get( 'wgScript' ) + '?title=' + encodeURIComponent(shape.title) ; + if(shape.isPageExist) + window.location.href = url; + else + window.location.href = url+"&action=edit&redlink=1"; + } + }); + + canvas.addEventListener('mousemove', function(evt) { + var mousePos = getMousePos(canvas, evt); + var shape = graphFlow.getShape(mousePos.x ,mousePos.y) + if( shape != null) + $(this).css('cursor', 'pointer'); + else + $(this).css('cursor', 'default'); + }, false); + }); +}); \ No newline at end of file diff --git a/js/lwgraph.js b/js/lwgraph.js new file mode 100644 index 0000000..1f16c1f --- /dev/null +++ b/js/lwgraph.js @@ -0,0 +1,157 @@ +jQuery.fn.lwDrawPolygon = function(shape) { + var a = $(this)[0].getContext('2d'); + + a.beginPath(); + a.moveTo(shape[0][0],shape[0][1]); + for(p in shape) + if (p > 0) a.lineTo(shape[p][0],shape[p][1]); + a.lineTo(shape[0][0],shape[0][1]); + a.fill(); + +}; + +function lwTranslateShape(shape,x,y) { + var rv = []; + for(p in shape) + rv.push([ shape[p][0] + x, shape[p][1] + y ]); + return rv; + +}; + +function lwRotateShape(shape,ang) { + + function rotatePoint(ang,x,y) { + return [ + (x * Math.cos(ang)) - (y * Math.sin(ang)), + (x * Math.sin(ang)) + (y * Math.cos(ang)) + ]; + }; + + var rv = []; + for(p in shape) + rv.push(rotatePoint(ang,shape[p][0],shape[p][1])); + return rv; + +}; + +jQuery.fn.lwDrawLineArrow= function(text,color,x1,y1,x2,y2,draw) { + var a = $(this)[0].getContext('2d'); + var arrow = [ + [ 2, 0 ], + [ -10, -4 ], + [ -10, 4] + ]; + var marge = 10 ; + var height = 14; + var width = 0; + if(text !=null){ + a.save(); + + a.font = '14px Arial'; + var metrics = a.measureText(text); + width = metrics.width; + + var cx = x1 > x2 ? x2 + x1 - x2 : x1 + x2 - x1; + var cy = y1 > y2 ? y2 + y1 - y2 : y1 + y2 - y1; + //a.translate(x1 - width/2 - marge -5 + x2-x1,y1 - height/2 + y2-y1); + a.translate(cx- width/2 - marge -5 ,cy - height/2 ); + a.textAlign = 'center'; + a.textBaseline = 'middle'; + a.fillStyle = color; + a.fillText(text, 0,0); + a.restore(); + } + if(draw){ + a.beginPath(); + a.moveTo(x1,y1); + a.lineTo(x2,y2); + a.stroke(); + var ang = Math.atan2(y2-y1,x2-x1); + $(this).lwDrawPolygon(lwTranslateShape(lwRotateShape(arrow,ang),x2,y2)); + } + + return width + marge*2; +}; + + +jQuery.fn.lwDrawRect= function(text,color,x,y,draw) { + var a = $(this)[0].getContext('2d'); + var rect = [[-30,30],[-30,-30],[30,-30],[30,30],[-28,30],[-28,28],[28,28],[28,-28],[-28,-28],[-28,30]]; + var marge = 15; + a.save(); + + a.translate(x,y); + var height = 20; + a.font = '20px Arial'; + var metrics = a.measureText(text); + + var width = metrics.width; + if(draw){ + for(var i = 0; i < rect.length ; i++){ + //console.log(rect[i][0]); + //console.log(rect[i][1]); + rect[i][0] = rect[i][0]>0? rect[i][0] -(30-marge) + width/2: rect[i][0]+(30-marge) - width/2 ; + rect[i][1] = rect[i][1]>0? rect[i][1] -(30-marge) + height/2: rect[i][1]+(30-marge) - height/2 ; + } + $(this).lwDrawPolygon(lwTranslateShape(rect,0,0)); + a.textAlign = 'center'; + a.textBaseline = 'middle'; + a.fillStyle = color; + a.fillText(text, 0,0); + } + a.restore(); + + return width + 30; + +}; + +// $( function () { +// // This code must not be executed before the document is loaded. +// +// function getMousePos(canvas, evt) { +// var rect = canvas.getBoundingClientRect(); +// return { +// x: evt.clientX - rect.left, +// y: evt.clientY - rect.top +// }; +// } +// +// var $elsCanvas = $("canvas.lwgraph-flow"); +// $elsCanvas.css("border","2px solid black"); +// +// $elsCanvas.each(function(index, canvas) { +// canvas.width = parseInt($(this).css('width')); +// canvas.height = parseInt($(this).css('height')); +// +// console.log( index + ": " + $(this).text() ); +// +// canvas.addEventListener('click', function(evt) { +// var mousePos = getMousePos(canvas, evt); +// var shape = graphXXXX.getShape(mousePos.x ,mousePos.y) +// if( shape != null){ +// var url = mw.config.get( 'wgScript' ) + '?title=' + encodeURIComponent(shape.title) ; +// if(shape.isPageExist) +// window.location.href = url; +// else +// window.location.href = url+"&action=edit&redlink=1"; +// } +// }); +// +// canvas.addEventListener('mousemove', function(evt) { +// var mousePos = getMousePos(canvas, evt); +// //console.log('Mouse position: ' + mousePos.x + ',' + mousePos.y); +// var shape = graphXXXX.getShape(mousePos.x ,mousePos.y) +// if( shape != null) +// $(this).css('cursor', 'pointer'); +// else +// $(this).css('cursor', 'default'); +// }, false); +// +// +// //$(this).lwDrawPolygon(lwTranslateShape(lwRotateShape(shape,Math.PI ),50,50)); +// //$(this).lwDrawLineArrow(0,0,250,50); +// //$(this).lwDrawRect("tteteteteoto",250,50); +// +// }); +// +// }); -- To view, visit https://gerrit.wikimedia.org/r/76331 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I87650a8b611e86e96fb849b29c69f6dd7fa69051 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/LinkedWiki Gerrit-Branch: master Gerrit-Owner: Karima Rafes <karima.ra...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits