4 * @author Alexander Ebert
5 * @copyright 2001-2014 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
8 WCF
.Like
= Class
.extend({
10 * true, if users can like their own content
13 _allowForOwnContent
: false,
34 * enables the dislike option
36 _enableDislikes
: true,
39 * prevents like/dislike until the server responded
45 * cached template for like details
51 * dialog overlay for like details
54 _likeDetailsDialog
: null,
58 * @var WCF.Action.Proxy
63 * shows the detailed summary of users who liked the object
68 * Initializes like support.
70 * @param boolean canLike
71 * @param boolean enableDislikes
72 * @param boolean showSummary
73 * @param boolean allowForOwnContent
75 init: function(canLike
, enableDislikes
, showSummary
, allowForOwnContent
) {
76 this._canLike
= canLike
;
77 this._enableDislikes
= enableDislikes
;
79 this._likeDetails
= { };
80 this._likeDetailsDialog
= null;
81 this._showSummary
= showSummary
;
82 this._allowForOwnContent
= allowForOwnContent
;
84 var $containers
= this._getContainers();
85 this._initContainers($containers
);
87 this._proxy
= new WCF
.Action
.Proxy({
88 success
: $.proxy(this._success
, this)
91 // bind dom node inserted listener
92 var $date
= new Date();
93 var $identifier
= $date
.toString().hashCode
+ $date
.getUTCMilliseconds();
94 WCF
.DOMNodeInsertedHandler
.addCallback('WCF.Like' + $identifier
, $.proxy(this._domNodeInserted
, this));
98 * Initialize containers once new nodes are inserted.
100 _domNodeInserted: function() {
101 var $containers
= this._getContainers();
102 this._initContainers($containers
);
107 * Initializes like containers.
109 * @param object containers
111 _initContainers: function(containers
) {
112 var $createdWidgets
= false;
113 containers
.each($.proxy(function(index
, container
) {
115 var $container
= $(container
);
116 var $containerID
= $container
.wcfIdentify();
118 if (!this._containers
[$containerID
]) {
119 this._containers
[$containerID
] = $container
;
121 // set container data
122 this._containerData
[$containerID
] = {
125 'dislikeButton': null,
126 'likes': $container
.data('like-likes'),
127 'dislikes': $container
.data('like-dislikes'),
128 'objectType': $container
.data('objectType'),
129 'objectID': this._getObjectID($containerID
),
130 'users': eval($container
.data('like-users')),
131 'liked': $container
.data('like-liked')
135 this._createWidget($containerID
);
137 $createdWidgets
= true;
141 if ($createdWidgets
) {
142 new WCF
.PeriodicalExecuter(function(pe
) {
145 WCF
.DOMNodeInsertedHandler
.execute();
151 * Returns a list of available object containers.
155 _getContainers: function() { },
158 * Returns widget container for target object container.
160 * @param string containerID
163 _getWidgetContainer: function(containerID
) { },
166 * Returns object id for targer object container.
168 * @param string containerID
171 _getObjectID: function(containerID
) { },
174 * Adds the like widget.
176 * @param integer containerID
177 * @param jQuery widget
179 _addWidget: function(containerID
, widget
) {
180 var $widgetContainer
= this._getWidgetContainer(containerID
);
182 widget
.appendTo($widgetContainer
);
186 * Builds the like widget.
188 * @param integer containerID
189 * @param jQuery likeButton
190 * @param jQuery dislikeButton
191 * @param jQuery badge
192 * @param jQuery summary
194 _buildWidget: function(containerID
, likeButton
, dislikeButton
, badge
, summary
) {
195 var $widget
= $('<aside class="likesWidget"><ul></ul></aside>');
197 likeButton
.appendTo($widget
.find('ul'));
198 dislikeButton
.appendTo($widget
.find('ul'));
200 badge
.appendTo($widget
);
202 this._addWidget(containerID
, $widget
);
206 * Creates the like widget.
208 * @param integer containerID
210 _createWidget: function(containerID
) {
211 var $likeButton
= $('<li class="likeButton"><a title="'+WCF
.Language
.get('wcf.like.button.like')+'" class="jsTooltip"><span class="icon icon16 icon-thumbs-up-alt" /> <span class="invisible">'+WCF
.Language
.get('wcf.like.button.like')+'</span></a></li>');
212 var $dislikeButton
= $('<li class="dislikeButton"><a title="'+WCF
.Language
.get('wcf.like.button.dislike')+'" class="jsTooltip"><span class="icon icon16 icon-thumbs-down-alt" /> <span class="invisible">'+WCF
.Language
.get('wcf.like.button.dislike')+'</span></a></li>');
213 if (!this._enableDislikes
) $dislikeButton
.hide();
215 if (!this._allowForOwnContent
&& (WCF
.User
.userID
== this._containers
[containerID
].data('userID'))) {
217 $dislikeButton
= $('');
220 var $badge
= $('<a class="badge jsTooltip likesBadge" />').data('containerID', containerID
).click($.proxy(this._showLikeDetails
, this));
223 if (this._showSummary
) {
224 $summary
= $('<p class="likesSummary"><span class="pointer" /></p>');
225 $summary
.children('span').data('containerID', containerID
).click($.proxy(this._showLikeDetails
, this));
227 this._buildWidget(containerID
, $likeButton
, $dislikeButton
, $badge
, $summary
);
229 this._containerData
[containerID
].likeButton
= $likeButton
;
230 this._containerData
[containerID
].dislikeButton
= $dislikeButton
;
231 this._containerData
[containerID
].badge
= $badge
;
232 this._containerData
[containerID
].summary
= $summary
;
234 $likeButton
.data('containerID', containerID
).data('type', 'like').click($.proxy(this._click
, this));
235 $dislikeButton
.data('containerID', containerID
).data('type', 'dislike').click($.proxy(this._click
, this));
236 this._setActiveState($likeButton
, $dislikeButton
, this._containerData
[containerID
].liked
);
237 this._updateBadge(containerID
);
238 if (this._showSummary
) this._updateSummary(containerID
);
242 * Displays like details for an object.
244 * @param object event
245 * @param string containerID
247 _showLikeDetails: function(event
, containerID
) {
248 var $containerID
= (event
=== null) ? containerID
: $(event
.currentTarget
).data('containerID');
250 if (this._likeDetails
[$containerID
] === undefined) {
251 this._proxy
.setOption('data', {
252 actionName
: 'getLikeDetails',
253 className
: 'wcf\\data\\like\\LikeAction',
256 containerID
: $containerID
,
257 objectID
: this._containerData
[$containerID
].objectID
,
258 objectType
: this._containerData
[$containerID
].objectType
262 this._proxy
.sendRequest();
265 if (this._likeDetailsDialog
=== null) {
266 this._likeDetailsDialog
= $('<div>' + this._likeDetails
[$containerID
] + '</div>').hide().appendTo(document
.body
);
267 this._likeDetailsDialog
.wcfDialog({
268 title
: WCF
.Language
.get('wcf.like.details')
272 this._likeDetailsDialog
.html(this._likeDetails
[$containerID
]).wcfDialog('open');
278 * Handles likes and dislikes.
280 * @param object event
282 _click: function(event
) {
283 var $button
= $(event
.currentTarget
);
284 if ($button
=== null) {
285 console
.debug("[WCF.Like] Unable to find target button, aborting.");
289 this._sendRequest($button
.data('containerID'), $button
.data('type'));
293 * Sends request through proxy.
295 * @param integer containerID
298 _sendRequest: function(containerID
, type
) {
299 // ignore retards spamming clicks on the buttons
306 this._proxy
.setOption('data', {
308 className
: 'wcf\\data\\like\\LikeAction',
311 containerID
: containerID
,
312 objectID
: this._containerData
[containerID
].objectID
,
313 objectType
: this._containerData
[containerID
].objectType
318 this._proxy
.sendRequest();
322 * Updates likeable object.
325 * @param string textStatus
326 * @param object jqXHR
328 _success: function(data
, textStatus
, jqXHR
) {
329 var $containerID
= data
.returnValues
.containerID
;
331 if (!this._containers
[$containerID
]) {
335 switch (data
.actionName
) {
338 // update container data
339 this._containerData
[$containerID
].likes
= parseInt(data
.returnValues
.likes
);
340 this._containerData
[$containerID
].dislikes
= parseInt(data
.returnValues
.dislikes
);
341 this._containerData
[$containerID
].users
= data
.returnValues
.users
;
344 this._updateBadge($containerID
);
346 if (this._showSummary
) this._updateSummary($containerID
);
348 // mark button as active
349 var $likeButton
= this._containerData
[$containerID
].likeButton
;
350 var $dislikeButton
= this._containerData
[$containerID
].dislikeButton
;
352 if (data
.returnValues
.isLiked
) $likeStatus
= 1;
353 else if (data
.returnValues
.isDisliked
) $likeStatus
= -1;
354 this._setActiveState($likeButton
, $dislikeButton
, $likeStatus
);
356 // invalidate cache for like details
357 if (this._likeDetails
[$containerID
] !== undefined) {
358 delete this._likeDetails
[$containerID
];
361 this._isBusy
= false;
364 case 'getLikeDetails':
365 this._likeDetails
[$containerID
] = data
.returnValues
.template
;
366 this._showLikeDetails(null, $containerID
);
371 _updateBadge: function(containerID
) {
372 if (!this._containerData
[containerID
].likes
&& !this._containerData
[containerID
].dislikes
) {
373 this._containerData
[containerID
].badge
.hide();
376 this._containerData
[containerID
].badge
.show();
378 // update like counter
379 var $cumulativeLikes
= this._containerData
[containerID
].likes
- this._containerData
[containerID
].dislikes
;
380 var $badge
= this._containerData
[containerID
].badge
;
381 $badge
.removeClass('green red');
382 if ($cumulativeLikes
> 0) {
383 $badge
.text('+' + WCF
.String
.formatNumeric($cumulativeLikes
));
384 $badge
.addClass('green');
386 else if ($cumulativeLikes
< 0) {
387 $badge
.text(WCF
.String
.formatNumeric($cumulativeLikes
));
388 $badge
.addClass('red');
391 $badge
.text('\u00B10');
395 var $likes
= this._containerData
[containerID
].likes
;
396 var $dislikes
= this._containerData
[containerID
].dislikes
;
397 $badge
.data('tooltip', WCF
.Language
.get('wcf.like.tooltip', { likes
: $likes
, dislikes
: $dislikes
}));
401 _updateSummary: function(containerID
) {
402 if (!this._containerData
[containerID
].likes
) {
403 this._containerData
[containerID
].summary
.hide();
406 this._containerData
[containerID
].summary
.show();
408 var $users
= this._containerData
[containerID
].users
;
410 for (var $userID
in $users
) $userArray
.push($users
[$userID
].username
);
411 var $others
= this._containerData
[containerID
].likes
- $userArray
.length
;
413 this._containerData
[containerID
].summary
.children('span').html(WCF
.Language
.get('wcf.like.summary', { users
: $userArray
, others
: $others
}));
418 * Sets button active state.
420 * @param jquery likeButton
421 * @param jquery dislikeButton
422 * @param integer likeStatus
424 _setActiveState: function(likeButton
, dislikeButton
, likeStatus
) {
425 likeButton
.removeClass('active');
426 dislikeButton
.removeClass('active');
428 if (likeStatus
== 1) {
429 likeButton
.addClass('active');
431 else if (likeStatus
== -1) {
432 dislikeButton
.addClass('active');