Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
 * WikiEditor tools for CSS editing
 *
 * The WikiEditor-Tools-loader gadget will only load this when editing a
 * page with CSS content model. Users not editing such pages will never have
 * it loaded.
 *
 * Suggest new help items on the talk page.
 */

/* eslint-disable one-var  */

( function () {

	'strict';

	var makeHeadings = function ( hs ) {
		return hs.map( function ( h ) {
			return { text: h };
		} );
	};

	var makeRows = function ( hs, ents ) {
		return ents.map( function ( entry ) {
			var row = {};
			hs.forEach( function ( h, i ) {
				row[ h ] = { text: entry[ i ] };
			} );
			return row;
		} );
	};

	var makeTable = function ( label, headings, entries ) {
		var table = {
			layout: 'table',
			label: label,
			headings: makeHeadings( headings ),
			rows: makeRows( headings, entries )
		};
		return table;
	};

	var makeHelpPanel = function () {

		return {
			type: 'booklet',
			label: 'CSS Help',
			pages: {
				basics: makeTable(
					'CSS Basics',
					[ 'Term', 'Information' ],
					[
						[ 'Selectors',
							'Selectors are used to target any number of elements on the page. They can be used to select based on "class", "ID", the HTML tag type and other attributes.' ],
						[ 'Properties',
							'Properties are individual styles that can be applied to a "selected" element.' ],
						[ 'Rules',
							'A CSS rule applies a set of one or more properties to all the elements selected by one or more selectors. Rules have the form: <pre>Selector1, Selector2 {\n    Property1: Value1;\n    Property2: Value2;\n    ...\n}</pre>' ],
						[ 'Cascading',
							'The rules "cascade" (this is the C in CSS). A rule can be overridden by a rule that comes later, or a more <em>specific</em> rule. For example, <code>.classA .classB</code> is more specific than <code>.classB</code> so it "wins".' ],
						[ 'Classes',
							'Classes can apply to any number of elements. They are set with HTML <code>class</code> attribute: <code>&lt;span class="classA classB"&gt;. One element can have any number of classes. In the CSS rules, classes are denoted by a dot: <code>.classA</code>.' ],
						[ 'IDs',
							'IDs can be set on any element with the <code>id<code> attribute: <code>&lt;span id="some_id"&gt;</code>. Every ID should be unique on a page: no two elements should have the same ID. Each element can only have one ID.' ],
						[ 'Pseudo-classes',
							'Pseudo-classes are way for CSS to target elements based on various "implicit" properties of elements. For example, the first child element of another element can be selected with <code>:first-child</code>. They are denoted by a colon.' ]
					] ),
				selectors: makeTable(
					'Selectors',
					[ 'Selector', 'Description' ],
					[
						[ '.class',
							'Select all elements with this class.' ],
						[ '#id', 'Select the element with the given ID (there should be only one.' ],
						[ 'parent > child',
							'A > sign means an element matching "child" that is a <em>direct</em> child of an element matching "parent".' ],
						[ 'ancestor&nbsp;descendant',
							'A space means an element matching "descendant" that is a descendant of an element matching "ancestor", with any number of intervening layers.' ],
						[ ':nth-child(n)',
							'Pseudo-class that selects the given element that is the n\'th child (of any type) of its parent. Useful for cells in tables.' ],
						[ ':first-child,\n:last-child',
							'Pseudo-class that selects the first/last child of the parent element' ]
					] ),
				properties: makeTable(
					'Properties',
					[ 'Property', 'Description' ],
					[
						[ 'font-size', 'The size of the font, usually relative to the surrounding text.' ],
						[ 'font-variant', 'Set to <code>small-caps</code> for <span style="font-variant:small-caps;">small caps</span>, or <code>normal</code> to unset.' ],
						[ 'font-style', '<code>italic</code> or <code>normal</code>' ],
						[ 'font-weight', '<code>bold</code> or <code>normal</code>' ],
						[ 'text-align', 'Alignment of the text within a block element: <code>left</code>, <code>center</code> or <code>right</code>. <code>justify</code> can also be used, but should not be applied to a whole work.' ],
						[ 'text-align-last', 'Alignment of the <em>last</em> line of text within a block.' ],
						[ '<span style="white-space:nowrap;">text-decoration</span>', '<code style="text-decoration:underline;">underline</code>, <code style="text-decoration:line-through;">line-through</code>, <code style="text-decoration:overline;">overline</code>. Can be combined.' ],
						[ 'padding', 'Set an internal "buffer" of space within an element. If one value is given, all four side are the same. If two are given, it is top/bottom, then left/right. If four, it is top, right, bottom, left (TRBL)' ],
						[ 'margin', 'Similar to padding, but the "buffer" is outside the element\'s box' ],
						[ 'float', 'Remove an element from the normal page flow and "float" it <code>left</code> or <code>right</code>' ]
					] ),
				more: makeTable(
					'Learn more',
					[ 'Where', 'Information' ],
					[
						[ 'Wikisource', '<a href="/wiki/Help:Page styles">Help:Page styles</a>' ],
						[ 'W3Schools', '<a href="https://w3schools.com/cssref">W3Schools CSS Reference</a>' ],
						[ 'MDN Web Docs', '<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference">Mozilla CSS Reference</a>' ]
					] )
			}
		};
	};

	var addCssTemplates = function ( wikiEditor ) {

		var makeSelectorRule = function ( label, selector, peri, post ) {
			return {
				label: label,
				action: {
					type: 'encapsulate',
					options: {
						pre: selector,
						periMsg: peri,
						post: post
					}
				}
			};
		};

		var templates = {
			idRule: makeSelectorRule( '#id', '#', 'ID' ),
			classRule: makeSelectorRule( '.class', '.', 'CLASS_NAME' ),
			directChild: makeSelectorRule( '> direct child', '> ', 'CHILD' ),
			descendant: makeSelectorRule( 'descendant', ' ', 'DESCENDANT' ),
			nthChild: makeSelectorRule( ':nth-child', '.', 'CLASS_NAME', ':nth-child(1)' )
		};

		var makePropRule = function ( prop, val, post ) {
			return {
				label: prop + ': ' + val + ';',
				action: {
					type: 'encapsulate',
					options: {
						pre: prop + ': ' + val + ';',
						post: post
					}
				}
			};
		};

		var props = {
			bold: makePropRule( 'font-weight', 'bold' ),
			normal: makePropRule( 'font-weight', 'normal' ),
			italic: makePropRule( 'font-style', 'italic' ),
			sc: makePropRule( 'font-variant', 'small-caps' ),
			asc: makePropRule( 'font-variant', 'all-small-caps' ),
			tc: makePropRule( 'text-align', 'center' ),
			tr: makePropRule( 'text-align', 'right' )
		};

		wikiEditor( 'addToToolbar', {
			section: 'main',
			groups: {
				list: {
					tools: {
						templates: {
							label: 'Selectors',
							type: 'select',
							list: templates
						},
						props: {
							label: 'Common rules',
							type: 'select',
							list: props
						}
					}
				}
			}
		} );

		wikiEditor( 'addToToolbar', {
			sections: {
				info: makeHelpPanel()
			}
		} );
	};

	window.wsutils.wikiEditorConfigIncantation( addCssTemplates );

}() );