GNOME's JavaScript

JavaScript (sometimes abbreviated JS), is a prototype-based scripting language. It is dynamic, weakly typed, has first-class functions and supports object-oriented.

Gnome-shell is written in JavaScript because it allows for quick prototyping and development cycles. The difference between javascript and other dynamic languages (python, ruby, php, etc...) is that javascript doesn't depend on a specific platform. Gnome-shell can than run it with its own platform (Gjs).

From Scratch

Why use an IDE

It is very helpful to develop using an IDE with javascript support. It will help us with code folding, autocompletion, outline, etc.

The recommended IDE for this tutorial is Gnome Builder. It should be packaged for all distributions that also offer Gnome Desktop. It has syntax highlighting and code completion for Javascript.

Gnome also has an IDE called Anjuta. The problem is that Anjuta lack some features, like autocompletion, code folding, etc... so it is better to develop using either Eclipse or Gnome Builder until Anjuta gets to have this kind of features.

So, we start setting up eclipse to have javascript support.

Install eclipse from your app store of your distribution. Open eclipse and select your workspace. Go to Help. Install new software. Select Work with "All available sites". Search for javascript. Install. That's it!

Problems

I haven't got eclipse in my distribution

Go to http://www.eclipse.org/downloads/ and download eclipse classic. I select work with "All available sites" but nothing is shown. Go to "Find more software by working with "All available software sites" preferences" and add your eclipse download site which depens on the eclipse version you have. For eclipse Juno it is http://download.eclipse.org/releases/juno.

Using eclipse

When you create an extension, the extension is saved at ~/.local/shared/gnome-shell/extensions with a gnome-shell extension folder format, so you can't create a project here and edit the files expecting that each change you do reflects directly in the gnome-shell. So, if we use eclipse to develop extensions, the best way is to open directly the file you are editing, like ~/.local/shared/gnome-shell/extensions/myExtensionFolder/myExtension.js

To visualize the changes, you need to have enabled the extension. To enable the extension use gnome-tweak-tool.

After that, every time you make a change, you will have to save the file and restart the shell (Alt+F2 , write "r" without quotes, enter).

For Beginners

Variables

   1 const x = 0; // a variable that cannot be reassigned
   2 var y =1 ; // a variable without a scope, you can use it anywhere in your code.
   3 let z= 2; // a variable that has single block scope, like in a for loop for example.
   4 

Variables can also be declared by using matching arrays or objects :

   1 let [ a, b, c ] = [ 1, 2, 3 ];
   2 let { a, b } = { a: "apple", b: "banana" };

If/else/elseif

   1 if (x>0) {
   2         print(“Positive number”)  ;
   3 } else if (x<0) {
   4         print(“Not a positive number”);
   5 } else {
   6         print(“Not a number”);
   7 };

For loops

   1 for (let i = 0; i < 10; i++) {
   2         print(i)
   3 };

Looping over arrays using for...of is much more convenient.

   1 let fruits = [ "apple", "orange", "banana" ];
   2 
   3 for (let fruit of fruits) {
   4         print(fruit);
   5 }

Functions

   1 function Foo(arg1, arg2) {do something};

Function parameters can accept default values;

   1 function add(x, y = 0) {
   2         return x + y;
   3 }

Imports

You import objects from the imports object.

   1 const Gio = imports.gi.Gio;
   2 const Panel = imports.ui.Panel;

If you want to include your own files, you have to add the current directory to imports' search path.

   1 imports.searchPath.unshift(".");
   2 const Awesome = imports.awesome; // import awesome.js from current directory
   3 const Brilliant = imports.lib.brilliant; // import lib/brilliant.js
   4 

Gjs Particularities

Class inheritance

I like the simplicity and readability of the Lang.Class function :

   1 const Lang = imports.lang
   2 
   3 const New_Class = new Lang.Class({
   4                 Name: 'new class',
   5                 Extends: Old_class,
   6 
   7                 _init: function() {do something};
   8 });

Using "this" in closures

"this" has a restricted scope, relative to how the closure is invoked, not to the value of "this" where the closure is created. It needs to be passed to the function or callback at invocation time. To do this, use the Lang.bind() helper function :

   1 const Lang = imports.lang;
   2 
   3 let closure = Lang.bind( this, this._Foo )

Extending existing functions

Rather than disabeling parts of the gnome-shell and re-writing the whole code to fit your needs, you can modify it directly.

Say we have the following class Foo :

   1 const Lang = imports.gi.lang
   2 
   3 const Foo = new Lang.Class({
   4         Name: 'Foo',
   5 
   6                 _init : function() {
   7                 this.variable  = 2
   8                 this._Bar()
   9         },
  10 
  11         _Bar : function (metaWindow) {
  12                 return this.variable ;
  13                 }
  14 })

Overwriting functions

If we want to re-write the _Bar function to always return 1, we edit the class' "prototype". Simply name the function, object or variable that you want to modify in the brackets and replace it's code :

   1 Foo.prototype['_Bar'] = function() { return 1; }

Adding to functions

Another way to extend functionality is to inject code, with these functions:

   1 function injectToFunction(parent, name, func)
   2 {
   3         let origin = parent[name];
   4         parent[name] = function()
   5         {
   6                 let ret;
   7                 ret = origin.apply(this, arguments);
   8                 if (ret === undefined)
   9                         ret = func.apply(this, arguments);
  10                 return ret;
  11         }
  12         return origin;
  13 }
  14 
  15 function removeInjection(object, injection, name)
  16 {
  17         if (injection[name] === undefined)
  18                 delete object[name];
  19         else
  20                 object[name] = injection[name];
  21 }

To use them :

   1 // We store the injection, to have the option to disable it later
   2 inections=[];
   3 // Inject the code
   4 injections['injection1']=injectToFunction(Foo.prototype, '_Bar',  function(){return 1;});
   5 // To "de-inject" the code
   6 removeInjection(Foo.prototype, injections,  'injection1');

With this, we can add some code to the function without replacing the whole function. Our code is written after all code of the original function but before it's return statement.

GNOME's UI Elements

Unfortunately, I don't have the patience to go through all these files and describe in details what they do how. If you want something a bit more complete, but outdated, you can go to Mathematical Coffee's excelent blog post on the subject.

UI files in imports.ui

  • accessDialog.js
  • altTab.js: the popup that appears when you press Alt+Tab.
  • animation.js
  • appDisplay.js: to do with the applications tab in the overview - searching for apps and displaying their icons.
  • appFavorites.js: manages app favourites in the dash (left hand sidebar in the overview).
  • audioDeviceSelection.js
  • background.js
  • backgroundMenu.js
  • boxpointer.js: whenever you open a popup menu there's a little arrow connecting the button to the menu. That's what this is.
  • calendar.js: stuff to do with the calendar in the clock dropdown menu.
  • checkBox.js: A checkbox. As far as I can tell there's one in the Keyring Prompt and that's it.
  • ctrlAltTab.js: Handles the Accessibility switcher which lets you select UI elements (top panel, dash, ...) so that you can navigate around them with the keyboard (I never knew this existed!).
  • dash.js: Handles the dash (left-hand sidebar in the overview showing the application favourites).
  • dateMenu.js: The graphical calendar widget in the calendar menu (the grid of squares where you click on the date to see events for that date).
  • dnd.js: Handles drag and drop.
  • edgeDragAction.js
  • endSessionDialog.js: the dialog that appears when you log out/shut down/etc.
  • environment.js: sets up the GJS environment for the rest of the code.
  • extensionDownloader.js
  • extensionSystem.js: handles installing, enabling, and disabling extensions.
  • focusCaretTracker.js
  • grabHelper.js
  • ibusCandidatePopup.js
  • iconGrid.js: classes for layout out icons in a grid (e.g. the Overview search results)
  • keyboard.js: on-screen keyboard class.
  • layout.js: stuff to do with laying out actors on the stage? (panel, message tray, hot corners, ...)
  • legacyTray.js
  • lightbox.js: Creates a dark translucent shade covering a UI element (e.g. when the end session modal dialog causes the rest of the screen to be shaded; this is the shade).
  • lookingGlass.js: the looking glass (Alt+F2, 'r').
  • messageList.js
  • messageTray.js: the message tray (bottom of the screen showing notifications).
  • modalDialog.js: defines the gnome-shell popup dialogs (logout/shutdown, authentication, ...).
  • mpris.js
  • notificationDaemon.js: listens for notifications via DBus and adds them to the message tray.
  • osdMonitorLabeler.js
  • overview.js: The overview (press the windows key).
  • overviewControls.js
  • padOsd.js
  • panel.js: Defines the top panel.
  • panelMenu.js: Defines various helper functions for items in the panel (notably, the system status icon class, being a button in the top panel with a dropdown menu).
  • pointerWatcher.js
  • popupMenu.js: Defines the popup menus and items that can go in them.
  • remoteMenu.js
  • remoteSearch.js (GNOME 3.4 only): Handles remote search providers (search providers that operate through DBus, like Google and Wikipedia where searches go through the internet).
  • runDialog.js: The run dialog/command prompt when you pres Alt+F2.
  • screenShield.js
  • screencast.js
  • screeshot.js
  • scripting.js: A scripting module for gnome-shell devs to do performance/unit tests...(?)
  • search.js: Abstract classes that handle searching and providing results (that are then displayed in the overview by searchDisplay.js classes). They are all implemented through other classes (e.g. appDisplay.js, contactDisplay.js, ...
  • seperator.js
  • sessionMode.js
  • shellDBus.js: GNOME shell DBus implementation (interface name org.gnome.shell, path /org/gnome/Shell) - for installing/enabling/disabling/uninstalling extensions and requesting information from them, taking screenshots, and so on.
  • shellEntry.js: Adds context menus to entry widgets with 'copy' and 'paste' and (if it's a password widget) 'show/hide text'.
  • shellMountOperation.js: Wrappers around Shell.MountOperation that handle the ejecting of a device from the autorunManager.AutorunResidentNotification. Provides a dialog letting you know if the device is busy/what processes are inhibiting unmounting and notifications if passwords are needed for the volume.

  • slider.js : A slider Item you can add to menus.
  • switcherPopup.js
  • tweener.js: a module that wraps around imports.tweener.tweener adding extra goodness for gnome-shell (initialised in main.js).
  • unlockDialog.js
  • userWidget.js
  • viewSelector.js: The main part of the Overview - defining a notebook/tabs model. For example the 'Applications' and 'Windows' sections of the Overview are 'tabs' within the notebook.
  • windowAttentionHandler.js: Handles requests for attention from windows (e.g. when you start up and app and it takes a while to start, when the app is done loading you get a ' is ready' notification).
  • windowManager.js: Extra bells and whistles for the window manager implemented on the JS side (mainly window animations).
  • windowMenu.js
  • workspace.js: Classes handling the window previews you see in the 'Windows' tab of the Overview.
  • workspaceSwitcherPopup.js: The popup you get upon switching workspaces through the keybindings that shows you which workspace you're switching to.
  • workspaceThumbnail.js: Defines the classes in the workspaces sidebar in the 'Windows' tab of the Overview - the little snapshots of each workspace allowing you to drag windows between them.
  • workspacesView.js: Essentially the 'Windows' tab in the Overview - handles assembling the window previews (from workspace.js) and the - workspaces sidebar (workspaceThumbnail.js) into one 'tab' for the Overview.
  • xdndHandler.js: GNOME-shell handling of Xdnd: dragging and dropping things between an X window and a gnome-shell object. E.g. if you try to drag a file from Nautilus over the Overview button, panel.js uses xdndHandler to detect this and trigger the overview.

UI files in imports.ui.components

  • init.js

  • automountManager.js: handles the automagic detection of external media (USB sticks, ...).
  • autorunManager.js: handles the popup menu when you mount an external media offering you options to view photos, browse, play music, etc on the media.
  • keyringPrompt.js (GNOME 3.4 only): prompt for gnome-keyring.
  • networkAgent.js: wrapper around network authentication (listens on dbus for password requests and pops up the authentication dialog).
  • polkitAgent.js
  • telepathyClient.js

UI files in imports.ui.status

This contains the files for all the standard status indicators in the status area.

  • accessibility.js: the accessibility (a11y) indicator.
  • bluetooth.js: the bluetooth indicator.
  • brightness.js
  • keyboard.js: keyboard layout indicator (letting you switch between layouts).
  • location.js
  • network.js: the network (wifi/wired) indicator.
  • nightLight.js
  • power.js: the power (battery life/power settings) indicator.
  • rfkill.js
  • screencast.js
  • system.js
  • volume.js: the volume/sound settings indicator.

Projects/GnomeShell/Extensions/EcoDoc/JavaScript (last edited 2017-05-06 04:34:28 by Fulleco)