diff --git a/src/diagrams/flowchart/flowDb.js b/src/diagrams/flowchart/flowDb.js index 4217cebd1..b34db18ed 100644 --- a/src/diagrams/flowchart/flowDb.js +++ b/src/diagrams/flowchart/flowDb.js @@ -247,7 +247,7 @@ const setTooltip = function(ids, tooltip) { }); }; -const setClickFun = function(id, functionName) { +const setClickFun = function(id, functionName, functionArgs) { let domId = lookUpDomId(id); // if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id; if (configApi.getConfig().securityLevel !== 'loose') { @@ -256,6 +256,29 @@ const setClickFun = function(id, functionName) { if (typeof functionName === 'undefined') { return; } + let argList = []; + if (typeof functionArgs === 'string') { + /* Splits functionArgs by ',', ignoring all ',' in double quoted strings */ + argList = functionArgs.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/); + for (let i = 0; i < argList.length; i++) { + let item = argList[i].trim(); + /* Removes all double quotes at the start and end of an argument */ + /* This preserves all starting and ending whitespace inside */ + if (item.charAt(0) === '"' && item.charAt(item.length - 1) === '"') { + item = item.substr(1, item.length - 2); + + + + } + argList[i] = item; + } + } + + /* if no arguments passed into callback, default to passing in id */ + if (argList.length === 0) { + argList.push(id); + } + if (typeof vertices[id] !== 'undefined') { vertices[id].haveCallback = true; funs.push(function() { @@ -264,7 +287,7 @@ const setClickFun = function(id, functionName) { elem.addEventListener( 'click', function() { - utils.runFunc(functionName, id); + utils.runFunc(functionName, ...argList); }, false ); @@ -277,16 +300,14 @@ const setClickFun = function(id, functionName) { * Called by parser when a link is found. Adds the URL to the vertex data. * @param ids Comma separated list of ids * @param linkStr URL to create a link for - * @param tooltip Tooltip for the clickable element */ -export const setLink = function(ids, linkStr, tooltip, target) { +export const setLink = function(ids, linkStr, target) { ids.split(',').forEach(function(id) { if (typeof vertices[id] !== 'undefined') { vertices[id].link = utils.formatUrl(linkStr, config); vertices[id].linkTarget = target; } }); - setTooltip(ids, tooltip); setClass(ids, 'clickable'); }; export const getTooltip = function(id) { @@ -299,11 +320,10 @@ export const getTooltip = function(id) { * @param functionName Function to be called on click * @param tooltip Tooltip for the clickable element */ -export const setClickEvent = function(ids, functionName, tooltip) { +export const setClickEvent = function(ids, functionName, functionArgs) { ids.split(',').forEach(function(id) { - setClickFun(id, functionName); + setClickFun(id, functionName, functionArgs); }); - setTooltip(ids, tooltip); setClass(ids, 'clickable'); }; diff --git a/src/diagrams/flowchart/parser/flow.jison b/src/diagrams/flowchart/parser/flow.jison index 794e9839c..f8e2e3529 100644 --- a/src/diagrams/flowchart/parser/flow.jison +++ b/src/diagrams/flowchart/parser/flow.jison @@ -9,6 +9,10 @@ %x string %x dir %x vertex +%x click +%x href +%x callbackname +%x callbackargs %x open_directive %x type_directive %x arg_directive @@ -31,7 +35,42 @@ "interpolate" return 'INTERPOLATE'; "classDef" return 'CLASSDEF'; "class" return 'CLASS'; -"click" return 'CLICK'; + +/* +---interactivity command--- +'href' adds a link to the specified task. 'href' can only be specified when the +line was introduced with 'click'. +'href ""' attaches the specified link to the node that was specified by 'click'. +*/ +"href"[\s]+["] this.begin("href"); +["] this.popState(); +[^"]* return 'HREF'; + +/* +---interactivity command--- +'call' adds a callback to the specified task. 'call' can only be specified when +the line was introduced with 'click'. +'call ()' attaches the function 'callbackname' with the specified +arguments to the task that was specified by 'click'. +Function arguments are optional: 'call ()' simply executes 'callbackname' without any arguments. +*/ +"call"[\s]+ this.begin("callbackname"); +\([\s]*\) this.popState(); +\( this.popState(); this.begin("callbackargs"); +[^(]* return 'CALLBACKNAME'; +\) this.popState(); +[^)]* return 'CALLBACKARGS'; + +/* +'click' is the keyword to introduce a line that contains interactivity commands. +'click' must be followed by an existing node-id. All commands are attached to +that id. +'click ' can be followed by href or call commands in any desired order +*/ +"click"[\s]+ this.begin("click"); +[\s\n] this.popState(); +[^\s\n]* return 'CLICK'; + "graph" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} "flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} "subgraph" return 'subgraph'; @@ -411,12 +450,26 @@ classStatement:CLASS SPACE alphaNum SPACE alphaNum ; clickStatement - : CLICK SPACE alphaNum SPACE alphaNum {$$ = $1;yy.setClickEvent($3, $5, undefined);} - | CLICK SPACE alphaNum SPACE alphaNum SPACE STR {$$ = $1;yy.setClickEvent($3, $5, $7) ;} - | CLICK SPACE alphaNum SPACE STR {$$ = $1;yy.setLink($3, $5, undefined, undefined);} - | CLICK SPACE alphaNum SPACE STR SPACE STR {$$ = $1;yy.setLink($3, $5, $7, undefined );} - | CLICK SPACE alphaNum SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($3, $5, undefined, $7 );} - | CLICK SPACE alphaNum SPACE STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($3, $5, $7, $9 );} + : CLICK CALLBACKNAME {$$ = $1;yy.setClickEvent($1, $2); + | CLICK CALLBACKNAME CALLBACKARGS {$$ = $1;yy.setClickEvent($1, $2, $3);} + | CLICK CALLBACKNAME CALLBACKARGS HREF {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setLink($1,$4);} + | CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setTooltip($1, $4)} + | CLICK CALLBACKNAME CALLBACKARGS HREF SPACE STR {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setLink($1,$4);yy.setTooltip($1, $6)} + | CLICK CALLBACKNAME CALLBACKARGS HREF SPACE LINK_TARGET {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setLink($1,$4, $6)} + | CLICK CALLBACKNAME CALLBACKARGS HREF SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setLink($1,$4, $8);yy.setTooltip($1, $6)} + + | CLICK CALLBACKNAME HREF {$$ = $1;yy.setClickEvent($1, $2);yy.setLink($1,$3);} + | CLICK CALLBACKNAME SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4)} + | CLICK CALLBACKNAME HREF SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setLink($1,$3);yy.setTooltip($1, $5)} + | CLICK CALLBACKNAME HREF SPACE LINK_TARGET {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setLink($1,$4, $5)} + | CLICK CALLBACKNAME HREF SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setLink($1,$4, $7);yy.setTooltip($1, $5)} + + | CLICK HREF {$$ = $1;yy.setLink($1, $2);} + | CLICK HREF CALLBACKNAME {$$ = $1;yy.setClickEvent($1, $3);yy.setLink($1,$2);} + | CLICK HREF CALLBACKNAME CALLBACKARGS {$$ = $1;yy.setClickEvent($1, $3, $4);yy.setLink($1,$2);} + | CLICK HREF SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4)} + | CLICK HREF SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);} + | CLICK HREF SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $7);yy.setTooltip($1, $4)} ; styleStatement:STYLE SPACE alphaNum SPACE stylesOpt