--- /dev/null
--- /dev/null
++/**
++ * Default implementation for inline editors.
++ *
++ * @param string elementSelector
++ */
++WCF.InlineEditor = Class.extend({
++ /**
++ * list of registered callbacks
++ * @var array<object>
++ */
++ _callbacks: [],
++
++ /**
++ * list of dropdown selections
++ * @var object
++ */
++ _dropdowns: {},
++
++ /**
++ * list of container elements
++ * @var object
++ */
++ _elements: {},
++
++ /**
++ * notification object
++ * @var WCF.System.Notification
++ */
++ _notification: null,
++
++ /**
++ * list of known options
++ * @var array<object>
++ */
++ _options: [],
++
++ /**
++ * action proxy
++ * @var WCF.Action.Proxy
++ */
++ _proxy: null,
++
++ /**
++ * list of trigger elements by element id
++ * @var object<object>
++ */
++ _triggerElements: {},
++
++ /**
++ * list of data to update upon success
++ * @var array<object>
++ */
++ _updateData: [],
++
++ /**
++ * Initializes a new inline editor.
++ */
++ init: function (elementSelector) {
++ var $elements = $(elementSelector);
++ if (!$elements.length) {
++ return;
++ }
++
++ this._setOptions();
++ var $quickOption = '';
++ for (var $i = 0, $length = this._options.length; $i < $length; $i++) {
++ if (this._options[$i].isQuickOption) {
++ $quickOption = this._options[$i].optionName;
++ break;
++ }
++ }
++
++ var self = this;
++ $elements.each(function (index, element) {
++ var $element = $(element);
++ var $elementID = $element.wcfIdentify();
++
++ // find trigger element
++ var $trigger = self._getTriggerElement($element);
++ if ($trigger === null || $trigger.length !== 1) {
++ return;
++ }
++
++ $trigger.on(WCF_CLICK_EVENT, $.proxy(self._show, self)).data('elementID', $elementID);
++ if ($quickOption) {
++ // simulate click on target action
++ $trigger.disableSelection().data('optionName', $quickOption).dblclick($.proxy(self._click, self));
++ }
++
++ // store reference
++ self._elements[$elementID] = $element;
++ });
++
++ this._proxy = new WCF.Action.Proxy({
++ success: $.proxy(this._success, this)
++ });
++
++ WCF.CloseOverlayHandler.addCallback('WCF.InlineEditor', $.proxy(this._closeAll, this));
++
++ this._notification = new WCF.System.Notification(WCF.Language.get('wcf.global.success'), 'success');
++ },
++
++ /**
++ * Closes all inline editors.
++ */
++ _closeAll: function () {
++ for (var $elementID in this._elements) {
++ this._hide($elementID);
++ }
++ },
++
++ /**
++ * Sets options for this inline editor.
++ */
++ _setOptions: function () {
++ this._options = [];
++ },
++
++ /**
++ * Register an option callback for validation and execution.
++ *
++ * @param object callback
++ */
++ registerCallback: function (callback) {
++ if ($.isFunction(callback)) {
++ this._callbacks.push(callback);
++ }
++ },
++
++ /**
++ * Returns the triggering element.
++ *
++ * @param jQuery element
++ * @return jQuery
++ */
++ _getTriggerElement: function (element) {
++ return null;
++ },
++
++ /**
++ * Shows a dropdown menu if options are available.
++ *
++ * @param object event
++ */
++ _show: function (event) {
++ event.preventDefault();
++ var $elementID = $(event.currentTarget).data('elementID');
++
++ // build dropdown
++ var $trigger = null;
++ if (!this._dropdowns[$elementID]) {
++ this._triggerElements[$elementID] = $trigger = this._getTriggerElement(this._elements[$elementID]).addClass('dropdownToggle');
++ var parent = $trigger[0].parentNode;
++ if (parent && parent.nodeName === 'LI' && parent.childElementCount === 1) {
++ // do not add a wrapper element if the trigger is the only child
++ parent.classList.add('dropdown');
++ }
++ else {
++ $trigger.wrap('<span class="dropdown" />');
++ }
++
++ this._dropdowns[$elementID] = $('<ul class="dropdownMenu" />').insertAfter($trigger);
++ }
++ this._dropdowns[$elementID].empty();
++
++ // validate options
++ var $hasOptions = false;
++ var $lastElementType = '';
++ for (var $i = 0, $length = this._options.length; $i < $length; $i++) {
++ var $option = this._options[$i];
++
++ if ($option.optionName === 'divider') {
++ if ($lastElementType !== '' && $lastElementType !== 'divider') {
++ $('<li class="dropdownDivider" />').appendTo(this._dropdowns[$elementID]);
++ $lastElementType = $option.optionName;
++ }
++ }
++ else if (this._validate($elementID, $option.optionName) || this._validateCallbacks($elementID, $option.optionName)) {
++ var $listItem = $('<li><span>' + $option.label + '</span></li>').appendTo(this._dropdowns[$elementID]);
++ $listItem.data('elementID', $elementID).data('optionName', $option.optionName).data('isQuickOption', ($option.isQuickOption ? true : false)).click($.proxy(this._click, this));
++
++ $hasOptions = true;
++ $lastElementType = $option.optionName;
++ }
++ }
++
++ if ($hasOptions) {
++ // if last child is divider, remove it
++ var $lastChild = this._dropdowns[$elementID].children().last();
++ if ($lastChild.hasClass('dropdownDivider')) {
++ $lastChild.remove();
++ }
++
++ // check if only element is a quick option
++ var $quickOption = null;
++ var $count = 0;
++ this._dropdowns[$elementID].children().each(function (index, child) {
++ var $child = $(child);
++ if (!$child.hasClass('dropdownDivider')) {
++ if ($child.data('isQuickOption')) {
++ $quickOption = $child;
++ }
++ else {
++ $count++;
++ }
++ }
++ });
++
++ if (!$count) {
++ $quickOption.trigger('click');
++
++ if (this._triggerElements[$elementID]) {
++ WCF.Dropdown.close(this._triggerElements[$elementID].parents('.dropdown').wcfIdentify());
++ }
++
++ return false;
++ }
++ }
++
++ if ($trigger !== null) {
++ WCF.Dropdown.initDropdown($trigger, true);
++ }
++
++ return false;
++ },
++
++ /**
++ * Validates an option.
++ *
++ * @param string elementID
++ * @param string optionName
++ * @returns boolean
++ */
++ _validate: function (elementID, optionName) {
++ return false;
++ },
++
++ /**
++ * Validates an option provided by callbacks.
++ *
++ * @param string elementID
++ * @param string optionName
++ * @return boolean
++ */
++ _validateCallbacks: function (elementID, optionName) {
++ var $length = this._callbacks.length;
++ if ($length) {
++ for (var $i = 0; $i < $length; $i++) {
++ if (this._callbacks[$i].validate(this._elements[elementID], optionName)) {
++ return true;
++ }
++ }
++ }
++
++ return false;
++ },
++
++ /**
++ * Handles AJAX responses.
++ *
++ * @param object data
++ * @param string textStatus
++ * @param jQuery jqXHR
++ */
++ _success: function (data, textStatus, jqXHR) {
++ var $length = this._updateData.length;
++ if (!$length) {
++ return;
++ }
++
++ this._updateState(data);
++
++ this._updateData = [];
++ },
++
++ /**
++ * Update element states based upon update data.
++ *
++ * @param object data
++ */
++ _updateState: function (data) {
++ },
++
++ /**
++ * Handles clicks within dropdown.
++ *
++ * @param object event
++ */
++ _click: function (event) {
++ var $listItem = $(event.currentTarget);
++ var $elementID = $listItem.data('elementID');
++ var $optionName = $listItem.data('optionName');
++
++ if (!this._execute($elementID, $optionName)) {
++ this._executeCallback($elementID, $optionName);
++ }
++
++ this._hide($elementID);
++ },
++
++ /**
++ * Executes actions associated with an option.
++ *
++ * @param string elementID
++ * @param string optionName
++ * @return boolean
++ */
++ _execute: function (elementID, optionName) {
++ return false;
++ },
++
++ /**
++ * Executes actions associated with an option provided by callbacks.
++ *
++ * @param string elementID
++ * @param string optionName
++ * @return boolean
++ */
++ _executeCallback: function (elementID, optionName) {
++ var $length = this._callbacks.length;
++ if ($length) {
++ for (var $i = 0; $i < $length; $i++) {
++ if (this._callbacks[$i].execute(this._elements[elementID], optionName)) {
++ return true;
++ }
++ }
++ }
++
++ return false;
++ },
++
++ /**
++ * Hides a dropdown menu.
++ *
++ * @param string elementID
++ */
++ _hide: function (elementID) {
++ if (this._dropdowns[elementID]) {
++ this._dropdowns[elementID].empty().removeClass('dropdownOpen');
++ }
++ }
++});
}
};
-/**
- * Worker support for frontend based upon DatabaseObjectActions.
- *
- * @param string className
- * @param string title
- * @param object parameters
- * @param object callback
- */
-WCF.System.Worker = Class.extend({
- /**
- * worker aborted
- * @var boolean
- */
- _aborted: false,
-
- /**
- * DBOAction method name
- * @var string
- */
- _actionName: '',
-
- /**
- * callback invoked after worker completed
- * @var object
- */
- _callback: null,
-
- /**
- * DBOAction class name
- * @var string
- */
- _className: '',
-
- /**
- * dialog object
- * @var jQuery
- */
- _dialog: null,
-
- /**
- * action proxy
- * @var WCF.Action.Proxy
- */
- _proxy: null,
-
- /**
- * dialog title
- * @var string
- */
- _title: '',
-
- /**
- * Initializes a new worker instance.
- *
- * @param string actionName
- * @param string className
- * @param string title
- * @param object parameters
- * @param object callback
- * @param object confirmMessage
- */
- init: function(actionName, className, title, parameters, callback) {
- this._aborted = false;
- this._actionName = actionName;
- this._callback = callback || null;
- this._className = className;
- this._dialog = null;
- this._proxy = new WCF.Action.Proxy({
- autoSend: true,
- data: {
- actionName: this._actionName,
- className: this._className,
- parameters: parameters || { }
- },
- showLoadingOverlay: false,
- success: $.proxy(this._success, this)
- });
- this._title = title;
- },
-
+if (COMPILER_TARGET_DEFAULT) {
/**
- * Handles response from server.
- *
- * @param object data
+ * Worker support for frontend based upon DatabaseObjectActions.
+ *
+ * @param string className
+ * @param string title
+ * @param object parameters
+ * @param object callback
*/
- _success: function(data) {
- // init binding
- if (this._dialog === null) {
- this._dialog = $('<div />').hide().appendTo(document.body);
- this._dialog.wcfDialog({
- closeConfirmMessage: WCF.Language.get('wcf.worker.abort.confirmMessage'),
- closeViaModal: false,
- onClose: $.proxy(function() {
- this._aborted = true;
- this._proxy.abortPrevious();
-
- window.location.reload();
- }, this),
- title: this._title
- });
- }
+ WCF.System.Worker = Class.extend({
+ /**
+ * worker aborted
+ * @var boolean
+ */
+ _aborted: false,
- if (this._aborted) {
- return;
- }
+ /**
+ * DBOAction method name
+ * @var string
+ */
+ _actionName: '',
- if (data.returnValues.template) {
- this._dialog.html(data.returnValues.template);
- }
+ /**
+ * callback invoked after worker completed
+ * @var object
+ */
+ _callback: null,
- // update progress
- this._dialog.find('progress').attr('value', data.returnValues.progress).text(data.returnValues.progress + '%').next('span').text(data.returnValues.progress + '%');
+ /**
+ * DBOAction class name
+ * @var string
+ */
+ _className: '',
- // worker is still busy with its business, carry on
- if (data.returnValues.progress < 100) {
- // send request for next loop
- var $parameters = data.returnValues.parameters || { };
- $parameters.loopCount = data.returnValues.loopCount;
-
- this._proxy.setOption('data', {
- actionName: this._actionName,
- className: this._className,
- parameters: $parameters
- });
- this._proxy.sendRequest();
- }
- else if (this._callback !== null) {
- this._callback(this, data);
- }
- else {
- // exchange icon
- this._dialog.find('.fa-spinner').removeClass('fa-spinner').addClass('fa-check green');
- this._dialog.find('.contentHeader h1').text(WCF.Language.get('wcf.global.worker.completed'));
-
- // display continue button
- var $formSubmit = $('<div class="formSubmit" />').appendTo(this._dialog);
- $('<button class="buttonPrimary">' + WCF.Language.get('wcf.global.button.next') + '</button>').appendTo($formSubmit).focus().click(function() {
- if (data.returnValues.redirectURL) {
- window.location = data.returnValues.redirectURL;
- }
- else {
- window.location.reload();
- }
+ /**
+ * dialog object
+ * @var jQuery
+ */
+ _dialog: null,
+
+ /**
+ * action proxy
+ * @var WCF.Action.Proxy
+ */
+ _proxy: null,
+
+ /**
+ * dialog title
+ * @var string
+ */
+ _title: '',
+
+ /**
+ * Initializes a new worker instance.
+ *
+ * @param string actionName
+ * @param string className
+ * @param string title
+ * @param object parameters
+ * @param object callback
+ * @param object confirmMessage
+ */
+ init: function (actionName, className, title, parameters, callback) {
+ this._aborted = false;
+ this._actionName = actionName;
+ this._callback = callback || null;
+ this._className = className;
+ this._dialog = null;
+ this._proxy = new WCF.Action.Proxy({
+ autoSend: true,
+ data: {
+ actionName: this._actionName,
+ className: this._className,
+ parameters: parameters || {}
+ },
+ showLoadingOverlay: false,
+ success: $.proxy(this._success, this)
});
+ this._title = title;
+ },
+
+ /**
+ * Handles response from server.
+ *
+ * @param object data
+ */
+ _success: function (data) {
+ // init binding
+ if (this._dialog === null) {
+ this._dialog = $('<div />').hide().appendTo(document.body);
+ this._dialog.wcfDialog({
+ closeConfirmMessage: WCF.Language.get('wcf.worker.abort.confirmMessage'),
+ closeViaModal: false,
+ onClose: $.proxy(function () {
+ this._aborted = true;
+ this._proxy.abortPrevious();
+
+ window.location.reload();
+ }, this),
+ title: this._title
+ });
+ }
- this._dialog.wcfDialog('render');
- }
- }
-});
-
-/**
- * Default implementation for inline editors.
- *
- * @param string elementSelector
- */
-WCF.InlineEditor = Class.extend({
- /**
- * list of registered callbacks
- * @var array<object>
- */
- _callbacks: [ ],
-
- /**
- * list of dropdown selections
- * @var object
- */
- _dropdowns: { },
-
- /**
- * list of container elements
- * @var object
- */
- _elements: { },
-
- /**
- * notification object
- * @var WCF.System.Notification
- */
- _notification: null,
-
- /**
- * list of known options
- * @var array<object>
- */
- _options: [ ],
-
- /**
- * action proxy
- * @var WCF.Action.Proxy
- */
- _proxy: null,
-
- /**
- * list of trigger elements by element id
- * @var object<object>
- */
- _triggerElements: { },
-
- /**
- * list of data to update upon success
- * @var array<object>
- */
- _updateData: [ ],
-
- /**
- * element selector
- * @var string
- */
- _elementSelector: null,
-
- /**
- * quick option for the inline editor
- * @var string
- */
- _quickOption: null,
+ if (this._aborted) {
+ return;
+ }
+
+ if (data.returnValues.template) {
+ this._dialog.html(data.returnValues.template);
+ }
+
+ // update progress
+ this._dialog.find('progress').attr('value', data.returnValues.progress).text(data.returnValues.progress + '%').next('span').text(data.returnValues.progress + '%');
+
+ // worker is still busy with its business, carry on
+ if (data.returnValues.progress < 100) {
+ // send request for next loop
+ var $parameters = data.returnValues.parameters || {};
+ $parameters.loopCount = data.returnValues.loopCount;
+
+ this._proxy.setOption('data', {
+ actionName: this._actionName,
+ className: this._className,
+ parameters: $parameters
+ });
+ this._proxy.sendRequest();
+ }
+ else if (this._callback !== null) {
+ this._callback(this, data);
+ }
+ else {
+ // exchange icon
+ this._dialog.find('.fa-spinner').removeClass('fa-spinner').addClass('fa-check green');
+ this._dialog.find('.contentHeader h1').text(WCF.Language.get('wcf.global.worker.completed'));
+
+ // display continue button
+ var $formSubmit = $('<div class="formSubmit" />').appendTo(this._dialog);
+ $('<button class="buttonPrimary">' + WCF.Language.get('wcf.global.button.next') + '</button>').appendTo($formSubmit).focus().click(function () {
+ if (data.returnValues.redirectURL) {
+ window.location = data.returnValues.redirectURL;
+ }
+ else {
+ window.location.reload();
+ }
+ });
+
+ this._dialog.wcfDialog('render');
+ }
+ }
+ });
/**
- * Initializes a new inline editor.
+ * Default implementation for inline editors.
+ *
+ * @param string elementSelector
*/
- init: function(elementSelector) {
- this._elementSelector = elementSelector;
+ WCF.InlineEditor = Class.extend({
+ /**
+ * list of registered callbacks
+ * @var array<object>
+ */
+ _callbacks: [],
- var $elements = $(elementSelector);
- if (!$elements.length) {
- return;
- }
+ /**
+ * list of dropdown selections
+ * @var object
+ */
+ _dropdowns: {},
- this._setOptions();
- for (var $i = 0, $length = this._options.length; $i < $length; $i++) {
- if (this._options[$i].isQuickOption) {
- this._quickOption = this._options[$i].optionName;
- break;
- }
- }
+ /**
+ * list of container elements
+ * @var object
+ */
+ _elements: {},
- this.rebuild();
+ /**
+ * notification object
+ * @var WCF.System.Notification
+ */
+ _notification: null,
- WCF.DOMNodeInsertedHandler.addCallback('WCF.InlineEditor' + this._elementSelector.hashCode(), $.proxy(this.rebuild, this));
+ /**
+ * list of known options
+ * @var array<object>
+ */
+ _options: [],
- this._proxy = new WCF.Action.Proxy({
- success: $.proxy(this._success, this)
- });
+ /**
+ * action proxy
+ * @var WCF.Action.Proxy
+ */
+ _proxy: null,
- WCF.CloseOverlayHandler.addCallback('WCF.InlineEditor', $.proxy(this._closeAll, this));
+ /**
+ * list of trigger elements by element id
+ * @var object<object>
+ */
+ _triggerElements: {},
- this._notification = new WCF.System.Notification(WCF.Language.get('wcf.global.success'), 'success');
- },
-
- /**
- * Identify new elements and adds the event listeners to them.
- */
- rebuild: function() {
- var $elements = $(this._elementSelector);
- var self = this;
- $elements.each(function (index, element) {
- var $element = $(element);
- var $elementID = $element.wcfIdentify();
+ /**
+ * list of data to update upon success
+ * @var array<object>
+ */
+ _updateData: [],
+
++ /**
++ * element selector
++ * @var string
++ */
++ _elementSelector: null,
++
++ /**
++ * quick option for the inline editor
++ * @var string
++ */
++ _quickOption: null,
++
+ /**
+ * Initializes a new inline editor.
+ */
+ init: function (elementSelector) {
++ this._elementSelector = elementSelector;
+
- if (self._elements[$elementID] === undefined) {
- // find trigger element
- var $trigger = self._getTriggerElement($element);
- if ($trigger === null || $trigger.length !== 1) {
- return;
+ var $elements = $(elementSelector);
+ if (!$elements.length) {
+ return;
+ }
+
+ this._setOptions();
- var $quickOption = '';
+ for (var $i = 0, $length = this._options.length; $i < $length; $i++) {
+ if (this._options[$i].isQuickOption) {
- $quickOption = this._options[$i].optionName;
++ this._quickOption = this._options[$i].optionName;
+ break;
}
- var self = this;
- $elements.each(function (index, element) {
- var $element = $(element);
- var $elementID = $element.wcfIdentify();
-
- // find trigger element
- var $trigger = self._getTriggerElement($element);
- if ($trigger === null || $trigger.length !== 1) {
- return;
- }
-
- $trigger.on(WCF_CLICK_EVENT, $.proxy(self._show, self)).data('elementID', $elementID);
- if ($quickOption) {
- // simulate click on target action
- $trigger.disableSelection().data('optionName', $quickOption).dblclick($.proxy(self._click, self));
- }
-
- // store reference
- self._elements[$elementID] = $element;
- });
+ }
+
- $trigger.on(WCF_CLICK_EVENT, $.proxy(self._show, self)).data('elementID', $elementID);
- if (this._quickOption) {
- // simulate click on target action
- $trigger.disableSelection().data('optionName', $quickOption).dblclick($.proxy(self._click, self));
++ this.rebuild();
++
++ WCF.DOMNodeInsertedHandler.addCallback('WCF.InlineEditor' + this._elementSelector.hashCode(), $.proxy(this.rebuild, this));
+
+ this._proxy = new WCF.Action.Proxy({
+ success: $.proxy(this._success, this)
+ });
+
+ WCF.CloseOverlayHandler.addCallback('WCF.InlineEditor', $.proxy(this._closeAll, this));
+
+ this._notification = new WCF.System.Notification(WCF.Language.get('wcf.global.success'), 'success');
+ },
+
++ /**
++ * Identify new elements and adds the event listeners to them.
++ */
++ rebuild: function() {
++ var $elements = $(this._elementSelector);
++ var self = this;
++ $elements.each(function (index, element) {
++ var $element = $(element);
++ var $elementID = $element.wcfIdentify();
+
-
- // store reference
- self._elements[$elementID] = $element;
- }
- });
- },
-
- /**
- * Closes all inline editors.
- */
- _closeAll: function() {
- for (var $elementID in this._elements) {
- this._hide($elementID);
- }
- },
-
- /**
- * Sets options for this inline editor.
- */
- _setOptions: function() {
- this._options = [ ];
- },
-
- /**
- * Register an option callback for validation and execution.
- *
- * @param object callback
- */
- registerCallback: function(callback) {
- if ($.isFunction(callback)) {
- this._callbacks.push(callback);
- }
- },
-
- /**
- * Returns the triggering element.
- *
- * @param jQuery element
- * @return jQuery
- */
- _getTriggerElement: function(element) {
- return null;
- },
-
- /**
- * Shows a dropdown menu if options are available.
- *
- * @param object event
- */
- _show: function(event) {
- event.preventDefault();
- var $elementID = $(event.currentTarget).data('elementID');
++ if (self._elements[$elementID] === undefined) {
++ // find trigger element
++ var $trigger = self._getTriggerElement($element);
++ if ($trigger === null || $trigger.length !== 1) {
++ return;
++ }
++
++ $trigger.on(WCF_CLICK_EVENT, $.proxy(self._show, self)).data('elementID', $elementID);
++ if (this._quickOption) {
++ // simulate click on target action
++ $trigger.disableSelection().data('optionName', $quickOption).dblclick($.proxy(self._click, self));
++ }
++
++ // store reference
++ self._elements[$elementID] = $element;
+ }
- // build dropdown
- var $trigger = null;
- if (!this._dropdowns[$elementID]) {
- this._triggerElements[$elementID] = $trigger = this._getTriggerElement(this._elements[$elementID]).addClass('dropdownToggle');
- var parent = $trigger[0].parentNode;
- if (parent && parent.nodeName === 'LI' && parent.childElementCount === 1) {
- // do not add a wrapper element if the trigger is the only child
- parent.classList.add('dropdown');
++ });
++ },
+
+ /**
+ * Closes all inline editors.
+ */
+ _closeAll: function () {
+ for (var $elementID in this._elements) {
+ this._hide($elementID);
}
- else {
- $trigger.wrap('<span class="dropdown" />');
+ },
+
+ /**
+ * Sets options for this inline editor.
+ */
+ _setOptions: function () {
+ this._options = [];
+ },
+
+ /**
+ * Register an option callback for validation and execution.
+ *
+ * @param object callback
+ */
+ registerCallback: function (callback) {
+ if ($.isFunction(callback)) {
+ this._callbacks.push(callback);
}
-
- this._dropdowns[$elementID] = $('<ul class="dropdownMenu" />').insertAfter($trigger);
- }
- this._dropdowns[$elementID].empty();
+ },
+
+ /**
+ * Returns the triggering element.
+ *
+ * @param jQuery element
+ * @return jQuery
+ */
+ _getTriggerElement: function (element) {
+ return null;
+ },
- // validate options
- var $hasOptions = false;
- var $lastElementType = '';
- for (var $i = 0, $length = this._options.length; $i < $length; $i++) {
- var $option = this._options[$i];
+ /**
+ * Shows a dropdown menu if options are available.
+ *
+ * @param object event
+ */
+ _show: function (event) {
+ event.preventDefault();
+ var $elementID = $(event.currentTarget).data('elementID');
- if ($option.optionName === 'divider') {
- if ($lastElementType !== '' && $lastElementType !== 'divider') {
- $('<li class="dropdownDivider" />').appendTo(this._dropdowns[$elementID]);
- $lastElementType = $option.optionName;
+ // build dropdown
+ var $trigger = null;
+ if (!this._dropdowns[$elementID]) {
+ this._triggerElements[$elementID] = $trigger = this._getTriggerElement(this._elements[$elementID]).addClass('dropdownToggle');
+ var parent = $trigger[0].parentNode;
+ if (parent && parent.nodeName === 'LI' && parent.childElementCount === 1) {
+ // do not add a wrapper element if the trigger is the only child
+ parent.classList.add('dropdown');
+ }
+ else {
+ $trigger.wrap('<span class="dropdown" />');
}
- }
- else if (this._validate($elementID, $option.optionName) || this._validateCallbacks($elementID, $option.optionName)) {
- var $listItem = $('<li><span>' + $option.label + '</span></li>').appendTo(this._dropdowns[$elementID]);
- $listItem.data('elementID', $elementID).data('optionName', $option.optionName).data('isQuickOption', ($option.isQuickOption ? true : false)).click($.proxy(this._click, this));
- $hasOptions = true;
- $lastElementType = $option.optionName;
- }
- }
-
- if ($hasOptions) {
- // if last child is divider, remove it
- var $lastChild = this._dropdowns[$elementID].children().last();
- if ($lastChild.hasClass('dropdownDivider')) {
- $lastChild.remove();
+ this._dropdowns[$elementID] = $('<ul class="dropdownMenu" />').insertAfter($trigger);
}
+ this._dropdowns[$elementID].empty();
- // check if only element is a quick option
- var $quickOption = null;
- var $count = 0;
- this._dropdowns[$elementID].children().each(function(index, child) {
- var $child = $(child);
- if (!$child.hasClass('dropdownDivider')) {
- if ($child.data('isQuickOption')) {
- $quickOption = $child;
- }
- else {
- $count++;
+ // validate options
+ var $hasOptions = false;
+ var $lastElementType = '';
+ for (var $i = 0, $length = this._options.length; $i < $length; $i++) {
+ var $option = this._options[$i];
+
+ if ($option.optionName === 'divider') {
+ if ($lastElementType !== '' && $lastElementType !== 'divider') {
+ $('<li class="dropdownDivider" />').appendTo(this._dropdowns[$elementID]);
+ $lastElementType = $option.optionName;
}
}
- });
+ else if (this._validate($elementID, $option.optionName) || this._validateCallbacks($elementID, $option.optionName)) {
+ var $listItem = $('<li><span>' + $option.label + '</span></li>').appendTo(this._dropdowns[$elementID]);
+ $listItem.data('elementID', $elementID).data('optionName', $option.optionName).data('isQuickOption', ($option.isQuickOption ? true : false)).click($.proxy(this._click, this));
+
+ $hasOptions = true;
+ $lastElementType = $option.optionName;
+ }
+ }
- if (!$count) {
- $quickOption.trigger('click');
-
- if (this._triggerElements[$elementID]) {
- WCF.Dropdown.close(this._triggerElements[$elementID].parents('.dropdown').wcfIdentify());
+ if ($hasOptions) {
+ // if last child is divider, remove it
+ var $lastChild = this._dropdowns[$elementID].children().last();
+ if ($lastChild.hasClass('dropdownDivider')) {
+ $lastChild.remove();
}
- return false;
+ // check if only element is a quick option
+ var $quickOption = null;
+ var $count = 0;
+ this._dropdowns[$elementID].children().each(function (index, child) {
+ var $child = $(child);
+ if (!$child.hasClass('dropdownDivider')) {
+ if ($child.data('isQuickOption')) {
+ $quickOption = $child;
+ }
+ else {
+ $count++;
+ }
+ }
+ });
+
+ if (!$count) {
+ $quickOption.trigger('click');
+
+ if (this._triggerElements[$elementID]) {
+ WCF.Dropdown.close(this._triggerElements[$elementID].parents('.dropdown').wcfIdentify());
+ }
+
+ return false;
+ }
}
- }
+
+ if ($trigger !== null) {
+ WCF.Dropdown.initDropdown($trigger, true);
+ }
+
+ return false;
+ },
- if ($trigger !== null) {
- WCF.Dropdown.initDropdown($trigger, true);
- }
+ /**
+ * Validates an option.
+ *
+ * @param string elementID
+ * @param string optionName
+ * @returns boolean
+ */
+ _validate: function (elementID, optionName) {
+ return false;
+ },
- return false;
- },
-
- /**
- * Validates an option.
- *
- * @param string elementID
- * @param string optionName
- * @returns boolean
- */
- _validate: function(elementID, optionName) {
- return false;
- },
-
- /**
- * Validates an option provided by callbacks.
- *
- * @param string elementID
- * @param string optionName
- * @return boolean
- */
- _validateCallbacks: function(elementID, optionName) {
- var $length = this._callbacks.length;
- if ($length) {
- for (var $i = 0; $i < $length; $i++) {
- if (this._callbacks[$i].validate(this._elements[elementID], optionName)) {
- return true;
+ /**
+ * Validates an option provided by callbacks.
+ *
+ * @param string elementID
+ * @param string optionName
+ * @return boolean
+ */
+ _validateCallbacks: function (elementID, optionName) {
+ var $length = this._callbacks.length;
+ if ($length) {
+ for (var $i = 0; $i < $length; $i++) {
+ if (this._callbacks[$i].validate(this._elements[elementID], optionName)) {
+ return true;
+ }
}
}
- }
+
+ return false;
+ },
- return false;
- },
-
- /**
- * Handles AJAX responses.
- *
- * @param object data
- * @param string textStatus
- * @param jQuery jqXHR
- */
- _success: function(data, textStatus, jqXHR) {
- var $length = this._updateData.length;
- if (!$length) {
- return;
- }
+ /**
+ * Handles AJAX responses.
+ *
+ * @param object data
+ * @param string textStatus
+ * @param jQuery jqXHR
+ */
+ _success: function (data, textStatus, jqXHR) {
+ var $length = this._updateData.length;
+ if (!$length) {
+ return;
+ }
+
+ this._updateState(data);
+
+ this._updateData = [];
+ },
- this._updateState(data);
+ /**
+ * Update element states based upon update data.
+ *
+ * @param object data
+ */
+ _updateState: function (data) {
+ },
- this._updateData = [ ];
- },
-
- /**
- * Update element states based upon update data.
- *
- * @param object data
- */
- _updateState: function(data) { },
-
- /**
- * Handles clicks within dropdown.
- *
- * @param object event
- */
- _click: function(event) {
- var $listItem = $(event.currentTarget);
- var $elementID = $listItem.data('elementID');
- var $optionName = $listItem.data('optionName');
+ /**
+ * Handles clicks within dropdown.
+ *
+ * @param object event
+ */
+ _click: function (event) {
+ var $listItem = $(event.currentTarget);
+ var $elementID = $listItem.data('elementID');
+ var $optionName = $listItem.data('optionName');
+
+ if (!this._execute($elementID, $optionName)) {
+ this._executeCallback($elementID, $optionName);
+ }
+
+ this._hide($elementID);
+ },
- if (!this._execute($elementID, $optionName)) {
- this._executeCallback($elementID, $optionName);
- }
+ /**
+ * Executes actions associated with an option.
+ *
+ * @param string elementID
+ * @param string optionName
+ * @return boolean
+ */
+ _execute: function (elementID, optionName) {
+ return false;
+ },
- this._hide($elementID);
- },
-
- /**
- * Executes actions associated with an option.
- *
- * @param string elementID
- * @param string optionName
- * @return boolean
- */
- _execute: function(elementID, optionName) {
- return false;
- },
-
- /**
- * Executes actions associated with an option provided by callbacks.
- *
- * @param string elementID
- * @param string optionName
- * @return boolean
- */
- _executeCallback: function(elementID, optionName) {
- var $length = this._callbacks.length;
- if ($length) {
- for (var $i = 0; $i < $length; $i++) {
- if (this._callbacks[$i].execute(this._elements[elementID], optionName)) {
- return true;
+ /**
+ * Executes actions associated with an option provided by callbacks.
+ *
+ * @param string elementID
+ * @param string optionName
+ * @return boolean
+ */
+ _executeCallback: function (elementID, optionName) {
+ var $length = this._callbacks.length;
+ if ($length) {
+ for (var $i = 0; $i < $length; $i++) {
+ if (this._callbacks[$i].execute(this._elements[elementID], optionName)) {
+ return true;
+ }
}
}
- }
+
+ return false;
+ },
- return false;
- },
-
- /**
- * Hides a dropdown menu.
- *
- * @param string elementID
- */
- _hide: function(elementID) {
- if (this._dropdowns[elementID]) {
- this._dropdowns[elementID].empty().removeClass('dropdownOpen');
+ /**
+ * Hides a dropdown menu.
+ *
+ * @param string elementID
+ */
+ _hide: function (elementID) {
+ if (this._dropdowns[elementID]) {
+ this._dropdowns[elementID].empty().removeClass('dropdownOpen');
+ }
}
- }
-});
-
-/**
- * Default implementation for ajax file uploads.
- *
- * @deprecated Use WoltLabSuite/Core/Upload
- *
- * @param jquery buttonSelector
- * @param jquery fileListSelector
- * @param string className
- * @param jquery options
- */
-WCF.Upload = Class.extend({
- /**
- * name of the upload field
- * @var string
- */
- _name: '__files[]',
-
- /**
- * button selector
- * @var jQuery
- */
- _buttonSelector: null,
-
- /**
- * file list selector
- * @var jQuery
- */
- _fileListSelector: null,
-
- /**
- * upload file
- * @var jQuery
- */
- _fileUpload: null,
-
- /**
- * class name
- * @var string
- */
- _className: '',
-
- /**
- * iframe for IE<10 fallback
- * @var jQuery
- */
- _iframe: null,
-
- /**
- * internal file id
- * @var integer
- */
- _internalFileID: 0,
-
- /**
- * additional options
- * @var jQuery
- */
- _options: {},
-
- /**
- * upload matrix
- * @var array
- */
- _uploadMatrix: [],
-
- /**
- * true, if the active user's browser supports ajax file uploads
- * @var boolean
- */
- _supportsAJAXUpload: true,
-
- /**
- * fallback overlay for stupid browsers
- * @var jquery
- */
- _overlay: null,
+ });
/**
- * Initializes a new upload handler.
- *
- * @param string buttonSelector
- * @param string fileListSelector
- * @param string className
- * @param object options
+ * Default implementation for ajax file uploads.
+ *
+ * @deprecated Use WoltLabSuite/Core/Upload
+ *
+ * @param jquery buttonSelector
+ * @param jquery fileListSelector
+ * @param string className
+ * @param jquery options
*/
- init: function(buttonSelector, fileListSelector, className, options) {
- this._buttonSelector = buttonSelector;
- this._fileListSelector = fileListSelector;
- this._className = className;
- this._internalFileID = 0;
- this._options = $.extend(true, {
- action: 'upload',
- multiple: false,
- url: 'index.php?ajax-upload/&t=' + SECURITY_TOKEN
- }, options || { });
+ WCF.Upload = Class.extend({
+ /**
+ * name of the upload field
+ * @var string
+ */
+ _name: '__files[]',
- this._options.url = WCF.convertLegacyURL(this._options.url);
- if (this._options.url.indexOf('index.php') === 0) {
- this._options.url = WSC_API_URL + this._options.url;
- }
+ /**
+ * button selector
+ * @var jQuery
+ */
+ _buttonSelector: null,
+
+ /**
+ * file list selector
+ * @var jQuery
+ */
+ _fileListSelector: null,
- // check for ajax upload support
- var $xhr = new XMLHttpRequest();
- this._supportsAJAXUpload = ($xhr && ('upload' in $xhr) && ('onprogress' in $xhr.upload));
+ /**
+ * upload file
+ * @var jQuery
+ */
+ _fileUpload: null,
- // create upload button
- this._createButton();
- },
-
- /**
- * Creates the upload button.
- */
- _createButton: function() {
- if (this._supportsAJAXUpload) {
- this._fileUpload = $('<input type="file" name="' + this._name + '" ' + (this._options.multiple ? 'multiple="true" ' : '') + '/>');
- this._fileUpload.change($.proxy(this._upload, this));
- var $button = $('<p class="button uploadButton"><span>' + WCF.Language.get('wcf.global.button.upload') + '</span></p>');
- $button.prepend(this._fileUpload);
- }
- else {
- var $button = $('<p class="button uploadFallbackButton"><span>' + WCF.Language.get('wcf.global.button.upload') + '</span></p>');
- $button.click($.proxy(this._showOverlay, this));
- }
+ /**
+ * class name
+ * @var string
+ */
+ _className: '',
- this._insertButton($button);
- },
-
- /**
- * Inserts the upload button.
- *
- * @param jQuery button
- */
- _insertButton: function(button) {
- this._buttonSelector.prepend(button);
- },
-
- /**
- * Removes the upload button.
- */
- _removeButton: function() {
- var $selector = '.uploadButton';
- if (!this._supportsAJAXUpload) {
- $selector = '.uploadFallbackButton';
- }
+ /**
+ * iframe for IE<10 fallback
+ * @var jQuery
+ */
+ _iframe: null,
- this._buttonSelector.find($selector).remove();
- },
-
- /**
- * Callback for file uploads.
- *
- * @param object event
- * @param File file
- * @param Blob blob
- * @return integer
- */
- _upload: function(event, file, blob) {
- var $uploadID = null;
- var $files = [ ];
- if (file) {
- $files.push(file);
- }
- else if (blob) {
- var $ext = '';
- switch (blob.type) {
- case 'image/png':
- $ext = '.png';
- break;
-
- case 'image/jpeg':
- $ext = '.jpg';
- break;
-
- case 'image/gif':
- $ext = '.gif';
- break;
+ /**
+ * internal file id
+ * @var integer
+ */
+ _internalFileID: 0,
+
+ /**
+ * additional options
+ * @var jQuery
+ */
+ _options: {},
+
+ /**
+ * upload matrix
+ * @var array
+ */
+ _uploadMatrix: [],
+
+ /**
+ * true, if the active user's browser supports ajax file uploads
+ * @var boolean
+ */
+ _supportsAJAXUpload: true,
+
+ /**
+ * fallback overlay for stupid browsers
+ * @var jquery
+ */
+ _overlay: null,
+
+ /**
+ * Initializes a new upload handler.
+ *
+ * @param string buttonSelector
+ * @param string fileListSelector
+ * @param string className
+ * @param object options
+ */
+ init: function (buttonSelector, fileListSelector, className, options) {
+ this._buttonSelector = buttonSelector;
+ this._fileListSelector = fileListSelector;
+ this._className = className;
+ this._internalFileID = 0;
+ this._options = $.extend(true, {
+ action: 'upload',
+ multiple: false,
+ url: 'index.php?ajax-upload/&t=' + SECURITY_TOKEN
+ }, options || {});
+
+ this._options.url = WCF.convertLegacyURL(this._options.url);
+ if (this._options.url.indexOf('index.php') === 0) {
+ this._options.url = WSC_API_URL + this._options.url;
}
- $files.push({
- name: 'pasted-from-clipboard' + $ext
- });
- }
- else {
- $files = this._fileUpload.prop('files');
- }
+ // check for ajax upload support
+ var $xhr = new XMLHttpRequest();
+ this._supportsAJAXUpload = ($xhr && ('upload' in $xhr) && ('onprogress' in $xhr.upload));
+
+ // create upload button
+ this._createButton();
+ },
- if ($files.length) {
- var $fd = new FormData();
- $uploadID = this._createUploadMatrix($files);
+ /**
+ * Creates the upload button.
+ */
+ _createButton: function () {
+ if (this._supportsAJAXUpload) {
+ this._fileUpload = $('<input type="file" name="' + this._name + '" ' + (this._options.multiple ? 'multiple="true" ' : '') + '/>');
+ this._fileUpload.change($.proxy(this._upload, this));
+ var $button = $('<p class="button uploadButton"><span>' + WCF.Language.get('wcf.global.button.upload') + '</span></p>');
+ $button.prepend(this._fileUpload);
+ }
+ else {
+ var $button = $('<p class="button uploadFallbackButton"><span>' + WCF.Language.get('wcf.global.button.upload') + '</span></p>');
+ $button.click($.proxy(this._showOverlay, this));
+ }
- // no more files left, abort
- if (!this._uploadMatrix[$uploadID].length) {
- return null;
+ this._insertButton($button);
+ },
+
+ /**
+ * Inserts the upload button.
+ *
+ * @param jQuery button
+ */
+ _insertButton: function (button) {
+ this._buttonSelector.prepend(button);
+ },
+
+ /**
+ * Removes the upload button.
+ */
+ _removeButton: function () {
+ var $selector = '.uploadButton';
+ if (!this._supportsAJAXUpload) {
+ $selector = '.uploadFallbackButton';
}
- for (var $i = 0, $length = $files.length; $i < $length; $i++) {
- if (this._uploadMatrix[$uploadID][$i]) {
- var $internalFileID = this._uploadMatrix[$uploadID][$i].data('internalFileID');
+ this._buttonSelector.find($selector).remove();
+ },
+
+ /**
+ * Callback for file uploads.
+ *
+ * @param object event
+ * @param File file
+ * @param Blob blob
+ * @return integer
+ */
+ _upload: function (event, file, blob) {
+ var $uploadID = null;
+ var $files = [];
+ if (file) {
+ $files.push(file);
+ }
+ else if (blob) {
+ var $ext = '';
+ switch (blob.type) {
+ case 'image/png':
+ $ext = '.png';
+ break;
- if (blob) {
- $fd.append('__files[' + $internalFileID + ']', blob, $files[$i].name);
- }
- else {
- $fd.append('__files[' + $internalFileID + ']', $files[$i]);
- }
+ case 'image/jpeg':
+ $ext = '.jpg';
+ break;
+
+ case 'image/gif':
+ $ext = '.gif';
+ break;
}
+
+ $files.push({
+ name: 'pasted-from-clipboard' + $ext
+ });
}
-
- $fd.append('actionName', this._options.action);
- $fd.append('className', this._className);
- var $additionalParameters = this._getParameters();
- for (var $name in $additionalParameters) {
- $fd.append('parameters[' + $name + ']', $additionalParameters[$name]);
+ else {
+ $files = this._fileUpload.prop('files');
}
- var self = this;
- $.ajax({
- type: 'POST',
- url: this._options.url,
- enctype: 'multipart/form-data',
- data: $fd,
- contentType: false,
- processData: false,
- success: function(data, textStatus, jqXHR) {
- self._success($uploadID, data);
- },
- error: $.proxy(this._error, this),
- xhr: function() {
- var $xhr = $.ajaxSettings.xhr();
- if ($xhr) {
- $xhr.upload.addEventListener('progress', function(event) {
- self._progress($uploadID, event);
- }, false);
+ if ($files.length) {
+ var $fd = new FormData();
+ $uploadID = this._createUploadMatrix($files);
+
+ // no more files left, abort
+ if (!this._uploadMatrix[$uploadID].length) {
+ return null;
+ }
+
+ for (var $i = 0, $length = $files.length; $i < $length; $i++) {
+ if (this._uploadMatrix[$uploadID][$i]) {
+ var $internalFileID = this._uploadMatrix[$uploadID][$i].data('internalFileID');
+
+ if (blob) {
+ $fd.append('__files[' + $internalFileID + ']', blob, $files[$i].name);
+ }
+ else {
+ $fd.append('__files[' + $internalFileID + ']', $files[$i]);
+ }
}
- return $xhr;
- },
- xhrFields: {
- withCredentials: true
}
- });
- }
-
- return $uploadID;
- },
-
- /**
- * Creates upload matrix for provided files.
- *
- * @param array<object> files
- * @return integer
- */
- _createUploadMatrix: function(files) {
- if (files.length) {
- var $uploadID = this._uploadMatrix.length;
- this._uploadMatrix[$uploadID] = [ ];
-
- for (var $i = 0, $length = files.length; $i < $length; $i++) {
- var $file = files[$i];
- var $li = this._initFile($file);
- if (!$li.hasClass('uploadFailed')) {
- $li.data('filename', $file.name).data('internalFileID', this._internalFileID++);
- this._uploadMatrix[$uploadID][$i] = $li;
+ $fd.append('actionName', this._options.action);
+ $fd.append('className', this._className);
+ var $additionalParameters = this._getParameters();
+ for (var $name in $additionalParameters) {
+ $fd.append('parameters[' + $name + ']', $additionalParameters[$name]);
}
+
+ var self = this;
+ $.ajax({
+ type: 'POST',
+ url: this._options.url,
+ enctype: 'multipart/form-data',
+ data: $fd,
+ contentType: false,
+ processData: false,
+ success: function (data, textStatus, jqXHR) {
+ self._success($uploadID, data);
+ },
+ error: $.proxy(this._error, this),
+ xhr: function () {
+ var $xhr = $.ajaxSettings.xhr();
+ if ($xhr) {
+ $xhr.upload.addEventListener('progress', function (event) {
+ self._progress($uploadID, event);
+ }, false);
+ }
+ return $xhr;
+ },
+ xhrFields: {
+ withCredentials: true
+ }
+ });
}
return $uploadID;