...
 
Commits (2)
......@@ -26,7 +26,7 @@ export default {
<style lang="scss">
.showcase {
padding: 2rem;
margin: 2rem;
text-decoration: none !important;
color: black;
......
<template>
<div class="showcase-box">
<h2>{{ title }}</h2>
<p>{{ subtitle }}</p>
<div class="showcase-box-inner">
<slot />
</div>
</div>
</template>
<script>
export default {
props: {
title: { type: String },
subtitle: { type: String }
}
};
</script>
<style lang="scss">
.showcase-box {
padding: 2rem;
.showcase-box-inner {
display: flex;
& > *:last-child {
padding-right: 0 !important;
}
& > *:first-child {
padding-left: 0 !important;
}
}
}
</style>
......@@ -4,6 +4,8 @@ module.exports = {
editLinks: true,
dest: "public",
themeConfig: {
editLinks: true,
repo: 'https://gitlab.com/gjs-guide/gjs-guide.gitlab.io/',
logo: '/logo.svg',
docsDir: 'docs',
nav: [{
......@@ -28,16 +30,6 @@ module.exports = {
},
],
sidebar: {
'/tutorials/': [
'',
'gjs-transition',
'gjs-features-across-versions',
'gjs-style-guide',
'gjs-basic-file-operations',
'gjs-gtk-application-packaging',
'gjs-gtk-templating',
'gjs-legacy-class-syntax'
],
'/examples/tags/': [
'',
'00-creating-user-interface',
......
<template>
<div class="page">
<slot name="top" />
<Content class="index-page-padding" :custom="true" />
<div class="index-page-edit">
<div class="edit-link" v-if="editLink">
<a :href="editLink" target="_blank" rel="noopener noreferrer">{{ editLinkText }}</a>
<OutboundLink/>
</div>
<div class="last-updated" v-if="lastUpdated">
<span class="prefix">{{ lastUpdatedText }}: </span>
<span class="time">{{ lastUpdated }}</span>
</div>
</div>
<slot name="bottom" />
</div>
</template>
<script>
import { resolvePage, normalize, outboundRE, endingSlashRE } from './util';
import Page from './Page';
export default {
extends: Page
};
</script>
<style lang="scss">
@import './styles/config.scss';
@import './styles/wrapper.scss';
.index-page-edit {
padding: 1rem 5rem !important;
overflow: auto;
.edit-link {
display: inline-block;
a {
color: lighten($textColor, 25%);
margin-right: 0.25rem;
}
}
.last-updated {
float: right;
font-size: 0.9em;
.prefix {
font-weight: 500;
color: lighten($textColor, 25%);
}
.time {
font-weight: 400;
color: #aaa;
}
}
}
.index-page-padding {
padding: 1rem 5rem !important;
}
</style>
......@@ -10,7 +10,7 @@
<component :is="$page.frontmatter.layout" />
</div>
<Home v-else-if="$page.frontmatter.home" />
<Page v-else :sidebar-items="sidebarItems">
<Page v-else :custom="custom" :sidebar-items="sidebarItems">
<slot name="page-top" slot="top" />
<slot name="page-bottom" slot="bottom" />
</Page>
......@@ -23,11 +23,12 @@ import nprogress from 'nprogress';
import Home from './Home.vue';
import Navbar from './Navbar.vue';
import Page from './Page.vue';
import IndexPage from './IndexPage.vue';
import Sidebar from './Sidebar.vue';
import { resolveSidebarItems } from './util';
export default {
components: { Home, Page, Sidebar, Navbar },
components: { Home, Page, IndexPage, Sidebar, Navbar },
data() {
return {
isSidebarOpen: false
......@@ -58,6 +59,9 @@ export default {
this.sidebarItems.length
);
},
custom() {
return this.$page.frontmatter.isCustom;
},
sidebarItems() {
return resolveSidebarItems(
this.$page,
......
<template>
<div class="page">
<slot name="top" />
<Content :custom="false" />
<Content :custom="custom" />
<div class="page-edit">
<div class="edit-link" v-if="editLink">
<a :href="editLink" target="_blank" rel="noopener noreferrer">{{ editLinkText }}</a>
......@@ -35,7 +35,17 @@
import { resolvePage, normalize, outboundRE, endingSlashRE } from './util';
export default {
props: ['sidebarItems'],
props: {
sidebarItems: {
default() {
return [];
}
},
custom: {
type: Boolean,
default: false
}
},
computed: {
lastUpdated() {
if (this.$page.lastUpdated) {
......
......@@ -12,7 +12,7 @@ body {
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Cantarell, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 16px;
......@@ -79,6 +79,9 @@ body {
}
.content.custom {
>*:first-child {
margin-top: $navbarHeight;
}
padding: 0;
margin: 0;
img {
......
---
title: Introduction
title: GJS Tutorials
date: 2018-07-25 16:10:11
layout: IndexPage
---
# GJS Introduction
## What is GJS?
Welcome to GJS!
<ShowCaseBox title="GJS" subtitle="Tutorials for GJS">
<ShowCase link="gjs-style-guide.html" title="Style Guide" subtitle="The official style guide for GJS and GNOME projects written in GJS." image="" />
<ShowCase link="gjs-transition.html" title="Transition" subtitle="How does GJS compare to other JavaScript environments?" image="" />
\ No newline at end of file
<ShowCase link="gjs-transition.html" title="Transition" subtitle="How does GJS compare to other JavaScript environments?" image="" />
<ShowCase link="gjs-legacy-class-syntax.html" title="Legacy Classes" subtitle="How do I use the deprecated Lang.Class objects?" image="" />
<ShowCase link="gjs-features-across-versions.html" title="Feature Compatibility" subtitle="Which features work in my version of GJS?" image="" />
</ShowCaseBox>
<ShowCaseBox title="GTK" subtitle="Gtk Tutorials">
<ShowCase link="gjs-gtk-application-packaging.html" title="GTK+ Application Packaging" subtitle="How do I package my GTK+ application with GJS?" image="" />
</ShowCaseBox>
<ShowCaseBox title="Gio" subtitle="Gio Tutorials">
<ShowCase link="gjs-basic-file-operations.html" title="Files in GJS" subtitle="Basic File Operations in GJS" image="" />
</ShowCaseBox>
\ No newline at end of file
---
title: Tips for Other Editing Environments
---
# Tips for Other Editing Environments
## Emacs
- If using Emacs, try js2-mode. It functions as a "lint" by highlighting missing semicolons and the like.
<!--## ESLint
- We have a plugin, `eslint-plugin-gjs`, which has excellent support for GJS development -->
......@@ -21,17 +21,17 @@ The following variables are used throughout this document:
## Runtime API
The following API will be available to applications, through the package.js module.
* `window.pkg` (`pkg` on the global object) will provide access to the package module
* `pkg.name` and `pkg.version` will return the package name and version, as passed to `pkg.init()`
* `pkg.prefix`, `pkg.datadir`, `pkg.libdir` will return the installed locations of those folders
* `pkg.pkgdatadir`, `pkg.moduledir`, `pkg.pkglibdir`, `pkg.localedir` will return the respective directories, or the appropriate subdirectory of the current directory if running uninstalled
* `pkg.initGettext()` will initialize gettext. After calling window._, window.C_ and window.N_ will be available
* `pkg.initFormat()` will initialize the format module. After calling, String.prototype.format will be available
* `pkg.initSubmodule(name)` will initialize a submodule named @name. It must be called before accessing the typelibs installed by that submodule
* `pkg.loadResource(name)` will load and register a GResource named @name. @name is optional and defaults to ${package-name}
* `pkg.require(deps)` will mark a set of dependencies on GI and standard JS modules. @deps is a object whose keys are repository names and whose values are API versions. If the dependencies are not satisfied, `pkg.require` will print an error message and quit.
The following API is available to applications through the [package module]().
* `window.pkg` (`pkg` on the global object) provides access to the package module
* `pkg.name` and `pkg.version` return the package name and version, as passed to `pkg.init()`
* `pkg.prefix`, `pkg.datadir`, `pkg.libdir` returns the installed locations of those folders
* `pkg.pkgdatadir`, `pkg.moduledir`, `pkg.pkglibdir`, `pkg.localedir` return the respective directories, or the appropriate subdirectory of the current directory if running uninstalled
* `pkg.initGettext()` initializes gettext. After calling window._, window.C_ and window.N_ be available
* `pkg.initFormat()` initializes the format module. After calling, String.prototype.format be available
* `pkg.initSubmodule(name)` initializes a submodule named @name. It must be called before accessing the typelibs installed by that submodule
* `pkg.loadResource(name)` loads and registers a GResource named @name. @name is optional and defaults to ${package-name}
* `pkg.require(deps)` marks a set of dependencies on GI and standard JS modules. @deps is a object whose keys are repository names and whose values are API versions. If the dependencies are not satisfied, `pkg.require` print an error message and quit.
## Package layout
......@@ -61,11 +61,11 @@ The following API will be available to applications, through the package.js modu
## Usage
Applications complying with this specification will have one application script, installed in \${prefix}/share/\${package-name} (aka ${pkgdatadir}), and named as ${entry-point-name}, without any extension or mangling.
Applications complying with this specification have one application script, installed in \${prefix}/share/\${package-name} (aka ${pkgdatadir}), and named as ${entry-point-name}, without any extension or mangling.
Optionally, one or more symlinks will be placed in ${bindir}, pointing to the appropriate script in ${pkgdatadir} and named in a fashion more suitable for command line usage (usually ${package-tarname}). Alternatively, a script that calls "gapplication launch ${package-name}" can be used.
Optionally, one or more symlinks be placed in ${bindir}, pointing to the appropriate script in ${pkgdatadir} and named in a fashion more suitable for command line usage (usually ${package-tarname}). Alternatively, a script that calls "gapplication launch ${package-name}" can be used.
The application itself will be DBus activated from a script called src/\${entry-point-name}, generated from configure substitution of the following
The application itself be DBus activated from a script called src/\${entry-point-name}, generated from configure substitution of the following
\${entry-point-name}.in:
```js
......@@ -79,7 +79,7 @@ imports.package.init({
imports.package.run(/* ${main-module} */);
```
Where ${main-module} is a module containing `main()` the function that will be invoked to start the process. This function should accept a single argument, an array of command line args. The first element in the array will be the full resolved path to the entry point itself (unlike the global ARGV variable for gjs). Also unlike ARGV, it is safe to modify this array.
Where ${main-module} is a module containing `main()` the function that be invoked to start the process. This function should accept a single argument, an array of command line args. The first element in the array be the full resolved path to the entry point itself (unlike the global ARGV variable for gjs). Also unlike ARGV, it is safe to modify this array.
`main()` should initialize a GApplication whose id is ${entry-point-name}, and do all the work inside the GApplication vfunc handlers.
......
......@@ -2,28 +2,53 @@
title: Style Guide
date: 2018-07-25 16:10:11
---
# GJS Coding Style
# GJS Style Guide
Our goal is to have all JavaScript code in GNOME follow a consistent style. In a dynamic language like
JavaScript, it is essential to be rigorous about style (and unit tests), or you rapidly end up
with a spaghetti-code mess.
This guide represents the official coding style of GJS and all official GNOME projects which utilize GJS.
The goal of this guide is to provide a flexible, balanced JavaScript syntax for all projects, GNOME or otherwise, which utilize our platform.
In an amazing, dynamic language like JavaScript, a correct style guide (and unit tests) are critical or your codebase rapidly becomes a spaghetti-code mess.
## Rule List
[[toc]]
## Semicolons
While JavaScript allows omitting semicolons at the end of lines,we do not. Always end statements with a semicolon.
While JavaScript allows omitting semicolons at the end of lines, we do not. Always end statements with a semicolon.
## Variable naming ##
- We use *CamelCase* variable names, with CamelCase for type names and lowerCamelCase for variable, property, and method names.
- Private variables, whether object member variables or module-scoped variables, should begin with `_`.
- Global variables (in the global or 'window' object) should be avoided whenever possible. If you do create them, the variable name should have a namespace.
<!-- DISCUSS - If you are need to name a variable something weird to avoid a namespace collision, add a trailing `_` (not leading, leading `_` means private). -->
## Imports
Always use **CamelCase** when importing modules and classes to distinguish them from other variables.
1. Always use **CamelCase** when importing modules and classes to distinguish them from other variables.
```js
const Big = imports.big;
const GLib = imports.gi.GLib;
```
```js
const Lang = imports.lang;
```
2. Use quick object syntax when importing multiple classes from one source.
```js
const { GLib, GObject } = imports.gi;
const Lang = imports.lang;
```
3. Always separate library imports from local imports.
```js
const { GLib, GObject } = imports.gi;
const Lang = imports.lang;
const LocalClass = imports.localClass;
```
## Variable declaration
Always use one of `const`, `let`, or `var` when defining a variable. Always use `let` when block scope is intended; in particular, inside `for()` and `while()` loops, `let` is almost always correct.
Always use one of `const`, `let`, or `var` when defining a variable. Always use `let` when block scope is intended; in particular, inside `for()` and `while()` loops, `let` or `const` is almost always correct.
### `var`
......@@ -77,31 +102,120 @@ for (const i = 0; i < 10; ++i) {
If you used `var` instead of `const` it would print "10" numerous times.
## `this` in closures ##
## GObject Properties
::: warning
These rules only apply to GObject properties!
:::
GJS provides numerous ways to access properties on GObject classes.we recommend using `camelCase` for getting properties and utilizing the provided setter function instead of setting the property directly.
1. ### Use **lowerCamelCase** when getting or setting a simple property
#### Getting a property value
```js
/* incorrect */
let a = obj['property-name'];
/* incorrect */
let b = obj.property_name;
/* correct */
let c = obj.propertyName;
```
#### Setting a property value
```js
/* incorrect */
obj['property-name'] = 10;
/* incorrect */
obj.property_name = 10;
/* correct */
obj.propertyName = 10;
```
2. ### Use the `C` setter function when setting a property *that has side effects*
If setting the property causes change beyond simply changing the value, prefer the setter function for clarity.
```js
/* incorrect */
obj['property-name-has-side-effects'] = 10;
/* incorrect */
obj.property_name_has_side_effects = 10;
/* incorrect */
obj.propertyNameHasSideEffects = 10;
/* correct */
obj.set_property_name_has_side_effects(10);
```
## JavaScript Properties
Do not use JavaScript's getter and setter syntax if your code has side effects.
For example, the code `foo.bar = 10;` should not do anything other than set `foo.bar` to `10`. If the setting has side effects use a setter method:
```js
function setBar(value) {
this._bar = value;
}
```
The same logic applies to getters.
In practice, this means getters are only used to make internal properties public or for creating static properties on the object.
```js
class Demo {
get bar() {
return this._bar;
}
/* and */
static get bar() {
return 'bar?';
}
}
const demo = new Demo();
let staticBar = Demo.bar;
let bar = demo.bar;
```
## `this` in closures
`this` will not be captured in a closure; `this` is relative to how the closure is invoked, not to the value of this where the closure is created, because `this` is a keyword with a value passed in at function invocation time, it is not a variable that can be captured in closures.
To solve this, use `Function.prototype.bind()` or fat arrow functions:
To solve this, use [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) or `Function.prototype.bind()`:
```js
let closure = function() { this._fnorbate() }.bind(this);
let closure = () => {
this._fn();
};
// or
let closure = () => { this._fnorbate(); };
let closure = function() {
this._fn();
}.bind(this);
```
A more realistic example would be connecting to a signal on a
method of a prototype:
An example of this would be connecting to a signal on a GObject:
```js
MyPrototype = {
_init : function() {
fnorb.connect('frobate', this._onFnorbFrobate.bind(this));
},
_onFnorbFrobate : function(fnorb) {
this._updateFnorb();
},
};
const { Gtk } = imports.gi;
const button = new Gtk.Button({ label: 'Click Me!' });
button.connect('clicked', () => {
/* do something */
});
```
## Object literal syntax ##
......@@ -121,41 +235,84 @@ If your usage of an object is like an object, then you're defining "member varia
If your usage of an object is like a hash table (and thus conceptually the keys can have special chars in them), don't use quotes, but use brackets, `{ bar: 42 }`, `foo['bar']`.
## Variable naming ##
## Whitespace
- We use javaStyle variable names, with CamelCase for type names and lowerCamelCase for variable and method names. However, when calling a C method with underscore-based names via introspection, we just keep them looking as they do in C for simplicity.
- Private variables, whether object member variables or module-scoped variables, should begin with `_`.
- True global variables (in the global or 'window' object) should be avoided whenever possible. If you do create them, the variable name should have a namespace in it, like `BigFoo`
- When you assign a module to an alias to avoid typing `imports.foo.bar` all the time, the alias should be `const TitleCase` so `const Bar = imports.foo.bar;`
- If you need to name a variable something weird to avoid a namespace collision, add a trailing `_` (not leading, leading `_` means private).
- For GObject constructors, always use the `lowerCamelCase` style for property names instead of dashes or underscores.
## Whitespace ##
### General Rules
* 4-space indentation (the Java style)
* No trailing whitespace.
* No tabs.
* If you `chmod +x .git/hooks/pre-commit` it will not let you commit with messed-up whitespace (well, it doesn't catch tabs. turn off tabs in your text editor.)
## JavaScript attributes ##
### No whitespace when calling a function.
*eslint: func-call-spacing*
Don't use the getter/setter syntax when getting and setting has side effects, that is, the code:
```js
foo.bar = 10;
/* very incorrect */
fn
();
/* incorrect */
fn ();
/* perfect */
fn();
```
should not do anything other than save "10" as a property of `foo`. It's obfuscated otherwise; if the setting has side effects, it's better if it looks like a method.
In practice this means the only use of attributes is to create read-only properties:
### No spaces between brackets and elements.
*eslint: array-bracket-spacing*
```js
get bar() {
return this._bar;
}
/* incorrect */
let arr = [ 1, 2, 3 ];
/* correct */
let arr = [1, 2, 3];
```
### Spacing in a function signature.
*eslint: space-before-function-paren, space-before-blocks*
```js
```
If the property requires a setter, or if getting it has side effects, methods are probably clearer.
### Enforce spacing between keys and values in object literal properties.
*eslint: key-spacing*
```js
/* incorrect */
const objA = { foo:42 };
const objB = { foo : 42 };
const objC = { foo :42 };
/* correct */
var obj = { foo: 42 };
/* incorrect */
const a = function (){};
const b = function (){};
const c = function(){};
/* correct */
const d = function() {};
const e = function a() {};
/* incorrect */
const arr = [ 1, 2, 3 ];
log(arr[ 0 ]);
/* correct */
const foo = [1, 2, 3];
log(foo[0]);
```
[1] http://developer.mozilla.org/en/docs/index.php?title=New_in_JavaScript_1.7&printable=yes#Block_scope_with_let
### Use spaces inside curly braces.
*eslint: object-curly-spacing*
## Tips
```js
/* incorrect*
const foo = {clark: 'kent'};
- If using Emacs, try js2-mode. It functions as a "lint" by highlighting missing semicolons and the like.
\ No newline at end of file
/* correct */
const foo = { clark: 'kent' };
```