Commit 46f1cb0c authored by Rafael Ostertag's avatar Rafael Ostertag
Browse files

Merge branch 'raos_work' into raos_merge_branch

Merged JavaScript sources.

Conflicts:
	.gitignore
	qfq.ini
parents 7b18a4b4 bbdc2f08
......@@ -5,10 +5,14 @@
/packages
/bower_components
# Created by .ignore support plugin (hsz.mobi)
/.bowerpackages
/.npmpackages
/.phpdocinstall
/js
/qfq.flowchart.dia.autosave
/extension/qfq.ini
/support
/extension/Resources/Public/fonts
/extension/Resources/Public/JavaScript
/extension/Resources/Public/Css
/doc/jsdoc
......@@ -84,14 +84,49 @@ module.exports = function (grunt) {
}
]
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n',
},
build: {
src: ['js/<%= pkg.name %>.debug.js'],
dest: typo3_js + '<%= pkg.name %>.min.js'
}
},
jshint: {
all: [
'javascript/src/*.js'
]
},
concat: {
debug: {
src: [ 'javascript/src/*.js'],
dest: 'js/<%= pkg.name %>.debug.js'
}
},
watch: {
scripts: {
files: [
'javascript/src/*.js'
],
tasks: [ 'default' ],
options: {
spawn: true
}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
// Default task(s).
grunt.registerTask('default', ['copy']);
grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'copy']);
};
\ No newline at end of file
{
"source": {
"include": [
"javascript/src/"
],
"includePattern": ".+\\.js"
},
"plugins": [
"plugins/markdown"
],
"opts": {
"destination": "doc/jsdoc",
"private": true
}
}
PHPDOC ?= support/pear/phpdoc
JSDOC ?= jsdoc
doc: phpdoc jsdoc
support:
mkdir support
bootstrap: .phpdocinstall
bootstrap: .phpdocinstall .npmpackages .bowerpackages
npm install
bower install
grunt default
jsdoc: .npmpackages
$(JSDOC) -c JSDocConf.json
phpdoc: .phpdocinstall
$(PHPDOC)
......@@ -17,3 +23,14 @@ phpdoc: .phpdocinstall
pear -c "`pwd`/support/pear.config" channel-discover pear.phpdoc.org
pear -c "`pwd`/support/pear.config" install phpdoc/phpDocumentor
touch $@
.npmpackages: package.json
npm ls -g grunt-cli 2>/dev/null || { echo "Please install grunt-cli npm package using 'npm install -g grunt-cli'" 1>&2 ; exit 1; }
npm ls -g jsdoc 2>/dev/null || { echo "Please install jsdoc npm package using 'npm install -g jsdoc'" 1>&2 ; exit 1; }
npm ls -g bower 2>/dev/null || { echo "Please install bower npm package using 'npm install -g bower'" 1>&2 ; exit 1; }
npm install
touch $@
.bowerpackages: bower.json
bower --silent install
touch $@
/**
* @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
*/
/* global $ */
/* global console */
if (!QfqNS) {
var QfqNS = {};
}
(function (n) {
'use strict';
/**
* Tab Constructor.
*
* @param {string} tabId HTML id of the element having `nav` and `nav-tabs` classes
* @constructor
*/
n.BSTabs = function (tabId) {
this.tabId = tabId;
this._tabContainerLinkSelector = '#' + this.tabId + ' a[data-toggle="tab"]';
this._tabActiveSelector = '#' + this.tabId + ' .active a[data-toggle="tab"]';
this.tabs = {};
this.currentTab = this.getActiveTabFromDOM();
this.userTabShowHandlers = [];
// Fill this.tabs
this.fillTabInformation();
// Enable update of current tab field
this.installTabHandlers();
};
/**
* Get active tab from DOM
*
* @private
*/
n.BSTabs.prototype.getActiveTabFromDOM = function () {
var activeTabAnchors = $(this._tabActiveSelector);
if (activeTabAnchors.length < 1) {
// This could be due to the DOM not fully loaded. If that's really the case, then the active tab
// attribute should be set by the show.bs.tab handler
return null;
}
return activeTabAnchors[0].hash.slice(1);
};
/**
* Fill tabs object.
*
* Fill the tabs object using the tab HTML id as attribute name
*
* @private
*/
n.BSTabs.prototype.fillTabInformation = function () {
var tabLinks = $(this._tabContainerLinkSelector);
if ($(tabLinks).length === 0) {
throw new Error("Unable to find a BootStrap container matching: " + this._tabContainerLinkSelector);
}
var that = this;
tabLinks.each(function (index, element) {
if (element.hash !== "") {
var tabId = element.hash.slice(1);
that.tabs[tabId] = {
index: index,
element: element
};
}
}
);
};
/**
* @private
*/
n.BSTabs.prototype.installTabHandlers = function () {
$(this._tabContainerLinkSelector)
.on('show.bs.tab', this.tabShowHandler.bind(this));
};
/**
* Tab Show handler.
*
* Sets this.currentTab to the clicked tab and calls all registered tab click handlers.
*
* @private
* @param event
*/
n.BSTabs.prototype.tabShowHandler = function (event) {
QfqNS.Log.debug('Enter: BSTabs.tabShowHandler()');
this.currentTab = event.target.hash.slice(1);
var that = this;
QfqNS.Log.debug("BSTabs.tabShowHandler(): invoke user handler(s)");
this.userTabShowHandlers.forEach(function (handler) {
handler(that);
});
QfqNS.Log.debug('Exit: BSTabs.tabShowHandler()');
};
/**
* Get all tab IDs.
*
* @returns {Array}
*
* @public
*/
n.BSTabs.prototype.getTabIds = function () {
var tabIds = [];
for (var tabId in this.tabs) {
if (this.tabs.hasOwnProperty(tabId)) {
tabIds.push(tabId);
}
}
return tabIds;
};
/**
*
* @returns {Array}
*
* @public
*/
n.BSTabs.prototype.getTabAnchors = function () {
var tabLinks = [];
for (var tabId in this.tabs) {
if (this.tabs.hasOwnProperty(tabId)) {
tabLinks.push(this.tabs[tabId].element);
}
}
return tabLinks;
};
/**
* Activate a given tab.
*
* @param {string} tabId Id of the tab to activate
*
*/
n.BSTabs.prototype.activateTab = function (tabId) {
if (!this.tabs[tabId]) {
console.error("Unable to find tab with id: " + tabId);
return false;
}
$(this.tabs[tabId].element).tab('show');
return true;
};
n.BSTabs.prototype.getCurrentTab = function () {
return this.currentTab;
};
/**
* Add tab show handler.
*
* @param {function} handler handler function. `this` will be passed as first and only argument to the handler.
*/
n.BSTabs.prototype.addTabShowHandler = function (handler) {
if (typeof handler !== "function") {
throw new Error("Tab show handler must be function");
}
this.userTabShowHandlers.push(handler);
};
n.BSTabs.prototype.getTabName = function (tabId) {
if (!this.tabs[tabId]) {
console.error("Unable to find tab with id: " + tabId);
return null;
}
return $(this.tabs[tabId].element).text().trim();
};
n.BSTabs.prototype.getActiveTab = function () {
return this.currentTab;
};
})(QfqNS);
\ No newline at end of file
/**
* @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
*/
/* global console */
if (!QfqNS) {
var QfqNS = {};
}
(function (n) {
'use strict';
n.Log = {
level: 3,
message: function (msg) {
if (this.level <= 0) {
console.log('[message] ' + msg);
}
},
debug: function (msg) {
if (this.level <= 1) {
console.log('[debug] ' + msg);
}
},
warning: function (msg) {
if (this.level <= 2) {
console.log('[warning] ' + msg);
}
},
error: function (msg) {
if (this.level <= 3) {
console.log('[error] ' + msg);
}
}
};
})(QfqNS);
\ No newline at end of file
/**
* @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
*/
if (!QfqNS) {
var QfqNS = {};
}
(function (n) {
'use strict';
n.PageState = function () {
this.pageState = location.hash.slice(1);
this.inPoppingHandler = false;
this.userPopStateHandlers = [];
window.addEventListener("popstate", this.popStateHandler.bind(this));
};
/**
*
* @param event
*
* @private
*/
n.PageState.prototype.popStateHandler = function (event) {
QfqNS.Log.debug("Enter: PageState.popStateHandler()");
this.inPoppingHandler = true;
this.pageState = location.hash.slice(1);
QfqNS.Log.debug("PageState.popStateHandler(): invoke user pop state handler(s)");
this.userPopStateHandlers.forEach(function (handler) {
handler(this.pageState);
}, this);
this.inPoppingHandler = false;
QfqNS.Log.debug("Exit: PageState.popStateHandler()");
};
n.PageState.prototype.getPageState = function () {
return this.pageState;
};
n.PageState.prototype.setPageState = function (state) {
if (state.startsWith('#')) {
this.pageState = state.slice(1);
window.history.replaceState(null, null, state);
} else {
this.pageState = state;
window.history.replaceState(null, null, '#' + state);
}
};
n.PageState.prototype.newPageState = function (state) {
if (state.startsWith('#')) {
this.pageState = state.slice(1);
window.history.pushState(null, null, state);
} else {
this.pageState = state;
window.history.pushState(null, null, '#' + state);
}
};
n.PageState.prototype.addStateActivationHandler = function (handler) {
if (typeof handler !== "function") {
throw new Error("PageState.addStateActivationHandler(): argument must be function.");
}
this.userPopStateHandlers.push(handler);
};
})(QfqNS);
\ No newline at end of file
/**
* @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
*/
if (!QfqNS) {
var QfqNS = {};
}
(function (n) {
'use strict';
n.PageTitle = {
set: function (title) {
document.title = title;
},
get: function () {
return document.title;
},
setSubTitle: function (subTitle) {
var currentTitle = this.get();
var subtitleStrippedOff = currentTitle.replace(/ - (.*)$/, '');
document.title = subtitleStrippedOff + " - (" + subTitle + ")";
}
};
})(QfqNS);
\ No newline at end of file
/**
* @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
*/
/* global $ */
/* global console */
if (!QfqNS) {
var QfqNS = {};
}
(function (n) {
'use strict';
n.QfqPage = function (settings) {
this.settings = $.extend(
{
tabsId: "qfqTabs",
pageState: new QfqNS.PageState()
}, settings
);
this.bsTabs = new QfqNS.BSTabs(this.settings.tabsId);
if (this.settings.pageState.getPageState() !== "") {
this.bsTabs.activateTab(this.settings.pageState.getPageState());
}
this.bsTabs.addTabShowHandler(this.tabShowHandler.bind(this));
this.settings.pageState.addStateActivationHandler(this.popStateHandler.bind(this));
};
n.QfqPage.prototype.tabShowHandler = function (bsTabs) {
// tabShowHandler will be called every time the tab will be shown, regardless of whether or not this happens
// because of BSTabs.activateTab() or user interaction.
//
// Therefore, we have to make sure, that tabShowHandler() does not save the page state while we're restoring
// a previous state, i.e. we're called because of the popStateHandler() below.
if (this.settings.pageState.inPoppingHandler) {
QfqNS.Log.debug("Prematurely terminating QfqPage.tabShowHandler(): called while due to page state" +
" restoration.");
return;
}
var currentTabId = bsTabs.getCurrentTab();
QfqNS.Log.debug('Saving state: ' + currentTabId);
this.settings.pageState.newPageState(currentTabId);
QfqNS.PageTitle.setSubTitle(bsTabs.getTabName(currentTabId));
};
n.QfqPage.prototype.popStateHandler = function (state) {
this.bsTabs.activateTab(state);
};
})(QfqNS);
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="../packages/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="../packages/bootstrap/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="../packages/jqwidgets/css/jqx.base.css">
<link rel="stylesheet" href="../packages/jqwidgets/css/jqx.darkblue.css">
<title>Navstate Mock</title>
</head>
<body>
<section class="container-fluid">
<header class="page-header">
<h1>Keep Track of Navigation State</h1>
</header>
<nav>
<ul id="qfqTabs" class="nav nav-pills" role="tablist">
<li role="presentation" class="active"><a href="#tab1" data-toggle="tab">Tab 1</a>
</li>
<li role="presentation"><a href="#tab2" data-toggle="tab">Tab 2</a>
</li>
<li role="presentation"><a href="#tab3" data-toggle="tab">Tab 3</a>
</li>
</ul>
</nav>
<div class="row">
<div class="col-sm-12">
<a href="http://qfq.math.uzh.ch/raos/qfq-jqw/mockup/first.html">Up and away</a>
</div>
</div>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="tab1">
<div class="row">
<h1 class="col-sm-12">
Tab Pane 1
</h1>
</div>
<div class="row">
<p class="col-sm-8 col-sm-offset-2">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eu augue id lectus eleifend rutrum
eu in metus. Nunc aliquet fringilla quam, vitae congue lorem imperdiet ut. Lorem ipsum dolor sit
amet, consectetur adipiscing elit. Donec id dignissim libero, quis fringilla quam. Nam non convallis
mi. Phasellus vel tortor lacus. Phasellus nec enim non est venenatis bibendum. Ut sed tristique
ante, ut lacinia ligula. Curabitur interdum porta sem. Vivamus finibus metus massa, sed ullamcorper
nibh pulvinar nec. Nam in dolor tempus, eleifend mi sit amet, dictum diam. Aliquam lacus eros,
maximus nec lacinia sit amet, accumsan quis odio. Maecenas sodales libero in lorem consequat,
ullamcorper placerat diam ullamcorper. Nullam eu gravida nunc. Donec sit amet urna ullamcorper,
euismod nibh a, fermentum sapien.
</p>
</div>
<div class="row">
<p class="col-sm-8 col-sm-offset-2">
Ut eget odio sit amet ipsum congue efficitur. Ut pellentesque venenatis dolor a tempor. Morbi eu
velit non leo consequat maximus. Vestibulum bibendum, nisi vestibulum faucibus facilisis, lacus nunc
facilisis quam, non maximus tellus felis quis dolor. Fusce molestie nibh nec eros dapibus, ut auctor
augue condimentum. Proin dignissim dictum est ut luctus. Fusce consectetur velit id vehicula
condimentum.
</p>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab2">
<div class="row">
<h1 class="col-sm-12">
Tab Pane 2
</h1>
</div>
<div class="row">
<p class="col-sm-8 col-sm-offset-2">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eu augue id lectus eleifend rutrum
eu in metus. Nunc aliquet fringilla quam, vitae congue lorem imperdiet ut. Lorem ipsum dolor sit
amet, consectetur adipiscing elit. Donec id dignissim libero, quis fringilla quam. Nam non convallis
mi. Phasellus vel tortor lacus. Phasellus nec enim non est venenatis bibendum. Ut sed tristique
ante, ut lacinia ligula. Curabitur interdum porta sem. Vivamus finibus metus massa, sed ullamcorper
nibh pulvinar nec. Nam in dolor tempus, eleifend mi sit amet, dictum diam. Aliquam lacus eros,
maximus nec lacinia sit amet, accumsan quis odio. Maecenas sodales libero in lorem consequat,
ullamcorper placerat diam ullamcorper. Nullam eu gravida nunc. Donec sit amet urna ullamcorper,
euismod nibh a, fermentum sapien.
</p>
</div>
<div class="row">
<p class="col-sm-8 col-sm-offset-2">
Ut eget odio sit amet ipsum congue efficitur. Ut pellentesque venenatis dolor a tempor. Morbi eu
velit non leo consequat maximus. Vestibulum bibendum, nisi vestibulum faucibus facilisis, lacus nunc
facilisis quam, non maximus tellus felis quis dolor. Fusce molestie nibh nec eros dapibus, ut auctor
augue condimentum. Proin dignissim dictum est ut luctus. Fusce consectetur velit id vehicula
condimentum.
</p>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab3">
<div class="row">
<h1 class="col-sm-12">
Tab Pane 3
</h1>
</div>
<div class="row">
<p class="col-sm-8 col-sm-offset-2">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eu augue id lectus eleifend rutrum
eu in metus. Nunc aliquet fringilla quam, vitae congue lorem imperdiet ut. Lorem ipsum dolor sit
amet, consectetur adipiscing elit. Donec id dignissim libero, quis fringilla quam. Nam non convallis
mi. Phasellus vel tortor lacus. Phasellus nec enim non est venenatis bibendum. Ut sed tristique