Getting Started
Initially all dependencies should be loaded.
cd src/modules/Ui/assets
npm install && bower install
Important: After the installation finishes, the browser_components
directory should have the dependencies in asset/source
subdirectory and NOT in the assets
subdirectory. If this is not the case, the depencies should be manually copied into the proper assets/source
subdirectory.
File Structure
The entire source files are located in the directory ui/assets/source
.
The Grunt system creates optimised pictures and extensive CSS and Java Script and stores them in ui/assets/build
, where they are used by Assetic for further processing.
Grunt - Automation, Configration Management
Athene2/ui has two predefined Grunt tasks:
grunt dev
:
- Executes a
watch
command on Sass, JS and picture files
- Makes the Sass files into uncompressed CSS with comments
- Creates vendor prefixes for CSS3, for instance -webkit-transition
- Puts all the requirejs modules into a
scripts.js
- Checks the Java Script with jshint
grunt build
:
This is the same as 'grunt dev' with the following additions:
- shrinks files for CSS / Java Script (styles.min.css / scripts.min.js)
- Creates a 'moderizr' custom build based on all CSS/JS files
Third Party Frameworks and Libraries
See also 'bower.json'
Linting
All JavaScript files are "linted" by grunt using jshint.
JavaScript Module
All JavaScript code is developed using the AMD module system requirejs
.
/*global define*/
define('myModule', ['jquery'], function ($) {
"use strict";
var MyModule = function () {...};
return MyModule;
});
(see http://requirejs.org/)
For modules to be globally available, they have to be registered in the main configuration file main.js
.
require.config({
paths: {
"myModule": "modules/my_module"
}
});
The modules or components are available for other modules:
define("ATHENE2", ['jquery', 'referrer_history', 'my_module'], function ($, ReferrerHistory, MyModule) {
"use strict";
var myInstance = new MyModule();
});
Athene2
source/scripts/ATHENE2.js
Initializes nearly everything.
Methods:
initialize($context)
: initializes jQuery and DOM manipulation in given $context
(jQuery object)
libs/cache
cache
Client side key-value storage that uses localStorage
or jQuery.cookie
.
var myCache = new Cache('my-cache-id');
myCache.memorize(obj)
: saves the given object
myCache.remember()
: restores saved data or returns null
myCache.forget()
: clears the cache
libs/easing
Extends jQuery
s easing functions
libs/eventscope
Function that gives any object or Function custom events.
var myObject = eventScope({});
myObject.addEventListener('event-name', callback);
: Adds callback
as a listener to a event-name
event
myObject.trigger('event-name', some, values)
: Triggers formerly added callback
function
myObject.removeEventListener('event-name', callback);
Removes callback
as a listener
libs/polyfills
A place to put browser polyfills. Currently only requestAnimationFrame.
modules/serlo_ajax_overlay
See Components
modules/serlo_common
common
Contains many helper methods and static variables:
Common.log
: wrapper for console.log
Common.KeyCode
: for simpler key-detection in onKey
eventScope
Common.CarbonCopy(element)
: copies arrays and objects
Common.sortArrayByObjectKey(key, array, ascending)
: sorts an array that contains objects.
Common.findObjectByKey
: searches an array or object containing objects
Common.genericError
: Global error handling (logs only if console.log is available)
Common.memoize
: memoizes a function
Common.expr
: simple wrapper for isDefined && isDefined()
calls, to satisfy jshint.
Common.trim
: trims Strings
Common.setInterval
: window.setInterval interface that uses requestAnimationFrame
if available
Common.clearInterval
: clears intervals set by Common.setInterval
modules/serlo_content
This component is used to initialize DOM parts. Content.init
is always called, if some content parts change.
Content.init($context)
: Calls all formerly added callback functions in context of DOM object $context
Content.add(callback)
: Adds a callback that.
Example:
// All links should call alert!
Content.add(function ($context) {
// this callback gets called, whenever there is new HTML
// added to the main page. $context is the new HTML
$('a', $context).click(function () {
alert('you clicked a link');
});
})
modules/serlo_i18n
'Grunt' generates this component! It contains all strings that are needed for internationalization.
To add or remove strings, edit a translation file in /src/assets/lang/
.
modules/serlo_injections
This component creates a jQuery Plugin.
It parses the DOM for injections, loads them and decides how to treat them (Image/HTML/GeoGebra).
modules/serlo_layout
This component makes sure the collapsable side navigation and context bar are clickable.
modules/serlo_modals
This module creates a jQuery Plugin and provides a small API.
See Components.
modules/serlo_referrer_history
Stores the users last visited pages.
This runs by its own.
ReferrerHistory.isInHistory(path)
: Checks if given path
has been visited
ReferrerHistory.getRage(n)
: returns the last n
visited paths
ReferrerHistory.getOne(i)
: returns the path on index i
ReferrerHistory.getAll()
: returns the complete serlo_referrer_history
modules/serlo_router
Router.navigate(url)
: sends the user to given url
Router.post(path, params, method)
: performs a POST request
Router.reload()
: reloads the browser
modules/serlo_search
Everything visually related to the Quicksearch.
modules/serlo_side_navigation
Everything visualy related to the side navigation.
modules/serlo_sortable_list
This ia a wrapper for jQuery Nestable.
Performs POST request to save new list order.
modules/serlo_spoiler
This component creates a jQuery Plugin
and handles slideToggle
s for content spoilers.
modules/supporter
Performs simple checks on the browser API.
Notifies the user if an API is not available.
Supporter.add('APIName')
: Adds APIName
to the list of variables that must be available in window
.
Supporter.add(callback)
: Adds callback
to the list of functions that will be checked on Supporter.check()
.
If callback
returns a String
, that string will cause a notification.
Supporter.check()
: Checks all formerly added methods and strings.
modules/serlo_system_notification
This module generates and communicates notifications:
SystemNotification.notify(message, status, html, uniqueID);
: Creates a notification.
SystemNotification.error(message)
: Shortcut for error messages.
// your module
define(['system_notification'], function (SystemNotification) {
SystemNotification.notify('The force is strong with you');
// or with specified status-level and html set to true
SystemNotification.notify('The force is <strong>strong</strong> with you', 'success', true);
});
Available status levels:
- info (default)
- success
- warning
- danger
When a plugin triggers many notifications, it is adviseable to add uniqueID
to the method call:
SystemNotification.notify('You typed the wrong answer <strong>again</strong>!', 'warning', true, 'wrong-answer');
modules/serlo_timeago
This creates a jQuery Plugin that automatically updates time fields.
modules/translator
Enables interface translation:
// your module
define(['translator'], function (t) {
"use strict";
// normal string
t('Hello'); // => 'Hallo'
// placeholders
t('Hello %s', 'Athene'); // => 'Hallo Athene'
// numbers
t('Since %d days', 2); // => 'Seit 2 Tagen'
// combination
t('Hello %s, you have %d new messages', 'Athene', 3); // => 'Hallo Athene, du hast 3 neue Nachrichten'
});
At present there is no No singular/plural support. (contribute!)
Updating Localization Files
The grunt
task language-update
parses all JavaScript files for t(..)
calls and updates all lang.json files in src/assets/lang/
.
Ready to use functionalities
Overlay
Alle Links mit der Klasse .ajax-content
öffnen sich in einem Overlay. Wenn sich im geladenen Inhalt wieder .ajax-content
Links befinden öffnen sich diese bei Klick im selben Overlay als neues Tab.
All links with the class '.ajax.content' open in an overlay. If the link contains '.ajax-content' nested links, they open with a click in the same overlay as a new tab.
All available options can be passed to the module ATHENE2 during the AjaxOverlay initiation / instantiation .
Alle verfügbaren Optionen können im Modul ATHENE2
während der Instanzierung des AjaxOverlays übergeben werden:
{
// die Klasse auf die das Overlay angewandt werden soll
// The class that is used by the overlay
linkClass: 'ajax-content',
// Elemente die das Overlay schließen
// Elements that close the overlay
closeClass: 'close-overlay',
// Standard Context für das initialisieren der .linkClass
// Standard context for the '.linkclass' initialisation
context: 'body',
// active body class
overlayActiveClass: 'ajax-content-active',
// der Inhalt Selector, nach dem in per ajax geladenen Inhalt gesucht wird
// The inhalt selector in which ajax loaded content searches
ajaxContentSelector: '#content-container',
// der Selector der dem Tab den Titel gibt
// Selector that give titles tabs
titleSelector: '#pagetitle',
// Klasse für das aktive Tab
// Class for the active tab
activeTabClass: 'active',
// Wie viele Tabs maximal dargestellt werden sollen
// Defines the maximum number of tabs to be displayed
tabLimit: 5,
// Callbacks
on: {
// Wenn neuer Inhalt geladen wurde
// After new content loads
contentLoaded: function (AjaxOverlayInstance) {
// 'this' is the AjaxPage instance
},
// Wenn eine AjaxPage (tab) geöffnet wurde
// After an AjaxPage opens
contentOpened: function (AjaxOverlayInstance) {
// 'this' is the AjaxPage instance
},
// Wenn ein Ajax Fehler auftritt
// After an Ajax error occurs
error: function () {
// 'this' is the AjaxOverlay instance,
// arguments are all the arguments from jQuery.ajax.error
},
// Bevor das Overlay geschlossen wird
// Before the Overlay closes
beforeClose: function () {
// 'this' is the AjaxOverlay instance
},
// Nachdem das Overlay geschlossen wurde
// After the Overlay closed
afterClose: function () {
// 'this' is the AjaxOverlay instance
},
// Bevor das Overlay geöffnet wird
// Before the Overlay opens
beforeOpen: function () {
// 'this' is the AjaxOverlay instance
},
// Nachdem das Overlay geöffnet wurde
// After the Overlay opens
afterOpen: function () {
// gets called right after the AjaxOverlay has been opened
// 'this' is the AjaxOverlay instance
}
}
}
Modals
Confirm, Alert und Notify Modals können durch bestimmte HTML Klassen automatisch auf Links und Buttons gesetzt werden:
Confirm, Alert und Notify Modals use certain HTML classes to automatically set links and buttons
Optionen:
Options
data-type
Attribut: primary
(default), success
, warning
, info
, danger
data-title
Attribut (optional): Titel des Modals
data-label
Attribut (optional): Titel des Okay Buttons
data-cancel
Attribut (optional): "false", wenn kein Close Button dargestellt werden soll
<button class="dialog" href="/some/action" data-content="Do you really want to delete this item?" data-title="Heads up!" data-type="danger">Delete</button>
Ein Modal kann auch per Javascript erstellt werden:
// Modal.show(options[, uid]);
Modal.show({
type: 'primary' || 'success' || 'warning' || 'info'
title: 'Title', // Titel des Modals
content: '', // Inhalt des Modals
href: '' // target url for okay button (optional),
cancel: true, // (optional)
label: 'Okay' // (optional)
});
Datepicker
Ein Datepicker lässt sich durch folgendes Markup initialisieren:
A Datapicker is initialised using the following markup:
<input type="text" class="datepicker form-control" />
Eine Daterange wie folgt:
A data range as follows:
<div class="input-daterange input-group">
<input type="text" class="form-control" name="start" />
<span class="input-group-addon">to</span>
<input type="text" class="form-control" name="end" />
</div>
(See https://github.com/eternicode/bootstrap-datepicker)
TimeAgo Felder
Um ein Datum im "Vor x"-Format darzustellen, reicht es aus, einem Tag die Klasse .timeago
und als title
Attribut ein valides Datum zu geben:
To display a date in "days ago" format, use Day in the '.timeago' class and set the 'title' attribute using a valid date as follows:
<span class="timeago" title="Mon Oct 20 2013 12:25:20 GMT+0200 (CEST)">21.10.2013</span>
Wird automatisch zu (etwas ähnlichem wie):
There is an automatic conversion to (something like) as follows:
<span class="timeago" title="20.10.2013">Vor zwei Tagen</span>
Sortable List
Sortierbare Listen sind durch das jQuery Modul Nestable ermöglicht.
Using the jQuery Modul Nestable to create sortable lists as follows:
List Options
data-action
Attribute (required): the URL to make the sorted list persistent
data-depth
Attribute: The maximum depth in a nested list Die maximale tiefe einer Verschachtelten Liste (default: 0)
data-active
: When set to 'false' an event is needed to activate the sort capability (default: 'true') Wenn auf false
gesetzt, muss der Benutzer das Sortieren erst aktivieren (default: true
)
Item Option
data-id
Attribute (required): The content ID die ID des jeweiligen Inhaltes
Example Markup:
<!-- das Attribut data-action enthält die URL
über die die neue Sortierung gespeichert werden kann -->
<!-- the attribute data-action contains the URL
that points to the place where the new sort will/can be made persistent -->
<div class="sortable" data-action="/save/my/sort" data-depth="5" data-active="false">
<div class="sortable-actions">
<!-- (Optional) Button um das Sortieren zu aktivieren -->
<!-- Nur wichtig wenn data-active="false" -->
<!-- (Optional) Button to activate the sort -->
<!-- only important when data-active="false" -->
<button class="btn btn-success sortable-activate-action">
Sort Sortieren
</button>
<!-- ein Link oder Button, der die aktion "Speichern" auslöst -->
<!-- wichtig sind die Klassen .sortable-save-action und .is-hidden -->
<!-- a Link or Button, that initiates the "Save" action -->
<!-- in this context the classes '.sortable-save-action' and '.is-hidden' are important -->
<button class="btn btn-success sortable-save-action is-hidden">
Save list Reihenfolge speichern
</button>
<!-- Ein Button um alle Änderungen rückgängig zu machen -->
<button class="btn btn-success sortable-abort-action is-hidden">
Interupt Abbrechen
</button>
</div>
<ol class="sortable-list">
<!-- das data-id Attribut enthält die ID des jeweiligen Inhaltes -->
<!-- the data-id attribute contains the ID for the specific content -->
<li class="sortable-item" data-id="1">
<!-- das element, das dem Benutzer das Draggen erlaubt -->
<!-- the element that permits dragging -->
<span class="sortable-handle"></span>
<div class="sortable-item-inner">
<!-- Inhalt des jeweiligen Items -->
<!-- Content of the specific sort item -->
Sortable Item No. 1!
</div>
<!-- weitere Verschachtelungen -->
<!-- further nesting -->
<ol class="sortable-list">
<li class="sortable-item" data-id="941">
...
</li>
</ol>
</li>
<li class="sortable-item" data-id="312">
...
</li>
</ol>
</div>
Das Handle kann entweder ein eigenes span
Element sein (siehe oben), oder mit dem gesamten Listen Inhalt verknüpft sein:
**The handle can either be a span
Element (see above), or bound with the entire list content:
...
<li class="sortable-item" data-id="1">
<div class="sortable-item-inner sortable-handle">
...
</div>
</li>
...
In beiden Fällen muss der .sortable-handle
allerdings ein direktes Child des .sortable-item
s sein.
In both cases .sortable-handle
should be a child of .sortable-item
.