From cf3cad2174931c420a8a247e6749dace920b3fed Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Fri, 26 Aug 2016 01:17:26 +0430 Subject: [PATCH 01/10] min --- core/api/api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/api/api.ts b/core/api/api.ts index bbb7a45..0fde39b 100644 --- a/core/api/api.ts +++ b/core/api/api.ts @@ -86,7 +86,7 @@ /** * Does a function to each dot in the array. */ - function foreachdot ( dots: Array, + function foreachdot ( dots: Array, f: ( dot: KaryGraph.Dot ) => void ) { dots.forEach( dotOrId => { f( KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dotOrId ) ); @@ -163,7 +163,7 @@ * Disconnects two dots from each other. * https://github.com/karyfoundation/graph/wiki/API#disconnect */ - function disconnect ( a: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, + function disconnect ( a: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, b: KaryGraph.API.AbstractionLayer.DotObjectOrDotID ): boolean { let d1 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( a ); let d2 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( b ); @@ -178,7 +178,7 @@ * Checks if two dots are connected to each other. * https://github.com/karyfoundation/graph/wiki/API#has-edge */ - function hasEdge ( start: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, + function hasEdge ( start: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, end: KaryGraph.API.AbstractionLayer.DotObjectOrDotID ) { let d1 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( start ); let d2 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( end ); From c7564a329d092079b13af6332e5a991dd58b6a2f Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Tue, 27 Sep 2016 15:34:29 +0330 Subject: [PATCH 02/10] finally able to pack Graph --- .vscode/cSpell.json | 3 ++- .vscode/settings.json | 3 --- build.sh | 29 +++++++++++++++++++++++---- core/objects/dot/dot.ts | 2 +- core/script/algorithms.ts | 1 - gulpfile.js | 41 +++++++++++++++++++++++++++++--------- javascript/graph-monaco.js | 2 +- libs/snap-svg-min.js | 21 +++++++++++++++++++ 8 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 libs/snap-svg-min.js diff --git a/.vscode/cSpell.json b/.vscode/cSpell.json index a8213bb..1348231 100644 --- a/.vscode/cSpell.json +++ b/.vscode/cSpell.json @@ -41,7 +41,8 @@ "onresize", "prismjs", "Script's", - "subdirs" + "subdirs", + "subfolder" ], // flagWords - list of words to be always considered incorrect // This is useful for offensive words and common spelling errors. diff --git a/.vscode/settings.json b/.vscode/settings.json index 0d96591..42a51d8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,4 @@ { - "editor.fontSize": 14, - "editor.lineHeight": 24, - "editor.fontFamily": "SourceCodePro-Medium", "editor.tabSize": 4, "editor.wrappingColumn": 0, "editor.quickSuggestionsDelay": 0, diff --git a/build.sh b/build.sh index 5cccaf2..0ba266e 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,26 @@ -gulp -cd ./_compiled -electron main.js -cd .. +# +# ─── GULP BUILD ───────────────────────────────────────────────────────────────── +# + + gulp + + #cd ./_compiled + #electron main.js + #cd .. + +# +# ─── PACKING ──────────────────────────────────────────────────────────────────── +# + + electron-packager _compiled "Graph" --platform=darwin --arch=x64 --overwrite=true --app-copyrigh="Copyright 2016 by Kary Foundation, Inc." --app-version="1" --icon=./designs/icon/icns/icon.icns --name="Graph" --out=_release + + #electron-packager ./binary "Comment IV" --platform=win32 --arch=x64 --app-copyrigh="Copyright 2016 by Kary Foundation, Inc." --app-version="IV.3.124" --icon=icon/ico/icon.ico --name="Comment IV" --out=release --overwrite=true + +# +# ─── RUNNING ──────────────────────────────────────────────────────────────────── +# + + #"_release/Orchestra-darwin-x64/Orchestra.app/Contents/MacOS/Orchestra" + +# ──────────────────────────────────────────────────────────────────────────────── \ No newline at end of file diff --git a/core/objects/dot/dot.ts b/core/objects/dot/dot.ts index b49d7eb..cb658cb 100644 --- a/core/objects/dot/dot.ts +++ b/core/objects/dot/dot.ts @@ -355,7 +355,7 @@ namespace KaryGraph { // /** Creates a Snap Label object to present the graph node number */ - private CreateNumberLabel ( ) : ISnapObject { + private CreateNumberLabel ( ): ISnapObject { var label = GraphView.text( this.Position.X - DotNumberLabelDisplacementX, this.Position.Y - DotNumberLabelDisplacementY, diff --git a/core/script/algorithms.ts b/core/script/algorithms.ts index 5313d2c..9940f89 100644 --- a/core/script/algorithms.ts +++ b/core/script/algorithms.ts @@ -32,7 +32,6 @@ namespace KaryGraph.ScriptEngine.Algorithms { // export function ExecuteAlgorithm ( handle: string ) { - console.log('here'); let algorithm = Storage.Algorithms[ handle ]; algorithm({ }); } diff --git a/gulpfile.js b/gulpfile.js index ae44358..2bb7c19 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -48,17 +48,25 @@ // /** Copy to binary from dir */ - function copyToBinaryFromDir ( dir ) { + function copyToBinaryFromDir ( dir, subfolder ) { fs.readdir( dir , ( err , files ) => { // if error if ( err ) { console.log(`Could not get files from directory ${ dir }`); } + // if right files.forEach( name => { + let dest; + if ( !( subfolder === undefined || subfolder === '' || subfolder === null ) ) { + dest = path.join( resultDirPath , subfolder , name ); + } else { + dest = path.join( resultDirPath , name ); + } + copyFile( getLocalPath( path.join( dir , name ) ), - getLocalPath( path.join( resultDirPath , name ) ) + getLocalPath( dest ) ); }); }); @@ -113,6 +121,12 @@ /** Copies static resource files into the result directory */ gulp.task( 'copyResourceFiles', callback => { + + function copyNodeModules ( handle ) { + let address = path.join( 'node_modules', handle ); + copyToBinaryFromDir( address, address ); + } + copyToBinaryFromDir( 'resources' ); copyToBinaryFromDir( 'view' ); copyToBinaryFromDir( 'electron' ); @@ -120,6 +134,15 @@ copyToBinaryFromDir( 'libs' ); copyToBinaryFromDir( 'javascript' ); copyToBinaryFromDir( path.join( 'node_modules', 'monaco-editor', 'min' ) ); + + copyNodeModules( 'prismjs' ); + copyNodeModules( 'snapsvg' ); + copyNodeModules( 'eve' ); + + copyFile( + getLocalPath( 'package.json' ), + getLocalPath( path.join( resultDirPath , 'package.json' ) ) + ); callback(); }); @@ -130,9 +153,9 @@ gulp.task( 'resources', ['copyResourceFiles'], callback => { // fixing monaco folders name /*mv( - path.join( resultDirPath , 'vs' ), + path.join( resultDirPath , 'vs' ), path.join( resultDirPath , 'monaco' ), - { + { \ mkdirp: true, clobber: false }, @@ -151,15 +174,15 @@ /** Compiles the Less style sheets */ gulp.task( 'sheets', callback => { try { - let lessSourceCode = fs.readFileSync( + let lessSourceCode = fs.readFileSync( path.join( __dirname, 'sheets', 'ui.less' ), 'utf8' ); - + less.render( lessSourceCode, ( err, output ) => { if ( err ) { console.log(`Less failure: ${ err }`); return; } - fs.writeFile( - path.join( __dirname, '_compiled/style.css' ), + fs.writeFile( + path.join( __dirname, '_compiled/style.css' ), output.css, error => { if ( error ) { @@ -172,7 +195,7 @@ ); }); } catch ( err ) { - console.log('Compiling less failed ' + err ); + console.log('Compiling less failed ' + err ); } }); diff --git a/javascript/graph-monaco.js b/javascript/graph-monaco.js index 9b559c9..99ecc5b 100644 --- a/javascript/graph-monaco.js +++ b/javascript/graph-monaco.js @@ -45,4 +45,4 @@ } } -// ──────────────────────────────────────────────────────────────────────────────── \ No newline at end of file +// ──────────────────────────────────────────────────────────────────────────────── diff --git a/libs/snap-svg-min.js b/libs/snap-svg-min.js new file mode 100644 index 0000000..fad3fa8 --- /dev/null +++ b/libs/snap-svg-min.js @@ -0,0 +1,21 @@ +// Snap.svg 0.4.1 +// +// Copyright (c) 2013 – 2015 Adobe Systems Incorporated. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// build: 2015-04-13 + +!function(a){var b,c,d="0.4.2",e="hasOwnProperty",f=/[\.\/]/,g=/\s*,\s*/,h="*",i=function(a,b){return a-b},j={n:{}},k=function(){for(var a=0,b=this.length;b>a;a++)if("undefined"!=typeof this[a])return this[a]},l=function(){for(var a=this.length;--a;)if("undefined"!=typeof this[a])return this[a]},m=function(a,d){a=String(a);var e,f=c,g=Array.prototype.slice.call(arguments,2),h=m.listeners(a),j=0,n=[],o={},p=[],q=b;p.firstDefined=k,p.lastDefined=l,b=a,c=0;for(var r=0,s=h.length;s>r;r++)"zIndex"in h[r]&&(n.push(h[r].zIndex),h[r].zIndex<0&&(o[h[r].zIndex]=h[r]));for(n.sort(i);n[j]<0;)if(e=o[n[j++]],p.push(e.apply(d,g)),c)return c=f,p;for(r=0;s>r;r++)if(e=h[r],"zIndex"in e)if(e.zIndex==n[j]){if(p.push(e.apply(d,g)),c)break;do if(j++,e=o[n[j]],e&&p.push(e.apply(d,g)),c)break;while(e)}else o[e.zIndex]=e;else if(p.push(e.apply(d,g)),c)break;return c=f,b=q,p};m._events=j,m.listeners=function(a){var b,c,d,e,g,i,k,l,m=a.split(f),n=j,o=[n],p=[];for(e=0,g=m.length;g>e;e++){for(l=[],i=0,k=o.length;k>i;i++)for(n=o[i].n,c=[n[m[e]],n[h]],d=2;d--;)b=c[d],b&&(l.push(b),p=p.concat(b.f||[]));o=l}return p},m.on=function(a,b){if(a=String(a),"function"!=typeof b)return function(){};for(var c=a.split(g),d=0,e=c.length;e>d;d++)!function(a){for(var c,d=a.split(f),e=j,g=0,h=d.length;h>g;g++)e=e.n,e=e.hasOwnProperty(d[g])&&e[d[g]]||(e[d[g]]={n:{}});for(e.f=e.f||[],g=0,h=e.f.length;h>g;g++)if(e.f[g]==b){c=!0;break}!c&&e.f.push(b)}(c[d]);return function(a){+a==+a&&(b.zIndex=+a)}},m.f=function(a){var b=[].slice.call(arguments,1);return function(){m.apply(null,[a,null].concat(b).concat([].slice.call(arguments,0)))}},m.stop=function(){c=1},m.nt=function(a){return a?new RegExp("(?:\\.|\\/|^)"+a+"(?:\\.|\\/|$)").test(b):b},m.nts=function(){return b.split(f)},m.off=m.unbind=function(a,b){if(!a)return void(m._events=j={n:{}});var c=a.split(g);if(c.length>1)for(var d=0,i=c.length;i>d;d++)m.off(c[d],b);else{c=a.split(f);var k,l,n,d,i,o,p,q=[j];for(d=0,i=c.length;i>d;d++)for(o=0;od;d++)for(k=q[d];k.n;){if(b){if(k.f){for(o=0,p=k.f.length;p>o;o++)if(k.f[o]==b){k.f.splice(o,1);break}!k.f.length&&delete k.f}for(l in k.n)if(k.n[e](l)&&k.n[l].f){var r=k.n[l].f;for(o=0,p=r.length;p>o;o++)if(r[o]==b){r.splice(o,1);break}!r.length&&delete k.n[l].f}}else{delete k.f;for(l in k.n)k.n[e](l)&&k.n[l].f&&delete k.n[l].f}k=k.n}}},m.once=function(a,b){var c=function(){return m.unbind(a,c),b.apply(this,arguments)};return m.on(a,c)},m.version=d,m.toString=function(){return"You are running Eve "+d},"undefined"!=typeof module&&module.exports?module.exports=m:"function"==typeof define&&define.amd?define("eve",[],function(){return m}):a.eve=m}(this),function(a,b){if("function"==typeof define&&define.amd)define(["eve"],function(c){return b(a,c)});else if("undefined"!=typeof exports){var c=require("eve");module.exports=b(a,c)}else b(a,a.eve)}(window||this,function(a,b){var c=function(b){var c={},d=a.requestAnimationFrame||a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame||a.msRequestAnimationFrame||function(a){setTimeout(a,16)},e=Array.isArray||function(a){return a instanceof Array||"[object Array]"==Object.prototype.toString.call(a)},f=0,g="M"+(+new Date).toString(36),h=function(){return g+(f++).toString(36)},i=Date.now||function(){return+new Date},j=function(a){var b=this;if(null==a)return b.s;var c=b.s-a;b.b+=b.dur*c,b.B+=b.dur*c,b.s=a},k=function(a){var b=this;return null==a?b.spd:void(b.spd=a)},l=function(a){var b=this;return null==a?b.dur:(b.s=b.s*a/b.dur,void(b.dur=a))},m=function(){var a=this;delete c[a.id],a.update(),b("mina.stop."+a.id,a)},n=function(){var a=this;a.pdif||(delete c[a.id],a.update(),a.pdif=a.get()-a.b)},o=function(){var a=this;a.pdif&&(a.b=a.get()-a.pdif,delete a.pdif,c[a.id]=a)},p=function(){var a,b=this;if(e(b.start)){a=[];for(var c=0,d=b.start.length;d>c;c++)a[c]=+b.start[c]+(b.end[c]-b.start[c])*b.easing(b.s)}else a=+b.start+(b.end-b.start)*b.easing(b.s);b.set(a)},q=function(){var a=0;for(var e in c)if(c.hasOwnProperty(e)){var f=c[e],g=f.get();a++,f.s=(g-f.b)/(f.dur/f.spd),f.s>=1&&(delete c[e],f.s=1,a--,function(a){setTimeout(function(){b("mina.finish."+a.id,a)})}(f)),f.update()}a&&d(q)},r=function(a,b,e,f,g,i,s){var t={id:h(),start:a,end:b,b:e,s:0,dur:f-e,spd:1,get:g,set:i,easing:s||r.linear,status:j,speed:k,duration:l,stop:m,pause:n,resume:o,update:p};c[t.id]=t;var u,v=0;for(u in c)if(c.hasOwnProperty(u)&&(v++,2==v))break;return 1==v&&d(q),t};return r.time=i,r.getById=function(a){return c[a]||null},r.linear=function(a){return a},r.easeout=function(a){return Math.pow(a,1.7)},r.easein=function(a){return Math.pow(a,.48)},r.easeinout=function(a){if(1==a)return 1;if(0==a)return 0;var b=.48-a/1.04,c=Math.sqrt(.1734+b*b),d=c-b,e=Math.pow(Math.abs(d),1/3)*(0>d?-1:1),f=-c-b,g=Math.pow(Math.abs(f),1/3)*(0>f?-1:1),h=e+g+.5;return 3*(1-h)*h*h+h*h*h},r.backin=function(a){if(1==a)return 1;var b=1.70158;return a*a*((b+1)*a-b)},r.backout=function(a){if(0==a)return 0;a-=1;var b=1.70158;return a*a*((b+1)*a+b)+1},r.elastic=function(a){return a==!!a?a:Math.pow(2,-10*a)*Math.sin(2*(a-.075)*Math.PI/.3)+1},r.bounce=function(a){var b,c=7.5625,d=2.75;return 1/d>a?b=c*a*a:2/d>a?(a-=1.5/d,b=c*a*a+.75):2.5/d>a?(a-=2.25/d,b=c*a*a+.9375):(a-=2.625/d,b=c*a*a+.984375),b},a.mina=r,r}("undefined"==typeof b?function(){}:b),d=function(a){function c(a,b){if(a){if(a.nodeType)return w(a);if(e(a,"array")&&c.set)return c.set.apply(c,a);if(a instanceof s)return a;if(null==b)return a=y.doc.querySelector(String(a)),w(a)}return a=null==a?"100%":a,b=null==b?"100%":b,new v(a,b)}function d(a,b){if(b){if("#text"==a&&(a=y.doc.createTextNode(b.text||b["#text"]||"")),"#comment"==a&&(a=y.doc.createComment(b.text||b["#text"]||"")),"string"==typeof a&&(a=d(a)),"string"==typeof b)return 1==a.nodeType?"xlink:"==b.substring(0,6)?a.getAttributeNS(T,b.substring(6)):"xml:"==b.substring(0,4)?a.getAttributeNS(U,b.substring(4)):a.getAttribute(b):"text"==b?a.nodeValue:null;if(1==a.nodeType){for(var c in b)if(b[z](c)){var e=A(b[c]);e?"xlink:"==c.substring(0,6)?a.setAttributeNS(T,c.substring(6),e):"xml:"==c.substring(0,4)?a.setAttributeNS(U,c.substring(4),e):a.setAttribute(c,e):a.removeAttribute(c)}}else"text"in b&&(a.nodeValue=b.text)}else a=y.doc.createElementNS(U,a);return a}function e(a,b){return b=A.prototype.toLowerCase.call(b),"finite"==b?isFinite(a):"array"==b&&(a instanceof Array||Array.isArray&&Array.isArray(a))?!0:"null"==b&&null===a||b==typeof a&&null!==a||"object"==b&&a===Object(a)||J.call(a).slice(8,-1).toLowerCase()==b}function f(a){if("function"==typeof a||Object(a)!==a)return a;var b=new a.constructor;for(var c in a)a[z](c)&&(b[c]=f(a[c]));return b}function h(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return a.push(a.splice(c,1)[0])}function i(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("␀"),g=d.cache=d.cache||{},i=d.count=d.count||[];return g[z](f)?(h(i,f),c?c(g[f]):g[f]):(i.length>=1e3&&delete g[i.shift()],i.push(f),g[f]=a.apply(b,e),c?c(g[f]):g[f])}return d}function j(a,b,c,d,e,f){if(null==e){var g=a-c,h=b-d;return g||h?(180+180*D.atan2(-h,-g)/H+360)%360:0}return j(a,b,e,f)-j(c,d,e,f)}function k(a){return a%360*H/180}function l(a){return 180*a/H%360}function m(a){var b=[];return a=a.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g,function(a,c,d){return d=d.split(/\s*,\s*|\s+/),"rotate"==c&&1==d.length&&d.push(0,0),"scale"==c&&(d.length>2?d=d.slice(0,2):2==d.length&&d.push(0,0),1==d.length&&d.push(d[0],0,0)),b.push("skewX"==c?["m",1,0,D.tan(k(d[0])),1,0,0]:"skewY"==c?["m",1,D.tan(k(d[0])),0,1,0,0]:[c.charAt(0)].concat(d)),a}),b}function n(a,b){var d=ab(a),e=new c.Matrix;if(d)for(var f=0,g=d.length;g>f;f++){var h,i,j,k,l,m=d[f],n=m.length,o=A(m[0]).toLowerCase(),p=m[0]!=o,q=p?e.invert():0;"t"==o&&2==n?e.translate(m[1],0):"t"==o&&3==n?p?(h=q.x(0,0),i=q.y(0,0),j=q.x(m[1],m[2]),k=q.y(m[1],m[2]),e.translate(j-h,k-i)):e.translate(m[1],m[2]):"r"==o?2==n?(l=l||b,e.rotate(m[1],l.x+l.width/2,l.y+l.height/2)):4==n&&(p?(j=q.x(m[2],m[3]),k=q.y(m[2],m[3]),e.rotate(m[1],j,k)):e.rotate(m[1],m[2],m[3])):"s"==o?2==n||3==n?(l=l||b,e.scale(m[1],m[n-1],l.x+l.width/2,l.y+l.height/2)):4==n?p?(j=q.x(m[2],m[3]),k=q.y(m[2],m[3]),e.scale(m[1],m[1],j,k)):e.scale(m[1],m[1],m[2],m[3]):5==n&&(p?(j=q.x(m[3],m[4]),k=q.y(m[3],m[4]),e.scale(m[1],m[2],j,k)):e.scale(m[1],m[2],m[3],m[4])):"m"==o&&7==n&&e.add(m[1],m[2],m[3],m[4],m[5],m[6])}return e}function o(a){var b=a.node.ownerSVGElement&&w(a.node.ownerSVGElement)||a.node.parentNode&&w(a.node.parentNode)||c.select("svg")||c(0,0),d=b.select("defs"),e=null==d?!1:d.node;return e||(e=u("defs",b.node).node),e}function p(a){return a.node.ownerSVGElement&&w(a.node.ownerSVGElement)||c.select("svg")}function q(a,b,c){function e(a){if(null==a)return I;if(a==+a)return a;d(j,{width:a});try{return j.getBBox().width}catch(b){return 0}}function f(a){if(null==a)return I;if(a==+a)return a;d(j,{height:a});try{return j.getBBox().height}catch(b){return 0}}function g(d,e){null==b?i[d]=e(a.attr(d)||0):d==b&&(i=e(null==c?a.attr(d)||0:c))}var h=p(a).node,i={},j=h.querySelector(".svg---mgr");switch(j||(j=d("rect"),d(j,{x:-9e9,y:-9e9,width:10,height:10,"class":"svg---mgr",fill:"none"}),h.appendChild(j)),a.type){case"rect":g("rx",e),g("ry",f);case"image":g("width",e),g("height",f);case"text":g("x",e),g("y",f);break;case"circle":g("cx",e),g("cy",f),g("r",e);break;case"ellipse":g("cx",e),g("cy",f),g("rx",e),g("ry",f);break;case"line":g("x1",e),g("x2",e),g("y1",f),g("y2",f);break;case"marker":g("refX",e),g("markerWidth",e),g("refY",f),g("markerHeight",f);break;case"radialGradient":g("fx",e),g("fy",f);break;case"tspan":g("dx",e),g("dy",f);break;default:g(b,e)}return h.removeChild(j),i}function r(a){e(a,"array")||(a=Array.prototype.slice.call(arguments,0));for(var b=0,c=0,d=this.node;this[b];)delete this[b++];for(b=0;bc;c++){var e={type:a[c].type,attr:a[c].attr()},f=a[c].children();b.push(e),f.length&&x(f,e.childNodes=[])}}c.version="0.4.0",c.toString=function(){return"Snap v"+this.version},c._={};var y={win:a.window,doc:a.window.document};c._.glob=y;{var z="hasOwnProperty",A=String,B=parseFloat,C=parseInt,D=Math,E=D.max,F=D.min,G=D.abs,H=(D.pow,D.PI),I=(D.round,""),J=Object.prototype.toString,K=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\))\s*$/i,L=(c._.separator=/[,\s]+/,/[\s]*,[\s]*/),M={hs:1,rg:1},N=/([a-z])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,O=/([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,P=/(-?\d*\.?\d*(?:e[\-+]?\\d+)?)[\s]*,?[\s]*/gi,Q=0,R="S"+(+new Date).toString(36),S=function(a){return(a&&a.type?a.type:I)+R+(Q++).toString(36)},T="http://www.w3.org/1999/xlink",U="http://www.w3.org/2000/svg",V={};c.url=function(a){return"url('#"+a+"')"}}c._.$=d,c._.id=S,c.format=function(){var a=/\{([^\}]+)\}/g,b=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,c=function(a,c,d){var e=d;return c.replace(b,function(a,b,c,d,f){b=b||d,e&&(b in e&&(e=e[b]),"function"==typeof e&&f&&(e=e()))}),e=(null==e||e==d?a:e)+""};return function(b,d){return A(b).replace(a,function(a,b){return c(a,b,d)})}}(),c._.clone=f,c._.cacher=i,c.rad=k,c.deg=l,c.sin=function(a){return D.sin(c.rad(a))},c.tan=function(a){return D.tan(c.rad(a))},c.cos=function(a){return D.cos(c.rad(a))},c.asin=function(a){return c.deg(D.asin(a))},c.acos=function(a){return c.deg(D.acos(a))},c.atan=function(a){return c.deg(D.atan(a))},c.atan2=function(a){return c.deg(D.atan2(a))},c.angle=j,c.len=function(a,b,d,e){return Math.sqrt(c.len2(a,b,d,e))},c.len2=function(a,b,c,d){return(a-c)*(a-c)+(b-d)*(b-d)},c.closestPoint=function(a,b,c){function d(a){var d=a.x-b,e=a.y-c;return d*d+e*e}for(var e,f,g,h,i=a.node,j=i.getTotalLength(),k=j/i.pathSegList.numberOfItems*.125,l=1/0,m=0;j>=m;m+=k)(h=d(g=i.getPointAtLength(m))).5;){var n,o,p,q,r,s;(p=f-k)>=0&&(r=d(n=i.getPointAtLength(p)))f)return b-f;if(f>a-c)return b-f+a}return b},c.getRGB=i(function(a){if(!a||(a=A(a)).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z};if("none"==a)return{r:-1,g:-1,b:-1,hex:"none",toString:Z};if(!(M[z](a.toLowerCase().substring(0,2))||"#"==a.charAt())&&(a=W(a)),!a)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z};var b,d,f,g,h,i,j=a.match(K);return j?(j[2]&&(f=C(j[2].substring(5),16),d=C(j[2].substring(3,5),16),b=C(j[2].substring(1,3),16)),j[3]&&(f=C((h=j[3].charAt(3))+h,16),d=C((h=j[3].charAt(2))+h,16),b=C((h=j[3].charAt(1))+h,16)),j[4]&&(i=j[4].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b*=2.55),d=B(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),f=B(i[2]),"%"==i[2].slice(-1)&&(f*=2.55),"rgba"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100)),j[5]?(i=j[5].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b/=100),d=B(i[1]),"%"==i[1].slice(-1)&&(d/=100),f=B(i[2]),"%"==i[2].slice(-1)&&(f/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsba"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),c.hsb2rgb(b,d,f,g)):j[6]?(i=j[6].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b/=100),d=B(i[1]),"%"==i[1].slice(-1)&&(d/=100),f=B(i[2]),"%"==i[2].slice(-1)&&(f/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsla"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),c.hsl2rgb(b,d,f,g)):(b=F(D.round(b),255),d=F(D.round(d),255),f=F(D.round(f),255),g=F(E(g,0),1),j={r:b,g:d,b:f,toString:Z},j.hex="#"+(16777216|f|d<<8|b<<16).toString(16).slice(1),j.opacity=e(g,"finite")?g:1,j)):{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z}},c),c.hsb=i(function(a,b,d){return c.hsb2rgb(a,b,d).hex}),c.hsl=i(function(a,b,d){return c.hsl2rgb(a,b,d).hex}),c.rgb=i(function(a,b,c,d){if(e(d,"finite")){var f=D.round;return"rgba("+[f(a),f(b),f(c),+d.toFixed(2)]+")"}return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)});var W=function(a){var b=y.doc.getElementsByTagName("head")[0]||y.doc.getElementsByTagName("svg")[0],c="rgb(255, 0, 0)";return(W=i(function(a){if("red"==a.toLowerCase())return c;b.style.color=c,b.style.color=a;var d=y.doc.defaultView.getComputedStyle(b,I).getPropertyValue("color");return d==c?null:d}))(a)},X=function(){return"hsb("+[this.h,this.s,this.b]+")"},Y=function(){return"hsl("+[this.h,this.s,this.l]+")"},Z=function(){return 1==this.opacity||null==this.opacity?this.hex:"rgba("+[this.r,this.g,this.b,this.opacity]+")"},$=function(a,b,d){if(null==b&&e(a,"object")&&"r"in a&&"g"in a&&"b"in a&&(d=a.b,b=a.g,a=a.r),null==b&&e(a,string)){var f=c.getRGB(a);a=f.r,b=f.g,d=f.b}return(a>1||b>1||d>1)&&(a/=255,b/=255,d/=255),[a,b,d]},_=function(a,b,d,f){a=D.round(255*a),b=D.round(255*b),d=D.round(255*d);var g={r:a,g:b,b:d,opacity:e(f,"finite")?f:1,hex:c.rgb(a,b,d),toString:Z};return e(f,"finite")&&(g.opacity=f),g};c.color=function(a){var b;return e(a,"object")&&"h"in a&&"s"in a&&"b"in a?(b=c.hsb2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):e(a,"object")&&"h"in a&&"s"in a&&"l"in a?(b=c.hsl2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):(e(a,"string")&&(a=c.getRGB(a)),e(a,"object")&&"r"in a&&"g"in a&&"b"in a&&!("error"in a)?(b=c.rgb2hsl(a),a.h=b.h,a.s=b.s,a.l=b.l,b=c.rgb2hsb(a),a.v=b.b):(a={hex:"none"},a.r=a.g=a.b=a.h=a.s=a.v=a.l=-1,a.error=1)),a.toString=Z,a},c.hsb2rgb=function(a,b,c,d){e(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,d=a.o,a=a.h),a*=360;var f,g,h,i,j;return a=a%360/60,j=c*b,i=j*(1-G(a%2-1)),f=g=h=c-j,a=~~a,f+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],_(f,g,h,d)},c.hsl2rgb=function(a,b,c,d){e(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h),(a>1||b>1||c>1)&&(a/=360,b/=100,c/=100),a*=360;var f,g,h,i,j;return a=a%360/60,j=2*b*(.5>c?c:1-c),i=j*(1-G(a%2-1)),f=g=h=c-j/2,a=~~a,f+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],_(f,g,h,d)},c.rgb2hsb=function(a,b,c){c=$(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;return f=E(a,b,c),g=f-F(a,b,c),d=0==g?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=0==g?0:g/f,{h:d,s:e,b:f,toString:X}},c.rgb2hsl=function(a,b,c){c=$(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;return g=E(a,b,c),h=F(a,b,c),i=g-h,d=0==i?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=0==i?0:.5>f?i/(2*f):i/(2-2*f),{h:d,s:e,l:f,toString:Y}},c.parsePathString=function(a){if(!a)return null;var b=c.path(a);if(b.arr)return c.path.clone(b.arr);var d={a:7,c:6,o:2,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,u:3,z:0},f=[];return e(a,"array")&&e(a[0],"array")&&(f=c.path.clone(a)),f.length||A(a).replace(N,function(a,b,c){var e=[],g=b.toLowerCase();if(c.replace(P,function(a,b){b&&e.push(+b)}),"m"==g&&e.length>2&&(f.push([b].concat(e.splice(0,2))),g="l",b="m"==b?"l":"L"),"o"==g&&1==e.length&&f.push([b,e[0]]),"r"==g)f.push([b].concat(e));else for(;e.length>=d[g]&&(f.push([b].concat(e.splice(0,d[g]))),d[g]););}),f.toString=c.path.toString,b.arr=c.path.clone(f),f};var ab=c.parseTransformString=function(a){if(!a)return null;var b=[];return e(a,"array")&&e(a[0],"array")&&(b=c.path.clone(a)),b.length||A(a).replace(O,function(a,c,d){{var e=[];c.toLowerCase()}d.replace(P,function(a,b){b&&e.push(+b)}),b.push([c].concat(e))}),b.toString=c.path.toString,b};c._.svgTransform2string=m,c._.rgTransform=/^[a-z][\s]*-?\.?\d/i,c._.transform2matrix=n,c._unit2px=q;y.doc.contains||y.doc.compareDocumentPosition?function(a,b){var c=9==a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a==d||!(!d||1!=d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)for(;b;)if(b=b.parentNode,b==a)return!0;return!1};c._.getSomeDefs=o,c._.getSomeSVG=p,c.select=function(a){return a=A(a).replace(/([^\\]):/g,"$1\\:"),w(y.doc.querySelector(a))},c.selectAll=function(a){for(var b=y.doc.querySelectorAll(a),d=(c.set||Array)(),e=0;ei;i++)h[g[i].nodeName]=g[i].nodeValue;return h}if(e(a,"string")){if(!(arguments.length>1))return b("snap.util.getattr."+a,d).firstDefined();var k={};k[a]=c,a=k}for(var l in a)a[z](l)&&b("snap.util.attr."+l,d,a[l]);return d},c.parse=function(a){var b=y.doc.createDocumentFragment(),c=!0,d=y.doc.createElement("div");if(a=A(a),a.match(/^\s*<\s*svg(?:\s|>)/)||(a=""+a+"",c=!1),d.innerHTML=a,a=d.getElementsByTagName("svg")[0])if(c)b=a;else for(;a.firstChild;)b.appendChild(a.firstChild);return new t(b)},c.fragment=function(){for(var a=Array.prototype.slice.call(arguments,0),b=y.doc.createDocumentFragment(),d=0,e=a.length;e>d;d++){var f=a[d];f.node&&f.node.nodeType&&b.appendChild(f.node),f.nodeType&&b.appendChild(f),"string"==typeof f&&b.appendChild(c.parse(f).node)}return new t(b)},c._.make=u,c._.wrap=w,v.prototype.el=function(a,b){var c=u(a,this.node);return b&&c.attr(b),c},s.prototype.children=function(){for(var a=[],b=this.node.childNodes,d=0,e=b.length;e>d;d++)a[d]=c(b[d]);return a},s.prototype.toJSON=function(){var a=[];return x([this],a),a[0]},b.on("snap.util.getattr",function(){var a=b.nt();a=a.substring(a.lastIndexOf(".")+1);var c=a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()});return bb[z](c)?this.node.ownerDocument.defaultView.getComputedStyle(this.node,null).getPropertyValue(c):d(this.node,a)});var bb={"alignment-baseline":0,"baseline-shift":0,clip:0,"clip-path":0,"clip-rule":0,color:0,"color-interpolation":0,"color-interpolation-filters":0,"color-profile":0,"color-rendering":0,cursor:0,direction:0,display:0,"dominant-baseline":0,"enable-background":0,fill:0,"fill-opacity":0,"fill-rule":0,filter:0,"flood-color":0,"flood-opacity":0,font:0,"font-family":0,"font-size":0,"font-size-adjust":0,"font-stretch":0,"font-style":0,"font-variant":0,"font-weight":0,"glyph-orientation-horizontal":0,"glyph-orientation-vertical":0,"image-rendering":0,kerning:0,"letter-spacing":0,"lighting-color":0,marker:0,"marker-end":0,"marker-mid":0,"marker-start":0,mask:0,opacity:0,overflow:0,"pointer-events":0,"shape-rendering":0,"stop-color":0,"stop-opacity":0,stroke:0,"stroke-dasharray":0,"stroke-dashoffset":0,"stroke-linecap":0,"stroke-linejoin":0,"stroke-miterlimit":0,"stroke-opacity":0,"stroke-width":0,"text-anchor":0,"text-decoration":0,"text-rendering":0,"unicode-bidi":0,visibility:0,"word-spacing":0,"writing-mode":0};b.on("snap.util.attr",function(a){var c=b.nt(),e={};c=c.substring(c.lastIndexOf(".")+1),e[c]=a;var f=c.replace(/-(\w)/gi,function(a,b){return b.toUpperCase()}),g=c.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()});bb[z](g)?this.node.style[f]=null==a?I:a:d(this.node,e)}),function(){}(v.prototype),c.ajax=function(a,c,d,f){var g=new XMLHttpRequest,h=S();if(g){if(e(c,"function"))f=d,d=c,c=null;else if(e(c,"object")){var i=[];for(var j in c)c.hasOwnProperty(j)&&i.push(encodeURIComponent(j)+"="+encodeURIComponent(c[j]));c=i.join("&")}return g.open(c?"POST":"GET",a,!0),c&&(g.setRequestHeader("X-Requested-With","XMLHttpRequest"),g.setRequestHeader("Content-type","application/x-www-form-urlencoded")),d&&(b.once("snap.ajax."+h+".0",d),b.once("snap.ajax."+h+".200",d),b.once("snap.ajax."+h+".304",d)),g.onreadystatechange=function(){4==g.readyState&&b("snap.ajax."+h+"."+g.status,f,g)},4==g.readyState?g:(g.send(c),g)}},c.load=function(a,b,d){c.ajax(a,function(a){var e=c.parse(a.responseText);d?b.call(d,e):b(e)})};var cb=function(a){var b=a.getBoundingClientRect(),c=a.ownerDocument,d=c.body,e=c.documentElement,f=e.clientTop||d.clientTop||0,h=e.clientLeft||d.clientLeft||0,i=b.top+(g.win.pageYOffset||e.scrollTop||d.scrollTop)-f,j=b.left+(g.win.pageXOffset||e.scrollLeft||d.scrollLeft)-h;return{y:i,x:j}};return c.getElementByPoint=function(a,b){var c=this,d=(c.canvas,y.doc.elementFromPoint(a,b));if(y.win.opera&&"svg"==d.tagName){var e=cb(d),f=d.createSVGRect();f.x=a-e.x,f.y=b-e.y,f.width=f.height=1;var g=d.getIntersectionList(f,null);g.length&&(d=g[g.length-1])}return d?w(d):null},c.plugin=function(a){a(c,s,v,y,t)},y.win.Snap=c,c}(a||this);return d.plugin(function(d,e,f,g,h){function i(a,b){if(null==b){var c=!0;if(b=a.node.getAttribute("linearGradient"==a.type||"radialGradient"==a.type?"gradientTransform":"pattern"==a.type?"patternTransform":"transform"),!b)return new d.Matrix;b=d._.svgTransform2string(b)}else b=d._.rgTransform.test(b)?o(b).replace(/\.{3}|\u2026/g,a._.transform||""):d._.svgTransform2string(b),n(b,"array")&&(b=d.path?d.path.toString.call(b):o(b)),a._.transform=b;var e=d._.transform2matrix(b,a.getBBox(1));return c?e:void(a.matrix=e)}function j(a){function b(a,b){var c=q(a.node,b);c=c&&c.match(f),c=c&&c[2],c&&"#"==c.charAt()&&(c=c.substring(1),c&&(h[c]=(h[c]||[]).concat(function(c){var d={};d[b]=URL(c),q(a.node,d)})))}function c(a){var b=q(a.node,"xlink:href");b&&"#"==b.charAt()&&(b=b.substring(1),b&&(h[b]=(h[b]||[]).concat(function(b){a.attr("xlink:href","#"+b)})))}for(var d,e=a.selectAll("*"),f=/^\s*url\(("|'|)(.*)\1\)\s*$/,g=[],h={},i=0,j=e.length;j>i;i++){d=e[i],b(d,"fill"),b(d,"stroke"),b(d,"filter"),b(d,"mask"),b(d,"clip-path"),c(d);var k=q(d.node,"id");k&&(q(d.node,{id:d.id}),g.push({old:k,id:d.id}))}for(i=0,j=g.length;j>i;i++){var l=h[g[i].old];if(l)for(var m=0,n=l.length;n>m;m++)l[m](g[i].id)}}function k(a,b,c){return function(d){var e=d.slice(a,b);return 1==e.length&&(e=e[0]),c?c(e):e}}function l(a){return function(){var b=a?"<"+this.type:"",c=this.node.attributes,d=this.node.childNodes;if(a)for(var e=0,f=c.length;f>e;e++)b+=" "+c[e].name+'="'+c[e].value.replace(/"/g,'\\"')+'"';if(d.length){for(a&&(b+=">"),e=0,f=d.length;f>e;e++)3==d[e].nodeType?b+=d[e].nodeValue:1==d[e].nodeType&&(b+=u(d[e]).toString());a&&(b+="")}else a&&(b+="/>");return b}}var m=e.prototype,n=d.is,o=String,p=d._unit2px,q=d._.$,r=d._.make,s=d._.getSomeDefs,t="hasOwnProperty",u=d._.wrap;m.getBBox=function(a){if(!d.Matrix||!d.path)return this.node.getBBox();var b=this,c=new d.Matrix;if(b.removed)return d._.box();for(;"use"==b.type;)if(a||(c=c.add(b.transform().localMatrix.translate(b.attr("x")||0,b.attr("y")||0))),b.original)b=b.original;else{var e=b.attr("xlink:href");b=b.original=b.node.ownerDocument.getElementById(e.substring(e.indexOf("#")+1))}var f=b._,g=d.path.get[b.type]||d.path.get.deflt;try{return a?(f.bboxwt=g?d.path.getBBox(b.realPath=g(b)):d._.box(b.node.getBBox()),d._.box(f.bboxwt)):(b.realPath=g(b),b.matrix=b.transform().localMatrix,f.bbox=d.path.getBBox(d.path.map(b.realPath,c.add(b.matrix))),d._.box(f.bbox))}catch(h){return d._.box()}};var v=function(){return this.string};m.transform=function(a){var b=this._;if(null==a){for(var c,e=this,f=new d.Matrix(this.node.getCTM()),g=i(this),h=[g],j=new d.Matrix,k=g.toTransformString(),l=o(g)==o(this.matrix)?o(b.transform):k;"svg"!=e.type&&(e=e.parent());)h.push(i(e));for(c=h.length;c--;)j.add(h[c]);return{string:l,globalMatrix:f,totalMatrix:j,localMatrix:g,diffMatrix:f.clone().add(g.invert()),global:f.toTransformString(),total:j.toTransformString(),local:k,toString:v}}return a instanceof d.Matrix?(this.matrix=a,this._.transform=a.toTransformString()):i(this,a),this.node&&("linearGradient"==this.type||"radialGradient"==this.type?q(this.node,{gradientTransform:this.matrix}):"pattern"==this.type?q(this.node,{patternTransform:this.matrix}):q(this.node,{transform:this.matrix})),this},m.parent=function(){return u(this.node.parentNode)},m.append=m.add=function(a){if(a){if("set"==a.type){var b=this;return a.forEach(function(a){b.add(a)}),this}a=u(a),this.node.appendChild(a.node),a.paper=this.paper}return this},m.appendTo=function(a){return a&&(a=u(a),a.append(this)),this},m.prepend=function(a){if(a){if("set"==a.type){var b,c=this;return a.forEach(function(a){b?b.after(a):c.prepend(a),b=a}),this}a=u(a);var d=a.parent();this.node.insertBefore(a.node,this.node.firstChild),this.add&&this.add(),a.paper=this.paper,this.parent()&&this.parent().add(),d&&d.add()}return this},m.prependTo=function(a){return a=u(a),a.prepend(this),this},m.before=function(a){if("set"==a.type){var b=this;return a.forEach(function(a){var c=a.parent();b.node.parentNode.insertBefore(a.node,b.node),c&&c.add()}),this.parent().add(),this}a=u(a);var c=a.parent();return this.node.parentNode.insertBefore(a.node,this.node),this.parent()&&this.parent().add(),c&&c.add(),a.paper=this.paper,this},m.after=function(a){a=u(a);var b=a.parent();return this.node.nextSibling?this.node.parentNode.insertBefore(a.node,this.node.nextSibling):this.node.parentNode.appendChild(a.node),this.parent()&&this.parent().add(),b&&b.add(),a.paper=this.paper,this},m.insertBefore=function(a){a=u(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},m.insertAfter=function(a){a=u(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node.nextSibling),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},m.remove=function(){var a=this.parent();return this.node.parentNode&&this.node.parentNode.removeChild(this.node),delete this.paper,this.removed=!0,a&&a.add(),this},m.select=function(a){return u(this.node.querySelector(a))},m.selectAll=function(a){for(var b=this.node.querySelectorAll(a),c=(d.set||Array)(),e=0;eb;b++)a[b].stop();return this},m.animate=function(a,d,e,f){"function"!=typeof e||e.length||(f=e,e=c.linear),a instanceof w&&(f=a.callback,e=a.easing,d=a.dur,a=a.attr);var g,h,i,j,l=[],m=[],p={},q=this;for(var r in a)if(a[t](r)){q.equal?(j=q.equal(r,o(a[r])),g=j.from,h=j.to,i=j.f):(g=+q.attr(r),h=+a[r]);var s=n(g,"array")?g.length:1;p[r]=k(l.length,l.length+s,i),l=l.concat(g),m=m.concat(h)}var u=c.time(),v=c(l,m,u,u+d,c.time,function(a){var b={};for(var c in p)p[t](c)&&(b[c]=p[c](a));q.attr(b)},e);return q.anims[v.id]=v,v._attrs=a,v._callback=f,b("snap.animcreated."+q.id,v),b.once("mina.finish."+v.id,function(){delete q.anims[v.id],f&&f.call(q)}),b.once("mina.stop."+v.id,function(){delete q.anims[v.id]}),q};var x={};m.data=function(a,c){var e=x[this.id]=x[this.id]||{};if(0==arguments.length)return b("snap.data.get."+this.id,this,e,null),e; +if(1==arguments.length){if(d.is(a,"object")){for(var f in a)a[t](f)&&this.data(f,a[f]);return this}return b("snap.data.get."+this.id,this,e[a],a),e[a]}return e[a]=c,b("snap.data.set."+this.id,this,c,a),this},m.removeData=function(a){return null==a?x[this.id]={}:x[this.id]&&delete x[this.id][a],this},m.outerSVG=m.toString=l(1),m.innerSVG=l(),m.toDataURL=function(){if(a&&a.btoa){var b=this.getBBox(),c=d.format('{contents}',{x:+b.x.toFixed(3),y:+b.y.toFixed(3),width:+b.width.toFixed(3),height:+b.height.toFixed(3),contents:this.outerSVG()});return"data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(c)))}},h.prototype.select=m.select,h.prototype.selectAll=m.selectAll}),d.plugin(function(a){function b(a,b,d,e,f,g){return null==b&&"[object SVGMatrix]"==c.call(a)?(this.a=a.a,this.b=a.b,this.c=a.c,this.d=a.d,this.e=a.e,void(this.f=a.f)):void(null!=a?(this.a=+a,this.b=+b,this.c=+d,this.d=+e,this.e=+f,this.f=+g):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0))}var c=Object.prototype.toString,d=String,e=Math,f="";!function(c){function g(a){return a[0]*a[0]+a[1]*a[1]}function h(a){var b=e.sqrt(g(a));a[0]&&(a[0]/=b),a[1]&&(a[1]/=b)}c.add=function(a,c,d,e,f,g){var h,i,j,k,l=[[],[],[]],m=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],n=[[a,d,f],[c,e,g],[0,0,1]];for(a&&a instanceof b&&(n=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]),h=0;3>h;h++)for(i=0;3>i;i++){for(k=0,j=0;3>j;j++)k+=m[h][j]*n[j][i];l[h][i]=k}return this.a=l[0][0],this.b=l[1][0],this.c=l[0][1],this.d=l[1][1],this.e=l[0][2],this.f=l[1][2],this},c.invert=function(){var a=this,c=a.a*a.d-a.b*a.c;return new b(a.d/c,-a.b/c,-a.c/c,a.a/c,(a.c*a.f-a.d*a.e)/c,(a.b*a.e-a.a*a.f)/c)},c.clone=function(){return new b(this.a,this.b,this.c,this.d,this.e,this.f)},c.translate=function(a,b){return this.add(1,0,0,1,a,b)},c.scale=function(a,b,c,d){return null==b&&(b=a),(c||d)&&this.add(1,0,0,1,c,d),this.add(a,0,0,b,0,0),(c||d)&&this.add(1,0,0,1,-c,-d),this},c.rotate=function(b,c,d){b=a.rad(b),c=c||0,d=d||0;var f=+e.cos(b).toFixed(9),g=+e.sin(b).toFixed(9);return this.add(f,g,-g,f,c,d),this.add(1,0,0,1,-c,-d)},c.x=function(a,b){return a*this.a+b*this.c+this.e},c.y=function(a,b){return a*this.b+b*this.d+this.f},c.get=function(a){return+this[d.fromCharCode(97+a)].toFixed(4)},c.toString=function(){return"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")"},c.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},c.determinant=function(){return this.a*this.d-this.b*this.c},c.split=function(){var b={};b.dx=this.e,b.dy=this.f;var c=[[this.a,this.c],[this.b,this.d]];b.scalex=e.sqrt(g(c[0])),h(c[0]),b.shear=c[0][0]*c[1][0]+c[0][1]*c[1][1],c[1]=[c[1][0]-c[0][0]*b.shear,c[1][1]-c[0][1]*b.shear],b.scaley=e.sqrt(g(c[1])),h(c[1]),b.shear/=b.scaley,this.determinant()<0&&(b.scalex=-b.scalex);var d=-c[0][1],f=c[1][1];return 0>f?(b.rotate=a.deg(e.acos(f)),0>d&&(b.rotate=360-b.rotate)):b.rotate=a.deg(e.asin(d)),b.isSimple=!(+b.shear.toFixed(9)||b.scalex.toFixed(9)!=b.scaley.toFixed(9)&&b.rotate),b.isSuperSimple=!+b.shear.toFixed(9)&&b.scalex.toFixed(9)==b.scaley.toFixed(9)&&!b.rotate,b.noRotation=!+b.shear.toFixed(9)&&!b.rotate,b},c.toTransformString=function(a){var b=a||this.split();return+b.shear.toFixed(9)?"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]:(b.scalex=+b.scalex.toFixed(4),b.scaley=+b.scaley.toFixed(4),b.rotate=+b.rotate.toFixed(4),(b.dx||b.dy?"t"+[+b.dx.toFixed(4),+b.dy.toFixed(4)]:f)+(1!=b.scalex||1!=b.scaley?"s"+[b.scalex,b.scaley,0,0]:f)+(b.rotate?"r"+[+b.rotate.toFixed(4),0,0]:f))}}(b.prototype),a.Matrix=b,a.matrix=function(a,c,d,e,f,g){return new b(a,c,d,e,f,g)}}),d.plugin(function(a,c,d,e,f){function g(d){return function(e){if(b.stop(),e instanceof f&&1==e.node.childNodes.length&&("radialGradient"==e.node.firstChild.tagName||"linearGradient"==e.node.firstChild.tagName||"pattern"==e.node.firstChild.tagName)&&(e=e.node.firstChild,n(this).appendChild(e),e=l(e)),e instanceof c)if("radialGradient"==e.type||"linearGradient"==e.type||"pattern"==e.type){e.node.id||p(e.node,{id:e.id});var g=q(e.node.id)}else g=e.attr(d);else if(g=a.color(e),g.error){var h=a(n(this).ownerSVGElement).gradient(e);h?(h.node.id||p(h.node,{id:h.id}),g=q(h.node.id)):g=e}else g=r(g);var i={};i[d]=g,p(this.node,i),this.node.style[d]=t}}function h(a){b.stop(),a==+a&&(a+="px"),this.node.style.fontSize=a}function i(a){for(var b=[],c=a.childNodes,d=0,e=c.length;e>d;d++){var f=c[d];3==f.nodeType&&b.push(f.nodeValue),"tspan"==f.tagName&&b.push(1==f.childNodes.length&&3==f.firstChild.nodeType?f.firstChild.nodeValue:i(f))}return b}function j(){return b.stop(),this.node.style.fontSize}var k=a._.make,l=a._.wrap,m=a.is,n=a._.getSomeDefs,o=/^url\(#?([^)]+)\)$/,p=a._.$,q=a.url,r=String,s=a._.separator,t="";b.on("snap.util.attr.mask",function(a){if(a instanceof c||a instanceof f){if(b.stop(),a instanceof f&&1==a.node.childNodes.length&&(a=a.node.firstChild,n(this).appendChild(a),a=l(a)),"mask"==a.type)var d=a;else d=k("mask",n(this)),d.node.appendChild(a.node);!d.node.id&&p(d.node,{id:d.id}),p(this.node,{mask:q(d.id)})}}),function(a){b.on("snap.util.attr.clip",a),b.on("snap.util.attr.clip-path",a),b.on("snap.util.attr.clipPath",a)}(function(a){if(a instanceof c||a instanceof f){if(b.stop(),"clipPath"==a.type)var d=a;else d=k("clipPath",n(this)),d.node.appendChild(a.node),!d.node.id&&p(d.node,{id:d.id});p(this.node,{"clip-path":q(d.node.id||d.id)})}}),b.on("snap.util.attr.fill",g("fill")),b.on("snap.util.attr.stroke",g("stroke"));var u=/^([lr])(?:\(([^)]*)\))?(.*)$/i;b.on("snap.util.grad.parse",function(a){a=r(a);var b=a.match(u);if(!b)return null;var c=b[1],d=b[2],e=b[3];return d=d.split(/\s*,\s*/).map(function(a){return+a==a?+a:a}),1==d.length&&0==d[0]&&(d=[]),e=e.split("-"),e=e.map(function(a){a=a.split(":");var b={color:a[0]};return a[1]&&(b.offset=parseFloat(a[1])),b}),{type:c,params:d,stops:e}}),b.on("snap.util.attr.d",function(c){b.stop(),m(c,"array")&&m(c[0],"array")&&(c=a.path.toString.call(c)),c=r(c),c.match(/[ruo]/i)&&(c=a.path.toAbsolute(c)),p(this.node,{d:c})})(-1),b.on("snap.util.attr.#text",function(a){b.stop(),a=r(a);for(var c=e.doc.createTextNode(a);this.node.firstChild;)this.node.removeChild(this.node.firstChild);this.node.appendChild(c)})(-1),b.on("snap.util.attr.path",function(a){b.stop(),this.attr({d:a})})(-1),b.on("snap.util.attr.class",function(a){b.stop(),this.node.className.baseVal=a})(-1),b.on("snap.util.attr.viewBox",function(a){var c;c=m(a,"object")&&"x"in a?[a.x,a.y,a.width,a.height].join(" "):m(a,"array")?a.join(" "):a,p(this.node,{viewBox:c}),b.stop()})(-1),b.on("snap.util.attr.transform",function(a){this.transform(a),b.stop()})(-1),b.on("snap.util.attr.r",function(a){"rect"==this.type&&(b.stop(),p(this.node,{rx:a,ry:a}))})(-1),b.on("snap.util.attr.textpath",function(a){if(b.stop(),"text"==this.type){var d,e,f;if(!a&&this.textPath){for(e=this.textPath;e.node.firstChild;)this.node.appendChild(e.node.firstChild);return e.remove(),void delete this.textPath}if(m(a,"string")){var g=n(this),h=l(g.parentNode).path(a);g.appendChild(h.node),d=h.id,h.attr({id:d})}else a=l(a),a instanceof c&&(d=a.attr("id"),d||(d=a.id,a.attr({id:d})));if(d)if(e=this.textPath,f=this.node,e)e.attr({"xlink:href":"#"+d});else{for(e=p("textPath",{"xlink:href":"#"+d});f.firstChild;)e.appendChild(f.firstChild);f.appendChild(e),this.textPath=l(e)}}})(-1),b.on("snap.util.attr.text",function(a){if("text"==this.type){for(var c=this.node,d=function(a){var b=p("tspan");if(m(a,"array"))for(var c=0;c1&&(a=Array.prototype.slice.call(arguments,0));var b={};return h(a,"object")&&!h(a,"array")?b=a:null!=a&&(b={points:a}),this.el("polyline",b)},g.polygon=function(a){arguments.length>1&&(a=Array.prototype.slice.call(arguments,0));var b={};return h(a,"object")&&!h(a,"array")?b=a:null!=a&&(b={points:a}),this.el("polygon",b)},function(){function d(){return this.selectAll("stop")}function e(a,b){var d=k("stop"),e={offset:+b+"%"};return a=c.color(a),e["stop-color"]=a.hex,a.opacity<1&&(e["stop-opacity"]=a.opacity),k(d,e),this.node.appendChild(d),this}function f(){if("linearGradient"==this.type){var a=k(this.node,"x1")||0,b=k(this.node,"x2")||1,d=k(this.node,"y1")||0,e=k(this.node,"y2")||0;return c._.box(a,d,math.abs(b-a),math.abs(e-d))}var f=this.node.cx||.5,g=this.node.cy||.5,h=this.node.r||0;return c._.box(f-h,g-h,2*h,2*h)}function h(a,c){function d(a,b){for(var c=(b-l)/(a-m),d=m;a>d;d++)g[d].offset=+(+l+c*(d-m)).toFixed(2);m=a,l=b}var e,f=b("snap.util.grad.parse",null,c).firstDefined();if(!f)return null;f.params.unshift(a),e="l"==f.type.toLowerCase()?i.apply(0,f.params):j.apply(0,f.params),f.type!=f.type.toLowerCase()&&k(e.node,{gradientUnits:"userSpaceOnUse"});var g=f.stops,h=g.length,l=0,m=0;h--;for(var n=0;h>n;n++)"offset"in g[n]&&d(n,g[n].offset);for(g[h].offset=g[h].offset||100,d(h,g[h].offset),n=0;h>=n;n++){var o=g[n];e.addStop(o.color,o.offset)}return e}function i(a,b,g,h,i){var j=c._.make("linearGradient",a);return j.stops=d,j.addStop=e,j.getBBox=f,null!=b&&k(j.node,{x1:b,y1:g,x2:h,y2:i}),j}function j(a,b,g,h,i,j){var l=c._.make("radialGradient",a);return l.stops=d,l.addStop=e,l.getBBox=f,null!=b&&k(l.node,{cx:b,cy:g,r:h}),null!=i&&null!=j&&k(l.node,{fx:i,fy:j}),l}var k=c._.$;g.gradient=function(a){return h(this.defs,a)},g.gradientLinear=function(a,b,c,d){return i(this.defs,a,b,c,d)},g.gradientRadial=function(a,b,c,d,e){return j(this.defs,a,b,c,d,e)},g.toString=function(){var a,b=this.node.ownerDocument,d=b.createDocumentFragment(),e=b.createElement("div"),f=this.node.cloneNode(!0);return d.appendChild(e),e.appendChild(f),c._.$(f,{xmlns:"http://www.w3.org/2000/svg"}),a=e.innerHTML,d.removeChild(d.firstChild),a},g.toDataURL=function(){return a&&a.btoa?"data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(this))):void 0},g.clear=function(){for(var a,b=this.node.firstChild;b;)a=b.nextSibling,"defs"!=b.tagName?b.parentNode.removeChild(b):g.clear.call({node:b}),b=a}}()}),d.plugin(function(a,b){function c(a){var b=c.ps=c.ps||{};return b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[K](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])}),b[a]}function d(a,b,c,d){return null==a&&(a=b=c=d=0),null==b&&(b=a.y,c=a.width,d=a.height,a=a.x),{x:a,y:b,width:c,w:c,height:d,h:d,x2:a+c,y2:b+d,cx:a+c/2,cy:b+d/2,r1:N.min(c,d)/2,r2:N.max(c,d)/2,r0:N.sqrt(c*c+d*d)/2,path:w(a,b,c,d),vb:[a,b,c,d].join(" ")}}function e(){return this.join(",").replace(L,"$1")}function f(a){var b=J(a);return b.toString=e,b}function g(a,b,c,d,e,f,g,h,j){return null==j?n(a,b,c,d,e,f,g,h):i(a,b,c,d,e,f,g,h,o(a,b,c,d,e,f,g,h,j))}function h(c,d){function e(a){return+(+a).toFixed(3)}return a._.cacher(function(a,f,h){a instanceof b&&(a=a.attr("d")),a=E(a);for(var j,k,l,m,n,o="",p={},q=0,r=0,s=a.length;s>r;r++){if(l=a[r],"M"==l[0])j=+l[1],k=+l[2];else{if(m=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6]),q+m>f){if(d&&!p.start){if(n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q),o+=["C"+e(n.start.x),e(n.start.y),e(n.m.x),e(n.m.y),e(n.x),e(n.y)],h)return o;p.start=o,o=["M"+e(n.x),e(n.y)+"C"+e(n.n.x),e(n.n.y),e(n.end.x),e(n.end.y),e(l[5]),e(l[6])].join(),q+=m,j=+l[5],k=+l[6];continue}if(!c&&!d)return n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q)}q+=m,j=+l[5],k=+l[6]}o+=l.shift()+l}return p.end=o,n=c?q:d?p:i(j,k,l[0],l[1],l[2],l[3],l[4],l[5],1)},null,a._.clone)}function i(a,b,c,d,e,f,g,h,i){var j=1-i,k=R(j,3),l=R(j,2),m=i*i,n=m*i,o=k*a+3*l*i*c+3*j*i*i*e+n*g,p=k*b+3*l*i*d+3*j*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,w=j*e+i*g,x=j*f+i*h,y=90-180*N.atan2(q-s,r-t)/O;return{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:w,y:x},alpha:y}}function j(b,c,e,f,g,h,i,j){a.is(b,"array")||(b=[b,c,e,f,g,h,i,j]);var k=D.apply(null,b);return d(k.min.x,k.min.y,k.max.x-k.min.x,k.max.y-k.min.y)}function k(a,b,c){return b>=a.x&&b<=a.x+a.width&&c>=a.y&&c<=a.y+a.height}function l(a,b){return a=d(a),b=d(b),k(b,a.x,a.y)||k(b,a.x2,a.y)||k(b,a.x,a.y2)||k(b,a.x2,a.y2)||k(a,b.x,b.y)||k(a,b.x2,b.y)||k(a,b.x,b.y2)||k(a,b.x2,b.y2)||(a.xb.x||b.xa.x)&&(a.yb.y||b.ya.y)}function m(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function n(a,b,c,d,e,f,g,h,i){null==i&&(i=1),i=i>1?1:0>i?0:i;for(var j=i/2,k=12,l=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],n=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],o=0,p=0;k>p;p++){var q=j*l[p]+j,r=m(q,a,c,e,g),s=m(q,b,d,f,h),t=r*r+s*s;o+=n[p]*N.sqrt(t)}return j*o}function o(a,b,c,d,e,f,g,h,i){if(!(0>i||n(a,b,c,d,e,f,g,h)o;)l/=2,m+=(i>j?1:-1)*l,j=n(a,b,c,d,e,f,g,h,m);return m}}function p(a,b,c,d,e,f,g,h){if(!(Q(a,c)Q(e,g)||Q(b,d)Q(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(k){var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(!(n<+P(a,c).toFixed(2)||n>+Q(a,c).toFixed(2)||n<+P(e,g).toFixed(2)||n>+Q(e,g).toFixed(2)||o<+P(b,d).toFixed(2)||o>+Q(b,d).toFixed(2)||o<+P(f,h).toFixed(2)||o>+Q(f,h).toFixed(2)))return{x:l,y:m}}}}function q(a,b,c){var d=j(a),e=j(b);if(!l(d,e))return c?0:[];for(var f=n.apply(0,a),g=n.apply(0,b),h=~~(f/8),k=~~(g/8),m=[],o=[],q={},r=c?0:[],s=0;h+1>s;s++){var t=i.apply(0,a.concat(s/h));m.push({x:t.x,y:t.y,t:s/h})}for(s=0;k+1>s;s++)t=i.apply(0,b.concat(s/k)),o.push({x:t.x,y:t.y,t:s/k});for(s=0;h>s;s++)for(var u=0;k>u;u++){var v=m[s],w=m[s+1],x=o[u],y=o[u+1],z=S(w.x-v.x)<.001?"y":"x",A=S(y.x-x.x)<.001?"y":"x",B=p(v.x,v.y,w.x,w.y,x.x,x.y,y.x,y.y);if(B){if(q[B.x.toFixed(4)]==B.y.toFixed(4))continue;q[B.x.toFixed(4)]=B.y.toFixed(4);var C=v.t+S((B[z]-v[z])/(w[z]-v[z]))*(w.t-v.t),D=x.t+S((B[A]-x[A])/(y[A]-x[A]))*(y.t-x.t);C>=0&&1>=C&&D>=0&&1>=D&&(c?r++:r.push({x:B.x,y:B.y,t1:C,t2:D}))}}return r}function r(a,b){return t(a,b)}function s(a,b){return t(a,b,1)}function t(a,b,c){a=E(a),b=E(b);for(var d,e,f,g,h,i,j,k,l,m,n=c?0:[],o=0,p=a.length;p>o;o++){var r=a[o];if("M"==r[0])d=h=r[1],e=i=r[2];else{"C"==r[0]?(l=[d,e].concat(r.slice(1)),d=l[6],e=l[7]):(l=[d,e,d,e,h,i,h,i],d=h,e=i);for(var s=0,t=b.length;t>s;s++){var u=b[s];if("M"==u[0])f=j=u[1],g=k=u[2];else{"C"==u[0]?(m=[f,g].concat(u.slice(1)),f=m[6],g=m[7]):(m=[f,g,f,g,j,k,j,k],f=j,g=k);var v=q(l,m,c);if(c)n+=v;else{for(var w=0,x=v.length;x>w;w++)v[w].segment1=o,v[w].segment2=s,v[w].bez1=l,v[w].bez2=m;n=n.concat(v)}}}}}return n}function u(a,b,c){var d=v(a);return k(d,b,c)&&t(a,[["M",b,c],["H",d.x2+10]],1)%2==1}function v(a){var b=c(a);if(b.bbox)return J(b.bbox);if(!a)return d();a=E(a);for(var e,f=0,g=0,h=[],i=[],j=0,k=a.length;k>j;j++)if(e=a[j],"M"==e[0])f=e[1],g=e[2],h.push(f),i.push(g);else{var l=D(f,g,e[1],e[2],e[3],e[4],e[5],e[6]);h=h.concat(l.min.x,l.max.x),i=i.concat(l.min.y,l.max.y),f=e[5],g=e[6]}var m=P.apply(0,h),n=P.apply(0,i),o=Q.apply(0,h),p=Q.apply(0,i),q=d(m,n,o-m,p-n);return b.bbox=J(q),q}function w(a,b,c,d,f){if(f)return[["M",+a+ +f,b],["l",c-2*f,0],["a",f,f,0,0,1,f,f],["l",0,d-2*f],["a",f,f,0,0,1,-f,f],["l",2*f-c,0],["a",f,f,0,0,1,-f,-f],["l",0,2*f-d],["a",f,f,0,0,1,f,-f],["z"]];var g=[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]];return g.toString=e,g}function x(a,b,c,d,f){if(null==f&&null==d&&(d=c),a=+a,b=+b,c=+c,d=+d,null!=f)var g=Math.PI/180,h=a+c*Math.cos(-d*g),i=a+c*Math.cos(-f*g),j=b+c*Math.sin(-d*g),k=b+c*Math.sin(-f*g),l=[["M",h,j],["A",c,c,0,+(f-d>180),0,i,k]];else l=[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]];return l.toString=e,l}function y(b){var d=c(b),g=String.prototype.toLowerCase;if(d.rel)return f(d.rel);a.is(b,"array")&&a.is(b&&b[0],"array")||(b=a.parsePathString(b));var h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=b[0][1],j=b[0][2],k=i,l=j,m++,h.push(["M",i,j]));for(var n=m,o=b.length;o>n;n++){var p=h[n]=[],q=b[n];if(q[0]!=g.call(q[0]))switch(p[0]=g.call(q[0]),p[0]){case"a":p[1]=q[1],p[2]=q[2],p[3]=q[3],p[4]=q[4],p[5]=q[5],p[6]=+(q[6]-i).toFixed(3),p[7]=+(q[7]-j).toFixed(3);break;case"v":p[1]=+(q[1]-j).toFixed(3);break;case"m":k=q[1],l=q[2];default:for(var r=1,s=q.length;s>r;r++)p[r]=+(q[r]-(r%2?i:j)).toFixed(3)}else{p=h[n]=[],"m"==q[0]&&(k=q[1]+i,l=q[2]+j);for(var t=0,u=q.length;u>t;t++)h[n][t]=q[t]}var v=h[n].length;switch(h[n][0]){case"z":i=k,j=l;break;case"h":i+=+h[n][v-1];break;case"v":j+=+h[n][v-1];break;default:i+=+h[n][v-2],j+=+h[n][v-1]}}return h.toString=e,d.rel=f(h),h}function z(b){var d=c(b);if(d.abs)return f(d.abs);if(I(b,"array")&&I(b&&b[0],"array")||(b=a.parsePathString(b)),!b||!b.length)return[["M",0,0]];var g,h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=+b[0][1],j=+b[0][2],k=i,l=j,m++,h[0]=["M",i,j]);for(var n,o,p=3==b.length&&"M"==b[0][0]&&"R"==b[1][0].toUpperCase()&&"Z"==b[2][0].toUpperCase(),q=m,r=b.length;r>q;q++){if(h.push(n=[]),o=b[q],g=o[0],g!=g.toUpperCase())switch(n[0]=g.toUpperCase(),n[0]){case"A":n[1]=o[1],n[2]=o[2],n[3]=o[3],n[4]=o[4],n[5]=o[5],n[6]=+o[6]+i,n[7]=+o[7]+j;break;case"V":n[1]=+o[1]+j;break;case"H":n[1]=+o[1]+i;break;case"R":for(var s=[i,j].concat(o.slice(1)),t=2,u=s.length;u>t;t++)s[t]=+s[t]+i,s[++t]=+s[t]+j;h.pop(),h=h.concat(G(s,p));break;case"O":h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);break;case"U":h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));break;case"M":k=+o[1]+i,l=+o[2]+j;default:for(t=1,u=o.length;u>t;t++)n[t]=+o[t]+(t%2?i:j)}else if("R"==g)s=[i,j].concat(o.slice(1)),h.pop(),h=h.concat(G(s,p)),n=["R"].concat(o.slice(-2));else if("O"==g)h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);else if("U"==g)h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));else for(var v=0,w=o.length;w>v;v++)n[v]=o[v];if(g=g.toUpperCase(),"O"!=g)switch(n[0]){case"Z":i=+k,j=+l;break;case"H":i=n[1];break;case"V":j=n[1];break;case"M":k=n[n.length-2],l=n[n.length-1];default:i=n[n.length-2],j=n[n.length-1]}}return h.toString=e,d.abs=f(h),h}function A(a,b,c,d){return[a,b,c,d,c,d]}function B(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]}function C(b,c,d,e,f,g,h,i,j,k){var l,m=120*O/180,n=O/180*(+f||0),o=[],p=a._.cacher(function(a,b,c){var d=a*N.cos(c)-b*N.sin(c),e=a*N.sin(c)+b*N.cos(c);return{x:d,y:e}});if(k)y=k[0],z=k[1],w=k[2],x=k[3];else{l=p(b,c,-n),b=l.x,c=l.y,l=p(i,j,-n),i=l.x,j=l.y;var q=(N.cos(O/180*f),N.sin(O/180*f),(b-i)/2),r=(c-j)/2,s=q*q/(d*d)+r*r/(e*e);s>1&&(s=N.sqrt(s),d=s*d,e=s*e);var t=d*d,u=e*e,v=(g==h?-1:1)*N.sqrt(S((t*u-t*r*r-u*q*q)/(t*r*r+u*q*q))),w=v*d*r/e+(b+i)/2,x=v*-e*q/d+(c+j)/2,y=N.asin(((c-x)/e).toFixed(9)),z=N.asin(((j-x)/e).toFixed(9));y=w>b?O-y:y,z=w>i?O-z:z,0>y&&(y=2*O+y),0>z&&(z=2*O+z),h&&y>z&&(y-=2*O),!h&&z>y&&(z-=2*O)}var A=z-y;if(S(A)>m){var B=z,D=i,E=j;z=y+m*(h&&z>y?1:-1),i=w+d*N.cos(z),j=x+e*N.sin(z),o=C(i,j,d,e,f,0,h,D,E,[z,B,w,x])}A=z-y;var F=N.cos(y),G=N.sin(y),H=N.cos(z),I=N.sin(z),J=N.tan(A/4),K=4/3*d*J,L=4/3*e*J,M=[b,c],P=[b+K*G,c-L*F],Q=[i+K*I,j-L*H],R=[i,j];if(P[0]=2*M[0]-P[0],P[1]=2*M[1]-P[1],k)return[P,Q,R].concat(o);o=[P,Q,R].concat(o).join().split(",");for(var T=[],U=0,V=o.length;V>U;U++)T[U]=U%2?p(o[U-1],o[U],n).y:p(o[U],o[U+1],n).x;return T}function D(a,b,c,d,e,f,g,h){for(var i,j,k,l,m,n,o,p,q=[],r=[[],[]],s=0;2>s;++s)if(0==s?(j=6*a-12*c+6*e,i=-3*a+9*c-9*e+3*g,k=3*c-3*a):(j=6*b-12*d+6*f,i=-3*b+9*d-9*f+3*h,k=3*d-3*b),S(i)<1e-12){if(S(j)<1e-12)continue;l=-k/j,l>0&&1>l&&q.push(l)}else o=j*j-4*k*i,p=N.sqrt(o),0>o||(m=(-j+p)/(2*i),m>0&&1>m&&q.push(m),n=(-j-p)/(2*i),n>0&&1>n&&q.push(n));for(var t,u=q.length,v=u;u--;)l=q[u],t=1-l,r[0][u]=t*t*t*a+3*t*t*l*c+3*t*l*l*e+l*l*l*g,r[1][u]=t*t*t*b+3*t*t*l*d+3*t*l*l*f+l*l*l*h;return r[0][v]=a,r[1][v]=b,r[0][v+1]=g,r[1][v+1]=h,r[0].length=r[1].length=v+2,{min:{x:P.apply(0,r[0]),y:P.apply(0,r[1])},max:{x:Q.apply(0,r[0]),y:Q.apply(0,r[1])}}}function E(a,b){var d=!b&&c(a);if(!b&&d.curve)return f(d.curve);for(var e=z(a),g=b&&z(b),h={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},i={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},j=(function(a,b,c){var d,e;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];switch(!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null),a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"].concat(C.apply(0,[b.x,b.y].concat(a.slice(1))));break;case"S":"C"==c||"S"==c?(d=2*b.x-b.bx,e=2*b.y-b.by):(d=b.x,e=b.y),a=["C",d,e].concat(a.slice(1));break;case"T":"Q"==c||"T"==c?(b.qx=2*b.x-b.qx,b.qy=2*b.y-b.qy):(b.qx=b.x,b.qy=b.y),a=["C"].concat(B(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"].concat(B(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"].concat(A(b.x,b.y,a[1],a[2]));break;case"H":a=["C"].concat(A(b.x,b.y,a[1],b.y));break;case"V":a=["C"].concat(A(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"].concat(A(b.x,b.y,b.X,b.Y))}return a}),k=function(a,b){if(a[b].length>7){a[b].shift();for(var c=a[b];c.length;)m[b]="A",g&&(n[b]="A"),a.splice(b++,0,["C"].concat(c.splice(0,6)));a.splice(b,1),r=Q(e.length,g&&g.length||0)}},l=function(a,b,c,d,f){a&&b&&"M"==a[f][0]&&"M"!=b[f][0]&&(b.splice(f,0,["M",d.x,d.y]),c.bx=0,c.by=0,c.x=a[f][1],c.y=a[f][2],r=Q(e.length,g&&g.length||0))},m=[],n=[],o="",p="",q=0,r=Q(e.length,g&&g.length||0);r>q;q++){e[q]&&(o=e[q][0]),"C"!=o&&(m[q]=o,q&&(p=m[q-1])),e[q]=j(e[q],h,p),"A"!=m[q]&&"C"==o&&(m[q]="C"),k(e,q),g&&(g[q]&&(o=g[q][0]),"C"!=o&&(n[q]=o,q&&(p=n[q-1])),g[q]=j(g[q],i,p),"A"!=n[q]&&"C"==o&&(n[q]="C"),k(g,q)),l(e,g,h,i,q),l(g,e,i,h,q);var s=e[q],t=g&&g[q],u=s.length,v=g&&t.length;h.x=s[u-2],h.y=s[u-1],h.bx=M(s[u-4])||h.x,h.by=M(s[u-3])||h.y,i.bx=g&&(M(t[v-4])||i.x),i.by=g&&(M(t[v-3])||i.y),i.x=g&&t[v-2],i.y=g&&t[v-1]}return g||(d.curve=f(e)),g?[e,g]:e}function F(a,b){if(!b)return a;var c,d,e,f,g,h,i;for(a=E(a),e=0,g=a.length;g>e;e++)for(i=a[e],f=1,h=i.length;h>f;f+=2)c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d;return a}function G(a,b){for(var c=[],d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}var H=b.prototype,I=a.is,J=a._.clone,K="hasOwnProperty",L=/,?([a-z]),?/gi,M=parseFloat,N=Math,O=N.PI,P=N.min,Q=N.max,R=N.pow,S=N.abs,T=h(1),U=h(),V=h(0,1),W=a._unit2px,X={path:function(a){return a.attr("path")},circle:function(a){var b=W(a);return x(b.cx,b.cy,b.r)},ellipse:function(a){var b=W(a);return x(b.cx||0,b.cy||0,b.rx,b.ry)},rect:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height,b.rx,b.ry)},image:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height)},line:function(a){return"M"+[a.attr("x1")||0,a.attr("y1")||0,a.attr("x2"),a.attr("y2")]},polyline:function(a){return"M"+a.attr("points")},polygon:function(a){return"M"+a.attr("points")+"z"},deflt:function(a){var b=a.node.getBBox();return w(b.x,b.y,b.width,b.height)}};a.path=c,a.path.getTotalLength=T,a.path.getPointAtLength=U,a.path.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return V(a,b).end;var d=V(a,c,1);return b?V(d,b).end:d},H.getTotalLength=function(){return this.node.getTotalLength?this.node.getTotalLength():void 0},H.getPointAtLength=function(a){return U(this.attr("d"),a)},H.getSubpath=function(b,c){return a.path.getSubpath(this.attr("d"),b,c)},a._.box=d,a.path.findDotsAtSegment=i,a.path.bezierBBox=j,a.path.isPointInsideBBox=k,a.closest=function(b,c,e,f){for(var g=100,h=d(b-g/2,c-g/2,g,g),i=[],j=e[0].hasOwnProperty("x")?function(a){return{x:e[a].x,y:e[a].y}}:function(a){return{x:e[a],y:f[a]}},l=0;1e6>=g&&!l;){for(var m=0,n=e.length;n>m;m++){var o=j(m);if(k(h,o.x,o.y)){l++,i.push(o);break}}l||(g*=2,h=d(b-g/2,c-g/2,g,g))}if(1e6!=g){var p,q=1/0;for(m=0,n=i.length;n>m;m++){var r=a.len(b,c,i[m].x,i[m].y);q>r&&(q=r,i[m].len=r,p=i[m])}return p}},a.path.isBBoxIntersect=l,a.path.intersection=r,a.path.intersectionNumber=s,a.path.isPointInside=u,a.path.getBBox=v,a.path.get=X,a.path.toRelative=y,a.path.toAbsolute=z,a.path.toCubic=E,a.path.map=F,a.path.toString=e,a.path.clone=f}),d.plugin(function(a){var d=Math.max,e=Math.min,f=function(a){if(this.items=[],this.bindings={},this.length=0,this.type="set",a)for(var b=0,c=a.length;c>b;b++)a[b]&&(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},g=f.prototype;g.push=function(){for(var a,b,c=0,d=arguments.length;d>c;c++)a=arguments[c],a&&(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},g.pop=function(){return this.length&&delete this[this.length--],this.items.pop()},g.forEach=function(a,b){for(var c=0,d=this.items.length;d>c;c++)if(a.call(b,this.items[c],c)===!1)return this;return this},g.animate=function(d,e,f,g){"function"!=typeof f||f.length||(g=f,f=c.linear),d instanceof a._.Animation&&(g=d.callback,f=d.easing,e=f.dur,d=d.attr);var h=arguments;if(a.is(d,"array")&&a.is(h[h.length-1],"array"))var i=!0;var j,k=function(){j?this.b=j:j=this.b},l=0,m=this,n=g&&function(){++l==m.length&&g.call(this) +};return this.forEach(function(a,c){b.once("snap.animcreated."+a.id,k),i?h[c]&&a.animate.apply(a,h[c]):a.animate(d,e,f,n)})},g.remove=function(){for(;this.length;)this.pop().remove();return this},g.bind=function(a,b,c){var d={};if("function"==typeof b)this.bindings[a]=b;else{var e=c||a;this.bindings[a]=function(a){d[e]=a,b.attr(d)}}return this},g.attr=function(a){var b={};for(var c in a)this.bindings[c]?this.bindings[c](a[c]):b[c]=a[c];for(var d=0,e=this.items.length;e>d;d++)this.items[d].attr(b);return this},g.clear=function(){for(;this.length;)this.pop()},g.splice=function(a,b){a=0>a?d(this.length+a,0):a,b=d(0,e(this.length-a,b));var c,g=[],h=[],i=[];for(c=2;cc;c++)h.push(this[a+c]);for(;cc?i[c]:g[c-j];for(c=this.items.length=this.length-=b-j;this[c];)delete this[c++];return new f(h)},g.exclude=function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]==a)return this.splice(b,1),!0;return!1},g.insertAfter=function(a){for(var b=this.items.length;b--;)this.items[b].insertAfter(a);return this},g.getBBox=function(){for(var a=[],b=[],c=[],f=[],g=this.items.length;g--;)if(!this.items[g].removed){var h=this.items[g].getBBox();a.push(h.x),b.push(h.y),c.push(h.x+h.width),f.push(h.y+h.height)}return a=e.apply(0,a),b=e.apply(0,b),c=d.apply(0,c),f=d.apply(0,f),{x:a,y:b,x2:c,y2:f,width:c-a,height:f-b,cx:a+(c-a)/2,cy:b+(f-b)/2}},g.clone=function(a){a=new f;for(var b=0,c=this.items.length;c>b;b++)a.push(this.items[b].clone());return a},g.toString=function(){return"Snap‘s set"},g.type="set",a.Set=f,a.set=function(){var a=new f;return arguments.length&&a.push.apply(a,Array.prototype.slice.call(arguments,0)),a}}),d.plugin(function(a,c){function d(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return 4==a.length?[b,0,a[2],a[3]]:[b,0];case"s":return 5==a.length?[b,1,1,a[3],a[4]]:3==a.length?[b,1,1]:[b,1]}}function e(b,c,e){c=p(c).replace(/\.{3}|\u2026/g,b),b=a.parseTransformString(b)||[],c=a.parseTransformString(c)||[];for(var f,g,h,i,l=Math.max(b.length,c.length),m=[],n=[],o=0;l>o;o++){if(h=b[o]||d(c[o]),i=c[o]||d(h),h[0]!=i[0]||"r"==h[0].toLowerCase()&&(h[2]!=i[2]||h[3]!=i[3])||"s"==h[0].toLowerCase()&&(h[3]!=i[3]||h[4]!=i[4])){b=a._.transform2matrix(b,e()),c=a._.transform2matrix(c,e()),m=[["m",b.a,b.b,b.c,b.d,b.e,b.f]],n=[["m",c.a,c.b,c.c,c.d,c.e,c.f]];break}for(m[o]=[],n[o]=[],f=0,g=Math.max(h.length,i.length);g>f;f++)f in h&&(m[o][f]=h[f]),f in i&&(n[o][f]=i[f])}return{from:k(m),to:k(n),f:j(m)}}function f(a){return a}function g(a){return function(b){return+b.toFixed(3)+a}}function h(a){return a.join(" ")}function i(b){return a.rgb(b[0],b[1],b[2])}function j(a){var b,c,d,e,f,g,h=0,i=[];for(b=0,c=a.length;c>b;b++){for(f="[",g=['"'+a[b][0]+'"'],d=1,e=a[b].length;e>d;d++)g[d]="val["+h++ +"]";f+=g+"]",i[b]=f}return Function("val","return Snap.path.toString.call(["+i+"])")}function k(a){for(var b=[],c=0,d=a.length;d>c;c++)for(var e=1,f=a[c].length;f>e;e++)b.push(a[c][e]);return b}function l(a){return isFinite(parseFloat(a))}function m(b,c){return a.is(b,"array")&&a.is(c,"array")?b.toString()==c.toString():!1}var n={},o=/[a-z]+$/i,p=String;n.stroke=n.fill="colour",c.prototype.equal=function(a,c){return b("snap.util.equal",this,a,c).firstDefined()},b.on("snap.util.equal",function(b,c){var d,q,r=p(this.attr(b)||""),s=this;if(l(r)&&l(c))return{from:parseFloat(r),to:parseFloat(c),f:f};if("colour"==n[b])return d=a.color(r),q=a.color(c),{from:[d.r,d.g,d.b,d.opacity],to:[q.r,q.g,q.b,q.opacity],f:i};if("viewBox"==b)return d=this.attr(b).vb.split(" ").map(Number),q=c.split(" ").map(Number),{from:d,to:q,f:h};if("transform"==b||"gradientTransform"==b||"patternTransform"==b)return c instanceof a.Matrix&&(c=c.toTransformString()),a._.rgTransform.test(c)||(c=a._.svgTransform2string(c)),e(r,c,function(){return s.getBBox(1)});if("d"==b||"path"==b)return d=a.path.toCubic(r,c),{from:k(d[0]),to:k(d[1]),f:j(d[0])};if("points"==b)return d=p(r).split(a._.separator),q=p(c).split(a._.separator),{from:d,to:q,f:function(a){return a}};var t=r.match(o),u=p(c).match(o);return t&&m(t,u)?{from:parseFloat(r),to:parseFloat(c),f:g(t)}:{from:this.asPX(b),to:this.asPX(b,c),f:f}})}),d.plugin(function(a,c,d,e){for(var f=c.prototype,g="hasOwnProperty",h=("createTouch"in e.doc),i=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","touchstart","touchmove","touchend","touchcancel"],j={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},k=(function(a,b){var c="y"==a?"scrollTop":"scrollLeft",d=b&&b.node?b.node.ownerDocument:e.doc;return d[c in d.documentElement?"documentElement":"body"][c]}),l=function(){return this.originalEvent.preventDefault()},m=function(){return this.originalEvent.stopPropagation()},n=function(a,b,c,d){var e=h&&j[b]?j[b]:b,f=function(e){var f=k("y",d),i=k("x",d);if(h&&j[g](b))for(var n=0,o=e.targetTouches&&e.targetTouches.length;o>n;n++)if(e.targetTouches[n].target==a||a.contains(e.targetTouches[n].target)){var p=e;e=e.targetTouches[n],e.originalEvent=p,e.preventDefault=l,e.stopPropagation=m;break}var q=e.clientX+i,r=e.clientY+f;return c.call(d,e,q,r)};return b!==e&&a.addEventListener(b,f,!1),a.addEventListener(e,f,!1),function(){return b!==e&&a.removeEventListener(b,f,!1),a.removeEventListener(e,f,!1),!0}},o=[],p=function(a){for(var c,d=a.clientX,e=a.clientY,f=k("y"),g=k("x"),i=o.length;i--;){if(c=o[i],h){for(var j,l=a.touches&&a.touches.length;l--;)if(j=a.touches[l],j.identifier==c.el._drag.id||c.el.node.contains(j.target)){d=j.clientX,e=j.clientY,(a.originalEvent?a.originalEvent:a).preventDefault();break}}else a.preventDefault();{var m=c.el.node;m.nextSibling,m.parentNode,m.style.display}d+=g,e+=f,b("snap.drag.move."+c.el.id,c.move_scope||c.el,d-c.el._drag.x,e-c.el._drag.y,d,e,a)}},q=function(c){a.unmousemove(p).unmouseup(q);for(var d,e=o.length;e--;)d=o[e],d.el._drag={},b("snap.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,c),b.off("snap.drag.*."+d.el.id);o=[]},r=i.length;r--;)!function(b){a[b]=f[b]=function(c,d){if(a.is(c,"function"))this.events=this.events||[],this.events.push({name:b,f:c,unbind:n(this.node||document,b,c,d||this)});else for(var e=0,f=this.events.length;f>e;e++)if(this.events[e].name==b)try{this.events[e].f.call(this)}catch(g){}return this},a["un"+b]=f["un"+b]=function(a){for(var c=this.events||[],d=c.length;d--;)if(c[d].name==b&&(c[d].f==a||!a))return c[d].unbind(),c.splice(d,1),!c.length&&delete this.events,this;return this}}(i[r]);f.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},f.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var s=[];f.drag=function(c,d,e,f,g,h){function i(i,j,l){(i.originalEvent||i).preventDefault(),k._drag.x=j,k._drag.y=l,k._drag.id=i.identifier,!o.length&&a.mousemove(p).mouseup(q),o.push({el:k,move_scope:f,start_scope:g,end_scope:h}),d&&b.on("snap.drag.start."+k.id,d),c&&b.on("snap.drag.move."+k.id,c),e&&b.on("snap.drag.end."+k.id,e),b("snap.drag.start."+k.id,g||f||k,j,l,i)}function j(a,c,d){b("snap.draginit."+k.id,k,a,c,d)}var k=this;if(!arguments.length){var l;return k.drag(function(a,b){this.attr({transform:l+(l?"T":"t")+[a,b]})},function(){l=this.transform().local})}return b.on("snap.draginit."+k.id,i),k._drag={},s.push({el:k,start:i,init:j}),k.mousedown(j),k},f.undrag=function(){for(var c=s.length;c--;)s[c].el==this&&(this.unmousedown(s[c].init),s.splice(c,1),b.unbind("snap.drag.*."+this.id),b.unbind("snap.draginit."+this.id));return!s.length&&a.unmousemove(p).unmouseup(q),this}}),d.plugin(function(a,c,d){var e=(c.prototype,d.prototype),f=/^\s*url\((.+)\)/,g=String,h=a._.$;a.filter={},e.filter=function(b){var d=this;"svg"!=d.type&&(d=d.paper);var e=a.parse(g(b)),f=a._.id(),i=(d.node.offsetWidth,d.node.offsetHeight,h("filter"));return h(i,{id:f,filterUnits:"userSpaceOnUse"}),i.appendChild(e.node),d.defs.appendChild(i),new c(i)},b.on("snap.util.getattr.filter",function(){b.stop();var c=h(this.node,"filter");if(c){var d=g(c).match(f);return d&&a.select(d[1])}}),b.on("snap.util.attr.filter",function(d){if(d instanceof c&&"filter"==d.type){b.stop();var e=d.node.id;e||(h(d.node,{id:d.id}),e=d.id),h(this.node,{filter:a.url(e)})}d&&"none"!=d||(b.stop(),this.node.removeAttribute("filter"))}),a.filter.blur=function(b,c){null==b&&(b=2);var d=null==c?b:[b,c];return a.format('',{def:d})},a.filter.blur.toString=function(){return this()},a.filter.shadow=function(b,c,d,e,f){return"string"==typeof d&&(e=d,f=e,d=4),"string"!=typeof e&&(f=e,e="#000"),e=e||"#000",null==d&&(d=4),null==f&&(f=1),null==b&&(b=0,c=2),null==c&&(c=b),e=a.color(e),a.format('',{color:e,dx:b,dy:c,blur:d,opacity:f})},a.filter.shadow.toString=function(){return this()},a.filter.grayscale=function(b){return null==b&&(b=1),a.format('',{a:.2126+.7874*(1-b),b:.7152-.7152*(1-b),c:.0722-.0722*(1-b),d:.2126-.2126*(1-b),e:.7152+.2848*(1-b),f:.0722-.0722*(1-b),g:.2126-.2126*(1-b),h:.0722+.9278*(1-b)})},a.filter.grayscale.toString=function(){return this()},a.filter.sepia=function(b){return null==b&&(b=1),a.format('',{a:.393+.607*(1-b),b:.769-.769*(1-b),c:.189-.189*(1-b),d:.349-.349*(1-b),e:.686+.314*(1-b),f:.168-.168*(1-b),g:.272-.272*(1-b),h:.534-.534*(1-b),i:.131+.869*(1-b)})},a.filter.sepia.toString=function(){return this()},a.filter.saturate=function(b){return null==b&&(b=1),a.format('',{amount:1-b})},a.filter.saturate.toString=function(){return this()},a.filter.hueRotate=function(b){return b=b||0,a.format('',{angle:b})},a.filter.hueRotate.toString=function(){return this()},a.filter.invert=function(b){return null==b&&(b=1),a.format('',{amount:b,amount2:1-b})},a.filter.invert.toString=function(){return this()},a.filter.brightness=function(b){return null==b&&(b=1),a.format('',{amount:b})},a.filter.brightness.toString=function(){return this()},a.filter.contrast=function(b){return null==b&&(b=1),a.format('',{amount:b,amount2:.5-b/2})},a.filter.contrast.toString=function(){return this()}}),d.plugin(function(a,b){var c=a._.box,d=a.is,e=/^[^a-z]*([tbmlrc])/i,f=function(){return"T"+this.dx+","+this.dy};b.prototype.getAlign=function(a,b){null==b&&d(a,"string")&&(b=a,a=null),a=a||this.paper;var g=a.getBBox?a.getBBox():c(a),h=this.getBBox(),i={};switch(b=b&&b.match(e),b=b?b[1].toLowerCase():"c"){case"t":i.dx=0,i.dy=g.y-h.y;break;case"b":i.dx=0,i.dy=g.y2-h.y2;break;case"m":i.dx=0,i.dy=g.cy-h.cy;break;case"l":i.dx=g.x-h.x,i.dy=0;break;case"r":i.dx=g.x2-h.x2,i.dy=0;break;default:i.dx=g.cx-h.cx,i.dy=0}return i.toString=f,i},b.prototype.align=function(a,b){return this.transform("..."+this.getAlign(a,b))}}),d}); \ No newline at end of file From 3627c335eee2e14b863474f41cbc29f247d92856 Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Tue, 27 Sep 2016 15:35:07 +0330 Subject: [PATCH 03/10] min --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ce02306..ae1af61 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ _release/* _documentation/* node_modules/* npm-debug.log -www/* \ No newline at end of file +www/* +.vscode/.browse.VC.db \ No newline at end of file From a9f7b1d2a604f42133aa56ef65c7cbaef28ce725 Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Tue, 11 Oct 2016 01:35:30 +0330 Subject: [PATCH 04/10] Going TypeScript 2 --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 42a51d8..b894b18 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,6 @@ "editor.wrappingColumn": 0, "editor.quickSuggestionsDelay": 0, "editor.insertSpaces": true, - "alex.enable": true + "alex.enable": true, + "typescript.check.workspaceVersion": false } \ No newline at end of file From 95f8b2b81715e069835bdf59ec5852015e1a3fd0 Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Tue, 11 Oct 2016 01:35:43 +0330 Subject: [PATCH 05/10] adding standard KF extensions toolset --- .vscode/extensions.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..0ccd107 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "karyfoundation.comment", + "karyfoundation.theme-karyfoundation-themes", + "karyfoundation.righteous", + "streetsidesoftware.code-spell-checker", + "ybaumes.highlight-trailing-white-spaces" + ] +} \ No newline at end of file From 999328c2204eb299120f1c468b5314c661acbc0c Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Fri, 25 Nov 2016 20:40:05 +0330 Subject: [PATCH 06/10] never is better... --- .vscode/tasks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4465ea3..cb58b93 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,6 +3,6 @@ "command": "bash", "isShellCommand": false, "args": [ "build.sh" ], - "showOutput": "silent", + "showOutput": "never", "echoCommand": false } \ No newline at end of file From 7419704a51a692ff85306d576b3864159adf9437 Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Fri, 25 Nov 2016 20:43:30 +0330 Subject: [PATCH 07/10] so much more code cleanups... --- core/api/api.ts | 30 +++++++++++---------------- core/api/layer.ts | 38 ++++++++++++++--------------------- core/api/stdlib/algorithms.ts | 3 +-- core/api/stdlib/graphs.ts | 24 ++++++++++------------ core/api/stdlib/sortings.ts | 30 +++++++++++++-------------- core/editor/graph.ts | 14 ++++++------- core/editor/snapobject.ts | 10 ++++----- core/objects/dot/dot.ts | 14 ++++--------- core/rendering/circular.ts | 2 +- core/script/algorithms.ts | 4 ++-- 10 files changed, 72 insertions(+), 97 deletions(-) diff --git a/core/api/api.ts b/core/api/api.ts index 0fde39b..382de27 100644 --- a/core/api/api.ts +++ b/core/api/api.ts @@ -45,9 +45,8 @@ */ function newdots ( howmuch: number ): Array { var dots = new Array( ); - for ( var counter = 0; counter < howmuch; counter++ ) { + for ( var counter = 0; counter < howmuch; counter++ ) dots.push( KaryGraph.API.AbstractionLayer.AddNewDot( ) ); - } return dots; } @@ -73,9 +72,8 @@ */ function getdots ( ids: Array ): Array { let result = new Array( ); - ids.forEach( id => { + for ( let id of ids ) result.push( getdot( id ) ); - }); return result; } @@ -88,9 +86,8 @@ */ function foreachdot ( dots: Array, f: ( dot: KaryGraph.Dot ) => void ) { - dots.forEach( dotOrId => { + for ( let dotOrId of dots ) f( KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dotOrId ) ); - }); } // @@ -358,15 +355,12 @@ */ function size ( dots?: Array ): number { var size: number = 0; - if ( dots === undefined ) { - for ( var i = 0; i < KaryGraph.Dot.TotalDots; i++ ) { + if ( dots === undefined ) + for ( var i = 0; i < KaryGraph.Dot.TotalDots; i++ ) size += getdot(i + 1).NumberOfInputs(); - } - } else { - for ( var i = 0; i < dots.length; i++ ) { + else + for ( var i = 0; i < dots.length; i++ ) size += KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dots[ i ] ).NumberOfInputs( ); - } - } return size; } @@ -439,7 +433,7 @@ // function range( start: number, end: number ): Array { - return Array.from( new Array( end - start + 1 ), ( x, i ) => i + start ) + return Array.from( new Array( end - start + 1 ), ( x, i ) => i + start ); } // @@ -499,7 +493,7 @@ // ─── RND ──────────────────────────────────────────────────────────────────────── // - function rnd( num: number ): number { + function rnd ( num: number ): number { return KaryGraph.Random( num ); } @@ -507,7 +501,7 @@ // ─── RADIAN ───────────────────────────────────────────────────────────────────── // - function radian( num: number ): number { + function radian ( num: number ): number { return num * ( Math.PI / 180 ); } @@ -515,7 +509,7 @@ // ─── SIN ──────────────────────────────────────────────────────────────────────── // - function sin( num: number ): number { + function sin ( num: number ): number { return Math.sin( radian( num ) ); } @@ -523,7 +517,7 @@ // ─── COS ──────────────────────────────────────────────────────────────────────── // - function cos( num: number ): number { + function cos ( num: number ): number { return Math.cos( radian( num ) ); } diff --git a/core/api/layer.ts b/core/api/layer.ts index 07ab045..46a04bf 100644 --- a/core/api/layer.ts +++ b/core/api/layer.ts @@ -40,11 +40,10 @@ namespace KaryGraph.API.AbstractionLayer { // export function GetDotByDotOrId ( dotOrId: DotObjectOrDotID ): Dot { - if ( typeof ( dotOrId ) === "number" ) { + if ( typeof ( dotOrId ) === "number" ) return GetDotByNumberId( dotOrId ); - } else { + else return dotOrId; - } } // @@ -65,15 +64,10 @@ namespace KaryGraph.API.AbstractionLayer { // export function GetDotByNumberId ( numberId: number ): Dot { - let dot: Dot; - let keys = Object.keys( Storage.Nodes ); - keys.forEach( key => { - if ( ( Storage.Nodes[ key ] ).GetNumberId( ) == numberId ) { - dot = Storage.Nodes[ key ]; - return; - } - }); - return dot; + for ( let key of Object.keys( Storage.Nodes ) ) + if ( ( Storage.Nodes[ key ] ).GetNumberId( ) === numberId ) + return Storage.Nodes[ key ]; + return null; } // @@ -82,12 +76,9 @@ namespace KaryGraph.API.AbstractionLayer { export function NumberOfOddVertices ( ): number { var verticesWithOddDegree: number = 0; - var keys = Object.keys( Storage.Nodes ); - keys.forEach( key => { - if ( ( Storage.Nodes[ key ] ).GetDegree() % 2 ) { + for ( let key of Object.keys( Storage.Nodes ) ) + if ( ( Storage.Nodes[ key ] ).GetDegree() % 2 ) verticesWithOddDegree++; - } - }); return verticesWithOddDegree; } @@ -96,9 +87,8 @@ namespace KaryGraph.API.AbstractionLayer { // export function Reset ( ) { - Object.keys( Storage.Nodes ).forEach( - key => ( Storage.Nodes[ key ] ).Remove( ) - ); + for ( let key of Object.keys( Storage.Nodes ) ) + ( Storage.Nodes[ key ] ).Remove( ); Dot.ResetNumberIdPlace( ); } @@ -130,16 +120,18 @@ namespace KaryGraph.API.AbstractionLayer { export function Render ( text: string ) { switch ( text ) { case 'circle': - Rendering.RenderCircular(); + Rendering.RenderCircular( ); break; + case 'spiral': - Rendering.RenderSpiral(); + Rendering.RenderSpiral( ); break; + case 'order': KaryGraph.API.StandardLibrary.Sortings.Tree( ); break; + default: - // UI.Console.PrintError(`Graph API: No rendering option ${text}`); break; } } diff --git a/core/api/stdlib/algorithms.ts b/core/api/stdlib/algorithms.ts index 578e62b..1232a37 100644 --- a/core/api/stdlib/algorithms.ts +++ b/core/api/stdlib/algorithms.ts @@ -8,7 +8,7 @@ namespace KaryGraph.API.StandardLibrary.Algorithms { // // ─── BFS ──────────────────────────────────────────────────────────────────────── - // + // export function BFS ( start: Dot, steps: number ) { let bfs = new Array>( ); @@ -29,7 +29,6 @@ namespace KaryGraph.API.StandardLibrary.Algorithms { let neighbors = bfs[ bfs.length-1 ][ it ].GetNeighbors( ); for ( let nit = 0; nit < neighbors.length; nit++ ) { - if ( checked[ neighbors[ nit ].GetNumberId( ) ] == false ) { checked[ neighbors[ nit ].GetNumberId( ) ] = true; tmp.push( neighbors[ nit ] ); diff --git a/core/api/stdlib/graphs.ts b/core/api/stdlib/graphs.ts index 2530fb0..dfd419c 100644 --- a/core/api/stdlib/graphs.ts +++ b/core/api/stdlib/graphs.ts @@ -6,14 +6,6 @@ namespace KaryGraph.API.StandardLibrary { - // - // ─── RND ──────────────────────────────────────────────────────────────────────── - // - - export function RND ( ) { - - } - // // ─── COMPLETE GRAPH ───────────────────────────────────────────────────────────── // @@ -22,21 +14,27 @@ namespace KaryGraph.API.StandardLibrary { let keys = new Array( ); // creating dots - for ( var counter = 0; counter < size; counter++ ) { + for ( var counter = 0; counter < size; counter++ ) keys.push( API.AbstractionLayer.AddNewDot( ).Id ); - } // connecting keys.forEach( key => { for ( var index = 0; index < size; index++ ) { var currentDot = keys[ index ]; - if ( currentDot !== key ) { - Storage.Nodes[ key ].ConnectTo( Storage.Nodes[ keys[ index] ] ); - } + if ( currentDot !== key ) + Storage.Nodes[ key ].ConnectTo( Storage.Nodes[ keys[ index ] ] ); } }); } + // + // ─── RND ──────────────────────────────────────────────────────────────────────── + // + + export function RND ( ) { + + } + // ──────────────────────────────────────────────────────────────────────────────── } \ No newline at end of file diff --git a/core/api/stdlib/sortings.ts b/core/api/stdlib/sortings.ts index 931a189..bcb5ddb 100644 --- a/core/api/stdlib/sortings.ts +++ b/core/api/stdlib/sortings.ts @@ -18,22 +18,21 @@ namespace KaryGraph.API.StandardLibrary.Sortings { var layers: number = 0; // create tree map - keys.forEach( key => { + for ( let key of keys ) { var dot = Storage.Nodes[ key ]; if ( dot.NumberOfInputs( ) == 0 ) { var children = dot.GetChildren( ); - if ( children == -1 ) return false; - map.set( dot, children ); - } - }); + if ( children == -1 ) + return false; + map.set( dot, children ); } } // count layers function dive ( map: any, layer: number ) { - if (layer > layers) layers = layer; - for ( var [ key, value ] of map.entries() ) { - if ( value.size > 0 ) dive( value, layer + 1 ); - } - } + if ( layer > layers ) layers = layer; + for ( var [ key, value ] of map.entries( ) ) + if ( value.size > 0 ) + dive( value, layer + 1 ) } + dive( map, 1 ); // move dots @@ -41,18 +40,17 @@ namespace KaryGraph.API.StandardLibrary.Sortings { var count = map.size; var x = 0; for ( var [ key, value ] of map.entries( ) ) { - var positionX = width / count * x++ + (width / count / 2) + xOff; + var positionX = width / count * x++ + ( width / count / 2 ) + xOff; + ( key ).MoveTo( positionX, positionY ); + if ( value.size > 0 ) { moveDots( value, width / count, positionY + GraphHeight / layers, - positionX - (width / count / 2 ) - ); - } - } - } + positionX - ( width / count / 2 ) + )}}} moveDots( map, diff --git a/core/editor/graph.ts b/core/editor/graph.ts index 8adc61b..7437b89 100644 --- a/core/editor/graph.ts +++ b/core/editor/graph.ts @@ -32,15 +32,17 @@ namespace KaryGraph { } function ComputeRealXCoordinate ( unit: number ) { - return ( GraphWidth / ( widthUnit + 3 * paddingLeftRight ) ) * ( unit + 2 * paddingLeftRight ); + return ( GraphWidth / + ( widthUnit + 3 * paddingLeftRight ) ) * ( unit + 2 * paddingLeftRight ); } function ComputeRealYCoordinate ( unit: number ) { - return ( GraphHeight / ( heightUnit + 2 * paddingTopBottom ) ) * ( unit + paddingTopBottom ); + return ( GraphHeight / + ( heightUnit + 2 * paddingTopBottom ) ) * ( unit + paddingTopBottom ); } // body - var counter = 0; + var counter = 0 while ( counter < howManyNodes ) { var x: number = Random( widthUnit ); var y: number = Random( heightUnit ); @@ -74,9 +76,8 @@ namespace KaryGraph { for ( var index = 0; index < howManyTimes; ) { var d1 = GetRandomDot( ); var d2 = GetRandomDot( ); - if ( d1.ConnectTo( d2 ) ) { + if ( d1.ConnectTo( d2 ) ) index++; - } } } @@ -87,9 +88,8 @@ namespace KaryGraph { /** Populates the places array */ function CreatePlacesArray ( w: number, h: number ): Array { var result = new Array ( ); - for ( var counter = 0; counter < ( w * h ); counter++ ) { + for ( var counter = 0; counter < ( w * h ); counter++ ) result.push( 0 ); - } return result; } diff --git a/core/editor/snapobject.ts b/core/editor/snapobject.ts index 4a1a802..40d25e2 100644 --- a/core/editor/snapobject.ts +++ b/core/editor/snapobject.ts @@ -19,7 +19,7 @@ namespace KaryGraph { // interface ISnapAnimateFunction { - ( attributes: any , duration: number ) + ( attributes: any , duration: number ); } // @@ -27,7 +27,7 @@ namespace KaryGraph { // interface ISnapDataFunction { - ( string, any? ) + ( string, any? ); } // @@ -35,7 +35,7 @@ namespace KaryGraph { // interface ISnapDragMoveFunction { - ( dx: number, dy: number, posx: number, posy: number ) + ( dx: number, dy: number, posx: number, posy: number ); } // @@ -43,7 +43,7 @@ namespace KaryGraph { // interface ISnapDragFunction { - ( move: ISnapDragMoveFunction, start: Function, stop: Function ) + ( move: ISnapDragMoveFunction, start: Function, stop: Function ); } // @@ -59,7 +59,7 @@ namespace KaryGraph { animate: ISnapAnimateFunction; /** removes the object */ - remove(); + remove( ): void; /** drag implementation */ drag: ISnapDragFunction; diff --git a/core/objects/dot/dot.ts b/core/objects/dot/dot.ts index cb658cb..80f0e00 100644 --- a/core/objects/dot/dot.ts +++ b/core/objects/dot/dot.ts @@ -263,24 +263,18 @@ namespace KaryGraph { if ( !ids ) var ids: number[ ] = [ ]; var map = new Map(); var keys = Object.keys( Storage.Nodes ); - - keys.forEach( key => { + for ( let key of keys ) { var dot = Storage.Nodes[ key ]; if ( this.Outputs[ dot.Id ] != undefined ) { - if ( ids.indexOf( dot.GetNumberId( ) ) != -1 ) { + if ( ids.indexOf( dot.GetNumberId( ) ) != -1 ) return -1 - } - ids.push( dot.GetNumberId( ) ); var children = dot.GetChildren( ids ); - - if ( children == -1 ) { + if ( children == -1 ) return -1 - } - map.set( dot, children ); } - }); + }; return map; } diff --git a/core/rendering/circular.ts b/core/rendering/circular.ts index 84abe1d..d8a6be5 100644 --- a/core/rendering/circular.ts +++ b/core/rendering/circular.ts @@ -34,7 +34,7 @@ namespace KaryGraph.Rendering { for ( var i = 0; i < count; i++ ) { let x = computeX( i ); let y = computeY( i ); - ( Storage.Nodes[ keys[ i ] ] ).MoveTo( x, y ); + ( Storage.Nodes[ keys[ i ] ] as Dot ).MoveTo( x, y ); } } diff --git a/core/script/algorithms.ts b/core/script/algorithms.ts index 9940f89..f6cdbfb 100644 --- a/core/script/algorithms.ts +++ b/core/script/algorithms.ts @@ -32,7 +32,7 @@ namespace KaryGraph.ScriptEngine.Algorithms { // export function ExecuteAlgorithm ( handle: string ) { - let algorithm = Storage.Algorithms[ handle ]; + let algorithm = Storage.Algorithms[ handle ] as IAlgorithm; algorithm({ }); } @@ -113,7 +113,7 @@ namespace KaryGraph.ScriptEngine.Algorithms { function LoadAlgorithmsManifest ( algorithmsAddress: string ) { let manifestPath = JoinPath([ algorithmsAddress, AlgorithmsPackageName ]); if ( FSExistsSync( manifestPath ) ) { - return JSON.parse( ReadFileSync( manifestPath ) ); + return JSON.parse( ReadFileSync( manifestPath ) ) as IAlgorithmManifest; } else { return undefined; } From e827b8253f6ba239c792707e804e82ddf7a805ce Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Fri, 25 Nov 2016 20:43:55 +0330 Subject: [PATCH 08/10] icon changes... --- designs/icon/icon.sketch | Bin 442368 -> 425984 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/designs/icon/icon.sketch b/designs/icon/icon.sketch index 0c2cbd21fd369a9a0dde0d542f7420246fc25a1f..2390e064d965b2ee540a6540b77440a0433f15f8 100644 GIT binary patch delta 87109 zcmb@P2YeL8|M$1v?OyMf>p4IfAD|Ei{vtyncbbwy!SIZZ3V$02=;{@vihPDjWo}4 z)C56Lo?(P%muJ}a5l`LTEYBKa7mv)n-QCCa-1&ublGE%s=9sX3L{EozLfwDagoIi( zt9d8XYpdxpb84KNaZK-=k(-%5%6q16jQ4)Mf#nl2YE@6KS-XF~>hb9VYE|!Ft$IdA ztpV|w)vDL3m0lA9Gcx+sj<1p7ol?IlJN4!?g79vuZ>Fj0ao*4CS0lT4pViM$wPP6@ zipk0xK5*0^udZPdJN0fI8ANQ*S=N?jtSH8-Y}i;Srkx%m�vgOKqD|fAwSFS?Eo^8@n2WN~LoIN}} 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 From 865016e762746c371c8b6dae60e4e09a2669f0c5 Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Fri, 25 Nov 2016 20:44:07 +0330 Subject: [PATCH 09/10] dec files... --- core/typings/electron-prebuilt-tests.ts | 7 + core/typings/electron-prebuilt.d.ts | 10 + core/typings/github-electron-main-tests.ts | 948 +++ .../typings/github-electron-renderer-tests.ts | 258 + core/typings/github-electron.d.ts | 5250 +++++++++++++++++ 5 files changed, 6473 insertions(+) create mode 100644 core/typings/electron-prebuilt-tests.ts create mode 100644 core/typings/electron-prebuilt.d.ts create mode 100644 core/typings/github-electron-main-tests.ts create mode 100644 core/typings/github-electron-renderer-tests.ts create mode 100644 core/typings/github-electron.d.ts diff --git a/core/typings/electron-prebuilt-tests.ts b/core/typings/electron-prebuilt-tests.ts new file mode 100644 index 0000000..771fd06 --- /dev/null +++ b/core/typings/electron-prebuilt-tests.ts @@ -0,0 +1,7 @@ +/// +/// + +import electron = require('electron-prebuilt'); +import child_process = require('child_process'); + +child_process.spawn(electron); diff --git a/core/typings/electron-prebuilt.d.ts b/core/typings/electron-prebuilt.d.ts new file mode 100644 index 0000000..d6b6dff --- /dev/null +++ b/core/typings/electron-prebuilt.d.ts @@ -0,0 +1,10 @@ +// Type definitions for electron-prebuilt 0.30.1 +// Project: https://github.com/mafintosh/electron-prebuilt +// Definitions by: rhysd +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare module 'electron-prebuilt' { + var electron: string; + export = electron; +} + diff --git a/core/typings/github-electron-main-tests.ts b/core/typings/github-electron-main-tests.ts new file mode 100644 index 0000000..c8d0c8d --- /dev/null +++ b/core/typings/github-electron-main-tests.ts @@ -0,0 +1,948 @@ +/// +import { + app, + autoUpdater, + BrowserWindow, + contentTracing, + dialog, + globalShortcut, + ipcMain, + Menu, + MenuItem, + powerMonitor, + powerSaveBlocker, + protocol, + Tray, + clipboard, + crashReporter, + nativeImage, + screen, + shell, + session, + systemPreferences, + webContents +} from 'electron'; + +import * as path from 'path'; + +// Quick start +// https://github.com/atom/electron/blob/master/docs/tutorial/quick-start.md + +// Keep a global reference of the window object, if you don't, the window will +// be closed automatically when the javascript object is GCed. +var mainWindow: Electron.BrowserWindow = null; + +// Quit when all windows are closed. +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') + app.quit(); +}); + +// Check single instance app +var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) { + // Someone tried to run a second instance, we should focus our window + if (mainWindow) { + if (mainWindow.isMinimized()) mainWindow.restore(); + mainWindow.focus(); + } +}); + +if (shouldQuit) { + app.quit(); + process.exit(0); +} + +// This method will be called when Electron has done everything +// initialization and ready for creating browser windows. +app.on('ready', () => { + // Create the browser window. + mainWindow = new BrowserWindow({ width: 800, height: 600 }); + + // and load the index.html of the app. + mainWindow.loadURL(`file://${__dirname}/index.html`); + mainWindow.loadURL('file://foo/bar', {userAgent: 'cool-agent', httpReferrer: 'greateRefferer'}); + mainWindow.webContents.loadURL('file://foo/bar', {userAgent: 'cool-agent', httpReferrer: 'greateRefferer'}); + + mainWindow.webContents.openDevTools(); + mainWindow.webContents.toggleDevTools(); + mainWindow.webContents.openDevTools({mode: 'detach'}); + mainWindow.webContents.closeDevTools(); + mainWindow.webContents.addWorkSpace('/path/to/workspace'); + mainWindow.webContents.removeWorkSpace('/path/to/workspace'); + var opened: boolean = mainWindow.webContents.isDevToolsOpened() + var focused = mainWindow.webContents.isDevToolsFocused(); + // Emitted when the window is closed. + mainWindow.on('closed', () => { + // Dereference the window object, usually you would store windows + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + mainWindow = null; + }); + + mainWindow.webContents.print({silent: true, printBackground: false}); + mainWindow.webContents.print(); + + mainWindow.webContents.printToPDF({ + marginsType: 1, + pageSize: 'A3', + printBackground: true, + printSelectionOnly: true, + landscape: true, + }, (error: Error, data: Buffer) => {}); + + mainWindow.webContents.printToPDF({}, (err, data) => {}); + + mainWindow.webContents.executeJavaScript('return true;'); + mainWindow.webContents.executeJavaScript('return true;', true); + mainWindow.webContents.executeJavaScript('return true;', true, (result: boolean) => console.log(result)); + mainWindow.webContents.insertText('blah, blah, blah'); + mainWindow.webContents.startDrag({file: '/path/to/img.png', icon: nativeImage.createFromPath('/path/to/icon.png')}); + mainWindow.webContents.findInPage('blah'); + mainWindow.webContents.findInPage('blah', { + forward: true, + matchCase: false, + }); + mainWindow.webContents.stopFindInPage('clearSelection'); + mainWindow.webContents.stopFindInPage('keepSelection'); + mainWindow.webContents.stopFindInPage('activateSelection'); + + mainWindow.loadURL('https://github.com'); + + mainWindow.webContents.on('did-finish-load', function() { + mainWindow.webContents.savePage('/tmp/test.html', 'HTMLComplete', function(error) { + if (!error) + console.log("Save page successfully"); + }); + }); + + try { + mainWindow.webContents.debugger.attach("1.1"); + } catch(err) { + console.log("Debugger attach failed : ", err); + }; + + mainWindow.webContents.debugger.on('detach', function(event, reason) { + console.log("Debugger detached due to : ", reason); + }); + + mainWindow.webContents.debugger.on('message', function(event, method, params) { + if (method == "Network.requestWillBeSent") { + if (params.request.url == "https://www.github.com") { + win.webContents.debugger.detach(); + } + } + }); + + mainWindow.webContents.debugger.sendCommand("Network.enable"); + mainWindow.webContents.capturePage(image => { + console.log(image.toDataURL()); + }); + mainWindow.webContents.capturePage({width: 100, height: 200}, image => { + console.log(image.toPNG()); + }); +}); + +app.commandLine.appendSwitch('enable-web-bluetooth'); + +app.on('ready', () => { + mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => { + event.preventDefault(); + + let result = (() => { + for (let device of deviceList) { + if (device.deviceName === 'test') { + return device; + } + } + return null; + })(); + + if (!result) { + callback(''); + } else { + callback(result.deviceId); + } + }); +}); + +// Locale +app.getLocale(); + +// Desktop environment integration +// https://github.com/atom/electron/blob/master/docs/tutorial/desktop-environment-integration.md + +app.addRecentDocument('/Users/USERNAME/Desktop/work.type'); +app.clearRecentDocuments(); +var dockMenu = Menu.buildFromTemplate([ + { + label: 'New Window', + click: () => { + console.log('New Window'); + } + }, + { + label: 'New Window with Settings', + submenu: [ + { label: 'Basic' }, + { label: 'Pro' } + ] + }, + { label: 'New Command...' }, + { + label: 'Edit', + submenu: [ + { + label: 'Undo', + accelerator: 'CmdOrCtrl+Z', + role: 'undo' + }, + { + label: 'Redo', + accelerator: 'Shift+CmdOrCtrl+Z', + role: 'redo' + }, + { + type: 'separator' + }, + { + label: 'Cut', + accelerator: 'CmdOrCtrl+X', + role: 'cut' + }, + { + label: 'Copy', + accelerator: 'CmdOrCtrl+C', + role: 'copy' + }, + { + label: 'Paste', + accelerator: 'CmdOrCtrl+V', + role: 'paste' + }, + ] + }, +]); +app.dock.setMenu(dockMenu); +app.dock.setBadge('foo'); +var id = app.dock.bounce('informational'); +app.dock.cancelBounce(id); +app.dock.setIcon('/path/to/icon.png'); +app.dock.setBadgeCount(app.dock.getBadgeCount() + 1); + +app.setUserTasks([ + { + program: process.execPath, + arguments: '--new-window', + iconPath: process.execPath, + iconIndex: 0, + title: 'New Window', + description: 'Create a new window' + } +]); +app.setUserTasks([]); +if (app.isUnityRunning()) { +} +if (app.isAccessibilitySupportEnabled()) { +} +app.setLoginItemSettings({openAtLogin: true, openAsHidden: false}); +console.log(app.getLoginItemSettings().wasOpenedAtLogin); + +var window = new BrowserWindow(); +window.setProgressBar(0.5); +window.setRepresentedFilename('/etc/passwd'); +window.setDocumentEdited(true); + +// Online/Offline Event Detection +// https://github.com/atom/electron/blob/master/docs/tutorial/online-offline-events.md + +var onlineStatusWindow: Electron.BrowserWindow; + +app.on('ready', () => { + onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); + onlineStatusWindow.loadURL(`file://${__dirname}/online-status.html`); +}); +app.on('accessibility-support-changed', (_, enabled) => console.log('accessibility: ' + enabled)); + +ipcMain.on('online-status-changed', (event: any, status: any) => { + console.log(status); +}); + +// Synopsis +// https://github.com/atom/electron/blob/master/docs/api/synopsis.md + +app.on('ready', () => { + window = new BrowserWindow({ + width: 800, + height: 600, + titleBarStyle: 'hidden-inset', + }); + window.loadURL('https://github.com'); +}); + +// Supported Chrome command line switches +// https://github.com/atom/electron/blob/master/docs/api/chrome-command-line-switches.md + +app.commandLine.appendSwitch('remote-debugging-port', '8315'); +app.commandLine.appendSwitch('host-rules', 'MAP * 127.0.0.1'); +app.commandLine.appendSwitch('vmodule', 'console=0'); + +// systemPreferences +// https://github.com/electron/electron/blob/master/docs/api/system-preferences.md + +var browserOptions = { + width: 1000, + height: 800, + transparent: false, + frame: true +}; + +// Make the window transparent only if the platform supports it. +if (process.platform !== 'win32' || systemPreferences.isAeroGlassEnabled()) { + browserOptions.transparent = true; + browserOptions.frame = false; +} + +// Create the window. +var win = new BrowserWindow(browserOptions); + +// Navigate. +if (browserOptions.transparent) { + win.loadURL('file://' + __dirname + '/index.html'); +} else { + // No transparency, so we load a fallback that uses basic styles. + win.loadURL('file://' + __dirname + '/fallback.html'); +} + +// app +// https://github.com/atom/electron/blob/master/docs/api/app.md + +app.on('certificate-error', function(event, webContents, url, error, certificate, callback) { + if (url == "https://github.com") { + // Verification logic. + event.preventDefault(); + callback(true); + } else { + callback(false); + } +}); + +app.on('select-client-certificate', function(event, webContents, url, list, callback) { + event.preventDefault(); + callback(list[0]); +}); + +app.on('login', function(event, webContents, request, authInfo, callback) { + event.preventDefault(); + callback('username', 'secret'); +}); + +var win = new BrowserWindow({show: false}) +win.once('ready-to-show', () => { + win.show(); +}); + +app.relaunch({args: process.argv.slice(1).concat(['--relaunch'])}); +app.exit(0); + +// auto-updater +// https://github.com/atom/electron/blob/master/docs/api/auto-updater.md + +autoUpdater.setFeedURL('http://mycompany.com/myapp/latest?version=' + app.getVersion()); +autoUpdater.checkForUpdates(); +autoUpdater.quitAndInstall(); + +autoUpdater.on('error', (error) => { + console.log('error', error); +}); + +autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName, releaseDate, updateURL) => { + console.log('update-downloaded', releaseNotes, releaseName, releaseDate, updateURL); +}); + +// browser-window +// https://github.com/atom/electron/blob/master/docs/api/browser-window.md + +var win = new BrowserWindow({ width: 800, height: 600, show: false }); +win.on('closed', () => { + win = null; +}); + +win.loadURL('https://github.com'); +win.show(); + +var toolbarRect = document.getElementById('toolbar').getBoundingClientRect(); +win.setSheetOffset(toolbarRect.height); + +var installed = BrowserWindow.getDevToolsExtensions().hasOwnProperty('devtron'); + +// content-tracing +// https://github.com/atom/electron/blob/master/docs/api/content-tracing.md + +const options = { + categoryFilter: '*', + traceOptions: 'record-until-full,enable-sampling' +} + +contentTracing.startRecording(options, function() { + console.log('Tracing started'); + setTimeout(function() { + contentTracing.stopRecording('', function(path) { + console.log('Tracing data recorded to ' + path); + }); + }, 5000); +}); + +// dialog +// https://github.com/atom/electron/blob/master/docs/api/dialog.md + +// variant without browserWindow +var openDialogResult: string[] = dialog.showOpenDialog({ + title: 'Testing showOpenDialog', + defaultPath: '/var/log/syslog', + filters: [{name: '', extensions: ['']}], + properties: ['openFile', 'openDirectory', 'multiSelections'] +}); + +// variant with browserWindow +openDialogResult = dialog.showOpenDialog(win, { + title: 'Testing showOpenDialog', + defaultPath: '/var/log/syslog', + filters: [{name: '', extensions: ['']}], + properties: ['openFile', 'openDirectory', 'multiSelections'] +}); + +// global-shortcut +// https://github.com/atom/electron/blob/master/docs/api/global-shortcut.md + +// Register a 'ctrl+x' shortcut listener. +var ret = globalShortcut.register('ctrl+x', () => { + console.log('ctrl+x is pressed'); +}); +if (!ret) + console.log('registerion fails'); + +// Check whether a shortcut is registered. +console.log(globalShortcut.isRegistered('ctrl+x')); + +// Unregister a shortcut. +globalShortcut.unregister('ctrl+x'); + +// Unregister all shortcuts. +globalShortcut.unregisterAll(); + +// ipcMain +// https://github.com/atom/electron/blob/master/docs/api/ipc-main-process.md + +ipcMain.on('asynchronous-message', (event: Electron.IpcMainEvent, arg: any) => { + console.log(arg); // prints "ping" + event.sender.send('asynchronous-reply', 'pong'); +}); + +ipcMain.on('synchronous-message', (event: Electron.IpcMainEvent, arg: any) => { + console.log(arg); // prints "ping" + event.returnValue = 'pong'; +}); + +var winWindows = new BrowserWindow({ + width: 800, + height: 600, + show: false, + thickFrame: false, + type: 'toolbar', +}); + +// menu-item +// https://github.com/atom/electron/blob/master/docs/api/menu-item.md + +var menuItem = new MenuItem({}); + +menuItem.label = 'Hello World!'; +menuItem.click = (menuItem, browserWindow) => { + console.log('click', menuItem, browserWindow); +}; + +// menu +// https://github.com/atom/electron/blob/master/docs/api/menu.md + +var menu = new Menu(); +menu.append(new MenuItem({ label: 'MenuItem1', click: () => { console.log('item 1 clicked'); } })); +menu.append(new MenuItem({ type: 'separator' })); +menu.append(new MenuItem({ label: 'MenuItem2', type: 'checkbox', checked: true })); +menu.insert(0, menuItem); + +console.log(menu.items); + +var pos = screen.getCursorScreenPoint(); +menu.popup(null, pos.x, pos.y); + +// main.js +var template = [ + { + label: 'Electron', + submenu: [ + { + label: 'About Electron', + role: 'about' + }, + { + type: 'separator' + }, + { + label: 'Services', + role: 'services', + submenu: [] + }, + { + type: 'separator' + }, + { + label: 'Hide Electron', + accelerator: 'Command+H', + role: 'hide' + }, + { + label: 'Hide Others', + accelerator: 'Command+Shift+H', + role: 'hideothers' + }, + { + label: 'Show All', + role: 'unhide' + }, + { + type: 'separator' + }, + { + label: 'Quit', + accelerator: 'Command+Q', + click: () => { app.quit(); } + } + ] + }, + { + label: 'Edit', + submenu: [ + { + label: 'Undo', + accelerator: 'Command+Z', + role: 'undo' + }, + { + label: 'Redo', + accelerator: 'Shift+Command+Z', + role: 'redo' + }, + { + type: 'separator' + }, + { + label: 'Cut', + accelerator: 'Command+X', + role: 'cut' + }, + { + label: 'Copy', + accelerator: 'Command+C', + role: 'copy' + }, + { + label: 'Paste', + accelerator: 'Command+V', + role: 'paste' + }, + { + label: 'Select All', + accelerator: 'Command+A', + role: 'selectall' + } + ] + }, + { + label: 'View', + submenu: [ + { + label: 'Reload', + accelerator: 'Command+R', + click: (item, focusedWindow) => { + if (focusedWindow) { + focusedWindow.webContents.reloadIgnoringCache(); + } + } + }, + { + label: 'Toggle DevTools', + accelerator: 'Alt+Command+I', + click: (item, focusedWindow) => { + if (focusedWindow) { + focusedWindow.webContents.toggleDevTools(); + } + } + } + ] + }, + { + label: 'Window', + submenu: [ + { + label: 'Minimize', + accelerator: 'Command+M', + role: 'minimize' + }, + { + label: 'Close', + accelerator: 'Command+W', + role: 'close' + }, + { + type: 'separator' + }, + { + label: 'Bring All to Front', + role: 'front' + } + ] + }, + { + label: 'Help', + submenu: [] + } +]; + +menu = Menu.buildFromTemplate(template); + +Menu.setApplicationMenu(menu); // Must be called within app.on('ready', function(){ ... }); + +Menu.buildFromTemplate([ + { label: '4', id: '4' }, + { label: '5', id: '5' }, + { label: '1', id: '1', position: 'before=4' }, + { label: '2', id: '2' }, + { label: '3', id: '3' } +]); + +Menu.buildFromTemplate([ + { label: 'a', position: 'endof=letters' }, + { label: '1', position: 'endof=numbers' }, + { label: 'b', position: 'endof=letters' }, + { label: '2', position: 'endof=numbers' }, + { label: 'c', position: 'endof=letters' }, + { label: '3', position: 'endof=numbers' } +]); + +// power-monitor +// https://github.com/atom/electron/blob/master/docs/api/power-monitor.md + +app.on('ready', () => { + powerMonitor.on('suspend', () => { + console.log('The system is going to sleep'); + }); + powerMonitor.on('resume', () => { + console.log('The system has resumed from sleep'); + }); + powerMonitor.on('on-ac', () => { + console.log('The system changed to AC power') + }); + powerMonitor.on('on-battery', () => { + console.log('The system changed to battery power'); + }); +}); + +// power-save-blocker +// https://github.com/atom/electron/blob/master/docs/api/power-save-blocker.md + +var id = powerSaveBlocker.start('prevent-display-sleep'); +console.log(powerSaveBlocker.isStarted(id)); + +powerSaveBlocker.stop(id); + +// protocol +// https://github.com/atom/electron/blob/master/docs/api/protocol.md + +app.on('ready', () => { + protocol.registerStandardSchemes(['https']); + protocol.registerServiceWorkerSchemes(['https']); + + protocol.registerFileProtocol('atom', (request, callback) => { + callback(`${__dirname}/${request.url}`); + }); + + protocol.registerBufferProtocol('atom', (request, callback) => { + callback({mimeType: 'text/html', data: new Buffer('
Response
')}); + }); + + protocol.registerStringProtocol('atom', (request, callback) => { + callback('Hello World!'); + }); + + protocol.registerHttpProtocol('atom', (request, callback) => { + callback({url: request.url, method: request.method}); + }); + + protocol.unregisterProtocol('atom', (error) => { + console.log(error ? error.message : 'ok'); + }); + + protocol.isProtocolHandled('atom', (handled) => { + console.log(handled); + }); +}); + +// tray +// https://github.com/atom/electron/blob/master/docs/api/tray.md + +var appIcon: Electron.Tray = null; +app.on('ready', () => { + appIcon = new Tray('/path/to/my/icon'); + var contextMenu = Menu.buildFromTemplate([ + { label: 'Item1', type: 'radio' }, + { label: 'Item2', type: 'radio' }, + { label: 'Item3', type: 'radio', checked: true }, + { label: 'Item4', type: 'radio' }, + ]); + appIcon.setToolTip('This is my application.'); + appIcon.setContextMenu(contextMenu); + appIcon.setImage('/path/to/new/icon'); + appIcon.popUpContextMenu(contextMenu, {x: 100, y: 100}); + + appIcon.on('click', (event, bounds) => { + console.log('click', event, bounds); + }); + + appIcon.on('ballon-show', () => { + console.log('ballon-show'); + }); + + appIcon.displayBalloon({ + title: 'Hello World!' + }); +}); + +// clipboard +// https://github.com/atom/electron/blob/master/docs/api/clipboard.md + +clipboard.writeText('Example String'); +clipboard.writeText('Example String', 'selection'); +clipboard.writeBookmark('foo', 'http://example.com'); +clipboard.writeBookmark('foo', 'http://example.com', 'selection'); +console.log(clipboard.readText('selection')); +console.log(clipboard.availableFormats()); +console.log(clipboard.readBookmark().title); +clipboard.clear(); + +clipboard.write({ + html: '', + text: 'Hello World!', + image: clipboard.readImage() +}); + +// crash-reporter +// https://github.com/atom/electron/blob/master/docs/api/crash-reporter.md + +crashReporter.start({ + productName: 'YourName', + companyName: 'YourCompany', + submitURL: 'https://your-domain.com/url-to-submit', + autoSubmit: true, + extra: { + someKey: "value" + } +}); + +console.log(crashReporter.getLastCrashReport()); +console.log(crashReporter.getUploadedReports()); + +// nativeImage +// https://github.com/atom/electron/blob/master/docs/api/native-image.md + +var appIcon2 = new Tray('/Users/somebody/images/icon.png'); +var window2 = new BrowserWindow({ icon: '/Users/somebody/images/window.png' }); +var image = clipboard.readImage(); +var appIcon3 = new Tray(image); +var appIcon4 = new Tray('/Users/somebody/images/icon.png'); + +let image2 = nativeImage.createFromPath('/Users/somebody/images/icon.png'); + +// process +// https://github.com/electron/electron/blob/master/docs/api/process.md + +console.log(process.versions.electron); +console.log(process.versions.chrome); +console.log(process.type); +console.log(process.resourcesPath); +console.log(process.mas); +console.log(process.windowsStore); +process.noAsar = true; +process.crash(); +process.hang(); +process.setFdLimit(8192); + +// screen +// https://github.com/atom/electron/blob/master/docs/api/screen.md + +app.on('ready', () => { + var size = screen.getPrimaryDisplay().workAreaSize; + mainWindow = new BrowserWindow({ width: size.width, height: size.height }); +}); + +app.on('ready', () => { + var displays = screen.getAllDisplays(); + var externalDisplay: any = null; + for (var i in displays) { + if (displays[i].bounds.x > 0 || displays[i].bounds.y > 0) { + externalDisplay = displays[i]; + break; + } + } + + if (externalDisplay) { + mainWindow = new BrowserWindow({ + x: externalDisplay.bounds.x + 50, + y: externalDisplay.bounds.y + 50, + }); + } + + screen.on('display-added', (event, display) => { + console.log('display-added', display); + }); + + screen.on('display-removed', (event, display) => { + console.log('display-removed', display); + }); + + screen.on('display-metrics-changed', (event, display, changes) => { + console.log('display-metrics-changed', display, changes); + }); +}); + +// shell +// https://github.com/atom/electron/blob/master/docs/api/shell.md + +shell.showItemInFolder('/home/user/Desktop/test.txt'); +shell.openItem('/home/user/Desktop/test.txt'); +shell.moveItemToTrash('/home/user/Desktop/test.txt'); + +shell.openExternal('https://github.com', { + activate: false +}); + +shell.beep(); + +// session +// https://github.com/atom/electron/blob/master/docs/api/session.md + +session.defaultSession.on('will-download', (event, item, webContents) => { + event.preventDefault(); + require('request')(item.getURL(), (data: any) => { + require('fs').writeFileSync('/somewhere', data); + }); +}); + +// Query all cookies. +session.defaultSession.cookies.get({}, (error, cookies) => { + console.log(cookies); +}); + +// Query all cookies associated with a specific url. +session.defaultSession.cookies.get({ url : "http://www.github.com" }, (error, cookies) => { + console.log(cookies); +}); + +// Set a cookie with the given cookie data; +// may overwrite equivalent cookies if they exist. +var cookie = { url : "http://www.github.com", name : "dummy_name", value : "dummy" }; +session.defaultSession.cookies.set(cookie, (error) => { + if (error) { + console.error(error); + } +}); + +// In the main process. +session.defaultSession.on('will-download', (event, item, webContents) => { + // Set the save path, making Electron not to prompt a save dialog. + item.setSavePath('/tmp/save.pdf'); + console.log(item.getMimeType()); + console.log(item.getFilename()); + console.log(item.getTotalBytes()); + + item.on('updated', (event, state) => { + if (state === 'interrupted') { + console.log('Download is interrupted but can be resumed'); + } else if (state === 'progressing') { + if (item.isPaused()) { + console.log('Download is paused'); + } else { + console.log(`Received bytes: ${item.getReceivedBytes()}`); + } + } + }); + + item.on('done', function(e, state) { + if (state == "completed") { + console.log("Download successfully"); + } else { + console.log(`Download failed: ${state}`) + } + }); +}); + +// To emulate a GPRS connection with 50kbps throughput and 500 ms latency. +session.defaultSession.enableNetworkEmulation({ + latency: 500, + downloadThroughput: 6400, + uploadThroughput: 6400 +}); + +// To emulate a network outage. +session.defaultSession.enableNetworkEmulation({ + offline: true +}); + +session.defaultSession.setCertificateVerifyProc((hostname, cert, callback) => { + callback((hostname === 'github.com') ? true : false); +}); + +session.defaultSession.setPermissionRequestHandler(function(webContents, permission, callback) { + if (webContents.getURL() === 'github.com') { + if (permission == "notifications") { + callback(false); + return; + } + } + + callback(true); +}); + +// consider any url ending with `example.com`, `foobar.com`, `baz` +// for integrated authentication. +session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com, *baz') + +// consider all urls for integrated authentication. +session.defaultSession.allowNTLMCredentialsForDomains('*') + +// Modify the user agent for all requests to the following urls. +var filter = { + urls: ["https://*.github.com/*", "*://electron.github.io"] +}; + +session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, callback) { + details.requestHeaders['User-Agent'] = "MyAgent"; + callback({cancel: false, requestHeaders: details.requestHeaders}); +}); + +app.on('ready', function () { + const protocol = session.defaultSession.protocol + protocol.registerFileProtocol('atom', function (request, callback) { + var url = request.url.substr(7); + callback({path: path.normalize(__dirname + '/' + url)}); + }, function (error) { + if (error) { + console.error('Failed to register protocol'); + } + }) +}); + +// webContents +// https://github.com/electron/electron/blob/master/docs/api/web-contents.md + +console.log(webContents.getAllWebContents()); +console.log(webContents.getFocusedWebContents()); diff --git a/core/typings/github-electron-renderer-tests.ts b/core/typings/github-electron-renderer-tests.ts new file mode 100644 index 0000000..b7292e3 --- /dev/null +++ b/core/typings/github-electron-renderer-tests.ts @@ -0,0 +1,258 @@ +/// +import { + ipcRenderer, + remote, + webFrame, + clipboard, + crashReporter, + nativeImage, + screen, + shell +} from 'electron'; + +import * as fs from 'fs'; + +// In renderer process (web page). +// https://github.com/atom/electron/blob/master/docs/api/ipc-renderer.md +console.log(ipcRenderer.sendSync('synchronous-message', 'ping')); // prints "pong" + +ipcRenderer.on('asynchronous-reply', (event: Electron.IpcRendererEvent, arg: any) => { + console.log(arg); // prints "pong" + event.sender.send('another-message', 'Hello World!'); +}); + +ipcRenderer.send('asynchronous-message', 'ping'); + +// remote +// https://github.com/atom/electron/blob/master/docs/api/remote.md + +var BrowserWindow: typeof Electron.BrowserWindow = remote.require('browser-window'); +var win = new BrowserWindow({ width: 800, height: 600 }); +win.loadURL('https://github.com'); + +remote.getCurrentWindow().on('close', () => { + // blabla... +}); + +remote.getCurrentWindow().capturePage(buf => { + fs.writeFile('/tmp/screenshot.png', buf, err => { + console.log(err); + }); +}); + +remote.getCurrentWebContents().print(); + +remote.getCurrentWindow().capturePage(buf => { + remote.require('fs').writeFile('/tmp/screenshot.png', buf, (err: Error) => { + console.log(err); + }); +}); + +// web-frame +// https://github.com/atom/electron/blob/master/docs/api/web-frame.md + +webFrame.setZoomFactor(2); +console.log(webFrame.getZoomFactor()); + +webFrame.setZoomLevel(200); +console.log(webFrame.getZoomLevel()); + +webFrame.setZoomLevelLimits(50, 200); + +webFrame.setSpellCheckProvider('en-US', true, { + spellCheck: text => { + return !(require('spellchecker').isMisspelled(text)); + } +}); + +webFrame.registerURLSchemeAsSecure('app'); +webFrame.registerURLSchemeAsBypassingCSP('app'); +webFrame.registerURLSchemeAsPrivileged('app'); + +webFrame.insertText('text'); + +webFrame.executeJavaScript('JSON.stringify({})', false, (result) => { + console.log(result); +}); + +console.log(webFrame.getResourceUsage()); +webFrame.clearCache(); + +// clipboard +// https://github.com/atom/electron/blob/master/docs/api/clipboard.md + +clipboard.writeText('Example String'); +clipboard.writeText('Example String', 'selection'); +console.log(clipboard.readText('selection')); +console.log(clipboard.availableFormats()); +clipboard.clear(); + +clipboard.write({ + html: '', + text: 'Hello World!', + image: clipboard.readImage() +}); + +// crash-reporter +// https://github.com/atom/electron/blob/master/docs/api/crash-reporter.md + +crashReporter.start({ + productName: 'YourName', + companyName: 'YourCompany', + submitURL: 'https://your-domain.com/url-to-submit', + autoSubmit: true +}); + +// desktopCapturer +// https://github.com/atom/electron/blob/master/docs/api/desktop-capturer.md + +var desktopCapturer = require('electron').desktopCapturer; + +desktopCapturer.getSources({types: ['window', 'screen']}, function(error, sources) { + if (error) throw error; + for (var i = 0; i < sources.length; ++i) { + if (sources[i].name == "Electron") { + (navigator as any).webkitGetUserMedia({ + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: sources[i].id, + minWidth: 1280, + maxWidth: 1280, + minHeight: 720, + maxHeight: 720 + } + } + }, gotStream, getUserMediaError); + return; + } + } +}); + +function gotStream(stream: any) { + (document.querySelector('video') as HTMLVideoElement).src = URL.createObjectURL(stream); +} + +function getUserMediaError(error: Error) { + console.log('getUserMediaError', error); +} + +// File object +// https://github.com/atom/electron/blob/master/docs/api/file-object.md + +/* +
+ Drag your file here +
+*/ + +var holder = document.getElementById('holder'); + +holder.ondragover = function () { + return false; +}; + +holder.ondragleave = holder.ondragend = function () { + return false; +}; + +holder.ondrop = function (e) { + e.preventDefault(); + var file = e.dataTransfer.files[0]; + console.log('File you dragged here is', file.path); + return false; +}; + +// nativeImage +// https://github.com/atom/electron/blob/master/docs/api/native-image.md + +var Tray: Electron.Tray = remote.require('Tray'); +var appIcon2 = new Tray('/Users/somebody/images/icon.png'); +var window2 = new BrowserWindow({ icon: '/Users/somebody/images/window.png' }); +var image = clipboard.readImage(); +var appIcon3 = new Tray(image); +var appIcon4 = new Tray('/Users/somebody/images/icon.png'); + +// https://github.com/electron/electron/blob/master/docs/api/process.md + +// preload.js +var _setImmediate = setImmediate; +var _clearImmediate = clearImmediate; +process.once('loaded', function() { + global.setImmediate = _setImmediate; + global.clearImmediate = _clearImmediate; +}); + +// screen +// https://github.com/atom/electron/blob/master/docs/api/screen.md + +var app: Electron.App = remote.require('app'); + +var mainWindow: Electron.BrowserWindow = null; + +app.on('ready', () => { + var size = screen.getPrimaryDisplay().workAreaSize; + mainWindow = new BrowserWindow({ width: size.width, height: size.height }); +}); + +app.on('ready', () => { + var displays = screen.getAllDisplays(); + var externalDisplay: any = null; + for (var i in displays) { + if (displays[i].bounds.x > 0 || displays[i].bounds.y > 0) { + externalDisplay = displays[i]; + break; + } + } + + if (externalDisplay) { + mainWindow = new BrowserWindow({ + x: externalDisplay.bounds.x + 50, + y: externalDisplay.bounds.y + 50, + }); + } +}); + +// shell +// https://github.com/atom/electron/blob/master/docs/api/shell.md + +shell.openExternal('https://github.com'); + +// +// https://github.com/atom/electron/blob/master/docs/api/web-view-tag.md + +var webview = document.createElement('webview'); +webview.loadURL('https://github.com'); + +webview.addEventListener('console-message', function(e) { + console.log('Guest page logged a message:', e.message); +}); + +webview.addEventListener('found-in-page', function(e) { + if (e.result.finalUpdate) { + webview.stopFindInPage("keepSelection"); + } +}); + +var requestId = webview.findInPage("test"); + +webview.addEventListener('new-window', function(e) { + require('electron').shell.openExternal(e.url); +}); + +webview.addEventListener('close', function() { + webview.src = 'about:blank'; +}); + +// In embedder page. +webview.addEventListener('ipc-message', function(event) { + console.log(event.channel); // Prints "pong" +}); +webview.send('ping'); +webview.capturePage((image) => { console.log(image); }); + +// In guest page. +ipcRenderer.on('ping', function() { + ipcRenderer.sendToHost('pong'); +}); diff --git a/core/typings/github-electron.d.ts b/core/typings/github-electron.d.ts new file mode 100644 index 0000000..ce01bb2 --- /dev/null +++ b/core/typings/github-electron.d.ts @@ -0,0 +1,5250 @@ +// Type definitions for Electron v1.3.1 +// Project: http://electron.atom.io/ +// Definitions by: jedmao , rhysd , Milan Burda +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/// + +declare namespace Electron { + + class EventEmitter implements NodeJS.EventEmitter { + addListener(event: string, listener: Function): this; + on(event: string, listener: Function): this; + once(event: string, listener: Function): this; + removeListener(event: string, listener: Function): this; + removeAllListeners(event?: string): this; + setMaxListeners(n: number): this; + getMaxListeners(): number; + listeners(event: string): Function[]; + emit(event: string, ...args: any[]): boolean; + listenerCount(type: string): number; + } + + interface Event { + preventDefault: Function; + sender: EventEmitter; + } + + // https://github.com/electron/electron/blob/master/docs/api/app.md + + /** + * The app module is responsible for controlling the application's lifecycle. + */ + interface App extends NodeJS.EventEmitter { + /** + * Emitted when the application has finished basic startup. + * On Windows and Linux, the will-finish-launching event + * is the same as the ready event; on macOS, this event represents + * the applicationWillFinishLaunching notification of NSApplication. + * You would usually set up listeners for the open-file and open-url events here, + * and start the crash reporter and auto updater. + * + * In most cases, you should just do everything in the ready event handler. + */ + on(event: 'will-finish-launching', listener: Function): this; + /** + * Emitted when Electron has finished initialization. + */ + on(event: 'ready', listener: Function): this; + /** + * Emitted when all windows have been closed. + * + * If you do not subscribe to this event and all windows are closed, + * the default behavior is to quit the app; however, if you subscribe, + * you control whether the app quits or not. + * If the user pressed Cmd + Q, or the developer called app.quit(), + * Electron will first try to close all the windows and then emit the will-quit event, + * and in this case the window-all-closed event would not be emitted. + */ + on(event: 'window-all-closed', listener: Function): this; + /** + * Emitted before the application starts closing its windows. + * Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. + */ + on(event: 'before-quit', listener: (event: Event) => void): this; + /** + * Emitted when all windows have been closed and the application will quit. + * Calling event.preventDefault() will prevent the default behaviour, which is terminating the application. + */ + on(event: 'will-quit', listener: (event: Event) => void): this; + /** + * Emitted when the application is quitting. + */ + on(event: 'quit', listener: (event: Event, exitCode: number) => void): this; + /** + * Emitted when the user wants to open a file with the application. + * The open-file event is usually emitted when the application is already open + * and the OS wants to reuse the application to open the file. + * open-file is also emitted when a file is dropped onto the dock and the application + * is not yet running. Make sure to listen for the open-file event very early + * in your application startup to handle this case (even before the ready event is emitted). + * + * You should call event.preventDefault() if you want to handle this event. + * + * Note: This is only implemented on macOS. + */ + on(event: 'open-file', listener: (event: Event, url: string) => void): this; + /** + * Emitted when the user wants to open a URL with the application. + * The URL scheme must be registered to be opened by your application. + * + * You should call event.preventDefault() if you want to handle this event. + * + * Note: This is only implemented on macOS. + */ + on(event: 'open-url', listener: (event: Event, url: string) => void): this; + /** + * Emitted when the application is activated, which usually happens when clicks on the applications’s dock icon. + * Note: This is only implemented on macOS. + */ + on(event: 'activate', listener: Function): this; + /** + * Emitted during Handoff when an activity from a different device wants to be resumed. + * You should call event.preventDefault() if you want to handle this event. + */ + on(event: 'continue-activity', listener: (event: Event, type: string, userInfo: Object) => void): this; + /** + * Emitted when a browserWindow gets blurred. + */ + on(event: 'browser-window-blur', listener: (event: Event, browserWindow: BrowserWindow) => void): this; + /** + * Emitted when a browserWindow gets focused. + */ + on(event: 'browser-window-focus', listener: (event: Event, browserWindow: BrowserWindow) => void): this; + /** + * Emitted when a new browserWindow is created. + */ + on(event: 'browser-window-created', listener: (event: Event, browserWindow: BrowserWindow) => void): this; + /** + * Emitted when a new webContents is created. + */ + on(event: 'web-contents-created', listener: (event: Event, webContents: WebContents) => void): this; + /** + * Emitted when failed to verify the certificate for url, to trust the certificate + * you should prevent the default behavior with event.preventDefault() and call callback(true). + */ + on(event: 'certificate-error', listener: (event: Event, + webContents: WebContents, + url: string, + error: string, + certificate: Certificate, + callback: (trust: boolean) => void + ) => void): this; + /** + * Emitted when a client certificate is requested. + * + * The url corresponds to the navigation entry requesting the client certificate + * and callback needs to be called with an entry filtered from the list. + * Using event.preventDefault() prevents the application from using the first certificate from the store. + */ + on(event: 'select-client-certificate', listener: (event: Event, + webContents: WebContents, + url: string, + certificateList: Certificate[], + callback: (certificate: Certificate) => void + ) => void): this; + /** + * Emitted when webContents wants to do basic auth. + * + * The default behavior is to cancel all authentications, to override this + * you should prevent the default behavior with event.preventDefault() + * and call callback(username, password) with the credentials. + */ + on(event: 'login', listener: (event: Event, + webContents: WebContents, + request: LoginRequest, + authInfo: LoginAuthInfo, + callback: (username: string, password: string) => void + ) => void): this; + /** + * Emitted when the gpu process crashes. + */ + on(event: 'gpu-process-crashed', listener: Function): this; + /** + * Emitted when Chrome's accessibility support changes. + * + * Note: This API is only available on macOS and Windows. + */ + on(event: 'accessibility-support-changed', listener: (event: Event, accessibilitySupportEnabled: boolean) => void): this; + on(event: string, listener: Function): this; + /** + * Try to close all windows. The before-quit event will first be emitted. + * If all windows are successfully closed, the will-quit event will be emitted + * and by default the application would be terminated. + * + * This method guarantees all beforeunload and unload handlers are correctly + * executed. It is possible that a window cancels the quitting by returning + * false in beforeunload handler. + */ + quit(): void; + /** + * Exits immediately with exitCode. + * All windows will be closed immediately without asking user + * and the before-quit and will-quit events will not be emitted. + */ + exit(exitCode: number): void; + /** + * Relaunches the app when current instance exits. + * + * By default the new instance will use the same working directory + * and command line arguments with current instance. + * When args is specified, the args will be passed as command line arguments instead. + * When execPath is specified, the execPath will be executed for relaunch instead of current app. + * + * Note that this method does not quit the app when executed, you have to call app.quit + * or app.exit after calling app.relaunch to make the app restart. + * + * When app.relaunch is called for multiple times, multiple instances + * will be started after current instance exited. + */ + relaunch(options?: { + args?: string[], + execPath?: string + }): void; + /** + * On Linux, focuses on the first visible window. + * On macOS, makes the application the active app. + * On Windows, focuses on the application’s first window. + */ + focus(): void; + /** + * Hides all application windows without minimizing them. + * Note: This is only implemented on macOS. + */ + hide(): void; + /** + * Shows application windows after they were hidden. Does not automatically focus them. + * Note: This is only implemented on macOS. + */ + show(): void; + /** + * Returns the current application directory. + */ + getAppPath(): string; + /** + * @returns The path to a special directory or file associated with name. + * On failure an Error would throw. + */ + getPath(name: AppPathName): string; + /** + * Overrides the path to a special directory or file associated with name. + * If the path specifies a directory that does not exist, the directory will + * be created by this method. On failure an Error would throw. + * + * You can only override paths of names defined in app.getPath. + * + * By default web pages' cookies and caches will be stored under userData + * directory, if you want to change this location, you have to override the + * userData path before the ready event of app module gets emitted. + */ + setPath(name: AppPathName, path: string): void; + /** + * @returns The version of loaded application, if no version is found in + * application's package.json, the version of current bundle or executable. + */ + getVersion(): string; + /** + * @returns The current application's name, the name in package.json would be used. + * Usually the name field of package.json is a short lowercased name, according to + * the spec of npm modules. So usually you should also specify a productName field, + * which is your application's full capitalized name, and it will be preferred over + * name by Electron. + */ + getName(): string; + /** + * Overrides the current application's name. + */ + setName(name: string): void; + /** + * @returns The current application locale. + */ + getLocale(): string; + /** + * Adds path to recent documents list. + * + * This list is managed by the system, on Windows you can visit the list from + * task bar, and on macOS you can visit it from dock menu. + * + * Note: This is only implemented on macOS and Windows. + */ + addRecentDocument(path: string): void; + /** + * Clears the recent documents list. + * + * Note: This is only implemented on macOS and Windows. + */ + clearRecentDocuments(): void; + /** + * Sets the current executable as the default handler for a protocol (aka URI scheme). + * Once registered, all links with your-protocol:// will be opened with the current executable. + * The whole link, including protocol, will be passed to your application as a parameter. + * + * Note: This is only implemented on macOS and Windows. + * On macOS, you can only register protocols that have been added to your app's info.plist. + */ + setAsDefaultProtocolClient(protocol: string): void; + /** + * Removes the current executable as the default handler for a protocol (aka URI scheme). + * + * Note: This is only implemented on macOS and Windows. + */ + removeAsDefaultProtocolClient(protocol: string): void; + /** + * @returns Whether the current executable is the default handler for a protocol (aka URI scheme). + * + * Note: This is only implemented on macOS and Windows. + */ + isDefaultProtocolClient(protocol: string): boolean; + /** + * Adds tasks to the Tasks category of JumpList on Windows. + * + * Note: This API is only available on Windows. + */ + setUserTasks(tasks: Task[]): void; + /** + * This method makes your application a Single Instance Application instead of allowing + * multiple instances of your app to run, this will ensure that only a single instance + * of your app is running, and other instances signal this instance and exit. + */ + makeSingleInstance(callback: (args: string[], workingDirectory: string) => void): boolean; + /** + * Releases all locks that were created by makeSingleInstance. This will allow + * multiple instances of the application to once again run side by side. + */ + releaseSingleInstance(): void; + /** + * Creates an NSUserActivity and sets it as the current activity. + * The activity is eligible for Handoff to another device afterward. + * + * @param type Uniquely identifies the activity. Maps to NSUserActivity.activityType. + * @param userInfo App-specific state to store for use by another device. + * @param webpageURL The webpage to load in a browser if no suitable app is + * installed on the resuming device. The scheme must be http or https. + * + * Note: This API is only available on macOS. + */ + setUserActivity(type: string, userInfo: Object, webpageURL?: string): void; + /** + * @returns The type of the currently running activity. + * + * Note: This API is only available on macOS. + */ + getCurrentActivityType(): string; + /** + * Changes the Application User Model ID to id. + */ + setAppUserModelId(id: string): void; + /** + * Imports the certificate in pkcs12 format into the platform certificate store. + * @param callback Called with the result of import operation, a value of 0 indicates success + * while any other value indicates failure according to chromium net_error_list. + * + * Note: This API is only available on Linux. + */ + importCertificate(options: ImportCertificateOptions, callback: (result: number) => void): void; + /** + * Disables hardware acceleration for current app. + * This method can only be called before app is ready. + */ + disableHardwareAcceleration(): void; + /** + * @returns whether current desktop environment is Unity launcher. (Linux) + * + * Note: This API is only available on Linux. + */ + isUnityRunning(): boolean; + /** + * Returns a Boolean, true if Chrome's accessibility support is enabled, false otherwise. + * This API will return true if the use of assistive technologies, such as screen readers, + * has been detected. + * See https://www.chromium.org/developers/design-documents/accessibility for more details. + * + * Note: This API is only available on macOS and Windows. + */ + isAccessibilitySupportEnabled(): boolean; + /** + * @returns an Object with the login item settings of the app. + * + * Note: This API is only available on macOS and Windows. + */ + getLoginItemSettings(): LoginItemSettings; + /** + * Set the app's login item settings. + * + * Note: This API is only available on macOS and Windows. + */ + setLoginItemSettings(settings: LoginItemSettings): void; + commandLine: CommandLine; + /** + * Note: This API is only available on macOS. + */ + dock: Dock; + } + + type AppPathName = 'home'|'appData'|'userData'|'temp'|'exe'|'module'|'desktop'|'documents'|'downloads'|'music'|'pictures'|'videos'|'pepperFlashSystemPlugin'; + + interface ImportCertificateOptions { + /** + * Path for the pkcs12 file. + */ + certificate: string; + /** + * Passphrase for the certificate. + */ + password: string; + } + + interface CommandLine { + /** + * Append a switch [with optional value] to Chromium's command line. + * + * Note: This will not affect process.argv, and is mainly used by developers + * to control some low-level Chromium behaviors. + */ + appendSwitch(_switch: string, value?: string): void; + /** + * Append an argument to Chromium's command line. The argument will quoted properly. + * + * Note: This will not affect process.argv. + */ + appendArgument(value: string): void; + } + + interface Dock { + /** + * When critical is passed, the dock icon will bounce until either the + * application becomes active or the request is canceled. + * + * When informational is passed, the dock icon will bounce for one second. + * However, the request remains active until either the application becomes + * active or the request is canceled. + * + * @param type The default is informational. + * @returns An ID representing the request. + */ + bounce(type?: 'critical' | 'informational'): number; + /** + * Cancel the bounce of id. + * + * Note: This API is only available on macOS. + */ + cancelBounce(id: number): void; + /** + * Bounces the Downloads stack if the filePath is inside the Downloads folder. + * + * Note: This API is only available on macOS. + */ + downloadFinished(filePath: string): void; + /** + * Sets the string to be displayed in the dock’s badging area. + * + * Note: This API is only available on macOS. + */ + setBadge(text: string): void; + /** + * Returns the badge string of the dock. + * + * Note: This API is only available on macOS. + */ + getBadge(): string; + /** + * Sets the counter badge for current app. Setting the count to 0 will hide the badge. + * + * @returns True when the call succeeded, otherwise returns false. + * + * Note: This API is only available on macOS and Linux. + */ + setBadgeCount(count: number): boolean; + /** + * @returns The current value displayed in the counter badge. + * + * Note: This API is only available on macOS and Linux. + */ + getBadgeCount(): number; + /** + * Hides the dock icon. + * + * Note: This API is only available on macOS. + */ + hide(): void; + /** + * Shows the dock icon. + * + * Note: This API is only available on macOS. + */ + show(): void; + /** + * Sets the application dock menu. + * + * Note: This API is only available on macOS. + */ + setMenu(menu: Menu): void; + /** + * Sets the image associated with this dock icon. + * + * Note: This API is only available on macOS. + */ + setIcon(icon: NativeImage | string): void; + } + + interface Task { + /** + * Path of the program to execute, usually you should specify process.execPath + * which opens current program. + */ + program: string; + /** + * The arguments of command line when program is executed. + */ + arguments: string; + /** + * The string to be displayed in a JumpList. + */ + title: string; + /** + * Description of this task. + */ + description?: string; + /** + * The absolute path to an icon to be displayed in a JumpList, it can be + * arbitrary resource file that contains an icon, usually you can specify + * process.execPath to show the icon of the program. + */ + iconPath: string; + /** + * The icon index in the icon file. If an icon file consists of two or more + * icons, set this value to identify the icon. If an icon file consists of + * one icon, this value is 0. + */ + iconIndex?: number; + } + + interface LoginItemSettings { + /** + * True if the app is set to open at login. + */ + openAtLogin: boolean; + /** + * True if the app is set to open as hidden at login. This setting is only supported on macOS. + */ + openAsHidden: boolean; + /** + * True if the app was opened at login automatically. This setting is only supported on macOS. + */ + wasOpenedAtLogin?: boolean; + /** + * True if the app was opened as a hidden login item. This indicates that the app should not + * open any windows at startup. This setting is only supported on macOS. + */ + wasOpenedAsHidden?: boolean; + /** + * True if the app was opened as a login item that should restore the state from the previous session. + * This indicates that the app should restore the windows that were open the last time the app was closed. + * This setting is only supported on macOS. + */ + restoreState?: boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/auto-updater.md + + /** + * This module provides an interface for the Squirrel auto-updater framework. + */ + interface AutoUpdater extends NodeJS.EventEmitter { + /** + * Emitted when there is an error while updating. + */ + on(event: 'error', listener: (error: Error) => void): this; + /** + * Emitted when checking if an update has started. + */ + on(event: 'checking-for-update', listener: Function): this; + /** + * Emitted when there is an available update. The update is downloaded automatically. + */ + on(event: 'update-available', listener: Function): this; + /** + * Emitted when there is no available update. + */ + on(event: 'update-not-available', listener: Function): this; + /** + * Emitted when an update has been downloaded. + * Note: On Windows only releaseName is available. + */ + on(event: 'update-downloaded', listener: (event: Event, releaseNotes: string, releaseName: string, releaseDate: Date, updateURL: string) => void): this; + on(event: string, listener: Function): this; + /** + * Set the url and initialize the auto updater. + */ + setFeedURL(url: string, requestHeaders?: Headers): void; + /** + * @returns The current update feed URL. + */ + getFeedURL(): string; + /** + * Ask the server whether there is an update, you have to call setFeedURL + * before using this API + */ + checkForUpdates(): void; + /** + * Restarts the app and installs the update after it has been downloaded. + * It should only be called after update-downloaded has been emitted. + */ + quitAndInstall(): void; + } + + // https://github.com/electron/electron/blob/master/docs/api/browser-window.md + + /** + * The BrowserWindow class gives you ability to create a browser window. + * You can also create a window without chrome by using Frameless Window API. + */ + class BrowserWindow extends EventEmitter { + /** + * Emitted when the document changed its title, + * calling event.preventDefault() would prevent the native window’s title to change. + */ + on(event: 'page-title-updated', listener: (event: Event) => void): this; + /** + * Emitted when the window is going to be closed. It’s emitted before the beforeunload + * and unload event of the DOM. Calling event.preventDefault() will cancel the close. + */ + on(event: 'close', listener: (event: Event) => void): this; + /** + * Emitted when the window is closed. After you have received this event + * you should remove the reference to the window and avoid using it anymore. + */ + on(event: 'closed', listener: Function): this; + /** + * Emitted when the web page becomes unresponsive. + */ + on(event: 'unresponsive', listener: Function): this; + /** + * Emitted when the unresponsive web page becomes responsive again. + */ + on(event: 'responsive', listener: Function): this; + /** + * Emitted when the window loses focus. + */ + on(event: 'blur', listener: Function): this; + /** + * Emitted when the window gains focus. + */ + on(event: 'focus', listener: Function): this; + /** + * Emitted when the window is shown. + */ + on(event: 'show', listener: Function): this; + /** + * Emitted when the window is hidden. + */ + on(event: 'hide', listener: Function): this; + /** + * Emitted when the web page has been rendered and window can be displayed without visual flash. + */ + on(event: 'ready-to-show', listener: Function): this; + /** + * Emitted when window is maximized. + */ + on(event: 'maximize', listener: Function): this; + /** + * Emitted when the window exits from maximized state. + */ + on(event: 'unmaximize', listener: Function): this; + /** + * Emitted when the window is minimized. + */ + on(event: 'minimize', listener: Function): this; + /** + * Emitted when the window is restored from minimized state. + */ + on(event: 'restore', listener: Function): this; + /** + * Emitted when the window is getting resized. + */ + on(event: 'resize', listener: Function): this; + /** + * Emitted when the window is getting moved to a new position. + */ + on(event: 'move', listener: Function): this; + /** + * Emitted when the window enters full screen state. + */ + on(event: 'enter-full-screen', listener: Function): this; + /** + * Emitted when the window leaves full screen state. + */ + on(event: 'leave-full-screen', listener: Function): this; + /** + * Emitted when the window enters full screen state triggered by HTML API. + */ + on(event: 'enter-html-full-screen', listener: Function): this; + /** + * Emitted when the window leaves full screen state triggered by HTML API. + */ + on(event: 'leave-html-full-screen', listener: Function): this; + /** + * Emitted when an App Command is invoked. These are typically related + * to keyboard media keys or browser commands, as well as the "Back" / + * "Forward" buttons built into some mice on Windows. + * Note: This is only implemented on Windows. + */ + on(event: 'app-command', listener: (event: Event, command: string) => void): this; + /** + * Emitted when scroll wheel event phase has begun. + * Note: This is only implemented on macOS. + */ + on(event: 'scroll-touch-begin', listener: Function): this; + /** + * Emitted when scroll wheel event phase has ended. + * Note: This is only implemented on macOS. + */ + on(event: 'scroll-touch-end', listener: Function): this; + /** + * Emitted on 3-finger swipe. + * Note: This is only implemented on macOS. + */ + on(event: 'swipe', listener: (event: Event, direction: SwipeDirection) => void): this; + on(event: string, listener: Function): this; + /** + * Creates a new BrowserWindow with native properties as set by the options. + */ + constructor(options?: BrowserWindowOptions); + /** + * @returns All opened browser windows. + */ + static getAllWindows(): BrowserWindow[]; + /** + * @returns The window that is focused in this application. + */ + static getFocusedWindow(): BrowserWindow; + /** + * Find a window according to the webContents it owns. + */ + static fromWebContents(webContents: WebContents): BrowserWindow; + /** + * Find a window according to its ID. + */ + static fromId(id: number): BrowserWindow; + /** + * Adds devtools extension located at path. The extension will be remembered + * so you only need to call this API once, this API is not for programming use. + * @returns The extension's name. + * + * Note: This API cannot be called before the ready event of the app module is emitted. + */ + static addDevToolsExtension(path: string): string; + /** + * Remove a devtools extension. + * @param name The name of the devtools extension to remove. + * + * Note: This API cannot be called before the ready event of the app module is emitted. + */ + static removeDevToolsExtension(name: string): void; + /** + * @returns devtools extensions. + * + * Note: This API cannot be called before the ready event of the app module is emitted. + */ + static getDevToolsExtensions(): DevToolsExtensions; + /** + * The WebContents object this window owns, all web page related events and + * operations would be done via it. + * Note: Users should never store this object because it may become null when + * the renderer process (web page) has crashed. + */ + webContents: WebContents; + /** + * Get the unique ID of this window. + */ + id: number; + /** + * Force closing the window, the unload and beforeunload event won't be emitted + * for the web page, and close event would also not be emitted for this window, + * but it would guarantee the closed event to be emitted. + * You should only use this method when the renderer process (web page) has crashed. + */ + destroy(): void; + /** + * Try to close the window, this has the same effect with user manually clicking + * the close button of the window. The web page may cancel the close though, + * see the close event. + */ + close(): void; + /** + * Focus on the window. + */ + focus(): void; + /** + * Remove focus on the window. + */ + blur(): void; + /** + * @returns Whether the window is focused. + */ + isFocused(): boolean; + /** + * Shows and gives focus to the window. + */ + show(): void; + /** + * Shows the window but doesn't focus on it. + */ + showInactive(): void; + /** + * Hides the window. + */ + hide(): void; + /** + * @returns Whether the window is visible to the user. + */ + isVisible(): boolean; + /** + * @returns Whether the window is a modal window. + */ + isModal(): boolean; + /** + * Maximizes the window. + */ + maximize(): void; + /** + * Unmaximizes the window. + */ + unmaximize(): void; + /** + * @returns Whether the window is maximized. + */ + isMaximized(): boolean; + /** + * Minimizes the window. On some platforms the minimized window will be + * shown in the Dock. + */ + minimize(): void; + /** + * Restores the window from minimized state to its previous state. + */ + restore(): void; + /** + * @returns Whether the window is minimized. + */ + isMinimized(): boolean; + /** + * Sets whether the window should be in fullscreen mode. + */ + setFullScreen(flag: boolean): void; + /** + * @returns Whether the window is in fullscreen mode. + */ + isFullScreen(): boolean; + /** + * This will have a window maintain an aspect ratio. + * The extra size allows a developer to have space, specified in pixels, + * not included within the aspect ratio calculations. + * This API already takes into account the difference between a window’s size and its content size. + * + * Note: This API is available only on macOS. + */ + setAspectRatio(aspectRatio: number, extraSize?: Dimension): void; + /** + * Resizes and moves the window to width, height, x, y. + */ + setBounds(options: Rectangle, animate?: boolean): void; + /** + * @returns The window's width, height, x and y values. + */ + getBounds(): Rectangle; + /** + * Resizes the window to width and height. + */ + setSize(width: number, height: number, animate?: boolean): void; + /** + * @returns The window's width and height. + */ + getSize(): number[]; + /** + * Resizes the window's client area (e.g. the web page) to width and height. + */ + setContentSize(width: number, height: number, animate?: boolean): void; + /** + * @returns The window's client area's width and height. + */ + getContentSize(): number[]; + /** + * Sets the minimum size of window to width and height. + */ + setMinimumSize(width: number, height: number): void; + /** + * @returns The window's minimum width and height. + */ + getMinimumSize(): number[]; + /** + * Sets the maximum size of window to width and height. + */ + setMaximumSize(width: number, height: number): void; + /** + * @returns The window's maximum width and height. + */ + getMaximumSize(): number[]; + /** + * Sets whether the window can be manually resized by user. + */ + setResizable(resizable: boolean): void; + /** + * @returns Whether the window can be manually resized by user. + */ + isResizable(): boolean; + /** + * Sets whether the window can be moved by user. On Linux does nothing. + * Note: This API is available only on macOS and Windows. + */ + setMovable(movable: boolean): void; + /** + * Note: This API is available only on macOS and Windows. + * @returns Whether the window can be moved by user. On Linux always returns true. + */ + isMovable(): boolean; + /** + * Sets whether the window can be manually minimized by user. On Linux does nothing. + * Note: This API is available only on macOS and Windows. + */ + setMinimizable(minimizable: boolean): void; + /** + * Note: This API is available only on macOS and Windows. + * @returns Whether the window can be manually minimized by user. On Linux always returns true. + */ + isMinimizable(): boolean; + /** + * Sets whether the window can be manually maximized by user. On Linux does nothing. + * Note: This API is available only on macOS and Windows. + */ + setMaximizable(maximizable: boolean): void; + /** + * Note: This API is available only on macOS and Windows. + * @returns Whether the window can be manually maximized by user. On Linux always returns true. + */ + isMaximizable(): boolean; + /** + * Sets whether the maximize/zoom window button toggles fullscreen mode or maximizes the window. + */ + setFullScreenable(fullscreenable: boolean): void; + /** + * @returns Whether the maximize/zoom window button toggles fullscreen mode or maximizes the window. + */ + isFullScreenable(): boolean; + /** + * Sets whether the window can be manually closed by user. On Linux does nothing. + * Note: This API is available only on macOS and Windows. + */ + setClosable(closable: boolean): void; + /** + * Note: This API is available only on macOS and Windows. + * @returns Whether the window can be manually closed by user. On Linux always returns true. + */ + isClosable(): boolean; + /** + * Sets whether the window should show always on top of other windows. After + * setting this, the window is still a normal window, not a toolbox window + * which can not be focused on. + */ + setAlwaysOnTop(flag: boolean): void; + /** + * @returns Whether the window is always on top of other windows. + */ + isAlwaysOnTop(): boolean; + /** + * Moves window to the center of the screen. + */ + center(): void; + /** + * Moves window to x and y. + */ + setPosition(x: number, y: number, animate?: boolean): void; + /** + * @returns The window's current position. + */ + getPosition(): number[]; + /** + * Changes the title of native window to title. + */ + setTitle(title: string): void; + /** + * Note: The title of web page can be different from the title of the native window. + * @returns The title of the native window. + */ + getTitle(): string; + /** + * Changes the attachment point for sheets on macOS. + * Note: This API is available only on macOS. + */ + setSheetOffset(offsetY: number, offsetX?: number): void; + /** + * Starts or stops flashing the window to attract user's attention. + */ + flashFrame(flag: boolean): void; + /** + * Makes the window do not show in Taskbar. + */ + setSkipTaskbar(skip: boolean): void; + /** + * Enters or leaves the kiosk mode. + */ + setKiosk(flag: boolean): void; + /** + * @returns Whether the window is in kiosk mode. + */ + isKiosk(): boolean; + /** + * The native type of the handle is HWND on Windows, NSView* on macOS, + * and Window (unsigned long) on Linux. + * @returns The platform-specific handle of the window as Buffer. + */ + getNativeWindowHandle(): Buffer; + /** + * Hooks a windows message. The callback is called when the message is received in the WndProc. + * Note: This API is available only on Windows. + */ + hookWindowMessage(message: number, callback: Function): void; + /** + * @returns Whether the message is hooked. + */ + isWindowMessageHooked(message: number): boolean; + /** + * Unhook the window message. + */ + unhookWindowMessage(message: number): void; + /** + * Unhooks all of the window messages. + */ + unhookAllWindowMessages(): void; + /** + * Sets the pathname of the file the window represents, and the icon of the + * file will show in window's title bar. + * Note: This API is available only on macOS. + */ + setRepresentedFilename(filename: string): void; + /** + * Note: This API is available only on macOS. + * @returns The pathname of the file the window represents. + */ + getRepresentedFilename(): string; + /** + * Specifies whether the window’s document has been edited, and the icon in + * title bar will become grey when set to true. + * Note: This API is available only on macOS. + */ + setDocumentEdited(edited: boolean): void; + /** + * Note: This API is available only on macOS. + * @returns Whether the window's document has been edited. + */ + isDocumentEdited(): boolean; + focusOnWebView(): void; + blurWebView(): void; + /** + * Captures the snapshot of page within rect, upon completion the callback + * will be called. Omitting the rect would capture the whole visible page. + * Note: Be sure to read documents on remote buffer in remote if you are going + * to use this API in renderer process. + * @param callback Supplies the image that stores data of the snapshot. + */ + capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + /** + * Captures the snapshot of page within rect, upon completion the callback + * will be called. Omitting the rect would capture the whole visible page. + * Note: Be sure to read documents on remote buffer in remote if you are going + * to use this API in renderer process. + * @param callback Supplies the image that stores data of the snapshot. + */ + capturePage(callback: (image: NativeImage) => void): void; + /** + * Same as webContents.loadURL(url). + */ + loadURL(url: string, options?: LoadURLOptions): void; + /** + * Same as webContents.reload. + */ + reload(): void; + /** + * Sets the menu as the window top menu. + * Note: This API is not available on macOS. + */ + setMenu(menu: Menu): void; + /** + * Sets the progress value in the progress bar. + * On Linux platform, only supports Unity desktop environment, you need to + * specify the *.desktop file name to desktopName field in package.json. + * By default, it will assume app.getName().desktop. + * @param progress Valid range is [0, 1.0]. If < 0, the progress bar is removed. + * If greater than 0, it becomes indeterminate. + */ + setProgressBar(progress: number): void; + /** + * Sets a 16px overlay onto the current Taskbar icon, usually used to convey + * some sort of application status or to passively notify the user. + * Note: This API is only available on Windows 7 or above. + * @param overlay The icon to display on the bottom right corner of the Taskbar + * icon. If this parameter is null, the overlay is cleared + * @param description Provided to Accessibility screen readers. + */ + setOverlayIcon(overlay: NativeImage, description: string): void; + /** + * Sets whether the window should have a shadow. On Windows and Linux does nothing. + * Note: This API is available only on macOS. + */ + setHasShadow(hasShadow: boolean): void; + /** + * Note: This API is available only on macOS. + * @returns whether the window has a shadow. On Windows and Linux always returns true. + */ + hasShadow(): boolean; + /** + * Add a thumbnail toolbar with a specified set of buttons to the thumbnail image + * of a window in a taskbar button layout. + * @returns Whether the thumbnail has been added successfully. + * + * Note: This API is available only on Windows. + */ + setThumbarButtons(buttons: ThumbarButton[]): boolean; + /** + * Sets the region of the window to show as the thumbnail image displayed when hovering + * over the window in the taskbar. You can reset the thumbnail to be the entire window + * by specifying an empty region: {x: 0, y: 0, width: 0, height: 0}. + * + * Note: This API is available only on Windows. + */ + setThumbnailClip(region: Bounds): void; + /** + * Same as webContents.showDefinitionForSelection(). + * Note: This API is available only on macOS. + */ + showDefinitionForSelection(): void; + /** + * Changes window icon. + * Note: This API is not available on macOS. + */ + setIcon(icon: NativeImage): void; + /** + * Sets whether the window menu bar should hide itself automatically. Once set + * the menu bar will only show when users press the single Alt key. + * If the menu bar is already visible, calling setAutoHideMenuBar(true) won't + * hide it immediately. + */ + setAutoHideMenuBar(hide: boolean): void; + /** + * @returns Whether menu bar automatically hides itself. + */ + isMenuBarAutoHide(): boolean; + /** + * Sets whether the menu bar should be visible. If the menu bar is auto-hide, + * users can still bring up the menu bar by pressing the single Alt key. + */ + setMenuBarVisibility(visibile: boolean): void; + /** + * @returns Whether the menu bar is visible. + */ + isMenuBarVisible(): boolean; + /** + * Sets whether the window should be visible on all workspaces. + * Note: This API does nothing on Windows. + */ + setVisibleOnAllWorkspaces(visible: boolean): void; + /** + * Note: This API always returns false on Windows. + * @returns Whether the window is visible on all workspaces. + */ + isVisibleOnAllWorkspaces(): boolean; + /** + * Makes the window ignore all mouse events. + * + * All mouse events happened in this window will be passed to the window below this window, + * but if this window has focus, it will still receive keyboard events. + */ + setIgnoreMouseEvents(ignore: boolean): void; + /** + * Prevents the window contents from being captured by other apps. + * + * On macOS it sets the NSWindow's sharingType to NSWindowSharingNone. + * On Windows it calls SetWindowDisplayAffinity with WDA_MONITOR. + */ + setContentProtection(enable: boolean): void; + /** + * Changes whether the window can be focused. + * Note: This API is available only on Windows. + */ + setFocusable(focusable: boolean): void; + /** + * Sets parent as current window's parent window, + * passing null will turn current window into a top-level window. + * Note: This API is not available on Windows. + */ + setParentWindow(parent: BrowserWindow): void; + /** + * @returns The parent window. + */ + getParentWindow(): BrowserWindow; + /** + * @returns All child windows. + */ + getChildWindows(): BrowserWindow[]; + } + + type SwipeDirection = 'up' | 'right' | 'down' | 'left'; + + type ThumbarButtonFlags = 'enabled' | 'disabled' | 'dismissonclick' | 'nobackground' | 'hidden' | 'noninteractive'; + + interface ThumbarButton { + icon: NativeImage | string; + click: Function; + tooltip?: string; + flags?: ThumbarButtonFlags[]; + } + + interface DevToolsExtensions { + [name: string]: { + name: string; + value: string; + } + } + + interface WebPreferences { + /** + * Whether node integration is enabled. + * Default: true. + */ + nodeIntegration?: boolean; + /** + * Specifies a script that will be loaded before other scripts run in the page. + * This script will always have access to node APIs no matter whether node integration is turned on or off. + * The value should be the absolute file path to the script. + * When node integration is turned off, the preload script can reintroduce + * Node global symbols back to the global scope. + */ + preload?: string; + /** + * Sets the session used by the page. Instead of passing the Session object directly, + * you can also choose to use the partition option instead, which accepts a partition string. + * When both session and partition are provided, session would be preferred. + * Default: the default session. + */ + session?: Session; + /** + * Sets the session used by the page according to the session’s partition string. + * If partition starts with persist:, the page will use a persistent session available + * to all pages in the app with the same partition. if there is no persist: prefix, + * the page will use an in-memory session. By assigning the same partition, + * multiple pages can share the same session. + * Default: the default session. + */ + partition?: string; + /** + * The default zoom factor of the page, 3.0 represents 300%. + * Default: 1.0. + */ + zoomFactor?: number; + /** + * Enables JavaScript support. + * Default: true. + */ + javascript?: boolean; + /** + * When setting false, it will disable the same-origin policy (Usually using testing + * websites by people), and set allowDisplayingInsecureContent and allowRunningInsecureContent + * to true if these two options are not set by user. + * Default: true. + */ + webSecurity?: boolean; + /** + * Allow an https page to display content like images from http URLs. + * Default: false. + */ + allowDisplayingInsecureContent?: boolean; + /** + * Allow a https page to run JavaScript, CSS or plugins from http URLs. + * Default: false. + */ + allowRunningInsecureContent?: boolean; + /** + * Enables image support. + * Default: true. + */ + images?: boolean; + /** + * Make TextArea elements resizable. + * Default: true. + */ + textAreasAreResizable?: boolean; + /** + * Enables WebGL support. + * Default: true. + */ + webgl?: boolean; + /** + * Enables WebAudio support. + * Default: true. + */ + webaudio?: boolean; + /** + * Whether plugins should be enabled. + * Default: false. + */ + plugins?: boolean; + /** + * Enables Chromium’s experimental features. + * Default: false. + */ + experimentalFeatures?: boolean; + /** + * Enables Chromium’s experimental canvas features. + * Default: false. + */ + experimentalCanvasFeatures?: boolean; + /** + * Enables DirectWrite font rendering system on Windows. + * Default: true. + */ + directWrite?: boolean; + /** + * Enables scroll bounce (rubber banding) effect on macOS. + * Default: false. + */ + scrollBounce?: boolean; + /** + * A list of feature strings separated by ",", like CSSVariables,KeyboardEventKey to enable. + */ + blinkFeatures?: string; + /** + * A list of feature strings separated by ",", like CSSVariables,KeyboardEventKey to disable. + */ + disableBlinkFeatures?: string; + /** + * Sets the default font for the font-family. + */ + defaultFontFamily?: { + /** + * Default: Times New Roman. + */ + standard?: string; + /** + * Default: Times New Roman. + */ + serif?: string; + /** + * Default: Arial. + */ + sansSerif?: string; + /** + * Default: Courier New. + */ + monospace?: string; + }; + /** + * Default: 16. + */ + defaultFontSize?: number; + /** + * Default: 13. + */ + defaultMonospaceFontSize?: number; + /** + * Default: 0. + */ + minimumFontSize?: number; + /** + * Default: ISO-8859-1. + */ + defaultEncoding?: string; + /** + * Whether to throttle animations and timers when the page becomes background. + * Default: true + */ + backgroundThrottling?: boolean; + } + + interface BrowserWindowOptions extends Rectangle { + /** + * Window’s width in pixels. + * Default: 800. + */ + width?: number; + /** + * Window’s height in pixels. + * Default: 600. + */ + height?: number; + /** + * Window’s left offset from screen. + * Default: center the window. + */ + x?: number; + /** + * Window’s top offset from screen. + * Default: center the window. + */ + y?: number; + /** + * The width and height would be used as web page’s size, which means + * the actual window’s size will include window frame’s size and be slightly larger. + * Default: false. + */ + useContentSize?: boolean; + /** + * Show window in the center of the screen. + * Default: true + */ + center?: boolean; + /** + * Window’s minimum width. + * Default: 0. + */ + minWidth?: number; + /** + * Window’s minimum height. + * Default: 0. + */ + minHeight?: number; + /** + * Window’s maximum width. + * Default: no limit. + */ + maxWidth?: number; + /** + * Window’s maximum height. + * Default: no limit. + */ + maxHeight?: number; + /** + * Whether window is resizable. + * Default: true. + */ + resizable?: boolean; + /** + * Whether window is movable. + * Note: This is not implemented on Linux. + * Default: true. + */ + movable?: boolean; + /** + * Whether window is minimizable. + * Note: This is not implemented on Linux. + * Default: true. + */ + minimizable?: boolean; + /** + * Whether window is maximizable. + * Note: This is not implemented on Linux. + * Default: true. + */ + maximizable?: boolean; + /** + * Whether window is closable. + * Note: This is not implemented on Linux. + * Default: true. + */ + closable?: boolean; + /** + * Whether the window can be focused. + * On Windows setting focusable: false also implies setting skipTaskbar: true. + * On Linux setting focusable: false makes the window stop interacting with wm, + * so the window will always stay on top in all workspaces. + * Default: true. + */ + focusable?: boolean; + /** + * Whether the window should always stay on top of other windows. + * Default: false. + */ + alwaysOnTop?: boolean; + /** + * Whether the window should show in fullscreen. + * When explicitly set to false the fullscreen button will be hidden or disabled on macOS. + * Default: false. + */ + fullscreen?: boolean; + /** + * Whether the window can be put into fullscreen mode. + * On macOS, also whether the maximize/zoom button should toggle full screen mode or maximize window. + * Default: true. + */ + fullscreenable?: boolean; + /** + * Whether to show the window in taskbar. + * Default: false. + */ + skipTaskbar?: boolean; + /** + * The kiosk mode. + * Default: false. + */ + kiosk?: boolean; + /** + * Default window title. + * Default: "Electron". + */ + title?: string; + /** + * The window icon, when omitted on Windows the executable’s icon would be used as window icon. + */ + icon?: NativeImage|string; + /** + * Whether window should be shown when created. + * Default: true. + */ + show?: boolean; + /** + * Specify false to create a Frameless Window. + * Default: true. + */ + frame?: boolean; + /** + * Specify parent window. + * Default: null. + */ + parent?: BrowserWindow; + /** + * Whether this is a modal window. This only works when the window is a child window. + * Default: false. + */ + modal?: boolean; + /** + * Whether the web view accepts a single mouse-down event that simultaneously activates the window. + * Default: false. + */ + acceptFirstMouse?: boolean; + /** + * Whether to hide cursor when typing. + * Default: false. + */ + disableAutoHideCursor?: boolean; + /** + * Auto hide the menu bar unless the Alt key is pressed. + * Default: true. + */ + autoHideMenuBar?: boolean; + /** + * Enable the window to be resized larger than screen. + * Default: false. + */ + enableLargerThanScreen?: boolean; + /** + * Window’s background color as Hexadecimal value, like #66CD00 or #FFF or #80FFFFFF (alpha is supported). + * Default: #FFF (white). + */ + backgroundColor?: string; + /** + * Whether window should have a shadow. + * Note: This is only implemented on macOS. + * Default: true. + */ + hasShadow?: boolean; + /** + * Forces using dark theme for the window. + * Note: Only works on some GTK+3 desktop environments. + * Default: false. + */ + darkTheme?: boolean; + /** + * Makes the window transparent. + * Default: false. + */ + transparent?: boolean; + /** + * The type of window, default is normal window. + */ + type?: BrowserWindowType; + /** + * The style of window title bar. + */ + titleBarStyle?: 'default' | 'hidden' | 'hidden-inset'; + /** + * Use WS_THICKFRAME style for frameless windows on Windows + */ + thickFrame?: boolean; + /** + * Settings of web page’s features. + */ + webPreferences?: WebPreferences; + } + + type BrowserWindowType = BrowserWindowTypeLinux | BrowserWindowTypeMac | BrowserWindowTypeWindows; + type BrowserWindowTypeLinux = 'desktop' | 'dock' | 'toolbar' | 'splash' | 'notification'; + type BrowserWindowTypeMac = 'desktop' | 'textured'; + type BrowserWindowTypeWindows = 'toolbar'; + + interface Rectangle { + x?: number; + y?: number; + width?: number; + height?: number; + } + + // https://github.com/electron/electron/blob/master/docs/api/clipboard.md + + /** + * The clipboard module provides methods to perform copy and paste operations. + */ + interface Clipboard { + /** + * @returns The contents of the clipboard as plain text. + */ + readText(type?: ClipboardType): string; + /** + * Writes the text into the clipboard as plain text. + */ + writeText(text: string, type?: ClipboardType): void; + /** + * @returns The contents of the clipboard as markup. + */ + readHTML(type?: ClipboardType): string; + /** + * Writes markup to the clipboard. + */ + writeHTML(markup: string, type?: ClipboardType): void; + /** + * @returns The contents of the clipboard as a NativeImage. + */ + readImage(type?: ClipboardType): NativeImage; + /** + * Writes the image into the clipboard. + */ + writeImage(image: NativeImage, type?: ClipboardType): void; + /** + * @returns The contents of the clipboard as RTF. + */ + readRTF(type?: ClipboardType): string; + /** + * Writes the text into the clipboard in RTF. + */ + writeRTF(text: string, type?: ClipboardType): void; + /** + * Clears everything in clipboard. + */ + clear(type?: ClipboardType): void; + /** + * @returns Array available formats for the clipboard type. + */ + availableFormats(type?: ClipboardType): string[]; + /** + * Returns whether the clipboard supports the format of specified data. + * Note: This API is experimental and could be removed in future. + * @returns Whether the clipboard has data in the specified format. + */ + has(format: string, type?: ClipboardType): boolean; + /** + * Reads the data in the clipboard of the specified format. + * Note: This API is experimental and could be removed in future. + */ + read(format: string, type?: ClipboardType): string | NativeImage; + /** + * Writes data to the clipboard. + */ + write(data: { + text?: string; + rtf?: string; + html?: string; + image?: NativeImage; + }, type?: ClipboardType): void; + /** + * @returns An Object containing title and url keys representing the bookmark in the clipboard. + * + * Note: This API is available on macOS and Windows. + */ + readBookmark(): Bookmark; + /** + * Writes the title and url into the clipboard as a bookmark. + * + * Note: This API is available on macOS and Windows. + */ + writeBookmark(title: string, url: string, type?: ClipboardType): void; + } + + type ClipboardType = '' | 'selection'; + + interface Bookmark { + title: string; + url: string; + } + + // https://github.com/electron/electron/blob/master/docs/api/content-tracing.md + + /** + * The content-tracing module is used to collect tracing data generated by the underlying Chromium content module. + * This module does not include a web interface so you need to open chrome://tracing/ + * in a Chrome browser and load the generated file to view the result. + */ + interface ContentTracing { + /** + * Get a set of category groups. The category groups can change as new code paths are reached. + * + * @param callback Called once all child processes have acknowledged the getCategories request. + */ + getCategories(callback: (categoryGroups: string[]) => void): void; + /** + * Start recording on all processes. Recording begins immediately locally and asynchronously + * on child processes as soon as they receive the EnableRecording request. + * + * @param callback Called once all child processes have acknowledged the startRecording request. + */ + startRecording(options: ContentTracingOptions, callback: Function): void; + /** + * Stop recording on all processes. Child processes typically are caching trace data and + * only rarely flush and send trace data back to the main process. That is because it may + * be an expensive operation to send the trace data over IPC, and we would like to avoid + * much runtime overhead of tracing. So, to end tracing, we must asynchronously ask all + * child processes to flush any pending trace data. + * + * @param resultFilePath Trace data will be written into this file if it is not empty, + * or into a temporary file. + * @param callback Called once all child processes have acknowledged the stopRecording request. + */ + stopRecording(resultFilePath: string, callback: (filePath: string) => void): void; + /** + * Start monitoring on all processes. Monitoring begins immediately locally and asynchronously + * on child processes as soon as they receive the startMonitoring request. + * + * @param callback Called once all child processes have acked to the startMonitoring request. + */ + startMonitoring(options: ContentTracingOptions, callback: Function): void; + /** + * Stop monitoring on all processes. + * + * @param callback Called once all child processes have acknowledged the stopMonitoring request. + */ + stopMonitoring(callback: Function): void; + /** + * Get the current monitoring traced data. Child processes typically are caching trace data + * and only rarely flush and send trace data back to the main process. That is because it may + * be an expensive operation to send the trace data over IPC, and we would like to avoid much + * runtime overhead of tracing. So, to end tracing, we must asynchronously ask all child + * processes to flush any pending trace data. + * + * @param callback Called once all child processes have acknowledged the captureMonitoringSnapshot request. + */ + captureMonitoringSnapshot(resultFilePath: string, callback: (filePath: string) => void): void; + /** + * Get the maximum usage across processes of trace buffer as a percentage of the full state. + * + * @param callback Called when the TraceBufferUsage value is determined. + */ + getTraceBufferUsage(callback: Function): void; + /** + * @param callback Called every time the given event occurs on any process. + */ + setWatchEvent(categoryName: string, eventName: string, callback: Function): void; + /** + * Cancel the watch event. This may lead to a race condition with the watch event callback if tracing is enabled. + */ + cancelWatchEvent(): void; + } + + interface ContentTracingOptions { + /** + * Filter to control what category groups should be traced. + * A filter can have an optional - prefix to exclude category groups + * that contain a matching category. Having both included and excluded + * category patterns in the same list is not supported. + * + * Examples: + * test_MyTest* + * test_MyTest*,test_OtherStuff + * -excluded_category1,-excluded_category2 + */ + categoryFilter: string; + /** + * Controls what kind of tracing is enabled, it is a comma-delimited list. + * + * Possible options are: + * record-until-full + * record-continuously + * trace-to-console + * enable-sampling + * enable-systrace + * + * The first 3 options are trace recoding modes and hence mutually exclusive. + * If more than one trace recording modes appear in the traceOptions string, + * the last one takes precedence. If none of the trace recording modes are specified, + * recording mode is record-until-full. + * + * The trace option will first be reset to the default option (record_mode set + * to record-until-full, enable_sampling and enable_systrace set to false) + * before options parsed from traceOptions are applied on it. + */ + traceOptions: string; + } + + // https://github.com/electron/electron/blob/master/docs/api/crash-reporter.md + + /** + * The crash-reporter module enables sending your app's crash reports. + */ + interface CrashReporter { + /** + * You are required to call this method before using other crashReporter APIs. + * + * Note: On macOS, Electron uses a new crashpad client, which is different from breakpad + * on Windows and Linux. To enable the crash collection feature, you are required to call + * the crashReporter.start API to initialize crashpad in the main process and in each + * renderer process from which you wish to collect crash reports. + */ + start(options: CrashReporterStartOptions): void; + /** + * @returns The crash report. When there was no crash report + * sent or the crash reporter is not started, null will be returned. + */ + getLastCrashReport(): CrashReport; + /** + * @returns All uploaded crash reports. + */ + getUploadedReports(): CrashReport[]; + } + + interface CrashReporterStartOptions { + /** + * Default: Electron + */ + productName?: string; + companyName: string; + /** + * URL that crash reports would be sent to as POST. + */ + submitURL: string; + /** + * Send the crash report without user interaction. + * Default: true. + */ + autoSubmit?: boolean; + /** + * Default: false. + */ + ignoreSystemCrashHandler?: boolean; + /** + * An object you can define that will be sent along with the report. + * Only string properties are sent correctly, nested objects are not supported. + */ + extra?: {[prop: string]: string}; + } + + interface CrashReport { + id: string; + date: Date; + } + + // https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md + + /** + * The desktopCapturer module can be used to get available sources + * that can be used to be captured with getUserMedia. + */ + interface DesktopCapturer { + /** + * Starts a request to get all desktop sources. + * + * Note: There is no guarantee that the size of source.thumbnail is always + * the same as the thumnbailSize in options. It also depends on the scale of the screen or window. + */ + getSources(options: DesktopCapturerOptions, callback: (error: Error, sources: DesktopCapturerSource[]) => any): void; + } + + interface DesktopCapturerOptions { + /** + * The types of desktop sources to be captured. + */ + types?: ('screen' | 'window')[]; + /** + * The suggested size that thumbnail should be scaled. + * Default: {width: 150, height: 150} + */ + thumbnailSize?: Dimension; + } + + interface DesktopCapturerSource { + /** + * The id of the captured window or screen used in navigator.webkitGetUserMedia. + * The format looks like window:XX or screen:XX where XX is a random generated number. + */ + id: string; + /** + * The described name of the capturing screen or window. + * If the source is a screen, the name will be Entire Screen or Screen ; + * if it is a window, the name will be the window’s title. + */ + name: string; + /** + * A thumbnail image. + */ + thumbnail: NativeImage; + } + + // https://github.com/electron/electron/blob/master/docs/api/dialog.md + + /** + * The dialog module provides APIs to show native system dialogs, such as opening files or alerting, + * so web applications can deliver the same user experience as native applications. + */ + interface Dialog { + /** + * Note: On Windows and Linux an open dialog can not be both a file selector and a directory selector, + * so if you set properties to ['openFile', 'openDirectory'] on these platforms, a directory selector will be shown. + * + * @param callback If supplied, the API call will be asynchronous. + * @returns On success, returns an array of file paths chosen by the user, + * otherwise returns undefined. + */ + showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: (fileNames: string[]) => void): string[]; + /** + * Note: On Windows and Linux an open dialog can not be both a file selector and a directory selector, + * so if you set properties to ['openFile', 'openDirectory'] on these platforms, a directory selector will be shown. + * + * @param callback If supplied, the API call will be asynchronous. + * @returns On success, returns an array of file paths chosen by the user, + * otherwise returns undefined. + */ + showOpenDialog(options: OpenDialogOptions, callback?: (fileNames: string[]) => void): string[]; + /** + * @param callback If supplied, the API call will be asynchronous. + * @returns On success, returns the path of file chosen by the user, otherwise + * returns undefined. + */ + showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions, callback?: (fileName: string) => void): string; + /** + * @param callback If supplied, the API call will be asynchronous. + * @returns On success, returns the path of file chosen by the user, otherwise + * returns undefined. + */ + showSaveDialog(options: SaveDialogOptions, callback?: (fileName: string) => void): string; + /** + * Shows a message box. It will block until the message box is closed. + * @param callback If supplied, the API call will be asynchronous. + * @returns The index of the clicked button. + */ + showMessageBox(browserWindow: BrowserWindow, options: ShowMessageBoxOptions, callback?: (response: number) => void): number; + /** + * Shows a message box. It will block until the message box is closed. + * @param callback If supplied, the API call will be asynchronous. + * @returns The index of the clicked button. + */ + showMessageBox(options: ShowMessageBoxOptions, callback?: (response: number) => void): number; + /** + * Displays a modal dialog that shows an error message. + * + * This API can be called safely before the ready event the app module emits, + * it is usually used to report errors in early stage of startup. + * If called before the app readyevent on Linux, the message will be emitted to stderr, + * and no GUI dialog will appear. + */ + showErrorBox(title: string, content: string): void; + } + + interface OpenDialogOptions { + title?: string; + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will be used. + */ + buttonLabel?: string; + /** + * File types that can be displayed or selected. + */ + filters?: { + name: string; + /** + * Extensions without wildcards or dots (e.g. 'png' is good but '.png' and '*.png' are bad). + * To show all files, use the '*' wildcard (no other wildcard is supported). + */ + extensions: string[]; + }[]; + /** + * Contains which features the dialog should use. + */ + properties?: ('openFile' | 'openDirectory' | 'multiSelections' | 'createDirectory' | 'showHiddenFiles')[]; + } + + interface SaveDialogOptions { + title?: string; + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will be used. + */ + buttonLabel?: string; + /** + * File types that can be displayed, see dialog.showOpenDialog for an example. + */ + filters?: { + name: string; + extensions: string[]; + }[]; + } + + interface ShowMessageBoxOptions { + /** + * On Windows, "question" displays the same icon as "info", unless you set an icon using the "icon" option. + */ + type?: 'none' | 'info' | 'error' | 'question' | 'warning'; + /** + * Texts for buttons. On Windows, an empty array will result in one button labeled "OK". + */ + buttons?: string[]; + /** + * Index of the button in the buttons array which will be selected by default when the message box opens. + */ + defaultId?: number; + /** + * Title of the message box (some platforms will not show it). + */ + title?: string; + /** + * Contents of the message box. + */ + message?: string; + /** + * Extra information of the message. + */ + detail?: string; + icon?: NativeImage; + /** + * The value will be returned when user cancels the dialog instead of clicking the buttons of the dialog. + * By default it is the index of the buttons that have "cancel" or "no" as label, + * or 0 if there is no such buttons. On macOS and Windows the index of "Cancel" button + * will always be used as cancelId, not matter whether it is already specified. + */ + cancelId?: number; + /** + * On Windows Electron will try to figure out which one of the buttons are common buttons + * (like "Cancel" or "Yes"), and show the others as command links in the dialog. + * This can make the dialog appear in the style of modern Windows apps. + * If you don’t like this behavior, you can set noLink to true. + */ + noLink?: boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/download-item.md + + /** + * DownloadItem represents a download item in Electron. + */ + interface DownloadItem extends NodeJS.EventEmitter { + /** + * Emitted when the download has been updated and is not done. + */ + on(event: 'updated', listener: (event: Event, state: 'progressing' | 'interrupted') => void): this; + /** + * Emits when the download is in a terminal state. This includes a completed download, + * a cancelled download (via downloadItem.cancel()), and interrupted download that can’t be resumed. + */ + on(event: 'done', listener: (event: Event, state: 'completed' | 'cancelled' | 'interrupted') => void): this; + on(event: string, listener: Function): this; + /** + * Set the save file path of the download item. + * Note: The API is only available in session’s will-download callback function. + * If user doesn’t set the save path via the API, Electron will use the original + * routine to determine the save path (Usually prompts a save dialog). + */ + setSavePath(path: string): void; + /** + * Pauses the download. + */ + pause(): void; + /** + * @returns Whether the download is paused. + */ + isPaused(): boolean; + /** + * Resumes the download that has been paused. + */ + resume(): void; + /** + * @returns Whether the download can resume. + */ + canResume(): boolean; + /** + * Cancels the download operation. + */ + cancel(): void; + /** + * @returns The origin url where the item is downloaded from. + */ + getURL(): string; + /** + * @returns The mime type. + */ + getMimeType(): string; + /** + * @returns Whether the download has user gesture. + */ + hasUserGesture(): boolean; + /** + * @returns The file name of the download item. + * Note: The file name is not always the same as the actual one saved in local disk. + * If user changes the file name in a prompted download saving dialog, + * the actual name of saved file will be different. + */ + getFilename(): string; + /** + * @returns The total size in bytes of the download item. If the size is unknown, it returns 0. + */ + getTotalBytes(): number; + /** + * @returns The received bytes of the download item. + */ + getReceivedBytes(): number; + /** + * @returns The Content-Disposition field from the response header. + */ + getContentDisposition(): string; + /** + * @returns The current state. + */ + getState(): 'progressing' | 'completed' | 'cancelled' | 'interrupted'; + } + + // https://github.com/electron/electron/blob/master/docs/api/global-shortcut.md + + /** + * The globalShortcut module can register/unregister a global keyboard shortcut + * with the operating system so that you can customize the operations for various shortcuts. + * Note: The shortcut is global; it will work even if the app does not have the keyboard focus. + * You should not use this module until the ready event of the app module is emitted. + */ + interface GlobalShortcut { + /** + * Registers a global shortcut of accelerator. + * @param accelerator Represents a keyboard shortcut. It can contain modifiers + * and key codes, combined by the "+" character. + * @param callback Called when the registered shortcut is pressed by the user. + */ + register(accelerator: string, callback: Function): void; + /** + * @param accelerator Represents a keyboard shortcut. It can contain modifiers + * and key codes, combined by the "+" character. + * @returns Whether the accelerator is registered. + */ + isRegistered(accelerator: string): boolean; + /** + * Unregisters the global shortcut of keycode. + * @param accelerator Represents a keyboard shortcut. It can contain modifiers + * and key codes, combined by the "+" character. + */ + unregister(accelerator: string): void; + /** + * Unregisters all the global shortcuts. + */ + unregisterAll(): void; + } + + // https://github.com/electron/electron/blob/master/docs/api/ipc-main.md + + /** + * The ipcMain module handles asynchronous and synchronous messages + * sent from a renderer process (web page). + * Messages sent from a renderer will be emitted to this module. + */ + interface IpcMain extends NodeJS.EventEmitter { + addListener(channel: string, listener: IpcMainEventListener): this; + on(channel: string, listener: IpcMainEventListener): this; + once(channel: string, listener: IpcMainEventListener): this; + removeListener(channel: string, listener: IpcMainEventListener): this; + removeAllListeners(channel?: string): this; + } + + type IpcMainEventListener = (event: IpcMainEvent, ...args: any[]) => void; + + interface IpcMainEvent { + /** + * Set this to the value to be returned in a synchronous message. + */ + returnValue?: any; + /** + * Returns the webContents that sent the message, you can call sender.send + * to reply to the asynchronous message. + */ + sender: WebContents; + } + + // https://github.com/electron/electron/blob/master/docs/api/ipc-renderer.md + + /** + * The ipcRenderer module provides a few methods so you can send synchronous + * and asynchronous messages from the render process (web page) to the main process. + * You can also receive replies from the main process. + */ + interface IpcRenderer extends NodeJS.EventEmitter { + addListener(channel: string, listener: IpcRendererEventListener): this; + on(channel: string, listener: IpcRendererEventListener): this; + once(channel: string, listener: IpcRendererEventListener): this; + removeListener(channel: string, listener: IpcRendererEventListener): this; + removeAllListeners(channel?: string): this; + /** + * Send ...args to the renderer via channel in asynchronous message, the main + * process can handle it by listening to the channel event of ipc module. + */ + send(channel: string, ...args: any[]): void; + /** + * Send ...args to the renderer via channel in synchronous message, and returns + * the result sent from main process. The main process can handle it by listening + * to the channel event of ipc module, and returns by setting event.returnValue. + * Note: Usually developers should never use this API, since sending synchronous + * message would block the whole renderer process. + * @returns The result sent from the main process. + */ + sendSync(channel: string, ...args: any[]): any; + /** + * Like ipc.send but the message will be sent to the host page instead of the main process. + * This is mainly used by the page in to communicate with host page. + */ + sendToHost(channel: string, ...args: any[]): void; + } + + type IpcRendererEventListener = (event: IpcRendererEvent, ...args: any[]) => void; + + interface IpcRendererEvent { + /** + * You can call sender.send to reply to the asynchronous message. + */ + sender: IpcRenderer; + } + + // https://github.com/electron/electron/blob/master/docs/api/menu-item.md + // https://github.com/electron/electron/blob/master/docs/api/accelerator.md + + /** + * The MenuItem allows you to add items to an application or context menu. + */ + class MenuItem { + /** + * Create a new menu item. + */ + constructor(options: MenuItemOptions); + + click: (menuItem: MenuItem, browserWindow: BrowserWindow, event: Event) => void; + /** + * Read-only property. + */ + type: MenuItemType; + /** + * Read-only property. + */ + role: MenuItemRole | MenuItemRoleMac; + /** + * Read-only property. + */ + accelerator: string; + /** + * Read-only property. + */ + icon: NativeImage | string; + /** + * Read-only property. + */ + submenu: Menu | MenuItemOptions[]; + + label: string; + sublabel: string; + enabled: boolean; + visible: boolean; + checked: boolean; + } + + type MenuItemType = 'normal' | 'separator' | 'submenu' | 'checkbox' | 'radio'; + type MenuItemRole = 'undo' | 'redo' | 'cut' | 'copy' | 'paste' | 'pasteandmatchstyle' | 'selectall' | 'delete' | 'minimize' | 'close' | 'quit' | 'togglefullscreen'; + type MenuItemRoleMac = 'about' | 'hide' | 'hideothers' | 'unhide' | 'front' | 'zoom' | 'window' | 'help' | 'services'; + + interface MenuItemOptions { + /** + * Callback when the menu item is clicked. + */ + click?: (menuItem: MenuItem, browserWindow: BrowserWindow) => void; + /** + * Can be normal, separator, submenu, checkbox or radio. + */ + type?: MenuItemType; + label?: string; + sublabel?: string; + /** + * An accelerator is string that represents a keyboard shortcut, it can contain + * multiple modifiers and key codes, combined by the + character. + * + * Examples: + * CommandOrControl+A + * CommandOrControl+Shift+Z + * + * Platform notice: + * On Linux and Windows, the Command key would not have any effect, + * you can use CommandOrControl which represents Command on macOS and Control on + * Linux and Windows to define some accelerators. + * + * Use Alt instead of Option. The Option key only exists on macOS, whereas + * the Alt key is available on all platforms. + * + * The Super key is mapped to the Windows key on Windows and Linux and Cmd on macOS. + * + * Available modifiers: + * Command (or Cmd for short) + * Control (or Ctrl for short) + * CommandOrControl (or CmdOrCtrl for short) + * Alt + * Option + * AltGr + * Shift + * Super + * + * Available key codes: + * 0 to 9 + * A to Z + * F1 to F24 + * Punctuations like ~, !, @, #, $, etc. + * Plus + * Space + * Tab + * Backspace + * Delete + * Insert + * Return (or Enter as alias) + * Up, Down, Left and Right + * Home and End + * PageUp and PageDown + * Escape (or Esc for short) + * VolumeUp, VolumeDown and VolumeMute + * MediaNextTrack, MediaPreviousTrack, MediaStop and MediaPlayPause + * PrintScreen + */ + accelerator?: string; + /** + * In Electron for the APIs that take images, you can pass either file paths + * or NativeImage instances. When passing null, an empty image will be used. + */ + icon?: NativeImage|string; + /** + * If false, the menu item will be greyed out and unclickable. + */ + enabled?: boolean; + /** + * If false, the menu item will be entirely hidden. + */ + visible?: boolean; + /** + * Should only be specified for 'checkbox' or 'radio' type menu items. + */ + checked?: boolean; + /** + * Should be specified for submenu type menu item, when it's specified the + * type: 'submenu' can be omitted for the menu item + */ + submenu?: Menu|MenuItemOptions[]; + /** + * Unique within a single menu. If defined then it can be used as a reference + * to this item by the position attribute. + */ + id?: string; + /** + * This field allows fine-grained definition of the specific location within + * a given menu. + */ + position?: string; + /** + * Define the action of the menu item, when specified the click property will be ignored + */ + role?: MenuItemRole | MenuItemRoleMac; + } + + // https://github.com/electron/electron/blob/master/docs/api/menu.md + + /** + * The Menu class is used to create native menus that can be used as application + * menus and context menus. This module is a main process module which can be used + * in a render process via the remote module. + * + * Each menu consists of multiple menu items, and each menu item can have a submenu. + */ + class Menu extends EventEmitter { + /** + * Creates a new menu. + */ + constructor(); + /** + * Sets menu as the application menu on macOS. On Windows and Linux, the menu + * will be set as each window's top menu. + */ + static setApplicationMenu(menu: Menu): void; + /** + * @returns The application menu if set, or null if not set. + */ + static getApplicationMenu(): Menu; + /** + * Sends the action to the first responder of application. + * This is used for emulating default Cocoa menu behaviors, + * usually you would just use the role property of MenuItem. + * + * Note: This method is macOS only. + */ + static sendActionToFirstResponder(action: string): void; + /** + * @param template Generally, just an array of options for constructing MenuItem. + * You can also attach other fields to element of the template, and they will + * become properties of the constructed menu items. + */ + static buildFromTemplate(template: MenuItemOptions[]): Menu; + /** + * Pops up this menu as a context menu in the browserWindow. You can optionally + * provide a (x,y) coordinate to place the menu at, otherwise it will be placed + * at the current mouse cursor position. + * @param x Horizontal coordinate where the menu will be placed. + * @param y Vertical coordinate where the menu will be placed. + */ + popup(browserWindow?: BrowserWindow, x?: number, y?: number): void; + /** + * Appends the menuItem to the menu. + */ + append(menuItem: MenuItem): void; + /** + * Inserts the menuItem to the pos position of the menu. + */ + insert(position: number, menuItem: MenuItem): void; + /** + * @returns an array containing the menu’s items. + */ + items: MenuItem[]; + } + + // https://github.com/electron/electron/blob/master/docs/api/native-image.md + + /** + * This class is used to represent an image. + */ + class NativeImage { + /** + * Creates an empty NativeImage instance. + */ + static createEmpty(): NativeImage; + /** + * Creates a new NativeImage instance from file located at path. + */ + static createFromPath(path: string): NativeImage; + /** + * Creates a new NativeImage instance from buffer. + * @param scaleFactor 1.0 by default. + */ + static createFromBuffer(buffer: Buffer, scaleFactor?: number): NativeImage; + /** + * Creates a new NativeImage instance from dataURL + */ + static createFromDataURL(dataURL: string): NativeImage; + /** + * @returns Buffer Contains the image's PNG encoded data. + */ + toPNG(): Buffer; + /** + * @returns Buffer Contains the image's JPEG encoded data. + */ + toJPEG(quality: number): Buffer; + /** + * @returns string The data URL of the image. + */ + toDataURL(): string; + /** + * The native type of the handle is NSImage* on macOS. + * Note: This is only implemented on macOS. + * @returns The platform-specific handle of the image as Buffer. + */ + getNativeHandle(): Buffer; + /** + * @returns boolean Whether the image is empty. + */ + isEmpty(): boolean; + /** + * @returns {} The size of the image. + */ + getSize(): Dimension; + /** + * Marks the image as template image. + */ + setTemplateImage(option: boolean): void; + /** + * Returns a boolean whether the image is a template image. + */ + isTemplateImage(): boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/power-monitor.md + + /** + * The power-monitor module is used to monitor power state changes. + * You should not use this module until the ready event of the app module is emitted. + */ + interface PowerMonitor extends NodeJS.EventEmitter { + /** + * Emitted when the system is suspending. + */ + on(event: 'suspend', listener: Function): this; + /** + * Emitted when system is resuming. + */ + on(event: 'resume', listener: Function): this; + /** + * Emitted when the system changes to AC power. + */ + on(event: 'on-ac', listener: Function): this; + /** + * Emitted when system changes to battery power. + */ + on(event: 'on-battery', listener: Function): this; + on(event: string, listener: Function): this; + } + + // https://github.com/electron/electron/blob/master/docs/api/power-save-blocker.md + + /** + * The powerSaveBlocker module is used to block the system from entering + * low-power (sleep) mode and thus allowing the app to keep the system and screen active. + */ + interface PowerSaveBlocker { + /** + * Starts preventing the system from entering lower-power mode. + * @returns an integer identifying the power save blocker. + * Note: prevent-display-sleep has higher has precedence over prevent-app-suspension. + */ + start(type: 'prevent-app-suspension' | 'prevent-display-sleep'): number; + /** + * @param id The power save blocker id returned by powerSaveBlocker.start. + * Stops the specified power save blocker. + */ + stop(id: number): void; + /** + * @param id The power save blocker id returned by powerSaveBlocker.start. + * @returns a boolean whether the corresponding powerSaveBlocker has started. + */ + isStarted(id: number): boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/protocol.md + + /** + * The protocol module can register a custom protocol or intercept an existing protocol. + */ + interface Protocol { + /** + * Registers custom schemes as standard schemes. + */ + registerStandardSchemes(schemes: string[]): void; + /** + * Registers custom schemes to handle service workers. + */ + registerServiceWorkerSchemes(schemes: string[]): void; + /** + * Registers a protocol of scheme that will send the file as a response. + */ + registerFileProtocol(scheme: string, handler: FileProtocolHandler, completion?: (error: Error) => void): void; + /** + * Registers a protocol of scheme that will send a Buffer as a response. + */ + registerBufferProtocol(scheme: string, handler: BufferProtocolHandler, completion?: (error: Error) => void): void; + /** + * Registers a protocol of scheme that will send a String as a response. + */ + registerStringProtocol(scheme: string, handler: StringProtocolHandler, completion?: (error: Error) => void): void; + /** + * Registers a protocol of scheme that will send an HTTP request as a response. + */ + registerHttpProtocol(scheme: string, handler: HttpProtocolHandler, completion?: (error: Error) => void): void; + /** + * Unregisters the custom protocol of scheme. + */ + unregisterProtocol(scheme: string, completion?: (error: Error) => void): void; + /** + * The callback will be called with a boolean that indicates whether there is already a handler for scheme. + */ + isProtocolHandled(scheme: string, callback: (handled: boolean) => void): void; + /** + * Intercepts scheme protocol and uses handler as the protocol’s new handler which sends a file as a response. + */ + interceptFileProtocol(scheme: string, handler: FileProtocolHandler, completion?: (error: Error) => void): void; + /** + * Intercepts scheme protocol and uses handler as the protocol’s new handler which sends a Buffer as a response. + */ + interceptBufferProtocol(scheme: string, handler: BufferProtocolHandler, completion?: (error: Error) => void): void; + /** + * Intercepts scheme protocol and uses handler as the protocol’s new handler which sends a String as a response. + */ + interceptStringProtocol(scheme: string, handler: StringProtocolHandler, completion?: (error: Error) => void): void; + /** + * Intercepts scheme protocol and uses handler as the protocol’s new handler which sends a new HTTP request as a response. + */ + interceptHttpProtocol(scheme: string, handler: HttpProtocolHandler, completion?: (error: Error) => void): void; + /** + * Remove the interceptor installed for scheme and restore its original handler. + */ + uninterceptProtocol(scheme: string, completion?: (error: Error) => void): void; + } + + type FileProtocolHandler = (request: ProtocolRequest, callback: FileProtocolCallback) => void; + type BufferProtocolHandler = (request: ProtocolRequest, callback: BufferProtocolCallback) => void; + type StringProtocolHandler = (request: ProtocolRequest, callback: StringProtocolCallback) => void; + type HttpProtocolHandler = (request: ProtocolRequest, callback: HttpProtocolCallback) => void; + + interface ProtocolRequest { + url: string; + referrer: string; + method: string; + uploadData?: { + bytes: Buffer, + file: string + }[]; + } + + interface ProtocolCallback { + (error: number): void; + (obj: { + error: number + }): void; + (): void; + } + + interface FileProtocolCallback extends ProtocolCallback { + (filePath: string): void; + (obj: { + path: string + }): void; + } + + interface BufferProtocolCallback extends ProtocolCallback { + (buffer: Buffer): void; + (obj: { + data: Buffer, + mimeType: string, + charset?: string + }): void; + } + + interface StringProtocolCallback extends ProtocolCallback { + (str: string): void; + (obj: { + data: Buffer, + mimeType: string, + charset?: string + }): void; + } + + interface HttpProtocolCallback extends ProtocolCallback { + (redirectRequest: { + url: string; + method: string; + session?: Object; + uploadData?: { + contentType: string; + data: string; + }; + }): void; + } + + // https://github.com/electron/electron/blob/master/docs/api/remote.md + + /** + * The remote module provides a simple way to do inter-process communication (IPC) + * between the renderer process (web page) and the main process. + */ + interface Remote extends CommonElectron { + /** + * @returns The object returned by require(module) in the main process. + */ + require(module: string): any; + /** + * @returns The BrowserWindow object which this web page belongs to. + */ + getCurrentWindow(): BrowserWindow; + /** + * @returns The WebContents object of this web page. + */ + getCurrentWebContents(): WebContents; + /** + * @returns The global variable of name (e.g. global[name]) in the main process. + */ + getGlobal(name: string): any; + /** + * Returns the process object in the main process. This is the same as + * remote.getGlobal('process'), but gets cached. + */ + process: NodeJS.Process; + } + + // https://github.com/electron/electron/blob/master/docs/api/screen.md + + /** + * The Display object represents a physical display connected to the system. + * A fake Display may exist on a headless system, or a Display may correspond to a remote, virtual display. + */ + interface Display { + /** + * Unique identifier associated with the display. + */ + id: number; + bounds: Bounds; + workArea: Bounds; + size: Dimension; + workAreaSize: Dimension; + /** + * Output device’s pixel scale factor. + */ + scaleFactor: number; + /** + * Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees. + */ + rotation: number; + touchSupport: 'available' | 'unavailable' | 'unknown'; + } + + type Bounds = { + x: number; + y: number; + width: number; + height: number; + } + + type Dimension = { + width: number; + height: number; + } + + type Point = { + x: number; + y: number; + } + + type DisplayMetrics = 'bounds' | 'workArea' | 'scaleFactor' | 'rotation'; + + /** + * The screen module retrieves information about screen size, displays, cursor position, etc. + * You can not use this module until the ready event of the app module is emitted. + */ + interface Screen extends NodeJS.EventEmitter { + /** + * Emitted when newDisplay has been added. + */ + on(event: 'display-added', listener: (event: Event, newDisplay: Display) => void): this; + /** + * Emitted when oldDisplay has been removed. + */ + on(event: 'display-removed', listener: (event: Event, oldDisplay: Display) => void): this; + /** + * Emitted when one or more metrics change in a display. + */ + on(event: 'display-metrics-changed', listener: (event: Event, display: Display, changedMetrics: DisplayMetrics[]) => void): this; + on(event: string, listener: Function): this; + /** + * @returns The current absolute position of the mouse pointer. + */ + getCursorScreenPoint(): Point; + /** + * @returns The primary display. + */ + getPrimaryDisplay(): Display; + /** + * @returns An array of displays that are currently available. + */ + getAllDisplays(): Display[]; + /** + * @returns The display nearest the specified point. + */ + getDisplayNearestPoint(point: Point): Display; + /** + * @returns The display that most closely intersects the provided bounds. + */ + getDisplayMatching(rect: Bounds): Display; + } + + // https://github.com/electron/electron/blob/master/docs/api/session.md + + /** + * The session module can be used to create new Session objects. + * You can also access the session of existing pages by using + * the session property of webContents which is a property of BrowserWindow. + */ + class Session extends EventEmitter { + /** + * @returns a new Session instance from partition string. + */ + static fromPartition(partition: string, options?: FromPartitionOptions): Session; + /** + * @returns the default session object of the app. + */ + static defaultSession: Session; + /** + * Emitted when Electron is about to download item in webContents. + * Calling event.preventDefault() will cancel the download + * and item will not be available from next tick of the process. + */ + on(event: 'will-download', listener: (event: Event, item: DownloadItem, webContents: WebContents) => void): this; + on(event: string, listener: Function): this; + /** + * The cookies gives you ability to query and modify cookies. + */ + cookies: SessionCookies; + /** + * @returns the session’s current cache size. + */ + getCacheSize(callback: (size: number) => void): void; + /** + * Clears the session’s HTTP cache. + */ + clearCache(callback: Function): void; + /** + * Clears the data of web storages. + */ + clearStorageData(callback: Function): void; + /** + * Clears the data of web storages. + */ + clearStorageData(options: ClearStorageDataOptions, callback: Function): void; + /** + * Writes any unwritten DOMStorage data to disk. + */ + flushStorageData(): void; + /** + * Sets the proxy settings. + */ + setProxy(config: ProxyConfig, callback: Function): void; + /** + * Resolves the proxy information for url. + */ + resolveProxy(url: URL, callback: (proxy: string) => void): void; + /** + * Sets download saving directory. + * By default, the download directory will be the Downloads under the respective app folder. + */ + setDownloadPath(path: string): void; + /** + * Emulates network with the given configuration for the session. + */ + enableNetworkEmulation(options: NetworkEmulationOptions): void; + /** + * Disables any network emulation already active for the session. + * Resets to the original network configuration. + */ + disableNetworkEmulation(): void; + /** + * Sets the certificate verify proc for session, the proc will be called + * whenever a server certificate verification is requested. + * + * Calling setCertificateVerifyProc(null) will revert back to default certificate verify proc. + */ + setCertificateVerifyProc(proc: (hostname: string, cert: Certificate, callback: (accepted: boolean) => void) => void): void; + /** + * Sets the handler which can be used to respond to permission requests for the session. + */ + setPermissionRequestHandler(handler: (webContents: WebContents, permission: Permission, callback: (allow: boolean) => void) => void): void; + /** + * Clears the host resolver cache. + */ + clearHostResolverCache(callback: Function): void; + /** + * Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate authentication. + * @param domains Comma-seperated list of servers for which integrated authentication is enabled. + */ + allowNTLMCredentialsForDomains(domains: string): void; + /** + * Overrides the userAgent and acceptLanguages for this session. + * The acceptLanguages must a comma separated ordered list of language codes, for example "en-US,fr,de,ko,zh-CN,ja". + * This doesn't affect existing WebContents, and each WebContents can use webContents.setUserAgent to override the session-wide user agent. + */ + setUserAgent(userAgent: string, acceptLanguages?: string): void; + /** + * @returns The user agent for this session. + */ + getUserAgent(): string; + /** + * The webRequest API set allows to intercept and modify contents of a request at various stages of its lifetime. + */ + webRequest: WebRequest; + /** + * @returns An instance of protocol module for this session. + */ + protocol: Protocol; + } + + type Permission = 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal'; + + interface FromPartitionOptions { + /** + * Whether to enable cache. + */ + cache?: boolean; + } + + interface ClearStorageDataOptions { + /** + * Should follow window.location.origin’s representation scheme://host:port. + */ + origin?: string; + /** + * The types of storages to clear. + */ + storages?: ('appcache' | 'cookies' | 'filesystem' | 'indexdb' | 'localstorage' | 'shadercache' | 'websql' | 'serviceworkers')[]; + /** + * The types of quotas to clear. + */ + quotas?: ('temporary' | 'persistent' | 'syncable')[]; + } + + interface ProxyConfig { + /** + * The URL associated with the PAC file. + */ + pacScript: string; + /** + * Rules indicating which proxies to use. + */ + proxyRules: string; + /** + * Rules indicating which URLs should bypass the proxy settings. + */ + proxyBypassRules: string; + } + + interface NetworkEmulationOptions { + /** + * Whether to emulate network outage. + */ + offline?: boolean; + /** + * RTT in ms. + */ + latency?: number; + /** + * Download rate in Bps. + */ + downloadThroughput?: number; + /** + * Upload rate in Bps. + */ + uploadThroughput?: number; + } + + interface CookieFilter { + /** + * Retrieves cookies which are associated with url. Empty implies retrieving cookies of all urls. + */ + url?: string; + /** + * Filters cookies by name. + */ + name?: string; + /** + * Retrieves cookies whose domains match or are subdomains of domains. + */ + domain?: string; + /** + * Retrieves cookies whose path matches path. + */ + path?: string; + /** + * Filters cookies by their Secure property. + */ + secure?: boolean; + /** + * Filters out session or persistent cookies. + */ + session?: boolean; + } + + interface Cookie { + /** + * The name of the cookie. + */ + name: string; + /** + * The value of the cookie. + */ + value: string; + /** + * The domain of the cookie. + */ + domain: string; + /** + * Whether the cookie is a host-only cookie. + */ + hostOnly: string; + /** + * The path of the cookie. + */ + path: string; + /** + * Whether the cookie is marked as secure. + */ + secure: boolean; + /** + * Whether the cookie is marked as HTTP only. + */ + httpOnly: boolean; + /** + * Whether the cookie is a session cookie or a persistent cookie with an expiration date. + */ + session: boolean; + /** + * The expiration date of the cookie as the number of seconds since the UNIX epoch. + * Not provided for session cookies. + */ + expirationDate?: number; + } + + interface CookieDetails { + /** + * The URL associated with the cookie. + */ + url: string; + /** + * The name of the cookie. + * Default: empty. + */ + name?: string; + /** + * The value of the cookie. + * Default: empty. + */ + value?: string; + /** + * The domain of the cookie. + * Default: empty. + */ + domain?: string; + /** + * The path of the cookie. + * Default: empty. + */ + path?: string; + /** + * Whether the cookie should be marked as secure. + * Default: false. + */ + secure?: boolean; + /** + * Whether the cookie should be marked as HTTP only. + * Default: false. + */ + httpOnly?: boolean; + /** + * The expiration date of the cookie as the number of seconds since the UNIX epoch. + * If omitted, the cookie becomes a session cookie. + */ + expirationDate?: number; + } + + interface SessionCookies { + /** + * Sends a request to get all cookies matching filter. + */ + get(filter: CookieFilter, callback: (error: Error, cookies: Cookie[]) => void): void; + /** + * Sets the cookie with details. + */ + set(details: CookieDetails, callback: (error: Error) => void): void; + /** + * Removes the cookies matching url and name. + */ + remove(url: string, name: string, callback: (error: Error) => void): void; + } + + /** + * Each API accepts an optional filter and a listener, the listener will be called when the API's event has happened. + * Passing null as listener will unsubscribe from the event. + * + * The filter will be used to filter out the requests that do not match the URL patterns. + * If the filter is omitted then all requests will be matched. + * + * For certain events the listener is passed with a callback, + * which should be called with an response object when listener has done its work. + */ + interface WebRequest { + /** + * The listener will be called when a request is about to occur. + */ + onBeforeRequest(listener: (details: WebRequest.BeforeRequestDetails, callback: WebRequest.BeforeRequestCallback) => void): void; + /** + * The listener will be called when a request is about to occur. + */ + onBeforeRequest(filter: WebRequest.Filter, listener: (details: WebRequest.BeforeRequestDetails, callback: WebRequest.BeforeRequestCallback) => void): void; + /** + * The listener will be called before sending an HTTP request, once the request headers are available. + * This may occur after a TCP connection is made to the server, but before any http data is sent. + */ + onBeforeSendHeaders(listener: (details: WebRequest.BeforeSendHeadersDetails, callback: WebRequest.BeforeSendHeadersCallback) => void): void; + /** + * The listener will be called before sending an HTTP request, once the request headers are available. + * This may occur after a TCP connection is made to the server, but before any http data is sent. + */ + onBeforeSendHeaders(filter: WebRequest.Filter, listener: (details: WebRequest.BeforeSendHeadersDetails, callback: WebRequest.BeforeSendHeadersCallback) => void): void; + /** + * The listener will be called just before a request is going to be sent to the server, + * modifications of previous onBeforeSendHeaders response are visible by the time this listener is fired. + */ + onSendHeaders(listener: (details: WebRequest.SendHeadersDetails) => void): void; + /** + * The listener will be called just before a request is going to be sent to the server, + * modifications of previous onBeforeSendHeaders response are visible by the time this listener is fired. + */ + onSendHeaders(filter: WebRequest.Filter, listener: (details: WebRequest.SendHeadersDetails) => void): void; + /** + * The listener will be called when HTTP response headers of a request have been received. + */ + onHeadersReceived(listener: (details: WebRequest.HeadersReceivedDetails, callback: WebRequest.HeadersReceivedCallback) => void): void; + /** + * The listener will be called when HTTP response headers of a request have been received. + */ + onHeadersReceived(filter: WebRequest.Filter, listener: (details: WebRequest.HeadersReceivedDetails, callback: WebRequest.HeadersReceivedCallback) => void): void; + /** + * The listener will be called when first byte of the response body is received. + * For HTTP requests, this means that the status line and response headers are available. + */ + onResponseStarted(listener: (details: WebRequest.ResponseStartedDetails) => void): void; + /** + * The listener will be called when first byte of the response body is received. + * For HTTP requests, this means that the status line and response headers are available. + */ + onResponseStarted(filter: WebRequest.Filter, listener: (details: WebRequest.ResponseStartedDetails) => void): void; + /** + * The listener will be called when a server initiated redirect is about to occur. + */ + onBeforeRedirect(listener: (details: WebRequest.BeforeRedirectDetails) => void): void; + /** + * The listener will be called when a server initiated redirect is about to occur. + */ + onBeforeRedirect(filter: WebRequest.Filter, listener: (details: WebRequest.BeforeRedirectDetails) => void): void; + /** + * The listener will be called when a request is completed. + */ + onCompleted(listener: (details: WebRequest.CompletedDetails) => void): void; + /** + * The listener will be called when a request is completed. + */ + onCompleted(filter: WebRequest.Filter, listener: (details: WebRequest.CompletedDetails) => void): void; + /** + * The listener will be called when an error occurs. + */ + onErrorOccurred(listener: (details: WebRequest.ErrorOccurredDetails) => void): void; + /** + * The listener will be called when an error occurs. + */ + onErrorOccurred(filter: WebRequest.Filter, listener: (details: WebRequest.ErrorOccurredDetails) => void): void; + } + + namespace WebRequest { + interface Filter { + urls: string[]; + } + + interface Details { + id: number; + url: string; + method: string; + resourceType: string; + timestamp: number; + } + + interface UploadData { + /** + * Content being sent. + */ + bytes: Buffer; + /** + * Path of file being uploaded. + */ + file: string; + } + + interface BeforeRequestDetails extends Details { + uploadData?: UploadData[]; + } + + type BeforeRequestCallback = (response: { + cancel?: boolean; + /** + * The original request is prevented from being sent or completed, and is instead redirected to the given URL. + */ + redirectURL?: string; + }) => void; + + interface BeforeSendHeadersDetails extends Details { + requestHeaders: Headers; + } + + type BeforeSendHeadersCallback = (response: { + cancel?: boolean; + /** + * When provided, request will be made with these headers. + */ + requestHeaders?: Headers; + }) => void; + + interface SendHeadersDetails extends Details { + requestHeaders: Headers; + } + + interface HeadersReceivedDetails extends Details { + statusLine: string; + statusCode: number; + responseHeaders: Headers; + } + + type HeadersReceivedCallback = (response: { + cancel?: boolean; + /** + * When provided, the server is assumed to have responded with these headers. + */ + responseHeaders?: Headers; + /** + * Should be provided when overriding responseHeaders to change header status + * otherwise original response header's status will be used. + */ + statusLine?: string; + }) => void; + + interface ResponseStartedDetails extends Details { + responseHeaders: Headers; + fromCache: boolean; + statusCode: number; + statusLine: string; + } + + interface BeforeRedirectDetails extends Details { + redirectURL: string; + statusCode: number; + ip?: string; + fromCache: boolean; + responseHeaders: Headers; + } + + interface CompletedDetails extends Details { + responseHeaders: Headers; + fromCache: boolean; + statusCode: number; + statusLine: string; + } + + interface ErrorOccurredDetails extends Details { + fromCache: boolean; + error: string; + } + } + + // https://github.com/electron/electron/blob/master/docs/api/shell.md + + /** + * The shell module provides functions related to desktop integration. + */ + interface Shell { + /** + * Show the given file in a file manager. If possible, select the file. + */ + showItemInFolder(fullPath: string): void; + /** + * Open the given file in the desktop's default manner. + */ + openItem(fullPath: string): void; + /** + * Open the given external protocol URL in the desktop's default manner + * (e.g., mailto: URLs in the default mail user agent). + * @returns true if an application was available to open the URL, false otherwise. + */ + openExternal(url: string, options?: { + /** + * Bring the opened application to the foreground. + * Default: true. + */ + activate: boolean; + }): boolean; + /** + * Move the given file to trash. + * @returns boolean status for the operation. + */ + moveItemToTrash(fullPath: string): boolean; + /** + * Play the beep sound. + */ + beep(): void; + } + + // https://github.com/electron/electron/blob/master/docs/api/system-preferences.md + + /** + * Get system preferences. + */ + interface SystemPreferences { + /** + * @returns If the system is in Dark Mode. + * + * Note: This is only implemented on macOS. + */ + isDarkMode(): boolean; + /** + * Subscribes to native notifications of macOS, callback will be called when the corresponding event happens. + * The id of the subscriber is returned, which can be used to unsubscribe the event. + * + * Note: This is only implemented on macOS. + */ + subscribeNotification(event: string, callback: (event: Event, userInfo: Object) => void): number; + /** + * Removes the subscriber with id. + * + * Note: This is only implemented on macOS. + */ + unsubscribeNotification(id: number): void; + /** + * Same as subscribeNotification, but uses NSNotificationCenter for local defaults. + */ + subscribeLocalNotification(event: string, callback: (event: Event, userInfo: Object) => void): number; + /** + * Same as unsubscribeNotification, but removes the subscriber from NSNotificationCenter. + */ + unsubscribeLocalNotification(id: number): void; + /** + * Get the value of key in system preferences. + * + * Note: This is only implemented on macOS. + */ + getUserDefault(key: string, type: 'string' | 'boolean' | 'integer' | 'float' | 'double' | 'url' | 'array' | 'dictionary'): any; + /** + * This method returns true if DWM composition (Aero Glass) is enabled, + * and false otherwise. You can use it to determine if you should create + * a transparent window or not (transparent windows won’t work correctly when DWM composition is disabled). + * + * Note: This is only implemented on Windows. + */ + isAeroGlassEnabled(): boolean; + } + + // https://github.com/electron/electron/blob/master/docs/api/tray.md + + /** + * A Tray represents an icon in an operating system's notification area. + */ + interface Tray extends NodeJS.EventEmitter { + /** + * Emitted when the tray icon is clicked. + * Note: The bounds payload is only implemented on macOS and Windows. + */ + on(event: 'click', listener: (modifiers: Modifiers, bounds: Bounds) => void): this; + /** + * Emitted when the tray icon is right clicked. + * Note: This is only implemented on macOS and Windows. + */ + on(event: 'right-click', listener: (modifiers: Modifiers, bounds: Bounds) => void): this; + /** + * Emitted when the tray icon is double clicked. + * Note: This is only implemented on macOS and Windows. + */ + on(event: 'double-click', listener: (modifiers: Modifiers, bounds: Bounds) => void): this; + /** + * Emitted when the tray balloon shows. + * Note: This is only implemented on Windows. + */ + on(event: 'balloon-show', listener: Function): this; + /** + * Emitted when the tray balloon is clicked. + * Note: This is only implemented on Windows. + */ + on(event: 'balloon-click', listener: Function): this; + /** + * Emitted when the tray balloon is closed because of timeout or user manually closes it. + * Note: This is only implemented on Windows. + */ + on(event: 'balloon-closed', listener: Function): this; + /** + * Emitted when any dragged items are dropped on the tray icon. + * Note: This is only implemented on macOS. + */ + on(event: 'drop', listener: Function): this; + /** + * Emitted when dragged files are dropped in the tray icon. + * Note: This is only implemented on macOS + */ + on(event: 'drop-files', listener: (event: Event, files: string[]) => void): this; + /** + * Emitted when dragged text is dropped in the tray icon. + * Note: This is only implemented on macOS + */ + on(event: 'drop-text', listener: (event: Event, text: string) => void): this; + /** + * Emitted when a drag operation enters the tray icon. + * Note: This is only implemented on macOS + */ + on(event: 'drag-enter', listener: Function): this; + /** + * Emitted when a drag operation exits the tray icon. + * Note: This is only implemented on macOS + */ + on(event: 'drag-leave', listener: Function): this; + /** + * Emitted when a drag operation ends on the tray or ends at another location. + * Note: This is only implemented on macOS + */ + on(event: 'drag-end', listener: Function): this; + on(event: string, listener: Function): this; + /** + * Creates a new tray icon associated with the image. + */ + new(image: NativeImage|string): Tray; + /** + * Destroys the tray icon immediately. + */ + destroy(): void; + /** + * Sets the image associated with this tray icon. + */ + setImage(image: NativeImage|string): void; + /** + * Sets the image associated with this tray icon when pressed. + */ + setPressedImage(image: NativeImage): void; + /** + * Sets the hover text for this tray icon. + */ + setToolTip(toolTip: string): void; + /** + * Sets the title displayed aside of the tray icon in the status bar. + * Note: This is only implemented on macOS. + */ + setTitle(title: string): void; + /** + * Sets when the tray's icon background becomes highlighted. + * Note: This is only implemented on macOS. + */ + setHighlightMode(mode: 'selection' | 'always' | 'never'): void; + /** + * Displays a tray balloon. + * Note: This is only implemented on Windows. + */ + displayBalloon(options?: { + icon?: NativeImage; + title?: string; + content?: string; + }): void; + /** + * Pops up the context menu of tray icon. When menu is passed, + * the menu will showed instead of the tray's context menu. + * The position is only available on Windows, and it is (0, 0) by default. + * Note: This is only implemented on macOS and Windows. + */ + popUpContextMenu(menu?: Menu, position?: Point): void; + /** + * Sets the context menu for this icon. + */ + setContextMenu(menu: Menu): void; + /** + * @returns The bounds of this tray icon. + */ + getBounds(): Bounds; + } + + interface Modifiers { + altKey: boolean; + shiftKey: boolean; + ctrlKey: boolean; + metaKey: boolean; + } + + interface DragItem { + /** + * The absolute path of the file to be dragged + */ + file: string; + /** + * The image showing under the cursor when dragging. + */ + icon: NativeImage; + } + + // https://github.com/electron/electron/blob/master/docs/api/web-contents.md + + interface WebContentsStatic { + /** + * @returns An array of all web contents. This will contain web contents for all windows, + * webviews, opened devtools, and devtools extension background pages. + */ + getAllWebContents(): WebContents[]; + /** + * @returns The web contents that is focused in this application, otherwise returns null. + */ + getFocusedWebContents(): WebContents; + } + + /** + * A WebContents is responsible for rendering and controlling a web page. + */ + interface WebContents extends NodeJS.EventEmitter { + /** + * Emitted when the navigation is done, i.e. the spinner of the tab has stopped spinning, + * and the onload event was dispatched. + */ + on(event: 'did-finish-load', listener: Function): this; + /** + * This event is like did-finish-load but emitted when the load failed or was cancelled, + * e.g. window.stop() is invoked. + */ + on(event: 'did-fail-load', listener: (event: Event, errorCode: number, errorDescription: string, validatedURL: string, isMainFrame: boolean) => void): this; + /** + * Emitted when a frame has done navigation. + */ + on(event: 'did-frame-finish-load', listener: (event: Event, isMainFrame: boolean) => void): this; + /** + * Corresponds to the points in time when the spinner of the tab started spinning. + */ + on(event: 'did-start-loading', listener: Function): this; + /** + * Corresponds to the points in time when the spinner of the tab stopped spinning. + */ + on(event: 'did-stop-loading', listener: Function): this; + /** + * Emitted when details regarding a requested resource are available. + * status indicates the socket connection to download the resource. + */ + on(event: 'did-get-response-details', listener: (event: Event, + status: boolean, + newURL: string, + originalURL: string, + httpResponseCode: number, + requestMethod: string, + referrer: string, + headers: Headers, + resourceType: string + ) => void): this; + /** + * Emitted when a redirect is received while requesting a resource. + */ + on(event: 'did-get-redirect-request', listener: (event: Event, + oldURL: string, + newURL: string, + isMainFrame: boolean, + httpResponseCode: number, + requestMethod: string, + referrer: string, + headers: Headers + ) => void): this; + /** + * Emitted when the document in the given frame is loaded. + */ + on(event: 'dom-ready', listener: (event: Event) => void): this; + /** + * Emitted when page receives favicon URLs. + */ + on(event: 'page-favicon-updated', listener: (event: Event, favicons: string[]) => void): this; + /** + * Emitted when the page requests to open a new window for a url. + * It could be requested by window.open or an external link like
. + * + * By default a new BrowserWindow will be created for the url. + * + * Calling event.preventDefault() will prevent creating new windows. + */ + on(event: 'new-window', listener: (event: Event, + url: string, + frameName: string, + disposition: NewWindowDisposition, + options: BrowserWindowOptions + ) => void): this; + /** + * Emitted when a user or the page wants to start navigation. + * It can happen when the window.location object is changed or a user clicks a link in the page. + * + * This event will not emit when the navigation is started programmatically with APIs like + * webContents.loadURL and webContents.back. + * + * It is also not emitted for in-page navigations, such as clicking anchor links + * or updating the window.location.hash. Use did-navigate-in-page event for this purpose. + * + * Calling event.preventDefault() will prevent the navigation. + */ + on(event: 'will-navigate', listener: (event: Event, url: string) => void): this; + /** + * Emitted when a navigation is done. + * + * This event is not emitted for in-page navigations, such as clicking anchor links + * or updating the window.location.hash. Use did-navigate-in-page event for this purpose. + */ + on(event: 'did-navigate', listener: (event: Event, url: string) => void): this; + /** + * Emitted when an in-page navigation happened. + * + * When in-page navigation happens, the page URL changes but does not cause + * navigation outside of the page. Examples of this occurring are when anchor links + * are clicked or when the DOM hashchange event is triggered. + */ + on(event: 'did-navigate-in-page', listener: (event: Event, url: string) => void): this; + /** + * Emitted when the renderer process has crashed. + */ + on(event: 'crashed', listener: Function): this; + /** + * Emitted when a plugin process has crashed. + */ + on(event: 'plugin-crashed', listener: (event: Event, name: string, version: string) => void): this; + /** + * Emitted when webContents is destroyed. + */ + on(event: 'destroyed', listener: Function): this; + /** + * Emitted when DevTools is opened. + */ + on(event: 'devtools-opened', listener: Function): this; + /** + * Emitted when DevTools is closed. + */ + on(event: 'devtools-closed', listener: Function): this; + /** + * Emitted when DevTools is focused / opened. + */ + on(event: 'devtools-focused', listener: Function): this; + /** + * Emitted when failed to verify the certificate for url. + * The usage is the same with the "certificate-error" event of app. + */ + on(event: 'certificate-error', listener: (event: Event, + url: string, + error: string, + certificate: Certificate, + callback: (trust: boolean) => void + ) => void): this; + /** + * Emitted when a client certificate is requested. + * The usage is the same with the "select-client-certificate" event of app. + */ + on(event: 'select-client-certificate', listener: (event: Event, + url: string, + certificateList: Certificate[], + callback: (certificate: Certificate) => void + ) => void): this; + /** + * Emitted when webContents wants to do basic auth. + * The usage is the same with the "login" event of app. + */ + on(event: 'login', listener: (event: Event, + request: LoginRequest, + authInfo: LoginAuthInfo, + callback: (username: string, password: string) => void + ) => void): this; + /** + * Emitted when a result is available for webContents.findInPage request. + */ + on(event: 'found-in-page', listener: (event: Event, result: FoundInPageResult) => void): this; + /** + * Emitted when media starts playing. + */ + on(event: 'media-started-playing', listener: Function): this; + /** + * Emitted when media is paused or done playing. + */ + on(event: 'media-paused', listener: Function): this; + /** + * Emitted when a page’s theme color changes. This is usually due to encountering a meta tag: + * + */ + on(event: 'did-change-theme-color', listener: Function): this; + /** + * Emitted when mouse moves over a link or the keyboard moves the focus to a link. + */ + on(event: 'update-target-url', listener: (event: Event, url: string) => void): this; + /** + * Emitted when the cursor’s type changes. + * If the type parameter is custom, the image parameter will hold the custom cursor image + * in a NativeImage, and the scale will hold scaling information for the image. + */ + on(event: 'cursor-changed', listener: (event: Event, type: CursorType, image?: NativeImage, scale?: number) => void): this; + /** + * Emitted when there is a new context menu that needs to be handled. + */ + on(event: 'context-menu', listener: (event: Event, params: ContextMenuParams) => void): this; + /** + * Emitted when bluetooth device needs to be selected on call to navigator.bluetooth.requestDevice. + * To use navigator.bluetooth api webBluetooth should be enabled. + * If event.preventDefault is not called, first available device will be selected. + * callback should be called with deviceId to be selected, + * passing empty string to callback will cancel the request. + */ + on(event: 'select-bluetooth-device', listener: (event: Event, deviceList: BluetoothDevice[], callback: (deviceId: string) => void) => void): this; + /** + * Emitted when a page's view is repainted. + */ + on(event: 'view-painted', listener: Function): this; + on(event: string, listener: Function): this; + /** + * Loads the url in the window. + * @param url Must contain the protocol prefix (e.g., the http:// or file://). + */ + loadURL(url: string, options?: LoadURLOptions): void; + /** + * Initiates a download of the resource at url without navigating. + * The will-download event of session will be triggered. + */ + downloadURL(url: string): void; + /** + * @returns The URL of current web page. + */ + getURL(): string; + /** + * @returns The title of web page. + */ + getTitle(): string; + /** + * @returns The favicon of the web page. + */ + getFavicon(): NativeImage; + /** + * @returns Whether web page is still loading resources. + */ + isLoading(): boolean; + /** + * @returns Whether the main frame (and not just iframes or frames within it) is still loading. + */ + isLoadingMainFrame(): boolean; + /** + * @returns Whether web page is waiting for a first-response for the main + * resource of the page. + */ + isWaitingForResponse(): boolean; + /** + * Stops any pending navigation. + */ + stop(): void; + /** + * Reloads current page. + */ + reload(): void; + /** + * Reloads current page and ignores cache. + */ + reloadIgnoringCache(): void; + /** + * @returns Whether the web page can go back. + */ + canGoBack(): boolean; + /** + * @returns Whether the web page can go forward. + */ + canGoForward(): boolean; + /** + * @returns Whether the web page can go to offset. + */ + canGoToOffset(offset: number): boolean; + /** + * Clears the navigation history. + */ + clearHistory(): void; + /** + * Makes the web page go back. + */ + goBack(): void; + /** + * Makes the web page go forward. + */ + goForward(): void; + /** + * Navigates to the specified absolute index. + */ + goToIndex(index: number): void; + /** + * Navigates to the specified offset from the "current entry". + */ + goToOffset(offset: number): void; + /** + * @returns Whether the renderer process has crashed. + */ + isCrashed(): boolean; + /** + * Overrides the user agent for this page. + */ + setUserAgent(userAgent: string): void; + /** + * @returns The user agent for this web page. + */ + getUserAgent(): string; + /** + * Injects CSS into this page. + */ + insertCSS(css: string): void; + /** + * Evaluates code in page. + * @param code Code to evaluate. + */ + executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): void; + /** + * Mute the audio on the current web page. + */ + setAudioMuted(muted: boolean): void; + /** + * @returns Whether this page has been muted. + */ + isAudioMuted(): boolean; + /** + * Executes the editing command undo in web page. + */ + undo(): void; + /** + * Executes the editing command redo in web page. + */ + redo(): void; + /** + * Executes the editing command cut in web page. + */ + cut(): void; + /** + * Executes the editing command copy in web page. + */ + copy(): void; + /** + * Copy the image at the given position to the clipboard. + */ + copyImageAt(x: number, y: number): void; + /** + * Executes the editing command paste in web page. + */ + paste(): void; + /** + * Executes the editing command pasteAndMatchStyle in web page. + */ + pasteAndMatchStyle(): void; + /** + * Executes the editing command delete in web page. + */ + delete(): void; + /** + * Executes the editing command selectAll in web page. + */ + selectAll(): void; + /** + * Executes the editing command unselect in web page. + */ + unselect(): void; + /** + * Executes the editing command replace in web page. + */ + replace(text: string): void; + /** + * Executes the editing command replaceMisspelling in web page. + */ + replaceMisspelling(text: string): void; + /** + * Inserts text to the focused element. + */ + insertText(text: string): void; + /** + * Starts a request to find all matches for the text in the web page. + * The result of the request can be obtained by subscribing to found-in-page event. + * @returns The request id used for the request. + */ + findInPage(text: string, options?: FindInPageOptions): number; + /** + * Stops any findInPage request for the webContents with the provided action. + */ + stopFindInPage(action: StopFindInPageAtion): void; + /** + * Checks if any serviceworker is registered. + */ + hasServiceWorker(callback: (hasServiceWorker: boolean) => void): void; + /** + * Unregisters any serviceworker if present. + */ + unregisterServiceWorker(callback: (isFulfilled: boolean) => void): void; + /** + * Prints window's web page. When silent is set to false, Electron will pick up system's default printer and default settings for printing. + * Calling window.print() in web page is equivalent to call WebContents.print({silent: false, printBackground: false}). + * Note: On Windows, the print API relies on pdf.dll. If your application doesn't need print feature, you can safely remove pdf.dll in saving binary size. + */ + print(options?: PrintOptions): void; + /** + * Prints windows' web page as PDF with Chromium's preview printing custom settings. + */ + printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; + /** + * Adds the specified path to DevTools workspace. + */ + addWorkSpace(path: string): void; + /** + * Removes the specified path from DevTools workspace. + */ + removeWorkSpace(path: string): void; + /** + * Opens the developer tools. + */ + openDevTools(options?: { + /** + * Opens the devtools with specified dock state. Defaults to last used dock state. + */ + mode?: 'right' | 'bottom' | 'undocked' | 'detach' + }): void; + /** + * Closes the developer tools. + */ + closeDevTools(): void; + /** + * Returns whether the developer tools are opened. + */ + isDevToolsOpened(): boolean; + /** + * Returns whether the developer tools are focussed. + */ + isDevToolsFocused(): boolean; + /** + * Toggle the developer tools. + */ + toggleDevTools(): void; + /** + * Starts inspecting element at position (x, y). + */ + inspectElement(x: number, y: number): void; + /** + * Opens the developer tools for the service worker context. + */ + inspectServiceWorker(): void; + /** + * Send args.. to the web page via channel in asynchronous message, the web page + * can handle it by listening to the channel event of ipc module. + * Note: + * 1. The IPC message handler in web pages do not have a event parameter, + * which is different from the handlers on the main process. + * 2. There is no way to send synchronous messages from the main process + * to a renderer process, because it would be very easy to cause dead locks. + */ + send(channel: string, ...args: any[]): void; + /** + * Enable device emulation with the given parameters. + */ + enableDeviceEmulation(parameters: DeviceEmulationParameters): void; + /** + * Disable device emulation. + */ + disableDeviceEmulation(): void; + /** + * Sends an input event to the page. + */ + sendInputEvent(event: SendInputEvent): void; + /** + * Begin subscribing for presentation events and captured frames, + * The callback will be called when there is a presentation event. + */ + beginFrameSubscription(onlyDirty: boolean, callback: BeginFrameSubscriptionCallback): void; + /** + * Begin subscribing for presentation events and captured frames, + * The callback will be called when there is a presentation event. + */ + beginFrameSubscription(callback: BeginFrameSubscriptionCallback): void; + /** + * End subscribing for frame presentation events. + */ + endFrameSubscription(): void; + /** + * @returns If the process of saving page has been initiated successfully. + */ + savePage(fullPath: string, saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML', callback?: (eror: Error) => void): boolean; + /** + * Shows pop-up dictionary that searches the selected word on the page. + * Note: This API is available only on macOS. + */ + showDefinitionForSelection(): void; + /** + * Sets the item as dragging item for current drag-drop operation. + */ + startDrag(item: DragItem): void; + /** + * Captures a snapshot of the page within rect. + */ + capturePage(callback: (image: NativeImage) => void): void; + /** + * Captures a snapshot of the page within rect. + */ + capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + /** + * @returns The unique ID of this WebContents. + */ + id: number; + /** + * @returns The session object used by this webContents. + */ + session: Session; + /** + * @returns The WebContents that might own this WebContents. + */ + hostWebContents: WebContents; + /** + * @returns The WebContents of DevTools for this WebContents. + * Note: Users should never store this object because it may become null + * when the DevTools has been closed. + */ + devToolsWebContents: WebContents; + /** + * @returns Debugger API + */ + debugger: Debugger; + } + + interface BeginFrameSubscriptionCallback { + ( + /** + * The frameBuffer is a Buffer that contains raw pixel data. + * On most machines, the pixel data is effectively stored in 32bit BGRA format, + * but the actual representation depends on the endianness of the processor + * (most modern processors are little-endian, on machines with big-endian + * processors the data is in 32bit ARGB format). + */ + frameBuffer: Buffer, + /** + * The dirtyRect is an object with x, y, width, height properties that describes which part of the page was repainted. + * If onlyDirty is set to true, frameBuffer will only contain the repainted area. onlyDirty defaults to false. + */ + dirtyRect?: Bounds + ): void + } + + interface ContextMenuParams { + /** + * x coordinate + */ + x: number; + /** + * y coordinate + */ + y: number; + /** + * URL of the link that encloses the node the context menu was invoked on. + */ + linkURL: string; + /** + * Text associated with the link. May be an empty string if the contents of the link are an image. + */ + linkText: string; + /** + * URL of the top level page that the context menu was invoked on. + */ + pageURL: string; + /** + * URL of the subframe that the context menu was invoked on. + */ + frameURL: string; + /** + * Source URL for the element that the context menu was invoked on. + * Elements with source URLs are images, audio and video. + */ + srcURL: string; + /** + * Type of the node the context menu was invoked on. + */ + mediaType: 'none' | 'image' | 'audio' | 'video' | 'canvas' | 'file' | 'plugin'; + /** + * Parameters for the media element the context menu was invoked on. + */ + mediaFlags: { + /** + * Whether the media element has crashed. + */ + inError: boolean; + /** + * Whether the media element is paused. + */ + isPaused: boolean; + /** + * Whether the media element is muted. + */ + isMuted: boolean; + /** + * Whether the media element has audio. + */ + hasAudio: boolean; + /** + * Whether the media element is looping. + */ + isLooping: boolean; + /** + * Whether the media element's controls are visible. + */ + isControlsVisible: boolean; + /** + * Whether the media element's controls are toggleable. + */ + canToggleControls: boolean; + /** + * Whether the media element can be rotated. + */ + canRotate: boolean; + } + /** + * Whether the context menu was invoked on an image which has non-empty contents. + */ + hasImageContents: boolean; + /** + * Whether the context is editable. + */ + isEditable: boolean; + /** + * These flags indicate whether the renderer believes it is able to perform the corresponding action. + */ + editFlags: { + /** + * Whether the renderer believes it can undo. + */ + canUndo: boolean; + /** + * Whether the renderer believes it can redo. + */ + canRedo: boolean; + /** + * Whether the renderer believes it can cut. + */ + canCut: boolean; + /** + * Whether the renderer believes it can copy + */ + canCopy: boolean; + /** + * Whether the renderer believes it can paste. + */ + canPaste: boolean; + /** + * Whether the renderer believes it can delete. + */ + canDelete: boolean; + /** + * Whether the renderer believes it can select all. + */ + canSelectAll: boolean; + } + /** + * Text of the selection that the context menu was invoked on. + */ + selectionText: string; + /** + * Title or alt text of the selection that the context was invoked on. + */ + titleText: string; + /** + * The misspelled word under the cursor, if any. + */ + misspelledWord: string; + /** + * The character encoding of the frame on which the menu was invoked. + */ + frameCharset: string; + /** + * If the context menu was invoked on an input field, the type of that field. + */ + inputFieldType: 'none' | 'plainText' | 'password' | 'other'; + /** + * Input source that invoked the context menu. + */ + menuSourceType: 'none' | 'mouse' | 'keyboard' | 'touch' | 'touchMenu'; + } + + interface BluetoothDevice { + deviceName: string; + deviceId: string; + } + + interface Headers { + [key: string]: string; + } + + type NewWindowDisposition = 'default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'other'; + + /** + * Specifies the action to take place when ending webContents.findInPage request. + * 'clearSelection' - Clear the selection. + * 'keepSelection' - Translate the selection into a normal selection. + * 'activateSelection' - Focus and click the selection node. + */ + type StopFindInPageAtion = 'clearSelection' | 'keepSelection' | 'activateSelection'; + + type CursorType = 'default' | 'crosshair' | 'pointer' | 'text' | 'wait' | 'help' | 'e-resize' | 'n-resize' | 'ne-resize' | 'nw-resize' | 's-resize' | 'se-resize' | 'sw-resize' | 'w-resize' | 'ns-resize' | 'ew-resize' | 'nesw-resize' | 'nwse-resize' | 'col-resize' | 'row-resize' | 'm-panning' | 'e-panning' | 'n-panning' | 'ne-panning' | 'nw-panning' | 's-panning' | 'se-panning' |'sw-panning' | 'w-panning' | 'move' | 'vertical-text' | 'cell' | 'context-menu' | 'alias' | 'progress' | 'nodrop' | 'copy' | 'none' | 'not-allowed' | 'zoom-in' | 'zoom-out' | 'grab' | 'grabbing' | 'custom'; + + interface LoadURLOptions { + /** + * HTTP Referrer URL. + */ + httpReferrer?: string; + /** + * User agent originating the request. + */ + userAgent?: string; + /** + * Extra headers separated by "\n" + */ + extraHeaders?: string; + } + + interface PrintOptions { + /** + * Don't ask user for print settings. + * Defaults: false. + */ + silent?: boolean; + /** + * Also prints the background color and image of the web page. + * Defaults: false. + */ + printBackground?: boolean; + } + + interface PrintToPDFOptions { + /** + * Specify the type of margins to use. + * 0 - default + * 1 - none + * 2 - minimum + * Default: 0 + */ + marginsType?: number; + /** + * Specify page size of the generated PDF. + * Default: A4. + */ + pageSize?: 'A3' | 'A4' | 'A5' | 'Legal' | 'Letter' | 'Tabloid' | Dimension; + /** + * Whether to print CSS backgrounds. + * Default: false. + */ + printBackground?: boolean; + /** + * Whether to print selection only. + * Default: false. + */ + printSelectionOnly?: boolean; + /** + * true for landscape, false for portrait. + * Default: false. + */ + landscape?: boolean; + } + + interface Certificate { + /** + * PEM encoded data. + */ + data: Buffer; + /** + * Issuer's Common Name. + */ + issuerName: string; + /** + * Subject's Common Name. + */ + subjectName: string; + /** + * Hex value represented string. + */ + serialNumber: string; + /** + * Start date of the certificate being valid in seconds. + */ + validStart: number; + /** + * End date of the certificate being valid in seconds. + */ + validExpiry: number; + /** + * Fingerprint of the certificate. + */ + fingerprint: string; + } + + interface LoginRequest { + method: string; + url: string; + referrer: string; + } + + interface LoginAuthInfo { + isProxy: boolean; + scheme: string; + host: string; + port: number; + realm: string; + } + + interface FindInPageOptions { + /** + * Whether to search forward or backward, defaults to true + */ + forward?: boolean; + /** + * Whether the operation is first request or a follow up, defaults to false. + */ + findNext?: boolean; + /** + * Whether search should be case-sensitive, defaults to false. + */ + matchCase?: boolean; + /** + * Whether to look only at the start of words. defaults to false. + */ + wordStart?: boolean; + /** + * When combined with wordStart, accepts a match in the middle of a word + * if the match begins with an uppercase letter followed by a lowercase + * or non-letter. Accepts several other intra-word matches, defaults to false. + */ + medialCapitalAsWordStart?: boolean; + } + + interface FoundInPageResult { + requestId: number; + /** + * Indicates if more responses are to follow. + */ + finalUpdate: boolean; + /** + * Position of the active match. + */ + activeMatchOrdinal?: number; + /** + * Number of Matches. + */ + matches?: number; + /** + * Coordinates of first match region. + */ + selectionArea?: Bounds; + } + + interface DeviceEmulationParameters { + /** + * Specify the screen type to emulated + * Default: desktop + */ + screenPosition?: 'desktop' | 'mobile'; + /** + * Set the emulated screen size (screenPosition == mobile) + */ + screenSize?: Dimension; + /** + * Position the view on the screen (screenPosition == mobile) + * Default: {x: 0, y: 0} + */ + viewPosition?: Point; + /** + * Set the device scale factor (if zero defaults to original device scale factor) + * Default: 0 + */ + deviceScaleFactor: number; + /** + * Set the emulated view size (empty means no override). + */ + viewSize?: Dimension; + /** + * Whether emulated view should be scaled down if necessary to fit into available space + * Default: false + */ + fitToView?: boolean; + /** + * Offset of the emulated view inside available space (not in fit to view mode) + * Default: {x: 0, y: 0} + */ + offset?: Point; + /** + * Scale of emulated view inside available space (not in fit to view mode) + * Default: 1 + */ + scale: number; + } + + interface SendInputEvent { + type: 'mouseDown' | 'mouseUp' | 'mouseEnter' | 'mouseLeave' | 'contextMenu' | 'mouseWheel' | 'mouseMove' | 'keyDown' | 'keyUp' | 'char'; + modifiers: ('shift' | 'control' | 'alt' | 'meta' | 'isKeypad' | 'isAutoRepeat' | 'leftButtonDown' | 'middleButtonDown' | 'rightButtonDown' | 'capsLock' | 'numLock' | 'left' | 'right')[]; + } + + interface SendInputKeyboardEvent extends SendInputEvent { + keyCode: string; + } + + interface SendInputMouseEvent extends SendInputEvent { + x: number; + y: number; + button?: 'left' | 'middle' | 'right'; + globalX?: number; + globalY?: number; + movementX?: number; + movementY?: number; + clickCount?: number; + } + + interface SendInputMouseWheelEvent extends SendInputEvent { + deltaX?: number; + deltaY?: number; + wheelTicksX?: number; + wheelTicksY?: number; + accelerationRatioX?: number; + accelerationRatioY?: number; + hasPreciseScrollingDeltas?: boolean; + canScroll?: boolean; + } + + /** + * Debugger API serves as an alternate transport for remote debugging protocol. + */ + interface Debugger extends NodeJS.EventEmitter { + /** + * Attaches the debugger to the webContents. + * @param protocolVersion Requested debugging protocol version. + */ + attach(protocolVersion?: string): void; + /** + * @returns Whether a debugger is attached to the webContents. + */ + isAttached(): boolean; + /** + * Detaches the debugger from the webContents. + */ + detach(): void; + /** + * Send given command to the debugging target. + * @param method Method name, should be one of the methods defined by the remote debugging protocol. + * @param commandParams JSON object with request parameters. + * @param callback Response defined by the ‘returns’ attribute of the command description in the remote debugging protocol. + */ + sendCommand(method: string, commandParams?: any, callback?: (error: Error, result: any) => void): void; + /** + * Emitted when debugging session is terminated. This happens either when + * webContents is closed or devtools is invoked for the attached webContents. + */ + on(event: 'detach', listener: (event: Event, reason: string) => void): this; + /** + * Emitted whenever debugging target issues instrumentation event. + * Event parameters defined by the ‘parameters’ attribute in the remote debugging protocol. + */ + on(event: 'message', listener: (event: Event, method: string, params: any) => void): this; + on(event: string, listener: Function): this; + } + + // https://github.com/electron/electron/blob/master/docs/api/web-frame.md + + /** + * The web-frame module allows you to customize the rendering of the current web page. + */ + interface WebFrame { + /** + * Changes the zoom factor to the specified factor, zoom factor is + * zoom percent / 100, so 300% = 3.0. + */ + setZoomFactor(factor: number): void; + /** + * @returns The current zoom factor. + */ + getZoomFactor(): number; + /** + * Changes the zoom level to the specified level, 0 is "original size", and each + * increment above or below represents zooming 20% larger or smaller to default + * limits of 300% and 50% of original size, respectively. + */ + setZoomLevel(level: number): void; + /** + * @returns The current zoom level. + */ + getZoomLevel(): number; + /** + * Sets the maximum and minimum zoom level. + */ + setZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; + /** + * Sets a provider for spell checking in input fields and text areas. + */ + setSpellCheckProvider(language: string, autoCorrectWord: boolean, provider: { + /** + * @returns Whether the word passed is correctly spelled. + */ + spellCheck: (text: string) => boolean; + }): void; + /** + * Sets the scheme as secure scheme. Secure schemes do not trigger mixed content + * warnings. For example, https and data are secure schemes because they cannot be + * corrupted by active network attackers. + */ + registerURLSchemeAsSecure(scheme: string): void; + /** + * Resources will be loaded from this scheme regardless of the current page’s Content Security Policy. + */ + registerURLSchemeAsBypassingCSP(scheme: string): void; + /** + * Registers the scheme as secure, bypasses content security policy for resources, + * allows registering ServiceWorker and supports fetch API. + */ + registerURLSchemeAsPrivileged(scheme: string): void; + /** + * Inserts text to the focused element. + */ + insertText(text: string): void; + /** + * Evaluates `code` in page. + * In the browser window some HTML APIs like `requestFullScreen` can only be + * invoked by a gesture from the user. Setting `userGesture` to `true` will remove + * this limitation. + */ + executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): void; + /** + * @returns Object describing usage information of Blink’s internal memory caches. + */ + getResourceUsage(): ResourceUsages; + /** + * Attempts to free memory that is no longer being used (like images from a previous navigation). + */ + clearCache(): void; + } + + interface ResourceUsages { + fonts: ResourceUsage; + images: ResourceUsage; + cssStyleSheets: ResourceUsage; + xslStyleSheets: ResourceUsage; + scripts: ResourceUsage; + other: ResourceUsage; + } + + interface ResourceUsage { + count: number; + decodedSize: number; + liveSize: number; + purgeableSize: number; + purgedSize: number; + size: number; + } + + // https://github.com/electron/electron/blob/master/docs/api/web-view-tag.md + + /** + * Use the webview tag to embed 'guest' content (such as web pages) in your Electron app. + * The guest content is contained within the webview container. + * An embedded page within your app controls how the guest content is laid out and rendered. + * + * Unlike an iframe, the webview runs in a separate process than your app. + * It doesn't have the same permissions as your web page and all interactions between your app + * and embedded content will be asynchronous. This keeps your app safe from the embedded content. + */ + interface WebViewElement extends HTMLElement { + /** + * Returns the visible URL. Writing to this attribute initiates top-level navigation. + * Assigning src its own value will reload the current page. + * The src attribute can also accept data URLs, such as data:text/plain,Hello, world!. + */ + src: string; + /** + * If "on", the webview container will automatically resize within the bounds specified + * by the attributes minwidth, minheight, maxwidth, and maxheight. + * These constraints do not impact the webview unless autosize is enabled. + * When autosize is enabled, the webview container size cannot be less than + * the minimum values or greater than the maximum. + */ + autosize: string; + /** + * If "on", the guest page in webview will have node integration and can use node APIs + * like require and process to access low level system resources. + */ + nodeintegration: string; + /** + * If "on", the guest page in webview will be able to use browser plugins. + */ + plugins: string; + /** + * Specifies a script that will be loaded before other scripts run in the guest page. + * The protocol of script's URL must be either file: or asar:, + * because it will be loaded by require in guest page under the hood. + * + * When the guest page doesn't have node integration this script will still have access to all Node APIs, + * but global objects injected by Node will be deleted after this script has finished executing. + */ + preload: string; + /** + * Sets the referrer URL for the guest page. + */ + httpreferrer: string; + /** + * Sets the user agent for the guest page before the page is navigated to. + * Once the page is loaded, use the setUserAgent method to change the user agent. + */ + useragent: string; + /** + * If "on", the guest page will have web security disabled. + */ + disablewebsecurity: string; + /** + * Sets the session used by the page. If partition starts with persist:, + * the page will use a persistent session available to all pages in the app with the same partition. + * If there is no persist: prefix, the page will use an in-memory session. + * By assigning the same partition, multiple pages can share the same session. + * If the partition is unset then default session of the app will be used. + * + * This value can only be modified before the first navigation, + * since the session of an active renderer process cannot change. + * Subsequent attempts to modify the value will fail with a DOM exception. + */ + partition: string; + /** + * If "on", the guest page will be allowed to open new windows. + */ + allowpopups: string; + /** + * A list of strings which specifies the blink features to be enabled separated by ,. + */ + blinkfeatures: string; + /** + * A list of strings which specifies the blink features to be disabled separated by ,. + */ + disableblinkfeatures: string; + /** + * Loads the url in the webview, the url must contain the protocol prefix, e.g. the http:// or file://. + */ + loadURL(url: string, options?: LoadURLOptions): void; + /** + * @returns URL of guest page. + */ + getURL(): string; + /** + * @returns The title of guest page. + */ + getTitle(): string; + /** + * @returns Whether guest page is still loading resources. + */ + isLoading(): boolean; + /** + * Returns a boolean whether the guest page is waiting for a first-response for the main resource of the page. + */ + isWaitingForResponse(): boolean; + /** + * Stops any pending navigation. + */ + stop(): void; + /** + * Reloads the guest page. + */ + reload(): void; + /** + * Reloads the guest page and ignores cache. + */ + reloadIgnoringCache(): void; + /** + * @returns Whether the guest page can go back. + */ + canGoBack(): boolean; + /** + * @returns Whether the guest page can go forward. + */ + canGoForward(): boolean; + /** + * @returns Whether the guest page can go to offset. + */ + canGoToOffset(offset: number): boolean; + /** + * Clears the navigation history. + */ + clearHistory(): void; + /** + * Makes the guest page go back. + */ + goBack(): void; + /** + * Makes the guest page go forward. + */ + goForward(): void; + /** + * Navigates to the specified absolute index. + */ + goToIndex(index: number): void; + /** + * Navigates to the specified offset from the "current entry". + */ + goToOffset(offset: number): void; + /** + * @returns Whether the renderer process has crashed. + */ + isCrashed(): boolean; + /** + * Overrides the user agent for the guest page. + */ + setUserAgent(userAgent: string): void; + /** + * @returns The user agent for guest page. + */ + getUserAgent(): string; + /** + * Injects CSS into the guest page. + */ + insertCSS(css: string): void; + /** + * Evaluates code in page. If userGesture is set, it will create the user gesture context in the page. + * HTML APIs like requestFullScreen, which require user action, can take advantage of this option for automation. + */ + executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): void; + /** + * Opens a DevTools window for guest page. + */ + openDevTools(): void; + /** + * Closes the DevTools window of guest page. + */ + closeDevTools(): void; + /** + * @returns Whether guest page has a DevTools window attached. + */ + isDevToolsOpened(): boolean; + /** + * @returns Whether DevTools window of guest page is focused. + */ + isDevToolsFocused(): boolean; + /** + * Starts inspecting element at position (x, y) of guest page. + */ + inspectElement(x: number, y: number): void; + /** + * Opens the DevTools for the service worker context present in the guest page. + */ + inspectServiceWorker(): void; + /** + * Set guest page muted. + */ + setAudioMuted(muted: boolean): void; + /** + * @returns Whether guest page has been muted. + */ + isAudioMuted(): boolean; + /** + * Executes editing command undo in page. + */ + undo(): void; + /** + * Executes editing command redo in page. + */ + redo(): void; + /** + * Executes editing command cut in page. + */ + cut(): void; + /** + * Executes editing command copy in page. + */ + copy(): void; + /** + * Executes editing command paste in page. + */ + paste(): void; + /** + * Executes editing command pasteAndMatchStyle in page. + */ + pasteAndMatchStyle(): void; + /** + * Executes editing command delete in page. + */ + delete(): void; + /** + * Executes editing command selectAll in page. + */ + selectAll(): void; + /** + * Executes editing command unselect in page. + */ + unselect(): void; + /** + * Executes editing command replace in page. + */ + replace(text: string): void; + /** + * Executes editing command replaceMisspelling in page. + */ + replaceMisspelling(text: string): void; + /** + * Inserts text to the focused element. + */ + insertText(text: string): void; + /** + * Starts a request to find all matches for the text in the web page. + * The result of the request can be obtained by subscribing to found-in-page event. + * @returns The request id used for the request. + */ + findInPage(text: string, options?: FindInPageOptions): number; + /** + * Stops any findInPage request for the webview with the provided action. + */ + stopFindInPage(action: StopFindInPageAtion): void; + /** + * Prints webview's web page. Same with webContents.print([options]). + */ + print(options?: PrintOptions): void; + /** + * Prints webview's web page as PDF, Same with webContents.printToPDF(options, callback) + */ + printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; + /** + * Send an asynchronous message to renderer process via channel, you can also send arbitrary arguments. + * The renderer process can handle the message by listening to the channel event with the ipcRenderer module. + * See webContents.send for examples. + */ + send(channel: string, ...args: any[]): void; + /** + * Sends an input event to the page. + * See webContents.sendInputEvent for detailed description of event object. + */ + sendInputEvent(event: SendInputEvent): void + /** + * Shows pop-up dictionary that searches the selected word on the page. + * Note: This API is available only on macOS. + */ + showDefinitionForSelection(): void; + /** + * @returns The WebContents associated with this webview. + */ + getWebContents(): WebContents; + /** + * Captures a snapshot of the webview's page. Same as webContents.capturePage([rect, ]callback). + */ + capturePage(callback: (image: NativeImage) => void): void; + /** + * Captures a snapshot of the webview's page. Same as webContents.capturePage([rect, ]callback). + */ + capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + /** + * Fired when a load has committed. This includes navigation within the current document + * as well as subframe document-level loads, but does not include asynchronous resource loads. + */ + addEventListener(type: 'load-commit', listener: (event: WebViewElement.LoadCommitEvent) => void, useCapture?: boolean): void; + /** + * Fired when the navigation is done, i.e. the spinner of the tab will stop spinning, and the onload event is dispatched. + */ + addEventListener(type: 'did-finish-load', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * This event is like did-finish-load, but fired when the load failed or was cancelled, e.g. window.stop() is invoked. + */ + addEventListener(type: 'did-fail-load', listener: (event: WebViewElement.DidFailLoadEvent) => void, useCapture?: boolean): void; + /** + * Fired when a frame has done navigation. + */ + addEventListener(type: 'did-frame-finish-load', listener: (event: WebViewElement.DidFrameFinishLoadEvent) => void, useCapture?: boolean): void; + /** + * Corresponds to the points in time when the spinner of the tab starts spinning. + */ + addEventListener(type: 'did-start-loading', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Corresponds to the points in time when the spinner of the tab stops spinning. + */ + addEventListener(type: 'did-stop-loading', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when details regarding a requested resource is available. + * status indicates socket connection to download the resource. + */ + addEventListener(type: 'did-get-response-details', listener: (event: WebViewElement.DidGetResponseDetails) => void, useCapture?: boolean): void; + /** + * Fired when a redirect was received while requesting a resource. + */ + addEventListener(type: 'did-get-redirect-request', listener: (event: WebViewElement.DidGetRedirectRequestEvent) => void, useCapture?: boolean): void; + /** + * Fired when document in the given frame is loaded. + */ + addEventListener(type: 'dom-ready', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when page title is set during navigation. explicitSet is false when title is synthesized from file URL. + */ + addEventListener(type: 'page-title-updated', listener: (event: WebViewElement.PageTitleUpdatedEvent) => void, useCapture?: boolean): void; + /** + * Fired when page receives favicon URLs. + */ + addEventListener(type: 'page-favicon-updated', listener: (event: WebViewElement.PageFaviconUpdatedEvent) => void, useCapture?: boolean): void; + /** + * Fired when page enters fullscreen triggered by HTML API. + */ + addEventListener(type: 'enter-html-full-screen', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when page leaves fullscreen triggered by HTML API. + */ + addEventListener(type: 'leave-html-full-screen', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when the guest window logs a console message. + */ + addEventListener(type: 'console-message', listener: (event: WebViewElement.ConsoleMessageEvent) => void, useCapture?: boolean): void; + /** + * Fired when a result is available for webview.findInPage request. + */ + addEventListener(type: 'found-in-page', listener: (event: WebViewElement.FoundInPageEvent) => void, useCapture?: boolean): void; + /** + * Fired when the guest page attempts to open a new browser window. + */ + addEventListener(type: 'new-window', listener: (event: WebViewElement.NewWindowEvent) => void, useCapture?: boolean): void; + /** + * Emitted when a user or the page wants to start navigation. + * It can happen when the window.location object is changed or a user clicks a link in the page. + * + * This event will not emit when the navigation is started programmatically with APIs + * like .loadURL and .back. + * + * It is also not emitted during in-page navigation, such as clicking anchor links + * or updating the window.location.hash. Use did-navigate-in-page event for this purpose. + * + * Calling event.preventDefault() does NOT have any effect. + */ + addEventListener(type: 'will-navigate', listener: (event: WebViewElement.NavigateEvent) => void, useCapture?: boolean): void; + /** + * Emitted when a navigation is done. + * + * This event is not emitted for in-page navigations, such as clicking anchor links + * or updating the window.location.hash. Use did-navigate-in-page event for this purpose. + */ + addEventListener(type: 'did-navigate', listener: (event: WebViewElement.NavigateEvent) => void, useCapture?: boolean): void; + /** + * Emitted when an in-page navigation happened. + * + * When in-page navigation happens, the page URL changes but does not cause + * navigation outside of the page. Examples of this occurring are when anchor links + * are clicked or when the DOM hashchange event is triggered. + */ + addEventListener(type: 'did-navigate-in-page', listener: (event: WebViewElement.NavigateEvent) => void, useCapture?: boolean): void; + /** + * Fired when the guest page attempts to close itself. + */ + addEventListener(type: 'close', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when the guest page has sent an asynchronous message to embedder page. + */ + addEventListener(type: 'ipc-message', listener: (event: WebViewElement.IpcMessageEvent) => void, useCapture?: boolean): void; + /** + * Fired when the renderer process is crashed. + */ + addEventListener(type: 'crashed', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when the gpu process is crashed. + */ + addEventListener(type: 'gpu-crashed', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Fired when a plugin process is crashed. + */ + addEventListener(type: 'plugin-crashed', listener: (event: WebViewElement.PluginCrashedEvent) => void, useCapture?: boolean): void; + /** + * Fired when the WebContents is destroyed. + */ + addEventListener(type: 'destroyed', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when media starts playing. + */ + addEventListener(type: 'media-started-playing', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when media is paused or done playing. + */ + addEventListener(type: 'media-paused', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when a page's theme color changes. This is usually due to encountering a meta tag: + * + */ + addEventListener(type: 'did-change-theme-color', listener: (event: WebViewElement.DidChangeThemeColorEvent) => void, useCapture?: boolean): void; + /** + * Emitted when mouse moves over a link or the keyboard moves the focus to a link. + */ + addEventListener(type: 'update-target-url', listener: (event: WebViewElement.UpdateTargetUrlEvent) => void, useCapture?: boolean): void; + /** + * Emitted when DevTools is opened. + */ + addEventListener(type: 'devtools-opened', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when DevTools is closed. + */ + addEventListener(type: 'devtools-closed', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + /** + * Emitted when DevTools is focused / opened. + */ + addEventListener(type: 'devtools-focused', listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + addEventListener(type: string, listener: (event: WebViewElement.Event) => void, useCapture?: boolean): void; + } + + namespace WebViewElement { + type Event = ElectronPrivate.GlobalEvent; + + interface LoadCommitEvent extends Event { + url: string; + isMainFrame: boolean; + } + + interface DidFailLoadEvent extends Event { + errorCode: number; + errorDescription: string; + validatedURL: string; + isMainFrame: boolean; + } + + interface DidFrameFinishLoadEvent extends Event { + isMainFrame: boolean; + } + + interface DidGetResponseDetails extends Event { + status: boolean; + newURL: string; + originalURL: string; + httpResponseCode: number; + requestMethod: string; + referrer: string; + headers: Headers; + resourceType: string; + } + + interface DidGetRedirectRequestEvent extends Event { + oldURL: string; + newURL: string; + isMainFrame: boolean; + httpResponseCode: number; + requestMethod: string; + referrer: string; + headers: Headers; + } + + interface PageTitleUpdatedEvent extends Event { + title: string; + explicitSet: string; + } + + interface PageFaviconUpdatedEvent extends Event { + favicons: string[]; + } + + interface ConsoleMessageEvent extends Event { + level: number; + message: string; + line: number; + sourceId: string; + } + + interface FoundInPageEvent extends Event { + result: FoundInPageResult; + } + + interface NewWindowEvent extends Event { + url: string; + frameName: string; + disposition: NewWindowDisposition; + options: BrowserWindowOptions; + } + + interface NavigateEvent extends Event { + url: string; + } + + interface IpcMessageEvent extends Event { + channel: string; + args: any[]; + } + + interface PluginCrashedEvent extends Event { + name: string; + version: string; + } + + interface DidChangeThemeColorEvent extends Event { + themeColor: string; + } + + interface UpdateTargetUrlEvent extends Event { + url: string; + } + } + + /** + * The BrowserWindowProxy object is returned from window.open and provides limited functionality with the child window. + */ + interface BrowserWindowProxy { + /** + * Removes focus from the child window. + */ + blur(): void; + /** + * Forcefully closes the child window without calling its unload event. + */ + close(): void; + /** + * Set to true after the child window gets closed. + */ + closed: boolean; + /** + * Evaluates the code in the child window. + */ + eval(code: string): void; + /** + * Focuses the child window (brings the window to front). + */ + focus(): void; + /** + * Sends a message to the child window with the specified origin or * for no origin preference. + * In addition to these methods, the child window implements window.opener object with no + * properties and a single method. + */ + postMessage(message: string, targetOrigin: string): void; + /** + * Invokes the print dialog on the child window. + */ + print(): void; + } + + // https://github.com/electron/electron/blob/master/docs/api/synopsis.md + + interface CommonElectron { + clipboard: Electron.Clipboard; + crashReporter: Electron.CrashReporter; + nativeImage: typeof Electron.NativeImage; + shell: Electron.Shell; + + app: Electron.App; + autoUpdater: Electron.AutoUpdater; + BrowserWindow: typeof Electron.BrowserWindow; + contentTracing: Electron.ContentTracing; + dialog: Electron.Dialog; + ipcMain: Electron.IpcMain; + globalShortcut: Electron.GlobalShortcut; + Menu: typeof Electron.Menu; + MenuItem: typeof Electron.MenuItem; + powerMonitor: Electron.PowerMonitor; + powerSaveBlocker: Electron.PowerSaveBlocker; + protocol: Electron.Protocol; + screen: Electron.Screen; + session: typeof Electron.Session; + systemPreferences: Electron.SystemPreferences; + Tray: Electron.Tray; + webContents: Electron.WebContentsStatic; + } + + interface ElectronMainAndRenderer extends CommonElectron { + desktopCapturer: Electron.DesktopCapturer; + ipcRenderer: Electron.IpcRenderer; + remote: Electron.Remote; + webFrame: Electron.WebFrame; + } +} + +declare namespace ElectronPrivate { + type GlobalEvent = Event; +} + +interface Document { + createElement(tagName: 'webview'): Electron.WebViewElement; +} + +// https://github.com/electron/electron/blob/master/docs/api/window-open.md + +interface Window { + /** + * Creates a new window. + */ + open(url: string, frameName?: string, features?: string): Electron.BrowserWindowProxy; +} + +// https://github.com/electron/electron/blob/master/docs/api/file-object.md + +interface File { + /** + * Exposes the real path of the filesystem. + */ + path: string; +} + +// https://github.com/electron/electron/blob/master/docs/api/process.md + +declare namespace NodeJS { + + interface ProcessVersions { + /** + * Electron's version string. + */ + electron: string; + /** + * Chrome's version string. + */ + chrome: string; + } + + interface Process { + /** + * Setting this to true can disable the support for asar archives in Node's built-in modules. + */ + noAsar?: boolean; + /** + * Process's type + */ + type: 'browser' | 'renderer'; + /** + * Path to JavaScript source code. + */ + resourcesPath: string; + /** + * For Mac App Store build, this value is true, for other builds it is undefined. + */ + mas?: boolean; + /** + * If the app is running as a Windows Store app (appx), this value is true, for other builds it is undefined. + */ + windowsStore?: boolean; + /** + * When app is started by being passed as parameter to the default app, + * this value is true in the main process, otherwise it is undefined. + */ + defaultApp?: boolean; + /** + * Emitted when Electron has loaded its internal initialization script + * and is beginning to load the web page or the main script. + */ + on(event: 'loaded', listener: Function): this; + on(event: string, listener: Function): this; + /** + * Causes the main thread of the current process crash; + */ + crash(): void; + /** + * Causes the main thread of the current process hang. + */ + hang(): void; + /** + * Sets the file descriptor soft limit to maxDescriptors or the OS hard limit, + * whichever is lower for the current process. + * + * Note: This API is only available on macOS and Linux. + */ + setFdLimit(maxDescriptors: number): void; + /** + * @returns Object giving memory usage statistics about the current process. + * Note: All statistics are reported in Kilobytes. + */ + getProcessMemoryInfo(): ProcessMemoryInfo; + /** + * @returns Object giving memory usage statistics about the entire system. + * Note: All statistics are reported in Kilobytes. + */ + getSystemMemoryInfo(): SystemMemoryInfo; + } + + interface ProcessMemoryInfo { + /** + * The amount of memory currently pinned to actual physical RAM. + */ + workingSetSize: number; + /** + * The maximum amount of memory that has ever been pinned to actual physical RAM. + */ + peakWorkingSetSize: number; + /** + * The amount of memory not shared by other processes, such as JS heap or HTML content. + */ + privateBytes: number; + /** + * The amount of memory shared between processes, typically memory consumed by the Electron code itself. + */ + sharedBytes: number; + } + + interface SystemMemoryInfo { + /** + * The total amount of physical memory available to the system. + */ + total: number; + /** + * The total amount of memory not being used by applications or disk cache. + */ + free: number; + /** + * The total amount of swap memory available to the system. + */ + swapTotal: number; + /** + * The free amount of swap memory available to the system. + */ + swapFree: number; + } +} + +declare module 'electron' { + var electron: Electron.ElectronMainAndRenderer; + export = electron; +} + +interface NodeRequireFunction { + (moduleName: 'electron'): Electron.ElectronMainAndRenderer; +} From 70a573b4c214a2e6997702e0dbb001eb23595cca Mon Sep 17 00:00:00 2001 From: Pouya Kary Date: Sat, 3 Dec 2016 21:46:11 +0330 Subject: [PATCH 10/10] biggest refactor ever... --- core/api/api.ts | 142 +++++++++++----------- core/api/layer.ts | 4 +- core/api/stdlib/algorithms.ts | 4 +- core/api/stdlib/graphs.ts | 2 +- core/api/stdlib/kary-foundation.ts | 2 +- core/api/stdlib/sortings.ts | 2 +- core/constants.ts | 2 +- core/editor/graph.ts | 2 +- core/editor/selection.ts | 2 +- core/editor/snapobject.ts | 2 +- core/editor/svg.ts | 2 +- core/installer.ts | 2 +- core/language-tools.ts | 2 +- core/main.ts | 18 ++- core/objects/dot/circle.ts | 2 +- core/objects/dot/dot.ts | 6 +- core/objects/dot/drag-move.ts | 2 +- core/objects/dot/drag-start.ts | 2 +- core/objects/dot/drag-stop.ts | 2 +- core/objects/point.ts | 2 +- core/objects/vertex/arrow/interfaces.ts | 2 +- core/objects/vertex/arrow/line.ts | 2 +- core/objects/vertex/arrow/loop.ts | 2 +- core/objects/vertex/vertex-static.ts | 2 +- core/objects/vertex/vertex.ts | 2 +- core/rendering/circular.ts | 2 +- core/rendering/spiral.ts | 2 +- core/script/algorithms.ts | 2 +- core/script/engine.ts | 2 +- core/script/events.ts | 2 +- core/storage/storage.ts | 2 +- core/ui/algorithms-tab/view.ts | 2 +- core/ui/editor-tab/notebook/programmer.ts | 6 +- core/ui/editor-tab/notebook/say.ts | 4 +- core/ui/editor-tab/view.ts | 2 +- core/ui/events.ts | 2 +- core/ui/loader.ts | 2 +- core/ui/programmer-tab/ribbon.ts | 2 +- core/ui/programmer-tab/view.ts | 2 +- core/ui/tabs.ts | 2 +- core/ui/toolbar.ts | 2 +- 41 files changed, 133 insertions(+), 119 deletions(-) diff --git a/core/api/api.ts b/core/api/api.ts index 382de27..347c7bd 100644 --- a/core/api/api.ts +++ b/core/api/api.ts @@ -19,8 +19,8 @@ * Creates a new dot at a random coordination. * https://github.com/karyfoundation/graph/wiki/API#new-dot */ - function newdot ( ): KaryGraph.Dot { - return KaryGraph.API.AbstractionLayer.AddNewDot( ); + function newdot ( ): Graph.Dot { + return Graph.API.AbstractionLayer.AddNewDot( ); } // @@ -31,8 +31,8 @@ * Creates a new dot at a specified coordination. * https://github.com/karyfoundation/graph/wiki/API#new-dot-at */ - function newdotat ( x: number, y: number ): KaryGraph.Dot { - return new KaryGraph.Dot( x , y ); + function newdotat ( x: number, y: number ): Graph.Dot { + return new Graph.Dot( x , y ); } // @@ -43,10 +43,10 @@ * Creates a number of dots at some random places. * https://github.com/karyfoundation/graph/wiki/API#new-dots */ - function newdots ( howmuch: number ): Array { - var dots = new Array( ); + function newdots ( howmuch: number ): Array { + var dots = new Array( ); for ( var counter = 0; counter < howmuch; counter++ ) - dots.push( KaryGraph.API.AbstractionLayer.AddNewDot( ) ); + dots.push( Graph.API.AbstractionLayer.AddNewDot( ) ); return dots; } @@ -58,8 +58,8 @@ * gets a dot with it's numerical id. * https://github.com/karyfoundation/graph/wiki/API#get-dot */ - function getdot ( numberId: number ): KaryGraph.Dot { - return KaryGraph.API.AbstractionLayer.GetDotByNumberId( numberId ); + function getdot ( numberId: number ): Graph.Dot { + return Graph.API.AbstractionLayer.GetDotByNumberId( numberId ); } // @@ -70,8 +70,8 @@ * returns an array of dots based on the array of their ids. * https://github.com/karyfoundation/graph/wiki/API#get-dots */ - function getdots ( ids: Array ): Array { - let result = new Array( ); + function getdots ( ids: Array ): Array { + let result = new Array( ); for ( let id of ids ) result.push( getdot( id ) ); return result; @@ -84,10 +84,10 @@ /** * Does a function to each dot in the array. */ - function foreachdot ( dots: Array, - f: ( dot: KaryGraph.Dot ) => void ) { + function foreachdot ( dots: Array, + f: ( dot: Graph.Dot ) => void ) { for ( let dotOrId of dots ) - f( KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dotOrId ) ); + f( Graph.API.AbstractionLayer.GetDotByDotOrId( dotOrId ) ); } // @@ -97,9 +97,9 @@ /** * Does a function to all the dots around. */ - function foralldots ( f: ( dot: KaryGraph.Dot ) => void ) { - Object.keys( KaryGraph.Storage.Nodes ).forEach( key => { - f( KaryGraph.Storage.Nodes[ key ] ); + function foralldots ( f: ( dot: Graph.Dot ) => void ) { + Object.keys( Graph.Storage.Nodes ).forEach( key => { + f( Graph.Storage.Nodes[ key ] ); }); } @@ -111,7 +111,7 @@ * Returns an array of dot's of all the dots. */ function all ( ): Array { - return range( 1 , KaryGraph.Dot.TotalDots ); + return range( 1 , Graph.Dot.TotalDots ); } // @@ -122,11 +122,11 @@ * Connects an array of dots together * https://github.com/karyfoundation/graph/wiki/API#connect */ - function connect ( args: Array ): boolean { + function connect ( args: Array ): boolean { let result = false; for ( var i = 1; i < args.length; i++ ) { - let d1 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( args[ i - 1 ] ); - let d2 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( args[ i ] ); + let d1 = Graph.API.AbstractionLayer.GetDotByDotOrId( args[ i - 1 ] ); + let d2 = Graph.API.AbstractionLayer.GetDotByDotOrId( args[ i ] ); d1.ConnectTo( d2 ); //FIXME Should be true only if the connection is really established. result = true; @@ -142,11 +142,11 @@ * Connects an array of dots like a fan. * https://github.com/karyfoundation/graph/wiki/API#fan */ - function fan ( args: Array ): boolean { + function fan ( args: Array ): boolean { let result = false; for ( var i = 1; i < args.length; i++ ) { - let d1 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( args[ 0 ] ); - let d2 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( args[ i ] ); + let d1 = Graph.API.AbstractionLayer.GetDotByDotOrId( args[ 0 ] ); + let d2 = Graph.API.AbstractionLayer.GetDotByDotOrId( args[ i ] ); d1.ConnectTo( d2 ); } return result; @@ -160,10 +160,10 @@ * Disconnects two dots from each other. * https://github.com/karyfoundation/graph/wiki/API#disconnect */ - function disconnect ( a: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, - b: KaryGraph.API.AbstractionLayer.DotObjectOrDotID ): boolean { - let d1 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( a ); - let d2 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( b ); + function disconnect ( a: Graph.API.AbstractionLayer.DotObjectOrDotID, + b: Graph.API.AbstractionLayer.DotObjectOrDotID ): boolean { + let d1 = Graph.API.AbstractionLayer.GetDotByDotOrId( a ); + let d2 = Graph.API.AbstractionLayer.GetDotByDotOrId( b ); return d1.DisconnectFrom( d2 ); } @@ -175,10 +175,10 @@ * Checks if two dots are connected to each other. * https://github.com/karyfoundation/graph/wiki/API#has-edge */ - function hasEdge ( start: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, - end: KaryGraph.API.AbstractionLayer.DotObjectOrDotID ) { - let d1 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( start ); - let d2 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( end ); + function hasEdge ( start: Graph.API.AbstractionLayer.DotObjectOrDotID, + end: Graph.API.AbstractionLayer.DotObjectOrDotID ) { + let d1 = Graph.API.AbstractionLayer.GetDotByDotOrId( start ); + let d2 = Graph.API.AbstractionLayer.GetDotByDotOrId( end ); return d1.IsConnectedTo( d2 ); } @@ -191,7 +191,7 @@ * https://github.com/karyfoundation/graph/wiki/API#reset */ function reset ( ) { - KaryGraph.API.AbstractionLayer.Reset( ); + Graph.API.AbstractionLayer.Reset( ); } // @@ -202,11 +202,11 @@ * Returns the adjacency matrix of the the specified dots in graph. * https://github.com/karyfoundation/graph/wiki/API#matrix */ - function matrix ( input?: Array): number[][] { + function matrix ( input?: Array): number[][] { if ( input === undefined ) { - return KaryGraph.API.AbstractionLayer.CreateMatrix( all( ) ); + return Graph.API.AbstractionLayer.CreateMatrix( all( ) ); } else { - return KaryGraph.API.AbstractionLayer.CreateMatrix( input ); + return Graph.API.AbstractionLayer.CreateMatrix( input ); } } @@ -246,7 +246,7 @@ * https://github.com/karyfoundation/graph/wiki/API#count-dots */ function countdots ( ): number { - return KaryGraph.Dot.TotalDots; + return Graph.Dot.TotalDots; } // @@ -257,8 +257,8 @@ * Moves a dot to coordinates (x, y). * https://github.com/karyfoundation/graph/wiki/API#move */ - function move ( dot: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, x: number, y: number ) { - KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dot ).MoveTo( x, y ); + function move ( dot: Graph.API.AbstractionLayer.DotObjectOrDotID, x: number, y: number ) { + Graph.API.AbstractionLayer.GetDotByDotOrId( dot ).MoveTo( x, y ); } // @@ -269,8 +269,8 @@ * Moves a dot to the given x coordinate. * https://github.com/karyfoundation/graph/wiki/API#move-to-x */ - function movex ( dot: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, x: number ) { - let d = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dot ); + function movex ( dot: Graph.API.AbstractionLayer.DotObjectOrDotID, x: number ) { + let d = Graph.API.AbstractionLayer.GetDotByDotOrId( dot ); d.MoveTo( x, d.Position.Y ); } @@ -282,8 +282,8 @@ * Moves a dot to the given x coordinate. * https://github.com/karyfoundation/graph/wiki/API#move-to-y */ - function movey ( dot: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, y: number ) { - let d = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dot ); + function movey ( dot: Graph.API.AbstractionLayer.DotObjectOrDotID, y: number ) { + let d = Graph.API.AbstractionLayer.GetDotByDotOrId( dot ); d.MoveTo( d.Position.X, y ); } @@ -295,8 +295,8 @@ * Moves a dot by the given x coordinate. * https://github.com/karyfoundation/graph/wiki/API#move-by-x */ - function movebx ( dot: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, x: number ) { - let d = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dot ); + function movebx ( dot: Graph.API.AbstractionLayer.DotObjectOrDotID, x: number ) { + let d = Graph.API.AbstractionLayer.GetDotByDotOrId( dot ); d.MoveTo( d.Position.X + x, d.Position.Y ); } @@ -308,8 +308,8 @@ * Moves a dot by the given y coordinate. * https://github.com/karyfoundation/graph/wiki/API#move-by-y */ - function moveby ( dot: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, y: number ) { - let d = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dot ); + function moveby ( dot: Graph.API.AbstractionLayer.DotObjectOrDotID, y: number ) { + let d = Graph.API.AbstractionLayer.GetDotByDotOrId( dot ); d.MoveTo( d.Position.X, d.Position.Y + y ); } @@ -318,7 +318,7 @@ // function render ( option: string ) { - KaryGraph.API.AbstractionLayer.Render( option ); + Graph.API.AbstractionLayer.Render( option ); } // @@ -330,7 +330,7 @@ * https://github.com/karyfoundation/graph/wiki/API#tree */ function sort ( ) { - KaryGraph.API.StandardLibrary.Sortings.Tree( ); + Graph.API.StandardLibrary.Sortings.Tree( ); } // @@ -353,14 +353,14 @@ * Returns the size of the graph made of the specified dots in array. * https://github.com/karyfoundation/graph/wiki/API#size */ - function size ( dots?: Array ): number { + function size ( dots?: Array ): number { var size: number = 0; if ( dots === undefined ) - for ( var i = 0; i < KaryGraph.Dot.TotalDots; i++ ) + for ( var i = 0; i < Graph.Dot.TotalDots; i++ ) size += getdot(i + 1).NumberOfInputs(); else for ( var i = 0; i < dots.length; i++ ) - size += KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dots[ i ] ).NumberOfInputs( ); + size += Graph.API.AbstractionLayer.GetDotByDotOrId( dots[ i ] ).NumberOfInputs( ); return size; } @@ -372,8 +372,8 @@ * Returns the degree of a vertex. * https://github.com/karyfoundation/graph/wiki/API#degree */ - function degree ( dot: KaryGraph.API.AbstractionLayer.DotObjectOrDotID ): number { - return KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dot ).GetDegree( ); + function degree ( dot: Graph.API.AbstractionLayer.DotObjectOrDotID ): number { + return Graph.API.AbstractionLayer.GetDotByDotOrId( dot ).GetDegree( ); } // @@ -384,10 +384,10 @@ * Checks if two dots are neighbors. * https://github.com/karyfoundation/graph/wiki/API#neighbors */ - function neighbors ( a: KaryGraph.API.AbstractionLayer.DotObjectOrDotID, - b: KaryGraph.API.AbstractionLayer.DotObjectOrDotID ): boolean { - let d1 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( a ); - let d2 = KaryGraph.API.AbstractionLayer.GetDotByDotOrId( b ); + function neighbors ( a: Graph.API.AbstractionLayer.DotObjectOrDotID, + b: Graph.API.AbstractionLayer.DotObjectOrDotID ): boolean { + let d1 = Graph.API.AbstractionLayer.GetDotByDotOrId( a ); + let d2 = Graph.API.AbstractionLayer.GetDotByDotOrId( b ); return d1.IsConnectedTo( d2 ); } @@ -399,8 +399,8 @@ * Returns the neighborhood of a dot. * https://github.com/karyfoundation/graph/wiki/API#get-neighborhood */ - function neighborhood ( dot: KaryGraph.API.AbstractionLayer.DotObjectOrDotID ): KaryGraph.Dot[] { - return KaryGraph.API.AbstractionLayer.GetDotByDotOrId( dot ).GetNeighbors( ); + function neighborhood ( dot: Graph.API.AbstractionLayer.DotObjectOrDotID ): Graph.Dot[] { + return Graph.API.AbstractionLayer.GetDotByDotOrId( dot ).GetNeighbors( ); } // @@ -412,7 +412,7 @@ * https://github.com/karyfoundation/graph/wiki/API#clear-screen */ function cls ( ) { - KaryGraph.UI.Programmer.ClearNotebookScreen( ); + Graph.UI.Programmer.ClearNotebookScreen( ); } // ──────────────────────────────────────────────────────────────────────────────── @@ -441,7 +441,7 @@ // function completegraph( size: number ) { - KaryGraph.API.StandardLibrary.CreateCompleteGraph( size ); + Graph.API.StandardLibrary.CreateCompleteGraph( size ); } @@ -454,7 +454,7 @@ * https://github.com/karyfoundation/graph/wiki/API#eulerian-path */ function eulerianpath( ): boolean { - var verticesWithOddDegree = KaryGraph.API.AbstractionLayer.NumberOfOddVertices( ); + var verticesWithOddDegree = Graph.API.AbstractionLayer.NumberOfOddVertices( ); return ( verticesWithOddDegree == 0 || verticesWithOddDegree == 2 ); } @@ -467,7 +467,7 @@ * https://github.com/karyfoundation/graph/wiki/API#eulerian-cycle */ function euleriancycle( ): boolean { - var verticesWithOddDegree = KaryGraph.API.AbstractionLayer.NumberOfOddVertices( ); + var verticesWithOddDegree = Graph.API.AbstractionLayer.NumberOfOddVertices( ); return ( verticesWithOddDegree == 0 ); } @@ -476,7 +476,7 @@ // function bfs( start: number ) { - return KaryGraph.API.StandardLibrary.Algorithms.BFS( getdot( start ), -1 ); + return Graph.API.StandardLibrary.Algorithms.BFS( getdot( start ), -1 ); } // ──────────────────────────────────────────────────────────────────────────────── @@ -494,7 +494,7 @@ // function rnd ( num: number ): number { - return KaryGraph.Random( num ); + return Graph.Random( num ); } // @@ -536,7 +536,7 @@ // function width ( ): number { - return KaryGraph.API.AbstractionLayer.GetGraphViewWidth( ); + return Graph.API.AbstractionLayer.GetGraphViewWidth( ); } // @@ -544,7 +544,7 @@ // function height ( ): number { - return KaryGraph.API.AbstractionLayer.GetGraphViewHeight( ); + return Graph.API.AbstractionLayer.GetGraphViewHeight( ); } // ──────────────────────────────────────────────────────────────────────────────── @@ -563,7 +563,7 @@ /** A very small easter... */ function kary ( ) { - KaryGraph.API.KaryFoundation.CreateKaryHorseGraph( ); + Graph.API.KaryFoundation.CreateKaryHorseGraph( ); } // ──────────────────────────────────────────────────────────────────────────────── @@ -577,8 +577,8 @@ function using ( library: string ) { return NodeRequire( JoinPath([ GetHomeDir( ), - KaryGraph.GraphUserFolderPath, - KaryGraph.GraphUserFolderForLibraries, + Graph.GraphUserFolderPath, + Graph.GraphUserFolderForLibraries, library ])); } diff --git a/core/api/layer.ts b/core/api/layer.ts index 46a04bf..814b62d 100644 --- a/core/api/layer.ts +++ b/core/api/layer.ts @@ -11,7 +11,7 @@ * the API to reduce complexity when scripting */ -namespace KaryGraph.API.AbstractionLayer { +namespace Graph.API.AbstractionLayer { // // ─── TYPES ────────────────────────────────────────────────────────────────────── @@ -128,7 +128,7 @@ namespace KaryGraph.API.AbstractionLayer { break; case 'order': - KaryGraph.API.StandardLibrary.Sortings.Tree( ); + Graph.API.StandardLibrary.Sortings.Tree( ); break; default: diff --git a/core/api/stdlib/algorithms.ts b/core/api/stdlib/algorithms.ts index 1232a37..11faa15 100644 --- a/core/api/stdlib/algorithms.ts +++ b/core/api/stdlib/algorithms.ts @@ -4,7 +4,7 @@ // Author: Sina Bakhtiari // -namespace KaryGraph.API.StandardLibrary.Algorithms { +namespace Graph.API.StandardLibrary.Algorithms { // // ─── BFS ──────────────────────────────────────────────────────────────────────── @@ -14,7 +14,7 @@ namespace KaryGraph.API.StandardLibrary.Algorithms { let bfs = new Array>( ); let checked = new Array( ); - for ( let it = 0; it < KaryGraph.Dot.TotalDots; it++ ) { + for ( let it = 0; it < Graph.Dot.TotalDots; it++ ) { checked.push( false ); } diff --git a/core/api/stdlib/graphs.ts b/core/api/stdlib/graphs.ts index dfd419c..c1e8f82 100644 --- a/core/api/stdlib/graphs.ts +++ b/core/api/stdlib/graphs.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.API.StandardLibrary { +namespace Graph.API.StandardLibrary { // // ─── COMPLETE GRAPH ───────────────────────────────────────────────────────────── diff --git a/core/api/stdlib/kary-foundation.ts b/core/api/stdlib/kary-foundation.ts index 45e9ae2..e352db4 100644 --- a/core/api/stdlib/kary-foundation.ts +++ b/core/api/stdlib/kary-foundation.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.API.KaryFoundation { +namespace Graph.API.KaryFoundation { // // ─── THE KARY GRAPH ───────────────────────────────────────────────────────────── diff --git a/core/api/stdlib/sortings.ts b/core/api/stdlib/sortings.ts index bcb5ddb..cc7b3a7 100644 --- a/core/api/stdlib/sortings.ts +++ b/core/api/stdlib/sortings.ts @@ -4,7 +4,7 @@ // Author: Micha Hanselmann // -namespace KaryGraph.API.StandardLibrary.Sortings { +namespace Graph.API.StandardLibrary.Sortings { // // ─── CLASSIC TREE ─────────────────────────────────────────────────────── diff --git a/core/constants.ts b/core/constants.ts index 2a636fa..09271d9 100644 --- a/core/constants.ts +++ b/core/constants.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── GRAPH VIEW ───────────────────────────────────────────────────────────────── diff --git a/core/editor/graph.ts b/core/editor/graph.ts index 7437b89..6771251 100644 --- a/core/editor/graph.ts +++ b/core/editor/graph.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── GENERATE RANDOM NODES ────────────────────────────────────────────────────── diff --git a/core/editor/selection.ts b/core/editor/selection.ts index d8b7ea1..a306224 100644 --- a/core/editor/selection.ts +++ b/core/editor/selection.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { /** * Selection - Creates a selection box and at the end of the selection diff --git a/core/editor/snapobject.ts b/core/editor/snapobject.ts index 40d25e2..577c92b 100644 --- a/core/editor/snapobject.ts +++ b/core/editor/snapobject.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── ATTRIBUTE ────────────────────────────────────────────────────────────────── diff --git a/core/editor/svg.ts b/core/editor/svg.ts index 4cfc84b..50077ab 100644 --- a/core/editor/svg.ts +++ b/core/editor/svg.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── ADD EVENT TO SVG ─────────────────────────────────────────────────────────── diff --git a/core/installer.ts b/core/installer.ts index 75d81a2..47ce159 100644 --- a/core/installer.ts +++ b/core/installer.ts @@ -3,7 +3,7 @@ // Author: Pouya Kary // -namespace KaryGraph.Installer { +namespace Graph.Installer { // // ─── INSTALL OUTER DEPENDENCIES ───────────────────────────────────────────────── diff --git a/core/language-tools.ts b/core/language-tools.ts index 188d9ed..776a53c 100644 --- a/core/language-tools.ts +++ b/core/language-tools.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.LanguageTools { +namespace Graph.LanguageTools { // // ─── ARRAY EXISTS ─────────────────────────────────────────────────────────────── diff --git a/core/main.ts b/core/main.ts index 7b3707a..c428deb 100644 --- a/core/main.ts +++ b/core/main.ts @@ -7,7 +7,7 @@ /** * **Kary Graph** - A full featured Graph Theory Studio */ -namespace KaryGraph { +namespace Graph { // // ─── INIT ─────────────────────────────────────────────────────────────────────── @@ -36,7 +36,21 @@ namespace KaryGraph { function RenderStartingGraph ( ) { // Just remember this is the only place that using API // inside namespace "KaryGraph" is acceptable... - API.KaryFoundation.CreateKaryHorseGraph( ); + //API.KaryFoundation.CreateKaryHorseGraph( ); + + newdots( 15 ); + connect( range( 1, 15 ) ); + + let ys = [ 93, 171, 134, 271, 167, 134, 91, 169, 87, 130, 173, 281, 120, 81, 160 ]; + let counter = 0; + + foralldots( dot => { + dot.MoveTo( + ( counter + 1 ) * 50, + ys[ counter ] + ) + counter++; + }) } // ──────────────────────────────────────────────────────────────────────────────── diff --git a/core/objects/dot/circle.ts b/core/objects/dot/circle.ts index 74f8634..04a0189 100644 --- a/core/objects/dot/circle.ts +++ b/core/objects/dot/circle.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.Circle { +namespace Graph.Circle { // // ─── GENERATOR ────────────────────────────────────────────────────────────────── diff --git a/core/objects/dot/dot.ts b/core/objects/dot/dot.ts index 80f0e00..6788c78 100644 --- a/core/objects/dot/dot.ts +++ b/core/objects/dot/dot.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── DOT OBJECT ───────────────────────────────────────────────────────────────── @@ -290,8 +290,8 @@ namespace KaryGraph { // ─── GET NEIGHBORS ─────────────────────────────────────────────── // - public GetNeighbors ( ): KaryGraph.Dot[ ] { - var neighbors: KaryGraph.Dot[ ] = [ ]; + public GetNeighbors ( ): Graph.Dot[ ] { + var neighbors: Graph.Dot[ ] = [ ]; this.Inputs.forEach( input => { neighbors.push(Storage.Nodes[input]); diff --git a/core/objects/dot/drag-move.ts b/core/objects/dot/drag-move.ts index 66564b1..3620ab3 100644 --- a/core/objects/dot/drag-move.ts +++ b/core/objects/dot/drag-move.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── DRAG MOVE FUNCTION LIST ──────────────────────────────────────────────────── diff --git a/core/objects/dot/drag-start.ts b/core/objects/dot/drag-start.ts index d8605ac..d8bc835 100644 --- a/core/objects/dot/drag-start.ts +++ b/core/objects/dot/drag-start.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── DRAG START FUNCTION LIST ─────────────────────────────────────────────────── diff --git a/core/objects/dot/drag-stop.ts b/core/objects/dot/drag-stop.ts index 57143b2..b54cbea 100644 --- a/core/objects/dot/drag-stop.ts +++ b/core/objects/dot/drag-stop.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── DRAG START FUNCTION LIST ─────────────────────────────────────────────────── diff --git a/core/objects/point.ts b/core/objects/point.ts index 2947d5c..66bedb7 100644 --- a/core/objects/point.ts +++ b/core/objects/point.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── POINT OBJECT ─────────────────────────────────────────────────────────────── diff --git a/core/objects/vertex/arrow/interfaces.ts b/core/objects/vertex/arrow/interfaces.ts index a7510c1..27e8c7a 100644 --- a/core/objects/vertex/arrow/interfaces.ts +++ b/core/objects/vertex/arrow/interfaces.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── ARROW BASE INTERFACE ─────────────────────────────────────────────────────── diff --git a/core/objects/vertex/arrow/line.ts b/core/objects/vertex/arrow/line.ts index aed528f..5bfc439 100644 --- a/core/objects/vertex/arrow/line.ts +++ b/core/objects/vertex/arrow/line.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── DIRECTED LINE OBJECT ─────────────────────────────────────────────────────── diff --git a/core/objects/vertex/arrow/loop.ts b/core/objects/vertex/arrow/loop.ts index 2bc705f..b2b385c 100644 --- a/core/objects/vertex/arrow/loop.ts +++ b/core/objects/vertex/arrow/loop.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── DIRECTED LOOP OBJECT ─────────────────────────────────────────────────────── diff --git a/core/objects/vertex/vertex-static.ts b/core/objects/vertex/vertex-static.ts index 1f4f26f..ebed330 100644 --- a/core/objects/vertex/vertex-static.ts +++ b/core/objects/vertex/vertex-static.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.VertexStatic { +namespace Graph.VertexStatic { // // ─── GENERATE VERTEX ID ───────────────────────────────────────────────────────── diff --git a/core/objects/vertex/vertex.ts b/core/objects/vertex/vertex.ts index 0f42c4c..2c742a2 100644 --- a/core/objects/vertex/vertex.ts +++ b/core/objects/vertex/vertex.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -module KaryGraph { +module Graph { // // ─── VERTEX CLASS ─────────────────────────────────────────────────────────────── diff --git a/core/rendering/circular.ts b/core/rendering/circular.ts index d8a6be5..b51f340 100644 --- a/core/rendering/circular.ts +++ b/core/rendering/circular.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.Rendering { +namespace Graph.Rendering { // // ─── CIRCULAR RENDERING ───────────────────────────────────────────────────────── diff --git a/core/rendering/spiral.ts b/core/rendering/spiral.ts index 805ad09..740bfae 100644 --- a/core/rendering/spiral.ts +++ b/core/rendering/spiral.ts @@ -4,7 +4,7 @@ // Author: Micha Hanselmann // -namespace KaryGraph.Rendering { +namespace Graph.Rendering { // // ─── SPIRAL RENDERING ─────────────────────────────────────────────────────────── diff --git a/core/script/algorithms.ts b/core/script/algorithms.ts index f6cdbfb..9c84da1 100644 --- a/core/script/algorithms.ts +++ b/core/script/algorithms.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.ScriptEngine.Algorithms { +namespace Graph.ScriptEngine.Algorithms { // // ─── INTERFACES ───────────────────────────────────────────────────────────────── diff --git a/core/script/engine.ts b/core/script/engine.ts index c4c4e7a..079698c 100644 --- a/core/script/engine.ts +++ b/core/script/engine.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.ScriptEngine { +namespace Graph.ScriptEngine { // // ─── STATUS VARS ──────────────────────────────────────────────────────────────── diff --git a/core/script/events.ts b/core/script/events.ts index 0880692..e965b3c 100644 --- a/core/script/events.ts +++ b/core/script/events.ts @@ -5,7 +5,7 @@ // Author: Pouya Kary // -namespace KaryGraph.ScriptEngine.EventHandler { +namespace Graph.ScriptEngine.EventHandler { // // ─── INTERFACES ───────────────────────────────────────────────────────────────── diff --git a/core/storage/storage.ts b/core/storage/storage.ts index 57d5532..7054824 100644 --- a/core/storage/storage.ts +++ b/core/storage/storage.ts @@ -7,7 +7,7 @@ /** * This name space is used to store all the Graph's ***model information***. */ -namespace KaryGraph.Storage { +namespace Graph.Storage { // // ─── CONNECTIONS ──────────────────────────────────────────────────────────────── diff --git a/core/ui/algorithms-tab/view.ts b/core/ui/algorithms-tab/view.ts index ec12bf0..1a71b28 100644 --- a/core/ui/algorithms-tab/view.ts +++ b/core/ui/algorithms-tab/view.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.UI.AlgorithmsTab { +namespace Graph.UI.AlgorithmsTab { // // ─── MAKE AN ALGORITHM CONTROL VIEW ───────────────────────────────────────────── diff --git a/core/ui/editor-tab/notebook/programmer.ts b/core/ui/editor-tab/notebook/programmer.ts index 9fdb52d..aabb4ea 100644 --- a/core/ui/editor-tab/notebook/programmer.ts +++ b/core/ui/editor-tab/notebook/programmer.ts @@ -5,7 +5,7 @@ // /** This module is responsible for the */ -namespace KaryGraph.UI.Programmer { +namespace Graph.UI.Programmer { // // ─── DEFS ─────────────────────────────────────────────────────────────────────── @@ -179,9 +179,9 @@ namespace KaryGraph.UI.Programmer { // function RunAndGenerateResults ( code: string ) { - let runResults = KaryGraph.ScriptEngine.Run( code ); + let runResults = Graph.ScriptEngine.Run( code ); if ( runResults.success ) { - return KaryGraph.UI.Programmer.GenerateSayHTML( runResults.result ); + return Graph.UI.Programmer.GenerateSayHTML( runResults.result ); } else { return `
${ runResults.error }
` } diff --git a/core/ui/editor-tab/notebook/say.ts b/core/ui/editor-tab/notebook/say.ts index a916459..257f72b 100644 --- a/core/ui/editor-tab/notebook/say.ts +++ b/core/ui/editor-tab/notebook/say.ts @@ -11,7 +11,7 @@ */ -namespace KaryGraph.UI.Programmer { +namespace Graph.UI.Programmer { // // ─── SAY MAIN ─────────────────────────────────────────────────────────────────── @@ -59,7 +59,7 @@ namespace KaryGraph.UI.Programmer { -namespace KaryGraph.UI.Programmer.SayImplementations { +namespace Graph.UI.Programmer.SayImplementations { // // ────────────────────────────────────────────────────────────────────────────── I ────────── diff --git a/core/ui/editor-tab/view.ts b/core/ui/editor-tab/view.ts index 2133e31..4104b0e 100644 --- a/core/ui/editor-tab/view.ts +++ b/core/ui/editor-tab/view.ts @@ -5,7 +5,7 @@ // Author: Pouya Kary // -namespace KaryGraph { +namespace Graph { // // ─── GRAPH ────────────────────────────────────────────────────────────────────── diff --git a/core/ui/events.ts b/core/ui/events.ts index 330a3a1..8c405f6 100644 --- a/core/ui/events.ts +++ b/core/ui/events.ts @@ -5,7 +5,7 @@ // Author: Pouya Kary // -namespace KaryGraph.UI.Events { +namespace Graph.UI.Events { // // ─── WINDOW RESIZE ────────────────────────────────────────────────────────────── diff --git a/core/ui/loader.ts b/core/ui/loader.ts index 1188494..ef8778c 100644 --- a/core/ui/loader.ts +++ b/core/ui/loader.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.UI { +namespace Graph.UI { // // ─── LOAD IMAGES ──────────────────────────────────────────────────────────────── diff --git a/core/ui/programmer-tab/ribbon.ts b/core/ui/programmer-tab/ribbon.ts index 0c5e6be..b751b05 100644 --- a/core/ui/programmer-tab/ribbon.ts +++ b/core/ui/programmer-tab/ribbon.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.UI.ProgrammerTab.Ribbon { +namespace Graph.UI.ProgrammerTab.Ribbon { // // ─── ON RUN BUTTON CLICKED ────────────────────────────────────────────────────── diff --git a/core/ui/programmer-tab/view.ts b/core/ui/programmer-tab/view.ts index a2dd600..731e018 100644 --- a/core/ui/programmer-tab/view.ts +++ b/core/ui/programmer-tab/view.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.UI.ProgrammerTab { +namespace Graph.UI.ProgrammerTab { // // ─── GLOBALS ──────────────────────────────────────────────────────────────────── diff --git a/core/ui/tabs.ts b/core/ui/tabs.ts index 2db0bc6..020b828 100644 --- a/core/ui/tabs.ts +++ b/core/ui/tabs.ts @@ -4,7 +4,7 @@ // Author: Pouya Kary // -namespace KaryGraph.UI.Tabs { +namespace Graph.UI.Tabs { // // ─── ENUMS ────────────────────────────────────────────────────────────────────── diff --git a/core/ui/toolbar.ts b/core/ui/toolbar.ts index e437f40..b74473b 100644 --- a/core/ui/toolbar.ts +++ b/core/ui/toolbar.ts @@ -6,7 +6,7 @@ /// -namespace KaryGraph.UI.Toolbar { +namespace Graph.UI.Toolbar { // // ─── TOOLBAR MODE ───────────────────────────────────────────────────────────────