From 5a62f2a15ab90db9d721155d1b92b6a8535757ca Mon Sep 17 00:00:00 2001 From: SeanLi Date: Thu, 12 Nov 2020 22:10:58 +0800 Subject: [PATCH] add graphxr iframe injection example --- .eslintignore | 5 + .eslintrc.js | 33 ++++++ .gitignore | 10 ++ README.md | 155 ++++++++++++++++++++++++++++ favicon.ico | Bin 0 -> 5430 bytes graphXR.injection.js | 240 +++++++++++++++++++++++++++++++++++++++++++ index.html | 134 ++++++++++++++++++++++++ jsconfig.json | 9 ++ package.json | 15 +++ 9 files changed, 601 insertions(+) create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 README.md create mode 100644 favicon.ico create mode 100644 graphXR.injection.js create mode 100644 index.html create mode 100644 jsconfig.json create mode 100644 package.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e2dd1af --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +/coverage +/build +/dist +/node_modules +/test/* \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..63478df --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,33 @@ +module.exports = { + "parserOptions": { + "ecmaVersion": 2017 + }, + + + "env": { + "browser": true, + "node": true, + "jest": true, + "commonjs": true, + "es6": true, + }, + "settings": { + "react": { + "version": "detect" + }, + "propWrapperFunctions": [ + // The names of any function used to wrap propTypes, e.g. `forbidExtraProps`. If this isn't set, any propTypes wrapped in a function will be skipped. + "forbidExtraProps", + // {"property": "freeze", "object": "Object"}, + // {"property": "myFavoriteWrapper"} + ], + }, + "rules": { + "no-unused-vars":1, + "no-useless-escape":1, + "no-extra-semi":1, + "no-var": 1, + }, + +}; + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..44350cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +*.lerna_backup +private.xml +*.log +*.code-workspace +package-lock.json +node_modules +build +start.sh +yarn.lock diff --git a/README.md b/README.md new file mode 100644 index 0000000..9b29c86 --- /dev/null +++ b/README.md @@ -0,0 +1,155 @@ +### GraphXR Iframe example(V2.8.0) + +> Please refer the graphXR.injection.js in tag at first + +``` + +``` + +iframeElem is a iframe dom element. + +``` +let iframeElem = document.getElementById("injection-graphXR-iframe-id"); +``` + +Embed iframe +``` + +``` + + + +### How run it(For Develop) + +``` +yarn && yarn start +``` +then add graphXR share link as iframe embedGraphURL. +You can change defaultEmbedGraphURL in index.html line 116 + +``` +http://localhost:3000?embedGraphURL=https://graphxr.kineviz.com/share/5c633dfe197b00001e855294/VC%20investment%202004-2013/5c65e7be851f2c0036ef27c9 +``` + +### 1. ApiCommand + +#### 1.1 getGraph resData.content {nodes,edges} + +``` +graphXR.injectionApiCommand(':getGraph', iframeElem) +.then((resData) => { + console.warn("Receive graphData:", resData.content) +}) +``` + +#### 1.2 getGraphStat resData.content {nodes:number,edges:number} + +``` +graphXR.injectionApiCommand(':getGraphStat', iframeElem) +.then((resData) => { + console.warn("Receive getGraphStat:", resData.content) +}) +``` + +#### 1.3 clearGraph resData.content {} + +``` +graphXR.injectionApiCommand(':clearGraph', iframeElem) +.then((resData) => { + console.warn("Receive clearGraph:", resData.content) +}) +``` + +#### 1.4 selected graph resData.content {nodes:number,edges:number} + +``` +graphXR.injectionApiCommand(':selected', iframeElem) +.then((resData) => { + console.warn("Receive query data:", resData.content) +}) +``` + +#### 1.5 query resData.content {nodes:number,edges:number} + +``` +graphXR.injectionApiCommand('MATCH (n)-[r]-(m) RETURN * LIMIT 100', iframeElem) +.then((resData) => { + console.warn("Receive query data:", resData.content) +}) +``` + + +### 2. Injection codes + +#### 2.1 select all + +``` +graphXR.injectionApiCommand(':getGraph', iframeElem) +.then((resData) => { + console.warn("Receive all data:", resData.content) + const {nodes, edges} = resData.content; + + //select all + graphXR.injectionCode(` + _GXR.NodesSelectManager.selectWithNodeIds(${JSON.stringify(nodes.map(n => n._GXRID))},'new') + ` , iframeElem).then(resData => { + console.warn("Injection success:", resData) + }) +}) +``` + + +### 3. event, only support ['change','select'] + + +#### 3.1 graphxr change event + +``` +graphXR.injectionOn("change", () => { + console.warn("receive change event"); + // Please use :getGraph got all data + // graphXR.injectionApiCommand(':getGraph', iframeElem) + // .then((resData) => { + // console.warn("Receive graphData:", resData.content) + // }) +}, iframeElem, "iframe-unique-name") +``` + +#### 3.2 graphxr select event + +``` +graphXR.injectionOn("select", () => { + console.warn("receive select event") + //please use :selected got all selected nodes + graphXR.injectionApiCommand(':selected', iframeElem) + .then((resData) => { + console.warn("Receive selected:", resData.content) + }) +}, iframeElem, "iframe-unique-name") + +``` + + +#### 4. Layout + +Support force, line, grid, circle, cube + +force Layout e.g. + +``` + graphXR.injectionCode(` + _app.controller.API.setLayout("force") + ` , iframeElem).then(resData => { + console.warn("use force success:", resData) + }) +``` + +circle Layout e.g. + +``` + graphXR.injectionCode(` + _app.controller.API.setLayout("circle") + ` , iframeElem).then(resData => { + console.warn("use circle success:", resData) + }) +``` \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2dd3f6d3ad45cf80f076c497a94d42f2de5ddd78 GIT binary patch literal 5430 zcmeGgX-t$ybhg`;#uy<@n^qK|))H!K)EcTWL~C1Wt2WZ~hY7Jw)7DVqQ8YDbwXj0* zfP&m8YEcnu#T!@-5tc(vfyH$}gk4twkwaWqZufKh-omUOvlk2YN0V;y_I)$+j+r;} zju}D(`>wnPC{&YGW`L98rx_SB| zRf)&L+gU4H>#|pL-oLSMu&3j-e`_62IZl9js?Kdpyc#^WCC%TF1JI2Ezo}eK_@V|` z+OJWM8!O$P*IXXlO8Ht6=}|jM7pvR2%@zOoRUX=9yz?0B?R>R7>AQK$zp(KN0m zL-YstWbh?h=TUog@7@XNer6L6ZD^~0PWwby5VyR1qH~a5^gU{;Zc5)MJ?A|U{i*&_ zx$f4wcd0yEareX8_a>rW9N;_vzBBIl0zSTYBKp%r)3n;b-buEuo6@VBtLKC7)s1%! zMaR7I2Q=-kQ+c*v*tgjb=bX@YltuBrS^H-{qVqtzR-W}a(@+q-tv)YeSILPrSukdZ zNf?Ldna4F)n*-lJwL|@O_bX0+pSjPiGyV6sd+V+p2z)ZoGa2K7T>M*je#|FQO?!hY zyOwrE!N)lVKU69TW4D65m;1X~9O3LIz#ee95sT~F?&N%qaTNIw({F66%1*4m8Zx(8 z617AQIyH2i6yq4o>4yc zUV01H#*fi8837?wmnD2VdtVUhk*E8GF?3v$^xRq;D^2tkGh?G;;z(R-zIkyC(j(lw z{7d{;c+QVn3}c4% z@afp^^uV|JVIMZ34x-7Lb_qwVf4!qMM zPxHb#S)ygfsxNIhLg!Ags_eKAOJ}X!)gqsRvC0}9GtTjSpX$fBhw+4=HR-#QW<2^K zKBQ5dEQT|=Iu+_jD&HEmRX_Miuc_G0pNA}UN3=D)blgV$9PSlgm#J^8yUI^nsXP4~ zx%@cK%D%^qGluidA_bk38O+sX9|$m|&r~1A5$T1^2_Va8FS3H?w6-_odGh(tm+qA( zuEV@+q#tS!$cfwd^ke;|d6>!+i>41fZjvrA$ItpgPMEs_?6b@q{aE_3cEB1wGssn4 znY{ilOFUo)B8l**y%xHlQBfGP6@AJQy)9!{$H9I(S>x&HP|m_LWR33sal^eIOaVU? zY?Vl$drFAE767r1KziXrUZ76v)45dQv0Es5Wu8#9?<1~bIRAKz8;(|lVD?lbE1{=WLJ7}Q0^$os$ zxQjSq{U^J!Jr3hs^`)&xG4^&SuP<$a`U&ePtno44RQ$az8Sf5ICxR(glbkJHcZyZjD9?WJNUf#jWHVQRLmu~_xyW^vgoic z%0NE=yXfy!EI(~y{i=X_DQ#1XI?)$t`}tu)2Al~|*%PAXL2)A?0n3IVa1S9O0U;70 MfE^*4s%OFLAMw maxTime) { + clearInterval(__timer); + reject(new Error(`Timed out in ${maxTime}ms.`)) + } + }, 60); + }); + } + + function injectionCode( code, iframeElement = getIframeElem(), index = Date.now(), type = 'codes') { + if (!iframeElement || iframeElement.tagName !== "IFRAME") { + let err = new Error(`Only support iframe element, please try document.getElementById("your_iframe_id");`); + console.error(err.message); + return Promise.reject(err); + } + index = String(index + Date.now()); + //reset GTempResponse + GTempResponse = { + index: -1, + status: 0, + message: null + }; + + + let postMessageBody = { + + }; + + if (type === 'api') { + index = code; + postMessageBody = { + type: "api", + command: code + } + } else { + postMessageBody = { + type: "codes", + codes: [{ + index: index, + code: code + }] + } + } + + handleIframeAddMessage(index, iframeElement); + + // * allow cross origin post message + iframeElement.contentWindow.postMessage(postMessageBody, "*"); + + return untilCheck(() => { return GTempResponse && String(GTempResponse.index).toLowerCase() === String(index).toLowerCase() }); + } + + function injectionCodes(codes, iframeElement = getIframeElem()) { + + if (!Array.isArray(codes)) { + let err = new Error(`The codes must be as array`); + console.error(err.message); + return Promise.reject(err); + } + + //keep the async waterfall + return codes.reduce((promiseChain, currentCodeItem, currentIndex) => { + return promiseChain.then((chainResults) => { + injectionCode( currentCodeItem.code, iframeElement, currentIndex).then((currentResult) => { + [...chainResults, currentResult] + }) + }) + }, Promise.resolve([])); + } + + function injectionApiCommand(command, iframeElement = getIframeElem()) { + + let newCommand = String(command).trim(); + if (!newCommand && !command) { + let err = new Error(`Please try use those commands [:clearGraph, :getGraphStat, :getGraph]`); + console.error(err.message); + return Promise.reject(err); + } + return injectionCode( newCommand, iframeElement, newCommand , 'api') + } + + function injectionApiCommands(commands, iframeElement = getIframeElem()) { + if (!Array.isArray(commands)) { + let err = new Error(`The commands must be as array`); + console.error(err.message); + return Promise.reject(err); + } + + //keep the async waterfall + return commands.reduce((promiseChain, commandItem, currentIndex) => { + return promiseChain.then((chainResults) => { + injectionApiCommand(commandItem, iframeElement).then((currentResult) => { + [...chainResults, currentResult] + }) + }) + }, Promise.resolve([])); + } + + + function _handleInjectionOn(e) { + let data = e.data && typeof (e.data) === 'object' ? e.data : {}; + let type = String(data.type).toLocaleLowerCase(); + let eventName = String(data.eventName).toLocaleLowerCase(); + if (type === 'events-response' && GEvents && Object.keys(GEvents[eventName] || {}).length > 0) { + Object.values(GEvents[eventName] || {}) + .forEach(cb => { + cb(eventName, data.response || {}); + }); + } + } + + function injectionOn(eventName = 'change', callback = () => { }, iframeElement = getIframeElem(), uniqueName = '') { + if (!['change', 'select'].includes(eventName) || !callback) { + let err = new Error(`Miss eventName['change','select'] ro callback function`); + return console.error(err.message); + } + + //try remove at first, then add eventLister + iframeElement.contentWindow.parent.removeEventListener('message', _handleInjectionOn, false); + iframeElement.contentWindow.parent.addEventListener('message', _handleInjectionOn, false); + + if (GEvents[eventName] && callback) { + uniqueName = uniqueName || callback.name; + GEvents[eventName][uniqueName] = callback; + + let codeIndex = Date.now(); + let code = ` + //convert to global + _app.controller.API.on('${eventName}', (data) => { + if (window.parent) { + window.parent.postMessage({ + type: "events-response", + eventName:"${eventName}", + response: data + }, "*"); + } + }, '${uniqueName}') + ` + injectionCode( code, iframeElement, codeIndex); + + } else if ( + callback[eventName] && + !callback && + GEvents[eventName][uniqueName] + ) { + return delete GEvents[eventName][uniqueName]; + } else { + return console.warn("Only support the events :", Object.keys(callback)); + } + } + + const graphXR = { + version, + injectionCode, + injectionCodes, + injectionApiCommand, + injectionApiCommands, + injectionOn + } + + if (typeof define === 'function' && define.amd) { + define(function () { + return graphXR + }) + } else if (typeof module === 'object' && module.exports) { + module.exports = graphXR + } else { + $.graphXR = graphXR + } +})(this) \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..e51806a --- /dev/null +++ b/index.html @@ -0,0 +1,134 @@ + + + + + + + Iframe Example + + + + + + + + + + + + + + + + + + +
+
+ +
+
RUN
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..257d0d6 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "es2015", + "allowSyntheticDefaultImports": true, + "checkJs":false, + "baseUrl": "./", + }, + "exclude": ["node_modules/fast-safe-stringify","build", "dist","public"] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..e030d00 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "graphxr-injection", + "version": "0.0.1", + "license": "MIT", + "preferGlobal": true, + "scripts": { + "start": "browser-sync start --server --serveStatic --watch" + }, + "dependencies": { + }, + "devDependencies": { + "browser-sync": "^2.26.7", + "eslint": "^7.1.0" + } +}