diff --git a/Gruntfile.js b/Gruntfile.js index 916d61c018dc755e3bed22cfe1535edd5b72e12a..2f8a257bc57de1d27a6ee2cb6e52c57c81795949 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -89,11 +89,7 @@ module.exports = function (grunt) { expand: true, dest: typo3_js, flatten: true - } - ] - }, - jquery_devel: { - files: [ + }, { cwd: 'bower_components/jquery/dist/', src: [ @@ -115,11 +111,7 @@ module.exports = function (grunt) { expand: true, dest: typo3_js, flatten: true - } - ] - }, - jquery_tablesorter_devel: { - files: [ + }, { cwd: 'bower_components/tablesorter/dist/js/', src: [ @@ -194,6 +186,28 @@ module.exports = function (grunt) { dest: 'css/' } ] + }, + eventEmitter: { + files: [ + { + cwd: 'bower_components/eventEmitter/', + src: [ + 'EventEmitter.min.js' + ], + expand: true, + dest: typo3_js, + flatten: true + }, + { + cwd: 'bower_components/eventEmitter/', + src: [ + 'EventEmitter.min.js' + ], + expand: true, + dest: 'js/', + flatten: true + } + ] } }, uglify: { @@ -285,6 +299,7 @@ module.exports = function (grunt) { vendor: [ 'js/jquery.min.js', 'js/bootstrap.min.js', + 'js/EventEmitter.min.js', 'js/jqx-all.js', 'js/qfq.debug.js' ], diff --git a/bower.json b/bower.json index 0628c19f7ec7eb4e2ba96f27eeb6659ef07bdee5..d59a7e79a7269e7cf9a90734e109e87824a4e083 100644 --- a/bower.json +++ b/bower.json @@ -19,6 +19,7 @@ "dependencies": { "bootstrap": "~3.3.6", "jqwidgets": "*", - "tablesorter": "jquery.tablesorter#^2.25.6" + "tablesorter": "jquery.tablesorter#^2.25.6", + "eventEmitter": "^4.3.0" } } diff --git a/javascript/src/Alert.js b/javascript/src/Alert.js index ac0ac28f2f8c9bed07fb78c0b92c62ec34348bfd..5958d9c6a5ae8d693cc21ab1c881522ca0848c17 100644 --- a/javascript/src/Alert.js +++ b/javascript/src/Alert.js @@ -3,10 +3,10 @@ */ /* global $ */ +/* global EventEmitter */ +/* @depend QfqEvents.js */ -if (!QfqNS) { - var QfqNS = {}; -} +var QfqNS = QfqNS || {}; (function (n) { 'use strict'; @@ -64,11 +64,15 @@ if (!QfqNS) { this.fadeOutDuration = 400; this.timerId = null; + this.eventEmitter = new EventEmitter(); + this.userOkButtonHandlers = new n.Helper.FunctionList(); this.userCancelButtonHandlers = new n.Helper.FunctionList(); this.userSaveButtonHandlers = new n.Helper.FunctionList(); }; + n.Alert.prototype.on = n.EventEmitter.onMixin; + /** * * @private @@ -255,19 +259,6 @@ if (!QfqNS) { }; - n.Alert.prototype.addOkButtonHandler = function (handler) { - this.userOkButtonHandlers.addFunction(handler); - }; - - n.Alert.prototype.addCancelButtonHandler = function (handler) { - this.userCancelButtonHandlers.addFunction(handler); - }; - - n.Alert.prototype.addSaveButtonHandler = function (handler) { - this.userSaveButtonHandlers.addFunction(handler); - }; - - /** * * @param handler @@ -276,7 +267,7 @@ if (!QfqNS) { */ n.Alert.prototype.okButtonHandler = function (handler) { this.removeAlert(); - this.userOkButtonHandlers.call(this); + this.eventEmitter.emitEvent('alert.ok', n.EventEmitter.makePayload(this, null)); }; /** @@ -287,7 +278,7 @@ if (!QfqNS) { */ n.Alert.prototype.saveButtonHandler = function (handler) { this.removeAlert(); - this.userSaveButtonHandlers.call(this); + this.eventEmitter.emitEvent('alert.save', n.EventEmitter.makePayload(this, null)); }; /** @@ -298,7 +289,7 @@ if (!QfqNS) { */ n.Alert.prototype.cancelButtonHandler = function (handler) { this.removeAlert(); - this.userCancelButtonHandlers.call(this); + this.eventEmitter.emitEvent('alert.cancel', n.EventEmitter.makePayload(this, null)); }; n.Alert.prototype.isShown = function () { diff --git a/javascript/src/BSTabs.js b/javascript/src/BSTabs.js index 75f8b56015fffc0bb7ad4bba104ebd4c3651b267..88a6b71b90dc9c7dc13b649549b292b048cd8b2f 100644 --- a/javascript/src/BSTabs.js +++ b/javascript/src/BSTabs.js @@ -4,10 +4,12 @@ /* global $ */ /* global console */ +/* global EventEmitter */ + +/* @depend QfqEvents.js */ + +var QfqNS = QfqNS || {}; -if (!QfqNS) { - var QfqNS = {}; -} (function (n) { 'use strict'; @@ -25,8 +27,7 @@ if (!QfqNS) { this._tabActiveSelector = '#' + this.tabId + ' .active a[data-toggle="tab"]'; this.tabs = {}; this.currentTab = this.getActiveTabFromDOM(); - this.userTabShowHandlers = new n.Helper.FunctionList(); - + this.eventEmitter = new EventEmitter(); // Fill this.tabs this.fillTabInformation(); @@ -35,6 +36,8 @@ if (!QfqNS) { this.installTabHandlers(); }; + n.BSTabs.prototype.on = n.EventEmitter.onMixin; + /** * Get active tab from DOM. * @@ -102,9 +105,8 @@ if (!QfqNS) { n.Log.debug('Enter: BSTabs.tabShowHandler()'); this.currentTab = event.target.hash.slice(1); - var that = this; n.Log.debug("BSTabs.tabShowHandler(): invoke user handler(s)"); - this.userTabShowHandlers.call(that); + this.eventEmitter.emitEvent('bootstrap.tab.shown', n.EventEmitter.makePayload(this, null)); n.Log.debug('Exit: BSTabs.tabShowHandler()'); }; @@ -162,15 +164,6 @@ if (!QfqNS) { 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) { - this.userTabShowHandlers.addFunction(handler); - }; - n.BSTabs.prototype.getTabName = function (tabId) { if (!this.tabs[tabId]) { console.error("Unable to find tab with id: " + tabId); diff --git a/javascript/src/Element/Checkbox.js b/javascript/src/Element/Checkbox.js index fee1ab3ff69b44cdda57f4cbe15f4532dcb003e4..ebd8c23f3bc7989c169a09d26aeb7313c401985c 100644 --- a/javascript/src/Element/Checkbox.js +++ b/javascript/src/Element/Checkbox.js @@ -4,13 +4,8 @@ /* @depend FormGroup.js */ -if (!QfqNS) { - var QfqNS = {}; -} - -if (!QfqNS.Element) { - QfqNS.Element = {}; -} +var QfqNS = QfqNS || {}; +QfqNS.Element = QfqNS.Element || {}; (function (n) { 'use strict'; diff --git a/javascript/src/Element/FormGroup.js b/javascript/src/Element/FormGroup.js index 1f120d658c4648aa1393c975faf4ca34d19ca595..0d893d32ecc5b83505009969b94c719a1889b3fc 100644 --- a/javascript/src/Element/FormGroup.js +++ b/javascript/src/Element/FormGroup.js @@ -2,13 +2,8 @@ * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> */ -if (!QfqNS) { - var QfqNS = {}; -} - -if (!QfqNS.Element) { - QfqNS.Element = {}; -} +var QfqNS = QfqNS || {}; +QfqNS.Element = QfqNS.Element || {}; (function (n) { 'use strict'; diff --git a/javascript/src/Element/NameSpaceFunctions.js b/javascript/src/Element/NameSpaceFunctions.js index 014d03a6846f14924ff9441f1532883f5f9a5375..b177344dae389e8ce27edfc4a45de2d562403d9d 100644 --- a/javascript/src/Element/NameSpaceFunctions.js +++ b/javascript/src/Element/NameSpaceFunctions.js @@ -4,13 +4,8 @@ /* global $ */ -if (!QfqNS) { - var QfqNS = {}; -} - -if (!QfqNS.Element) { - QfqNS.Element = {}; -} +var QfqNS = QfqNS || {}; +QfqNS.Element = QfqNS.Element || {}; (function (n) { 'use strict'; diff --git a/javascript/src/Element/Radio.js b/javascript/src/Element/Radio.js index 9f3e3688f4e3c820b7d89aadd65ee4c9af91444f..d449bf9281b66c6b34495b3e56bea8b3dd1472f6 100644 --- a/javascript/src/Element/Radio.js +++ b/javascript/src/Element/Radio.js @@ -2,13 +2,8 @@ * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> */ -if (!QfqNS) { - var QfqNS = {}; -} - -if (!QfqNS.Element) { - QfqNS.Element = {}; -} +var QfqNS = QfqNS || {}; +QfqNS.Element = QfqNS.Element || {}; (function (n) { 'use strict'; diff --git a/javascript/src/Element/Select.js b/javascript/src/Element/Select.js index 2c80615a98837f9f0654623b6010b2fbb2309016..e1968cb0041c90f5904d5c73c1219b467b30125d 100644 --- a/javascript/src/Element/Select.js +++ b/javascript/src/Element/Select.js @@ -3,14 +3,8 @@ */ /* global $ */ - -if (!QfqNS) { - var QfqNS = {}; -} - -if (!QfqNS.Element) { - QfqNS.Element = {}; -} +var QfqNS = QfqNS || {}; +QfqNS.Element = QfqNS.Element || {}; (function (n) { 'use strict'; diff --git a/javascript/src/Element/Text.js b/javascript/src/Element/Text.js index e94d1a69a3401c792a566a52d7b970c8b37909fc..61bc4dd40d80d3b5b4ac28d3c746eb4d86fde80b 100644 --- a/javascript/src/Element/Text.js +++ b/javascript/src/Element/Text.js @@ -2,13 +2,8 @@ * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> */ -if (!QfqNS) { - var QfqNS = {}; -} - -if (!QfqNS.Element) { - QfqNS.Element = {}; -} +var QfqNS = QfqNS || {}; +QfqNS.Element = QfqNS.Element || {}; (function (n) { 'use strict'; diff --git a/javascript/src/Element/data.js b/javascript/src/Element/data.js index 8719181f23aa99a4a34837be390762969ccd74ac..b86efcd68f5712328dcd05fcec15d94961f32209 100644 --- a/javascript/src/Element/data.js +++ b/javascript/src/Element/data.js @@ -2,14 +2,8 @@ * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> */ - -if (!QfqNS) { - var QfqNS = {}; -} - -if (!QfqNS.Element) { - QfqNS.Element = {}; -} +var QfqNS = QfqNS || {}; +QfqNS.Element = QfqNS.Element || {}; (function (n) { 'use strict'; diff --git a/javascript/src/FileUpload.js b/javascript/src/FileUpload.js index b7b5f6af2d6752dfe99b943b0fc846364724f08f..3a54530db2790494862fd6ce21683ee340c66251 100644 --- a/javascript/src/FileUpload.js +++ b/javascript/src/FileUpload.js @@ -3,10 +3,12 @@ */ /* global $ */ +/* global EventEmitter */ -if (!QfqNS) { - var QfqNS = {}; -} +/* @depend QfqEvents.js */ + + +var QfqNS = QfqNS || {}; (function (n) { 'use strict'; @@ -15,47 +17,12 @@ if (!QfqNS) { this.formSelector = formSelector; this.targetUrl = targetUrl; this.sip = sip; - - // TODO: Seriously, I'd like to have an event system. - this.fileUploadStartCallbacks = new n.Helper.FunctionList(); - this.fileUploadEndCallbacks = new n.Helper.FunctionList(); - this.fileUploadSuccessCallbacks = new n.Helper.FunctionList(); - this.fileUploadErrorCallbacks = new n.Helper.FunctionList(); + this.eventEmitter = new EventEmitter(); this.setupOnChangeHandler(); }; - /** - * @public - * @param handler - */ - n.FileUpload.prototype.addFileUploadStartHandler = function (handler) { - this.fileUploadStartCallbacks.addFunction(handler); - }; - - /** - * @public - * @param handler - */ - n.FileUpload.prototype.addFileUploadEndHandler = function (handler) { - this.fileUploadEndCallbacks.addFunction(handler); - }; - - /** - * @public - * @param handler - */ - n.FileUpload.prototype.addFileUploadSuccessHandler = function (handler) { - this.fileUploadSuccessCallbacks.addFunction(handler); - }; - - /** - * @public - * @param handler - */ - n.FileUpload.prototype.addFileUploadErrorHandler = function (handler) { - this.fileUploadErrorCallbacks.addFunction(handler); - }; + n.FileUpload.prototype.on = n.EventEmitter.onMixin; /** * @@ -70,7 +37,7 @@ if (!QfqNS) { * @param event */ n.FileUpload.prototype.performFileUpload = function (event) { - this.fileUploadStartCallbacks.call(event.target); + this.eventEmitter.emitEvent('fileupload.started', n.EventEmitter.makePayload(event.target, null)); var data = this.prepareData(event.target); @@ -111,8 +78,12 @@ if (!QfqNS) { */ n.FileUpload.prototype.ajaxSuccessHandler = function (uploadTriggeredBy, data, textStatus, jqXHR) { - this.fileUploadSuccessCallbacks.call(uploadTriggeredBy, data, textStatus); - this.fileUploadEndCallbacks.call(uploadTriggeredBy); + var eventData = n.EventEmitter.makePayload(uploadTriggeredBy, data, { + textStatus: textStatus, + jqXHR: jqXHR + }); + this.eventEmitter.emitEvent('fileupload.upload.successful', eventData); + this.eventEmitter.emitEvent('fileupload.ended', eventData); }; /** @@ -122,8 +93,13 @@ if (!QfqNS) { * @param errorThrown */ n.FileUpload.prototype.ajaxErrorHandler = function (uploadTriggeredBy, jqXHR, textStatus, errorThrown) { - this.fileUploadErrorCallbacks.call(uploadTriggeredBy, textStatus, errorThrown); - this.fileUploadEndCallbacks.call(uploadTriggeredBy); + var eventData = n.EventEmitter.makePayload(uploadTriggeredBy, null, { + textStatus: textStatus, + errorThrown: errorThrown, + jqXHR: jqXHR + }); + this.eventEmitter.emitEvent('fileupload.upload.failed', eventData); + this.eventEmitter.emitEvent('fileupload.ended', eventData); }; diff --git a/javascript/src/Form.js b/javascript/src/Form.js index 0899f180d3f727b0f70e69e58778ac17e10bc5e0..81245710ca93f3458b81cb8eea84ea5dbadfa31c 100644 --- a/javascript/src/Form.js +++ b/javascript/src/Form.js @@ -3,27 +3,23 @@ */ /* global $ */ +/* global EventEmitter */ +/* @depend QfqEvents.js */ -if (!QfqNS) { - var QfqNS = {}; -} +var QfqNS = QfqNS || {}; (function (n) { 'use strict'; n.Form = function (formId) { this.formId = formId; + this.eventEmitter = new EventEmitter(); if (!document.forms[this.formId]) { throw new Error("Form '" + formId + "' does not exist."); } this.formChanged = false; - this.userFormChangeHandlers = new QfqNS.Helper.FunctionList(); - this.userResetHandlers = new QfqNS.Helper.FunctionList(); - this.userSubmitSuccessHandlers = new QfqNS.Helper.FunctionList(); - this.userSubmitFailureHandlers = new QfqNS.Helper.FunctionList(); - this.$form = $(document.forms[this.formId]); this.$form.on("change", this.changeHandler.bind(this)); @@ -32,6 +28,8 @@ if (!QfqNS) { this.$form.find("input[type=text]").on("input paste", this.changeHandler.bind(this)); }; + n.Form.prototype.on = n.EventEmitter.onMixin; + /** * * @param event @@ -40,23 +38,8 @@ if (!QfqNS) { */ n.Form.prototype.changeHandler = function (event) { this.formChanged = true; - this.userFormChangeHandlers.call(this); - }; - - n.Form.prototype.addChangeHandler = function (callback) { - this.userFormChangeHandlers.addFunction(callback); - }; - - n.Form.prototype.addResetHandler = function (callback) { - this.userResetHandlers.addFunction(callback); - }; - - n.Form.prototype.addSubmitSuccessHandler = function (callback) { - this.userSubmitSuccessHandlers.addFunction(callback); - }; - - n.Form.prototype.addSubmitFailureHandler = function (callback) { - this.userSubmitFailureHandlers.addFunction(callback); + this.eventEmitter.emitEvent('form.changed', n.EventEmitter.makePayload(this, null)); + // REMOVE: this.userFormChangeHandlers.call(this); }; n.Form.prototype.getFormChanged = function () { @@ -65,7 +48,8 @@ if (!QfqNS) { n.Form.prototype.resetFormChanged = function () { this.formChanged = false; - this.userResetHandlers.call(this); + this.eventEmitter.emitEvent('form.reset', n.EventEmitter.makePayload(this, null)); + // REMOVE: this.userResetHandlers.call(this); }; n.Form.prototype.submitTo = function (to) { @@ -87,7 +71,11 @@ if (!QfqNS) { * @private */ n.Form.prototype.ajaxSuccessHandler = function (data, textStatus, jqXHR) { - this.userSubmitSuccessHandlers.call(this, data, textStatus); + this.eventEmitter.emitEvent('form.submit.successful', + n.EventEmitter.makePayload(this, data, { + textStatus: textStatus, + jqXHR: jqXHR + })); }; /** @@ -96,7 +84,12 @@ if (!QfqNS) { * @private */ n.Form.prototype.submitFailureHandler = function (jqXHR, textStatus, errorThrown) { - this.userSubmitFailureHandlers.call(this, textStatus, jqXHR, errorThrown); + this.eventEmitter.emitEvent('form.submit.failed', n.EventEmitter.makePayload(this, null, { + textStatus: textStatus, + errorThrown: errorThrown, + jqXHR: jqXHR + })); + // REMOVE: this.userSubmitFailureHandlers.call(this, textStatus, jqXHR, errorThrown); }; })(QfqNS); diff --git a/javascript/src/Helper/FunctionList.js b/javascript/src/Helper/FunctionList.js index 9a877641fbf84f242b29117bb3028557f44ee757..f0ccd20cb3907766636b03d0f5fee5123f8cb188 100644 --- a/javascript/src/Helper/FunctionList.js +++ b/javascript/src/Helper/FunctionList.js @@ -4,17 +4,16 @@ /* global $ */ -if (!QfqNS) { - var QfqNS = {}; -} - -if (!QfqNS.Helper) { - QfqNS.Helper = {}; -} +var QfqNS = QfqNS || {}; +QfqNS.Helper = QfqNS.Helper || {}; (function (n) { 'use strict'; + /** + * @deprecated + * @constructor + */ n.FunctionList = function () { this.functions = []; }; diff --git a/javascript/src/Helper/NameSpaceFunctions.js b/javascript/src/Helper/NameSpaceFunctions.js index 5f32b8adf60feab9f19dbad3838860b1e46777c5..e4fb0da622d2044a5d3394eea4fa93b4a9d8bb66 100644 --- a/javascript/src/Helper/NameSpaceFunctions.js +++ b/javascript/src/Helper/NameSpaceFunctions.js @@ -4,14 +4,8 @@ /* global $ */ -if (!QfqNS) { - var QfqNS = {}; -} - -if (!QfqNS.Helper) { - QfqNS.Helper = {}; -} - +var QfqNS = QfqNS || {}; +QfqNS.Helper = QfqNS.Helper || {}; (function (n) { 'use strict'; diff --git a/javascript/src/Log.js b/javascript/src/Log.js index ddd084a85b1e8c0e8605459e45620e367898e0b7..3361f5985c779124cf5001b3f2c858c256c539df 100644 --- a/javascript/src/Log.js +++ b/javascript/src/Log.js @@ -4,9 +4,7 @@ /* global console */ -if (!QfqNS) { - var QfqNS = {}; -} +var QfqNS = QfqNS || {}; (function (n) { 'use strict'; diff --git a/javascript/src/PageState.js b/javascript/src/PageState.js index 5138827efcb2fdce07b98dc6f1fe9283d5f92c40..52f371a38e09fafd4c4c8a955c41ea087f8d43de 100644 --- a/javascript/src/PageState.js +++ b/javascript/src/PageState.js @@ -2,9 +2,10 @@ * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> */ -if (!QfqNS) { - var QfqNS = {}; -} +/* @depend QfqEvents.js */ +/* global EventEmitter */ + +var QfqNS = QfqNS || {}; (function (n) { 'use strict'; @@ -14,10 +15,12 @@ if (!QfqNS) { this.pageState = location.hash.slice(1); this.data = null; this.inPoppingHandler = false; - this.userPopStateHandlers = new n.Helper.FunctionList(); + this.eventEmitter = new EventEmitter(); window.addEventListener("popstate", this.popStateHandler.bind(this)); }; + + n.PageState.prototype.on = n.EventEmitter.onMixin; /** * * @param event @@ -34,7 +37,7 @@ if (!QfqNS) { n.Log.debug("PageState.popStateHandler(): invoke user pop state handler(s)"); - this.userPopStateHandlers.call(this); + this.eventEmitter.emitEvent('pagestate.state.popped', n.EventEmitter.makePayload(this, null)); this.inPoppingHandler = false; n.Log.debug("Exit: PageState.popStateHandler()"); @@ -72,9 +75,4 @@ if (!QfqNS) { this.data = data; }; - n.PageState.prototype.addStateActivationHandler = function (handler) { - this.userPopStateHandlers.addFunction(handler); - }; - - })(QfqNS); \ No newline at end of file diff --git a/javascript/src/PageTitle.js b/javascript/src/PageTitle.js index 29e45b25f32a9705c7fbfa857401ff19d4aff21e..e5e4567b8d707ad52dc562e7039999838d3800ca 100644 --- a/javascript/src/PageTitle.js +++ b/javascript/src/PageTitle.js @@ -2,10 +2,7 @@ * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> */ - -if (!QfqNS) { - var QfqNS = {}; -} +var QfqNS = QfqNS || {}; (function (n) { 'use strict'; diff --git a/javascript/src/QfqEvents.js b/javascript/src/QfqEvents.js new file mode 100644 index 0000000000000000000000000000000000000000..26be83cc0395ee412c55c7f7b2a4ae27542f7cd3 --- /dev/null +++ b/javascript/src/QfqEvents.js @@ -0,0 +1,28 @@ +/** + * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> + */ + +/* global EventEmitter */ +/* global $ */ + +var QfqNS = QfqNS || {}; + +(function (n) { + 'use strict'; + + n.EventEmitter = { + makePayload: function (target, data, additionalArgs) { + return [$.extend({}, + typeof additionalArgs === "object" ? additionalArgs : null, + { + target: target, + data: data + } + )]; + }, + onMixin: function (event, func) { + this.eventEmitter.addListener(event, func); + } + }; + +})(QfqNS); \ No newline at end of file diff --git a/javascript/src/QfqForm.js b/javascript/src/QfqForm.js index fc69cead71e5974392477a76b37ae11b01c22bc5..5898b57d5f96ae543977cec87db6263f91df5342 100644 --- a/javascript/src/QfqForm.js +++ b/javascript/src/QfqForm.js @@ -3,14 +3,15 @@ */ /* global $ */ +/* global EventEmitter */ +/* @depend QfqEvents.js */ -if (!QfqNS) { - var QfqNS = {}; -} +var QfqNS = QfqNS || {}; (function (n) { 'use strict'; + // TODO: This object is getting too big. Start refactoring. n.QfqForm = function (formId, submitTo, deleteUrl, dataRefreshUrl, fileUploadTo) { this.formId = formId; this.submitTo = submitTo; @@ -21,14 +22,16 @@ if (!QfqNS) { // This is required when displaying validation messages, in to activate the tab, which has validation issues this.bsTabs = null; this.lastButtonPress = null; - this.destroyFormUserCallbacks = new n.Helper.FunctionList(); + this.eventEmitter = new EventEmitter(); this.getSaveButton().addClass("disabled").attr("disabled", "disabled"); - this.form.addChangeHandler(this.changeHandler.bind(this)); - this.form.addResetHandler(this.resetHandler.bind(this)); - this.form.addSubmitSuccessHandler(this.submitSuccessDispatcher.bind(this)); - this.form.addSubmitFailureHandler(this.submitFailureHandler.bind(this)); + this.form.on('form.changed', this.changeHandler.bind(this)); + this.form.on('form.reset', this.resetHandler.bind(this)); + this.form.on('form.submit.successful', this.submitSuccessDispatcher.bind(this)); + this.form.on('form.submit.failed', function (obj) { + n.Helper.showAjaxError(null, obj.textStatus, obj.errorThrown); + }); this.getSaveButton().click(this.handleSaveClick.bind(this)); this.getCloseButton().click(this.handleCloseClick.bind(this)); @@ -37,15 +40,41 @@ if (!QfqNS) { this.setupFormUpdateHandler(); - this.fileUploader = new QfqNS.FileUpload('#' + this.formId, this.fileUploadTo, this.getSip()); - this.fileUploader.addFileUploadStartHandler(this.startUploadHandler); - this.fileUploader.addFileUploadEndHandler(this.endUploadHandler); + this.fileUploader = new n.FileUpload('#' + this.formId, this.fileUploadTo, this.getSip()); + this.fileUploader.on('fileupload.started', this.startUploadHandler); + this.fileUploader.on('fileupload.upload.success', this.fileUploadSuccessHandler); + + this.fileUploader.on('fileupload.upload.failed', + function (obj) { + n.Helper.showAjaxError(null, obj.textStatus, obj.errorThrown); + }); + this.fileUploader.on('fileupload.ended', this.endUploadHandler); }; + n.QfqForm.prototype.on = n.EventEmitter.onMixin; + + /** + * @public + * @param bsTabs + */ n.QfqForm.prototype.setBsTabs = function (bsTabs) { this.bsTabs = bsTabs; }; + /** + * @private + */ + n.QfqForm.prototype.fileUploadSuccessHandler = function (obj) { + if (!obj.data.status) { + throw Error("Response on file upload missing status"); + } + + if (obj.data.status === "error") { + var alert = new n.Alert(obj.data.message, "error"); + alert.show(); + } + }; + /** * * @param $button @@ -87,7 +116,7 @@ if (!QfqNS) { } if (data.status === "error") { - var alert = new QfqNS.Alert("Error while updating form:<br>" + (data.message ? data.message : "No reason" + + var alert = new n.Alert("Error while updating form:<br>" + (data.message ? data.message : "No reason" + " given"), "error"); alert.show(); return; @@ -112,7 +141,7 @@ if (!QfqNS) { n.QfqForm.prototype.destroyFormAndSetText = function (text) { this.form = null; $('#' + this.formId).replaceWith($("<p>").append(text)); - this.destroyFormUserCallbacks.call(); + this.eventEmitter.emitEvent('qfqform.destroyed', n.EventEmitter.makePayload(this, null)); }; /** @@ -120,7 +149,7 @@ if (!QfqNS) { */ n.QfqForm.prototype.handleSaveClick = function () { this.lastButtonPress = "save"; - QfqNS.Log.debug("save click"); + n.Log.debug("save click"); // First, remove all validation states, in case a previous submit has set a validation state, thus we're not // stockpiling them. this.clearAllValidationStates(); @@ -133,12 +162,12 @@ if (!QfqNS) { n.QfqForm.prototype.handleCloseClick = function () { this.lastButtonPress = "close"; if (this.form.getFormChanged()) { - var alert = new QfqNS.Alert("You have unsaved changes. Do you want to close?", "warning", "yesnosave"); + var alert = new n.Alert("You have unsaved changes. Do you want to close?", "warning", "yesnosave"); var that = this; - alert.addSaveButtonHandler(function () { + alert.on('alert.save', function () { that.form.submitTo(that.submitTo); }); - alert.addOkButtonHandler(function () { + alert.on('alert.ok', function () { window.history.back(); }); alert.show(); @@ -152,7 +181,7 @@ if (!QfqNS) { */ n.QfqForm.prototype.handleNewClick = function () { this.lastButtonPress = "new"; - QfqNS.Log.debug("new click"); + n.Log.debug("new click"); }; /** @@ -160,10 +189,10 @@ if (!QfqNS) { */ n.QfqForm.prototype.handleDeleteClick = function () { this.lastButtonPress = "delete"; - QfqNS.Log.debug("delete click"); - var alert = new QfqNS.Alert("Do you really want to delete the record?", "warning", "yesno"); + n.Log.debug("delete click"); + var alert = new n.Alert("Do you really want to delete the record?", "warning", "yesno"); var that = this; - alert.addOkButtonHandler(function () { + alert.on('alert.ok', function () { $.post(that.deleteUrl) .done(that.ajaxDeleteSuccessDispatcher.bind(that)) .fail(n.Helper.showAjaxError); @@ -215,7 +244,7 @@ if (!QfqNS) { } if (data.redirect === "no") { - var alert = new QfqNS.Alert("redirect=='no' not allowed", "error"); + var alert = new n.Alert("redirect=='no' not allowed", "error"); alert.show(); return; } @@ -236,17 +265,17 @@ if (!QfqNS) { if (!data.message) { throw Error("Status is 'error' but required 'message' attribute is missing."); } - var alert = new QfqNS.Alert(data.message, "error"); + var alert = new n.Alert(data.message, "error"); alert.show(); }; /** * - * @param form {QfqNS.QfqForm} + * @param form {n.QfqForm} * * @private */ - n.QfqForm.prototype.changeHandler = function (form) { + n.QfqForm.prototype.changeHandler = function (obj) { this.getSaveButton().removeClass("disabled"); this.getSaveButton().addClass("alert-warning"); this.getSaveButton().removeAttr("disabled"); @@ -254,11 +283,11 @@ if (!QfqNS) { /** * - * @param form {QfqNS.QfqForm} + * @param form {n.QfqForm} * * @private */ - n.QfqForm.prototype.resetHandler = function (form) { + n.QfqForm.prototype.resetHandler = function (obj) { this.getSaveButton().removeClass("alert-warning"); this.getSaveButton().addClass("disabled"); this.getSaveButton().attr("disabled", "disabled"); @@ -304,36 +333,24 @@ if (!QfqNS) { return $("#new-button"); }; - /** - * - * @param jqXHR - * @param textStatus - * @param errorThrown - * - * @private - */ - n.QfqForm.prototype.submitFailureHandler = function (form, textStatus, jqXHR, errorThrown) { - n.Helper.showAjaxError(errorThrown); - }; - /** * @private */ - n.QfqForm.prototype.submitSuccessDispatcher = function (form, data, textStatus) { - if (!data.status) { + n.QfqForm.prototype.submitSuccessDispatcher = function (obj) { + if (!obj.data.status) { throw new Error("No 'status' property in 'data'"); } - switch (data.status) { + switch (obj.data.status) { case "error": - this.handleLogicSubmitError(form, data); + this.handleLogicSubmitError(obj.target, obj.data); break; case "success": - this.handleSubmitSuccess(form, data); + this.handleSubmitSuccess(obj.target, obj.data); break; default: - throw new Error("Status '" + data.status + "' unknown."); + throw new Error("Status '" + obj.data.status + "' unknown."); } }; @@ -349,7 +366,7 @@ if (!QfqNS) { if (!data.message) { throw Error("Status is 'error' but required 'message' attribute is missing."); } - var alert = new QfqNS.Alert(data.message, "error"); + var alert = new n.Alert(data.message, "error"); alert.show(); if (data["field-name"] && this.bsTabs) { @@ -371,12 +388,12 @@ if (!QfqNS) { * @private */ n.QfqForm.prototype.handleSubmitSuccess = function (form, data) { - QfqNS.Log.debug('Reset form state'); + n.Log.debug('Reset form state'); form.resetFormChanged(); if (this.lastButtonPress === 'save' || !data.redirect || data.redirect === "no") { if (data.message) { - var alert = new QfqNS.Alert(data.message); + var alert = new n.Alert(data.message); alert.timeout = 1500; alert.show(); } @@ -404,7 +421,7 @@ if (!QfqNS) { n.QfqForm.prototype.getFormGroupByControlName = function (formControlName) { var $formControl = $("[name='" + formControlName + "']"); if ($formControl.length === 0) { - QfqNS.Log.debug("QfqForm.setValidationState(): unable to find form control with name '" + formControlName + "'"); + n.Log.debug("QfqForm.setValidationState(): unable to find form control with name '" + formControlName + "'"); return null; } @@ -484,7 +501,7 @@ if (!QfqNS) { var configurationItem = configuration[i]; var formElementName = configurationItem["form-element"]; if (formElementName === undefined) { - QfqNS.Log.error("configuration lacks 'form-element' attribute. Skipping."); + n.Log.error("configuration lacks 'form-element' attribute. Skipping."); continue; } try { @@ -503,7 +520,7 @@ if (!QfqNS) { } } catch (e) { - QfqNS.Log.error(e.message); + n.Log.error(e.message); } } }; @@ -512,8 +529,8 @@ if (!QfqNS) { * @private * @param triggeredBy */ - n.QfqForm.prototype.startUploadHandler = function (triggeredBy) { - $(triggeredBy).after( + n.QfqForm.prototype.startUploadHandler = function (obj) { + $(obj.target).after( $('<i>').addClass('spinner') ); }; @@ -522,15 +539,11 @@ if (!QfqNS) { * @private * @param triggeredBy */ - n.QfqForm.prototype.endUploadHandler = function (triggeredBy) { - var $siblings = $(triggeredBy).siblings(); + n.QfqForm.prototype.endUploadHandler = function (obj) { + var $siblings = $(obj.target).siblings(); $siblings.filter("i").remove(); }; - n.QfqForm.prototype.ajaxFileUploadErrorHandler = function (triggeredBy, jqHXR, textStatus, errorThrown) { - n.Helper.showAjaxError(jqHXR, textStatus, errorThrown); - }; - /** * Retrieve SIP as stored in hidden input field. * diff --git a/javascript/src/QfqPage.js b/javascript/src/QfqPage.js index de80fa53db75356d93917540e67c86f7337b3cc1..e07a10d40abbd3da9a7d53ce8351f926632129d7 100644 --- a/javascript/src/QfqPage.js +++ b/javascript/src/QfqPage.js @@ -4,10 +4,9 @@ /* global $ */ /* global console */ +/* @depend QfqEvents.js */ -if (!QfqNS) { - var QfqNS = {}; -} +var QfqNS = QfqNS || {}; (function (n) { 'use strict'; @@ -36,9 +35,8 @@ if (!QfqNS) { this.settings.pageState.setPageState(this.bsTabs.getCurrentTab(), n.PageTitle.get()); } - - this.bsTabs.addTabShowHandler(this.tabShowHandler.bind(this)); - this.settings.pageState.addStateActivationHandler(this.popStateHandler.bind(this)); + this.bsTabs.on('bootstrap.tab.shown', this.tabShowHandler.bind(this)); + this.settings.pageState.on('pagestate.state.popped', this.popStateHandler.bind(this)); } catch (e) { n.Log.message(e.message); this.bsTabs = null; @@ -52,7 +50,7 @@ if (!QfqNS) { this.settings.refreshUrl, this.settings.fileUploadTo); this.qfqForm.setBsTabs(this.bsTabs); - this.qfqForm.destroyFormUserCallbacks.addFunction(this.destroyFormHandler.bind(this)); + this.qfqForm.on('qfqform.destroyed', this.destroyFormHandler.bind(this)); } catch (e) { n.Log.error(e.message); this.qfqForm = null; @@ -62,12 +60,12 @@ if (!QfqNS) { /** * @private */ - n.QfqPage.prototype.destroyFormHandler = function () { + n.QfqPage.prototype.destroyFormHandler = function (obj) { this.settings.qfqForm = null; $('#' + this.settings.tabsId).remove(); }; - n.QfqPage.prototype.tabShowHandler = function (bsTabs) { + n.QfqPage.prototype.tabShowHandler = function (obj) { // 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. // @@ -78,16 +76,15 @@ if (!QfqNS) { " restoration."); return; } - var currentTabId = bsTabs.getCurrentTab(); + var currentTabId = obj.target.getCurrentTab(); n.Log.debug('Saving state: ' + currentTabId); - n.PageTitle.setSubTitle(bsTabs.getTabName(currentTabId)); + n.PageTitle.setSubTitle(obj.target.getTabName(currentTabId)); this.settings.pageState.setPageState(currentTabId, n.PageTitle.get()); - }; - n.QfqPage.prototype.popStateHandler = function (pageState) { - this.bsTabs.activateTab(pageState.getPageState()); - n.PageTitle.set(pageState.getPageData()); + n.QfqPage.prototype.popStateHandler = function (obj) { + this.bsTabs.activateTab(obj.target.getPageState()); + n.PageTitle.set(obj.target.getPageData()); }; })(QfqNS); \ No newline at end of file diff --git a/javascript/src/QfqRecordList.js b/javascript/src/QfqRecordList.js index 3eae6b052e7d34c52ae42d175fae435088266b6b..18ea78a6f8a20ee521b5c269c7488829cf6cbe03 100644 --- a/javascript/src/QfqRecordList.js +++ b/javascript/src/QfqRecordList.js @@ -4,9 +4,7 @@ /* global $ */ /* global console */ -if (!QfqNS) { - var QfqNS = {}; -} +var QfqNS = QfqNS || {}; (function (n) { 'use strict'; @@ -44,7 +42,7 @@ if (!QfqNS) { var alert = new n.Alert("Do you really want to delete the record?", "warning", "yesno"); var that = this; - alert.addOkButtonHandler(function () { + alert.on('alert.ok', function () { $.post(that.deleteUrl + "?s=" + sip) .done(that.ajaxDeleteSuccessDispatcher.bind(that, $recordElement)) .fail(n.Helper.showAjaxError); diff --git a/mockup/alert.html b/mockup/alert.html index c0327e5328d092e20b9cba98413a5fb7b88503d6..7e640c77a6adebcc612518d9b05bc7875e799718 100644 --- a/mockup/alert.html +++ b/mockup/alert.html @@ -17,6 +17,7 @@ <script src="../js/jquery.min.js"></script> <script src="../js/bootstrap.min.js"></script> +<script src="../js/EventEmitter.min.js"></script> <script src="../js/qfq.debug.js"></script> <script> diff --git a/mockup/api/uploadhandler.php b/mockup/api/uploadhandler.php index 6690da171a36aca1b185d71fdfcfe6181fad88f2..2025a068ad0cf87fa6b6141d72dcdfe283215566 100644 --- a/mockup/api/uploadhandler.php +++ b/mockup/api/uploadhandler.php @@ -3,10 +3,15 @@ * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> */ -var_dump($_POST); +header("Content-Type: text/json"); -foreach ($_FILES as $key => $value) { - echo "$key"; - echo file_get_contents($value['tmp_name']); +foreach ($_FILES as $key => &$value) { + $value['file_content'] = file_get_contents($value['tmp_name']); } +echo json_encode([ + 'status' => "ok", + 'files_received' => $_FILES, + 'request_variables' => $_REQUEST +]); + diff --git a/mockup/api/uploadhandler_error.php b/mockup/api/uploadhandler_error.php new file mode 100644 index 0000000000000000000000000000000000000000..02ef17ea838ab9002c61613e4a872e097b05e367 --- /dev/null +++ b/mockup/api/uploadhandler_error.php @@ -0,0 +1,12 @@ +<?php +/** + * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> + */ + +header("Content-Type: text/json"); + +echo json_encode([ + 'status' => "error", + 'message' +]); + diff --git a/mockup/elementconfiguration.html b/mockup/elementconfiguration.html index 5e97dfcc122806f70a12aa8f475a593431452a2c..fad1efb7fda9ea4b5869bd23f32e8a1902802591 100644 --- a/mockup/elementconfiguration.html +++ b/mockup/elementconfiguration.html @@ -175,6 +175,7 @@ <script src="../js/jquery.min.js"></script> <script src="../js/bootstrap.min.js"></script> <script src="../js/jqx-all.js"></script> +<script src="../js/EventEmitter.min.js"></script> <script src="../js/qfq.debug.js"></script> <script type="text/javascript"> $(function () { diff --git a/mockup/emptyqfqpage.html b/mockup/emptyqfqpage.html index 149958a10220af8d47bf1fe0bfe52e87baf93fcb..d32bb1252b7e90910ebacfc98e46cb69b1e9dc39 100644 --- a/mockup/emptyqfqpage.html +++ b/mockup/emptyqfqpage.html @@ -11,6 +11,7 @@ <script src="../js/jquery.min.js"></script> <script src="../js/bootstrap.min.js"></script> <script src="../js/jqx-all.js"></script> +<script src="../js/EventEmitter.min.js"></script> <script src="../js/qfq.debug.js"></script> <script type="text/javascript"> var qfqPage = new QfqNS.QfqPage({ diff --git a/mockup/form.html b/mockup/form.html index 655b661fdaf51b8610c1cdacf4a262d0494ba80e..fa97ecaa93b67f5f1c8b6a80fdb18fde83aac078 100644 --- a/mockup/form.html +++ b/mockup/form.html @@ -34,6 +34,7 @@ </form> <script src="../js/jquery.min.js"></script> +<script src="../js/EventEmitter.min.js"></script> <script src="../js/qfq.debug.js"></script> <script> diff --git a/mockup/navstate.html b/mockup/navstate.html index eb8369746b73dc560530979a503baaa187415ac9..d48ac1da47e1485093122e0a1d3b8f29e8bbaa3d 100644 --- a/mockup/navstate.html +++ b/mockup/navstate.html @@ -140,9 +140,10 @@ </section> -<script src="../packages/jquery/js/jquery.min.js"></script> -<script src="../packages/bootstrap/js/bootstrap.min.js"></script> -<script src="../packages/jqwidgets/js/jqx-all.js"></script> +<script src="../js/jquery.min.js"></script> +<script src="../js/bootstrap.min.js"></script> +<script src="../js/jqx-all.js"></script> +<script src="../js/EventEmitter.min.js"></script> <script src="../js/qfq.debug.js"></script> <script type="text/javascript"> $(function () { diff --git a/mockup/personmock.html b/mockup/personmock.html index 036c60ae25a750f2dc501dba88413372bfa001cd..edc72d8ebe53d857e9b5e02dcfadc5115695285f 100644 --- a/mockup/personmock.html +++ b/mockup/personmock.html @@ -37,6 +37,14 @@ </select> </label> +<label>Upload to + <select name="uploadTo" id="uploadTo"> + <option>404 error</option> + <option>uploadhandler.php</option> + <option>uploadhandler_error.php</option> + </select> +</label> + <div class="container-fluid"> <div class="row hidden-xs"> @@ -751,6 +759,7 @@ <script src="../js/jquery.min.js"></script> <script src="../js/bootstrap.min.js"></script> <script src="../js/jqx-all.js"></script> +<script src="../js/EventEmitter.min.js"></script> <script src="../js/qfq.debug.js"></script> <script type="text/javascript"> $(function () { @@ -973,7 +982,7 @@ formId: 'myForm', submitTo: 'api/' + $("#submitTo").val(), deleteUrl: 'api/' + $("#deleteUrl").val(), - fileUploadTo: 'api/uploadhandler.php' + fileUploadTo: 'api/' + $("#uploadTo").val() }); $("#submitTo").on("change", function (evt) { @@ -986,6 +995,11 @@ qfqPage.qfqForm.deleteUrl = 'api/' + $(evt.target).val(); }); + $("#uploadTo").on("change", function (evt) { + qfqPage.settings.fileUploadTo = 'api/' + $(evt.target).val(); + qfqPage.qfqForm.fileUploader.targetUrl = 'api/' + $(evt.target).val(); + }); + QfqNS.Log.level = 0; }); </script> diff --git a/mockup/readonly.html b/mockup/readonly.html index b7b3115cff92bf8dafefeed9a68747c68a5eb6a6..0ac97f6739841ea4ebf95b09c427bc6492d64773 100644 --- a/mockup/readonly.html +++ b/mockup/readonly.html @@ -104,6 +104,7 @@ <script src="../js/jquery.min.js"></script> <script src="../js/bootstrap.min.js"></script> <script src="../js/jqx-all.js"></script> +<script src="../js/EventEmitter.min.js"></script> <script src="../js/qfq.debug.js"></script> <script type="text/javascript"> $(function () { diff --git a/mockup/recordlist.html b/mockup/recordlist.html index fcea88d9cd4f269e786760e1d319b28d15ff7a52..a42b6a3850da1212dc75edc9e1a387bb07446359 100644 --- a/mockup/recordlist.html +++ b/mockup/recordlist.html @@ -61,6 +61,7 @@ <script src="../js/jquery.min.js"></script> <script src="../js/bootstrap.min.js"></script> <script src="../js/jqx-all.js"></script> +<script src="../js/EventEmitter.min.js"></script> <script src="../js/qfq.debug.js"></script> <script type="text/javascript"> $(function () { diff --git a/mockup/upload.html b/mockup/upload.html index 6ee74c3284a330052674826da601bd151d6e93aa..88d991b858fcb8d4b2a115d4a1f209665b84e7a5 100644 --- a/mockup/upload.html +++ b/mockup/upload.html @@ -18,24 +18,25 @@ <pre id="display"></pre> <script src="../js/jquery.min.js"></script> +<script src="../js/EventEmitter.min.js"></script> <script src="../js/qfq.debug.js"></script> <script> var fileUpload = new QfqNS.FileUpload('#myForm', 'api/uploadhandler.php', 'the_sip'); - fileUpload.addFileUploadStartHandler(function () { + fileUpload.on('fileupload.started', function () { $('#progress').empty().append('<p>Upload started</p>'); }); - fileUpload.addFileUploadEndHandler(function () { + fileUpload.on('fileupload.ended', function () { $('#progress').append('<p>Upload finished</p>'); }); - fileUpload.addFileUploadSuccessHandler(function (data) { + fileUpload.on('fileupload.upload.successful', function (obj) { $('#progress').append('<p>Upload success</p>'); - $('#display').empty().append(data); + $('#display').empty().append(obj.data.file_content); }); - fileUpload.addFileUploadErrorHandler(function () { + fileUpload.on('fileupload.upload.failed', function () { $('#progress').append('<p>Upload made a booboo</p>'); }); diff --git a/tests/jasmine/SpecRunner.html b/tests/jasmine/SpecRunner.html index 3ae7adf567c8034940ca18ea57c0b81261645346..2b1365c68d32e2e1e49e5bfec9d6aaf7e3af966b 100644 --- a/tests/jasmine/SpecRunner.html +++ b/tests/jasmine/SpecRunner.html @@ -14,8 +14,9 @@ <script src="lib/jasmine-2.4.1/boot.js"></script> <script src="helper/mock-ajax.js"></script> - <script src="../../packages/jquery/js/jquery.min.js"></script> - <script src="../../packages/bootstrap/js/bootstrap.min.js"></script> + <script src="../../js/jquery.min.js"></script> + <script src="../../js/bootstrap.min.js"></script> + <script src="../../js/EventEmitter.min.js"></script> <!-- include source files here... --> <script src="../../js/qfq.debug.js"></script>