Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / WCF.User.js
1 /**
2 * User-related classes.
3 *
4 * @author Alexander Ebert
5 * @copyright 2001-2014 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7 */
8
9 /**
10 * User login
11 *
12 * @param boolean isQuickLogin
13 */
14 WCF.User.Login = Class.extend({
15 /**
16 * login button
17 * @var jQuery
18 */
19 _loginSubmitButton: null,
20
21 /**
22 * password input
23 * @var jQuery
24 */
25 _password: null,
26
27 /**
28 * password input container
29 * @var jQuery
30 */
31 _passwordContainer: null,
32
33 /**
34 * cookie input
35 * @var jQuery
36 */
37 _useCookies: null,
38
39 /**
40 * cookie input container
41 * @var jQuery
42 */
43 _useCookiesContainer: null,
44
45 /**
46 * Initializes the user login
47 *
48 * @param boolean isQuickLogin
49 */
50 init: function(isQuickLogin) {
51 this._loginSubmitButton = $('#loginSubmitButton');
52 this._password = $('#password'),
53 this._passwordContainer = this._password.parents('dl');
54 this._useCookies = $('#useCookies');
55 this._useCookiesContainer = this._useCookies.parents('dl');
56
57 var $loginForm = $('#loginForm');
58 $loginForm.find('input[name=action]').change($.proxy(this._change, this));
59
60 if (isQuickLogin) {
61 WCF.User.QuickLogin.init();
62 }
63 },
64
65 /**
66 * Handle toggle between login and register.
67 *
68 * @param object event
69 */
70 _change: function(event) {
71 if ($(event.currentTarget).val() === 'register') {
72 this._setState(false, WCF.Language.get('wcf.user.button.register'));
73 }
74 else {
75 this._setState(true, WCF.Language.get('wcf.user.button.login'));
76 }
77 },
78
79 /**
80 * Sets form states.
81 *
82 * @param boolean enable
83 * @param string buttonTitle
84 */
85 _setState: function(enable, buttonTitle) {
86 if (enable) {
87 this._password.enable();
88 this._passwordContainer.removeClass('disabled');
89 this._useCookies.enable();
90 this._useCookiesContainer.removeClass('disabled');
91 }
92 else {
93 this._password.disable();
94 this._passwordContainer.addClass('disabled');
95 this._useCookies.disable();
96 this._useCookiesContainer.addClass('disabled');
97 }
98
99 this._loginSubmitButton.val(buttonTitle);
100 }
101 });
102
103 /**
104 * Quick login box
105 */
106 WCF.User.QuickLogin = {
107 /**
108 * dialog overlay
109 * @var jQuery
110 */
111 _dialog: null,
112
113 /**
114 * login message container
115 * @var jQuery
116 */
117 _loginMessage: null,
118
119 /**
120 * Initializes the quick login box
121 */
122 init: function() {
123 $('.loginLink').click($.proxy(this._render, this));
124
125 // prepend protocol and hostname
126 $('#loginForm input[name=url]').val(function(index, value) {
127 return window.location.protocol + '//' + window.location.host + value;
128 });
129 },
130
131 /**
132 * Displays the quick login box with a info message
133 *
134 * @param string message
135 */
136 show: function(message) {
137 if (message) {
138 if (this._loginMessage === null) {
139 this._loginMessage = $('<p class="info" />').hide().prependTo($('#loginForm > form'));
140 }
141
142 this._loginMessage.show().text(message);
143 }
144 else if (this._loginMessage !== null) {
145 this._loginMessage.hide();
146 }
147
148 this._render();
149 },
150
151 /**
152 * Renders the dialog
153 *
154 * @param jQuery.Event event
155 */
156 _render: function(event) {
157 if (event !== undefined) {
158 event.preventDefault();
159 }
160
161 if (this._dialog === null) {
162 this._dialog = $('#loginForm').wcfDialog({
163 title: WCF.Language.get('wcf.user.login')
164 });
165 this._dialog.find('#username').focus();
166 }
167 else {
168 this._dialog.wcfDialog('open');
169 }
170 }
171 };
172
173 /**
174 * UserProfile namespace
175 */
176 WCF.User.Profile = {};
177
178 /**
179 * Shows the activity point list for users.
180 */
181 WCF.User.Profile.ActivityPointList = {
182 /**
183 * list of cached templates
184 * @var object
185 */
186 _cache: { },
187
188 /**
189 * dialog overlay
190 * @var jQuery
191 */
192 _dialog: null,
193
194 /**
195 * initialization state
196 * @var boolean
197 */
198 _didInit: false,
199
200 /**
201 * action proxy
202 * @var WCF.Action.Proxy
203 */
204 _proxy: null,
205
206 /**
207 * Initializes the WCF.User.Profile.ActivityPointList class.
208 */
209 init: function() {
210 if (this._didInit) {
211 return;
212 }
213
214 this._cache = { };
215 this._dialog = null;
216 this._proxy = new WCF.Action.Proxy({
217 success: $.proxy(this._success, this)
218 });
219
220 this._init();
221
222 WCF.DOMNodeInsertedHandler.addCallback('WCF.User.Profile.ActivityPointList', $.proxy(this._init, this));
223
224 this._didInit = true;
225 },
226
227 /**
228 * Initializes display for activity points.
229 */
230 _init: function() {
231 $('.activityPointsDisplay').removeClass('activityPointsDisplay').click($.proxy(this._click, this));
232 },
233
234 /**
235 * Shows or loads the activity point for selected user.
236 *
237 * @param object event
238 */
239 _click: function(event) {
240 var $userID = $(event.currentTarget).data('userID');
241
242 if (this._cache[$userID] === undefined) {
243 this._proxy.setOption('data', {
244 actionName: 'getDetailedActivityPointList',
245 className: 'wcf\\data\\user\\UserProfileAction',
246 objectIDs: [ $userID ]
247 });
248 this._proxy.sendRequest();
249 }
250 else {
251 this._show($userID);
252 }
253 },
254
255 /**
256 * Displays activity points for given user.
257 *
258 * @param integer userID
259 */
260 _show: function(userID) {
261 if (this._dialog === null) {
262 this._dialog = $('<div>' + this._cache[userID] + '</div>').hide().appendTo(document.body);
263 this._dialog.wcfDialog({
264 title: WCF.Language.get('wcf.user.activityPoint')
265 });
266 }
267 else {
268 this._dialog.html(this._cache[userID]);
269 this._dialog.wcfDialog('open');
270 }
271 },
272
273 /**
274 * Handles successful AJAX requests.
275 *
276 * @param object data
277 * @param string textStatus
278 * @param jQuery jqXHR
279 */
280 _success: function(data, textStatus, jqXHR) {
281 this._cache[data.returnValues.userID] = data.returnValues.template;
282 this._show(data.returnValues.userID);
283 }
284 };
285
286 /**
287 * Provides methods to follow an user.
288 *
289 * @param integer userID
290 * @param boolean following
291 */
292 WCF.User.Profile.Follow = Class.extend({
293 /**
294 * follow button
295 * @var jQuery
296 */
297 _button: null,
298
299 /**
300 * true if following current user
301 * @var boolean
302 */
303 _following: false,
304
305 /**
306 * action proxy object
307 * @var WCF.Action.Proxy
308 */
309 _proxy: null,
310
311 /**
312 * user id
313 * @var integer
314 */
315 _userID: 0,
316
317 /**
318 * Creates a new follow object.
319 *
320 * @param integer userID
321 * @param boolean following
322 */
323 init: function (userID, following) {
324 this._following = following;
325 this._userID = userID;
326 this._proxy = new WCF.Action.Proxy({
327 success: $.proxy(this._success, this)
328 });
329
330 this._createButton();
331 this._showButton();
332 },
333
334 /**
335 * Creates the (un-)follow button
336 */
337 _createButton: function () {
338 this._button = $('<li id="followUser"><a class="button jsTooltip" title="'+WCF.Language.get('wcf.user.button.'+(this._following ? 'un' : '')+'follow')+'"><span class="icon icon16 icon-plus"></span> <span class="invisible">'+WCF.Language.get('wcf.user.button.'+(this._following ? 'un' : '')+'follow')+'</span></a></li>').prependTo($('#profileButtonContainer'));
339 this._button.click($.proxy(this._execute, this));
340 },
341
342 /**
343 * Follows or unfollows an user.
344 */
345 _execute: function () {
346 var $actionName = (this._following) ? 'unfollow' : 'follow';
347 this._proxy.setOption('data', {
348 'actionName': $actionName,
349 'className': 'wcf\\data\\user\\follow\\UserFollowAction',
350 'parameters': {
351 data: {
352 userID: this._userID
353 }
354 }
355 });
356 this._proxy.sendRequest();
357 },
358
359 /**
360 * Displays current follow state.
361 */
362 _showButton: function () {
363 if (this._following) {
364 this._button.find('.button').data('tooltip', WCF.Language.get('wcf.user.button.unfollow')).addClass('active').children('.icon').removeClass('icon-plus').addClass('icon-minus');
365 }
366 else {
367 this._button.find('.button').data('tooltip', WCF.Language.get('wcf.user.button.follow')).removeClass('active').children('.icon').removeClass('icon-minus').addClass('icon-plus');
368 }
369 },
370
371 /**
372 * Update object state on success.
373 *
374 * @param object data
375 * @param string textStatus
376 * @param jQuery jqXHR
377 */
378 _success: function (data, textStatus, jqXHR) {
379 this._following = data.returnValues.following;
380 this._showButton();
381
382 var $notification = new WCF.System.Notification();
383 $notification.show();
384 }
385 });
386
387 /**
388 * Provides methods to manage ignored users.
389 *
390 * @param integer userID
391 * @param boolean isIgnoredUser
392 */
393 WCF.User.Profile.IgnoreUser = Class.extend({
394 /**
395 * ignore button
396 * @var jQuery
397 */
398 _button: null,
399
400 /**
401 * ignore state
402 * @var boolean
403 */
404 _isIgnoredUser: false,
405
406 /**
407 * ajax proxy object
408 * @var WCF.Action.Proxy
409 */
410 _proxy: null,
411
412 /**
413 * target user id
414 * @var integer
415 */
416 _userID: 0,
417
418 /**
419 * Initializes methods to manage an ignored user.
420 *
421 * @param integer userID
422 * @param boolean isIgnoredUser
423 */
424 init: function(userID, isIgnoredUser) {
425 this._userID = userID;
426 this._isIgnoredUser = isIgnoredUser;
427
428 // initialize proxy
429 this._proxy = new WCF.Action.Proxy({
430 success: $.proxy(this._success, this)
431 });
432
433 // handle button
434 this._updateButton();
435 this._button.click($.proxy(this._click, this));
436 },
437
438 /**
439 * Handle clicks, might cause 'ignore' or 'unignore' to be triggered.
440 */
441 _click: function() {
442 var $action = (this._isIgnoredUser) ? 'unignore' : 'ignore';
443
444 this._proxy.setOption('data', {
445 actionName: $action,
446 className: 'wcf\\data\\user\\ignore\\UserIgnoreAction',
447 parameters: {
448 data: {
449 ignoreUserID: this._userID
450 }
451 }
452 });
453
454 this._proxy.sendRequest();
455 },
456
457 /**
458 * Updates button label and function upon successful request.
459 *
460 * @param object data
461 * @param string textStatus
462 * @param jQuery jqXHR
463 */
464 _success: function(data, textStatus, jqXHR) {
465 this._isIgnoredUser = data.returnValues.isIgnoredUser;
466 this._updateButton();
467
468 var $notification = new WCF.System.Notification();
469 $notification.show();
470 },
471
472 /**
473 * Updates button label and inserts it if not exists.
474 */
475 _updateButton: function() {
476 if (this._button === null) {
477 this._button = $('<li id="ignoreUser"><a class="button jsTooltip" title="'+WCF.Language.get('wcf.user.button.'+(this._isIgnoredUser ? 'un' : '')+'ignore')+'"><span class="icon icon16 icon-ban-circle"></span> <span class="invisible">'+WCF.Language.get('wcf.user.button.'+(this._isIgnoredUser ? 'un' : '')+'ignore')+'</span></a></li>').prependTo($('#profileButtonContainer'));
478 }
479
480 this._button.find('.button').data('tooltip', WCF.Language.get('wcf.user.button.' + (this._isIgnoredUser ? 'un' : '') + 'ignore'));
481 if (this._isIgnoredUser) this._button.find('.button').addClass('active').children('.icon').removeClass('icon-ban-circle').addClass('icon-circle-blank');
482 else this._button.find('.button').removeClass('active').children('.icon').removeClass('icon-circle-blank').addClass('icon-ban-circle');
483 }
484 });
485
486 /**
487 * Provides methods to load tab menu content upon request.
488 */
489 WCF.User.Profile.TabMenu = Class.extend({
490 /**
491 * list of containers
492 * @var object
493 */
494 _hasContent: { },
495
496 /**
497 * profile content
498 * @var jQuery
499 */
500 _profileContent: null,
501
502 /**
503 * action proxy
504 * @var WCF.Action.Proxy
505 */
506 _proxy: null,
507
508 /**
509 * target user id
510 * @var integer
511 */
512 _userID: 0,
513
514 /**
515 * Initializes the tab menu loader.
516 *
517 * @param integer userID
518 */
519 init: function(userID) {
520 this._profileContent = $('#profileContent');
521 this._userID = userID;
522
523 var $activeMenuItem = this._profileContent.data('active');
524 var $enableProxy = false;
525
526 // fetch content state
527 this._profileContent.find('div.tabMenuContent').each($.proxy(function(index, container) {
528 var $containerID = $(container).wcfIdentify();
529
530 if ($activeMenuItem === $containerID) {
531 this._hasContent[$containerID] = true;
532 }
533 else {
534 this._hasContent[$containerID] = false;
535 $enableProxy = true;
536 }
537 }, this));
538
539 // enable loader if at least one container is empty
540 if ($enableProxy) {
541 this._proxy = new WCF.Action.Proxy({
542 success: $.proxy(this._success, this)
543 });
544
545 this._profileContent.bind('wcftabsbeforeactivate', $.proxy(this._loadContent, this));
546 }
547 },
548
549 /**
550 * Prepares to load content once tabs are being switched.
551 *
552 * @param object event
553 * @param object ui
554 */
555 _loadContent: function(event, ui) {
556 var $panel = $(ui.newPanel);
557 var $containerID = $panel.attr('id');
558
559 if (!this._hasContent[$containerID]) {
560 this._proxy.setOption('data', {
561 actionName: 'getContent',
562 className: 'wcf\\data\\user\\profile\\menu\\item\\UserProfileMenuItemAction',
563 parameters: {
564 data: {
565 containerID: $containerID,
566 menuItem: $panel.data('menuItem'),
567 userID: this._userID
568 }
569 }
570 });
571 this._proxy.sendRequest();
572 }
573 },
574
575 /**
576 * Shows previously requested content.
577 *
578 * @param object data
579 * @param string textStatus
580 * @param jQuery jqXHR
581 */
582 _success: function(data, textStatus, jqXHR) {
583 var $containerID = data.returnValues.containerID;
584 this._hasContent[$containerID] = true;
585
586 // insert content
587 var $content = this._profileContent.find('#' + $containerID);
588 $('<div>' + data.returnValues.template + '</div>').hide().appendTo($content);
589
590 // slide in content
591 $content.children('div').wcfBlindIn();
592 }
593 });
594
595 /**
596 * User profile inline editor.
597 *
598 * @param integer userID
599 * @param boolean editOnInit
600 */
601 WCF.User.Profile.Editor = Class.extend({
602 /**
603 * current action
604 * @var string
605 */
606 _actionName: '',
607
608 /**
609 * list of interface buttons
610 * @var object
611 */
612 _buttons: { },
613
614 /**
615 * cached tab content
616 * @var string
617 */
618 _cachedTemplate: '',
619
620 /**
621 * action proxy
622 * @var WCF.Action.Proxy
623 */
624 _proxy: null,
625
626 /**
627 * tab object
628 * @var jQuery
629 */
630 _tab: null,
631
632 /**
633 * target user id
634 * @var integer
635 */
636 _userID: 0,
637
638 /**
639 * Initializes the WCF.User.Profile.Editor object.
640 *
641 * @param integer userID
642 * @param boolean editOnInit
643 */
644 init: function(userID, editOnInit) {
645 this._actionName = '';
646 this._cachedTemplate = '';
647 this._tab = $('#about');
648 this._userID = userID;
649 this._proxy = new WCF.Action.Proxy({
650 success: $.proxy(this._success, this)
651 });
652
653 this._initButtons();
654
655 // begin editing on page load
656 if (editOnInit) {
657 this._beginEdit();
658 }
659 },
660
661 /**
662 * Initializes interface buttons.
663 */
664 _initButtons: function() {
665 var $buttonContainer = $('#profileButtonContainer');
666
667 // create buttons
668 this._buttons = {
669 beginEdit: $('<li><a class="button"><span class="icon icon16 icon-pencil" /> <span>' + WCF.Language.get('wcf.user.editProfile') + '</span></a></li>').click($.proxy(this._beginEdit, this)).appendTo($buttonContainer)
670 };
671 },
672
673 /**
674 * Begins editing.
675 */
676 _beginEdit: function() {
677 this._actionName = 'beginEdit';
678 this._buttons.beginEdit.hide();
679 $('#profileContent').wcfTabs('select', 'about');
680
681 // load form
682 this._proxy.setOption('data', {
683 actionName: 'beginEdit',
684 className: 'wcf\\data\\user\\UserProfileAction',
685 objectIDs: [ this._userID ]
686 });
687 this._proxy.sendRequest();
688 },
689
690 /**
691 * Saves input values.
692 */
693 _save: function() {
694 this._actionName = 'save';
695
696 // collect values
697 var $regExp = /values\[([a-zA-Z0-9._-]+)\]/;
698 var $values = { };
699 this._tab.find('input, textarea, select').each(function(index, element) {
700 var $element = $(element);
701
702 if ($element.getTagName() === 'input') {
703 var $type = $element.attr('type');
704
705 if (($type === 'radio' || $type === 'checkbox') && !$element.prop('checked')) {
706 return;
707 }
708 }
709
710 var $name = $element.attr('name');
711 if ($regExp.test($name)) {
712 $values[RegExp.$1] = $element.val();
713 }
714 });
715
716 this._proxy.setOption('data', {
717 actionName: 'save',
718 className: 'wcf\\data\\user\\UserProfileAction',
719 objectIDs: [ this._userID ],
720 parameters: {
721 values: $values
722 }
723 });
724 this._proxy.sendRequest();
725 },
726
727 /**
728 * Restores back to default view.
729 */
730 _restore: function() {
731 this._actionName = 'restore';
732 this._buttons.beginEdit.show();
733
734 this._destroyCKEditor();
735
736 this._tab.html(this._cachedTemplate).children().css({ height: 'auto' });
737 },
738
739 /**
740 * Handles successful AJAX requests.
741 *
742 * @param object data
743 * @param string textStatus
744 * @param jQuery jqXHR
745 */
746 _success: function(data, textStatus, jqXHR) {
747 switch (this._actionName) {
748 case 'beginEdit':
749 this._prepareEdit(data);
750 break;
751
752 case 'save':
753 // save was successful, show parsed template
754 if (data.returnValues.success) {
755 this._cachedTemplate = data.returnValues.template;
756 this._restore();
757 }
758 else {
759 this._prepareEdit(data, true);
760 }
761 break;
762 }
763 },
764
765 /**
766 * Prepares editing mode.
767 *
768 * @param object data
769 * @param boolean disableCache
770 */
771 _prepareEdit: function(data, disableCache) {
772 this._destroyCKEditor();
773
774 // update template
775 var self = this;
776 this._tab.html(function(index, oldHTML) {
777 if (disableCache !== true) {
778 self._cachedTemplate = oldHTML;
779 }
780
781 return data.returnValues.template;
782 });
783
784 // block autocomplete
785 this._tab.find('input[type=text]').attr('autocomplete', 'off');
786
787 // bind event listener
788 this._tab.find('.formSubmit > button[data-type=save]').click($.proxy(this._save, this));
789 this._tab.find('.formSubmit > button[data-type=restore]').click($.proxy(this._restore, this));
790 this._tab.find('input').keyup(function(event) {
791 if (event.which === 13) { // Enter
792 self._save();
793
794 event.preventDefault();
795 return false;
796 }
797 });
798 },
799
800 /**
801 * Destroys all CKEditor instances within current tab.
802 */
803 _destroyCKEditor: function() {
804 // destroy all CKEditor instances
805 this._tab.find('textarea + .cke').each(function(index, container) {
806 var $instanceName = $(container).attr('id').replace(/cke_/, '');
807 if (CKEDITOR.instances[$instanceName]) {
808 CKEDITOR.instances[$instanceName].destroy();
809 }
810 });
811 }
812 });
813
814 /**
815 * Namespace for registration functions.
816 */
817 WCF.User.Registration = {};
818
819 /**
820 * Validates the password.
821 *
822 * @param jQuery element
823 * @param jQuery confirmElement
824 * @param object options
825 */
826 WCF.User.Registration.Validation = Class.extend({
827 /**
828 * action name
829 * @var string
830 */
831 _actionName: '',
832
833 /**
834 * class name
835 * @var string
836 */
837 _className: '',
838
839 /**
840 * confirmation input element
841 * @var jQuery
842 */
843 _confirmElement: null,
844
845 /**
846 * input element
847 * @var jQuery
848 */
849 _element: null,
850
851 /**
852 * list of error messages
853 * @var object
854 */
855 _errorMessages: { },
856
857 /**
858 * list of additional options
859 * @var object
860 */
861 _options: { },
862
863 /**
864 * AJAX proxy
865 * @var WCF.Action.Proxy
866 */
867 _proxy: null,
868
869 /**
870 * Initializes the validation.
871 *
872 * @param jQuery element
873 * @param jQuery confirmElement
874 * @param object options
875 */
876 init: function(element, confirmElement, options) {
877 this._element = element;
878 this._element.blur($.proxy(this._blur, this));
879 this._confirmElement = confirmElement || null;
880
881 if (this._confirmElement !== null) {
882 this._confirmElement.blur($.proxy(this._blurConfirm, this));
883 }
884
885 options = options || { };
886 this._setOptions(options);
887
888 this._proxy = new WCF.Action.Proxy({
889 success: $.proxy(this._success, this),
890 showLoadingOverlay: false
891 });
892
893 this._setErrorMessages();
894 },
895
896 /**
897 * Sets additional options
898 */
899 _setOptions: function(options) { },
900
901 /**
902 * Sets error messages.
903 */
904 _setErrorMessages: function() {
905 this._errorMessages = {
906 ajaxError: '',
907 notEqual: ''
908 };
909 },
910
911 /**
912 * Validates once focus on input is lost.
913 *
914 * @param object event
915 */
916 _blur: function(event) {
917 var $value = this._element.val();
918 if (!$value) {
919 return this._showError(this._element, WCF.Language.get('wcf.global.form.error.empty'));
920 }
921
922 if (this._confirmElement !== null) {
923 var $confirmValue = this._confirmElement.val();
924 if ($confirmValue != '' && $value != $confirmValue) {
925 return this._showError(this._confirmElement, this._errorMessages.notEqual);
926 }
927 }
928
929 if (!this._validateOptions()) {
930 return;
931 }
932
933 this._proxy.setOption('data', {
934 actionName: this._actionName,
935 className: this._className,
936 parameters: this._getParameters()
937 });
938 this._proxy.sendRequest();
939 },
940
941 /**
942 * Returns a list of parameters.
943 *
944 * @return object
945 */
946 _getParameters: function() {
947 return { };
948 },
949
950 /**
951 * Validates input by options.
952 *
953 * @return boolean
954 */
955 _validateOptions: function() {
956 return true;
957 },
958
959 /**
960 * Validates value once confirmation input focus is lost.
961 *
962 * @param object event
963 */
964 _blurConfirm: function(event) {
965 var $value = this._confirmElement.val();
966 if (!$value) {
967 return this._showError(this._confirmElement, WCF.Language.get('wcf.global.form.error.empty'));
968 }
969
970 this._blur(event);
971 },
972
973 /**
974 * Handles AJAX responses.
975 *
976 * @param object data
977 * @param string textStatus
978 * @param jQuery jqXHR
979 */
980 _success: function(data, textStatus, jqXHR) {
981 if (data.returnValues.isValid) {
982 this._showSuccess(this._element);
983 if (this._confirmElement !== null && this._confirmElement.val()) {
984 this._showSuccess(this._confirmElement);
985 }
986 }
987 else {
988 this._showError(this._element, WCF.Language.get(this._errorMessages.ajaxError + data.returnValues.error));
989 }
990 },
991
992 /**
993 * Shows an error message.
994 *
995 * @param jQuery element
996 * @param string message
997 */
998 _showError: function(element, message) {
999 element.parent().parent().addClass('formError').removeClass('formSuccess');
1000
1001 var $innerError = element.parent().find('small.innerError');
1002 if (!$innerError.length) {
1003 $innerError = $('<small />').addClass('innerError').insertAfter(element);
1004 }
1005
1006 $innerError.text(message);
1007 },
1008
1009 /**
1010 * Displays a success message.
1011 *
1012 * @param jQuery element
1013 */
1014 _showSuccess: function(element) {
1015 element.parent().parent().addClass('formSuccess').removeClass('formError');
1016 element.next('small.innerError').remove();
1017 }
1018 });
1019
1020 /**
1021 * Username validation for registration.
1022 *
1023 * @see WCF.User.Registration.Validation
1024 */
1025 WCF.User.Registration.Validation.Username = WCF.User.Registration.Validation.extend({
1026 /**
1027 * @see WCF.User.Registration.Validation._actionName
1028 */
1029 _actionName: 'validateUsername',
1030
1031 /**
1032 * @see WCF.User.Registration.Validation._className
1033 */
1034 _className: 'wcf\\data\\user\\UserRegistrationAction',
1035
1036 /**
1037 * @see WCF.User.Registration.Validation._setOptions()
1038 */
1039 _setOptions: function(options) {
1040 this._options = $.extend(true, {
1041 minlength: 3,
1042 maxlength: 25
1043 }, options);
1044 },
1045
1046 /**
1047 * @see WCF.User.Registration.Validation._setErrorMessages()
1048 */
1049 _setErrorMessages: function() {
1050 this._errorMessages = {
1051 ajaxError: 'wcf.user.username.error.'
1052 };
1053 },
1054
1055 /**
1056 * @see WCF.User.Registration.Validation._validateOptions()
1057 */
1058 _validateOptions: function() {
1059 var $value = this._element.val();
1060 if ($value.length < this._options.minlength || $value.length > this._options.maxlength) {
1061 this._showError(this._element, WCF.Language.get('wcf.user.username.error.notValid'));
1062 return false;
1063 }
1064
1065 return true;
1066 },
1067
1068 /**
1069 * @see WCF.User.Registration.Validation._getParameters()
1070 */
1071 _getParameters: function() {
1072 return {
1073 username: this._element.val()
1074 };
1075 }
1076 });
1077
1078 /**
1079 * Email validation for registration.
1080 *
1081 * @see WCF.User.Registration.Validation
1082 */
1083 WCF.User.Registration.Validation.EmailAddress = WCF.User.Registration.Validation.extend({
1084 /**
1085 * @see WCF.User.Registration.Validation._actionName
1086 */
1087 _actionName: 'validateEmailAddress',
1088
1089 /**
1090 * @see WCF.User.Registration.Validation._className
1091 */
1092 _className: 'wcf\\data\\user\\UserRegistrationAction',
1093
1094 /**
1095 * @see WCF.User.Registration.Validation._getParameters()
1096 */
1097 _getParameters: function() {
1098 return {
1099 email: this._element.val()
1100 };
1101 },
1102
1103 /**
1104 * @see WCF.User.Registration.Validation._setErrorMessages()
1105 */
1106 _setErrorMessages: function() {
1107 this._errorMessages = {
1108 ajaxError: 'wcf.user.email.error.',
1109 notEqual: WCF.Language.get('wcf.user.confirmEmail.error.notEqual')
1110 };
1111 }
1112 });
1113
1114 /**
1115 * Password validation for registration.
1116 *
1117 * @see WCF.User.Registration.Validation
1118 */
1119 WCF.User.Registration.Validation.Password = WCF.User.Registration.Validation.extend({
1120 /**
1121 * @see WCF.User.Registration.Validation._actionName
1122 */
1123 _actionName: 'validatePassword',
1124
1125 /**
1126 * @see WCF.User.Registration.Validation._className
1127 */
1128 _className: 'wcf\\data\\user\\UserRegistrationAction',
1129
1130 /**
1131 * @see WCF.User.Registration.Validation._getParameters()
1132 */
1133 _getParameters: function() {
1134 return {
1135 password: this._element.val()
1136 };
1137 },
1138
1139 /**
1140 * @see WCF.User.Registration.Validation._setErrorMessages()
1141 */
1142 _setErrorMessages: function() {
1143 this._errorMessages = {
1144 ajaxError: 'wcf.user.password.error.',
1145 notEqual: WCF.Language.get('wcf.user.confirmPassword.error.notEqual')
1146 };
1147 }
1148 });
1149
1150 /**
1151 * Toggles input fields for lost password form.
1152 */
1153 WCF.User.Registration.LostPassword = Class.extend({
1154 /**
1155 * email input
1156 * @var jQuery
1157 */
1158 _email: null,
1159
1160 /**
1161 * username input
1162 * @var jQuery
1163 */
1164 _username: null,
1165
1166 /**
1167 * Initializes LostPassword-form class.
1168 */
1169 init: function() {
1170 // bind input fields
1171 this._email = $('#emailInput');
1172 this._username = $('#usernameInput');
1173
1174 // bind event listener
1175 this._email.keyup($.proxy(this._checkEmail, this));
1176 this._username.keyup($.proxy(this._checkUsername, this));
1177
1178 if ($.browser.mozilla && $.browser.touch) {
1179 this._email.on('input', $.proxy(this._checkEmail, this));
1180 this._username.on('input', $.proxy(this._checkUsername, this));
1181 }
1182
1183 // toggle fields on init
1184 this._checkEmail();
1185 this._checkUsername();
1186 },
1187
1188 /**
1189 * Checks for content in email field and toggles username.
1190 */
1191 _checkEmail: function() {
1192 if (this._email.val() == '') {
1193 this._username.enable();
1194 this._username.parents('dl:eq(0)').removeClass('disabled');
1195 }
1196 else {
1197 this._username.disable();
1198 this._username.parents('dl:eq(0)').addClass('disabled');
1199 }
1200 },
1201
1202 /**
1203 * Checks for content in username field and toggles email.
1204 */
1205 _checkUsername: function() {
1206 if (this._username.val() == '') {
1207 this._email.enable();
1208 this._email.parents('dl:eq(0)').removeClass('disabled');
1209 }
1210 else {
1211 this._email.disable();
1212 this._email.parents('dl:eq(0)').addClass('disabled');
1213 }
1214 }
1215 });
1216
1217 /**
1218 * Notification system for WCF.
1219 *
1220 * @author Alexander Ebert
1221 * @copyright 2001-2014 WoltLab GmbH
1222 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
1223 */
1224 WCF.Notification = {};
1225
1226 /**
1227 * Loads notification for the user panel.
1228 *
1229 * @see WCF.UserPanel
1230 */
1231 WCF.Notification.UserPanel = WCF.UserPanel.extend({
1232 /**
1233 * action proxy
1234 * @var WCF.Action.Proxy
1235 */
1236 _proxy: null,
1237
1238 /**
1239 * link to show all notifications
1240 * @var string
1241 */
1242 _showAllLink: '',
1243
1244 /**
1245 * @see WCF.UserPanel.init()
1246 */
1247 init: function(showAllLink) {
1248 this._noItems = 'wcf.user.notification.noMoreNotifications';
1249 this._proxy = new WCF.Action.Proxy({
1250 success: $.proxy(this._success, this)
1251 });
1252 this._showAllLink = showAllLink;
1253
1254 this._super('userNotifications');
1255
1256 // update page title
1257 if (this._container.data('count')) {
1258 document.title = '(' + this._container.data('count') + ') ' + document.title;
1259 }
1260 },
1261
1262 /**
1263 * @see WCF.UserPanel._addDefaultItems()
1264 */
1265 _addDefaultItems: function(dropdownMenu) {
1266 this._addDivider(dropdownMenu);
1267 if (this._container.data('count')) {
1268 $('<li><a href="' + this._showAllLink + '">' + WCF.Language.get('wcf.user.notification.showAll') + '</a></li>').appendTo(dropdownMenu);
1269 this._addDivider(dropdownMenu);
1270 }
1271 $('<li id="userNotificationsMarkAllAsConfirmed"><a>' + WCF.Language.get('wcf.user.notification.markAllAsConfirmed') + '</a></li>').click($.proxy(this._markAllAsConfirmed, this)).appendTo(dropdownMenu);
1272 },
1273
1274 /**
1275 * @see WCF.UserPanel._getParameters()
1276 */
1277 _getParameters: function() {
1278 return {
1279 actionName: 'getOutstandingNotifications',
1280 className: 'wcf\\data\\user\\notification\\UserNotificationAction'
1281 };
1282 },
1283
1284 /**
1285 * @see WCF.UserPanel._after()
1286 */
1287 _after: function(dropdownMenu) {
1288 WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify()).children('li.jsNotificationItem').click($.proxy(this._markAsConfirmed, this));
1289 },
1290
1291 /**
1292 * Marks a notification as confirmed.
1293 *
1294 * @param object event
1295 */
1296 _markAsConfirmed: function(event) {
1297 this._proxy.setOption('data', {
1298 actionName: 'markAsConfirmed',
1299 className: 'wcf\\data\\user\\notification\\UserNotificationAction',
1300 parameters: {
1301 notificationID: $(event.currentTarget).data('notificationID')
1302 }
1303 });
1304 this._proxy.sendRequest();
1305 },
1306
1307 /**
1308 * Marks all notifications as confirmed.
1309 */
1310 _markAllAsConfirmed: function() {
1311 WCF.System.Confirmation.show(WCF.Language.get('wcf.user.notification.markAllAsConfirmed.confirmMessage'), $.proxy(function(action) {
1312 if (action === 'confirm') {
1313 this._proxy.setOption('data', {
1314 actionName: 'markAllAsConfirmed',
1315 className: 'wcf\\data\\user\\notification\\UserNotificationAction'
1316 });
1317 this._proxy.sendRequest();
1318 }
1319 }, this));
1320 },
1321
1322 /**
1323 * @see WCF.UserPanel._success()
1324 */
1325 _success: function(data, textStatus, jqXHR) {
1326 switch (data.actionName) {
1327 case 'markAllAsConfirmed':
1328 $('.jsNotificationItem').remove();
1329 // remove notification count
1330 document.title = document.title.replace(/^\(([0-9]+)\) /, '');
1331 // fall through
1332 case 'getOutstandingNotifications':
1333 if (!data.returnValues || !data.returnValues.template) {
1334 $('#userNotificationsMarkAllAsConfirmed').prev('.dropdownDivider').remove();
1335 $('#userNotificationsMarkAllAsConfirmed').remove();
1336 }
1337
1338 this._super(data, textStatus, jqXHR);
1339 break;
1340
1341 case 'markAsConfirmed':
1342 WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify()).children('li.jsNotificationItem').each(function(index, item) {
1343 var $item = $(item);
1344 if (data.returnValues.notificationID == $item.data('notificationID')) {
1345 window.location = $item.data('link');
1346 return false;
1347 }
1348 });
1349 break;
1350 }
1351 }
1352 });
1353
1354 /**
1355 * Handles notification list actions.
1356 */
1357 WCF.Notification.List = Class.extend({
1358 /**
1359 * notification count
1360 * @var jQuery
1361 */
1362 _badge: null,
1363
1364 /**
1365 * list of notification items
1366 * @var object
1367 */
1368 _items: { },
1369
1370 /**
1371 * action proxy
1372 * @var WCF.Action.Proxy
1373 */
1374 _proxy: null,
1375
1376 /**
1377 * Initializes the notification list.
1378 */
1379 init: function() {
1380 var $containers = $('li.jsNotificationItem');
1381 if (!$containers.length) {
1382 return;
1383 }
1384
1385 $containers.each($.proxy(function(index, container) {
1386 var $container = $(container);
1387 this._items[$container.data('notificationID')] = $container;
1388
1389 $container.find('.jsMarkAsConfirmed').data('notificationID', $container.data('notificationID')).click($.proxy(this._click, this));
1390 $container.find('p').html(function(index, oldHTML) {
1391 return '<a>' + oldHTML + '</a>';
1392 }).children('a').data('notificationID', $container.data('notificationID')).click($.proxy(this._clickLink, this));
1393 }, this));
1394
1395 this._badge = $('.jsNotificationsBadge:eq(0)');
1396 this._proxy = new WCF.Action.Proxy({
1397 success: $.proxy(this._success, this)
1398 });
1399
1400 // mark all as confirmed button
1401 $('.contentNavigation .jsMarkAllAsConfirmed').click($.proxy(this._markAllAsConfirmed, this));
1402 },
1403
1404 /**
1405 * Handles clicks on the text link.
1406 *
1407 * @param object event
1408 */
1409 _clickLink: function(event) {
1410 this._items[$(event.currentTarget).data('notificationID')].data('redirect', true);
1411 this._click(event);
1412 },
1413
1414 /**
1415 * Handles button actions.
1416 *
1417 * @param object event
1418 */
1419 _click: function(event) {
1420 this._proxy.setOption('data', {
1421 actionName: 'markAsConfirmed',
1422 className: 'wcf\\data\\user\\notification\\UserNotificationAction',
1423 parameters: {
1424 notificationID: $(event.currentTarget).data('notificationID')
1425 }
1426 });
1427 this._proxy.sendRequest();
1428 },
1429
1430 /**
1431 * Marks all notifications as confirmed.
1432 */
1433 _markAllAsConfirmed: function() {
1434 WCF.System.Confirmation.show(WCF.Language.get('wcf.user.notification.markAllAsConfirmed.confirmMessage'), $.proxy(function(action) {
1435 if (action === 'confirm') {
1436 this._proxy.setOption('data', {
1437 actionName: 'markAllAsConfirmed',
1438 className: 'wcf\\data\\user\\notification\\UserNotificationAction'
1439 });
1440 this._proxy.sendRequest();
1441 }
1442 }, this));
1443 },
1444
1445 /**
1446 * Handles successful button actions.
1447 *
1448 * @param object data
1449 * @param string textStatus
1450 * @param jQuery jqXHR
1451 */
1452 _success: function(data, textStatus, jqXHR) {
1453 switch (data.actionName) {
1454 case 'markAllAsConfirmed':
1455 window.location.reload();
1456 break;
1457
1458 case 'markAsConfirmed':
1459 var $item = this._items[data.returnValues.notificationID];
1460 if ($item.data('redirect')) {
1461 window.location = $item.data('link');
1462 return;
1463 }
1464
1465 this._items[data.returnValues.notificationID].remove();
1466 delete this._items[data.returnValues.notificationID];
1467
1468 // reduce badge count
1469 this._badge.html(data.returnValues.totalCount);
1470
1471 // remove previous notification count
1472 document.title = document.title.replace(/^\(([0-9]+)\) /, '');
1473
1474 // update page title
1475 if (data.returnValues.totalCount > 0) {
1476 document.title = '(' + data.returnValues.totalCount + ') ' + document.title;
1477 }
1478 break;
1479 }
1480 }
1481 });
1482
1483 /**
1484 * Signature preview.
1485 *
1486 * @see WCF.Message.Preview
1487 */
1488 WCF.User.SignaturePreview = WCF.Message.Preview.extend({
1489 /**
1490 * @see WCF.Message.Preview._handleResponse()
1491 */
1492 _handleResponse: function(data) {
1493 // get preview container
1494 var $preview = $('#previewContainer');
1495 if (!$preview.length) {
1496 $preview = $('<fieldset id="previewContainer"><legend>' + WCF.Language.get('wcf.global.preview') + '</legend><div></div></fieldset>').insertBefore($('#signatureContainer')).wcfFadeIn();
1497 }
1498
1499 $preview.children('div').first().html(data.returnValues.message);
1500 }
1501 });
1502
1503 /**
1504 * Loads recent activity events once the user scrolls to the very bottom.
1505 *
1506 * @param integer userID
1507 */
1508 WCF.User.RecentActivityLoader = Class.extend({
1509 /**
1510 * container object
1511 * @var jQuery
1512 */
1513 _container: null,
1514
1515 /**
1516 * true if list should be filtered by followed users
1517 * @var boolean
1518 */
1519 _filteredByFollowedUsers: false,
1520
1521 /**
1522 * button to load next events
1523 * @var jQuery
1524 */
1525 _loadButton: null,
1526
1527 /**
1528 * action proxy
1529 * @var WCF.Action.Proxy
1530 */
1531 _proxy: null,
1532
1533 /**
1534 * user id
1535 * @var integer
1536 */
1537 _userID: 0,
1538
1539 /**
1540 * Initializes a new RecentActivityLoader object.
1541 *
1542 * @param integer userID
1543 * @param boolean filteredByFollowedUsers
1544 */
1545 init: function(userID, filteredByFollowedUsers) {
1546 this._container = $('#recentActivities');
1547 this._filteredByFollowedUsers = (filteredByFollowedUsers === true);
1548 this._userID = userID;
1549
1550 if (this._userID !== null && !this._userID) {
1551 console.debug("[WCF.User.RecentActivityLoader] Invalid parameter 'userID' given.");
1552 return;
1553 }
1554
1555 this._proxy = new WCF.Action.Proxy({
1556 success: $.proxy(this._success, this)
1557 });
1558
1559 this._loadButton = $('<li class="recentActivitiesMore"><button class="small">' + WCF.Language.get('wcf.user.recentActivity.more') + '</button></li>').appendTo(this._container);
1560 this._loadButton = this._loadButton.children('button').click($.proxy(this._click, this));
1561 },
1562
1563 /**
1564 * Loads next activity events.
1565 */
1566 _click: function() {
1567 this._loadButton.enable();
1568
1569 var $parameters = {
1570 lastEventTime: this._container.data('lastEventTime')
1571 };
1572 if (this._userID) {
1573 $parameters.userID = this._userID;
1574 }
1575 else if (this._filteredByFollowedUsers) {
1576 $parameters.filteredByFollowedUsers = 1;
1577 }
1578
1579 this._proxy.setOption('data', {
1580 actionName: 'load',
1581 className: 'wcf\\data\\user\\activity\\event\\UserActivityEventAction',
1582 parameters: $parameters
1583 });
1584 this._proxy.sendRequest();
1585 },
1586
1587 /**
1588 * Handles successful AJAX requests.
1589 *
1590 * @param object data
1591 * @param string textStatus
1592 * @param jQuery jqXHR
1593 */
1594 _success: function(data, textStatus, jqXHR) {
1595 if (data.returnValues.template) {
1596 $(data.returnValues.template).insertBefore(this._loadButton.parent());
1597
1598 this._container.data('lastEventTime', data.returnValues.lastEventTime);
1599 this._loadButton.enable();
1600 }
1601 else {
1602 $('<small>' + WCF.Language.get('wcf.user.recentActivity.noMoreEntries') + '</small>').appendTo(this._loadButton.parent());
1603 this._loadButton.remove();
1604 }
1605 }
1606 });
1607
1608 /**
1609 * Loads user profile previews.
1610 *
1611 * @see WCF.Popover
1612 */
1613 WCF.User.ProfilePreview = WCF.Popover.extend({
1614 /**
1615 * action proxy
1616 * @var WCF.Action.Proxy
1617 */
1618 _proxy: null,
1619
1620 /**
1621 * list of user profiles
1622 * @var object
1623 */
1624 _userProfiles: { },
1625
1626 /**
1627 * @see WCF.Popover.init()
1628 */
1629 init: function() {
1630 this._super('.userLink');
1631
1632 this._proxy = new WCF.Action.Proxy({
1633 showLoadingOverlay: false
1634 });
1635 },
1636
1637 /**
1638 * @see WCF.Popover._loadContent()
1639 */
1640 _loadContent: function() {
1641 var $element = $('#' + this._activeElementID);
1642 var $userID = $element.data('userID');
1643
1644 if (this._userProfiles[$userID]) {
1645 // use cached user profile
1646 this._insertContent(this._activeElementID, this._userProfiles[$userID], true);
1647 }
1648 else {
1649 this._proxy.setOption('data', {
1650 actionName: 'getUserProfile',
1651 className: 'wcf\\data\\user\\UserProfileAction',
1652 objectIDs: [ $userID ]
1653 });
1654
1655 var $elementID = this._activeElementID;
1656 var self = this;
1657 this._proxy.setOption('success', function(data, textStatus, jqXHR) {
1658 // cache user profile
1659 self._userProfiles[$userID] = data.returnValues.template;
1660
1661 // show user profile
1662 self._insertContent($elementID, data.returnValues.template, true);
1663 });
1664 this._proxy.setOption('failure', function(data, jqXHR, textStatus, errorThrown) {
1665 // cache user profile
1666 self._userProfiles[$userID] = data.message;
1667
1668 // show user profile
1669 self._insertContent($elementID, data.message, true);
1670
1671 return false;
1672 });
1673 this._proxy.sendRequest();
1674 }
1675 }
1676 });
1677
1678 /**
1679 * Initalizes WCF.User.Action namespace.
1680 */
1681 WCF.User.Action = {};
1682
1683 /**
1684 * Handles user follow and unfollow links.
1685 */
1686 WCF.User.Action.Follow = Class.extend({
1687 /**
1688 * list with elements containing follow and unfollow buttons
1689 * @var array
1690 */
1691 _containerList: null,
1692
1693 /**
1694 * CSS selector for follow buttons
1695 * @var string
1696 */
1697 _followButtonSelector: '.jsFollowButton',
1698
1699 /**
1700 * id of the user that is currently being followed/unfollowed
1701 * @var integer
1702 */
1703 _userID: 0,
1704
1705 /**
1706 * Initializes new WCF.User.Action.Follow object.
1707 *
1708 * @param array containerList
1709 * @param string followButtonSelector
1710 */
1711 init: function(containerList, followButtonSelector) {
1712 if (!containerList.length) {
1713 return;
1714 }
1715 this._containerList = containerList;
1716
1717 if (followButtonSelector) {
1718 this._followButtonSelector = followButtonSelector;
1719 }
1720
1721 // initialize proxy
1722 this._proxy = new WCF.Action.Proxy({
1723 success: $.proxy(this._success, this)
1724 });
1725
1726 // bind event listeners
1727 this._containerList.each($.proxy(function(index, container) {
1728 $(container).find(this._followButtonSelector).click($.proxy(this._click, this));
1729 }, this));
1730 },
1731
1732 /**
1733 * Handles a click on a follow or unfollow button.
1734 *
1735 * @param object event
1736 */
1737 _click: function(event) {
1738 var link = $(event.target);
1739 if (!link.is('a')) {
1740 link = link.closest('a');
1741 }
1742 this._userID = link.data('objectID');
1743
1744 this._proxy.setOption('data', {
1745 'actionName': link.data('following') ? 'unfollow' : 'follow',
1746 'className': 'wcf\\data\\user\\follow\\UserFollowAction',
1747 'parameters': {
1748 data: {
1749 userID: this._userID
1750 }
1751 }
1752 });
1753 this._proxy.sendRequest();
1754 },
1755
1756 /**
1757 * Handles the successful (un)following of a user.
1758 *
1759 * @param object data
1760 * @param string textStatus
1761 * @param jQuery jqXHR
1762 */
1763 _success: function(data, textStatus, jqXHR) {
1764 this._containerList.each($.proxy(function(index, container) {
1765 var button = $(container).find(this._followButtonSelector).get(0);
1766
1767 if (button && $(button).data('objectID') == this._userID) {
1768 button = $(button);
1769
1770 // toogle icon title
1771 if (data.returnValues.following) {
1772 button.data('tooltip', WCF.Language.get('wcf.user.button.unfollow')).children('.icon').removeClass('icon-plus').addClass('icon-minus');
1773 }
1774 else {
1775 button.data('tooltip', WCF.Language.get('wcf.user.button.follow')).children('.icon').removeClass('icon-minus').addClass('icon-plus');
1776 }
1777
1778 button.data('following', data.returnValues.following);
1779
1780 return false;
1781 }
1782 }, this));
1783
1784 var $notification = new WCF.System.Notification();
1785 $notification.show();
1786 }
1787 });
1788
1789 /**
1790 * Handles user ignore and unignore links.
1791 */
1792 WCF.User.Action.Ignore = Class.extend({
1793 /**
1794 * list with elements containing ignore and unignore buttons
1795 * @var array
1796 */
1797 _containerList: null,
1798
1799 /**
1800 * CSS selector for ignore buttons
1801 * @var string
1802 */
1803 _ignoreButtonSelector: '.jsIgnoreButton',
1804
1805 /**
1806 * id of the user that is currently being ignored/unignored
1807 * @var integer
1808 */
1809 _userID: 0,
1810
1811 /**
1812 * Initializes new WCF.User.Action.Ignore object.
1813 *
1814 * @param array containerList
1815 * @param string ignoreButtonSelector
1816 */
1817 init: function(containerList, ignoreButtonSelector) {
1818 if (!containerList.length) {
1819 return;
1820 }
1821 this._containerList = containerList;
1822
1823 if (ignoreButtonSelector) {
1824 this._ignoreButtonSelector = ignoreButtonSelector;
1825 }
1826
1827 // initialize proxy
1828 this._proxy = new WCF.Action.Proxy({
1829 success: $.proxy(this._success, this)
1830 });
1831
1832 // bind event listeners
1833 this._containerList.each($.proxy(function(index, container) {
1834 $(container).find(this._ignoreButtonSelector).click($.proxy(this._click, this));
1835 }, this));
1836 },
1837
1838 /**
1839 * Handles a click on a ignore or unignore button.
1840 *
1841 * @param object event
1842 */
1843 _click: function(event) {
1844 var link = $(event.target);
1845 if (!link.is('a')) {
1846 link = link.closest('a');
1847 }
1848 this._userID = link.data('objectID');
1849
1850 this._proxy.setOption('data', {
1851 'actionName': link.data('ignored') ? 'unignore' : 'ignore',
1852 'className': 'wcf\\data\\user\\ignore\\UserIgnoreAction',
1853 'parameters': {
1854 data: {
1855 ignoreUserID: this._userID
1856 }
1857 }
1858 });
1859 this._proxy.sendRequest();
1860 },
1861
1862 /**
1863 * Handles the successful (un)ignoring of a user.
1864 *
1865 * @param object data
1866 * @param string textStatus
1867 * @param jQuery jqXHR
1868 */
1869 _success: function(data, textStatus, jqXHR) {
1870 this._containerList.each($.proxy(function(index, container) {
1871 var button = $(container).find(this._ignoreButtonSelector).get(0);
1872
1873 if (button && $(button).data('objectID') == this._userID) {
1874 button = $(button);
1875
1876 // toogle icon title
1877 if (data.returnValues.isIgnoredUser) {
1878 button.data('tooltip', WCF.Language.get('wcf.user.button.unignore')).children('.icon').removeClass('icon-ban-circle').addClass('icon-circle-blank');
1879 }
1880 else {
1881 button.data('tooltip', WCF.Language.get('wcf.user.button.ignore')).children('.icon').removeClass('icon-circle-blank').addClass('icon-ban-circle');
1882 }
1883
1884 button.data('ignored', data.returnValues.isIgnoredUser);
1885
1886 return false;
1887 }
1888 }, this));
1889
1890 var $notification = new WCF.System.Notification();
1891 $notification.show();
1892 }
1893 });
1894
1895 /**
1896 * Namespace for avatar functions.
1897 */
1898 WCF.User.Avatar = {};
1899
1900 /**
1901 * Handles cropping an avatar.
1902 */
1903 WCF.User.Avatar.Crop = Class.extend({
1904 /**
1905 * current crop setting in x-direction
1906 * @var integer
1907 */
1908 _cropX: 0,
1909
1910 /**
1911 * current crop setting in y-direction
1912 * @var integer
1913 */
1914 _cropY: 0,
1915
1916 /**
1917 * avatar crop dialog
1918 * @var jQuery
1919 */
1920 _dialog: null,
1921
1922 /**
1923 * action proxy to send the crop AJAX requests
1924 * @var WCF.Action.Proxy
1925 */
1926 _proxy: null,
1927
1928 /**
1929 * maximum size of thumbnails
1930 * @var integer
1931 */
1932 MAX_THUMBNAIL_SIZE: 128,
1933
1934 /**
1935 * Creates a new instance of WCF.User.Avatar.Crop.
1936 *
1937 * @param integer avatarID
1938 */
1939 init: function(avatarID) {
1940 this._avatarID = avatarID;
1941
1942 if (this._dialog) {
1943 this.destroy();
1944 }
1945 this._dialog = null;
1946
1947 // check if object already had been initialized
1948 if (!this._proxy) {
1949 this._proxy = new WCF.Action.Proxy({
1950 success: $.proxy(this._success, this)
1951 });
1952 }
1953
1954 $('.userAvatarCrop').click($.proxy(this._showCropDialog, this));
1955 },
1956
1957 /**
1958 * Destroys the avatar crop interface.
1959 */
1960 destroy: function() {
1961 this._dialog.remove();
1962 },
1963
1964 /**
1965 * Sends AJAX request to crop avatar.
1966 *
1967 * @param object event
1968 */
1969 _crop: function(event) {
1970 this._proxy.setOption('data', {
1971 actionName: 'cropAvatar',
1972 className: 'wcf\\data\\user\\avatar\\UserAvatarAction',
1973 objectIDs: [ this._avatarID ],
1974 parameters: {
1975 cropX: this._cropX,
1976 cropY: this._cropY
1977 }
1978 });
1979 this._proxy.sendRequest();
1980 },
1981
1982 /**
1983 * Initializes the dialog after a successful 'getCropDialog' request.
1984 *
1985 * @param object data
1986 */
1987 _getCropDialog: function(data) {
1988 if (!this._dialog) {
1989 this._dialog = $('<div />').hide().appendTo(document.body);
1990 this._dialog.wcfDialog({
1991 title: WCF.Language.get('wcf.user.avatar.type.custom.crop')
1992 });
1993 }
1994
1995 this._dialog.html(data.returnValues.template);
1996 this._dialog.find('button[data-type="save"]').click($.proxy(this._crop, this));
1997
1998 this._cropX = data.returnValues.cropX;
1999 this._cropY = data.returnValues.cropY;
2000
2001 var $image = $('#userAvatarCropSelection > img');
2002 $('#userAvatarCropSelection').css({
2003 height: $image.height() + 'px',
2004 width: $image.width() + 'px'
2005 });
2006 $('#userAvatarCropOverlaySelection').css({
2007 'background-image': 'url(' + $image.attr('src') + ')',
2008 'background-position': -this._cropX + 'px ' + -this._cropY + 'px',
2009 'left': this._cropX + 'px',
2010 'top': this._cropY + 'px'
2011 }).draggable({
2012 containment: 'parent',
2013 drag : $.proxy(this._updateSelection, this),
2014 stop : $.proxy(this._updateSelection, this)
2015 });
2016
2017 this._dialog.find('button[data-type="save"]').click($.proxy(this._save, this));
2018
2019 this._dialog.wcfDialog('render');
2020 },
2021
2022 /**
2023 * Shows the cropping dialog.
2024 */
2025 _showCropDialog: function() {
2026 if (!this._dialog) {
2027 this._proxy.setOption('data', {
2028 actionName: 'getCropDialog',
2029 className: 'wcf\\data\\user\\avatar\\UserAvatarAction',
2030 objectIDs: [ this._avatarID ]
2031 });
2032 this._proxy.sendRequest();
2033 }
2034 else {
2035 this._dialog.wcfDialog('open');
2036 }
2037 },
2038
2039 /**
2040 * Handles successful AJAX request.
2041 *
2042 * @param object data
2043 * @param string textStatus
2044 * @param jQuery jqXHR
2045 */
2046 _success: function(data, textStatus, jqXHR) {
2047 switch (data.actionName) {
2048 case 'getCropDialog':
2049 this._getCropDialog(data);
2050 break;
2051
2052 case 'cropAvatar':
2053 $('#avatarUpload > dt > img').replaceWith($('<img src="' + data.returnValues.url + '" alt="" class="userAvatarCrop jsTooltip" title="' + WCF.Language.get('wcf.user.avatar.type.custom.crop') + '" />').css({
2054 width: '96px',
2055 height: '96px'
2056 }).click($.proxy(this._showCropDialog, this)));
2057
2058 WCF.DOMNodeInsertedHandler.execute();
2059
2060 this._dialog.wcfDialog('close');
2061
2062 var $notification = new WCF.System.Notification();
2063 $notification.show();
2064 break;
2065 }
2066 },
2067
2068 /**
2069 * Updates the current crop selection if the selection overlay is dragged.
2070 *
2071 * @param object event
2072 * @param object ui
2073 */
2074 _updateSelection: function(event, ui) {
2075 this._cropX = ui.position.left;
2076 this._cropY = ui.position.top;
2077
2078 $('#userAvatarCropOverlaySelection').css({
2079 'background-position': -ui.position.left + 'px ' + -ui.position.top + 'px'
2080 });
2081 }
2082 });
2083
2084 /**
2085 * Avatar upload function
2086 *
2087 * @see WCF.Upload
2088 */
2089 WCF.User.Avatar.Upload = WCF.Upload.extend({
2090 /**
2091 * handles cropping the avatar
2092 * @var WCF.User.Avatar.Crop
2093 */
2094 _avatarCrop: null,
2095
2096 /**
2097 * user id of avatar owner
2098 * @var integer
2099 */
2100 _userID: 0,
2101
2102 /**
2103 * Initalizes a new WCF.User.Avatar.Upload object.
2104 *
2105 * @param integer userID
2106 * @param WCF.User.Avatar.Crop avatarCrop
2107 */
2108 init: function(userID, avatarCrop) {
2109 this._super($('#avatarUpload > dd > div'), undefined, 'wcf\\data\\user\\avatar\\UserAvatarAction');
2110 this._userID = userID || 0;
2111 this._avatarCrop = avatarCrop;
2112
2113 $('#avatarForm input[type=radio]').change(function() {
2114 if ($(this).val() == 'custom') {
2115 $('#avatarUpload > dd > div').show();
2116 }
2117 else {
2118 $('#avatarUpload > dd > div').hide();
2119 }
2120 });
2121 if (!$('#avatarForm input[type=radio][value=custom]:checked').length) {
2122 $('#avatarUpload > dd > div').hide();
2123 }
2124 },
2125
2126 /**
2127 * @see WCF.Upload._initFile()
2128 */
2129 _initFile: function(file) {
2130 return $('#avatarUpload > dt > img');
2131 },
2132
2133 /**
2134 * @see WCF.Upload._success()
2135 */
2136 _success: function(uploadID, data) {
2137 if (data.returnValues.url) {
2138 this._updateImage(data.returnValues.url, data.returnValues.canCrop);
2139
2140 if (data.returnValues.canCrop) {
2141 if (!this._avatarCrop) {
2142 this._avatarCrop = new WCF.User.Avatar.Crop(data.returnValues.avatarID);
2143 }
2144 else {
2145 this._avatarCrop.init(data.returnValues.avatarID);
2146 }
2147 }
2148 else if (this._avatarCrop) {
2149 this._avatarCrop.destroy();
2150 this._avatarCrop = null;
2151 }
2152
2153 // hide error
2154 $('#avatarUpload > dd > .innerError').remove();
2155
2156 // show success message
2157 var $notification = new WCF.System.Notification(WCF.Language.get('wcf.user.avatar.upload.success'));
2158 $notification.show();
2159 }
2160 else if (data.returnValues.errorType) {
2161 // show error
2162 this._getInnerErrorElement().text(WCF.Language.get('wcf.user.avatar.upload.error.' + data.returnValues.errorType));
2163 }
2164 },
2165
2166 /**
2167 * Updates the displayed avatar image.
2168 *
2169 * @param string url
2170 * @param boolean canCrop
2171 */
2172 _updateImage: function(url, canCrop) {
2173 $('#avatarUpload > dt > img').remove();
2174 var $image = $('<img src="' + url + '" alt="" />').css({
2175 'height': 'auto',
2176 'max-height': '96px',
2177 'max-width': '96px',
2178 'width': 'auto'
2179 });
2180 if (canCrop) {
2181 $image.addClass('userAvatarCrop').addClass('jsTooltip');
2182 $image.attr('title', WCF.Language.get('wcf.user.avatar.type.custom.crop'));
2183 }
2184
2185 $('#avatarUpload > dt').prepend($image);
2186
2187 WCF.DOMNodeInsertedHandler.execute();
2188 },
2189
2190 /**
2191 * Returns the inner error element.
2192 *
2193 * @return jQuery
2194 */
2195 _getInnerErrorElement: function() {
2196 var $span = $('#avatarUpload > dd > .innerError');
2197 if (!$span.length) {
2198 $span = $('<small class="innerError"></span>');
2199 $('#avatarUpload > dd').append($span);
2200 }
2201
2202 return $span;
2203 },
2204
2205 /**
2206 * @see WCF.Upload._getParameters()
2207 */
2208 _getParameters: function() {
2209 return {
2210 userID: this._userID
2211 };
2212 },
2213 });
2214
2215 /**
2216 * Generic implementation for grouped user lists.
2217 *
2218 * @param string className
2219 * @param string dialogTitle
2220 * @param object additionalParameters
2221 */
2222 WCF.User.List = Class.extend({
2223 /**
2224 * list of additional parameters
2225 * @var object
2226 */
2227 _additionalParameters: { },
2228
2229 /**
2230 * list of cached pages
2231 * @var object
2232 */
2233 _cache: { },
2234
2235 /**
2236 * action class name
2237 * @var string
2238 */
2239 _className: '',
2240
2241 /**
2242 * dialog overlay
2243 * @var jQuery
2244 */
2245 _dialog: null,
2246
2247 /**
2248 * dialog title
2249 * @var string
2250 */
2251 _dialogTitle: '',
2252
2253 /**
2254 * page count
2255 * @var integer
2256 */
2257 _pageCount: 0,
2258
2259 /**
2260 * current page no
2261 * @var integer
2262 */
2263 _pageNo: 1,
2264
2265 /**
2266 * action proxy
2267 * @var WCF.Action.Proxy
2268 */
2269 _proxy: null,
2270
2271 /**
2272 * Initializes a new grouped user list.
2273 *
2274 * @param string className
2275 * @param string dialogTitle
2276 * @param object additionalParameters
2277 */
2278 init: function(className, dialogTitle, additionalParameters) {
2279 this._additionalParameters = additionalParameters || { };
2280 this._cache = { };
2281 this._className = className;
2282 this._dialog = null;
2283 this._dialogTitle = dialogTitle;
2284 this._pageCount = 0;
2285 this._pageNo = 1;
2286
2287 this._proxy = new WCF.Action.Proxy({
2288 success: $.proxy(this._success, this)
2289 });
2290 },
2291
2292 /**
2293 * Opens the dialog overlay.
2294 */
2295 open: function() {
2296 this._pageNo = 1;
2297 this._showPage();
2298 },
2299
2300 /**
2301 * Displays the specified page.
2302 *
2303 * @param object event
2304 * @param object data
2305 */
2306 _showPage: function(event, data) {
2307 if (data && data.activePage) {
2308 this._pageNo = data.activePage;
2309 }
2310
2311 if (this._pageCount != 0 && (this._pageNo < 1 || this._pageNo > this._pageCount)) {
2312 console.debug("[WCF.User.List] Cannot access page " + this._pageNo + " of " + this._pageCount);
2313 return;
2314 }
2315
2316 if (this._cache[this._pageNo]) {
2317 var $dialogCreated = false;
2318 if (this._dialog === null) {
2319 //this._dialog = $('<div id="userList' + this._className.hashCode() + '" style="min-width: 600px;" />').hide().appendTo(document.body);
2320 this._dialog = $('<div id="userList' + this._className.hashCode() + '" />').hide().appendTo(document.body);
2321 $dialogCreated = true;
2322 }
2323
2324 // remove current view
2325 this._dialog.empty();
2326
2327 // insert HTML
2328 this._dialog.html(this._cache[this._pageNo]);
2329
2330 // add pagination
2331 if (this._pageCount > 1) {
2332 this._dialog.find('.jsPagination').wcfPages({
2333 activePage: this._pageNo,
2334 maxPage: this._pageCount
2335 }).bind('wcfpagesswitched', $.proxy(this._showPage, this));
2336 }
2337
2338 // show dialog
2339 if ($dialogCreated) {
2340 this._dialog.wcfDialog({
2341 title: this._dialogTitle
2342 });
2343 }
2344 else {
2345 this._dialog.wcfDialog('open').wcfDialog('render');
2346 }
2347 }
2348 else {
2349 this._additionalParameters.pageNo = this._pageNo;
2350
2351 // load template via AJAX
2352 this._proxy.setOption('data', {
2353 actionName: 'getGroupedUserList',
2354 className: this._className,
2355 interfaceName: 'wcf\\data\\IGroupedUserListAction',
2356 parameters: this._additionalParameters
2357 });
2358 this._proxy.sendRequest();
2359 }
2360 },
2361
2362 /**
2363 * Handles successful AJAX requests.
2364 *
2365 * @param object data
2366 * @param string textStatus
2367 * @param jQuery jqXHR
2368 */
2369 _success: function(data, textStatus, jqXHR) {
2370 if (data.returnValues.pageCount) {
2371 this._pageCount = data.returnValues.pageCount;
2372 }
2373
2374 this._cache[this._pageNo] = data.returnValues.template;
2375 this._showPage();
2376 }
2377 });
2378
2379 /**
2380 * Namespace for object watch functions.
2381 */
2382 WCF.User.ObjectWatch = {};
2383
2384 /**
2385 * Handles subscribe/unsubscribe links.
2386 */
2387 WCF.User.ObjectWatch.Subscribe = Class.extend({
2388 /**
2389 * CSS selector for subscribe buttons
2390 * @var string
2391 */
2392 _buttonSelector: '.jsSubscribeButton',
2393
2394 /**
2395 * list of buttons
2396 * @var object
2397 */
2398 _buttons: { },
2399
2400 /**
2401 * dialog overlay
2402 * @var object
2403 */
2404 _dialog: null,
2405
2406 /**
2407 * system notification
2408 * @var WCF.System.Notification
2409 */
2410 _notification: null,
2411
2412 /**
2413 * WCF.User.ObjectWatch.Subscribe object.
2414 */
2415 init: function() {
2416 this._buttons = { };
2417 this._notification = null;
2418
2419 // initialize proxy
2420 this._proxy = new WCF.Action.Proxy({
2421 success: $.proxy(this._success, this)
2422 });
2423
2424 // bind event listeners
2425 $(this._buttonSelector).each($.proxy(function(index, button) {
2426 var $button = $(button);
2427 var $objectID = $button.data('objectID');
2428 this._buttons[$objectID] = $button.click($.proxy(this._click, this));
2429 }, this));
2430 },
2431
2432 /**
2433 * Handles a click on a subscribe button.
2434 *
2435 * @param object event
2436 */
2437 _click: function(event) {
2438 var $button = $(event.currentTarget);
2439
2440 this._proxy.setOption('data', {
2441 actionName: 'manageSubscription',
2442 className: 'wcf\\data\\user\\object\\watch\\UserObjectWatchAction',
2443 parameters: {
2444 objectID: $button.data('objectID'),
2445 objectType: $button.data('objectType')
2446 }
2447 });
2448 this._proxy.sendRequest();
2449 },
2450
2451 /**
2452 * Handles successful AJAX requests.
2453 *
2454 * @param object data
2455 * @param string textStatus
2456 * @param jQuery jqXHR
2457 */
2458 _success: function(data, textStatus, jqXHR) {
2459 if (data.actionName === 'manageSubscription') {
2460 if (this._dialog === null) {
2461 this._dialog = $('<div>' + data.returnValues.template + '</div>').hide().appendTo(document.body);
2462 this._dialog.wcfDialog({
2463 title: WCF.Language.get('wcf.user.objectWatch.manageSubscription')
2464 });
2465 }
2466 else {
2467 this._dialog.html(data.returnValues.template);
2468 this._dialog.wcfDialog('open');
2469 }
2470
2471 // bind event listener
2472 this._dialog.find('.formSubmit > .jsButtonSave').data('objectID', data.returnValues.objectID).click($.proxy(this._save, this));
2473 var $enableNotification = this._dialog.find('input[name=enableNotification]').disable();
2474
2475 // toggle subscription
2476 this._dialog.find('input[name=subscribe]').change(function(event) {
2477 var $input = $(event.currentTarget);
2478 if ($input.val() == 1) {
2479 $enableNotification.enable();
2480 }
2481 else {
2482 $enableNotification.disable();
2483 }
2484 });
2485
2486 // setup
2487 var $selectedOption = this._dialog.find('input[name=subscribe]:checked');
2488 if ($selectedOption.length && $selectedOption.val() == 1) {
2489 $enableNotification.enable();
2490 }
2491 }
2492 else if (data.actionName === 'saveSubscription' && this._dialog.is(':visible')) {
2493 this._dialog.wcfDialog('close');
2494
2495 if (this._notification === null) {
2496 this._notification = new WCF.System.Notification(WCF.Language.get('wcf.global.success.edit'));
2497 }
2498
2499 this._notification.show();
2500 }
2501 },
2502
2503 /**
2504 * Saves the subscription.
2505 *
2506 * @param object event
2507 */
2508 _save: function(event) {
2509 var $button = this._buttons[$(event.currentTarget).data('objectID')];
2510 var $subscribe = this._dialog.find('input[name=subscribe]:checked').val();
2511 var $enableNotification = (this._dialog.find('input[name=enableNotification]').is(':checked')) ? 1 : 0;
2512
2513 this._proxy.setOption('data', {
2514 actionName: 'saveSubscription',
2515 className: 'wcf\\data\\user\\object\\watch\\UserObjectWatchAction',
2516 parameters: {
2517 enableNotification: $enableNotification,
2518 objectID: $button.data('objectID'),
2519 objectType: $button.data('objectType'),
2520 subscribe: $subscribe
2521 }
2522 });
2523 this._proxy.sendRequest();
2524 }
2525 });
2526
2527 /**
2528 * Handles inline editing of users.
2529 */
2530 WCF.User.InlineEditor = WCF.InlineEditor.extend({
2531 /**
2532 * list of permissions
2533 * @var object
2534 */
2535 _permissions: { },
2536
2537 /**
2538 * @see WCF.InlineEditor._execute()
2539 */
2540 _execute: function(elementID, optionName) {
2541 if (!this._validate(elementID, optionName)) {
2542 return false;
2543 }
2544
2545 var $data = { };
2546 var $element = $('#' + elementID);
2547 switch (optionName) {
2548 case 'unban':
2549 case 'enableAvatar':
2550 case 'enableSignature':
2551 switch (optionName) {
2552 case 'unban':
2553 $data.banned = 0;
2554 break;
2555
2556 case 'enableAvatar':
2557 $data.disableAvatar = 0;
2558 break;
2559
2560 case 'enableSignature':
2561 $data.disableSignature = 0;
2562 break;
2563 }
2564
2565 this._proxy.setOption('data', {
2566 actionName: optionName,
2567 className: 'wcf\\data\\user\\UserAction',
2568 objectIDs: [ $element.data('objectID') ]
2569 });
2570 this._proxy.sendRequest();
2571 break;
2572
2573 case 'ban':
2574 case 'disableAvatar':
2575 case 'disableSignature':
2576 if (optionName == 'ban') {
2577 $data.banned = 1;
2578 }
2579 else {
2580 $data[optionName] = 1;
2581 }
2582
2583 this._showReasonDialog($element.data('objectID'), optionName);
2584 break;
2585
2586 case 'advanced':
2587 window.location = this._getTriggerElement($element).attr('href');
2588 break;
2589 }
2590
2591 if ($.getLength($data)) {
2592 this._updateData.push({
2593 data: $data,
2594 elementID: elementID,
2595 });
2596 }
2597 },
2598
2599 /**
2600 * Executes an action with a reason.
2601 *
2602 * @param integer userID
2603 * @param string optionName
2604 * @param string reason
2605 */
2606 _executeReasonAction: function(userID, optionName, reason) {
2607 var $parameters = { };
2608 $parameters[optionName + WCF.String.ucfirst('reason')] = reason;
2609
2610 this._proxy.setOption('data', {
2611 actionName: optionName,
2612 className: 'wcf\\data\\user\\UserAction',
2613 objectIDs: [ userID ],
2614 parameters: $parameters
2615 });
2616 this._proxy.sendRequest();
2617 },
2618
2619 /**
2620 * Returns a specific permission.
2621 *
2622 * @param string permission
2623 * @return integer
2624 */
2625 _getPermission: function(permission) {
2626 if (this._permissions[permission]) {
2627 return this._permissions[permission];
2628 }
2629
2630 return 0;
2631 },
2632
2633 /**
2634 * @see WCF.InlineEditor._getTriggerElement()
2635 */
2636 _getTriggerElement: function(element) {
2637 return element.find('.jsUserInlineEditor');
2638 },
2639
2640 /**
2641 * @see WCF.InlineEditor._setOptions()
2642 */
2643 _setOptions: function() {
2644 this._options = [
2645 // banning
2646 { label: WCF.Language.get('wcf.user.ban'), optionName: 'ban' },
2647 { label: WCF.Language.get('wcf.user.unban'), optionName: 'unban' },
2648
2649 // disabling avatar
2650 { label: WCF.Language.get('wcf.user.disableAvatar'), optionName: 'disableAvatar' },
2651 { label: WCF.Language.get('wcf.user.enableAvatar'), optionName: 'enableAvatar' },
2652
2653 // disabling signature
2654 { label: WCF.Language.get('wcf.user.disableSignature'), optionName: 'disableSignature' },
2655 { label: WCF.Language.get('wcf.user.enableSignature'), optionName: 'enableSignature' },
2656
2657 // divider
2658 { optionName: 'divider' },
2659
2660 // overlay
2661 { label: WCF.Language.get('wcf.user.edit'), optionName: 'advanced' }
2662 ];
2663 },
2664
2665 /**
2666 * @see WCF.InlineEditor._show()
2667 */
2668 _show: function(event) {
2669 var $element = $(event.currentTarget);
2670 var $elementID = $element.data('elementID');
2671
2672 if (!this._dropdowns[$elementID]) {
2673 var $dropdownMenu = $element.next('.dropdownMenu');
2674
2675 if ($dropdownMenu) {
2676 this._dropdowns[$elementID] = $dropdownMenu;
2677 WCF.Dropdown.initDropdown(this._getTriggerElement(this._elements[$elementID]), true);
2678 }
2679 }
2680
2681 return this._super(event);
2682 },
2683
2684 /**
2685 * Shows the dialog to enter a reason for executing the option with the
2686 * given name.
2687 *
2688 * @param string optionName
2689 */
2690 _showReasonDialog: function(userID, optionName) {
2691 var $languageItem = 'wcf.user.' + optionName + '.reason.description';
2692 var $reasonDescription = WCF.Language.get($languageItem);
2693
2694 WCF.System.Confirmation.show(WCF.Language.get('wcf.user.' + optionName + '.confirmMessage'), $.proxy(function(action) {
2695 if (action === 'confirm') {
2696 this._executeReasonAction(userID, optionName, $('#wcfSystemConfirmationContent').find('textarea').val());
2697 }
2698 }, this), { }, $('<fieldset><dl><dt>' + WCF.Language.get('wcf.global.reason') + '</dt><dd><textarea cols="40" rows="4" />' + ($reasonDescription != $languageItem ? '<small>' + $reasonDescription + '</small>' : '') + '</dd></dl></fieldset>'));
2699 },
2700
2701 /**
2702 * @see WCF.InlineEditor._updateState()
2703 */
2704 _updateState: function(data) {
2705 this._notification.show();
2706
2707 for (var $i = 0, $length = this._updateData.length; $i < $length; $i++) {
2708 var $data = this._updateData[$i];
2709 var $element = $('#' + $data.elementID);
2710
2711 for (var $property in $data.data) {
2712 $element.data($property, $data.data[$property]);
2713 }
2714 }
2715 },
2716
2717 /**
2718 * @see WCF.InlineEditor._validate()
2719 */
2720 _validate: function(elementID, optionName) {
2721 var $user = $('#' + elementID);
2722
2723 switch (optionName) {
2724 case 'ban':
2725 case 'unban':
2726 if (!this._getPermission('canBanUser')) {
2727 return false;
2728 }
2729
2730 if (optionName == 'ban') {
2731 return !$user.data('banned');
2732 }
2733 else {
2734 return $user.data('banned');
2735 }
2736 break;
2737
2738 case 'disableAvatar':
2739 case 'enableAvatar':
2740 if (!this._getPermission('canDisableAvatar')) {
2741 return false;
2742 }
2743
2744 if (optionName == 'disableAvatar') {
2745 return !$user.data('disableAvatar');
2746 }
2747 else {
2748 return $user.data('disableAvatar');
2749 }
2750 break;
2751
2752 case 'disableSignature':
2753 case 'enableSignature':
2754 if (!this._getPermission('canDisableSignature')) {
2755 return false;
2756 }
2757
2758 if (optionName == 'disableSignature') {
2759 return !$user.data('disableSignature');
2760 }
2761 else {
2762 return $user.data('disableSignature');
2763 }
2764 break;
2765
2766 case 'advanced':
2767 return this._getPermission('canEditUser');
2768 break;
2769 }
2770
2771 return false;
2772 },
2773
2774 /**
2775 * Sets a permission.
2776 *
2777 * @param string permission
2778 * @param integer value
2779 */
2780 setPermission: function(permission, value) {
2781 this._permissions[permission] = value;
2782 },
2783
2784 /**
2785 * Sets permissions.
2786 *
2787 * @param object permissions
2788 */
2789 setPermissions: function(permissions) {
2790 for (var $permission in permissions) {
2791 this.setPermission($permission, permissions[$permission]);
2792 }
2793 }
2794 });