├── .gitignore ├── .gitmodules ├── .npmignore ├── LICENSE ├── Makefile ├── PKGBUILD ├── README.md ├── archive └── jsgtk-0.9.0.tar.gz ├── examples ├── b.js ├── base.js ├── browser.gjs ├── browser.js ├── es6.js ├── extend.js ├── hello-world.js ├── lang.gjs ├── lang.js ├── notify.js ├── os.js ├── uf.gjs └── unframed ├── experiments ├── .jsgtk ├── README.md ├── fake-wraps-for-camel-case.js ├── global-gjs.js ├── hello-gjs.js ├── hello-gjs.png ├── hello-gtk.js ├── hello-world ├── jsgtk-mess ├── jsgtk │ ├── System.js │ ├── child_process.js │ ├── console.js │ ├── crypto.js │ ├── env.js │ ├── events.js │ ├── fs.js │ ├── gi.js │ ├── os.js │ ├── path.js │ ├── stream.js │ └── timers.js ├── location.js ├── module.js ├── package │ ├── js │ │ └── demo.js │ └── package.json ├── proxied_gtk_modules.js ├── python-to.js └── test ├── jsgtk ├── jsgtk_modules ├── assert.js ├── buffer.js ├── child_process.js ├── console.js ├── crypto.js ├── events.js ├── fs.js ├── http.js ├── https.js ├── jsgtk │ ├── babel.js │ ├── buffer.js │ ├── constants.js │ ├── core_modules.js │ ├── extended.js │ ├── gtk_modules.js │ ├── hybrid_emitter.js │ ├── mainloop.js │ ├── node_modules.js │ ├── polyfills.js │ ├── promise.js │ └── util.js ├── module.js ├── os.js ├── path.js ├── process.js ├── punycode.js ├── querystring.js ├── stream.js ├── timers.js ├── tty.js ├── url.js ├── util.js └── zlib.js ├── package-lock.json ├── package.json ├── postinstall.sh ├── tests ├── db.sqlite ├── dblite.js ├── delayed.sh ├── geo.js ├── main.js ├── runtime.js └── spawn.js └── utils ├── compare ├── documentation ├── gir-to-jsgtk-gi.html └── jshint /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | npm-debug.log -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/jsgtk/38c725037b3bd3b26952e7ed7c97299f16336295/.gitmodules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | archive 2 | experiments 3 | node_modules 4 | tests 5 | utils 6 | .DS_Store 7 | .gitignore 8 | .gitmodules 9 | npm-debug.log 10 | Makefile 11 | PKGBUILD 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 - 2016 2 | 3 | JSGtk: Andrea Giammarchi @WebReflection 4 | API + some module: Node.js Foundation & Contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: aur build hint lint 2 | 3 | VERSION=`node -e "console.log(require('./package.json').version)"` 4 | 5 | build: 6 | echo "Building $(VERSION)" 7 | echo "/*jshint esversion:6,strict:true,node:true*/(function (e) {'use strict';e.JSGTK=true;e.VERSION='$(VERSION)';}(this));">jsgtk_modules/jsgtk/constants.js 8 | sleep 1 9 | make hint 10 | cp node_modules/babel-standalone/babel.min.js jsgtk_modules/jsgtk/babel.js 11 | cp node_modules/es6-promise/dist/es6-promise.min.js jsgtk_modules/jsgtk/promise.js 12 | sync 13 | sed -i 's/function y(t,e,n,r){try{t.call(e,n,r)}/function y(t,e,n,r){try{return void t.call(e,n,r)}/' jsgtk_modules/jsgtk/promise.js 14 | sync 15 | sed -i 's/n._state!==tt/return void n._state!==tt/' jsgtk_modules/jsgtk/promise.js 16 | if [ -d "aur" ]; then rm -r aur; fi 17 | mkdir -p aur/jsgtk/jsgtk 18 | cp jsgtk aur/jsgtk/jsgtk/jsgtk.sh 19 | sed -i "s/'node_modules', //" aur/jsgtk/jsgtk/jsgtk.sh 20 | sed -i "s/pkgver=[0-9.]*/pkgver=$(VERSION)/" PKGBUILD 21 | cp -r jsgtk_modules aur/jsgtk/jsgtk/ 22 | sync 23 | tar -zcvf jsgtk-$(VERSION).tar.gz -C aur/jsgtk jsgtk 24 | updpkgsums 25 | makepkg --printsrcinfo > .SRCINFO 26 | makepkg -srf 27 | sync 28 | rm *.tar.xz 29 | rm -r {aur,pkg,src} 30 | if [ -d ~/code/aur/jsgtk ]; then make aur; fi 31 | 32 | aur: 33 | cp {LICENSE,PKGBUILD} ~/code/aur/jsgtk 34 | mv .SRCINFO ~/code/aur/jsgtk 35 | mv *.tar.*z ~/code/aur/jsgtk 36 | echo "$(VERSION)" > ~/code/aur/jsgtk/version 37 | git add . 38 | git commit -m "Updating to $(VERSION)" 39 | git push 40 | git checkout gh-pages 41 | mkdir -p archive 42 | mv ~/code/aur/jsgtk/*.tar.*z archive/ 43 | mv ~/code/aur/jsgtk/version ./ 44 | git add . 45 | git commit -m "Update `cat version`" 46 | git push 47 | git checkout master 48 | 49 | hint: 50 | ./utils/jshint 51 | 52 | lint: 53 | ./utils/jshint 54 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Andrea Giammarchi 2 | pkgname=jsgtk 3 | pkgrel=1 4 | pkgver=0.12.1 5 | pkgdesc="A simplified approach to GJS for Node.JS and JavaScript developers." 6 | arch=('any') 7 | url="https://github.com/WebReflection/jsgtk" 8 | license=('MIT') 9 | depends=('gjs') 10 | source=(https://webreflection.github.io/jsgtk/archive/$pkgname-$pkgver.tar.gz) 11 | md5sums=('a54b2e1521d77f2185a5532a2aef69f6') 12 | 13 | package() { 14 | 15 | cd "${srcdir}/${pkgname}" 16 | 17 | # Install program files 18 | mkdir -p "${pkgdir}/usr/lib/${pkgname}" 19 | cp -r jsgtk_modules "${pkgdir}/usr/lib/${pkgname}" 20 | 21 | # Install a launcher 22 | install -Dm755 ${pkgname}.sh "${pkgdir}/usr/bin/${pkgname}" 23 | 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSGtk+ [ DEPRECATED ] 2 | 3 | This project is death. **Long live [cgjs](https://github.com/cgjs/cgjs)**. 4 | 5 | - - - 6 | 7 | This project has not received enough support to be sustainable Open Source Software and it was too big to be maintained by a single developer. 8 | 9 | The new alternative, based on modern js52+ SpiderMonkey engine, dropped 90% of the useless crap I've implemented in here. 10 | 11 | Lesson learned, move on and switch to [CGJS](https://github.com/WebReflection/cgjs) today. 12 | 13 | - - - 14 | 15 | 16 | ### Project Ideas 17 | Following the current list of project ideas and achievements: 18 | 19 | * a _CommonJS_-like module loader, compatible with any [npm](https://www.npmjs.com/) module 20 | * a _Node.JS_ like environment with `process`, `console`, `require` and most common _Node.JS_ [core modules](https://github.com/WebReflection/jsgtk/tree/master/jsgtk_modules) 21 | * an automatically transformed ECMAScript 2015 syntax for _GJS SpiderMonkey_ environment via [babel-standalone](https://github.com/WebReflection/babel-standalone) and [loaded modules](https://github.com/WebReflection/jsgtk/blob/master/examples/es6.js) 22 | * a `camelCase` imported namespace to make _GJS_ modules look more _JavaScripty_ 23 | * a cross platform, lightweight, yet powerful namespace that brings **_Gtk_ native Widgets** to every compatible OS 24 | * a _Node.JS_ and "_Web.JS_" friendly way to attach/detach signals ( `obj.on(signal, listener)` ) 25 | 26 | 27 | 28 | ### Platforms 29 | Currently developed and tested on **Linux** ([ArchLinux](https://www.archlinux.org/), [Debian](http://www.debian.org/), [Fedora](https://getfedora.org/), [Ubuntu](http://www.ubuntu.com/)) and **Darwin** ([OSX](http://www.apple.com/uk/osx/)) platforms. 30 | 31 | 32 | 33 | ### How To Install 34 | 35 | On **ArchLinux** you can simply use `yaourt -S --needed jsgtk` installing from [AUR](https://aur.archlinux.org/packages/jsgtk/). 36 | 37 | On other common **Linux** distros, and also on **OSX**, you can use the following terminal command and follow its instructions: 38 | ```sh 39 | # optionally you can export WEBKIT=true upfront 40 | sh -c "$(curl -fsSL https://webreflection.github.io/jsgtk/install)" 41 | ``` 42 | 43 | If you would like to know what that script does, feel free to read the used [install file content](https://github.com/WebReflection/jsgtk/blob/gh-pages/install). 44 | 45 | 46 | There is also a `jsgtk` package on **npm** and it is possible to install it via `npm install -g jsgtk`. 47 | However, in this case, please be sure you have installed dependencies first. 48 | Some hint about best way to install them might be printed on the terminal once installed. 49 | 50 | 51 | 52 | ### Dependencies 53 | This project trusts and uses 100% _GJS_, which is usually available through one of the following procedures: 54 | 55 | * on **ArchLinux**: `pacman -S --needed gjs` and optionally `webkit2gtk` for the `browser.js` example 56 | * on **Debian** and **Ubuntu**: `apt-get install gjs` and optionally `libwebkit2gtk-4.0` or higher for the `browser.js` example 57 | * on **Fedora**: pretty much everything is already installed (both `gjs` and `webkitgtk4` are available) 58 | * on **OSX Homebrew**: `brew install gtk+3 gjs` and optionally `webkitgtk` for the `browser.js` example (although right now there is some problem) 59 | * on **OSX MacPorts**: `port install gjs adwaita-icon-theme xorg-server xinit` and optionally `webkit2-gtk` for the `browser.js` example. Please note `xorg-server` requires to log out and back in again before it can work. 60 | 61 | Please remember [using the installer](https://github.com/WebReflection/jsgtk#how-to-install) brings in all dependencies so most likely there's nothing else to do. 62 | 63 | 64 | 65 | ### Gtk/UI Examples 66 | To test if everything is fine, you can create a `test.js` file and run it via `jsgtk test.js` or, after `chmod +x test.js`, directly via `./test.js`: 67 | ```js 68 | #!/usr/bin/env jsgtk 69 | const Gtk = require('Gtk'); 70 | Gtk.init(null); 71 | const win = new Gtk.Window({ 72 | title: 'jsgtk', 73 | type: Gtk.WindowType.TOPLEVEL, 74 | windowPosition: Gtk.WindowPosition.CENTER 75 | }) 76 | .once('show', () => { 77 | win.setKeepAbove(true); 78 | Gtk.main(); 79 | }) 80 | .on('destroy', Gtk.mainQuit) 81 | ; 82 | win.add(new Gtk.Label({label: 'Hello jsGtk+'})); 83 | win.setDefaultSize(200, 80); 84 | win.showAll(); 85 | ``` 86 | 87 | There is also a [jsgtk-examples](https://github.com/WebReflection/jsgtk-examples#jsgtk-examples) repository which includes most common `GJS` examples, readapted for `jsgtk`. 88 | 89 | 90 | 91 | #### Tests and other examples 92 | If you clone this repository, `npm test` is the way to test few functionalities. 93 | Once cloned locally, you can also try `./jsgtk examples/base.js` or [any other file](https://github.com/WebReflection/jsgtk/tree/master/examples). 94 | 95 | If you'd like to debug an application, pass a `-d` or a `--debug` argument to the app. 96 | 97 | Optionally, you can set `GDK_DEBUG=all` to have all possible info about the app. 98 | 99 | 100 | ### Why not [node-gir](https://github.com/creationix/node-gir) or [node-gtk](https://github.com/WebReflection/node-gtk)? 101 | Well, apparently both projects are stuck/abandoned and unfortunately there's no other option ^_^;; -------------------------------------------------------------------------------- /archive/jsgtk-0.9.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/jsgtk/38c725037b3bd3b26952e7ed7c97299f16336295/archive/jsgtk-0.9.0.tar.gz -------------------------------------------------------------------------------- /examples/b.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jsgtk 2 | 3 | const Gtk = require('Gtk'); 4 | const WebKit2 = require('WebKit2'); 5 | 6 | Gtk.init(null); 7 | 8 | let view = new WebKit2.WebView(); 9 | view.loadUri('https://google.com'); 10 | let window = new Gtk.Window(); 11 | window.add(view); 12 | window.on('destroy', Gtk.mainQuit); 13 | window.showAll(); 14 | 15 | console.log(view.get_tls_info()); 16 | 17 | Gtk.main(); -------------------------------------------------------------------------------- /examples/base.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jsgtk 2 | 3 | // if you have installed jsgtk globally via npm 4 | // you can simply run: jsgtk base.js 5 | console.info('Hello jsGtk+'); 6 | console.log( 7 | require('util').inspect({ 8 | __filename: __filename, 9 | __dirname: __dirname, 10 | 'process.cwd()': process.cwd(), 11 | 'process.argv': process.argv, 12 | "require('fs').readdirSync('./') => ": require('fs').readdirSync('./') 13 | }, { 14 | colors: true 15 | }) 16 | ); -------------------------------------------------------------------------------- /examples/browser.gjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env gjs 2 | 3 | // A basic GJS Webkit based browser example. 4 | // Similar logic and basic interface found in this PyGTK example: 5 | // http://www.eurion.net/python-snippets/snippet/Webkit%20Browser.html 6 | 7 | ;(function (Gtk, WebKit2) {'use strict'; 8 | 9 | // necessary to initialize the graphic environment 10 | // if this fails it means the host cannot show GTK3 11 | Gtk.init(null); 12 | 13 | const 14 | 15 | argv = ARGV, 16 | 17 | // main program window 18 | window = new Gtk.Window({ 19 | title: 'jsGtk+ browser', 20 | type : Gtk.WindowType.TOPLEVEL, 21 | window_position: Gtk.WindowPosition.CENTER 22 | }), 23 | // the WebKit2 browser wrapper 24 | webView = new WebKit2.WebView(), 25 | // toolbar with buttons 26 | toolbar = new Gtk.Toolbar(), 27 | // buttons to go back, go forward, or refresh 28 | button = { 29 | back: Gtk.ToolButton.new_from_stock(Gtk.STOCK_GO_BACK), 30 | forward: Gtk.ToolButton.new_from_stock(Gtk.STOCK_GO_FORWARD), 31 | refresh: Gtk.ToolButton.new_from_stock(Gtk.STOCK_REFRESH) 32 | }, 33 | // where the URL is written and shown 34 | urlBar = new Gtk.Entry(), 35 | // the browser container, so that's scrollable 36 | scrollWindow = new Gtk.ScrolledWindow({}), 37 | // horizontal and vertical boxes 38 | hbox = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL}), 39 | vbox = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL}) 40 | ; 41 | 42 | // Setting up optional Dark theme (gotta love it!) 43 | // ./browser.js google.com --dark 44 | if (argv.some(info => info === '--dark')) { 45 | let gtkSettings = Gtk.Settings.get_default(); 46 | gtkSettings.gtk_application_prefer_dark_theme = true; 47 | gtkSettings.gtk_theme_name = 'Adwaita'; 48 | } else if(argv.some(info => info === '--light')) { 49 | let gtkSettings = Gtk.Settings.get_default(); 50 | gtkSettings.gtk_application_prefer_dark_theme = false; 51 | gtkSettings.gtk_theme_name = 'Adwaita'; 52 | } 53 | 54 | // open first argument or Google 55 | webView.load_uri(url(argv.filter(url => '-' !== url[0])[0] || 'google.com')); 56 | 57 | // whenever a new page is loaded ... 58 | webView.connect('load-changed', (widget, loadEvent, data) => { 59 | switch (loadEvent) { 60 | case 2: // XXX: where is WEBKIT_LOAD_COMMITTED ? 61 | // ... update the URL bar with the current adress 62 | urlBar.set_text(widget.get_uri()); 63 | button.back.set_sensitive(webView.can_go_back()); 64 | button.forward.set_sensitive(webView.can_go_forward()); 65 | break; 66 | } 67 | }); 68 | 69 | // configure buttons actions 70 | button.back.connect('clicked', () => webView.go_back()); 71 | button.forward.connect('clicked', () => webView.go_forward()); 72 | button.refresh.connect('clicked', () => webView.reload()); 73 | 74 | // enrich the toolbar 75 | toolbar.add(button.back); 76 | toolbar.add(button.forward); 77 | toolbar.add(button.refresh); 78 | 79 | // define "enter" / call-to-action event 80 | // whenever the url changes on the bar 81 | urlBar.connect('activate', () => { 82 | let href = url(urlBar.get_text()); 83 | urlBar.set_text(href); 84 | webView.load_uri(href); 85 | }); 86 | 87 | // make the container scrollable 88 | scrollWindow.add(webView); 89 | 90 | // pack horizontally toolbar and url bar 91 | hbox.pack_start(toolbar, false, false, 0); 92 | hbox.pack_start(urlBar, true, true, 8); 93 | 94 | // pack vertically top bar (hbox) and scrollable window 95 | vbox.pack_start(hbox, false, true, 0); 96 | vbox.pack_start(scrollWindow, true, true, 0); 97 | 98 | // configure main window 99 | window.set_default_size(1024, 720); 100 | window.set_resizable(true); 101 | window.connect('show', () => { 102 | // bring it on top in OSX 103 | window.set_keep_above(true); 104 | Gtk.main() 105 | }); 106 | window.connect('destroy', () => Gtk.main_quit()); 107 | window.connect('delete_event', () => false); 108 | 109 | // add vertical ui and show them all 110 | window.add(vbox); 111 | window.show_all(); 112 | 113 | // little helper 114 | // if link doesn't have a protocol, prefixes it via http:// 115 | function url(href) { 116 | return /^([a-z]{2,}):/.test(href) ? href : ('http://' + href); 117 | } 118 | 119 | }( 120 | imports.gi.Gtk, 121 | imports.gi.WebKit2 122 | )); -------------------------------------------------------------------------------- /examples/browser.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jsgtk 2 | 3 | // A basic JSGTK Webkit based browser example. 4 | // Similar logic and basic interface found in this PyGTK example: 5 | // http://www.eurion.net/python-snippets/snippet/Webkit%20Browser.html 6 | 7 | ;(function (Gtk, WebKit2) {'use strict'; 8 | 9 | // necessary to initialize the graphic environment 10 | // if this fails it means the host cannot show GTK3 11 | Gtk.init(null); 12 | 13 | const 14 | 15 | argv = process.argv.slice(2), 16 | 17 | // main program window 18 | window = new Gtk.Window({ 19 | title: 'jsGtk+ browser', 20 | type : Gtk.WindowType.TOPLEVEL, 21 | windowPosition: Gtk.WindowPosition.CENTER 22 | }), 23 | // the WebKit2 browser wrapper 24 | webView = new WebKit2.WebView(), 25 | // toolbar with buttons 26 | toolbar = new Gtk.Toolbar(), 27 | // buttons to go back, go forward, or refresh 28 | button = { 29 | back: Gtk.ToolButton.newFromStock(Gtk.STOCK_GO_BACK), 30 | forward: Gtk.ToolButton.newFromStock(Gtk.STOCK_GO_FORWARD), 31 | refresh: Gtk.ToolButton.newFromStock(Gtk.STOCK_REFRESH) 32 | }, 33 | // where the URL is written and shown 34 | urlBar = new Gtk.Entry(), 35 | // the browser container, so that's scrollable 36 | scrollWindow = new Gtk.ScrolledWindow({}), 37 | // horizontal and vertical boxes 38 | hbox = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL}), 39 | vbox = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL}) 40 | ; 41 | 42 | // Setting up optional Dark theme (gotta love it!) 43 | // ./browser.js google.com --dark 44 | if (argv.some(info => info === '--dark')) { 45 | let gtkSettings = Gtk.Settings.getDefault(); 46 | gtkSettings.gtkApplicationPreferDarkTheme = true; 47 | gtkSettings.gtkThemeName = 'Adwaita'; 48 | } else if(argv.some(info => info === '--light')) { 49 | let gtkSettings = Gtk.Settings.getDefault(); 50 | gtkSettings.gtkApplicationPreferDarkTheme = false; 51 | gtkSettings.gtkThemeName = 'Adwaita'; 52 | } 53 | 54 | // open first argument or Google 55 | webView.loadUri(url(argv.filter(url => '-' !== url[0])[0] || 'google.com')); 56 | 57 | // whenever a new page is loaded ... 58 | webView.connect('load-changed', (widget, loadEvent, data) => { 59 | switch (loadEvent) { 60 | case 2: // XXX: where is WEBKIT_LOAD_COMMITTED ? 61 | // ... update the URL bar with the current adress 62 | urlBar.setText(widget.getUri()); 63 | button.back.setSensitive(webView.canGoBack()); 64 | button.forward.setSensitive(webView.canGoForward()); 65 | break; 66 | } 67 | }); 68 | 69 | // configure buttons actions 70 | button.back.connect('clicked', () => webView.goBack()); 71 | button.forward.connect('clicked', () => webView.goForward()); 72 | button.refresh.connect('clicked', () => webView.reload()); 73 | 74 | // enrich the toolbar 75 | toolbar.add(button.back); 76 | toolbar.add(button.forward); 77 | toolbar.add(button.refresh); 78 | 79 | // define "enter" / call-to-action event 80 | // whenever the url changes on the bar 81 | urlBar.connect('activate', () => { 82 | let href = url(urlBar.getText()); 83 | urlBar.setText(href); 84 | webView.loadUri(href); 85 | }); 86 | 87 | // make the container scrollable 88 | scrollWindow.add(webView); 89 | 90 | // pack horizontally toolbar and url bar 91 | hbox.packStart(toolbar, false, false, 0); 92 | hbox.packStart(urlBar, true, true, 8); 93 | 94 | // pack vertically top bar (hbox) and scrollable window 95 | vbox.packStart(hbox, false, true, 0); 96 | vbox.packStart(scrollWindow, true, true, 0); 97 | 98 | // configure main window 99 | window.setDefaultSize(1024, 720); 100 | window.setResizable(true); 101 | window.connect('show', () => { 102 | // bring it on top in OSX 103 | window.setKeepAbove(true); 104 | setTimeout(() => { 105 | window.setKeepAbove(false); 106 | window.grabFocus(); 107 | }, 100); 108 | Gtk.main() 109 | }); 110 | window.connect('destroy', () => Gtk.mainQuit()); 111 | window.connect('delete_event', () => false); 112 | 113 | // add vertical ui and show them all 114 | window.add(vbox); 115 | window.showAll(); 116 | 117 | // little helper 118 | // if link doesn't have a protocol, prefixes it via http:// 119 | function url(href) { 120 | return /^([a-z]{2,}):/.test(href) ? href : ('http://' + href); 121 | } 122 | 123 | }( 124 | require('Gtk'), 125 | require('WebKit2') 126 | )); -------------------------------------------------------------------------------- /examples/es6.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class Named { 4 | constructor(name = 'ES2015') { 5 | this.name = name; 6 | } 7 | } 8 | 9 | console.log('' + new class Test extends Named { 10 | constructor(name) { 11 | super(name); 12 | return { 13 | name: this.name, 14 | toString() { 15 | return `Hello ${this.name}`; 16 | } 17 | }; 18 | } 19 | }); -------------------------------------------------------------------------------- /examples/extend.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jsgtk 2 | 3 | const Gtk = require("Gtk"); 4 | 5 | class Window extends Gtk.Window { 6 | constructor(props, extras) { 7 | super(props); 8 | if (extras) this.setup(extras); 9 | } 10 | setup(props) { 11 | this.add(new Gtk.Label({label: props.label || ''})); 12 | this 13 | .once('show', Gtk.main) 14 | .on('destroy', Gtk.mainQuit) 15 | .setDefaultSize( 16 | props.width || 200, 17 | props.height || 100 18 | ) 19 | ; 20 | } 21 | } 22 | 23 | Gtk.init(null); 24 | new Window( 25 | { // Gtk.Window setup 26 | type: Gtk.WindowType.TOPLEVEL, 27 | windowPosition: Gtk.WindowPosition.CENTER 28 | }, 29 | { // extra setup, if needed 30 | width: 120, 31 | height: 60, 32 | label: 'hello' 33 | } 34 | ).showAll(); 35 | -------------------------------------------------------------------------------- /examples/hello-world.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jsgtk 2 | 3 | let 4 | argv = process.argv.slice(2), 5 | Gtk = require('Gtk'), 6 | darkTheme = (choice) => { 7 | let gtkSettings = Gtk.Settings.getDefault(); 8 | gtkSettings.gtkApplicationPreferDarkTheme = choice; 9 | gtkSettings.gtkThemeName = 'Adwaita'; 10 | }, 11 | win 12 | ; 13 | 14 | Gtk.init(null); 15 | 16 | win = new Gtk.Window({ 17 | title: 'jsgtk', 18 | type : Gtk.WindowType.TOPLEVEL, 19 | windowPosition: Gtk.WindowPosition.CENTER 20 | }); 21 | 22 | if (argv.some(info => info === '--dark')) { 23 | darkTheme(true); 24 | } else if(argv.some(info => info === '--light')) { 25 | darkTheme(false); 26 | } 27 | 28 | win 29 | .once('show', () => { 30 | win.setKeepAbove(true); 31 | setTimeout(() => { 32 | win.setKeepAbove(false); 33 | win.grabFocus(); // TODO: ignored in OSX ? 34 | }, 100); 35 | Gtk.main(); 36 | }) 37 | .on('destroy', Gtk.mainQuit) 38 | .add(Gtk.Label.new('Hello jsGtk+')) 39 | ; 40 | 41 | win.setDefaultSize(200, 80); 42 | win.showAll(); 43 | -------------------------------------------------------------------------------- /examples/lang.gjs: -------------------------------------------------------------------------------- 1 | // see lang.js to see how different is jsgtk 2 | 3 | const GLib = imports.gi.GLib; 4 | const GObject = imports.gi.GObject; 5 | const Gtk = imports.gi.Gtk; 6 | const Lang = imports.lang; 7 | Gtk.init(null); 8 | 9 | const TimerLabel = new Lang.Class({ 10 | Name: 'TimerLabel', 11 | Extends: Gtk.Label, 12 | Properties: { 13 | 'timeout': GObject.ParamSpec.uint('timeout', '', '', 14 | GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 15 | 1, 10, 1), 16 | 'count': GObject.ParamSpec.uint('count', '', '', 17 | GObject.ParamFlags.READABLE, 0, GLib.MAXUINT32, 0), 18 | }, 19 | _init: function (props) { 20 | this.parent(props); 21 | this._count = 0; 22 | this.label = 'Hello World!'; 23 | GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, this.timeout, () => { 24 | this._count++; 25 | this.notify('count'); 26 | return GLib.SOURCE_CONTINUE; 27 | }); 28 | }, 29 | get count() { 30 | return this._count; 31 | }, 32 | }); 33 | 34 | let win = new Gtk.Window(); 35 | let label = new TimerLabel(); 36 | win.add(label); 37 | win.connect('destroy', Gtk.main_quit); 38 | label.connect('notify::count', (obj) => { 39 | if (obj.count === 2) 40 | Gtk.main_quit(); 41 | }); 42 | win.show_all(); 43 | Gtk.main(); 44 | -------------------------------------------------------------------------------- /examples/lang.js: -------------------------------------------------------------------------------- 1 | const Gtk = require('Gtk'); 2 | 3 | @GObjectProperties({ 4 | interval: { 5 | type: 'uint', 6 | value: 1000, 7 | flags: ['rw', 'co'] 8 | }, 9 | count: { 10 | type: 'uint', 11 | flags: ['r'] 12 | } 13 | }) 14 | class TimerLabel extends Gtk.Label { 15 | constructor(props) { 16 | super(props); 17 | this.label = 'Hello World!'; 18 | setInterval(() => { 19 | this._count++; 20 | this.notify('count'); 21 | }, this.interval); 22 | } 23 | } 24 | 25 | Gtk.init(null); 26 | 27 | let win = new Gtk.Window(); 28 | win.add( 29 | new TimerLabel() 30 | .on('notify::count', (obj) => { 31 | if (obj.count === 2 && obj.interval === 1000) 32 | Gtk.mainQuit(); // ^ just as example 33 | }) 34 | ); 35 | win 36 | .on('show', Gtk.main) 37 | .on('destroy', Gtk.mainQuit) 38 | .showAll() 39 | ; 40 | -------------------------------------------------------------------------------- /examples/notify.js: -------------------------------------------------------------------------------- 1 | require('child_process').spawn( 2 | 'notify-send', 3 | process.argv.slice(-2) 4 | ); -------------------------------------------------------------------------------- /examples/os.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | 3 | var outcome = {}; 4 | 5 | Object.keys(os).forEach(key => { 6 | outcome[key] = typeof os[key] === 'function' ? 7 | os[key]() : os[key]; 8 | }); 9 | 10 | console.log( 11 | require('util').inspect( 12 | outcome, 13 | {colors: true, depth: null} 14 | ) 15 | ); -------------------------------------------------------------------------------- /examples/uf.gjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env gjs 2 | 3 | ;(function (Gtk, WebKit2) {'use strict'; 4 | Gtk.init(null); 5 | const 6 | argv = ARGV, 7 | window = new Gtk.Window({ 8 | title: 'GJS browser', 9 | type : Gtk.WindowType.TOPLEVEL, 10 | decorated: false, 11 | window_position: Gtk.WindowPosition.CENTER 12 | }), 13 | webView = new WebKit2.WebView(), 14 | wvSettings = webView.get_settings(), 15 | scrollWindow = new Gtk.ScrolledWindow({}), 16 | vbox = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL}), 17 | gtkSettings = Gtk.Settings.get_default(); 18 | ; 19 | 20 | gtkSettings.gtk_application_prefer_dark_theme = true; 21 | gtkSettings.gtk_theme_name = 'Adwaita'; 22 | 23 | [ 24 | 'enable_webgl', 25 | 'enable_webaudio', 26 | 'enable_accelerated_compositing' 27 | ].forEach(function (key) { 28 | wvSettings[key] = true; 29 | }); 30 | 31 | webView.load_uri(url(argv.filter(url => '-' !== url[0])[0] || 'google.com')); 32 | scrollWindow.add(webView); 33 | vbox.pack_start(scrollWindow, true, true, 0); 34 | window.set_default_size(1920, 1080); 35 | window.set_resizable(true); 36 | window.connect('show', () => { 37 | window.fullscreen(); 38 | Gtk.main() 39 | }); 40 | window.connect('destroy', () => Gtk.main_quit()); 41 | window.connect('delete_event', () => false); 42 | window.add(vbox); 43 | window.show_all(); 44 | function url(href) { 45 | return /^([a-z]{2,}):/.test(href) ? href : ('http://' + href); 46 | } 47 | }( 48 | imports.gi.Gtk, 49 | imports.gi.WebKit2 50 | )); -------------------------------------------------------------------------------- /examples/unframed: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jsgtk 2 | 3 | ;(function (Gtk, WebKit2) {'use strict'; 4 | Gtk.init(null); 5 | const 6 | argv = process.argv.slice(2), 7 | window = new Gtk.Window({ 8 | title: 'jsGtk+ browser', 9 | type : Gtk.WindowType.TOPLEVEL, 10 | decorated: false, 11 | windowPosition: Gtk.WindowPosition.CENTER 12 | }), 13 | webView = new WebKit2.WebView(), 14 | scrollWindow = new Gtk.ScrolledWindow({}), 15 | vbox = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL}) 16 | ; 17 | if (argv.some(info => info === '--dark')) { 18 | let gtkSettings = Gtk.Settings.getDefault(); 19 | gtkSettings.gtkApplicationPreferDarkTheme = true; 20 | gtkSettings.gtkThemeName = 'Adwaita'; 21 | } else if(argv.some(info => info === '--light')) { 22 | let gtkSettings = Gtk.Settings.getDefault(); 23 | gtkSettings.gtkApplicationPreferDarkTheme = false; 24 | gtkSettings.gtkThemeName = 'Adwaita'; 25 | } 26 | webView.loadUri(url(argv.filter(url => '-' !== url[0])[0] || 'google.com')); 27 | scrollWindow.add(webView); 28 | vbox.packStart(scrollWindow, true, true, 0); 29 | window.setDefaultSize(1024, 720); 30 | window.setResizable(true); 31 | window.connect('show', () => { 32 | //window.fullscreen(); 33 | Gtk.main() 34 | }); 35 | window.connect('destroy', () => Gtk.mainQuit()); 36 | window.connect('delete_event', () => false); 37 | window.add(vbox); 38 | window.showAll(); 39 | function url(href) { 40 | return /^([a-z]{2,}):/.test(href) ? href : ('http://' + href); 41 | } 42 | }( 43 | require('Gtk'), 44 | require('WebKit2') 45 | )); -------------------------------------------------------------------------------- /experiments/.jsgtk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // (C) 2015 Andrea Giammarchi - @WebReflection 4 | 5 | var 6 | child_process = require('child_process'), 7 | path = require('path'), 8 | executable = process.argv[1], 9 | gjsARGV = process.argv.slice(2), 10 | nodeModules = '', 11 | logError = function (err) { 12 | console.error(err.toString()); 13 | }, 14 | cwd = process.cwd(), 15 | trim = ''.trim || function () { 16 | return this.replace(/^\s+|\s+$/g, ''); 17 | } 18 | ; 19 | 20 | if (!gjsARGV.length) process.exit(0); 21 | 22 | process.on('uncaughtException', logError); 23 | 24 | child_process.spawn('npm', ['config', 'get', 'prefix']).on('close', function () { 25 | var 26 | DEVEL = executable.slice(-6) === '.jsgtk', 27 | bin = DEVEL ? path.join('', '.jsgtk') : path.join('', 'bin', ''), 28 | lIO = executable.lastIndexOf(bin), 29 | jsgtkModule = DEVEL ? 30 | executable.slice(0, lIO) : 31 | path.join( 32 | lIO < 0 ? executable : executable.slice(0, lIO), 33 | 'lib', 34 | 'node_modules', 35 | 'jsgtk' 36 | ) 37 | ; 38 | // console.log(['- - - - - -', executable, jsgtkModule, __dirname, nodeModules + '/lib', '- - - - - -'].join('\n')); 39 | child_process.spawn( 40 | 'gjs', [ 41 | '-I', jsgtkModule, 42 | '-I', __dirname, 43 | '-I', path.join(trim.call(nodeModules), 'lib'), 44 | '-c', ''.concat( 45 | ';imports.jsgtk.env;', 46 | ';process.cwd = () => ', JSON.stringify(cwd), 47 | ';require(', JSON.stringify(path.resolve(cwd, gjsARGV[0])), ')' 48 | ) 49 | ].concat('jsgtk', gjsARGV), 50 | { 51 | stdio: [0, 0, 0] 52 | } 53 | ) 54 | .on('error', logError) 55 | .on('close', function () { 56 | process.exit(0); 57 | }) 58 | .on('exit', function (code) { 59 | process.exit(code); 60 | }) 61 | } 62 | ).stdout.on('data', function (data) { 63 | nodeModules += data.toString(); 64 | }); -------------------------------------------------------------------------------- /experiments/README.md: -------------------------------------------------------------------------------- 1 | # JSGtk+ 2 | An attempt to make [GJS](https://wiki.gnome.org/action/show/Projects/Gjs?action=show&redirect=Gjs) more JavaScript and Node.JS friendly, bringing in CommonJS modules loader and indeed able to work with [npm](https://www.npmjs.com/) modules too. 3 | 4 | [Related blog post](https://www.webreflection.co.uk/blog/2015/12/08/writing-native-apps-with-javascript) 5 | 6 | 7 | 8 | 9 | 10 | 11 | ### Quick How To 12 | In order to install `jsgtk` simply use `npm` 13 | ```sh 14 | npm install jsgtk 15 | ``` 16 | In order to include the `jsgtk` runtime, write the following code at the very top of your file: 17 | ```sh 18 | #!/usr/bin/env sh 19 | imports=imports// "exec" "gjs" "-I" "$(dirname $0)/node_modules/jsgtk/" "$0" "$@" 20 | imports.jsgtk.env; 21 | 22 | // the rest of your code here, e.g. 23 | console.info('Hello JSGTK'); 24 | ``` 25 | It is now possible to `chmod +x jsgtk-file-name` and launch it directly. 26 | 27 | 28 | #### Using global npm packages 29 | If `jsgtk` is installed as global `npm` package, we can use it as executable to load programs via it. 30 | 31 | ```sh 32 | npm install jsgtk -g 33 | 34 | echo "console.info('Hello JSGTK!');" > test-gjs.js 35 | 36 | jsgtk test-gjs.js 37 | ``` 38 | Above sequence should log `'Hello JSGTK!'` in console. 39 | 40 | The same will work flagging a file header via 41 | 42 | ```sh 43 | #!/usr/bin/env jsgtk 44 | console.log('Hello JSGTK!'); 45 | ``` 46 | 47 | 48 | 49 | ### How to install GJS 50 | It is quite pointless to have `jsgtk` if you don't have a usable version of GJS. 51 | Following how I've installed it in few platforms I could test. 52 | 53 | 54 | #### Linux 55 | 56 | * in [ArchLinux](https://www.archlinux.org/), a command such `pacman -S --needed npm gjs` would do 57 | * in [Ubuntu](http://www.ubuntu.com/), a command such `sudo apt-get install npm gjs` should do as well 58 | 59 | 60 | #### OSX 61 | The easiest way to install GJS in OSX and use JSGTK too is the following sequence of instruction in the terminal. 62 | Feel free to copy and paste below code into `gjs.sh` and then execute it via `sh gjs.sh`. 63 | ```sh 64 | # WARNING, if you have MacPorts already installed you should use it! 65 | # if you are planning to use WebKitGTK please install MacPorts 66 | if [ "$(which port)" != "" ]; then 67 | sudo port install gjs 68 | else 69 | # fallback 70 | # verify and eventually install Homebrew 71 | if [ "$(which brew)" = "" ]; then 72 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 73 | # eventually confirm or add password the first time it's installed 74 | fi 75 | # install gjs via https://github.com/TingPing/homebrew-gnome 76 | brew tap TingPing/gnome 77 | brew install gtk+3 78 | brew install gjs 79 | fi 80 | ``` 81 | Please note something else might need to be installed, it could take long time to download and install modules, please be patience. 82 | 83 | 84 | ### How to verify everything is OK 85 | 86 | ![Hello GJS!](hello-gjs.png) 87 | 88 | If everything went OK, you can now `gjs` in the terminal and play around with `js24` SpiderMonkey CLI. 89 | Please note `gjs` doesn't work as interactively as `node`, in order to run a program you need to launch it via `gjs program.js` or using file headers previously described. 90 | If you'd like to test the look and feel of a basic widget, feel free to save the following code in a `ui.js` file. 91 | ```js 92 | #!/usr/bin/env gjs 93 | 94 | (function (Gtk){'use strict'; 95 | 96 | Gtk.init(null, 0); 97 | 98 | const 99 | win = new Gtk.Window({ 100 | type : Gtk.WindowType.TOPLEVEL, 101 | window_position: Gtk.WindowPosition.CENTER 102 | }) 103 | ; 104 | 105 | win.set_default_size(200, 80); 106 | win.add(new Gtk.Label({label: 'Hello GJS!'})); 107 | win.connect('show', () => Gtk.main()); 108 | win.connect('destroy', () => Gtk.main_quit()); 109 | win.show_all(); 110 | 111 | }(imports.gi.Gtk)); 112 | ``` 113 | Feel free to `chmod +x ui.js` and then launch it directly. 114 | 115 | 116 | 117 | ### About the Dark Theme 118 | If there's something I love about [GNOME Desktop](https://www.gnome.org/) is its Adwaita Dark Theme. 119 | The good news is that you can have it in GJS too trying at least two things: 120 | 121 | * if you know the theme name has a dark variant, use these lines after the init: 122 | ```js 123 | Gtk.Settings.get_default().set_property('gtk-application-prefer-dark-theme', true); 124 | Gtk.Settings.get_default().gtk_theme_name = "Adwaita"; // theme name 125 | ``` 126 | 127 | * set an environment variable either before launching the app `GTK_THEME=Adwaita:dark ./app.js` or in the `~/.bashrc` or `profile` file in order to have it always set 128 | * use a special header that will also set an environment variable before launching the app 129 | 130 | The latter case is summarized in the following header: 131 | ```js 132 | #!/usr/bin/env sh 133 | imports=imports// "export" "GTK_THEME=Adwaita:dark" && "exec" "gjs" "-I" "$(dirname $0)/node_modules/jsgtk/" "$0" "$@" 134 | imports.jsgtk.env; 135 | 136 | // the rest of your code here 137 | ``` 138 | 139 | 140 | ### About the Python/C code style 141 | I am working to bring a JS like style through the `jsgtk.gi` import, which is nothing but enriched native API with all aliases that will make methods like `win.set_default_size(200, 80);` become `win.setDefaultSize(200, 80);`, as well as every other method, property, or public static method. This is a work in progress but so far the entire `Gio`, `GLib`, and `GObject` has been covered. 142 | 143 | Feel free to check on top of the [jsgtk.gi](jsgtk/gi.js) file which native module has been patched through `jsgtk.gi` module. 144 | 145 | 146 | 147 | ### Stability 148 | Please note while GTK3 is rock solid stable and production ready (GNOME Desktop uses it, to name just one) this project is highly experimental so its JS functionality might not be as perfect as expected. For instance, few File System operations are incomplete and options are partially ignored and not a 1:1 nodejs like solution. I am working on my free time to make this project as stable and reliable as possible, and every kind of contribution will be more than welcome, specially if you are more familiar than I am with GTK3 or even GJS. 149 | 150 | Thanks! -------------------------------------------------------------------------------- /experiments/fake-wraps-for-camel-case.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env gjs 2 | 3 | const require = (function () { 4 | 5 | const 6 | 7 | // to debug all steps, pas --debug 8 | D = ARGV.some(value => value === '--debug'), 9 | BUG = function () { 10 | print(Array.prototype.join.call(arguments, ' ')); 11 | }, 12 | 13 | // closure shortcuts 14 | GLib = imports.gi.GLib, 15 | 16 | // require('os') 17 | OS = { 18 | hostname: function getHostname() {}, 19 | loadavg: function getLoadAvg() {}, 20 | uptime: function getUptime() {}, 21 | freemem: function getFreeMem() {}, 22 | totalmem: function getTotalMem() {}, 23 | cpus: function getCPUs() {}, 24 | type: function getOSType() {}, 25 | release: function getOSRelease() {}, 26 | networkInterfaces: function getInterfaceAddresses() {}, 27 | homedir: function getHomeDirectory() {}, 28 | arch: function arch() {}, 29 | platform: function platform() {}, 30 | tmpdir: function tmpdir() {}, 31 | tmpDir: function tmpDir() {}, 32 | EOL: '\n', 33 | endianness: function endianness() {} 34 | }, 35 | 36 | 37 | // key used to relate wrappers and Gtk objects 38 | SECRET = '_' + GLib.random_int(), 39 | TERCES = SECRET.split('').reverse().join(''), 40 | 41 | // commonly used RegExp 42 | CONSTANT_CASE = /^[A-Z_]+$/, 43 | PascalCase = /^[A-Z]+[a-z]/, 44 | UPPERCASE = /[A-Z]+/g, 45 | newCase = /^new(?:[A-Z_]|$)/, 46 | 47 | // common shortcuts 48 | defineProperty = Object.defineProperty, 49 | getOwnPropertyNames = Object.getOwnPropertyNames, 50 | hasOwnProperty = Object.prototype.hasOwnProperty, 51 | initilaizer = [], 52 | toString = Object.prototype.toString, 53 | 54 | // the imports.gi namespace 55 | ns = Object.create(null) 56 | ; 57 | 58 | // the new(bind.apply(constructor,arguments)) does not work here 59 | // so per each amount of arguments craete a function like: 60 | // 61 | // let instance = (function () { 62 | // return new this(arguments[0]); 63 | // }.apply(Constructor, arguments)); 64 | // 65 | function createInit(l) { 66 | const a = []; 67 | for (let i = 0; i < l; i++) a[i] = 'arguments[' + i + ']'; 68 | return (initilaizer[l] = Function('return new this(' + a.join(',') + ')')); 69 | } 70 | 71 | // used to create Gtk constructors instances 72 | // createInstance.apply(Gtk.Window, [{title: 'Gtk+'}]); 73 | function createInstance() { 74 | const l = arguments.length; 75 | return (initilaizer[l] || createInit(l)).apply(this, arguments); 76 | } 77 | 78 | // from a generic object, get its Gtk counterpart 79 | function getGtk(object) { 80 | return typeof object === 'object' && 81 | object && 82 | hasOwnProperty.call(object, SECRET) ? 83 | object[SECRET] : object; 84 | } 85 | 86 | // from a generic gtk object, get its JS counterpart 87 | function getWrap(object) { 88 | return typeof object === 'object' && 89 | object && 90 | hasOwnProperty.call(object, TERCES) ? 91 | object[TERCES] : object; 92 | } 93 | 94 | // create setup objects with python_case properties 95 | function getTheRightObject(object) { 96 | const gtk = getGtk(object); 97 | return gtk === object && gtk && toString.call(gtk) === '[object Object]' ? 98 | toPythonCaseObject(gtk) : gtk; 99 | } 100 | 101 | // create arguments compatible with Gtk 102 | function getGtkArguments() { 103 | const a = []; 104 | for (let i = 0, l = arguments.length; i < l; i++) { 105 | a[i] = getTheRightObject(arguments[i]); 106 | } 107 | D&&a.length&&BUG('GTK ARGUMENTS', giArguments(a)); 108 | return a; 109 | } 110 | 111 | // debug Gtk arguments 112 | function giArguments(args) { 113 | try { 114 | return JSON.stringify(args); 115 | } catch(meh) { 116 | return args; 117 | } 118 | } 119 | 120 | // debug giName 121 | function giName(gtk) { 122 | const str = '' + gtk; 123 | return /GIName:([\S]+)/.test(str) ? RegExp.$1 : str; 124 | } 125 | 126 | // used to transform Case to _case 127 | function pythonCase($0) { 128 | return '_' + $0.toLowerCase(); 129 | } 130 | 131 | // transform pcamelCase to python_case 132 | function toPythonCase(name) { 133 | return CONSTANT_CASE.test(name) ? 134 | name : name.replace(UPPERCASE, pythonCase); 135 | } 136 | 137 | // create new object with python_keys keys 138 | function toPythonCaseObject(source) { 139 | const target = {}; 140 | for (let 141 | c, key, 142 | a = getOwnPropertyNames(source), 143 | i = 0; i < a.length; i++ 144 | ) { 145 | key = a[i]; 146 | target[toPythonCase(key)] = getTheRightObject(source[key]); 147 | } 148 | return target; 149 | } 150 | 151 | // pass wrapped arguments 152 | function wrapCallback(callback) { 153 | return function () { 154 | const a = []; 155 | for (let i = 0; i < arguments.length; i++) { 156 | a[i] = getWrap(arguments[i]); 157 | } 158 | return callback.apply(this, a); 159 | }; 160 | } 161 | 162 | // handle results and wrap them 163 | function wrapResult(name, result) { 164 | return typeof result === 'function' ? 165 | function () { 166 | const a = []; 167 | for (let i = 0, fn; i < arguments.length; i++) { 168 | fn = arguments[i]; 169 | a[i] = typeof fn === 'function' ? wrapCallback(fn) : fn; 170 | } 171 | return wrapResult(name, result.apply( 172 | getGtk(this), 173 | getGtkArguments.apply(null, a) 174 | )); 175 | } : 176 | getWrap(result); 177 | } 178 | 179 | // invoked once per module 180 | function createModule(parent, module) { 181 | 182 | D&&BUG('CREATING', module); 183 | 184 | const 185 | child = parent[module], 186 | ns = Object.create(null), 187 | hasChild = (target, property) => { 188 | return toPythonCase(property) in child; 189 | }, 190 | getChild = (target, property, receiver) => { 191 | D&&BUG('STATIC GET', module + '.' + property); 192 | switch (true) { 193 | case PascalCase.test(property): 194 | return getModule(ns, child, property); 195 | case newCase.test(property): 196 | let result = wrapResult(property, child[toPythonCase(property)]); 197 | return typeof result === 'function' ? 198 | function () { 199 | let outcome = result.apply(this, arguments); 200 | return typeof outcome === 'object' && 201 | outcome && 202 | outcome instanceof child ? 203 | new target(outcome) : outcome; 204 | } : 205 | result; 206 | default: 207 | return wrapResult(property, child[toPythonCase(property)]); 208 | } 209 | }, 210 | setChild = (target, property, value, receiver) => { 211 | D&&BUG('STATIC SET', module + '.' + property, value); 212 | child[toPythonCase(property)] = getGtk(value); 213 | } 214 | ; 215 | 216 | switch (true) { 217 | case PascalCase.test(module): 218 | switch (typeof child) { 219 | case 'function': 220 | let Class = function Class(gtk) { 221 | D&&BUG('INSTANCE', 'of', giName(gtk)); 222 | defineProperty(this, SECRET, {value: gtk}); 223 | defineProperty(gtk, TERCES, {value: this}); 224 | }; 225 | Class.prototype = new Proxy(Class.prototype, { 226 | has: function has(target, property) { 227 | D&&BUG('HAS', module + '#' + property); 228 | return toPythonCase(property) in child.prototype; 229 | }, 230 | get: function get(target, property, receiver) { 231 | D&&BUG('GET', module + '#' + property); 232 | return wrapResult(property, receiver[SECRET][toPythonCase(property)]); 233 | }, 234 | set: function set(target, property, value, receiver) { 235 | D&&BUG('SET', module + '#' + property, value); 236 | receiver[SECRET][toPythonCase(property)] = getGtk(value); 237 | } 238 | }); 239 | return new Proxy(Class, { 240 | has: hasChild, 241 | get: getChild, 242 | set: setChild, 243 | construct: (Class, args) => { 244 | D&&BUG('NEW(', child.name, giArguments(args), ')'); 245 | return new Class(createInstance.apply( 246 | child, 247 | getGtkArguments.apply(null, args) 248 | )); 249 | } 250 | }); 251 | case 'object': 252 | return child && new Proxy(child, { 253 | has: hasChild, 254 | get: getChild, 255 | set: setChild 256 | }); 257 | default: 258 | D&&BUG('[WARNING] UNHANDLED PascalCase', property); 259 | return child; 260 | } 261 | default: 262 | D&&BUG('[WARNING] UNHANDLED', property); 263 | return child; 264 | } 265 | } 266 | 267 | // internal namespace and module handler 268 | function getModule(ns, parent, module) { 269 | return ns[module] || ( 270 | ns[module] = createModule(parent, module) 271 | ); 272 | } 273 | 274 | // exported require 275 | return function require(module) { 276 | switch (true) { 277 | case PascalCase.test(module): 278 | return getModule(ns, imports.gi, module); 279 | case 'os': 280 | return OS; 281 | default: 282 | return {}; 283 | } 284 | }; 285 | 286 | }()); -------------------------------------------------------------------------------- /experiments/global-gjs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | imports=imports// "exec" "gjs" "-I" "$(npm config get prefix)/lib/node_modules/jsgtk/" "$0" "$@" 3 | imports.jsgtk.env; 4 | 5 | console.info('Hello global JSGTK!'); -------------------------------------------------------------------------------- /experiments/hello-gjs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env gjs 2 | 3 | ;(function (Gtk){'use strict'; 4 | 5 | Gtk.init(null); 6 | Gtk.Settings.get_default().set_property('gtk-application-prefer-dark-theme', true); 7 | Gtk.Settings.get_default().gtk_theme_name = "Adwaita"; 8 | 9 | const 10 | win = new Gtk.Window({ 11 | type : Gtk.WindowType.TOPLEVEL, 12 | window_position: Gtk.WindowPosition.CENTER 13 | }) 14 | ; 15 | 16 | win.set_default_size(200, 80); 17 | win.add(new Gtk.Label({label: 'Hello GJS!'})); 18 | win.connect('show', () => { 19 | imports.mainloop.timeout_add(1, () => win.present()); 20 | Gtk.main(); 21 | }); 22 | win.connect('destroy', () => Gtk.main_quit()); 23 | win.show_all(); 24 | 25 | }(imports.gi.Gtk)); -------------------------------------------------------------------------------- /experiments/hello-gjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/jsgtk/38c725037b3bd3b26952e7ed7c97299f16336295/experiments/hello-gjs.png -------------------------------------------------------------------------------- /experiments/hello-gtk.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jsgtk 2 | 3 | var 4 | GNode = require('node-gtk'), 5 | Gtk = GNode.importNS('Gtk'), 6 | win 7 | ; 8 | 9 | GNode.startLoop(); 10 | Gtk.init(null); 11 | 12 | win = new Gtk.Window({ 13 | title: 'jsgtk', 14 | window_position: Gtk.WindowPosition.CENTER 15 | }); 16 | 17 | win.connect('show', Gtk.main); 18 | win.connect('destroy', Gtk.main_quit); 19 | 20 | win.set_default_size(200, 80); 21 | win.add(new Gtk.Label({label: 'Hello Gtk+'})); 22 | 23 | win.show_all(); -------------------------------------------------------------------------------- /experiments/hello-world: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | imports=imports// "exec" "gjs" "-I" "$(dirname $0)" "$0" "$@" 3 | imports.jsgtk.env; 4 | 5 | console.info('Hello jsgtk!'); -------------------------------------------------------------------------------- /experiments/jsgtk/System.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const 4 | Gtk = imports.gi.Gtk, 5 | Gdk = imports.gi.Gdk, 6 | path = imports.jsgtk.path, 7 | global = Function('return this')() 8 | ; 9 | 10 | exports.global = global; 11 | global.global = global; 12 | global.System = exports; 13 | 14 | // screen 15 | Object.defineProperty( 16 | global, 17 | 'screen', 18 | { 19 | configurable: true, 20 | get: () => { 21 | Gtk.init(null, 0); 22 | return Object.defineProperty(global, 'screen', { 23 | value: { 24 | get width() { 25 | return Gdk.Screen.width(); 26 | }, 27 | get height() { 28 | return Gdk.Screen.height(); 29 | } 30 | } 31 | }).screen; 32 | } 33 | } 34 | ); 35 | 36 | // process 37 | global.process = { 38 | argv: ARGV.slice(0), 39 | cwd: () => path.resolve('.'), 40 | env: ((arr) => { 41 | const env = {}; 42 | for (let i = 0; i < arr.length; i++) { 43 | let 44 | info = arr[i], 45 | p = info.indexOf('=') 46 | ; 47 | env[info.slice(0, p)] = info.slice(p + 1); 48 | } 49 | return env; 50 | })(imports.gi.GLib.get_environ()), 51 | exit: () => setTimeout(() => Gtk.main_quit(), 0), 52 | run: (callback) => { 53 | global.screen && callback(); 54 | Gtk.main(); 55 | } 56 | }; 57 | 58 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/child_process.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const 4 | Gio = imports.gi.Gio, 5 | GLib = imports.gi.GLib, 6 | jsgtk = imports.jsgtk, 7 | path = jsgtk.path, 8 | dir = path.resolve('.'), 9 | EventEmitter = jsgtk.events.EventEmitter, 10 | Stream = jsgtk.stream.Stream, 11 | ChildProcess = jsgtk.env._.Class({ 12 | extends: EventEmitter, 13 | constructor: function ChildProcess(ok, pid, stdin, stdout, stderr) { 14 | if (ok) { 15 | this.pid = pid; 16 | this.connected = ok; 17 | this.stdin = new Stream(stdin) 18 | .on('error', (reason) => this.emit('error', reason)); 19 | this.stdout = new Stream(stdout) 20 | .on('close', (code, reason) => this.emit('close', code, reason)) 21 | .on('error', (reason) => this.emit('error', reason)); 22 | this.stderr = new Stream(stderr) 23 | .on('error', (reason) => this.emit('error', reason)); 24 | this.stdio = [ 25 | this.stdin, 26 | this.stdout, 27 | this.stderr 28 | ]; 29 | } 30 | }, 31 | connected: false, 32 | disconnect: function disconnect() { 33 | disconnectChild.call(this); 34 | this.emit('disconnect'); 35 | }, 36 | kill: function kill(signal) { 37 | disconnectChild.call(this); 38 | this.emit('exit', null, signal || 'SIGTERM'); 39 | } 40 | }), 41 | disconnectChild = function () { 42 | this.connected = false; 43 | this.stdin.emit('disconnect'); 44 | this.stdout.emit('disconnect'); 45 | this.stderr.emit('disconnect'); 46 | GLib.spawn_close_pid(this.pid); 47 | } 48 | ; 49 | 50 | exports.spawn = function aspawn(command, args, options) { 51 | if (!options) options = {}; 52 | let cp, [ok, pid, stdin, stdout, stderr] = GLib.spawn_async_with_pipes( 53 | options.cwd || dir, 54 | [command].concat(args || Array.prototype), 55 | options.env ? Object.keys(options.env) 56 | .reduce((a, k) => { 57 | a.push(k + '=' + options.env[k]); 58 | return a; 59 | }, []) : null, 60 | GLib.SpawnFlags.SEARCH_PATH, 61 | null 62 | ); 63 | if (ok) { 64 | cp = new ChildProcess(ok, pid, stdin, stdout, stderr); 65 | } else { 66 | cp = new ChildProcess(null); 67 | setTimeout(() => cp.emit('error', stderr), 0); 68 | } 69 | return cp; 70 | }; 71 | 72 | exports.execSync = function execSync(command, options) { 73 | // TODO: support other options ? 74 | if (!options) options = {}; 75 | let [ok, stdout, stderr, exit_status] = GLib.spawn_command_line_sync( 76 | options.cwd ? ('cd ' + options.cwd + ' && ' + command) : command 77 | ); 78 | if (!ok) throw stderr; 79 | return stdout; 80 | }; 81 | 82 | exports.spawnSync = function spawnSync(command, args, options) { 83 | // TODO: support other options ? 84 | if (!options) options = {}; 85 | let [ok, stdout, stderr, exit_status] = GLib.spawn_sync( 86 | options.cwd || dir, 87 | [command].concat(args || Array.prototype), 88 | options.env ? Object.keys(options.env) 89 | .reduce((a, k) => { 90 | a.push(k + '=' + options.env[k]); 91 | return a; 92 | }, []) : null, 93 | GLib.SpawnFlags.SEARCH_PATH, 94 | null 95 | ); 96 | return { 97 | output: [null, stdout, stderr], 98 | stdout: stdout, 99 | stderr: stderr, 100 | status: exit_status, 101 | error: ok ? null : stderr 102 | }; 103 | }; 104 | 105 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/console.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const 4 | RESET = '\x1b[0m', 5 | RED = '\x1b[0;31m', 6 | GREEN = '\x1b[0;32m', 7 | YELLOW = '\x1b[0;33m', 8 | BOLD = '\x1b[1m', 9 | jsgtk = imports.jsgtk, 10 | slice = jsgtk.env._.slice 11 | ; 12 | 13 | exports.assert = function assert(what, why) { 14 | if (!what) { 15 | printerr(RED + BOLD + '[WRONG]' + RESET + ' ' + RED + BOLD + (why || '') + RESET); 16 | // TODO: how to assert and fail natively? 17 | } 18 | }; 19 | 20 | exports.error = function error(what, why) { 21 | printerr(RED + BOLD + '[ERROR]' + RESET + ' ' + RED + slice.apply(0, arguments).join(', ') + RESET); 22 | }; 23 | 24 | exports.info = function info(what, why) { 25 | print(GREEN + BOLD + '[INFO]' + RESET + ' ' + BOLD + slice.apply(0, arguments).join(', ') + RESET); 26 | }; 27 | 28 | exports.log = function log(what, why) { 29 | print(slice.apply(0, arguments).join(', ')); 30 | }; 31 | 32 | exports.warn = function warn(what, why) { 33 | print(YELLOW + BOLD + '[WARNING]' + RESET + ' ' + YELLOW + slice.apply(0, arguments).join(', ') + RESET); 34 | }; 35 | 36 | // TODO: time and timeEnd ? 37 | 38 | jsgtk.System.global.console = exports; 39 | 40 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/crypto.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | exports.randomBytes = function randomBytes(size) { 4 | let out = []; 5 | while (size--) out.push(('0' + ((Math.random() * 16) | 0).toString(16)).slice(-2)); 6 | return out.join(''); 7 | }; 8 | 9 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/env.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const 4 | BUILTIN = [ 5 | 'child_process', 6 | 'console', 7 | 'crypto', 8 | 'events', 9 | 'fs', 10 | 'os', 11 | 'path', 12 | 'stream', 13 | 'timers' 14 | ], 15 | Gio = imports.gi.Gio, 16 | GLib = imports.gi.GLib, 17 | jsgtk = imports.jsgtk, 18 | System = jsgtk.System, 19 | cache = Object.create(null), 20 | nodeGTK = { 21 | importNS: function (ns, version) { 22 | return imports.gi[ns]; 23 | }, 24 | startLoop: function () { 25 | // this should be a noop in jsgtk 26 | } 27 | } 28 | ; 29 | 30 | exports.require = function require(id) { 31 | return id === 'node-gtk' ? nodeGTK : requireWithPath(id, dir); 32 | }; 33 | 34 | // basic utilities 35 | exports._ = { 36 | Class: function Class(proto) { 37 | 38 | let 39 | CONSTRUCTOR = 'constructor', 40 | EXTENDS = 'extends', 41 | hOP = Object.prototype.hasOwnProperty, 42 | hasSuper = hOP.call(proto, EXTENDS), 43 | parent = hasSuper ? proto[EXTENDS] : null, 44 | constructor 45 | ; 46 | 47 | if (!hOP.call(proto, CONSTRUCTOR)) { 48 | proto[CONSTRUCTOR] = hasSuper ? 49 | function Class() { 50 | parent.apply(this, arguments); 51 | } : 52 | function Class() {}; 53 | } else if (hasSuper) { 54 | constructor = proto[CONSTRUCTOR]; 55 | proto[CONSTRUCTOR] = function Class() { 56 | parent.apply(this, arguments); 57 | constructor.apply(this, arguments); 58 | }; 59 | } 60 | 61 | if (hasSuper) { 62 | proto[CONSTRUCTOR].prototype = Object.create( 63 | parent.prototype, 64 | {constructor: { 65 | configurable: true, 66 | writable: true, 67 | value: proto[CONSTRUCTOR] 68 | }} 69 | ); 70 | } 71 | 72 | Object.getOwnPropertyNames(proto).forEach((key) => { 73 | switch (key) { 74 | case CONSTRUCTOR: 75 | case EXTENDS: break; 76 | case 'static': 77 | Object.getOwnPropertyNames(proto[key]).forEach((k) => { 78 | Object.defineProperty(proto[CONSTRUCTOR], k, { 79 | enumerable: k.charAt(0) !== '_', 80 | value: proto[key][k] 81 | }); 82 | }); 83 | break; 84 | default: 85 | let descriptor = Object.getOwnPropertyDescriptor(proto, key); 86 | descriptor.enumerable = false; 87 | Object.defineProperty(proto[CONSTRUCTOR].prototype, key, descriptor); 88 | break; 89 | } 90 | }); 91 | 92 | return proto[CONSTRUCTOR]; 93 | 94 | }, 95 | slice: function slice() { 96 | for (var 97 | o = +this, 98 | i = o, 99 | l = arguments.length, 100 | n = l - o, 101 | a = Array(n < 0 ? 0 : n); 102 | i < l; i++ 103 | ) a[i - o] = arguments[i]; 104 | return a; 105 | } 106 | }; 107 | 108 | System.global.jsgtk = jsgtk; 109 | System.global.require = exports.require; 110 | BUILTIN.forEach((id) => jsgtk[id]); 111 | 112 | if (!Object.setPrototypeOf) 113 | Object.defineProperty(Object, 'setPrototypeOf', { 114 | configurable: true, 115 | writable: true, 116 | value: ((set) => (object, proto) => (set.call(object, proto), object)) 117 | (Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set) 118 | }); 119 | 120 | const 121 | fs = jsgtk.fs, 122 | path = jsgtk.path, 123 | dir = path.resolve('.') 124 | ; 125 | 126 | function evaluateAndCache(id, dir) { 127 | let 128 | exports = {}, 129 | module = {exports: exports, id: id} 130 | ; 131 | cache[id] = exports; 132 | Function( 133 | 'require', 134 | 'exports', 135 | 'module', 136 | '__dirname', 137 | '__filename', 138 | fs.readFileSync(id).replace(/^#![^\n\r]*/, '') 139 | ).call( 140 | exports, 141 | function require(id) { 142 | return id === 'node-gtk' ? nodeGTK : requireWithPath(id, dir); 143 | }, 144 | exports, 145 | module, 146 | dir, 147 | id 148 | ); 149 | return (cache[id] = module.exports); 150 | } 151 | 152 | function existsSync(file) { 153 | try { 154 | return fs.statSync(file); 155 | } catch(nope) { 156 | return false; 157 | } 158 | } 159 | 160 | // log(imports.seachPath); 161 | 162 | function requireWithPath(id, dir) { 163 | switch (true) { 164 | case -1 < BUILTIN.indexOf(id): 165 | return jsgtk[id]; 166 | case id.charAt(0) === '.': 167 | id = path.resolve(dir, id); 168 | case path.isAbsolute(id): 169 | if ( 170 | id.slice(-3) !== '.js' && 171 | !Gio.File.new_for_path(id).query_exists(null) 172 | ) id += '.js'; 173 | break; 174 | default: 175 | let 176 | currentDir = dir, 177 | subFolder = [currentDir, 'node_modules', id].join(path.sep), 178 | pkg, info 179 | ; 180 | while(!existsSync(subFolder)) { 181 | let i = currentDir.lastIndexOf(path.sep); 182 | if (i < 0) throw new Error('unable to load module ' + id); 183 | currentDir = currentDir.slice(0, i); 184 | subFolder = [currentDir, 'node_modules', id].join(path.sep); 185 | } 186 | pkg = [subFolder, 'package.json'].join(path.sep); 187 | info = JSON.parse(fs.readFileSync(pkg)); 188 | id = path.normalize([subFolder, info.main].join(path.sep)); 189 | break; 190 | } 191 | return cache[id] || evaluateAndCache(id, path.dirname(id)); 192 | } 193 | 194 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/events.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const empty = Array.prototype; 4 | 5 | exports.EventEmitter = imports.jsgtk.env._.Class({ 6 | constructor: function EventEmitter() { 7 | Object.defineProperty(this, '_eventEmitter', { 8 | configurable: true, 9 | value: Object.create(null) 10 | }); 11 | }, 12 | addListener: function addListener(event, listener) { 13 | return this.on(event, listener); 14 | }, 15 | emit: function emit(event, ...args) { 16 | (this._eventEmitter[event] || empty).forEach( 17 | (listener) => listener.apply(this, args) 18 | ); 19 | }, 20 | listenerCount: function listenerCount(event) { 21 | return (this._eventEmitter[event] || empty).length; 22 | }, 23 | listeners: function listeners(event) { 24 | return (this._eventEmitter[event] || empty).slice(0); 25 | }, 26 | on: function on(event, listener) { 27 | (this._eventEmitter[event] || 28 | (this._eventEmitter[event] = [])).push(listener); 29 | return this; 30 | }, 31 | once: function once(event, listener) { 32 | return this.on(event, function dropIt() { 33 | this.removeListener(event, dropIt); 34 | listener.apply(this, arguments); 35 | }); 36 | }, 37 | removeAllListeners: function removeAllListeners(event) { 38 | if (event) this._eventEmitter[event] = []; 39 | else EventEmitter.call(this); 40 | return this; 41 | }, 42 | removeListener: function removeListener(event, listener) { 43 | let i = (this._eventEmitter[event] || empty).indexOf(listener); 44 | if (-1 < i) this._eventEmitter[event].splice(i, 1); 45 | return this; 46 | } 47 | }); 48 | 49 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/fs.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const 4 | Gio = imports.gi.Gio, 5 | ByteArray = imports.byteArray, 6 | child_process = imports.jsgtk.child_process 7 | ; 8 | 9 | exports.readFile = function readFile(file, options, callback) { 10 | // TODO: supports options 11 | if (!callback) callback = options; 12 | Gio.File.new_for_path(file) 13 | .load_contents_async(null, (source, result) => { 14 | try { 15 | let [ok, data, etag] = source.load_contents_finish(result); 16 | if (!ok) throw 'Unable to read ' + file; 17 | callback(null, data.toString()); 18 | } catch(err) { 19 | callback(err); 20 | } 21 | }); 22 | } 23 | 24 | exports.readFileSync = function readFileSync(file, options) { 25 | // TODO: supports options 26 | return Gio.File.new_for_path(file).load_contents(null)[1].toString(); 27 | }; 28 | 29 | // TODO: find out if there's a better way 30 | exports.readdir = function readdirSync(path, callback) { 31 | let 32 | ls = child_process.spawn('ls', [path]), 33 | out = [], 34 | errors = false 35 | ; 36 | ls.stderr.on('data', (data) => (errors = true, out.push(data.trim()))); 37 | ls.stdout.on('data', (data) => out.push(data.trim())); 38 | ls.on('close', () => { 39 | if (errors) callback(out.join('')); 40 | else callback(null, out); 41 | }); 42 | }; 43 | exports.readdirSync = function readdirSync(path) { 44 | return child_process.spawnSync('ls', [path]).stdout.toString().trim().split('\n'); 45 | }; 46 | 47 | exports.statSync = function statSync(path) { 48 | let fd = Gio.File.new_for_path(path); 49 | if (fd.query_exists(null)) { 50 | // https://people.gnome.org/~gcampagna/docs/Gio-2.0/Gio.FileInfo.html 51 | let 52 | info = fd.query_info('*', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null), 53 | out = { 54 | // TODO: all wrong!!! 55 | dev: info.get_attribute_int32('dev'), 56 | ino: info.get_attribute_int32('ino'), 57 | mode: info.get_attribute_int32('mode'), 58 | nlink: info.get_attribute_int32('nlink'), 59 | uid: info.get_attribute_int32('uid'), 60 | gid: info.get_attribute_int32('gid'), 61 | rdev: info.get_attribute_int32('rdev'), 62 | size: info.get_size(), 63 | blksize: info.get_attribute_int32('blksize'), 64 | blocks: info.get_attribute_int32('blocks'), 65 | atime: info.get_attribute_int32('atime'), 66 | mtime: info.get_attribute_int32('mtime'), 67 | ctime: info.get_attribute_int32('ctime'), 68 | birthtime: info.get_attribute_int32('birthtime') 69 | } 70 | ; 71 | // Object.keys(out).forEach((key) => log(key + ': ' + out[key])); 72 | return out; 73 | } else { 74 | return null; 75 | } 76 | }; 77 | 78 | exports.writeFileSync = function writeFileSync(file, data, options) { 79 | // TODO: supports options 80 | let fd, stream, result; 81 | options = getWriteOptions(options); 82 | switch (options.flag) { 83 | case 'w': 84 | fd = Gio.File.new_for_path(file); 85 | try { 86 | stream = fd.create_readwrite(Gio.FileCreateFlags.REPLACE_DESTINATION, null).output_stream; 87 | } catch(e) { 88 | stream = fd.open_readwrite(null).output_stream; 89 | } 90 | stream.truncate(0, null); 91 | stream.write_all(String(data), null); 92 | stream.flush(null); 93 | result = stream.close(null); 94 | } 95 | return result; 96 | }; 97 | 98 | exports.writeFile = function writeFileSync(file, data, options, callback) { 99 | // TODO: supports options 100 | let fd; 101 | if (typeof options === 'function') { 102 | callback = options; 103 | options = getWriteOptions(null); 104 | } else { 105 | options = getWriteOptions(options); 106 | } 107 | switch (options.flag) { 108 | case 'w': 109 | let onceFoundAWayTowrite = (stream) => { 110 | stream.truncate(0, null); 111 | stream.output_stream.write_bytes_async( 112 | ByteArray.fromString(String(data)), 113 | 0, 114 | null, 115 | (source, result) => { 116 | source.write_bytes_finish(result); 117 | source.flush_async(0, null, (source, result) => { 118 | source.flush_finish(result); 119 | source.close_async(0, null, (source, result) => { 120 | callback(!source.close_finish(result)); 121 | }); 122 | }); 123 | } 124 | ); 125 | }; 126 | fd = Gio.File.new_for_path(file); 127 | fd.create_readwrite_async( 128 | Gio.FileCreateFlags.REPLACE_DESTINATION, 129 | 0, 130 | null, 131 | (source, result) => { 132 | try { 133 | onceFoundAWayTowrite(source.create_readwrite_finish(result)); 134 | } catch(e) { 135 | fd.open_readwrite_async( 136 | 0, 137 | null, 138 | (source, result) => { 139 | onceFoundAWayTowrite(source.open_readwrite_finish(result)); 140 | } 141 | ); 142 | } 143 | } 144 | ); 145 | break; 146 | } 147 | }; 148 | 149 | function getWriteOptions(options) { 150 | if (!options) options = {}; 151 | if (!options.encoding) options.encoding = 'utf8'; 152 | if (!options.mode) options.mode = 666; 153 | if (!options.flag) options.flag = 'w'; 154 | return options; 155 | } 156 | 157 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/os.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const 4 | jsgtk = imports.jsgtk, 5 | slice = jsgtk.env._.slice, 6 | child_process = jsgtk.child_process, 7 | execSync = child_process.execSync, 8 | spawnSync = child_process.spawnSync, 9 | node = execSync('which node').toString() ? 10 | 'node' : ( 11 | execSync('which nodejs').toString() ? 12 | 'nodejs' : '' 13 | ) 14 | ; 15 | 16 | if (!node) throw new Error('unable to find a valid Node.js executable'); 17 | 18 | exports.EOL = '\n'; 19 | 20 | [ 21 | 'arch', 22 | 'cpus', 23 | 'endianness', 24 | 'freemem', 25 | 'homedir', 26 | 'hostname', 27 | 'loadavg', 28 | 'networkInterfaces', 29 | 'platform', 30 | 'release', 31 | 'tmpdir', 32 | 'totalmem', 33 | 'type', 34 | 'uptime' 35 | ].forEach((method) => { 36 | exports[method] = function () { 37 | return JSON.parse(spawnSync(node, ['-e', ''.concat( 38 | 'var os = require(\'os\');', 39 | 'process.stdout.write(JSON.stringify(', 40 | 'os.', method, '.apply(os,', 41 | arguments.length ? JSON.stringify(slice.apply(0, arguments)) : '[]' 42 | ,')', 43 | '));' 44 | )]).stdout.toString()); 45 | }; 46 | }); 47 | 48 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/path.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const GLib = imports.gi.GLib; 4 | 5 | let 6 | dir = GLib.get_current_dir(), 7 | manysepsFind, 8 | manysepsReplace 9 | ; 10 | 11 | exports.sep = /jsgtk(\W+)path.js/.test((new Error).stack) && RegExp.$1; 12 | exports.delimiter = exports.sep === '/' ? ':' : ';'; 13 | 14 | if (exports.sep === '/') { 15 | manysepsFind = /\/{2,}/g; 16 | manysepsReplace = exports.sep; 17 | } else { 18 | manysepsFind = /\\{3,}/g; 19 | manysepsReplace = exports.sep + exports.sep; 20 | } 21 | 22 | exports.basename = function basename(path, suffix) { 23 | let len, file = GLib.path_get_basename(path); 24 | if (arguments.length === 2) { 25 | len = suffix.length; 26 | if (file.slice(-len) === suffix) { 27 | file = file.slice(0, -len); 28 | } 29 | } 30 | return file; 31 | }; 32 | 33 | exports.dirname = function dirname(path) { 34 | return GLib.path_get_dirname(path); 35 | }; 36 | 37 | exports.extname = function extname(path) { 38 | return /.(\.[A-Za-z]+)$/.test(path) ? RegExp.$1 : ''; 39 | }; 40 | 41 | exports.isAbsolute = function isAbsolute(path) { 42 | return GLib.path_is_absolute(path); 43 | }; 44 | 45 | exports.join = function join() { 46 | let 47 | normalized = [], 48 | i = 0, 49 | tmp 50 | ; 51 | while (i < arguments.length) { 52 | tmp = arguments[i++]; 53 | switch(tmp) { 54 | case '..': normalized.pop(); 55 | case '.': break; 56 | default: normalized.push(tmp); 57 | } 58 | } 59 | return clean(normalized.join(exports.sep)); 60 | }; 61 | 62 | exports.normalize = function normalize(path) { 63 | return exports.join.apply(exports, path.split(exports.sep)); 64 | }; 65 | 66 | exports.posix = exports; 67 | 68 | exports.resolve = function () { 69 | let resolved = [], out; 70 | for (let i = 0, tmp; i < arguments.length; i++) { 71 | tmp = arguments[i]; 72 | if (exports.isAbsolute(tmp)) { 73 | resolved = [tmp]; 74 | } else { 75 | resolved.push(tmp); 76 | } 77 | } 78 | out = exports.normalize(clean(resolved.join(exports.sep))); 79 | if (!exports.isAbsolute(out)) { 80 | out = out.length ? (dir + exports.sep + out) : dir; 81 | } 82 | return out; 83 | }; 84 | 85 | // find out which path is currently running jsgtk 86 | /* 87 | stack.split('\n').some(function (line) { 88 | let important = -1 < line.indexOf('path.js'); 89 | if (important) dir = exports.resolve( 90 | exports.dirname(line.replace(/^@/, '')), 91 | '..' 92 | ); 93 | return important; 94 | }); 95 | */ 96 | 97 | function clean(path) { 98 | return path.replace(manysepsFind, manysepsReplace); 99 | } 100 | 101 | /* TODO: add these too? 102 | path.format(pathObject) 103 | path.parse(pathString) 104 | path.relative(from, to) 105 | path.win32 106 | */ 107 | 108 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/stream.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const 4 | Gio = imports.gi.Gio, 5 | GLib = imports.gi.GLib, 6 | ByteArray = imports.byteArray, 7 | jsgtk = imports.jsgtk, 8 | Class = jsgtk.env._.Class, 9 | EventEmitter = jsgtk.events.EventEmitter, 10 | InernalStream = Class({ 11 | extends: EventEmitter, 12 | constructor: function Stream() { 13 | this.on('disconnect', () => { 14 | if (this._source) { 15 | this._source.unref(); 16 | if (this instanceof Writable) { 17 | this._source.shutdown(true); 18 | } 19 | } else { 20 | this._channel.unref(); 21 | if (this instanceof Writable) { 22 | this._channel.shutdown(true); 23 | } 24 | } 25 | this._channel = false; 26 | }); 27 | } 28 | }), 29 | Readable = Class({ 30 | extends: InernalStream, 31 | constructor: function Readable(channel) { 32 | this._bootstrap = true; 33 | this._channel = channel; 34 | this._watcher = GLib.io_add_watch( 35 | this._channel, 36 | GLib.PRIORITY_DEFAULT, 37 | GLib.IOCondition.IN, 38 | (source, condition, data) => { 39 | this._source = source; 40 | if (this._bootstrap) { 41 | this._bootstrap = false; 42 | if (this.listenerCount('readable')) { 43 | this.emit('readable'); 44 | } else { 45 | while (this.read()); 46 | } 47 | } else { 48 | while (this.read()); 49 | } 50 | } 51 | ); 52 | }, 53 | read: function read(size) { 54 | if (size) { 55 | // TODO: this is probably not going to work ... 56 | let buf = new ByteArray(size); 57 | this._source.read_line(buf, size, 0); 58 | return buf; 59 | } else { 60 | let [status, result] = this._source.read_line(); 61 | // apparently it's not possible to compare === 62 | // against a GTK status 63 | switch (true) { 64 | case status == GLib.IOStatus.NORMAL: 65 | this.emit('data', result); 66 | break; 67 | case status == GLib.IOStatus.EOF: 68 | this.emit('end'); 69 | this._timeout = setTimeout(() => { 70 | this._source = null; 71 | this.emit('close', 0, null); 72 | }, 0); 73 | result = null; 74 | break; 75 | case status == GLib.IOStatus.ERROR: 76 | this.emit('error'); 77 | break; 78 | case status == GLib.IOStatus.AGAIN: 79 | // Resource temporarily unavailable. 80 | // get out ? track it ? count ? 81 | result = ''; 82 | break; 83 | } 84 | return result; 85 | } 86 | } 87 | }), 88 | Writable = Class({ 89 | extends: InernalStream, 90 | constructor: function Writable(channel) { 91 | this._writable = false; 92 | this._writeBuffer = ''; 93 | this._channel = channel; 94 | this._watcher = GLib.io_add_watch( 95 | this._channel, 96 | GLib.PRIORITY_DEFAULT, 97 | GLib.IOCondition.OUT, 98 | (source, condition, data) => { 99 | this._source = source; 100 | if (!this._writable) { 101 | let buf = this._writeBuffer; 102 | this._writeBuffer = ''; 103 | this._writable = true; 104 | this.write(buf); 105 | } 106 | } 107 | ); 108 | }, 109 | end: function end(chunk, encoding, callback) { 110 | // TODO: support all arguments? 111 | this._source.close(); 112 | this.emit('finish'); 113 | }, 114 | write: function write(chunk, encoding, callback) { 115 | // TODO: support encoding, callback ? 116 | if (this._writable) { 117 | let [status, written] = this._source.write_chars(chunk, -1); 118 | if (status == GLib.IOStatus.NORMAL && status == this._source.flush()) { 119 | return true; 120 | } else { 121 | this.emit('error', status); 122 | return false; 123 | } 124 | } else { 125 | this._writeBuffer += chunk; 126 | return true; 127 | } 128 | } 129 | }) 130 | ; 131 | 132 | exports.Readable = Readable; 133 | exports.Writable = Writable; 134 | (exports.Stream = function Stream(std) { 135 | let 136 | channel = GLib.IOChannel.unix_new(std), 137 | flags = channel.get_flags(), 138 | constructor 139 | ; 140 | channel.set_flags(GLib.IOFlags.NONBLOCK); 141 | switch (true) { 142 | case flags == GLib.IOFlags.IS_WRITABLE: 143 | constructor = Writable; 144 | break; 145 | case flags == GLib.IOFlags.IS_READABLE: 146 | constructor = Readable; 147 | break; 148 | } 149 | return new constructor(channel); 150 | }).prototype = InernalStream.prototype; 151 | }(this)); -------------------------------------------------------------------------------- /experiments/jsgtk/timers.js: -------------------------------------------------------------------------------- 1 | (function (exports) {'use strict'; 2 | 3 | const 4 | Mainloop = imports.mainloop, 5 | createClearTimer = () => (id) => Mainloop.source_remove(id), 6 | createSetTimer = (repeat) => 7 | (fn, ms, ...args) => 8 | Mainloop.timeout_add( 9 | (ms * 1) || 0, 10 | () => (fn.apply(null, args), repeat) 11 | ) 12 | ; 13 | 14 | global.clearInterval = (exports.clearInterval = createClearTimer()); 15 | global.clearTimeout = (exports.clearTimeout = createClearTimer()); 16 | global.setInterval = (exports.setInterval = createSetTimer(true)); 17 | global.setTimeout = (exports.setTimeout = createSetTimer(false)); 18 | 19 | }(this)); -------------------------------------------------------------------------------- /experiments/location.js: -------------------------------------------------------------------------------- 1 | print( 2 | [ 3 | __filename, 4 | __dirname 5 | ].join('\n') 6 | ); -------------------------------------------------------------------------------- /experiments/module.js: -------------------------------------------------------------------------------- 1 | console.log( 2 | 'module', 3 | __dirname, 4 | __filename.slice( 5 | __dirname.length + require('path').sep.length 6 | ) 7 | ); 8 | 9 | exports.method = function () { 10 | return 'module works'; 11 | }; -------------------------------------------------------------------------------- /experiments/package/js/demo.js: -------------------------------------------------------------------------------- 1 | print( 2 | [ 3 | __filename, 4 | __dirname 5 | ].join('\n') 6 | ); 7 | 8 | require('../../location'); -------------------------------------------------------------------------------- /experiments/package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "js/demo.js" 3 | } 4 | -------------------------------------------------------------------------------- /experiments/proxied_gtk_modules.js: -------------------------------------------------------------------------------- 1 | /*! © Andrea Giammarchi @WebReflection */ 2 | 3 | /* jshint esversion: 6, strict: true, node: true */ 4 | 5 | (function (exports, Function, evaluate) {'use strict'; 6 | 7 | const 8 | 9 | gi = imports.gi, 10 | hybrid_emitter = imports.jsgtk.hybrid_emitter, 11 | 12 | // to debug all steps, pas --debug 13 | D = ARGV.some(value => value === '--debug'), 14 | BUG = D&&function () { 15 | print(Array.prototype.join.call(arguments, ' ')); 16 | }, 17 | 18 | CONSTANT_CASE = /^[A-Z_]+$/, 19 | PascalCase = /^[A-Z]+[a-z]/, 20 | UPPERCASE = /[A-Z]+/g, 21 | 22 | create = Object.create, 23 | getPrototypeOf = Object.getPrototypeOf, 24 | setPrototypeOf = Object.setPrototypeOf, 25 | 26 | gtk = create(null), 27 | init = [], 28 | weakWraps = new WeakMap(), 29 | 30 | exp = { 31 | has: (id) => PascalCase.test(id), 32 | get: (id) => getGtkModule(gi, id), 33 | load: function load(module) { 34 | return getGtkModule(gi, module); 35 | } 36 | } 37 | 38 | ; 39 | 40 | // the new(bind.apply(constructor,arguments)) does not work here 41 | // so per each amount of arguments craete a function like: 42 | // 43 | // let instance = (function () { 44 | // return new this(arguments[0]); 45 | // }.apply(Constructor, arguments)); 46 | // 47 | function createInit(l) { 48 | const a = []; 49 | for (let i = 0; i < l; i++) a[i] = 'arguments[' + i + ']'; 50 | /* jshint ignore: start */ 51 | return (init[l] = Function('return new this(' + a.join(',') + ')')); 52 | /* jshint ignore: end */ 53 | } 54 | 55 | // used to create Gtk constructors instances 56 | // createInstance.apply(Gtk.Window, [{title: 'Gtk+'}]); 57 | function createInstance() { 58 | /* jshint validthis: true */ 59 | const l = arguments.length; 60 | return (init[l] || createInit(l)).apply(this, arguments); 61 | } 62 | 63 | function createModule(parent, module) { 64 | 65 | if(D)BUG('CREATING', module); 66 | 67 | const 68 | child = parent[module], 69 | ns = create(null), 70 | hasChild = (target, property) => { 71 | return toPythonCase(property) in child; 72 | }, 73 | getChild = (target, property, receiver) => { 74 | if(D)BUG('STATIC GET', module + '.' + property); 75 | switch (true) { 76 | case PascalCase.test(property): 77 | return getGtkModule(child, property); 78 | default: 79 | return wrapResult(child[toPythonCase(property)]); 80 | } 81 | }, 82 | setChild = (target, property, value, receiver) => { 83 | if(D)BUG('STATIC SET', module + '.' + property, value); 84 | child[toPythonCase(property)] = getTheRightObject(value); 85 | } 86 | ; 87 | 88 | switch (true) { 89 | case PascalCase.test(module): 90 | switch (typeof child) { 91 | case 'function': 92 | // this is what happens when exported classes don't bring 93 | // any information whatsoever and there's no way to grab 94 | // all prototype methods or properties. Runtime it is then. 95 | const 96 | prototype = child.prototype, 97 | proxy = new Proxy(getPrototypeOf(prototype), { 98 | has: function has(parent, property) { 99 | if(D)BUG('HAS', module + '#' + property); 100 | setPrototypeOf(prototype, parent); 101 | const result = toPythonCase(property) in prototype; 102 | setPrototypeOf(prototype, proxy); 103 | return result; 104 | }, 105 | get: function get(parent, property, receiver) { 106 | if(D)BUG('GET', module + '#' + property); 107 | setPrototypeOf(prototype, parent); 108 | const result = receiver[toPythonCase(property)]; 109 | setPrototypeOf(prototype, proxy); 110 | return result; 111 | }, 112 | set: function set(parent, property, value, receiver) { 113 | if(D)BUG('SET', module + '#' + property, value); 114 | setPrototypeOf(prototype, parent); 115 | receiver[toPythonCase(property)] = value; 116 | setPrototypeOf(prototype, proxy); 117 | } 118 | }) 119 | ; 120 | // before setting the proxy 121 | // check if this is listener aware 122 | // in such case, make it a "node.js-ish" one 123 | if ('connect' in prototype) { 124 | hybrid_emitter.augment(prototype); 125 | } 126 | setPrototypeOf(prototype, proxy); 127 | return new Proxy(child, { 128 | has: hasChild, 129 | get: getChild, 130 | set: setChild, 131 | construct: (child, args) => { 132 | if(D)BUG('NEW(', child.name, giArguments(args), ')'); 133 | return createInstance.apply(child, args); 134 | } 135 | }); 136 | case 'object': 137 | return child && new Proxy(child, { 138 | has: hasChild, 139 | get: getChild, 140 | set: setChild 141 | }); 142 | default: 143 | if(D)BUG('[WARNING] UNHANDLED PascalCase', property); 144 | return child; 145 | } 146 | break; 147 | default: 148 | if(D)BUG('[WARNING] UNHANDLED', property); 149 | return child; 150 | } 151 | return null; 152 | } 153 | 154 | function getGtkModule(parent, module) { 155 | return gtk[module] || ( 156 | gtk[module] = createModule(parent, module) 157 | ); 158 | } 159 | 160 | // create setup objects with python_case properties 161 | function getTheRightObject(object) { 162 | return typeof object === 'object' && 163 | object && 164 | toString.call(object) === '[object Object]' ? 165 | toPythonCaseObject(object) : object; 166 | } 167 | 168 | // debug Gtk arguments 169 | function giArguments(args) { 170 | try { 171 | return JSON.stringify(args); 172 | } catch(meh) { 173 | return args; 174 | } 175 | } 176 | 177 | // used to transform Case to _case 178 | function pythonCase($0) { 179 | return '_' + $0.toLowerCase(); 180 | } 181 | 182 | // transform pcamelCase to python_case 183 | function toPythonCase(name) { 184 | return CONSTANT_CASE.test(name) ? 185 | name : name.replace(UPPERCASE, pythonCase); 186 | } 187 | 188 | // handle results and wrap them once 189 | function wrapResult(result) { 190 | if (typeof result === 'function') { 191 | let wrap = weakWraps.get(result); 192 | if (!wrap) weakWraps.set(result, wrap = function () { 193 | const a = []; 194 | for (let i = 0, l = arguments.length; i < l; i++) { 195 | a[i] = getTheRightObject(arguments[i]); 196 | } 197 | if(D&&a.length)BUG('GTK ARGUMENTS', giArguments(a)); 198 | return wrapResult(result.apply(this, a)); 199 | }); 200 | return wrap; 201 | } 202 | return result; 203 | } 204 | 205 | exports.withRuntime = function setup($evaluate) { 206 | if (!evaluate) evaluate = $evaluate; 207 | return exp; 208 | }; 209 | 210 | }(this, Function)); 211 | -------------------------------------------------------------------------------- /experiments/python-to.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | imports=imports// "exec" "gjs" "-I" "$(dirname $0)" "$0" "$@" 3 | imports.jsgtk.env; 4 | 5 | process.run(function () { 6 | 7 | let 8 | fs = require('fs'), 9 | path = require('path'), 10 | root = path.join('gjs-documentation', 'html'), 11 | fix = Object.create(null), 12 | isConstant = (property) => /^[_A-Z0-9]+$/.test(property), 13 | isClass = (property) => !isConstant(property) && /^[A-Z]/.test(property), 14 | swapName = (property) => { 15 | return property.replace(/_(.)/g, ($0, $1) => $1.toUpperCase()); 16 | } 17 | ; 18 | 19 | fs.readdirSync(root).forEach((file) => { 20 | let namespace = file.split('-')[0]; 21 | fix[namespace] = { 22 | namespace: Object.create(null), 23 | static: [] 24 | }; 25 | fs.readdirSync(path.join(root, file)).filter( 26 | (name) => 27 | name.slice(0, namespace.length + 1) === (namespace + '.') && 28 | name.indexOf('-') < 0 29 | ).forEach((info) => { 30 | info = info.slice(namespace.length + 1, info.lastIndexOf('.')); 31 | if (!isConstant(info)) { 32 | if (isClass(info)) { 33 | let 34 | both = info.split('.'), 35 | Class = both[0], 36 | property = both[1], 37 | target = fix[namespace].namespace 38 | ; 39 | if (both.length > 2) throw new Error('WTF ' + info); 40 | if (!(Class in target)) target[Class] = { 41 | prototype: { 42 | methods: [], 43 | properties: [] 44 | }, 45 | static: [] 46 | }; 47 | if ( 48 | property && 49 | !isConstant(property) && 50 | -1 < property.indexOf('_') 51 | ) { 52 | let constructor = imports.gi[namespace][Class]; 53 | if (property in constructor) { 54 | target[Class].static.push(property); 55 | } else { 56 | try { 57 | if (typeof constructor.prototype[property] === 'function') { 58 | target[Class].prototype.methods.push(property); 59 | } else { 60 | throw 'property'; 61 | } 62 | } catch(probablyAProperty) { 63 | target[Class].prototype.properties.push(property); 64 | } 65 | } 66 | } 67 | } else if (-1 < info.indexOf('_')) { 68 | try { 69 | let method = imports.gi[namespace][info]; 70 | if (typeof method === 'function') { 71 | fix[namespace].static.push(info); 72 | } else { 73 | console.error('unable to handle ' + namespace + '.' + info); 74 | } 75 | } catch(e) { 76 | console.warn(namespace + '.' + info + ' is probably broken'); 77 | } 78 | } 79 | } 80 | }); 81 | }); 82 | 83 | let output = [ 84 | "(function (exports) {'use strict';", 85 | ' // WARNING This file is generated automatically', 86 | ' // Do Not change it directly or it will be replaced', 87 | ' // and it could cause conflicts. Use python-to.js instead.', 88 | ' const', 89 | Object.keys(fix).map((namespace) => ( 90 | ' ' + namespace + ' = (exports.' + namespace + ' = imports.gi.' + namespace + ')' 91 | )).join(',\n'), 92 | ' ;', 93 | Object.keys(fix).map((namespace) => { 94 | let output = []; 95 | let descriptors = []; 96 | fix[namespace].static.forEach((property) => { 97 | descriptors.push(createMethod(property)); 98 | }); 99 | output.push(createDefinition(namespace, descriptors)); 100 | let target = fix[namespace].namespace; 101 | Object.keys(target).forEach((Class) => { 102 | if (target[Class].static.length) { 103 | descriptors = []; 104 | target[Class].static.forEach((property) => { 105 | descriptors.push(createMethod(property)); 106 | }); 107 | output.push(createDefinition(namespace + '.' + Class, descriptors)); 108 | } 109 | descriptors = []; 110 | if (target[Class].prototype.properties.length) { 111 | target[Class].prototype.properties.forEach((property) => { 112 | descriptors.push(createAccessor(property)); 113 | }); 114 | } 115 | if (target[Class].prototype.methods.length) { 116 | target[Class].prototype.methods.forEach((property) => { 117 | descriptors.push(createMethod(property)); 118 | }); 119 | } 120 | if (descriptors.length) { 121 | output.push(createDefinition(namespace + '.' + Class + '.prototype', descriptors)); 122 | } 123 | }); 124 | return output.join('\n'); 125 | }).join('\n'), 126 | '}(this));' 127 | ].join('\n'); 128 | 129 | function createAccessor(property) { 130 | let name = swapName(property); 131 | return [ 132 | ' ' + name + ': {', 133 | ' get: function () { return this.' + property + '; },', 134 | ' set: function (value) { this.' + property + ' = value; }', 135 | ' }' 136 | ].join('\n'); 137 | } 138 | 139 | function createDefinition(target, descriptors) { 140 | return [ 141 | ' Object.defineProperties(', 142 | ' ' + target + ',', 143 | ' {', 144 | descriptors.join(',\n'), 145 | ' }', 146 | ' );' 147 | ].join('\n'); 148 | } 149 | 150 | function createMethod(property) { 151 | let name = swapName(property); 152 | return [ 153 | ' ' + name + ': {', 154 | ' value: function ' + name + '() {', 155 | ' return this.' + property + '.apply(this, arguments);', 156 | ' }', 157 | ' }' 158 | ].join('\n'); 159 | } 160 | 161 | fs.writeFileSync('jsgtk/gi.js', output); 162 | 163 | process.exit(0); 164 | 165 | }); -------------------------------------------------------------------------------- /experiments/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | imports=imports// "exec" "gjs" "-I" "$(dirname $0)" "$0" "$@" 3 | imports.jsgtk.env; 4 | 5 | const GLib = jsgtk.gi.GLib; 6 | 7 | let 8 | utils = jsgtk.env._, 9 | Class = utils.Class, 10 | child_process = require('child_process'), 11 | fs = require('fs'), 12 | os = require('os'), 13 | path = require('path'), 14 | counter = 0, 15 | t = 0, 16 | async = function (cb) { 17 | if (t) clearTimeout(t); 18 | t = 0; 19 | counter++; 20 | return function () { 21 | let result = cb.apply(this, arguments); 22 | counter--; 23 | // if (counter === 0) t = setTimeout(process.exit, 500); 24 | }; 25 | } 26 | ; 27 | 28 | process.run(function () { 29 | 30 | /* 31 | const Promise = require('es6-promise').Promise; 32 | new Promise(function (res, rej) { 33 | setTimeout(res, 500, 'it worked!'); 34 | }).then(log); 35 | 36 | return; 37 | //*/ 38 | 39 | /* testing dblite like a boss 40 | var dblite = require('dblite'), 41 | db = dblite(':memomry:'); 42 | 43 | // Asynchronous, fast, and ... 44 | db.query('SELECT 123', function(err, rows) { 45 | log(rows); 46 | }); 47 | return; 48 | //*/ 49 | 50 | /* testing sqlite3 51 | let sqlite3 = child_process.spawn('sqlite3'); 52 | sqlite3.stdin.write('SELECT 123;\n'); 53 | sqlite3.stdout.on('data', (data) => { 54 | log(data); 55 | sqlite3.disconnect(); 56 | }); 57 | return; 58 | //*/ 59 | 60 | /* 61 | let dafuq = child_process.spawn('sqlite3'); 62 | dafuq.stdout.on('data', (data) => { 63 | log(data); 64 | dafuq.disconnect(); 65 | }); 66 | setTimeout(() => { 67 | dafuq.stdin.write('SELECT 123;\n'); 68 | }); 69 | return; 70 | //*/ 71 | 72 | require('holdon'); 73 | 74 | console.assert(screen.width > 0 && screen.height > 0, 75 | 'global.screen is correctly defined'); 76 | 77 | 78 | console.assert(System.global === Function('return this')(), 79 | 'System.global is correctly defined'); 80 | 81 | console.assert(path.extname('.git') === '', 82 | 'a file name starting with dot does not count as extension'); 83 | console.assert(path.extname('a.git') === '.git', 84 | 'path.extname works as expected'); 85 | console.assert(path.isAbsolute('/home'), 86 | 'path.isAbsolute works as expected'); 87 | console.assert(!path.isAbsolute('home'), 88 | 'path.isAbsolute still works as expected'); 89 | console.assert(path.normalize('/foo/bar//baz/./asdf/quux/..') === '/foo/bar/baz/asdf', 90 | 'path.normalize works as expected'); 91 | console.assert(path.normalize('/foo/bar//baz/./asdf/') === '/foo/bar/baz/asdf/', 92 | 'path.normalize works again as expected'); 93 | console.assert(path.join('/foo', 'bar', 'baz/asdf', 'quux', '..') === '/foo/bar/baz/asdf', 94 | 'path.join works as expected'); 95 | console.assert(path.resolve('foo/bar', '/tmp/file/', '..', 'a/../subfile') === '/tmp/subfile', 96 | 'path.resolve works as expected'); 97 | 98 | console.assert(require('./module').method() === 'module works', 99 | 'require in same folder works as expected'); 100 | console.assert(require('./node_modules/another/main').method() === 'node_modules works', 101 | 'require in sub folders works as expected too'); 102 | 103 | console.assert(require('/home/webreflection/code/jsgtk/module').method() === 'module works', 104 | 'require in absoulte folder works as expected'); 105 | console.assert(require('/home/webreflection/code/jsgtk/node_modules/another/main').method() === 'node_modules works', 106 | 'require in sub absoulte folders works as expected too'); 107 | 108 | console.assert(fs.readFileSync('./test') == GLib.fileGetContents('./test')[1], 109 | 'fs.readFileSync works as expected too'); 110 | 111 | fs.readFile('./test', async(function (err, data) { 112 | console.assert(data == GLib.fileGetContents('./test')[1], 'fs.readFile works as expected'); 113 | if (err) { 114 | console.error(err.message); 115 | } 116 | })); 117 | 118 | 119 | let tmp = Math.random(); 120 | fs.writeFileSync('./test.txt', tmp); 121 | console.assert(fs.readFileSync('./test.txt') == tmp, 122 | 'fs.writeFileSync works as expected'); 123 | 124 | (function (tmp) { 125 | fs.writeFile('./test.txt', tmp, async(function (err) { 126 | console.assert(fs.readFileSync('./test.txt') == tmp, 'fs.writeFile works as expected'); 127 | if (err) { 128 | console.error(err.message); 129 | } 130 | 131 | child_process.execSync('rm test.txt'); 132 | 133 | let syncData = child_process.spawnSync('ls', ['-lah']).stdout.toString(); 134 | console.assert(syncData.length > 0, 135 | 'child_process.spawnSync works as expected'); 136 | 137 | console.assert(!/test\.txt/.test(syncData), 'execSync worked as expected'); 138 | 139 | let cp = child_process.spawn('ls', ['-lah']); 140 | let content = ''; 141 | cp.stdout.on('data', (data) => content += data); 142 | cp.on('close', async((code) => { 143 | console.assert(code === 0, 'spawn exited with the right code'); 144 | console.assert(content === syncData, 'spawn collected the right data'); 145 | })); 146 | 147 | })); 148 | 149 | }(Math.random())); 150 | 151 | 152 | let A = Class({ 153 | name: 'a', 154 | method: function () { 155 | return this.name; 156 | } 157 | }); 158 | let B = Class({ 159 | extends: A, 160 | static: { 161 | B: true 162 | }, 163 | name: 'b' 164 | }); 165 | 166 | console.assert((new B).name === 'b', 'Class#extends work as expected'); 167 | console.assert((new B) instanceof B && (new A) instanceof A && (new B) instanceof A, 168 | 'Class inheritance work as expected'); 169 | console.assert(B.B === true, 'Class#static work as expected'); 170 | 171 | A = Class({ 172 | constructor: function () { 173 | this.a = true; 174 | } 175 | }); 176 | B = Class({ 177 | extends: A, 178 | constructor: function () { 179 | this.b = true; 180 | } 181 | }); 182 | console.assert((new B).a === true && (new B).b === true, 183 | 'Class parent invoke works as expected'); 184 | 185 | 186 | fs.readdir('gjs-documentation/static/Gio-2.0', async(function(err, files) { 187 | console.assert(files.length > 0, 'multiple async readdir work as expected'); 188 | })); 189 | fs.readdir('gjs-documentation/static/GLib-2.0', async(function(err, files) { 190 | console.assert(files.length > 0, 'multiple async readdir work as expected'); 191 | })); 192 | 193 | }); -------------------------------------------------------------------------------- /jsgtk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | Function=Function//; for a in "$@"; do if [ "$a" = "-d" ] || [ "$a" = "--debug" ]; then export GTK_DEBUG=interactive; fi; done; if [ "$GTK_DEBUG" != "interactive" ]; then export GJS_DISABLE_EXTRA_WARNINGS=1; fi; if [ "$(which gjs 2> /dev/null)" != "" ]; then exec gjs "$0" "$@"; elif [ "$(which cjs 2> /dev/null)" != "" ]; then exec cjs "$0" "$@"; fi; exit 3 | 4 | /* jshint esversion: 6, strict: true, node: true */ 5 | /* global imports */ 6 | 7 | ;(function (runtime) {'use strict'; 8 | 9 | /*! MIT Style License 10 | 11 | Copyright (c) 2015 - 2016 Andrea Giammarchi @WebReflection 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | THE SOFTWARE. 30 | 31 | */ 32 | 33 | const 34 | 35 | // basic dependencies 36 | gi = imports.gi, 37 | GLib = gi.GLib, 38 | GFile = gi.Gio.File, 39 | 40 | // scoped global + shortcuts 41 | global = window, 42 | replace = String.prototype.replace, 43 | 44 | // folders bootstrap 45 | CURRENT_DIR = GLib.get_current_dir(), 46 | DIR_SEPARATOR = /\//.test(CURRENT_DIR) ? '/' : '\\', 47 | PROGRAM_NAME = imports.system.programInvocationName, 48 | PROGRAM_DIR = ((exe) => { 49 | let 50 | dir = exe.slice(0, -(1 + GLib.path_get_basename(exe).length)), 51 | path = dir.split(DIR_SEPARATOR) 52 | ; 53 | switch (path[path.length - 1]) { 54 | // global case 55 | case 'bin': 56 | path.pop(); 57 | path.push('lib', 'node_modules', 'jsgtk'); 58 | dir = path.join(DIR_SEPARATOR); 59 | break; 60 | // local module 61 | case '.bin': 62 | path.pop(); 63 | path.push('jsgtk'); 64 | dir = path.join(DIR_SEPARATOR); 65 | break; 66 | } 67 | return dir; 68 | })(GFile.new_for_path(PROGRAM_NAME).get_path()) 69 | ; 70 | 71 | // inject the jsgtk folder to import at runtime internal helpers 72 | imports.searchPath.push([PROGRAM_DIR, 'jsgtk_modules'].join(DIR_SEPARATOR)); 73 | 74 | // populate the constants file 75 | Object.defineProperties( 76 | imports.jsgtk.constants, 77 | { 78 | CURRENT_DIR: {enumerable: true, value: CURRENT_DIR}, 79 | DEBUG: {enumerable: true, value: ARGV.some(arg => arg === '--debug')}, 80 | DIR_SEPARATOR: {enumerable: true, value: DIR_SEPARATOR}, 81 | PROGRAM_NAME: {enumerable: true, value: PROGRAM_NAME}, 82 | PROGRAM_DIR: {enumerable: true, value: PROGRAM_DIR}, 83 | TRANSFORM: {enumerable: true, value: !ARGV.some(arg => arg === '--no-transform')} 84 | } 85 | ); 86 | 87 | // bring in polyfills and all modules loaders + process and timers 88 | const 89 | polyfills = imports.jsgtk.polyfills, 90 | mainloop = imports.jsgtk.mainloop, 91 | gtk = imports.jsgtk.gtk_modules.withRuntime(), 92 | core = imports.jsgtk.core_modules.withRuntime(evaluateModule), 93 | modules = imports.jsgtk.node_modules.withRuntime(evaluateModule), 94 | Babel = imports.jsgtk.babel.Babel, 95 | BabelTransformer = {plugins: [ 96 | 'transform-decorators-legacy', 97 | 'transform-class-properties', 98 | 'transform-flow-strip-types', 99 | 'transform-es2015-classes', 100 | 'transform-es2015-literals', 101 | 'transform-es2015-object-super', 102 | 'transform-es2015-parameters', 103 | 'transform-es2015-shorthand-properties', 104 | 'transform-es2015-unicode-regex', 105 | 'transform-exponentiation-operator', 106 | 'transform-es2015-template-literals', 107 | ]} 108 | ; 109 | 110 | if (typeof Promise === 'undefined') { 111 | Object.defineProperty( 112 | global, 113 | 'Promise', 114 | {value: imports.jsgtk.promise.ES6Promise.Promise} 115 | ); 116 | } 117 | 118 | // env normalization 119 | Object.defineProperties( 120 | global, 121 | { 122 | global: {enumerable: true, value: global}, 123 | // used as ES6 classes properties decorator: 124 | // https://github.com/WebReflection/jsgtk/blob/master/examples/lang.js 125 | GObjectProperties: {value: imports.jsgtk.extended.GObjectProperties}, 126 | // access to modules even internally 127 | require: {value: function require(module) { 128 | return requireWithPath(module, CURRENT_DIR); 129 | }} 130 | } 131 | ); 132 | 133 | // module handler 134 | function evaluateModule(sanitize, nmsp, unique, id, fd) { 135 | const 136 | dir = id.slice(0, -1 -fd.get_basename().length), 137 | exports = {}, 138 | module = {exports: exports, id: id}, 139 | content = (sanitize ? transform : String)( 140 | replace.call(fd.load_contents(null)[1], /^#![^\n\r]*/, '') 141 | ) 142 | ; 143 | // sanitize && print(transform(content)); 144 | nmsp[unique] = exports; 145 | runtime( 146 | 'require', 147 | 'exports', 148 | 'module', 149 | '__dirname', 150 | '__filename', 151 | content 152 | ).call( 153 | exports, 154 | function require(module) { 155 | return requireWithPath(module, dir); 156 | }, 157 | exports, 158 | module, 159 | dir, 160 | id 161 | ); 162 | return (nmsp[unique] = module.exports); 163 | } 164 | 165 | // the actual require 166 | function requireWithPath(module, dir) { 167 | print(dir + ' => ' + module); 168 | switch (true) { 169 | case core.has(module): 170 | return core.get(module); 171 | case gtk.has(module): 172 | return gtk.get(module); 173 | default: 174 | return modules.get(module) || modules.load(module, dir); 175 | } 176 | } 177 | 178 | // makes most ES6 compatible with GJS 179 | function transform(code) { 180 | return Babel.transform(code, BabelTransformer).code; 181 | } 182 | 183 | // initialize basic global modules 184 | ARGV.core = core; 185 | const 186 | process = core.get('process'), 187 | timers = core.get('timers'), 188 | console = core.get('console') 189 | ; 190 | delete ARGV.core; 191 | 192 | // TODO: implement a better logic for this 193 | Error.captureStackTrace = function captureStackTrace(target, constructor) { 194 | if (!target.stack) target.stack = ''; 195 | target.stack += (constructor ? (constructor.name + ': ') : '') + String(target) + '\n'; 196 | }; 197 | 198 | // program bootstrap 199 | if (process.argv.length > 1) { 200 | requireWithPath(process.argv[1], CURRENT_DIR); 201 | mainloop.run(); 202 | } else { 203 | if (ARGV.some((info, i) => { 204 | if (i && /^-e|--eval$/.test(ARGV[i - 1])) { 205 | runtime( 206 | 'require', 207 | '__dirname', 208 | '__filename', 209 | transform(info) 210 | ).call( 211 | global, 212 | global.require, 213 | CURRENT_DIR, 214 | '[eval]' 215 | ); 216 | return true; 217 | } 218 | return false; 219 | })) { 220 | mainloop.run(); 221 | } else { 222 | switch (true) { 223 | case ARGV.some(info => /^-(?:-version|v)$/.test(info)): 224 | print(imports.jsgtk.constants.VERSION); 225 | break; 226 | default: 227 | print([ 228 | '', 229 | ' \x1B[1mjsgtk ' + imports.jsgtk.constants.VERSION + '\x1B[0m', 230 | ' usage: jsgtk script.js [arguments]', 231 | ' jsgtk [options] script.js [arguments]', 232 | ' jsgtk (-d|--debug) script.js [arguments]', 233 | ' jsgtk (-e|--eval) \'console.log("runtime")\'', 234 | ' jsgtk (-v|--version)', 235 | ' jsgtk --no-transform # avoid babel transform', 236 | '', 237 | ' Find more at https://github.com/WebReflection/jsgtk', 238 | '' 239 | ].join('\n')); 240 | } 241 | } 242 | } 243 | 244 | }(Function)); -------------------------------------------------------------------------------- /jsgtk_modules/buffer.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/buffer.html 5 | * JSGtk Status incomplete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports, unescape, escape */ 10 | 11 | // TODO: the utf16le or the conversion is somehow screwed 12 | 13 | const 14 | GLib = imports.gi.GLib, 15 | map = Array.prototype.map, 16 | defineProperty = Object.defineProperty, 17 | hOP = Object.prototype.hasOwnProperty, 18 | fromCharCode = String.fromCharCode, 19 | charCode = c => c.charCodeAt(0), 20 | toUTF16 = s => unescape(encodeURIComponent(s)), 21 | toUTF8 = s => decodeURIComponent(escape(s)), 22 | toBuffer = s => toUTF16(s).split('').map(charCode), 23 | utf16le = { 24 | fromString: toBuffer, 25 | toString: (a) => { 26 | const out = []; 27 | for(let i = 0; i < a.length; i += 2) 28 | out.push(a[i] | (a[i + 1] << 8)); 29 | return fromCharCode.apply(null, out); 30 | } 31 | }, 32 | ENCODINGS = { 33 | ascii: { 34 | map: c => c & 0x7F, 35 | fromString: toBuffer, 36 | toString: function (a) { 37 | return fromCharCode.apply(null, map.call(a, this.map)); 38 | } 39 | }, 40 | base64: { 41 | fromString: s => GLib.base64_decode(s), 42 | toString: a => GLib.base64_encode(a) 43 | }, 44 | binary: { 45 | fromString: s => s.split(''), 46 | toString: a => fromCharCode.apply(null, a) 47 | }, 48 | hex: { 49 | map: i => ('0' + i.toString(16)).slice(-2), 50 | fromString: s => { 51 | const out = Array(s.length / 2); 52 | for (let i = 0; i < s.length; i += 2) 53 | out[i / 2] = parseInt(s.substr(i, 2), 16); 54 | return out; 55 | }, 56 | toString: function (a) { 57 | return map.call(a, this.map).join(''); 58 | } 59 | }, 60 | ucs2: utf16le, 61 | utf8: { 62 | fromString: toBuffer, 63 | toString: (a) => toUTF8(fromCharCode.apply(null, a)) 64 | }, 65 | utf16le: utf16le 66 | }, 67 | target = (parent, u8a, key) => { 68 | switch (key) { 69 | case 'buffer': 70 | case 'constructor': 71 | case 'toString': 72 | return parent; 73 | } 74 | return u8a; 75 | }, 76 | bufferProxy = { 77 | isExtensible: () => false, 78 | get: (parent, key, receiver) => { 79 | switch (key) { 80 | case 'buffer': 81 | case 'constructor': 82 | case 'toString': 83 | return parent[key]; 84 | } 85 | return parent.buffer[key]; 86 | }, 87 | set: (parent, key, value, receiver) => { 88 | switch (key) { 89 | case 'buffer': 90 | case 'constructor': 91 | case 'toString': 92 | parent[key] = value; 93 | } 94 | parent.buffer[key] = value; 95 | } 96 | } 97 | ; 98 | 99 | function Buffer(data, encoding) { 100 | let u8a; 101 | switch (arguments.length) { 102 | case 2: 103 | if (!Buffer.isEncoding(encoding)) 104 | throw new TypeError('unsupported encoding ' + encoding); 105 | u8a = new Uint8Array(ENCODINGS[encoding].fromString(data)); 106 | break; 107 | case 1: 108 | if (typeof data === 'string') { 109 | u8a = new Uint8Array(ENCODINGS.utf8.fromString(data)); 110 | } else { 111 | u8a = new Uint8Array(data); 112 | } 113 | break; 114 | default: 115 | u8a = new Uint8Array(); 116 | break; 117 | } 118 | return new Proxy( 119 | defineProperty(this, 'buffer', {get: () => u8a}), 120 | bufferProxy 121 | ); 122 | } 123 | 124 | Buffer.isBuffer = function isBuffer(obj) { 125 | return obj instanceof Buffer; 126 | }; 127 | 128 | Buffer.isEncoding = function isEncoding(encoding) { 129 | return ENCODINGS.hasOwnProperty(encoding); 130 | }; 131 | 132 | // TODO: this ain't gonna work 133 | Buffer.from = function from(obj) { 134 | return new Buffer(String(obj)); 135 | }; 136 | 137 | // work around for axios 138 | Buffer.concat = function (list, length) { 139 | return list; 140 | }; 141 | 142 | 143 | Buffer.allocUnsafe = function (size) { 144 | return new Buffer(size); 145 | }; 146 | 147 | Object.defineProperties( 148 | Object.setPrototypeOf( 149 | Buffer.prototype, 150 | Uint8Array.prototype 151 | ), 152 | { 153 | toString: { 154 | configurable: true, 155 | writable: true, 156 | value: function toString(encoding) { 157 | if (!encoding) encoding = 'utf8'; 158 | if (!Buffer.isEncoding(encoding)) 159 | throw new TypeError('unsupported encoding ' + encoding); 160 | return ENCODINGS[encoding].toString(this.buffer); 161 | } 162 | } 163 | } 164 | ); 165 | 166 | module.exports = { 167 | Buffer: Buffer 168 | }; 169 | -------------------------------------------------------------------------------- /jsgtk_modules/child_process.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/child_process.html 5 | * JSGtk Status incomplete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports */ 10 | 11 | const 12 | 13 | GLib = imports.gi.GLib, 14 | 15 | Class = process.binding('util').Class, 16 | mainloop = process.binding('mainloop'), 17 | 18 | CURRENT_DIR = GLib.get_current_dir(), 19 | 20 | empty = Array.prototype, 21 | keys = Object.keys, 22 | 23 | EventEmitter = require('events').EventEmitter, 24 | Stream = require('stream').Stream, 25 | 26 | loopGoIfConnected = function () { 27 | if (this.pid) { 28 | GLib.spawn_close_pid(this.pid); 29 | this.pid = null; 30 | } 31 | if (this.connected) { 32 | this.connected = false; 33 | mainloop.go(); 34 | } 35 | }, 36 | 37 | disconnectChild = function () { 38 | loopGoIfConnected.call(this); 39 | this.stdin.emit('disconnect'); 40 | this.stdout.emit('disconnect'); 41 | this.stderr.emit('disconnect'); 42 | }, 43 | 44 | ChildProcess = Class(EventEmitter, { 45 | constructor: function ChildProcess(ok, pid, stdin, stdout, stderr) { 46 | EventEmitter.call(this); 47 | if (ok) { 48 | mainloop.wait(); 49 | this.pid = pid; 50 | this.connected = ok; 51 | this.stdin = new Stream(stdin) 52 | .on('error', (reason) => this.emit('error', reason)); 53 | this.stdout = new Stream(stdout) 54 | .on('close', (code, reason) => { 55 | loopGoIfConnected.call(this); 56 | this.emit('close', code, reason); 57 | }) 58 | .on('error', (reason) => this.emit('error', reason)); 59 | this.stderr = new Stream(stderr) 60 | .on('error', (reason) => this.emit('error', reason)); 61 | this.stdio = [ 62 | this.stdin, 63 | this.stdout, 64 | this.stderr 65 | ]; 66 | } 67 | }, 68 | unref: function () { 69 | // TODO: try to make unref possible 70 | }, 71 | connected: false, 72 | disconnect: function disconnect() { 73 | disconnectChild.call(this); 74 | this.emit('disconnect'); 75 | }, 76 | kill: function kill(signal) { 77 | disconnectChild.call(this); 78 | this.emit('exit', null, signal || 'SIGTERM'); 79 | } 80 | }) 81 | ; 82 | 83 | module.exports = { 84 | spawn: function spawn(command, args, options) { 85 | if (!options) options = {}; 86 | let cp, [ok, pid, stdin, stdout, stderr] = GLib.spawn_async_with_pipes( 87 | options.cwd || CURRENT_DIR, 88 | [command].concat(args || empty), 89 | options.env ? keys(options.env) 90 | .reduce((a, k) => { 91 | a.push(k + '=' + options.env[k]); 92 | return a; 93 | }, []) : null, 94 | GLib.SpawnFlags.SEARCH_PATH, 95 | null 96 | /* 97 | ,null 98 | ,null 99 | ,GLib.SPAWN_CHILD_INHERITS_STDIN 100 | ,GLib.SPAWN_STDOUT_TO_DEV_NULL 101 | ,GLib.SPAWN_STDERR_TO_DEV_NULL 102 | ,null 103 | //*/ 104 | ); 105 | if (ok) { 106 | cp = new ChildProcess(ok, pid, stdin, stdout, stderr); 107 | } else { 108 | cp = new ChildProcess(null); 109 | setTimeout(() => cp.emit('error', stderr), 0); 110 | } 111 | return cp; 112 | }, 113 | 114 | execSync: function execSync(command, options) { 115 | // TODO: support other options ? 116 | if (!options) options = {}; 117 | let [ok, stdout, stderr, exit_status] = GLib.spawn_command_line_sync( 118 | options.cwd ? ('cd ' + options.cwd + ' && ' + command) : command 119 | ); 120 | if (!ok) throw stderr; 121 | return stdout; 122 | }, 123 | 124 | spawnSync: function spawnSync(command, args, options) { 125 | // TODO: support other options ? 126 | if (!options) options = {}; 127 | let [ok, stdout, stderr, exit_status] = GLib.spawn_sync( 128 | options.cwd || CURRENT_DIR, 129 | [command].concat(args || empty), 130 | options.env ? keys(options.env) 131 | .reduce((a, k) => { 132 | a.push(k + '=' + options.env[k]); 133 | return a; 134 | }, []) : null, 135 | GLib.SpawnFlags.SEARCH_PATH, 136 | null 137 | ); 138 | return { 139 | output: [null, stdout, stderr], 140 | stdout: stdout, 141 | stderr: stderr, 142 | status: exit_status, 143 | error: ok ? null : stderr 144 | }; 145 | } 146 | }; 147 | -------------------------------------------------------------------------------- /jsgtk_modules/console.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/console.html 5 | * JSGtk Status incomplete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports, print, printerr */ 10 | 11 | const 12 | RESET = '\x1b[0m', 13 | RED = '\x1b[0;31m', 14 | GREEN = '\x1b[0;32m', 15 | YELLOW = '\x1b[0;33m', 16 | BOLD = '\x1b[1m', 17 | GLib = imports.gi.GLib, 18 | GFormat = imports.format, 19 | util = process.binding('util'), 20 | inspect = arg => typeof arg === 'string' ? 21 | arg : util.inspect(arg), 22 | show = (fn, pre, args, post) => { 23 | fn(pre + ( 24 | /%[sdxf]/.test(args[0]) ? 25 | GFormat.vprintf(args[0], args.slice(1)) : 26 | args.map(inspect).join(' ') 27 | ) + post); 28 | }, 29 | timers = Object.create(null), 30 | console = { 31 | assert: function assert(what, why) { 32 | if (!what) { 33 | imports.jsUnit.error( 34 | RED + BOLD + '[WRONG]' + RESET + ' ' + RED + BOLD + (why || '') + RESET 35 | ); 36 | } 37 | }, 38 | error: function error(what, why) { 39 | show( 40 | printerr, 41 | RED + BOLD + '[ERROR]' + RESET + ' ' + RED, 42 | util.slice.apply(0, arguments), 43 | RESET 44 | ); 45 | }, 46 | info: function info(what, why) { 47 | show( 48 | print, 49 | GREEN + BOLD + '[INFO]' + RESET + ' ' + BOLD, 50 | util.slice.apply(0, arguments), 51 | RESET 52 | ); 53 | }, 54 | log: function log(what, why) { 55 | show( 56 | print, 57 | '', 58 | util.slice.apply(0, arguments), 59 | '' 60 | ); 61 | }, 62 | time: function time(name) { 63 | timers[name] = GLib.get_real_time(); 64 | }, 65 | timeEnd: function timeEnd(name) { 66 | let result = (GLib.get_real_time() - timers[name]) / 1000; 67 | delete timers[name]; 68 | console.log(name + ': ' + result + 'ms'); 69 | }, 70 | warn: function warn(what, why) { 71 | show( 72 | print, 73 | YELLOW + BOLD + '[WARNING]' + RESET + ' ' + YELLOW, 74 | util.slice.apply(0, arguments), 75 | RESET 76 | ); 77 | } 78 | } 79 | ; 80 | 81 | Object.defineProperty( 82 | global, 83 | 'console', 84 | {enumerable: true, value: console} 85 | ); 86 | 87 | module.exports = console; 88 | -------------------------------------------------------------------------------- /jsgtk_modules/crypto.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/crypto.html 5 | * JSGtk Status incomplete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports */ 10 | 11 | const 12 | GLib = imports.gi.GLib, 13 | ChecksumType = GLib.ChecksumType, 14 | Class = process.binding('util').Class, 15 | buffer = require('buffer'), 16 | Buffer = buffer.Buffer, 17 | ENCODINGS = buffer.ENCODINGS, 18 | hashTypes = Object.keys(ChecksumType), 19 | getHashType = (type) => { 20 | for (let 21 | TYPE = type.toUpperCase(), 22 | i = 0; i < hashTypes.length; i++ 23 | ) { 24 | if (hashTypes[i] === TYPE) return ChecksumType[TYPE]; 25 | } 26 | throw new TypeError('Unknown type ' + type); 27 | }, 28 | HBase = Class({ 29 | digest: function digest(type) { 30 | var result = this.data.get_string(); 31 | return type === 'hex' ? 32 | result : 33 | new Buffer(result, 'hex').toString(type); 34 | }, 35 | update: function update(data, encoding) { 36 | this.data.update( 37 | (encoding ? 38 | new Buffer(data, encoding) : 39 | new Buffer(data) 40 | ).toString() 41 | ); 42 | } 43 | }), 44 | Hash = Class(HBase, { 45 | constructor: function Hash(type) { 46 | this.data = new GLib.Checksum(type); 47 | } 48 | }), 49 | Hmac = Class(HBase, { 50 | constructor: function Hmac(type, key) { 51 | this.type = type; 52 | this.key = key; 53 | this.data = []; 54 | this.data.update = this.data.push; 55 | }, 56 | digest: function digest(type) { 57 | var str = this.data.join(''); 58 | return GLib.compute_hmac_for_string( 59 | this.type, 60 | this.key, 61 | str, 62 | -1 63 | ); 64 | } 65 | }) 66 | ; 67 | 68 | module.exports = { 69 | createHash: function createHash(type) { 70 | return new Hash(getHashType(type)); 71 | }, 72 | createHmac: function createHmac(type, key) { 73 | return new Hmac(getHashType(type), key); 74 | }, 75 | getHashes: function getHashes() { 76 | return hashTypes.map(type => type.toLowerCase()); 77 | }, 78 | randomBytes: function randomBytes(size) { 79 | let chars = new Buffer(size); 80 | while (size--) chars[size] = GLib.random_int_range(0, 255); 81 | return String.fromCharCode.apply(null, chars); 82 | } 83 | }; 84 | -------------------------------------------------------------------------------- /jsgtk_modules/https.js: -------------------------------------------------------------------------------- 1 | module.exports = require('http'); -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/buffer.js: -------------------------------------------------------------------------------- 1 | /*! © Andrea Giammarchi @WebReflection */ 2 | 3 | /* jshint esversion: 6, strict: true, node: true */ 4 | 5 | // partially populated at runtime on bootstrap 6 | 7 | (function (exports) {'use strict'; 8 | 9 | exports.compare = function (a, b) { 10 | let 11 | al = a.length, 12 | bl = b.length, 13 | code = al < bl ? -1 : 1 14 | ; 15 | if (a === b) return 0; 16 | if (a.length === b.length) { 17 | for (let i = 0; i < a.length; i++) { 18 | if (a[i] !== b[i]) return code; 19 | } 20 | return 0; 21 | } 22 | return code; 23 | }; 24 | 25 | }(this)); 26 | -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/constants.js: -------------------------------------------------------------------------------- 1 | /*jshint esversion:6,strict:true,node:true*/(function (e) {'use strict';e.JSGTK=true;e.VERSION='0.12.1';}(this)); 2 | -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/core_modules.js: -------------------------------------------------------------------------------- 1 | /*! © Andrea Giammarchi @WebReflection */ 2 | 3 | /* jshint esversion: 6, strict: true, node: true */ 4 | 5 | (function (exports, evaluate) {'use strict'; 6 | 7 | const 8 | 9 | GFile = imports.gi.Gio.File, 10 | 11 | constants = imports.jsgtk.constants, 12 | 13 | core = [ 14 | 'assert', 15 | 'buffer', 16 | 'child_process', 17 | 'console', 18 | 'crypto', 19 | 'events', 20 | 'fs', 21 | 'http', 22 | 'https', 23 | 'module', 24 | 'os', 25 | 'path', 26 | 'process', 27 | 'punycode', 28 | 'querystring', 29 | 'stream', 30 | 'timers', 31 | 'tty', 32 | 'url', 33 | 'util', 34 | 'zlib' 35 | ].reduce( 36 | (core, id) => Object.defineProperty(core, id, { 37 | configurable: true, 38 | get: () => { 39 | delete core[id]; 40 | return load(id); 41 | } 42 | }), 43 | Object.create(null) 44 | ), 45 | 46 | exp = { 47 | has: (id) => id in core, 48 | get: (id) => core[id], 49 | load: load 50 | } 51 | 52 | ; 53 | 54 | function load(id) { 55 | let fd = GFile.new_for_path([ 56 | constants.PROGRAM_DIR, 57 | 'jsgtk_modules', 58 | id + '.js' 59 | ].join(constants.DIR_SEPARATOR)); 60 | return evaluate(false, core, id, fd.get_path(), fd); 61 | } 62 | 63 | exports.withRuntime = function setup($evaluate) { 64 | if (!evaluate) evaluate = $evaluate; 65 | return exp; 66 | }; 67 | 68 | }(this)); 69 | -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/extended.js: -------------------------------------------------------------------------------- 1 | /*! © Andrea Giammarchi @WebReflection */ 2 | 3 | /* jshint esversion: 6, strict: true, node: true */ 4 | /* global imports */ 5 | 6 | (function (exports) {'use strict'; 7 | 8 | const 9 | PROTODECORATORS = '@\x01protodecorators', 10 | DECORATORS = '@\x01decorators', 11 | PROPERTIES = '@\x01properties', 12 | GLib = imports.gi.GLib, 13 | GObject = imports.gi.GObject, 14 | ParamFlags = GObject.ParamFlags, 15 | ParamSpec = GObject.ParamSpec, 16 | Lang = imports.lang, 17 | empty = Array.prototype, 18 | hOP = Object.prototype.hasOwnProperty, 19 | toString = Object.prototype.toString, 20 | dP = Object.defineProperty, 21 | dPs = Object.defineProperties, 22 | gPO = Object.getPrototypeOf, 23 | sPO = Object.setPrototypeOf, 24 | gOPDs = Object.getOwnPropertyDescriptors, 25 | functionKeys = Object.getOwnPropertyNames(nope), 26 | sObject = toString.call(Object.prototype) 27 | ; 28 | 29 | function get(obj, prop, dft) { 30 | return hOP.call(obj, prop) ? obj[prop] : dft; 31 | } 32 | 33 | // accepts both 'readwrite' and GObject.ParamFlags.READWRITE 34 | function getFlags(flags) { 35 | let flag; 36 | for (let i = 0; i < flags.length; i++) { 37 | let tmp = flags[i]; 38 | flag |= typeof tmp === 'string' ? 39 | ParamFlags[resolveFlag(tmp)] : tmp; 40 | } 41 | return flag; 42 | } 43 | 44 | // transform shortcuts into real names 45 | function resolveFlag(f) { 46 | let F = f.toUpperCase(); 47 | switch (F) { 48 | case 'C': return 'CONSTRUCT'; 49 | case 'CO': return 'CONSTRUCT_ONLY'; 50 | case 'D': return 'DEPRECATED'; 51 | case 'EN': return 'EXPLICIT_NOTIFY'; 52 | case 'LV': return 'LAX_VALIDATION'; 53 | case 'PVT': return 'PRIVATE'; 54 | case 'R': return 'READABLE'; 55 | case 'RW': return 'READWRITE'; 56 | case 'SBLURB': return 'STATIC_BLURB'; 57 | case 'SNAME': return 'STATIC_NAME'; 58 | case 'SNICK': return 'STATIC_NICK'; 59 | case 'W': return 'WRITABLE'; 60 | default: return F; 61 | } 62 | } 63 | 64 | // accepts both 'uint' and GObject.ParamSpec.uint 65 | function getType(type) { 66 | return typeof type === 'string' ? ParamSpec[type] : type; 67 | } 68 | 69 | function getInstanceValue(value) { 70 | return value && typeof value === 'object' ? 71 | new value.constructor() : value; 72 | } 73 | 74 | // from a value type descriptor, retireve 75 | // the right amount of arguments to invoke its ParamSpec 76 | // defaults are included 77 | function getTypeInitializer(value, dfts) { 78 | switch (value.type.toLowerCase()) { 79 | case 'boolean': 80 | return [ 81 | (dfts.value = get(value, 'value', false)) 82 | ]; 83 | case 'char': 84 | case 'int': 85 | return [ 86 | get(value, 'min', GLib.MININT32), 87 | get(value, 'max', GLib.MAXINT32), 88 | (dfts.value = get(value, 'value', 0)), 89 | ]; 90 | case 'uchar': 91 | case 'uint': 92 | return [ 93 | get(value, 'min', 0), 94 | get(value, 'max', GLib.MAXUINT32), 95 | (dfts.value = get(value, 'value', 0)), 96 | ]; 97 | case 'int64': 98 | return [ 99 | get(value, 'min', GLib.MININT64), 100 | get(value, 'max', GLib.MAXINT64), 101 | (dfts.value = get(value, 'value', 0)), 102 | ]; 103 | case 'uint64': 104 | return [ 105 | get(value, 'min', 0), 106 | get(value, 'max', GLib.MAXUINT64), 107 | (dfts.value = get(value, 'value', 0)), 108 | ]; 109 | case 'enum': 110 | case 'flags': 111 | return [ 112 | (dfts.value = get(value, 'value', 0)), 113 | get(value, 'class', null) 114 | ]; 115 | case 'string': 116 | // to convert empty strings to null, null: true 117 | // to convert null to empty strings, null: false 118 | let nullStrings = ~get(value, 'null', 0); 119 | return [ 120 | get(value, 'first', ''), 121 | get(value, 'nth', ''), 122 | (dfts.value = get(value, 'value', '')), 123 | 2 + nullStrings, 124 | ~nullStrings 125 | ]; 126 | case 'param': 127 | case 'boxed': 128 | case 'object': 129 | case 'pointer': 130 | return empty; 131 | case 'unichar': 132 | return [ 133 | get(value, 'value', '') 134 | ]; 135 | case 'override': 136 | return [ 137 | get(value, 'overridden', null) 138 | ]; 139 | case 'valuearray': 140 | case 'variant': 141 | return [ 142 | (dfts.value = get(value, 'spec', get(value, 'value', []))), 143 | get(value, 'length', 0) 144 | ]; 145 | case 'gtype': /* falls through */ 146 | case 'double': /* falls through */ 147 | case 'float': /* falls through */ 148 | case 'long': /* falls through */ 149 | case 'ulong': /* falls through */ 150 | default: 151 | throw new Error( 152 | 'unable to define this kind of parameter: ' + 153 | value.type 154 | ); 155 | } 156 | } 157 | 158 | function createProperties(props, dfts) { 159 | const properties = {}; 160 | Object.keys(props).forEach(key => { 161 | let value = props[key]; 162 | // parse only objects, opt out using GObject directly 163 | if (toString.call(value) === sObject) { 164 | dfts[key] = {}; 165 | properties[key] = getType(value.type).apply(null, [ 166 | key, 167 | '', 168 | '', 169 | (dfts[key].flags = getFlags( 170 | empty.concat(value.flags || 'readwrite') 171 | )) 172 | ].concat( 173 | getTypeInitializer(value, dfts[key]) 174 | )); 175 | } else { 176 | properties[key] = value; 177 | } 178 | }); 179 | return properties; 180 | } 181 | 182 | function metaClass(p) { 183 | return typeof p === 'function' && 184 | p.prototype && 185 | p.prototype.__metaclass__; 186 | } 187 | 188 | function nope() {} 189 | 190 | exports.GObjectProperties = function (properties) { 191 | return function classDecorator(Class) { 192 | const dfts = {}; 193 | const protoDescriptors = {}; 194 | Class[PROPERTIES] = Object.assign( 195 | Class[PROPERTIES] || {}, 196 | createProperties(properties, dfts) 197 | ); 198 | Class[DECORATORS] = (Class[DECORATORS] || empty).concat(function () { 199 | dPs(this, dfts); 200 | }); 201 | Class[PROTODECORATORS] = (Class[PROTODECORATORS] || empty).concat(function () { 202 | dPs(this, protoDescriptors); 203 | }); 204 | Object.keys(dfts).forEach(key => { 205 | const 206 | guard = (who) => { 207 | if (who instanceof Class) 208 | throw new Error(key + ' should not be directly accessed'); 209 | return true; 210 | }, 211 | info = dfts[key], 212 | flags = info.flags, 213 | value = info.value, 214 | enumreable = !(flags & ParamFlags.PRIVATE), 215 | ukey = '_' + key, 216 | get = function () { 217 | return (enumreable || guard(this)) && this[ukey]; 218 | }, 219 | set = function (value) { 220 | if (enumreable || guard(this)) 221 | this[ukey] = value; 222 | } 223 | ; 224 | delete dfts[key]; 225 | switch (true) { 226 | case !!(flags & ParamFlags.READABLE) && !(flags & ParamFlags.WRITABLE): 227 | protoDescriptors[key] = {get: get}; 228 | dfts[ukey] = { 229 | writable: true, 230 | get value() { 231 | return getInstanceValue(value); 232 | } 233 | }; 234 | break; 235 | case !!(flags & ParamFlags.WRITABLE) && !(flags & ParamFlags.READABLE): 236 | protoDescriptors[key] = {set: set}; 237 | dfts[ukey] = { 238 | writable: true, 239 | get value() { 240 | return getInstanceValue(value); 241 | } 242 | }; 243 | break; 244 | case !!(flags & ParamFlags.READWRITE): 245 | if (!enumreable) (protoDescriptors[key] = { 246 | get: get, 247 | set: set 248 | }); 249 | dfts[enumreable ? key : ukey] = { 250 | enumreable: enumreable, 251 | writable: !(flags & ParamFlags.CONSTRUCT_ONLY), 252 | get value() { 253 | return getInstanceValue(value); 254 | } 255 | }; 256 | break; 257 | } 258 | }); 259 | return Class; 260 | }; 261 | }; 262 | 263 | exports.superProto = function (Class) { 264 | return metaClass(Class) ? nope : gPO(Class); 265 | }; 266 | 267 | exports.Class = function (Class) { 268 | let Parent = gPO(Class); 269 | // Gtk Classes have funny things attached to their prototype 270 | // when a Class is native one, use the lang.Class instead 271 | if (metaClass(Parent)) { 272 | let 273 | staticDescriptors = gOPDs(Class), 274 | protoDescriptors = Class.prototype 275 | ; 276 | // ignore common function descriptors 277 | functionKeys.forEach(key => delete staticDescriptors[key]); 278 | delete staticDescriptors[DECORATORS]; 279 | if (hOP.call(Class, PROPERTIES)) { 280 | delete staticDescriptors[PROPERTIES]; 281 | protoDescriptors.Properties = Class[PROPERTIES]; 282 | } 283 | delete protoDescriptors.constructor; 284 | protoDescriptors.Extends = Parent; 285 | protoDescriptors.Name = Class.name || 'Anonymous'; 286 | (Class[PROTODECORATORS] || empty).forEach(decorator => { 287 | decorator.call(protoDescriptors); 288 | }); 289 | return dPs( 290 | new Lang.Class( 291 | dP( 292 | protoDescriptors, 293 | '_init', 294 | { 295 | configurable: true, 296 | value: (function ($init, decorators) { 297 | return decorators.length ? 298 | function _init() { 299 | for (let 300 | i = 0; 301 | i < decorators.length; 302 | decorators[i++].call(this) 303 | ) {} 304 | $init.apply(this, arguments); 305 | } : 306 | $init; 307 | }( 308 | hOP.call(protoDescriptors, '_init') ? 309 | protoDescriptors._init : 310 | function _init() { 311 | this.parent.apply(this, arguments); 312 | Class.apply(this, arguments); 313 | }, 314 | Class[DECORATORS] || empty 315 | )) 316 | } 317 | ) 318 | ), 319 | staticDescriptors 320 | ); 321 | } 322 | return Class; 323 | }; 324 | 325 | }(this)); 326 | -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/hybrid_emitter.js: -------------------------------------------------------------------------------- 1 | /*! © Andrea Giammarchi @WebReflection */ 2 | 3 | /* jshint esversion: 6, strict: true, node: true */ 4 | 5 | (function (exports) {'use strict'; 6 | 7 | const 8 | defineProperties = Object.defineProperties, 9 | weakEmitters = new WeakMap(), 10 | descriptors = { 11 | addListener: {value: addListener}, 12 | emit: {value: null}, 13 | on: {value: addListener}, 14 | once: {value: function once(type, handler) { 15 | let tmp = function () { 16 | removeListener.call(this, type, tmp); 17 | handler.apply(this, arguments); 18 | }.bind(this); 19 | return addListener.call(this, type, tmp); 20 | }}, 21 | removeListener: {value: removeListener} 22 | } 23 | ; 24 | 25 | function addListener(type, handler) { 26 | /* jshint validthis: true */ 27 | let info = weakEmitters.get(this); 28 | if (!info) weakEmitters.set(this, info = Object.create(null)); 29 | let 30 | tinfo = info[type] || (info[type] = {k: [], v: []}), 31 | i = tinfo.k.indexOf(handler) 32 | ; 33 | if (i < 0) { 34 | i = tinfo.k.push(handler) - 1; 35 | tinfo.v[i] = addListenerNative.call( 36 | this, 37 | type, 38 | handler.bind(this) 39 | ); 40 | } 41 | return this; 42 | } 43 | 44 | function addListenerNative(type, handler) { 45 | /* jshint validthis: true */ 46 | try { 47 | return this.connect(type, handler); 48 | } catch(meh) { 49 | return Infinity; 50 | } 51 | } 52 | 53 | function emitUser(type, ...args) { 54 | /* jshint validthis: true */ 55 | let info = weakEmitters.get(this); 56 | if (!info) return false; 57 | let tinfo = info[type]; 58 | if (!tinfo) return false; 59 | tinfo.k.forEach(fn => fn.apply(this, args)); 60 | return true; 61 | } 62 | 63 | function removeListener(type, handler) { 64 | /* jshint validthis: true */ 65 | let info = weakEmitters.get(this); 66 | if (!info) return this; 67 | let tinfo = info[type]; 68 | if (!tinfo) return this; 69 | let i = tinfo.k.indexOf(handler); 70 | if (i < 0) return this; 71 | let signal = tinfo.v[i]; 72 | if (signal < Infinity) this.disconnect(signal); 73 | tinfo.k.splice(i, 1); 74 | tinfo.v.splice(i, 1); 75 | if (tinfo.k.length < 1) delete info[type]; 76 | return this; 77 | } 78 | 79 | exports.augment = function augment(proto) { 80 | let descriptor = Object.getOwnPropertyDescriptor(proto, 'emit'); 81 | if (!descriptor || descriptor.configurable) { 82 | const $emit = proto.emit; 83 | descriptors.emit.value = $emit ? 84 | function emit() { 85 | // TODO: is there a better way to know if 86 | // this is a native listener or not? 87 | try { 88 | $emit.apply(this, arguments); 89 | return true; 90 | } catch(meh) { 91 | return emitUser.apply(this, arguments); 92 | } 93 | } : 94 | emitUser 95 | ; 96 | defineProperties(proto, descriptors); 97 | } 98 | }; 99 | 100 | }(this)); 101 | -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/mainloop.js: -------------------------------------------------------------------------------- 1 | /*! © Andrea Giammarchi @WebReflection */ 2 | 3 | /* jshint esversion: 6, strict: true, node: true */ 4 | 5 | (function (exports) {'use strict'; 6 | 7 | let counter = 0, id = 0; 8 | 9 | const 10 | mainLoop = imports.gi.GLib.MainLoop.new(null, false), 11 | gjsLoop = imports.mainloop, 12 | quit = function quit() { 13 | id = 0; 14 | if (counter < 1) mainLoop.quit(); 15 | return false; 16 | }, 17 | 18 | // exported methods 19 | wait = () => { 20 | counter++; 21 | }, 22 | go = () => { 23 | if (--counter < 1) { 24 | if (id) gjsLoop.source_remove(id); 25 | id = gjsLoop.timeout_add(33, quit); 26 | } 27 | }, 28 | idle = (fn, ...args) => { 29 | wait(); 30 | gjsLoop.idle_add(() => { 31 | go(); 32 | fn.apply(null, args); 33 | }); 34 | }, 35 | run = () => { 36 | if (!mainLoop.is_running() && 0 < counter) mainLoop.run(); 37 | } 38 | ; 39 | 40 | exports.go = go; 41 | exports.idle = idle; 42 | exports.run = run; 43 | exports.wait = wait; 44 | 45 | }(this)); 46 | -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/node_modules.js: -------------------------------------------------------------------------------- 1 | /*! © Andrea Giammarchi @WebReflection */ 2 | 3 | /* jshint esversion: 6, strict: true, node: true, eqnull:true */ 4 | 5 | (function (exports, evaluate) {'use strict'; 6 | 7 | const 8 | 9 | gi = imports.gi, 10 | GLib = gi.GLib, 11 | Gio = gi.Gio, 12 | GFile = Gio.File, 13 | 14 | constants = imports.jsgtk.constants, 15 | DIR_SEPARATOR = constants.DIR_SEPARATOR, 16 | TRANSFORM = constants.TRANSFORM, 17 | CURRENT_DIR = constants.CURRENT_DIR, 18 | 19 | modules = Object.create(null), 20 | loading = Object.create(null), 21 | 22 | exp = { 23 | has: (id) => id in modules, 24 | get: (id) => modules[id], 25 | load: load 26 | } 27 | 28 | ; 29 | 30 | function error(module, dir) { 31 | throw new Error('unable to find module ' + module + ' @ ' + dir); 32 | } 33 | 34 | function grabModuleFD(path) { 35 | let fd = GFile.new_for_path(path); 36 | if (fd.query_exists(null)) { 37 | switch (fd.query_file_type(Gio.FileQueryInfoFlags.NONE, null)) { 38 | case Gio.FileType.REGULAR: return fd; 39 | case Gio.FileType.DIRECTORY: 40 | fd = GFile.new_for_path(path + DIR_SEPARATOR + 'package.json'); 41 | if (fd.query_exists(null)) { 42 | let content = JSON.parse(String.prototype.trim.call(fd.load_contents(null)[1])); 43 | if (content.main == null) content.main = 'index.js'; 44 | else if (content.main.slice(-3) !== '.js') content.main += '.js'; 45 | fd = GFile.new_for_path(path + DIR_SEPARATOR + content.main); 46 | if (fd.query_exists(null)) return fd; 47 | } else { 48 | fd = jsMainOrIndex(path); 49 | if (fd) return fd; 50 | } 51 | } 52 | } else { 53 | fd = jsMainOrIndex(path); 54 | if (fd) return fd; 55 | } 56 | return null; 57 | } 58 | 59 | function checkLoading(path) { 60 | if (loading[path] > 100) throw new Error('Circular dependencies detected: ' + path); 61 | loading[path] = (loading[path] || 0) + 1; 62 | } 63 | 64 | function getModuleFile(path, dir) { 65 | let fd; 66 | switch (true) { 67 | case GLib.path_is_absolute(path): 68 | checkLoading(path); 69 | fd = grabModuleFD(path); 70 | if (fd) { 71 | return fd; 72 | } else if (path.slice(-3) !== '.js') { 73 | fd = jsMainOrIndex(path); 74 | if (fd) return fd; 75 | } 76 | return error(path, dir); 77 | case '.' === path[0]: 78 | fd = GFile.new_for_path(dir + DIR_SEPARATOR + path); 79 | return getModuleFile(fd.get_path()); 80 | default: 81 | checkLoading(path); 82 | const gPs = [dir].concat(require('module').globalPaths); 83 | while (gPs.length) { 84 | let tmp = GFile.new_for_path(gPs.shift()); 85 | do { 86 | fd = grabModuleFD([ 87 | tmp.get_path(), 'node_modules', path 88 | ].join(DIR_SEPARATOR)); 89 | if (fd) return fd; 90 | } while((tmp = tmp.get_parent())); 91 | } 92 | // This is a fix for cases when a node module requires one of its dependencies from a child directory 93 | if (path.indexOf('.') === -1 && path.indexOf(DIR_SEPARATOR) === -1 && path.indexOf('index') === -1) { 94 | let newPath = [ 95 | CURRENT_DIR, 'node_modules', path, 'index' 96 | ].join(DIR_SEPARATOR); 97 | return getModuleFile(newPath, dir); 98 | } 99 | return error(path, dir); 100 | } 101 | } 102 | 103 | function jsMainOrIndex(path) { 104 | let fd = GFile.new_for_path(path + '.js'); 105 | if (fd.query_exists(null)) return fd; 106 | fd = GFile.new_for_path(path + DIR_SEPARATOR + 'index.js'); 107 | if (fd.query_exists(null)) return fd; 108 | fd = GFile.new_for_path(path + DIR_SEPARATOR + 'main.js'); 109 | if (fd.query_exists(null)) return fd; 110 | } 111 | 112 | function load(module, dir) { 113 | let 114 | fd = getModuleFile(module, dir), 115 | id = fd.get_path() 116 | ; 117 | if (exp.has(id)) { 118 | return exp.get(id); 119 | } else if (id.slice(-5) === '.json') { 120 | let [success, json] = fd.load_contents(null); 121 | try { 122 | if (!success) { 123 | throw new Error(); 124 | } 125 | return JSON.parse(json); 126 | } catch (e) { 127 | error(module, dir); 128 | } 129 | } else { 130 | return evaluate(TRANSFORM, modules, id, id, fd); 131 | } 132 | } 133 | 134 | exports.withRuntime = function setup($evaluate) { 135 | if (!evaluate) evaluate = $evaluate; 136 | return exp; 137 | }; 138 | 139 | }(this)); 140 | -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/polyfills.js: -------------------------------------------------------------------------------- 1 | /*! © Andrea Giammarchi @WebReflection */ 2 | 3 | /* jshint esversion: 6, strict: implied */ 4 | 5 | // inline polyfills for Object 6 | [ 7 | function assign() { 8 | function isEnumerable(key) { 9 | /* jshint validthis: true */ 10 | return Object.getOwnPropertyDescriptor(this, key).enumerable; 11 | } 12 | return { 13 | configurable: true, 14 | writable: true, 15 | value: function assign(target, ...sources) { 16 | for (var 17 | source, 18 | add = function (key) { 19 | target[key] = source[key]; 20 | }, 21 | i = 0; i < sources.length; i++ 22 | ) { 23 | source = sources[i]; 24 | Object.keys(source).forEach(add); 25 | Object.getOwnPropertySymbols(source) 26 | .filter(isEnumerable, source) 27 | .forEach(add); 28 | } 29 | return target; 30 | } 31 | }; 32 | }, 33 | function setPrototypeOf() { 34 | var set = Object.getOwnPropertyDescriptor( 35 | Object.prototype, 36 | '__proto__' 37 | ).set; 38 | return { 39 | configurable: true, 40 | writable: true, 41 | value: function setPrototypeOf(target, proto) { 42 | set.call(target, proto); 43 | return target; 44 | } 45 | }; 46 | }, 47 | function getOwnPropertyDescriptors() { 48 | var 49 | defineProperty = Object.defineProperty, 50 | gOPD = Object.getOwnPropertyDescriptor, 51 | gOPN = Object.getOwnPropertyNames, 52 | gOPS = Object.getOwnPropertySymbols, 53 | ownKeys = gOPS ? 54 | function (target) { 55 | return gOPN(target).concat(gOPS(target)); 56 | } : 57 | gOPN 58 | ; 59 | return { 60 | configurable: true, 61 | writable: true, 62 | value: function getOwnPropertyDescriptors(target) { 63 | return ownKeys(target).reduce( 64 | function (descriptors, name) { 65 | var descriptor = gOPD(target, name); 66 | if (name in descriptors) { 67 | return defineProperty( 68 | descriptors, 69 | name, 70 | { 71 | configurable: true, 72 | enumerable: true, 73 | writable: true, 74 | value: descriptor 75 | } 76 | ); 77 | } else { 78 | descriptors[name] = descriptor; 79 | return descriptors; 80 | } 81 | }, 82 | {} 83 | ); 84 | } 85 | }; 86 | }, 87 | function getOwnPropertySymbols() { 88 | return { 89 | configurable: true, 90 | writable: true, 91 | value: function getOwnPropertySymbols(target) { 92 | return []; 93 | } 94 | }; 95 | } 96 | ].forEach( 97 | function poly(fill) { 98 | if (fill.name in this) return; 99 | var descriptor = fill(this); 100 | if (descriptor) 101 | Object.defineProperty(this, fill.name, descriptor); 102 | }, 103 | Object 104 | ); -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/promise.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @overview es6-promise - a tiny implementation of Promises/A+. 3 | * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) 4 | * @license Licensed under MIT license 5 | * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE 6 | * @version 3.1.2 7 | */ 8 | 9 | (function(){"use strict";function t(t){return"function"==typeof t||"object"==typeof t&&null!==t}function e(t){return"function"==typeof t}function n(t){W=t}function r(t){H=t}function o(){return function(){process.nextTick(a)}}function i(){return function(){U(a)}}function s(){var t=0,e=new Q(a),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function u(){var t=new MessageChannel;return t.port1.onmessage=a,function(){t.port2.postMessage(0)}}function c(){return function(){setTimeout(a,1)}}function a(){for(var t=0;G>t;t+=2){var e=X[t],n=X[t+1];e(n),X[t]=void 0,X[t+1]=void 0}G=0}function f(){try{var t=require,e=t("vertx");return U=e.runOnLoop||e.runOnContext,i()}catch(n){return c()}}function l(t,e){var n=this,r=n._state;if(r===et&&!t||r===nt&&!e)return this;var o=new this.constructor(p),i=n._result;if(r){var s=arguments[r-1];H(function(){C(r,o,s,i)})}else j(n,o,t,e);return o}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function _(){return new TypeError("You cannot resolve a promise with itself")}function v(){return new TypeError("A promises callback cannot return that same promise.")}function d(t){try{return t.then}catch(e){return rt.error=e,rt}}function y(t,e,n,r){try{return void t.call(e,n,r)}catch(o){return o}}function m(t,e,n){H(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):E(t,n))},function(e){r||(r=!0,S(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,S(t,o))},t)}function w(t,e){e._state===et?E(t,e._result):e._state===nt?S(t,e._result):j(e,void 0,function(e){g(t,e)},function(e){S(t,e)})}function b(t,n,r){n.constructor===t.constructor&&r===Z&&constructor.resolve===$?w(t,n):r===rt?S(t,rt.error):void 0===r?E(t,n):e(r)?m(t,n,r):E(t,n)}function g(e,n){e===n?S(e,_()):t(n)?b(e,n,d(n)):E(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function E(t,e){t._state===tt&&(t._result=e,t._state=et,0!==t._subscribers.length&&H(T,t))}function S(t,e){t._state===tt&&(t._state=nt,t._result=e,H(A,t))}function j(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+et]=n,o[i+nt]=r,0===i&&t._state&&H(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,o,i=t._result,s=0;ss;s++)j(r.resolve(t[s]),void 0,e,n);return o}function Y(t){var e=this,n=new e(p);return S(n,t),n}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function D(t){this._id=ct++,this._state=void 0,this._result=void 0,this._subscribers=[],p!==t&&("function"!=typeof t&&q(),this instanceof D?M(this,t):F())}function K(t,e){this._instanceConstructor=t,this.promise=new t(p),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?E(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&E(this.promise,this._result))):S(this.promise,this._validationError())}function L(){var t;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;(!n||"[object Promise]"!==Object.prototype.toString.call(n.resolve())||n.cast)&&(t.Promise=at)}var N;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U,W,z,B=N,G=0,H=function(t,e){X[G]=t,X[G+1]=e,G+=2,2===G&&(W?W(a):z())},I="undefined"!=typeof window?window:void 0,J=I||{},Q=J.MutationObserver||J.WebKitMutationObserver,R="undefined"!=typeof process&&"[object process]"==={}.toString.call(process),V="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,X=new Array(1e3);z=R?o():Q?s():V?u():void 0===I&&"function"==typeof require?f():c();var Z=l,$=h,tt=void 0,et=1,nt=2,rt=new P,ot=new P,it=O,st=k,ut=Y,ct=0,at=D;D.all=it,D.race=st,D.resolve=$,D.reject=ut,D._setScheduler=n,D._setAsap=r,D._asap=H,D.prototype={constructor:D,then:Z,"catch":function(t){return this.then(null,t)}};var ft=K;K.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},K.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===tt&&t>n;n++)this._eachEntry(e[n],n)},K.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===$){var o=d(t);if(o===Z&&t._state!==tt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===at){var i=new n(p);b(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},K.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===tt&&(this._remaining--,t===nt?S(r,n):this._result[e]=n),0===this._remaining&&E(r,this._result)},K.prototype._willSettleAt=function(t,e){var n=this;j(t,void 0,function(t){n._settledAt(et,e,t)},function(t){n._settledAt(nt,e,t)})};var lt=L,ht={Promise:at,polyfill:lt};"function"==typeof define&&define.amd?define(function(){return ht}):"undefined"!=typeof module&&module.exports?module.exports=ht:"undefined"!=typeof this&&(this.ES6Promise=ht),lt()}).call(this); -------------------------------------------------------------------------------- /jsgtk_modules/jsgtk/util.js: -------------------------------------------------------------------------------- 1 | /*! © Andrea Giammarchi @WebReflection */ 2 | 3 | /* jshint esversion: 6, strict: true, node: true */ 4 | 5 | (function (exports, Array, Object) {'use strict'; 6 | 7 | const 8 | 9 | RESET = '\x1b[0m', 10 | RED = '\x1b[0;31m', 11 | GREEN = '\x1b[0;32m', 12 | YELLOW = '\x1b[0;33m', 13 | BLUE = '\x1b[0;34m', 14 | MAGENTA = '\x1b[0;35m', 15 | CYAN = '\x1b[0;36m', 16 | GREY = '\x1b[0;90m', 17 | BOLD = '\x1b[1m', 18 | 19 | GLib = imports.gi.GLib, 20 | 21 | create = Object.create, 22 | dP = Object.defineProperty, 23 | gOPD = Object.getOwnPropertyDescriptor, 24 | gOPNs = Object.getOwnPropertyNames, 25 | hOP = Object.prototype.hasOwnProperty, 26 | trim = String.prototype.trim 27 | ; 28 | 29 | function colored(what, color, how) { 30 | return how.colors ? (color + what + RESET) : what; 31 | } 32 | 33 | function inherits(Constructor, Super) { 34 | Constructor.super_ = Super; 35 | Constructor.prototype = create(Super.prototype, { 36 | constructor: { 37 | configurable: true, 38 | enumerable: false, 39 | writable: true, 40 | value: Constructor 41 | } 42 | }); 43 | } 44 | 45 | function inspectArray(obj, tab, wm, how, d) { 46 | let 47 | out = ['['], 48 | t = Array(tab + 1).join(' ') 49 | ; 50 | obj.forEach((value, i) => { 51 | out.push('\n', t, $inspect(value, tab, wm, how, d), ','); 52 | }); 53 | if (out.length > 1) { 54 | out.pop(); 55 | out.push('\n', Array(tab).join(' '), ']'); 56 | } else { 57 | out.push(']'); 58 | } 59 | return out.join(''); 60 | } 61 | 62 | function inspectBuffer(obj, tab, wm, how, d) { 63 | let out = [''; 67 | } 68 | 69 | function inspectObject(obj, tab, wm, how, d) { 70 | let 71 | out = ['{'], 72 | t = Array(tab + 1).join(' '), 73 | simple = /^[a-zA-Z$_]+[a-zA-Z0-9$_]*$/ 74 | ; 75 | (how.showHidden ? 76 | Object.getOwnPropertyNames : 77 | Object.keys 78 | )(obj).forEach((key, i) => { 79 | out.push('\n', t, 80 | simple.test(key) ? 81 | key : colored(JSON.stringify(key), GREEN, how), 82 | ': ', 83 | $inspect(obj[key], tab, wm, how, d), 84 | ','); 85 | }); 86 | if (out.length > 1) { 87 | out.pop(); 88 | out.push('\n', Array(tab).join(' '), '}'); 89 | } else { 90 | out.push('}'); 91 | } 92 | return out.join(''); 93 | } 94 | 95 | function $inspect(obj, tab, wm, how, d) { 96 | switch (typeof obj) { 97 | case 'boolean': 98 | case 'number': 99 | return colored(String(obj), YELLOW, how); 100 | case 'function': 101 | return -1 < wm.indexOf(obj) ? 102 | colored('[Circular]', CYAN, how) : 103 | (wm.push(obj), colored( 104 | '[Function' + ( 105 | obj.name ? (': ' + obj.name) : '' 106 | ) + ']', CYAN, how 107 | )); 108 | case 'object': 109 | return obj ? ( 110 | -1 < wm.indexOf(obj) ? 111 | colored('[Circular]', CYAN, how) : 112 | (wm.push(obj), 113 | (d <= how.depth ? 114 | (Array.isArray(obj) ? inspectArray : 115 | (obj instanceof Uint8Array ? inspectBuffer : inspectObject)) 116 | (obj, tab + 1, wm, how, d + 1) : 117 | (Array.isArray(obj) ? 118 | colored('[Array]', CYAN, how) : 119 | colored('[Object]', CYAN, how)) 120 | ) 121 | ) 122 | ) : 'null'; 123 | case 'string': 124 | return colored(JSON.stringify(obj), GREEN, how); 125 | case 'symbol': 126 | return colored(String(obj), YELLOW, how); 127 | case 'undefined': 128 | return colored(String(obj), GREY, how); 129 | } 130 | return 'unknown'; 131 | } 132 | 133 | // minimalistic utility to create classes 134 | // Class(Parent, {proto}) 135 | // Class({proto}) 136 | exports.Class = function Class(parent, proto) { 137 | let length = arguments.length; 138 | if (length === 1) proto = parent; 139 | if (!hOP.call(proto, 'constructor')) 140 | proto.constructor = function Class() {}; 141 | if (length > 1) inherits(proto.constructor, parent); 142 | return gOPNs(proto).reduce( 143 | (p, key) => { 144 | let d = gOPD(proto, key); 145 | d.enumerable = false; 146 | return dP(p, key, d); 147 | }, 148 | proto.constructor.prototype 149 | ).constructor; 150 | }; 151 | 152 | // same function used in util 153 | exports.inherits = inherits; 154 | 155 | // same function used in util 156 | exports.inspect = function inspect(what, how) { 157 | return $inspect( 158 | what, 159 | 0, 160 | [], 161 | how || { 162 | colors: false, 163 | depth: Infinity, 164 | showHidden: false 165 | }, 166 | 0 167 | ); 168 | }; 169 | 170 | // utility to slice.apply(0, arguments) 171 | // in a way that should be arguments leaks free 172 | exports.slice = function slice() { 173 | /* jshint validthis: true */ 174 | for (var 175 | o = +this, 176 | i = o, 177 | l = arguments.length, 178 | n = l - o, 179 | a = Array(n < 0 ? 0 : n); 180 | i < l; i++ 181 | ) a[i - o] = arguments[i]; 182 | return a; 183 | }; 184 | 185 | exports.system = function system(command) { 186 | return trim.call(GLib.spawn_command_line_sync(command)[1]); 187 | }; 188 | 189 | }(this, Array, Object)); 190 | -------------------------------------------------------------------------------- /jsgtk_modules/module.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/timers.html 5 | * JSGtk Status incomplete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports */ 10 | 11 | module.exports = { 12 | globalPaths: [] 13 | }; 14 | -------------------------------------------------------------------------------- /jsgtk_modules/os.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/os.html 5 | * JSGtk Status incomplete 6 | */ 7 | 8 | // TODO: this module is mostly based on synchronous CLI calls 9 | // not only it's not portable as it is but there is 10 | // surely a better way of doing this. 11 | 12 | /* jshint esversion: 6, strict: implied, node: true */ 13 | /* global imports */ 14 | 15 | const 16 | 17 | GLib = imports.gi.GLib, 18 | 19 | EOL = process.platform === 'win32' ? '\r\n' : '\n', 20 | NOMAC = '00:00:00:00:00:00', 21 | 22 | system = process.binding('util').system, 23 | 24 | trim = String.prototype.trim, 25 | 26 | getIPv4Subnet = createSubnet(32, 8, 10, '.'), 27 | getIPv6Subnet = createSubnet(128, 16, 16, ':'), 28 | 29 | // different per platform 30 | multiOp = { 31 | darwin: { 32 | cpus: function getCPUs() { 33 | let 34 | cores = parseFloat(system('sysctl -n hw.ncpu')), 35 | frequency = parseFloat(system('sysctl -n hw.cpufrequency')) / 1000 / 1000, 36 | brandString = system('sysctl -n machdep.cpu.brand_string').replace(/\s+/g, ' '), 37 | cpus = Array(cores) 38 | ; 39 | while (cores--) { 40 | /* jshint loopfunc:true */ 41 | cpus[cores] = { 42 | model: brandString, 43 | speed: frequency, 44 | get times() { 45 | console.warn('cpus.times is not supported'); 46 | return {}; 47 | } 48 | }; 49 | } 50 | return cpus; 51 | }, 52 | freemem: function getFreeMem() { 53 | return parseFloat(system('sysctl -n hw.memsize')) - 54 | parseFloat(system('sysctl -n hw.physmem')); 55 | }, 56 | loadavg: function getLoadAvg() { 57 | return /load\s+averages:\s+(\d+(?:\.\d+))\s+(\d+(?:\.\d+))\s+(\d+(?:\.\d+))/.test( 58 | system('uptime') 59 | ) && [ 60 | parseFloat(RegExp.$1), 61 | parseFloat(RegExp.$2), 62 | parseFloat(RegExp.$3) 63 | ]; 64 | }, 65 | networkInterfaces: function getInterfaceAddresses() { 66 | const 67 | ifaces = {}, 68 | groups = [], 69 | lines = system('ifconfig').split('\n') 70 | ; 71 | for (let 72 | group = [], 73 | re = /^\S+?:/, 74 | i = 0, 75 | length = lines.length; 76 | i < length; i++ 77 | ) { 78 | if (re.test(lines[i])) { 79 | group = [lines[i]]; 80 | while (++i < length && !re.test(lines[i])) { 81 | group.push(lines[i]); 82 | } 83 | --i; 84 | } 85 | groups.push(group.join('\n')); 86 | } 87 | /* jshint ignore: start */ 88 | groups.forEach(op.parseInterfaces, ifaces); 89 | /* jshint ignore: end */ 90 | return ifaces; 91 | }, 92 | parseInterfaces: function parseInterfaces(info, i) { 93 | info = info.trim(); 94 | if (info.length < 1 || !/\binet\b/.test(info)) return; 95 | let iface = [], mac = NOMAC; 96 | for (let 97 | line, 98 | lines = info.split('\n'), 99 | i = 0; i < lines.length; i++ 100 | ) { 101 | line = lines[i]; 102 | switch (true) { 103 | case /ether\s+((?:\S{2}:)+\S{2})/.test(line): 104 | mac = RegExp.$1; 105 | break; 106 | case /inet\s+(\d+\.\d+\.\d+\.\d+)\s+netmask\s+0x(.{2})(.{2})(.{2})(.{2})/.test(line): 107 | iface.push({ 108 | address: RegExp.$1, 109 | netmask: [ 110 | parseInt(RegExp.$2, 16), 111 | parseInt(RegExp.$3, 16), 112 | parseInt(RegExp.$4, 16), 113 | parseInt(RegExp.$5, 16) 114 | ].join('.'), 115 | family: 'IPv4', 116 | mac: mac, 117 | internal: RegExp.$1 === '127.0.0.1' 118 | }); 119 | break; 120 | case /inet6\s+((?:\S{0,4}:)+\S{1,4}).+?prefixlen\s+(\d+)/.test(line): 121 | iface.push({ 122 | address: RegExp.$1, 123 | netmask: getIPv6Subnet(RegExp.$2), 124 | family: 'IPv6', 125 | mac: mac, 126 | internal: mac !== NOMAC 127 | }); 128 | break; 129 | } 130 | } 131 | this[info.slice(0, info.indexOf(':'))] = iface; 132 | }, 133 | uptime: function getUptime() { 134 | let 135 | uptime = system('uptime'), 136 | up = /up\s+([^,]+)?,/.test(uptime) && RegExp.$1 137 | ; 138 | switch (true) { 139 | case /^(\d+):(\d+)$/.test(up): 140 | return ((parseInt(RegExp.$1, 10) * 60) + parseInt(RegExp.$2, 10)) * 60; 141 | case /^(\d+)\s+mins?$/.test(up): 142 | return parseInt(RegExp.$1, 10) * 60; 143 | case /^(\d+)\s+days?$/.test(up): 144 | return (parseInt(RegExp.$1, 10) * 86400) + ( 145 | /days?,\s+^(\d+):(\d+)$/.test(uptime) && ( 146 | ((parseInt(RegExp.$1, 10) * 60) + 147 | parseInt(RegExp.$2, 10)) * 60 148 | ) 149 | ); 150 | } 151 | return up; 152 | } 153 | }, 154 | linux: { 155 | cpus: function getCPUs() { 156 | let 157 | PROCESSOR = /^processor\s*:\s*(\d+)/i, 158 | NAME = /^model[\s_]+name\s*:([^\r\n]+)/i, 159 | FREQ = /^cpu[\s_]+MHz\s*:\s*(\d+)/i, 160 | cpus = [], 161 | cpu 162 | ; 163 | system('cat /proc/cpuinfo').split(/\r\n|\n|\r/).forEach((line) => { 164 | switch (true) { 165 | case PROCESSOR.test(line): 166 | cpus[trim.call(RegExp.$1)] = (cpu = { 167 | model: '', 168 | speed: 0, 169 | get times() { 170 | console.warn('cpus.times is not supported'); 171 | return {}; 172 | } 173 | }); 174 | break; 175 | case NAME.test(line): 176 | cpu.model = trim.call(RegExp.$1); 177 | break; 178 | case FREQ.test(line): 179 | cpu.speed = parseFloat(trim.call(RegExp.$1)); 180 | break; 181 | } 182 | }); 183 | return cpus; 184 | }, 185 | freemem: function getFreeMem() { 186 | let I, mem = system('free -b').split(EOL); 187 | mem[0].split(/\s+/).some((info, i) => info === 'free' && (I = i)); 188 | return parseFloat(mem[1].split(/\s+/)[I + 1]); 189 | }, 190 | loadavg: function getLoadAvg() { 191 | return /(\d+(?:\.\d+))\s+(\d+(?:\.\d+))\s+(\d+(?:\.\d+))/.test( 192 | system('cat /proc/loadavg') 193 | ) && [ 194 | parseFloat(RegExp.$1), 195 | parseFloat(RegExp.$2), 196 | parseFloat(RegExp.$3) 197 | ]; 198 | }, 199 | networkInterfaces: function getInterfaceAddresses() { 200 | const ifaces = {}; 201 | /* jshint ignore: start */ 202 | system('ip addr').split(/^\d+:\s+/m).forEach(op.parseInterfaces, ifaces); 203 | /* jshint ignore: end */ 204 | return ifaces; 205 | }, 206 | parseInterfaces: function parseInterfaces(info, i) { 207 | info = info.trim(); 208 | if (info.length < 1) return; 209 | let iface = [], mac; 210 | for (let 211 | line, 212 | lines = info.split('\n'), 213 | i = 0; i < lines.length; i++ 214 | ) { 215 | line = lines[i]; 216 | switch (true) { 217 | case /link\/\S+\s+((?:\S{2}:)+\S{2})/.test(line): 218 | mac = RegExp.$1; 219 | break; 220 | case /inet(\d*)\s+(\S+)/.test(line): 221 | let 222 | ip = RegExp.$2.split('/'), 223 | v = RegExp.$1 || '4' 224 | ; 225 | iface.push({ 226 | address: ip[0], 227 | netmask: (v === '4' ? getIPv4Subnet : getIPv6Subnet)(ip[1]), 228 | family: 'IPv' + v, 229 | mac: mac, 230 | internal: ip[0] === '127.0.0.1' 231 | }); 232 | break; 233 | } 234 | } 235 | if (mac) this[info.slice(0, info.indexOf(':'))] = iface; 236 | }, 237 | totalmem: function getTotalMem() { 238 | let I, mem = system('free -b').split(EOL); 239 | mem[0].split(/\s+/).some((info, i) => info === 'total' && (I = i)); 240 | return parseFloat(mem[1].split(/\s+/)[I + 1]); 241 | }, 242 | uptime: function getUptime() { 243 | return ( 244 | Date.now() - 245 | Date.parse(system('uptime -s').replace(' ', 'T')) 246 | ) / 1000; 247 | } 248 | } 249 | }, 250 | // current platform helpers 251 | op = multiOp[process.platform] 252 | ; 253 | 254 | function createSubnet(size, segment, base, sep) { 255 | const 256 | empty = Array(size + 1).join('0'), 257 | length = size / segment 258 | ; 259 | return function getSubnet(mask) { 260 | const 261 | str = (Array(parseInt(mask, 10) + 1).join('1') + empty).slice(0, size), 262 | out = Array(length) 263 | ; 264 | for (let i = 0, j = 0; i < size; i += segment) { 265 | out[j++] = parseInt(str.substr(i, segment), 2).toString(base); 266 | } 267 | return out.join(sep); 268 | }; 269 | } 270 | 271 | module.exports = { 272 | arch: function arch() { 273 | switch (system('uname -m')) { 274 | case 'x86_64': return 'x64'; 275 | case 'i686': return 'ia32'; 276 | default: return 'arm'; 277 | } 278 | }, 279 | cpus: op.cpus, 280 | endianness: function endianness() { 281 | // absolutely random untrustable check 282 | // just assume LE is OK, the rest who knows 283 | switch (process.platform) { 284 | case 'sunos': return 'BE'; 285 | default: return 'LE'; 286 | } 287 | }, 288 | freemem: op.freemem, 289 | getInterfaceAddresses: op.networkInterfaces, 290 | homedir: function getHomeDirectory() { 291 | return GLib.get_home_dir(); 292 | }, 293 | hostname: function getHostname() { 294 | return GLib.get_host_name(); 295 | }, 296 | loadavg: op.loadavg, 297 | networkInterfaces: op.networkInterfaces, 298 | platform: function platform() { 299 | return process.platform; 300 | }, 301 | release: function getOSRelease() { 302 | return system('uname -r'); 303 | }, 304 | tmpdir: function tmpdir() { 305 | return GLib.get_tmp_dir(); 306 | }, 307 | tmpDir: function tmpDir() { 308 | return GLib.get_tmp_dir(); 309 | }, 310 | totalmem: op.totalmem, 311 | type: function getOSType() { 312 | return system('uname'); 313 | }, 314 | uptime: op.uptime, 315 | EOL: EOL 316 | }; 317 | -------------------------------------------------------------------------------- /jsgtk_modules/path.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/path.html 5 | * JSGtk Status complete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports */ 10 | 11 | const 12 | 13 | gi = imports.gi, 14 | GLib = gi.GLib, 15 | GFile = gi.Gio.File, 16 | 17 | slice = process.binding('util').slice 18 | 19 | ; 20 | 21 | function createPathObject(DIR_SEPARATOR) { 22 | return { 23 | basename: function basename(path, suffix) { 24 | let len, file = GLib.path_get_basename(path); 25 | if (arguments.length === 2) { 26 | len = suffix.length; 27 | if (file.slice(-len) === suffix) { 28 | file = file.slice(0, -len); 29 | } 30 | } 31 | return file; 32 | }, 33 | delimiter: DIR_SEPARATOR === '/' ? ':' : ';', 34 | dirname: dirname, 35 | extname: extname, 36 | format: function format(pathObject) { 37 | return [pathObject.dir, pathObject.base].join(DIR_SEPARATOR); 38 | }, 39 | isAbsolute: isAbsolute, 40 | join: function join() { 41 | return GLib.build_pathv(DIR_SEPARATOR, slice.apply(0, arguments)); 42 | }, 43 | normalize: function normalize(path) { 44 | return GFile.new_for_path(path).get_path(); 45 | }, 46 | parse: function parse(pathString) { 47 | let 48 | dir = dirname(pathString), 49 | base = GLib.path_get_basename(pathString), 50 | ext = extname(base), 51 | name = base.slice(0, -ext.length) || base 52 | ; 53 | return { 54 | root: isAbsolute(pathString) ? 55 | pathString.slice( 56 | 0, pathString.indexOf(DIR_SEPARATOR) + DIR_SEPARATOR.length 57 | ) : '', 58 | dir: dir, 59 | base: base, 60 | ext: ext, 61 | name: name 62 | }; 63 | }, 64 | get posix() { 65 | return createPathObject('/'); 66 | }, 67 | relative: function relative(from, to) { 68 | let 69 | sfrom = GFile.new_for_path(from).get_path().split(DIR_SEPARATOR), 70 | sto = GFile.new_for_path(to).get_path().split(DIR_SEPARATOR), 71 | length = sfrom.length, 72 | i = 0, 73 | out = [] 74 | ; 75 | while (i < length && sfrom[i] === sto[i]) i++; 76 | sto = sto.slice(i); 77 | while (i++ < length) out.push('..'); 78 | return out.concat(sto).join(DIR_SEPARATOR); 79 | }, 80 | resolve: function resolve() { 81 | let args = [process.cwd()].concat(slice.apply(0, arguments)); 82 | let to = args.pop(); 83 | if (args.length) { 84 | let from = GFile.new_for_path(args.shift()); 85 | while (args.length) { 86 | from = from.resolve_relative_path(args.shift()); 87 | } 88 | return from.resolve_relative_path(to).get_path(); 89 | } 90 | return to; 91 | }, 92 | sep: DIR_SEPARATOR, 93 | get win32() { 94 | return createPathObject('\\'); 95 | } 96 | }; 97 | } 98 | 99 | function dirname(path) { 100 | return GLib.path_get_dirname(path); 101 | } 102 | 103 | function extname(path) { 104 | return /\S(\.[A-Za-z0-9]+)$/.test(path) ? RegExp.$1 : ''; 105 | } 106 | 107 | function isAbsolute(path) { 108 | return GLib.path_is_absolute(path); 109 | } 110 | 111 | module.exports = createPathObject( 112 | /\//.test(process.cwd()) ? '/' : '\\' 113 | ); 114 | -------------------------------------------------------------------------------- /jsgtk_modules/process.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/process.html 5 | * JSGtk Status incomplete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports, ARGV */ 10 | 11 | let mainloop; 12 | 13 | const 14 | 15 | gi = imports.gi, 16 | GLib = gi.GLib, 17 | GFile = gi.Gio.File, 18 | System = imports.system, 19 | 20 | BEGINNING = Date.now(), 21 | CURRENT_DIR = GLib.get_current_dir(), 22 | DIR_SEPARATOR = /\//.test(CURRENT_DIR) ? '/' : '\\', 23 | 24 | EventEmitter = ARGV.core.get('events').EventEmitter, 25 | 26 | process = Object.assign( 27 | new EventEmitter(), 28 | { 29 | argv: (() => { 30 | for (var 31 | evalArg = /^-e|--eval$/, 32 | argv = [GFile.new_for_path(System.programInvocationName).get_path()], 33 | i = 0; i < ARGV.length; i++ 34 | ) { 35 | if (ARGV[i][0] !== '-') { 36 | if (!evalArg.test(ARGV[i - 1])) { 37 | argv = argv.concat( 38 | GFile.new_for_path( 39 | GLib.path_is_absolute(ARGV[i]) ? 40 | ARGV[i] : ('.' + DIR_SEPARATOR + ARGV[i]) 41 | ).get_path(), 42 | ARGV.slice(i + 1) 43 | ); 44 | } 45 | break; 46 | } 47 | } 48 | return argv; 49 | })(), 50 | binding: (which) => imports.jsgtk[which], 51 | cwd: () => CURRENT_DIR, 52 | env: ((arr) => { 53 | const env = {}; 54 | for (let i = 0, p, info; i < arr.length; i++) { 55 | info = arr[i]; 56 | p = info.indexOf('='); 57 | env[info.slice(0, p)] = info.slice(p + 1); 58 | } 59 | return env; 60 | })(GLib.get_environ()), 61 | abort: status => { 62 | process.emit('abort'); 63 | System.exit(1); 64 | }, 65 | exit: status => { 66 | process.emit('exit', status); 67 | System.exit(status || 0); 68 | }, 69 | platform: ((platform) => { 70 | switch (true) { 71 | // TODO /Win|Mingw|WOW/i ??? 72 | case /\b(?:Win|WOW)\b/i.test(platform): 73 | return 'win32'; 74 | default: 75 | return platform.toLowerCase(); 76 | } 77 | })(''.trim.call(GLib.spawn_command_line_sync('uname')[1])), 78 | uptime: () => (Date.now() - BEGINNING) / 1000, 79 | nextTick: function nextTick() { 80 | (mainloop || ( 81 | mainloop = process.binding('mainloop') 82 | )).idle.apply(mainloop, arguments); 83 | } 84 | } 85 | ) 86 | ; 87 | 88 | Object.defineProperties( 89 | process, 90 | { 91 | arch: { 92 | enumerable: true, 93 | get: () => require('os').arch() 94 | } 95 | } 96 | ); 97 | 98 | Object.defineProperty( 99 | global, 100 | 'process', 101 | {enumerable: true, value: process} 102 | ); 103 | 104 | module.exports = process; 105 | -------------------------------------------------------------------------------- /jsgtk_modules/querystring.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/querystring.html 5 | * JSGtk Status complete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true, eqnull: true */ 9 | /* global imports */ 10 | 11 | const 12 | 13 | // gjs helpers and dependencies 14 | GLib = imports.gi.GLib, 15 | 16 | // module shortcuts 17 | defineProperty = Object.defineProperty, 18 | hOP = Object.prototype.hasOwnProperty, 19 | isArray = Array.isArray, 20 | 21 | // module.exports 22 | querystring = { 23 | ALLOW_UTF8: false, 24 | escape: function escape(str) { 25 | return GLib.uri_escape_string(str, null, querystring.ALLOW_UTF8); 26 | }, 27 | parse: function parse(str, sep, eq, options) { 28 | if (typeof str !== 'string' || str.length < 1) return {}; 29 | if (sep == null) sep = '&'; 30 | if (eq == null) eq = '='; 31 | if (options == null) options = {}; 32 | if (!hOP.call(options, 'decodeURIComponent')) 33 | options.decodeURIComponent = querystring.unescape; 34 | if (!hOP.call(options, 'maxKeys')) 35 | options.maxKeys = 1000; 36 | return parseString(str, sep, eq, options); 37 | }, 38 | stringify: function stringify(obj, sep, eq, options) { 39 | if (typeof obj !== 'object') return ''; 40 | if (sep == null) sep = '&'; 41 | if (eq == null) eq = '='; 42 | if (options == null) options = {}; 43 | if (!hOP.call(options, 'encodeURIComponent')) 44 | options.encodeURIComponent = querystring.escape; 45 | if (!hOP.call(options, 'maxKeys')) 46 | options.maxKeys = 1000; 47 | return parseObject(obj, sep, eq, options); 48 | }, 49 | unescape: function unescape(str) { 50 | return GLib.uri_unescape_string(str, null); 51 | } 52 | } 53 | ; 54 | 55 | function append(out, key, value, options) { 56 | key = options.decodeURIComponent(key); 57 | value = value.length ? options.decodeURIComponent(value) : value; 58 | if (key in out) { 59 | if (hOP.call(out, key)) 60 | out[key] = [].concat(out[key], value); 61 | else defineProperty(out, key, { 62 | configurable: true, 63 | enumerable: true, 64 | writable: true, 65 | value: value 66 | }); 67 | } else 68 | out[key] = value; 69 | } 70 | 71 | function isValid(value) { 72 | switch (typeof value) { 73 | case 'boolean': 74 | case 'number': 75 | case 'string': 76 | return true; 77 | } 78 | return false; 79 | } 80 | 81 | function parseObject(obj, sep, eq, options) { 82 | const out = []; 83 | for (let key in obj) { 84 | if (hOP.call(obj, key)) { 85 | let value = obj[key]; 86 | if (isValid(value)) { 87 | out.push( 88 | options.encodeURIComponent(key) + 89 | eq + 90 | options.encodeURIComponent(String(value)) 91 | ); 92 | } else if (isArray(value)) { 93 | let 94 | ekey = options.encodeURIComponent(key), 95 | i = 0, 96 | length = value.length 97 | ; 98 | while (i < length) { 99 | let evalue = value[i++]; 100 | out.push( 101 | ekey + 102 | eq + 103 | (isValid(evalue) ? 104 | options.encodeURIComponent(String(evalue)) : '') 105 | ); 106 | } 107 | } 108 | } 109 | } 110 | return out.join(sep); 111 | } 112 | 113 | function parseString(str, sep, eq, options) { 114 | const out = {}; 115 | for (var 116 | index, pair, 117 | pairs = str.split(sep), 118 | leq = eq.length, 119 | i = 0, 120 | length = pairs.length; i < length; i++ 121 | ) { 122 | pair = pairs[i]; 123 | index = pair.indexOf(eq); 124 | if (-1 < index) { 125 | append(out, pair.slice(0, index), pair.slice(index + leq), options); 126 | } else { 127 | append(out, pair, '', options); 128 | } 129 | } 130 | return out; 131 | } 132 | 133 | module.exports = querystring; 134 | -------------------------------------------------------------------------------- /jsgtk_modules/stream.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/stream.html 5 | * JSGtk Status incomplete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports, print */ 10 | 11 | const 12 | 13 | GLib = imports.gi.GLib, 14 | ByteArray = imports.byteArray, 15 | 16 | DEBUG = process.binding('constants').DEBUG, 17 | 18 | Class = process.binding('util').Class, 19 | 20 | EventEmitter = require('events').EventEmitter, 21 | 22 | createReadableWatcher = (stream) => 23 | GLib.io_add_watch( 24 | stream._channel, 25 | GLib.PRIORITY_DEFAULT, 26 | GLib.IOCondition.IN, 27 | (source, condition, data) => { 28 | stream._source = source; 29 | if (stream._bootstrap) { 30 | stream._bootstrap = false; 31 | if (stream.listenerCount('readable')) { 32 | stream.emit('readable'); 33 | } else { 34 | while (stream.read()); 35 | } 36 | } else { 37 | while (stream.read()); 38 | } 39 | } 40 | ), 41 | 42 | createWritableWatcher = (stream) => 43 | GLib.io_add_watch( 44 | stream._channel, 45 | GLib.PRIORITY_DEFAULT, 46 | GLib.IOCondition.OUT, 47 | (source, condition, data) => { 48 | stream._source = source; 49 | if (!stream._writable) { 50 | let buf = stream._writeBuffer; 51 | stream._writeBuffer = ''; 52 | stream._writable = true; 53 | stream.write(buf); 54 | } 55 | } 56 | ), 57 | 58 | createHangUpWatcher = (stream) => 59 | GLib.io_add_watch( 60 | stream._channel, 61 | GLib.PRIORITY_DEFAULT, 62 | GLib.IOCondition.HUP, 63 | (source, condition, data) => { 64 | if (stream._channel) stream.emit('disconnect'); 65 | } 66 | ), 67 | 68 | InernalStream = Class(EventEmitter, { 69 | constructor: function InernalStream() { 70 | EventEmitter.call(this); 71 | this.on('disconnect', () => { 72 | let 73 | channel = this._channel, 74 | source = this._source 75 | ; 76 | if (channel) { 77 | this._channel = null; 78 | if (source) { 79 | source.unref(); 80 | if (this instanceof Writable) { 81 | source.shutdown(true); 82 | this.emit('finish'); 83 | } else { 84 | this.emit('close', 0, null); 85 | } 86 | } else { 87 | channel.unref(); 88 | if (this instanceof Writable) { 89 | channel.shutdown(true); 90 | this.emit('finish'); 91 | } else { 92 | this.emit('close', 0, null); 93 | } 94 | } 95 | } 96 | }); 97 | } 98 | }), 99 | 100 | Readable = Class(InernalStream, { 101 | constructor: function Readable(channel) { 102 | InernalStream.call(this); 103 | this._bootstrap = true; 104 | this._channel = channel; 105 | this._watcher = createReadableWatcher(this); 106 | this._huWatcher = createHangUpWatcher(this); 107 | }, 108 | read: function read(size) { 109 | if (size) { 110 | // TODO: this is probably not going to work ... 111 | let buf = new ByteArray(size); 112 | this._source.read_line(buf, size, 0); 113 | return buf; 114 | } else { 115 | let [status, result] = this._source.read_line(); 116 | // 117 | // apparently it's not possible to compare === 118 | // against a GTK status 119 | switch (true) { 120 | case status == GLib.IOStatus.NORMAL: 121 | if (DEBUG) print('Readable: NORMAL ' + result); 122 | this.emit('data', result); 123 | break; 124 | case status == GLib.IOStatus.EOF: 125 | if (DEBUG) print('Readable: EOF ' + result); 126 | this.emit('end'); 127 | break; 128 | case status == GLib.IOStatus.ERROR: 129 | if (DEBUG) print('Readable: ERROR ' + result); 130 | this.emit('error'); 131 | break; 132 | case status == GLib.IOStatus.AGAIN: 133 | if (DEBUG) print('Readable: AGAIN ' + result); 134 | this._watcher = createReadableWatcher(this); 135 | break; 136 | } 137 | return result; 138 | } 139 | } 140 | }), 141 | 142 | Writable = Class(InernalStream, { 143 | constructor: function Writable(channel) { 144 | InernalStream.call(this); 145 | this._writable = false; 146 | this._writeBuffer = ''; 147 | this._channel = channel; 148 | try { 149 | this._watcher = createWritableWatcher(this); 150 | } catch (e) { 151 | this._channel = GLib.IOChannel.unix_new(1); 152 | this._watcher = createWritableWatcher(this); 153 | } 154 | this._huWatcher = createHangUpWatcher(this); 155 | }, 156 | end: function end(chunk, encoding, callback) { 157 | // TODO: support all arguments? 158 | this._source.close(); 159 | this.emit('finish'); 160 | }, 161 | write: function write(chunk, encoding, callback) { 162 | // TODO: support encoding, callback ? 163 | if (this._writable) { 164 | let [status, written] = this._source.write_chars(chunk, -1); 165 | if (status == GLib.IOStatus.NORMAL && status == this._source.flush()) { 166 | if (DEBUG) print('Writable: FLUSHED ' + chunk); 167 | return true; 168 | } else { 169 | this.emit('error', status); 170 | return false; 171 | } 172 | } else { 173 | this._writeBuffer += chunk; 174 | return true; 175 | } 176 | } 177 | }), 178 | 179 | Stream = Class(InernalStream, { 180 | constructor: function Stream(std) { 181 | InernalStream.call(this); 182 | let 183 | channel = GLib.IOChannel.unix_new(std), 184 | flags = channel.get_flags(), 185 | constructor 186 | ; 187 | channel.set_flags(GLib.IOFlags.NONBLOCK); 188 | switch (true) { 189 | case flags == GLib.IOFlags.IS_WRITABLE: 190 | constructor = Writable; 191 | break; 192 | case flags == GLib.IOFlags.IS_READABLE: 193 | constructor = Readable; 194 | break; 195 | } 196 | return new constructor(channel); 197 | } 198 | }) 199 | ; 200 | 201 | module.exports = { 202 | Readable: Readable, 203 | Writable: Writable, 204 | Stream: Stream 205 | }; 206 | -------------------------------------------------------------------------------- /jsgtk_modules/timers.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/timers.html 5 | * JSGtk Status incomplete 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports */ 10 | 11 | const 12 | 13 | Mainloop = imports.mainloop, 14 | jsgtkLoop = process.binding('mainloop'), 15 | 16 | ids = new Set(), 17 | clearID = (id) => { 18 | if (ids.has(id)) { 19 | ids.delete(id); 20 | jsgtkLoop.go(); 21 | } 22 | }, 23 | 24 | createClearTimer = () => (id) => Mainloop.source_remove(id), 25 | 26 | createSetTimer = (repeat) => 27 | (fn, ms, ...args) => { 28 | const id = Mainloop.timeout_add( 29 | (ms * 1) || 0, 30 | () => { 31 | if (!repeat) clearID(id); 32 | fn.apply(null, args); 33 | return repeat; 34 | } 35 | ); 36 | ids.add(id); 37 | jsgtkLoop.wait(); 38 | return id; 39 | }, 40 | 41 | timers = { 42 | clearInterval: createClearTimer(), 43 | clearTimeout: createClearTimer(), 44 | setInterval: createSetTimer(true), 45 | setTimeout: createSetTimer(false) 46 | } 47 | ; 48 | 49 | Object.defineProperties( 50 | global, 51 | { 52 | clearInterval: {enumerable: true, value: timers.clearInterval}, 53 | clearTimeout: {enumerable: true, value: timers.clearTimeout}, 54 | setInterval: {enumerable: true, value: timers.setInterval}, 55 | setTimeout: {enumerable: true, value: timers.setTimeout} 56 | } 57 | ); 58 | 59 | module.exports = timers; 60 | -------------------------------------------------------------------------------- /jsgtk_modules/tty.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | isatty: function () { 3 | return true; 4 | } 5 | }; -------------------------------------------------------------------------------- /jsgtk_modules/util.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Original API © 2016 Node.js Foundation 3 | * This implementation © Andrea Giammarchi @WebReflection 4 | * Documentation https://nodejs.org/api/util.html 5 | * JSGtk Status complete (without deprecated) 6 | */ 7 | 8 | /* jshint esversion: 6, strict: implied, node: true */ 9 | /* global imports */ 10 | 11 | const 12 | 13 | GFormat = imports.format, 14 | 15 | util = process.binding('util'), 16 | 17 | create = Object.create, 18 | hOP = Object.prototype.hasOwnProperty, 19 | 20 | ten = (i) => ('0' + i).slice(-2) 21 | ; 22 | 23 | module.exports = { 24 | deprecate: function deprecate(fn, message) { 25 | var shouldWarn = false; 26 | return function() { 27 | if (shouldWarn) { 28 | shouldWarn = !shouldWarn; 29 | console.warn(message); 30 | } 31 | return fn.apply(this, arguments); 32 | }; 33 | }, 34 | format: function format() { 35 | return GFormat.vprintf(arguments[0], util.slice.apply(1, arguments)); 36 | }, 37 | inherits: util.inherits, 38 | inspect: function inspect(what, how) { 39 | if (!how) how = {}; 40 | return util.inspect(what, { 41 | colors: hOP.call(how, 'colors') ? how.colors : false, 42 | depth: hOP.call(how, 'depth') ? 43 | (how.depth === null ? Infinity : how.depth) : 2, 44 | showHidden: hOP.call(how, 'showHidden') ? !!how.showHidden : false 45 | }); 46 | }, 47 | log: function log(message) { 48 | var d = new Date(); 49 | console.log([ 50 | d.getDate(), 51 | ( / (\S+) /.test('' + d) && RegExp.$1), 52 | [ 53 | ten(d.getHours()), 54 | ten(d.getMinutes()), 55 | ten(d.getSeconds()) 56 | ].join(':'), 57 | '-', 58 | message 59 | ].join(' ')); 60 | }, 61 | isDate: function isDate(date) { 62 | return date instanceof Date; 63 | }, 64 | isRegExp: function isRegExp(re) { 65 | return re instanceof RegExp; 66 | }, 67 | isPrimitive: function isPrimitive(value) { 68 | switch (typeof value) { 69 | case 'function': 70 | case 'object': 71 | return false; 72 | default: 73 | return true; 74 | } 75 | }, 76 | _extend: Object.assign 77 | }; 78 | -------------------------------------------------------------------------------- /jsgtk_modules/zlib.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | createUnzip: function () { 3 | return true; 4 | } 5 | }; -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsgtk", 3 | "version": "0.12.2", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "babel-standalone": { 8 | "version": "git+ssh://git@github.com/WebReflection/babel-standalone.git#917727b376fac08b385e98271e39eecf6d911805", 9 | "integrity": "sha1-pzSbIrgSTeUy2gI8c4mIEAyfjxs=", 10 | "dev": true 11 | }, 12 | "balanced-match": { 13 | "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", 14 | "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", 15 | "dev": true 16 | }, 17 | "brace-expansion": { 18 | "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", 19 | "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=", 20 | "dev": true, 21 | "requires": { 22 | "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", 23 | "concat-map": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" 24 | } 25 | }, 26 | "cli": { 27 | "version": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", 28 | "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", 29 | "dev": true, 30 | "requires": { 31 | "exit": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", 32 | "glob": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz" 33 | } 34 | }, 35 | "concat-map": { 36 | "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 37 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 38 | "dev": true 39 | }, 40 | "console-browserify": { 41 | "version": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", 42 | "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", 43 | "dev": true, 44 | "requires": { 45 | "date-now": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" 46 | } 47 | }, 48 | "core-util-is": { 49 | "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 50 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 51 | "dev": true 52 | }, 53 | "date-now": { 54 | "version": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", 55 | "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", 56 | "dev": true 57 | }, 58 | "dblite": { 59 | "version": "https://registry.npmjs.org/dblite/-/dblite-0.7.7.tgz", 60 | "integrity": "sha1-JNgZbphcRzP7Fb/flydEhoeKgqE=", 61 | "dev": true 62 | }, 63 | "dom-serializer": { 64 | "version": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", 65 | "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", 66 | "dev": true, 67 | "requires": { 68 | "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", 69 | "entities": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" 70 | }, 71 | "dependencies": { 72 | "domelementtype": { 73 | "version": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", 74 | "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", 75 | "dev": true 76 | }, 77 | "entities": { 78 | "version": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", 79 | "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", 80 | "dev": true 81 | } 82 | } 83 | }, 84 | "domelementtype": { 85 | "version": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", 86 | "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", 87 | "dev": true 88 | }, 89 | "domhandler": { 90 | "version": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", 91 | "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", 92 | "dev": true, 93 | "requires": { 94 | "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" 95 | } 96 | }, 97 | "domutils": { 98 | "version": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 99 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", 100 | "dev": true, 101 | "requires": { 102 | "dom-serializer": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", 103 | "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" 104 | } 105 | }, 106 | "entities": { 107 | "version": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", 108 | "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", 109 | "dev": true 110 | }, 111 | "es6-promise": { 112 | "version": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.1.2.tgz", 113 | "integrity": "sha1-eV4lzrR/e6uyY9FRr77dktGOagc=", 114 | "dev": true 115 | }, 116 | "exit": { 117 | "version": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", 118 | "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", 119 | "dev": true 120 | }, 121 | "fs.realpath": { 122 | "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 123 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 124 | "dev": true 125 | }, 126 | "glob": { 127 | "version": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 128 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 129 | "dev": true, 130 | "requires": { 131 | "fs.realpath": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 132 | "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 133 | "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 134 | "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", 135 | "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 136 | "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" 137 | } 138 | }, 139 | "htmlparser2": { 140 | "version": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", 141 | "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", 142 | "dev": true, 143 | "requires": { 144 | "domelementtype": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", 145 | "domhandler": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", 146 | "domutils": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 147 | "entities": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", 148 | "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" 149 | } 150 | }, 151 | "inflight": { 152 | "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 153 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 154 | "dev": true, 155 | "requires": { 156 | "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 157 | "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" 158 | } 159 | }, 160 | "inherits": { 161 | "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 162 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 163 | "dev": true 164 | }, 165 | "isarray": { 166 | "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 167 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 168 | "dev": true 169 | }, 170 | "jshint": { 171 | "version": "https://registry.npmjs.org/jshint/-/jshint-2.9.4.tgz", 172 | "integrity": "sha1-XjupeEjVKQJz21FK7kf+JM9ZKTQ=", 173 | "dev": true, 174 | "requires": { 175 | "cli": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", 176 | "console-browserify": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", 177 | "exit": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", 178 | "htmlparser2": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", 179 | "lodash": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz", 180 | "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", 181 | "shelljs": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 182 | "strip-json-comments": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz" 183 | } 184 | }, 185 | "lodash": { 186 | "version": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz", 187 | "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU=", 188 | "dev": true 189 | }, 190 | "minimatch": { 191 | "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", 192 | "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", 193 | "dev": true, 194 | "requires": { 195 | "brace-expansion": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz" 196 | } 197 | }, 198 | "once": { 199 | "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 200 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 201 | "dev": true, 202 | "requires": { 203 | "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" 204 | } 205 | }, 206 | "path-is-absolute": { 207 | "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 208 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 209 | "dev": true 210 | }, 211 | "ramda": { 212 | "version": "0.24.1", 213 | "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", 214 | "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" 215 | }, 216 | "readable-stream": { 217 | "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 218 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 219 | "dev": true, 220 | "requires": { 221 | "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 222 | "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 223 | "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 224 | "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" 225 | } 226 | }, 227 | "shelljs": { 228 | "version": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 229 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", 230 | "dev": true 231 | }, 232 | "string_decoder": { 233 | "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 234 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 235 | "dev": true 236 | }, 237 | "strip-json-comments": { 238 | "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", 239 | "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", 240 | "dev": true 241 | }, 242 | "wrappy": { 243 | "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 244 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 245 | "dev": true 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "bin": "jsgtk", 3 | "name": "jsgtk", 4 | "version": "0.12.2", 5 | "description": "A simplified approach to GJS for Node.JS and JavaScript developers.", 6 | "scripts": { 7 | "postinstall": "sh postinstall.sh", 8 | "test": "./jsgtk tests/main.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/WebReflection/jsgtk.git" 13 | }, 14 | "keywords": [ 15 | "JS", 16 | "Gtk", 17 | "gjs", 18 | "jsgtk", 19 | "node", 20 | "native", 21 | "desktop", 22 | "UI" 23 | ], 24 | "author": "Andrea Giammarchi", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/WebReflection/jsgtk/issues" 28 | }, 29 | "homepage": "https://github.com/WebReflection/jsgtk#readme", 30 | "devDependencies": { 31 | "babel-standalone": "git@github.com:WebReflection/babel-standalone.git", 32 | "dblite": "^0.7.7", 33 | "es6-promise": "3.1.2", 34 | "jshint": "^2.9.1" 35 | }, 36 | "dependencies": { 37 | "ramda": "^0.24.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "$(which gjs 2> /dev/null)" = "" ]; then 4 | echo '' 5 | echo -e "$(tput bold)Please install GJS via:$(tput sgr0)" 6 | if [ "$(uname)" = "Darwin" ]; then 7 | if [ "$(which brew)" = "" ]; then 8 | if [ "$(which port)" = "" ]; then 9 | echo ' please install either homebrew or macports' 10 | echo ' http://brew.sh/' 11 | echo ' brew install gtk+3 gjs' 12 | echo '' 13 | echo ' https://www.macports.org/' 14 | echo ' sudo port install gjs adwaita-icon-theme xorg-server xorg-xinit' 15 | else 16 | echo ' sudo port install gjs adwaita-icon-theme xorg-server xorg-xinit' 17 | fi 18 | else 19 | echo ' brew install gtk+3 gjs' 20 | fi 21 | else 22 | if [ "$(uname)" = "Linux" ]; then 23 | if [ "$(which pacman 2> /dev/null)" = "" ]; then 24 | if [ "$(which apt-get 2> /dev/null)" = "" ]; then 25 | if [ "$(which yum 2> /dev/null)" = "" ]; then 26 | echo ' not sure how you install stuff in here' 27 | echo ' but these are most likely modules you need:' 28 | echo ' gobject-introspection libgirepository-1.0 gjs' 29 | else 30 | echo ' sudo yum install gjs' 31 | fi 32 | else 33 | # gobject-introspection libgirepository1.0-dev # ??? 34 | echo ' sudo apt-get install gjs' 35 | fi 36 | else 37 | # gobject-introspection # ??? 38 | echo ' sudo pacman -S --needed gjs' 39 | fi 40 | else 41 | echo ' This platform is not supported.' 42 | echo ' Please consider using MSYS2' 43 | echo ' https://msys2.github.io/' 44 | fi 45 | fi 46 | echo '' 47 | sleep 3 48 | fi -------------------------------------------------------------------------------- /tests/db.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/jsgtk/38c725037b3bd3b26952e7ed7c97299f16336295/tests/db.sqlite -------------------------------------------------------------------------------- /tests/dblite.js: -------------------------------------------------------------------------------- 1 | const 2 | dblite = require('dblite'), 3 | db = dblite('tests/db.sqlite') 4 | ; 5 | 6 | db 7 | .query('CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, value TEXT)') 8 | .query('INSERT INTO test VALUES(null, ?)', ['some text']) 9 | .query('SELECT * FROM test', function (err, rows) { 10 | print(JSON.stringify(rows)); 11 | db.close(); 12 | }) 13 | .on('close', process.exit) 14 | ; -------------------------------------------------------------------------------- /tests/delayed.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo 1 && sleep 1 && echo 2 -------------------------------------------------------------------------------- /tests/geo.js: -------------------------------------------------------------------------------- 1 | const 2 | GeocodeGlib = require('GeocodeGlib') 3 | ; 4 | 5 | const location = new GeocodeGlib.Location( 6 | // {latitude, longitude, accuracy} 7 | ); 8 | 9 | // London 10 | location.setFromUri('geo:51.507222,-0.1275'); 11 | // console.log(location.toUri(GeocodeGlib.LocationURIScheme.GEO)); 12 | 13 | const reverse = new GeocodeGlib.Reverse(location); 14 | 15 | // nope, this always throws errors 16 | console.log(reverse.resolve()); 17 | -------------------------------------------------------------------------------- /tests/main.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jsgtk 2 | 3 | [ 4 | function crypto() { 5 | const buf = require('crypto').randomBytes(256); 6 | return [buf.length, !/^0+$/.test(buf.toString('hex'))].join(','); 7 | }, 8 | function buffer() { 9 | const Buffer = require('buffer').Buffer; 10 | return [ 11 | 12 | new Buffer('aGVsbG8gd29ybGQ=', 'base64').toString('ascii'), 13 | new Buffer('aGVsbG8gd29ybGQ=', 'base64').toString('base64'), 14 | new Buffer('aGVsbG8gd29ybGQ=', 'base64').toString('binary'), 15 | new Buffer('aGVsbG8gd29ybGQ=', 'base64').toString('hex'), 16 | new Buffer('aGVsbG8gd29ybGQ=', 'base64').toString('utf8'), 17 | new Buffer('aGVsbG8gd29ybGQ=', 'base64').toString(), 18 | 19 | new Buffer('7468697320697320612074c3a97374', 'hex').toString('ascii'), 20 | new Buffer('7468697320697320612074c3a97374', 'hex').toString('base64'), 21 | new Buffer('7468697320697320612074c3a97374', 'hex').toString('binary'), 22 | new Buffer('7468697320697320612074c3a97374', 'hex').toString('hex'), 23 | new Buffer('7468697320697320612074c3a97374', 'hex').toString('utf8'), 24 | new Buffer('7468697320697320612074c3a97374', 'hex').toString(), 25 | 26 | new Buffer([1,2,3]).toString('ascii'), 27 | new Buffer([1,2,3]).toString('base64'), 28 | new Buffer([1,2,3]).toString('binary'), 29 | new Buffer([1,2,3]).toString('hex'), 30 | new Buffer([1,2,3]).toString('utf8'), 31 | new Buffer([1,2,3]).toString(), 32 | 33 | new Buffer('test', 'utf8').toString('ascii'), 34 | new Buffer('test', 'utf8').toString('base64'), 35 | new Buffer('test', 'utf8').toString('binary'), 36 | new Buffer('test', 'utf8').toString('hex'), 37 | new Buffer('test', 'utf8').toString('utf8'), 38 | new Buffer('test', 'utf8').toString(), 39 | 40 | new Buffer('tést', 'utf8').toString('ascii'), 41 | new Buffer('tést', 'utf8').toString('base64'), 42 | new Buffer('tést', 'utf8').toString('binary'), 43 | new Buffer('tést', 'utf8').toString('hex'), 44 | new Buffer('tést', 'utf8').toString('utf8'), 45 | new Buffer('tést', 'utf8').toString(), 46 | 47 | new Buffer('test').toString('ascii'), 48 | new Buffer('test').toString('base64'), 49 | new Buffer('test').toString('binary'), 50 | new Buffer('test').toString('hex'), 51 | new Buffer('test').toString('utf8'), 52 | 53 | new Buffer('tést').toString('ascii'), 54 | new Buffer('tést').toString('base64'), 55 | new Buffer('tést').toString('binary'), 56 | new Buffer('tést').toString('hex'), 57 | new Buffer('tést').toString('utf8') 58 | 59 | ].join(','); 60 | }, 61 | function pathFormat() { 62 | return [ 63 | require('path').format({ 64 | root : "/", 65 | dir : "/home/user/dir", 66 | base : "file.txt", 67 | ext : ".txt", 68 | name : "file" 69 | }), 70 | require('path').format({ 71 | root : "", 72 | dir : "../jsgtk/tests", 73 | base : "main.js", 74 | ext : ".js", 75 | name : "main" 76 | }), 77 | ].join('\n'); 78 | }, 79 | function pathParse() { 80 | return [ 81 | JSON.stringify(require('path').parse('/home/user/dir/file.txt')), 82 | JSON.stringify(require('path').parse('/home/user/dir/.git')), 83 | JSON.stringify(require('path').parse('tests/main.js')), 84 | JSON.stringify(require('path').parse('../jsgtk/tests/main.js')), 85 | ].join('\n') 86 | }, 87 | function pathRelative() { 88 | return [ 89 | require('path').relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb'), 90 | require('path').relative('/data/orandea/test/aaa', '/data/orandea'), 91 | require('path').relative('/data/orandea', '/data/orandea'), 92 | require('path').relative('/data/orandea/a/', '/data/orandea/b/') 93 | ].join(','); 94 | }, 95 | function logUtil() { 96 | require('util').log('\u2764\uFE0F'); 97 | }, 98 | function inherits() { 99 | function A() {} 100 | function B() {} 101 | require('util').inherits(B, A); 102 | var b = new B; 103 | return b instanceof A && 104 | b instanceof B && 105 | B.super_ === A && 106 | b.constructor === B; 107 | } 108 | //*/ 109 | ].forEach( 110 | (function() {'use strict'; 111 | var spawnSync = require('child_process').spawnSync; 112 | var fnName = function (fn) { 113 | return String(fn.name || fn).slice(0, 32); 114 | }; 115 | return function (fn, i, arr) { 116 | var 117 | r1, 118 | r2, 119 | out, result, 120 | log = console.log, 121 | node = spawnSync( 122 | 'node', ['-e', 'console.log(' + fn.toString() + '())'] 123 | ).output 124 | ; 125 | if (!node[0]) { 126 | try { 127 | r1 = node[1].toString().trim().replace(/(?:\r\n|\r|\n)undefined$/, ''); 128 | console.log = function (msg) { 129 | out = msg; 130 | }; 131 | result = fn(); 132 | console.log = log; 133 | r2 = String(result === undefined ? out : result).trim(); 134 | if (r2 !== r1) { 135 | throw new Error('expecting ' + r1 + ' but got ' + r2 + ' in ' + fnName(fn)); 136 | } 137 | } catch(o_O) { 138 | console.log = log; 139 | if (!arr.failures) arr.failures = 0; 140 | arr.failures = arr.failures + 1; 141 | console.error(o_O.message); 142 | } 143 | } else { 144 | throw new Error('not even NodeJS passed ' + fnName(fn)); 145 | } 146 | if (i + 1 === arr.length) { 147 | console.info('[ ' + arr.length + ' tests executed ]'); 148 | if (arr.failures) { 149 | console.error(arr.failures + ' runtime errors'); 150 | } 151 | } 152 | }; 153 | }()) 154 | ); -------------------------------------------------------------------------------- /tests/runtime.js: -------------------------------------------------------------------------------- 1 | function asd(v) { 2 | if (v == 2) return 3; 3 | } 4 | 5 | asd(); 6 | 7 | setTimeout(require, 1000, 'GLib'); -------------------------------------------------------------------------------- /tests/spawn.js: -------------------------------------------------------------------------------- 1 | const spawn = require('child_process').spawn; 2 | const ls = spawn('sh', ['tests/delayed.sh']); 3 | 4 | ls.stdout.on('data', (data) => { 5 | console.log(`stdout: ${data}`); 6 | }); 7 | ls.stdout.on('close', () => { 8 | console.log('close'); 9 | }); 10 | 11 | ls.stderr.on('data', (data) => { 12 | console.log(`stderr: ${data}`); 13 | }); 14 | 15 | ls.on('close', (code) => { 16 | console.log(`child process exited with code ${code}`); 17 | }); -------------------------------------------------------------------------------- /utils/compare: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # example 4 | # ./utils/compare tests/runtime.js 5 | 6 | DIR=$(pwd) 7 | if [ "${DIR:${#DIR}-6}" == "/utils" ]; then 8 | DIR="${DIR}/.." 9 | fi 10 | 11 | echo "" 12 | echo -e "$(tput bold)# NodeJS ########################################$(tput sgr0)" 13 | time node $@ 14 | 15 | echo "" 16 | echo -e "$(tput bold)# JSGtk #########################################$(tput sgr0)" 17 | time ${DIR}/jsgtk $@ 18 | echo "" -------------------------------------------------------------------------------- /utils/documentation: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # [WARNIG] THIS PROCEDURE CAN TAKE JUST ABOUT FOREVER 4 | 5 | document() { 6 | local f=$1 7 | echo $(tput intr) | g-ir-doc-tool $f -o $2 > /dev/null 2>&1 8 | if [ $? -eq 0 ]; then 9 | echo "[OK] $f" 10 | else 11 | echo "[FAILURE] $f" 12 | fi 13 | } 14 | 15 | SOURCE=$1 16 | DEST=$2 17 | 18 | if [ "$SOURCE" = "" ] || [ "$DEST" = "" ]; then 19 | echo './documentation source dest' 20 | echo './documentation /usr/share/gir-1.0 ~/Documents/gir' 21 | exit 1 22 | fi 23 | 24 | mkdir -p "$DEST" 25 | 26 | if [ -f ${SOURCE} ]; then 27 | document ${SOURCE} $DEST 28 | else 29 | for f in $(ls ${SOURCE}/*.gir) 30 | do 31 | document $f $DEST 32 | done 33 | fi 34 | 35 | echo '[DONE] Generating HTML (this might take a while)' 36 | 37 | cd $DEST 38 | mkdir -p HTML 39 | yelp-build html -o ./HTML . -------------------------------------------------------------------------------- /utils/jshint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR=$(pwd) 4 | if [ "${DIR:${#DIR}-6}" == "/utils" ]; then 5 | DIR="${DIR}/.." 6 | fi 7 | 8 | jshintDir() { 9 | echo -e "$2$(tput bold)$1/*$(tput sgr0)" 10 | for file in $(ls $1); do 11 | if [ -d $1/$file ]; then 12 | jshintDir $1/$file "$2$2" 13 | else 14 | if [ "$file" != "babel.js" ] && [ "$file" != "promise.js" ]; then 15 | echo "$2 └─ $1/$file" 16 | ${DIR}/node_modules/.bin/jshint $1/$file 17 | fi 18 | fi 19 | done 20 | } 21 | 22 | echo '' 23 | echo -e "$(tput bold)jshint$(tput sgr0)" 24 | echo " └─ jsgtk" 25 | cat $DIR/jsgtk > /tmp/jsgtk 26 | sed -i "s/Function=.*//" /tmp/jsgtk 27 | ${DIR}/node_modules/.bin/jshint /tmp/jsgtk 28 | rm /tmp/jsgtk 29 | jshintDir $DIR/jsgtk_modules ' ' 30 | echo '' --------------------------------------------------------------------------------