2013-08-19 22:30:57 +09:00
2015-06-02 01:27:36 +09:00
/ * · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
· ·
· ·
· Q V I T T E R ·
· ·
2015-07-02 02:15:31 +09:00
· ·
2015-06-02 01:27:36 +09:00
· < o ) ·
· / _ //// ·
· ( _ _ _ _ / ·
· ( o < ·
· o > \ \ \ \ _ \ ·
2015-07-02 02:15:31 +09:00
· \ \ ) \ _ _ _ _ ) ·
· ·
2015-06-02 01:27:36 +09:00
· ·
· @ licstart The following is the entire license notice for the ·
· JavaScript code in this page . ·
· ·
· Copyright ( C ) 2015 Hannes Mannerheim and other contributors ·
2015-07-02 02:15:31 +09:00
· ·
2015-06-02 01:27:36 +09:00
· ·
· This program is free software : you can redistribute it and / or modify ·
· it under the terms of the GNU Affero General Public License as ·
· published by the Free Software Foundation , either version 3 of the ·
· License , or ( at your option ) any later version . ·
· ·
· This program is distributed in the hope that it will be useful , ·
· but WITHOUT ANY WARRANTY ; without even the implied warranty of ·
· MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the ·
· GNU Affero General Public License for more details . ·
· ·
· You should have received a copy of the GNU Affero General Public License ·
· along with this program . If not , see < http : //www.gnu.org/licenses/>. ·
· ·
2015-07-02 02:15:31 +09:00
· @ licend The above is the entire license notice ·
· for the JavaScript code in this page . ·
· ·
2015-06-02 01:27:36 +09:00
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · * /
2013-08-19 22:30:57 +09:00
2015-11-18 06:48:28 +09:00
/ * ·
·
2015-11-19 23:39:19 +09:00
· Build a menu
2015-11-18 06:48:28 +09:00
·
2015-11-19 23:39:19 +09:00
· Menus currently support four row types : divider , functions , links and profile - prefs - toggles
· Function rows run the function in the function attribute when clicked .
· Profile - prefs - toggle rows toggles the preference in the attribute when clicked .
2015-11-18 06:48:28 +09:00
·
· @ param streamObject : stream object returned by pathToStreamRouter ( )
·
· · · · · · · · · * /
2015-11-19 23:39:19 +09:00
function getMenu ( menuArray ) {
if ( typeof getMenu == 'undefined' || getMenu === false ) {
2015-11-18 06:48:28 +09:00
return false ;
}
var menuHTML = buildMenuTop ( ) ;
2015-11-19 23:39:19 +09:00
$ . each ( menuArray , function ( ) {
2015-11-18 06:48:28 +09:00
if ( this . type == 'divider' ) {
menuHTML = menuHTML + buildMenuDivider ( ) ;
}
else if ( this . type == 'function' ) {
menuHTML = menuHTML + buildMenuRowFullwidth ( this . label , {
class : 'row-type-' + this . type ,
'data-menu-row-type' : this . type ,
2015-11-19 23:39:19 +09:00
'data-function-name' : this . functionName ,
'data-function-arguments' : JSON . stringify ( this . functionArguments )
} ) ;
}
else if ( this . type == 'link' ) {
menuHTML = menuHTML + buildMenuRowFullwidth ( this . label , {
class : 'row-type-' + this . type ,
'href' : this . href
2015-11-18 06:48:28 +09:00
} ) ;
}
else if ( this . type == 'profile-prefs-toggle' ) {
// only prefs in the qvitter namespace is supported
if ( this . namespace == 'qvitter' ) {
// enabled?
var prefEnabledOrDisabled = 'disabled' ;
if ( typeof window . qvitterProfilePrefs [ this . topic ] != 'undefined'
&& window . qvitterProfilePrefs [ this . topic ] !== null
&& window . qvitterProfilePrefs [ this . topic ] != ''
&& window . qvitterProfilePrefs [ this . topic ] !== false
&& window . qvitterProfilePrefs [ this . topic ] != 0
&& window . qvitterProfilePrefs [ this . topic ] != '0' ) {
prefEnabledOrDisabled = 'enabled' ;
}
// get row html
menuHTML = menuHTML + buildMenuRowFullwidth ( this . label , {
id : this . topic ,
class : 'row-type-' + this . type + ' ' + prefEnabledOrDisabled ,
'data-menu-row-type' : this . type ,
'data-profile-prefs-topic' : this . topic ,
'data-profile-prefs-namespace' : this . namespace ,
2016-01-23 05:19:08 +09:00
'data-profile-pref-state' : prefEnabledOrDisabled ,
'data-profile-pref-callback' : this . callback
2015-11-18 06:48:28 +09:00
} ) ;
}
}
} ) ;
return menuHTML + buildMenuBottom ( ) ;
}
2015-11-19 23:39:19 +09:00
/ * ·
·
· Menu from streamObject
·
· · · · · · · · · * /
function streamObjectGetMenu ( streamObject ) {
if ( typeof streamObject == 'undefined' ) {
return false ;
}
if ( streamObject . menu === false ) {
return false ;
}
return getMenu ( streamObject . menu ) ;
}
2015-11-18 06:48:28 +09:00
/ * ·
·
· Menu components
·
· · · · · · · · · * /
function buildMenuTop ( ) {
return ' < ul class = "dropdown-menu" > \
< li class = "dropdown-caret right" > \
< span class = "caret-outer" > < / s p a n > \
< span class = "caret-inner" > < / s p a n > \
< / l i > ' ;
}
function buildMenuBottom ( ) {
return '</ul>' ;
}
function buildMenuDivider ( ) {
return '<li class="fullwidth dropdown-divider"></li>' ;
}
function buildMenuRowFullwidth ( label , attributes ) {
var attributesHTML = '' ;
$ . each ( attributes , function ( k , v ) {
2015-11-19 23:39:19 +09:00
attributesHTML = attributesHTML + ' ' + k + '=\'' + v + '\'' ;
2015-11-18 06:48:28 +09:00
} ) ;
return '<li class="fullwidth"><a' + attributesHTML + '>' + replaceHtmlSpecialChars ( label ) + '</a></li>' ;
}
2015-11-19 23:39:19 +09:00
function alignMenuToParent ( menu , parent , offsetLeft ) {
if ( typeof offsetLeft == 'undefined' ) {
var offsetLeft = 0 ;
}
var menuLeft = parent . innerWidth ( ) / 2 - menu . width ( ) + 15 + offsetLeft ;
2015-11-18 06:48:28 +09:00
var menuTop = parent . height ( ) + 5 ;
menu . css ( 'left' , menuLeft + 'px' ) ;
menu . css ( 'top' , menuTop + 'px' ) ;
2015-11-19 23:39:19 +09:00
menu . css ( 'display' , 'block' ) ; // show menu
2015-11-18 06:48:28 +09:00
}
2013-08-19 22:30:57 +09:00
2015-09-29 23:31:05 +09:00
/ * ·
·
· Show error message
·
· @ param message : error message
·
· · · · · · · · · * /
2015-11-17 04:27:40 +09:00
function showErrorMessage ( message , after ) {
if ( typeof after == 'undefined' ) {
var after = $ ( '#user-container' ) ;
}
after . after ( '<div class="error-message">' + message + '<span class="discard-error-message"></span></div>' ) ;
2015-09-29 23:31:05 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-02-26 05:16:24 +09:00
· Show favs and requeets in queet element
2015-07-02 02:15:31 +09:00
·
2013-08-19 22:30:57 +09:00
· @ param q : queet jQuery object
2015-07-02 02:15:31 +09:00
· @ param data : object with users that has faved and requeeted
·
2013-08-19 22:30:57 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
function showFavsAndRequeetsInQueet ( q , data ) {
2013-08-19 22:30:57 +09:00
2015-06-24 22:34:44 +09:00
// set the non-expanded fav- and rq-count
q . children ( '.queet' ) . find ( '.action-fav-num' ) . html ( data . favs . length ) ;
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . find ( '.action-fav-num' ) . attr ( 'data-fav-num' , data . favs . length ) ;
2015-06-24 22:34:44 +09:00
q . children ( '.queet' ) . find ( '.action-rq-num' ) . html ( data . repeats . length ) ;
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . find ( '.action-rq-num' ) . attr ( 'data-rq-num' , data . repeats . length ) ;
2015-06-24 22:34:44 +09:00
// don't proceed if queet is not expanded
if ( ! q . hasClass ( 'expanded' ) || q . hasClass ( 'collapsing' ) ) {
return ;
}
2015-07-02 02:15:31 +09:00
// don't proceed and remove expanded stats if all favs and repeats are removed
2015-06-24 22:34:44 +09:00
if ( data . favs . length < 1 && data . repeats . length < 1 ) {
q . children ( '.queet' ) . find ( '.stats' ) . remove ( ) ;
return ;
2015-07-02 02:15:31 +09:00
}
2015-06-24 22:34:44 +09:00
2015-02-26 05:16:24 +09:00
// remove any existing stats container and add a new empty one
if ( q . children ( '.queet' ) . find ( 'ul.stats' ) . length > 0 ) {
q . children ( '.queet' ) . find ( 'ul.stats' ) . remove ( ) ;
2013-08-19 22:30:57 +09:00
}
2015-06-24 22:34:44 +09:00
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . find ( '.queet-stats-container' ) . prepend ( '<ul class="stats"><li class="avatar-row"></li></ul>' ) ;
// set the expanded fav-count number
2015-02-26 05:16:24 +09:00
if ( data . favs . length > 0 ) {
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
if ( data . favs . length == 1 ) {
var favLabel = window . sL . favoriteNoun ;
}
else if ( data . favs . length > 1 ) {
var favLabel = window . sL . favoritesNoun ;
}
2015-06-24 22:34:44 +09:00
2015-02-26 05:16:24 +09:00
if ( q . children ( '.queet' ) . find ( '.fav-count' ) . length > 0 ) {
q . children ( '.queet' ) . find ( '.fav-count' ) . children ( 'strong' ) . html ( data . favs . length ) ;
2015-07-02 02:15:31 +09:00
}
2015-02-26 05:16:24 +09:00
else {
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . find ( 'li.avatar-row' ) . before ( '<li class="fav-count"><a>' + favLabel + ' </a><strong>' + data . favs . length + '</strong></li>' ) ;
}
2013-08-19 22:30:57 +09:00
}
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
// add repeats
if ( data . repeats . length > 0 ) {
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
if ( data . repeats . length == 1 ) {
var repeatsLabel = window . sL . requeetNoun ;
}
else if ( data . repeats . length > 1 ) {
var repeatsLabel = window . sL . requeetsNoun ;
}
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
if ( q . children ( '.queet' ) . find ( '.rq-count' ) . length > 0 ) {
q . children ( '.queet' ) . find ( '.rq-count' ) . children ( 'strong' ) . html ( data . repeats . length ) ;
2015-07-02 02:15:31 +09:00
}
2015-02-26 05:16:24 +09:00
else {
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . find ( 'li.avatar-row' ) . before ( '<li class="rq-count"><a>' + repeatsLabel + ' </a><strong>' + data . repeats . length + '</strong></li>' ) ;
}
2013-08-19 22:30:57 +09:00
}
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
// merge favs and repeats objects by user_id (removes duplicate users)
var favsAndRepeats = { } ;
$ . each ( data . repeats , function ( ) {
favsAndRepeats [ this . user _id ] = this ;
} ) ;
$ . each ( data . favs , function ( ) {
favsAndRepeats [ this . user _id ] = this ;
} ) ;
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
// make an object with time the key
var favsAndRepeatsByTime = { } ;
$ . each ( favsAndRepeats , function ( ) {
favsAndRepeatsByTime [ this . time ] = this ;
2015-07-02 02:15:31 +09:00
} ) ;
2015-02-26 05:16:24 +09:00
// create an array with times and sort it
var timeSorted = [ ] ;
$ . each ( favsAndRepeats , function ( ) {
timeSorted . push ( this . time ) ;
} ) ;
timeSorted . sort ( ) ;
// display avatars in chronological order, max 7
var avatarnum = 1 ;
$ . each ( timeSorted , function ( ) {
q . children ( '.queet' ) . find ( '.avatar-row' ) . append ( '<a title="' + favsAndRepeatsByTime [ this ] . fullname + '" data-user-id="' + favsAndRepeatsByTime [ this ] . user _id + '" href="' + favsAndRepeatsByTime [ this ] . profileurl + '"><img alt="' + favsAndRepeatsByTime [ this ] . fullname + '" src="' + favsAndRepeatsByTime [ this ] . avatarurl + '" class="avatar size24" id="av-' + favsAndRepeatsByTime [ this ] . user _id + '"></a>' ) ;
2015-07-02 02:15:31 +09:00
if ( avatarnum > 15 ) {
2015-02-26 05:50:16 +09:00
return false ;
}
2015-02-26 05:16:24 +09:00
avatarnum ++ ;
} ) ;
2013-08-19 22:30:57 +09:00
}
2015-06-03 06:45:46 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-06-03 06:45:46 +09:00
· Build profile card HTML
2015-07-02 02:15:31 +09:00
·
2015-06-03 06:45:46 +09:00
· @ param data : an object with a user array
2015-07-02 02:15:31 +09:00
·
2015-06-03 06:45:46 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2015-06-03 06:45:46 +09:00
function buildProfileCard ( data ) {
data = cleanUpUserObject ( data ) ;
2015-07-02 02:15:31 +09:00
2015-06-03 06:45:46 +09:00
// use avatar if no cover photo
var coverPhotoHtml = '' ;
if ( data . cover _photo !== false ) {
coverPhotoHtml = 'background-image:url(\'' + data . cover _photo + '\')' ;
2015-07-02 02:15:31 +09:00
}
2015-06-03 06:45:46 +09:00
// follows me?
var follows _you = '' ;
2015-11-05 21:10:11 +09:00
if ( data . follows _you === true && window . loggedIn . id != data . id ) {
2015-07-02 02:15:31 +09:00
var follows _you = '<span class="follows-you">' + window . sL . followsYou + '</span>' ;
2015-06-03 06:45:46 +09:00
}
// show user actions if logged in
var followingClass = '' ;
if ( data . following ) {
followingClass = 'following' ;
2015-07-02 02:15:31 +09:00
}
2015-09-19 05:08:53 +09:00
2015-06-03 06:45:46 +09:00
var followButton = '' ;
2015-07-02 02:15:31 +09:00
2015-09-19 05:08:53 +09:00
// only add follow button if this is a local user
if ( data . is _local == true ) {
2015-11-05 21:10:11 +09:00
if ( typeof window . loggedIn . screen _name != 'undefined' && window . loggedIn . id != data . id ) {
2015-09-19 05:08:53 +09:00
var followButton = '<div class="user-actions"><button data-follow-user-id="' + data . id + '" data-follow-user="' + data . statusnet _profile _url + '" type="button" class="qvitter-follow-button ' + followingClass + '"><span class="button-text follow-text"><i class="follow"></i>' + window . sL . userFollow + '</span><span class="button-text following-text">' + window . sL . userFollowing + '</span><span class="button-text unfollow-text">' + window . sL . userUnfollow + '</span></button></div>' ;
}
// follow from external instance if logged out
if ( typeof window . loggedIn . screen _name == 'undefined' ) {
var followButton = '<div class="user-actions"><button type="button" class="external-follow-button ' + followingClass + '"><span class="button-text follow-text"><i class="follow"></i>' + window . sL . userExternalFollow + '</span></button></div>' ;
}
2015-06-03 06:45:46 +09:00
2015-09-19 05:08:53 +09:00
// edit profile button if me
2015-11-05 21:10:11 +09:00
if ( typeof window . loggedIn . screen _name != 'undefined' && window . loggedIn . id == data . id ) {
2015-09-19 05:08:53 +09:00
var followButton = '<div class="user-actions"><button type="button" class="edit-profile-button"><span class="button-text edit-profile-text">' + window . sL . editMyProfile + '</span></button></div>' ;
}
2015-07-02 02:15:31 +09:00
}
2015-06-03 06:45:46 +09:00
// is webpage empty?
var emptyWebpage = '' ;
if ( data . url . length < 1 ) {
2015-07-02 02:15:31 +09:00
emptyWebpage = ' empty' ;
2015-06-03 06:45:46 +09:00
}
2015-07-02 02:15:31 +09:00
2015-06-03 06:45:46 +09:00
// full card html
data . profileCardHtml = ' \
< div class = "profile-card" > \
2015-12-04 05:41:00 +09:00
< div class = "profile-header-inner" style = "' + coverPhotoHtml + '" data - user - id = "' + data.id + '" > \
2015-06-03 06:45:46 +09:00
< div class = "profile-header-inner-overlay" > < / d i v > \
< a class = "profile-picture" href = "' + data.profile_image_url_original + '" > \
2015-12-04 05:41:00 +09:00
< img class = "avatar profile-size" src = "' + data.profile_image_url_profile_size + '" data - user - id = "' + data.id + '" / > \
2015-06-03 06:45:46 +09:00
< / a > \
< div class = "profile-card-inner" > \
2015-12-04 05:41:00 +09:00
< h1 class = "fullname" data - user - id = "' + data.id + '" > ' + data.name + ' < span > < / s p a n > < / h 1 > \
2015-06-03 06:45:46 +09:00
< h2 class = "username" > \
2015-12-04 05:41:00 +09:00
< span class = "screen-name" data - user - id = "' + data.id + '" > @ ' + data.screen_name + ' < / s p a n > \
2015-06-03 06:45:46 +09:00
' + follows_you + ' \
< / h 2 > \
< div class = "bio-container" > < p > ' + data.description + ' < / p > < / d i v > \
< p class = "location-and-url" > \
< span class = "location" > ' + data.location + ' < / s p a n > \
< span class = "url' + emptyWebpage + '" > \
< span class = "divider" > · < / s p a n > \
< a href = "' + data.url + '" > ' + data.url.replace(' http : //','').replace('https://','') + '</a>\
< / s p a n > \
< / p > \
< / d i v > \
< / d i v > \
< div class = "profile-banner-footer" > \
< ul class = "stats" > \
2015-09-18 08:42:52 +09:00
< li class = "tweet-num" > < a href = "' + data.statusnet_profile_url + '" class = "tweet-stats" > ' + window.sL.notices + ' < strong > ' + data.statuses_count + ' < / s t r o n g > < / a > < / l i > \
< li class = "following-num" > < a href = "' + data.statusnet_profile_url + '/subscriptions" class = "following-stats" > ' + window.sL.following + ' < strong > ' + data.friends_count + ' < / s t r o n g > < / a > < / l i > \
< li class = "follower-num" > < a href = "' + data.statusnet_profile_url + '/subscribers" class = "follower-stats" > ' + window.sL.followers + ' < strong > ' + data.followers_count + ' < / s t r o n g > < / a > < / l i > \
< li class = "groups-num" > < a href = "' + data.statusnet_profile_url + '/groups" class = "groups-stats" > ' + window.sL.groups + ' < strong > ' + data.groups_count + ' < / s t r o n g > < / a > < / l i > \
2015-06-03 06:45:46 +09:00
< / u l > \
' + followButton + ' \
< div class = "clearfix" > < / d i v > \
< / d i v > \
< / d i v > \
' ;
return data ;
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-06-03 06:45:46 +09:00
· Build external profile card HTML
2015-07-02 02:15:31 +09:00
·
· @ param data : an object containing data . external user array ,
2015-06-03 06:45:46 +09:00
· and maybe ( hopefully ) also a data . local user array
2015-07-02 02:15:31 +09:00
·
2015-06-03 06:45:46 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2015-06-03 06:45:46 +09:00
function buildExternalProfileCard ( data ) {
// local profile id and follow class
var followLocalIdHtml = '' ;
2015-07-02 02:15:31 +09:00
var followingClass = '' ;
2015-06-03 06:45:46 +09:00
if ( typeof data . local != 'undefined' && data . local !== null ) {
followLocalIdHtml = ' data-follow-user-id="' + data . local . id + '"' ;
if ( data . local . following ) {
followingClass = 'following' ;
}
}
2015-07-02 02:15:31 +09:00
2015-06-03 06:45:46 +09:00
// follows me?
var follows _you = '' ;
2015-11-05 21:10:11 +09:00
if ( data . local !== null && data . local . follows _you === true && window . loggedIn . id != data . local . id ) {
2015-07-02 02:15:31 +09:00
var follows _you = '<span class="follows-you">' + window . sL . followsYou + '</span>' ;
}
2015-06-03 06:45:46 +09:00
// empty strings and zeros instead of null
data = cleanUpUserObject ( data . external ) ;
2015-07-02 02:15:31 +09:00
2015-06-03 06:45:46 +09:00
// old statusnet-versions might not have full avatar urls in their api response
if ( typeof data . profile _image _url _original == 'undefined'
|| data . profile _image _url _original === null
|| data . profile _image _url _original . length == 0 ) {
2015-07-02 02:15:31 +09:00
data . profile _image _url _original = data . profile _image _url ;
2015-06-03 06:45:46 +09:00
}
if ( typeof data . profile _image _url _profile _size == 'undefined'
|| data . profile _image _url _profile _size === null
|| data . profile _image _url _profile _size . length == 0 ) {
2015-07-02 02:15:31 +09:00
data . profile _image _url _profile _size = data . profile _image _url ;
}
2015-06-03 06:45:46 +09:00
// we might have a cover photo
if ( typeof data . cover _photo != 'undefined' && data . cover _photo !== false ) {
var cover _photo = data . cover _photo ;
}
else {
2015-07-02 02:15:31 +09:00
var cover _photo = data . profile _image _url _original ;
2015-06-03 06:45:46 +09:00
}
// is webpage empty?
var emptyWebpage = '' ;
if ( data . url . length < 1 ) {
2015-07-02 02:15:31 +09:00
emptyWebpage = ' empty' ;
}
2015-06-05 09:17:33 +09:00
var serverUrl = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( data . statusnet _profile _url , data . screen _name ) ;
2015-07-02 02:15:31 +09:00
data . screenNameWithServer = '@' + data . screen _name + '@' + serverUrl ;
2015-09-19 05:08:53 +09:00
var followButton = '' ;
// we can only follow remote users if we're logged in at the moment
if ( window . loggedIn !== false ) {
var followButton = '<div class="user-actions"><button' + followLocalIdHtml + ' data-follow-user="' + data . statusnet _profile _url + '" type="button" class="qvitter-follow-button ' + followingClass + '"><span class="button-text follow-text"><i class="follow"></i>' + window . sL . userFollow + '</span><span class="button-text following-text">' + window . sL . userFollowing + '</span><span class="button-text unfollow-text">' + window . sL . userUnfollow + '</span></button></div>' ;
}
2015-06-03 06:45:46 +09:00
2015-09-15 10:58:11 +09:00
data . profileCardHtml = ' \
2015-06-03 06:45:46 +09:00
< div class = "profile-card" > \
< div class = "profile-header-inner" style = "background-image:url(\'' + cover_photo + '\')" > \
< div class = "profile-header-inner-overlay" > < / d i v > \
< a class = "profile-picture" > < img src = "' + data.profile_image_url_profile_size + '" / > < / a > \
< div class = "profile-card-inner" > \
2015-09-17 05:41:48 +09:00
< a target = "_blank" href = "' + data.statusnet_profile_url + '" > \
< h1 class = "fullname" > ' + data.name + ' < span > < / s p a n > < / h 1 > \
< h2 class = "username" > \
< span class = "screen-name" > ' + data.screenNameWithServer + ' < / s p a n > \
< span class = "ostatus-link" data - tooltip = "' + window.sL.goToTheUsersRemoteProfile + '" > ' + window.sL.goToTheUsersRemoteProfile + ' < / s p a n > \
' + follows_you + ' \
< / h 2 > \
< / a > \
2015-06-03 06:45:46 +09:00
< div class = "bio-container" > < p > ' + data.description + ' < / p > < / d i v > \
< p class = "location-and-url" > \
< span class = "location" > ' + data.location + ' < / s p a n > \
< span class = "url' + emptyWebpage + '" > \
< span class = "divider" > · < / s p a n > \
< a target = "_blank" href = "' + data.url + '" > ' + data.url.replace(' http : //','').replace('https://','') + '</a>\
< / s p a n > \
< / p > \
< / d i v > \
< / d i v > \
< div class = "profile-banner-footer" > \
< ul class = "stats" > \
2015-09-17 05:41:48 +09:00
< li class = "tweet-num" > < a class = "tweet-stats" target = "_blank" href = "' + data.statusnet_profile_url + '" > ' + window.sL.notices + ' < strong > ' + data.statuses_count + ' < / s t r o n g > < / a > < / l i > \
< li class = "following-num" > < a class = "following-stats" target = "_blank" href = "' + data.statusnet_profile_url + '/subscriptions" > ' + window.sL.following + ' < strong > ' + data.friends_count + ' < / s t r o n g > < / a > < / l i > \
< li class = "follower-num" > < a class = "follower-stats" target = "_blank" href = "' + data.statusnet_profile_url + '/subscribers" > ' + window.sL.followers + ' < strong > ' + data.followers_count + ' < / s t r o n g > < / a > < / l i > \
2015-06-03 06:45:46 +09:00
< / u l > \
' + followButton + ' \
< div class = "clearfix" > < / d i v > \
< / d i v > \
< / d i v > \
2015-07-02 02:15:31 +09:00
< div class = "clearfix" > < / d i v > ' ;
2015-06-03 06:45:46 +09:00
return data ;
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-06-03 06:45:46 +09:00
· Adds a profile card before feed element
2015-07-02 02:15:31 +09:00
·
2015-06-03 06:45:46 +09:00
· @ param data : an object with a user array
2015-07-02 02:15:31 +09:00
·
2015-06-03 06:45:46 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2015-06-03 06:45:46 +09:00
function addProfileCardToDOM ( data ) {
2015-07-02 02:15:31 +09:00
2015-06-03 06:45:46 +09:00
// change design
changeDesign ( { backgroundimage : data . background _image , backgroundcolor : data . backgroundcolor , linkcolor : data . linkcolor } ) ;
// remove any old profile card and show profile card
$ ( '#feed' ) . siblings ( '.profile-card' ) . remove ( ) ;
2015-07-02 02:15:31 +09:00
$ ( '#feed' ) . before ( data . profileCardHtml ) ;
2015-06-03 06:45:46 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Adds a group profile card before feed element
2015-07-02 02:15:31 +09:00
·
2013-08-19 22:30:57 +09:00
· @ param data : an object with one or more queet objects
2015-07-02 02:15:31 +09:00
·
2013-08-19 22:30:57 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
function groupProfileCard ( groupAlias ) {
2013-08-19 22:30:57 +09:00
getFromAPI ( 'statusnet/groups/show/' + groupAlias + '.json' , function ( data ) { if ( data ) {
2015-07-02 02:15:31 +09:00
2013-08-19 22:30:57 +09:00
data . nickname = data . nickname || '' ;
data . fullname = data . fullname || '' ;
2015-08-26 06:27:08 +09:00
data . stream _logo = data . stream _logo || window . defaultAvatarStreamSize ;
data . homepage _logo = data . homepage _logo || window . defaultAvatarProfileSize ;
data . original _logo = data . original _logo || window . defaultAvatarProfileSize ;
2013-08-19 22:30:57 +09:00
data . description = data . description || '' ;
data . homepage = data . homepage || '' ;
data . url = data . url || '' ;
data . member _count = data . member _count || 0 ;
2015-07-02 02:15:31 +09:00
data . admin _count = data . admin _count || 0 ;
2013-08-19 22:30:57 +09:00
// show user actions if logged in
var memberClass = '' ;
if ( data . member ) {
memberClass = 'member' ;
2015-07-02 02:15:31 +09:00
}
2013-08-19 22:30:57 +09:00
var memberButton = '' ;
2015-07-02 02:15:31 +09:00
if ( typeof window . loggedIn . screen _name != 'undefined' ) {
var memberButton = '<div class="user-actions"><button data-group-id="' + data . id + '" type="button" class="member-button ' + memberClass + '"><span class="button-text join-text"><i class="join"></i>' + window . sL . joinGroup + '</span><span class="button-text ismember-text">' + window . sL . isMemberOfGroup + '</span><span class="button-text leave-text">' + window . sL . leaveGroup + '</span></button></div>' ;
2013-08-19 22:30:57 +09:00
}
2015-07-02 02:15:31 +09:00
2014-01-29 03:42:47 +09:00
// follow from external instance if logged out
2015-07-02 02:15:31 +09:00
if ( typeof window . loggedIn . screen _name == 'undefined' ) {
var memberButton = '<div class="user-actions"><button type="button" class="external-member-button"><span class="button-text join-text"><i class="join"></i>' + window . sL . joinExternalGroup + '</span></button></div>' ;
}
2015-05-30 00:30:03 +09:00
// change design
changeDesign ( { backgroundimage : false , backgroundcolor : false , linkcolor : false } ) ;
2015-07-02 02:15:31 +09:00
2013-08-19 22:30:57 +09:00
// add card to DOM
2015-07-02 02:15:31 +09:00
$ ( '#feed' ) . siblings ( '.profile-card' ) . remove ( ) ; // remove any old profile card
2015-09-18 08:42:52 +09:00
$ ( '#feed' ) . before ( ' < div class = "profile-card group" > \
< div class = "profile-header-inner" style = "background-image:url(' + data.original_logo + ')" > \
< div class = "profile-header-inner-overlay" > < / d i v > \
< a class = "profile-picture" href = "' + data.original_logo + '" > \
< img src = "' + data.homepage_logo + '" / > \
< / a > \
< div class = "profile-card-inner" > \
< a href = "' + window.siteInstanceURL + 'group/' + data.nickname + '" > \
2015-10-04 02:24:02 +09:00
< h1 class = "fullname" > \
2015-09-18 08:42:52 +09:00
' + data.fullname + ' \
< span > < / s p a n > \
< / h 1 > \
< h2 class = "username" > \
< span class = "screen-name" > ! ' + data.nickname + ' < / s p a n > \
< / h 2 > \
< / a > \
< div class = "bio-container" > \
< p > ' + data.description + ' < / p > \
< / d i v > \
< p class = "location-and-url" > \
< span class = "url" > \
< a href = "' + data.homepage + '" > ' + data.homepage.replace(' http : //','').replace('https://','') + '</a>\
< / s p a n > \
< / p > \
< / d i v > \
< / d i v > \
< div class = "profile-banner-footer" > \
< ul class = "stats" > \
< li > \
< a href = "' + window.siteInstanceURL + 'group/' + data.nickname + '/members" class = "member-stats" > \
' + window.sL.memberCount + ' \
< strong > ' + data.member_count + ' < / s t r o n g > \
< / a > \
< / l i > \
< li > \
< a href = "' + window.siteInstanceURL + 'group/' + data.nickname + '/admins" class = "admin-stats" > \
' + window.sL.adminCount + ' \
< strong > ' + data.admin_count + ' < / s t r o n g > \
< / a > \
< / l i > \
< / u l > \
' + memberButton + ' \
< div class = "clearfix" > < / d i v > \
< / d i v > \
< / d i v > ' ) ;
2015-07-02 02:15:31 +09:00
} } ) ;
}
/ * ·
·
2013-08-19 22:30:57 +09:00
· Change stream
2015-07-02 02:15:31 +09:00
·
2015-09-18 08:42:52 +09:00
· @ param streamObject : object returned by pathToStreamRouter ( )
2015-09-18 20:42:19 +09:00
· @ param setLocation : whether we should update the browsers location bar when we set the new stream
· @ param fallbackId : if we fail to get the stream , it can be due to a bad / changed user / group nickname ,
· in that case this parameter can contain a user / group id that we can use to retrieve the correct nickname
2013-08-19 22:30:57 +09:00
· @ param actionOnSuccess : callback function on success
2015-07-02 02:15:31 +09:00
·
2013-08-19 22:30:57 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2015-09-18 20:42:19 +09:00
function setNewCurrentStream ( streamObject , setLocation , fallbackId , actionOnSuccess ) {
2015-09-18 08:42:52 +09:00
2015-11-23 08:24:33 +09:00
if ( ! streamObject || ! streamObject . stream ) {
2015-09-18 08:42:52 +09:00
console . log ( 'invalid streamObject, no stream to set!' ) ;
return ;
}
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
// update the cache for the old stream
rememberStreamStateInLocalStorage ( ) ;
2015-09-30 00:05:20 +09:00
// remove any old error messages
$ ( '.error-message' ) . remove ( ) ;
2013-08-19 22:30:57 +09:00
// halt interval that checks for new queets
window . clearInterval ( checkForNewQueetsInterval ) ;
2015-07-02 02:15:31 +09:00
2014-09-20 09:53:10 +09:00
// scroll to top
$ ( window ) . scrollTop ( 0 ) ;
$ ( 'body' ) . addClass ( 'androidFix' ) . scrollTop ( 0 ) . removeClass ( 'androidFix' ) ;
2015-07-02 02:15:31 +09:00
2015-07-02 02:49:50 +09:00
// blur any selected links
$ ( 'a' ) . blur ( ) ;
2015-11-20 05:22:59 +09:00
// unset metadata for the old stream saved in attributes
2015-07-02 02:49:50 +09:00
$ ( '#feed-body' ) . removeAttr ( 'data-search-page-number' ) ;
2015-11-20 05:22:59 +09:00
$ ( '#feed-body' ) . removeAttr ( 'data-end-reached' ) ;
2015-11-23 08:24:33 +09:00
// hide new queets bar and reload stream button
$ ( '#new-queets-bar-container' ) . addClass ( 'hidden' ) ;
$ ( '.reload-stream' ) . hide ( ) ;
display _spinner ( '#feed-header-inner' ) ;
2015-11-20 05:22:59 +09:00
// are we just reloading?
var weAreReloading = false ;
2015-11-23 08:24:33 +09:00
if ( typeof window . currentStreamObject != 'undefined' && window . currentStreamObject . stream == streamObject . stream ) {
2015-11-20 05:22:59 +09:00
weAreReloading = true ;
}
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
// remember the most recent stream object
2015-09-18 08:42:52 +09:00
window . currentStreamObject = streamObject ;
2015-11-20 05:22:59 +09:00
// set the new streams header
2015-09-18 08:42:52 +09:00
if ( streamObject . streamSubHeader ) {
2015-11-20 05:22:59 +09:00
$ ( '#feed-header-inner h2' ) . html ( streamObject . streamSubHeader ) ;
2015-07-02 02:15:31 +09:00
}
2013-08-19 22:30:57 +09:00
else {
2015-11-20 05:22:59 +09:00
$ ( '#feed-header-inner h2' ) . html ( streamObject . streamHeader ) ;
2015-07-02 02:15:31 +09:00
}
2014-01-29 03:42:47 +09:00
2015-11-20 05:22:59 +09:00
// add menu cog if this stream has a menu
if ( streamObject . menu && window . loggedIn ) {
$ ( '#feed-header-inner h2' ) . append ( '<div id="stream-menu-cog" data-tooltip="' + window . sL . timelineOptions + '"></div>' ) ;
}
2015-05-19 22:22:52 +09:00
2015-11-23 08:24:33 +09:00
// subtle animation to show somethings happening
$ ( '#feed-header-inner h2' ) . css ( 'opacity' , '0.2' ) ;
$ ( '#feed-header-inner h2' ) . animate ( { opacity : '1' } , 1000 ) ;
2015-11-20 05:22:59 +09:00
// if we're just reloading, we dont need to:
// (1) check if we have a cached version of this stream
// (2) remove the stream if we don't
// (3) change design
if ( weAreReloading === false ) {
// (1) check if we have a cached version of the stream
var haveOldStreamState = localStorageObjectCache _GET ( 'streamState' , window . currentStreamObject . path ) ;
// discard and remove any cached data that is not in this (new) format
if ( typeof haveOldStreamState . card == 'undefined'
|| typeof haveOldStreamState . feed == 'undefined' ) {
localStorageObjectCache _STORE ( 'streamState' , window . currentStreamObject . path , false ) ;
haveOldStreamState = false ;
2015-09-18 20:42:19 +09:00
}
2015-11-26 03:51:51 +09:00
// show cached version immediately
2015-11-20 05:22:59 +09:00
if ( haveOldStreamState ) {
$ ( '.profile-card,.hover-card,.hover-card-caret' ) . remove ( ) ;
$ ( '#feed' ) . before ( haveOldStreamState . card ) ;
2015-11-23 08:24:33 +09:00
var oldStreamState = $ ( '<div/>' ) . html ( haveOldStreamState . feed ) ;
// if the cached items has data-quitter-id-in-stream attributes, sort them before adding them
if ( oldStreamState . children ( '.stream-item[data-quitter-id-in-stream]' ) . length > 0 ) {
oldStreamState . sortDivsByAttrDesc ( 'data-quitter-id-in-stream' ) ;
}
2015-11-26 03:51:51 +09:00
// hide any removed notices that we know of
if ( typeof window . knownDeletedNotices != 'undefined' ) {
$ . each ( window . knownDeletedNotices , function ( delededURI , v ) {
oldStreamState . children ( '.stream-item[data-uri="' + delededURI + '"]' ) . addClass ( 'deleted always-hidden' ) ;
} ) ;
2015-11-23 08:24:33 +09:00
}
2015-09-22 05:46:15 +09:00
2015-12-03 19:29:08 +09:00
// hide all notices from blocked users
if ( typeof window . allBlocking != 'undefined' ) {
$ . each ( window . allBlocking , function ( ) {
oldStreamState . find ( 'strong.name[data-user-id="' + this + '"]' ) . closest ( '.stream-item' ) . addClass ( 'profile-blocked-by-me' ) ;
} ) ;
}
2016-01-17 08:21:30 +09:00
// hide dublicate repeats, only show the first/oldest instance of a notice
oldStreamState = hideAllButOldestInstanceOfStreamItem ( oldStreamState ) ;
2015-11-26 04:36:28 +09:00
// show full notice text for all cached notices, if we have it in cache
$ . each ( oldStreamState . children ( '.stream-item' ) , function ( ) {
getFullUnshortenedHtmlForQueet ( $ ( this ) , true ) ;
} ) ;
2015-11-26 04:11:59 +09:00
// if this is notidfications we have seen obviously seen them before
oldStreamState . children ( '.stream-item' ) . removeClass ( 'not-seen' ) ;
2015-11-26 03:51:51 +09:00
$ ( '#feed-body' ) . html ( oldStreamState . html ( ) ) ;
2015-11-20 05:22:59 +09:00
// set location bar from stream
if ( setLocation ) {
setUrlFromStream ( streamObject ) ;
setLocation = false ; // don't set location twice if we've already set it here
}
2015-09-22 05:46:15 +09:00
2015-12-22 21:36:44 +09:00
// update all queets times
updateAllQueetsTimes ( ) ;
2015-11-20 05:22:59 +09:00
// also mark this stream as the current stream immediately, if a saved copy exists
addStreamToHistoryMenuAndMarkAsCurrent ( streamObject ) ;
2016-01-23 05:19:08 +09:00
// make sure page-container and feed is visible
$ ( '#page-container' ) . css ( 'opacity' , '1' ) ;
$ ( '#feed' ) . css ( 'opacity' , '1' ) ;
// run callbacks for this stream
if ( streamObject . callbacks !== false ) {
$ . each ( streamObject . callbacks , function ( ) {
if ( typeof window [ this ] == 'function' ) {
window [ this ] ( ) ;
}
} ) ;
}
2015-11-20 05:22:59 +09:00
// maybe do something
if ( typeof actionOnSuccess == 'function' ) {
2016-01-17 06:24:48 +09:00
2015-11-20 05:22:59 +09:00
actionOnSuccess ( ) ;
// don't invoke actionOnSuccess later if we already invoked it here
actionOnSuccess = false ;
}
2015-09-22 05:46:15 +09:00
}
2015-11-20 05:22:59 +09:00
// (2) if we don't have a cached version we remove and hide the old stream and wait for the new one to load
else {
2015-09-18 08:42:52 +09:00
$ ( '.profile-card,.hover-card,.hover-card-caret' ) . remove ( ) ;
2015-11-20 05:22:59 +09:00
$ ( '#feed' ) . css ( 'opacity' , 0 ) ;
2015-09-29 23:31:05 +09:00
$ ( '#feed-body' ) . html ( '' ) ;
2015-11-23 08:24:33 +09:00
remove _spinner ( ) ; display _spinner ( ) ; // display spinner in page header instead feed header
2015-11-20 05:22:59 +09:00
}
2015-07-02 02:15:31 +09:00
2015-11-20 05:22:59 +09:00
// (3) change design immediately to either cached design or logged in user's
2015-11-23 08:24:33 +09:00
if ( typeof window . oldStreamsDesigns [ window . currentStreamObject . nickname ] != 'undefined' ) {
changeDesign ( window . oldStreamsDesigns [ window . currentStreamObject . nickname ] ) ;
2015-11-20 05:22:59 +09:00
}
else {
changeDesign ( { backgroundimage : window . loggedIn . background _image , backgroundcolor : window . loggedIn . backgroundcolor , linkcolor : window . loggedIn . linkcolor } ) ;
}
2015-06-07 18:48:41 +09:00
}
2015-07-02 02:15:31 +09:00
2015-06-03 06:45:46 +09:00
// get stream
2015-09-29 23:31:05 +09:00
getFromAPI ( streamObject . stream , function ( queet _data , userArray , error , url ) {
2015-07-02 02:15:31 +09:00
2015-09-18 20:42:19 +09:00
// while waiting for this data user might have changed stream, so only proceed if current stream still is this one
2015-11-23 08:24:33 +09:00
if ( window . currentStreamObject . stream != streamObject . stream ) {
2015-09-18 20:42:19 +09:00
console . log ( 'stream has changed, aborting' ) ;
return ;
}
2015-09-18 10:04:28 +09:00
2015-09-21 09:00:03 +09:00
// if we have a fallbackId and a userArray, and the userArray's id is not equal to
2015-09-18 20:42:19 +09:00
// the fallackId, this is the wrong stream! we need to re-invoke setNewCurrentStream()
// with the correct and up-to-date nickname (maybe best not to send a fallbackId here not
// to risk getting into an infinite loop caused by bad data)
// also, we do the same thing if getting the stream fails, but we have a fallback id
if ( ( userArray && fallbackId && userArray . id != fallbackId )
|| ( queet _data === false && fallbackId ) ) {
2015-09-21 09:00:03 +09:00
if ( streamObject . name == 'profile' ) {
getNicknameByUserIdFromAPI ( fallbackId , function ( nickname ) {
if ( nickname ) {
setNewCurrentStream ( pathToStreamRouter ( nickname ) , true , false , actionOnSuccess ) ;
}
else {
// redirect to front page if everything fails
setNewCurrentStream ( pathToStreamRouter ( '/' ) , true , false , actionOnSuccess ) ;
}
} ) ;
}
else if ( streamObject . name == 'group notice stream' ) {
getNicknameByGroupIdFromAPI ( fallbackId , function ( nickname ) {
if ( nickname ) {
setNewCurrentStream ( pathToStreamRouter ( 'group/' + nickname ) , true , false , actionOnSuccess ) ;
}
else {
// redirect to front page if everything fails
setNewCurrentStream ( pathToStreamRouter ( '/' ) , true , false , actionOnSuccess ) ;
}
} ) ;
}
2015-09-18 20:42:19 +09:00
}
2015-09-29 23:31:05 +09:00
// getting stream failed, and we don't have a fallback id
2015-09-18 20:42:19 +09:00
else if ( queet _data === false ) {
2015-09-29 23:31:05 +09:00
2015-11-20 05:22:59 +09:00
// e.g. maybe fade in user-container here, ("success" was a badly chosen name...)
2015-09-29 23:31:05 +09:00
if ( typeof actionOnSuccess == 'function' ) {
actionOnSuccess ( ) ;
}
if ( error . status == 401 ) {
showErrorMessage ( window . sL . ERRORmustBeLoggedIn ) ;
}
else if ( error . status == 404 ) {
if ( streamObject . name == 'profile'
|| streamObject . name == 'friends timeline'
|| streamObject . name == 'mentions'
|| streamObject . name == 'favorites'
|| streamObject . name == 'subscribers'
|| streamObject . name == 'subscriptions'
|| streamObject . name == 'user group list' ) {
showErrorMessage ( window . sL . ERRORcouldNotFindUserWithNickname . replace ( '{nickname}' , replaceHtmlSpecialChars ( streamObject . nickname ) ) ) ;
}
else if ( streamObject . name == 'group notice stream'
|| streamObject . name == 'group member list'
|| streamObject . name == 'group admin list' ) {
showErrorMessage ( window . sL . ERRORcouldNotFindGroupWithNickname . replace ( '{nickname}' , replaceHtmlSpecialChars ( streamObject . nickname ) ) ) ;
}
2015-11-30 04:19:23 +09:00
else if ( streamObject . name == 'list notice stream'
|| streamObject . name == 'list members'
|| streamObject . name == 'list subscribers' ) {
showErrorMessage ( window . sL . ERRORcouldNotFindList ) ;
}
2015-09-29 23:31:05 +09:00
else {
showErrorMessage ( window . sL . ERRORcouldNotFindPage + '<br><br>url: ' + url ) ;
}
}
2015-10-13 20:19:06 +09:00
else if ( error . status == 410 && streamObject . name == 'notice' ) {
showErrorMessage ( window . sL . ERRORnoticeRemoved ) ;
}
2015-11-23 08:24:33 +09:00
else if ( error . status == 0
|| ( error . status == 200 && error . responseText == 'An error occurred.' )
) {
2015-11-17 00:37:47 +09:00
showErrorMessage ( window . sL . ERRORnoContactWithServer + ' (' + replaceHtmlSpecialChars ( error . statusText ) + ')' ) ;
}
2015-09-29 23:31:05 +09:00
else {
showErrorMessage ( window . sL . ERRORsomethingWentWrong + ' < br > < br > \
url : ' + url + ' < br > < br > \
jQuery ajax ( ) error : < pre > < code > ' + replaceHtmlSpecialChars(JSON.stringify(error, null, ' ')) + ' < / c o d e > < / p r e > \
streamObject : < pre > < code > ' + replaceHtmlSpecialChars(JSON.stringify(streamObject, null, ' ')) + ' < / c o d e > < / p r e > \
' ) ;
}
2015-09-18 20:42:19 +09:00
}
2015-06-03 06:45:46 +09:00
2015-09-18 20:42:19 +09:00
// everything seems fine, show the new stream
else if ( queet _data ) {
2015-06-03 06:45:46 +09:00
2015-09-18 20:42:19 +09:00
// set location bar from stream
if ( setLocation ) {
setUrlFromStream ( streamObject ) ;
}
2015-06-03 06:45:46 +09:00
2015-09-18 20:42:19 +09:00
// profile card from user array
if ( userArray ) {
addProfileCardToDOM ( buildProfileCard ( userArray ) ) ;
}
2015-09-21 09:10:23 +09:00
// remove any trailing profile cards
else {
$ ( '.profile-card' ) . remove ( ) ;
}
2015-09-18 08:42:52 +09:00
2015-09-18 20:42:19 +09:00
// show group profile card if this is a group stream
if ( streamObject . name == 'group notice stream'
|| streamObject . name == 'group member list'
|| streamObject . name == 'group admin list' ) {
groupProfileCard ( streamObject . nickname ) ;
}
2015-11-18 06:48:28 +09:00
// say hello to the api if this is notifications stream, to
// get correct unread notifcation count
if ( window . currentStreamObject . name == 'notifications' ) {
helloAPI ( ) ;
}
2015-09-18 20:42:19 +09:00
// start checking for new queets again
window . clearInterval ( checkForNewQueetsInterval ) ;
checkForNewQueetsInterval = window . setInterval ( function ( ) { checkForNewQueets ( ) } , window . timeBetweenPolling ) ;
// add this stream to the history menu
addStreamToHistoryMenuAndMarkAsCurrent ( streamObject ) ;
remove _spinner ( ) ;
2015-11-23 08:24:33 +09:00
// some streams, e.g. /statuses/show/1234.json is not enclosed in an array, make sure it is
if ( ! $ . isArray ( queet _data ) ) {
queet _data = [ queet _data ] ;
}
// empty feed-body if this is a
// (1) notice page
// (2) if we got an empty result
// (3) it's not a stream of notices or notifications
if ( window . currentStreamObject . name == 'notice'
|| queet _data . length == 0
|| ( window . currentStreamObject . type != 'notices' && window . currentStreamObject . type != 'notifications' ) ) {
$ ( '#feed-body' ) . html ( '' ) ;
}
// if the last item in the stream doesn't exists in the feed-body, we can't
// just prepend the new items, since it will create a gap in the middle of the
// feed. in that case we just empty the body and start from scratch
else if ( $ ( '#feed-body' ) . children ( '.stream-item[data-quitter-id-in-stream="' + queet _data . slice ( - 1 ) [ 0 ] . id + '"]' ) . length == 0 ) {
$ ( '#feed-body' ) . html ( '' ) ;
}
// if the stream is slow to load, the user might have expanded a notice, or scrolled down
// and started reading. in that case we add the new items _hidden_
if ( $ ( '#feed-body' ) . children ( '.stream-item.expanded' ) . length > 0 || $ ( window ) . scrollTop ( ) > 50 ) {
addToFeed ( queet _data , false , 'hidden' ) ;
maybeShowTheNewQueetsBar ( ) ;
}
else {
addToFeed ( queet _data , false , 'visible' ) ;
}
2016-01-17 06:24:48 +09:00
// make sure page-container is visible
$ ( '#page-container' ) . css ( 'opacity' , '1' ) ;
2016-01-22 21:12:11 +09:00
$ ( '#feed' ) . css ( 'opacity' , '1' ) ;
2015-11-23 08:24:33 +09:00
2016-01-23 05:19:08 +09:00
// run callbacks for this stream
if ( streamObject . callbacks !== false ) {
$ . each ( streamObject . callbacks , function ( ) {
if ( typeof window [ this ] == 'function' ) {
window [ this ] ( ) ;
}
} ) ;
}
2015-11-17 01:13:26 +09:00
$ ( '.reload-stream' ) . show ( ) ;
2015-09-18 20:42:19 +09:00
$ ( 'body' ) . removeClass ( 'loading-older' ) ; $ ( 'body' ) . removeClass ( 'loading-newer' ) ;
// maybe do something
if ( typeof actionOnSuccess == 'function' ) {
actionOnSuccess ( ) ;
2013-08-19 22:30:57 +09:00
}
2015-06-03 06:45:46 +09:00
}
2015-07-02 02:15:31 +09:00
} ) ;
2013-08-19 22:30:57 +09:00
}
2015-07-02 02:15:31 +09:00
2015-09-18 08:42:52 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-09-18 08:42:52 +09:00
· Add this stream to history menu if it doesn 't exist in stream selection menus (if we' re logged in )
· and mark this stream as current
2015-07-02 02:15:31 +09:00
·
2015-09-18 08:42:52 +09:00
· @ param streamObject : stream object returned by pathToStreamRouter ( )
2015-07-02 02:15:31 +09:00
·
2015-05-19 22:22:52 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2015-05-19 22:22:52 +09:00
2015-09-18 08:42:52 +09:00
function addStreamToHistoryMenuAndMarkAsCurrent ( streamObject ) {
if ( streamObject . parentPath ) {
var urlToMarkAsCurrent = window . siteInstanceURL + streamObject . parentPath ;
}
else {
var urlToMarkAsCurrent = window . siteInstanceURL + streamObject . path ;
}
2015-05-19 22:22:52 +09:00
2015-09-18 08:42:52 +09:00
if ( $ ( '.stream-selection[href="' + urlToMarkAsCurrent + '"]' ) . length == 0
2015-07-02 02:15:31 +09:00
&& typeof window . loggedIn . screen _name != 'undefined' ) {
2015-09-18 08:42:52 +09:00
$ ( '#history-container' ) . prepend ( '<a class="stream-selection" href="' + urlToMarkAsCurrent + '">' + streamObject . streamHeader + '<i class="chev-right" data-tooltip="' + window . sL . tooltipBookmarkStream + '"></i></a>' ) ;
2015-05-19 22:22:52 +09:00
updateHistoryLocalStorage ( ) ;
2015-09-01 06:53:09 +09:00
// max 10 in history container
var historyNum = $ ( '#history-container' ) . children ( '.stream-selection' ) . length ;
if ( historyNum > 10 ) {
$ ( '#history-container' ) . children ( '.stream-selection' ) . slice ( - ( historyNum - 10 ) ) . remove ( ) ;
}
2015-05-19 22:22:52 +09:00
}
2015-07-02 02:15:31 +09:00
2015-05-19 22:22:52 +09:00
$ ( '.stream-selection' ) . removeClass ( 'current' ) ;
2015-09-18 08:42:52 +09:00
$ ( '.stream-selection[href="' + urlToMarkAsCurrent + '"]' ) . addClass ( 'current' ) ;
2015-05-19 22:22:52 +09:00
}
2013-08-19 22:30:57 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Expand / de - expand queet
2015-07-02 02:15:31 +09:00
·
2013-08-19 22:30:57 +09:00
· @ param q : the stream item to expand
2013-11-23 08:31:04 +09:00
· @ param doScrolling : if we should scroll back to position or not
2015-07-02 02:15:31 +09:00
·
2013-08-19 22:30:57 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2013-11-23 08:31:04 +09:00
function expand _queet ( q , doScrolling ) {
2015-07-02 02:15:31 +09:00
// don't do anything if this is a queet being posted
2014-11-24 21:47:45 +09:00
if ( q . hasClass ( 'temp-post' ) ) {
return ;
2015-07-02 02:15:31 +09:00
}
2014-11-24 21:47:45 +09:00
// don't expand if this is a remote profile popup
2015-06-05 16:13:36 +09:00
if ( q . closest ( '#popup-external-profile, #popup-local-profile' ) . length > 0 ) {
2014-11-24 21:47:45 +09:00
return ;
}
2015-07-02 02:15:31 +09:00
2013-11-23 08:31:04 +09:00
doScrolling = typeof doScrolling !== 'undefined' ? doScrolling : true ;
2013-08-19 22:30:57 +09:00
var qid = q . attr ( 'data-quitter-id' ) ;
2015-07-02 02:15:31 +09:00
2013-08-19 22:30:57 +09:00
// de-expand if expanded
if ( q . hasClass ( 'expanded' ) && ! q . hasClass ( 'collapsing' ) ) {
2013-11-23 08:31:04 +09:00
var sel = getSelection ( ) . toString ( ) ;
2015-07-02 02:15:31 +09:00
if ( ! sel
2014-10-18 00:08:14 +09:00
&& ! q . find ( '.queet-button' ) . children ( 'button' ) . hasClass ( 'enabled' )
&& ! q . find ( '.queet-button' ) . children ( 'button' ) . hasClass ( 'too-long' ) ) { // don't collapse if text is selected, or if queet has an active queet button, or if queet text is too long
2015-07-02 02:15:31 +09:00
2014-09-25 06:20:35 +09:00
// remove some things right away
q . find ( '.inline-reply-caret' ) . remove ( ) ;
2015-07-02 02:15:31 +09:00
2015-06-17 07:44:18 +09:00
// "unplay" gif image on collapse if there's only one attachment (switch to thumb)
var gifToUnPlay = q . children ( '.queet' ) . find ( '.queet-thumbs.thumb-num-1' ) . children ( '.thumb-container.play-button' ) . children ( '.attachment-thumb[data-mime-type="image/gif"]' ) ;
if ( gifToUnPlay . length > 0 ) {
gifToUnPlay . attr ( 'src' , gifToUnPlay . attr ( 'data-thumb-url' ) ) ;
gifToUnPlay . parent ( '.thumb-container' ) . css ( 'background-image' , 'url(\'' + gifToUnPlay . attr ( 'data-thumb-url' ) + '\')' ) ;
}
2015-07-02 02:15:31 +09:00
2015-06-17 07:44:18 +09:00
// show thumbs (if hidden) and remove any iframe video immediately
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . find ( '.queet-thumbs' ) . removeClass ( 'hide-thumbs' ) ;
2015-06-17 07:44:18 +09:00
q . children ( '.queet' ) . find ( 'iframe' ) . remove ( ) ;
2015-07-02 02:15:31 +09:00
2013-11-23 08:31:04 +09:00
q . addClass ( 'collapsing' ) ;
if ( q . hasClass ( 'conversation' ) ) {
2015-07-02 02:15:31 +09:00
q . removeClass ( 'expanded' ) ;
q . removeClass ( 'collapsing' ) ;
2013-11-23 08:31:04 +09:00
q . find ( '.view-more-container-top' ) . remove ( ) ;
q . find ( '.view-more-container-bottom' ) . remove ( ) ;
q . find ( '.stream-item.conversation' ) . remove ( ) ;
q . find ( '.inline-reply-queetbox' ) . remove ( ) ;
2015-07-02 02:15:31 +09:00
q . find ( '.expanded-content' ) . remove ( ) ;
2013-11-23 08:31:04 +09:00
}
else {
2015-07-02 02:15:31 +09:00
rememberMyScrollPos ( q . children ( '.queet' ) , qid , 0 ) ;
2014-01-29 03:42:47 +09:00
// give stream item a height
q . css ( 'height' , q . height ( ) + 'px' ) ;
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . find ( '.expanded-content' ) . css ( 'height' , q . find ( '.expanded-content' ) . height ( ) + 'px' ) ;
2015-06-10 06:42:41 +09:00
q . children ( '.queet' ) . find ( '.queet-thumbs.thumb-num-1' ) . css ( 'max-height' , q . find ( '.queet-thumbs.thumb-num-1' ) . height ( ) + 'px' ) ;
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . find ( '.queet-thumbs.thumb-num-1 .thumb-container' ) . css ( 'max-height' , q . find ( '.queet-thumbs.thumb-num-1' ) . height ( ) + 'px' ) ;
2014-01-29 03:42:47 +09:00
q . children ( 'div' ) . not ( '.queet' ) . children ( 'a' ) . css ( 'opacity' , '0.5' ) ;
q . children ( 'div' ) . not ( '.queet' ) . children ( ) . children ( ) . css ( 'opacity' , '0.5' ) ;
2014-11-24 21:47:45 +09:00
var collapseTime = 100 + q . find ( '.stream-item.conversation:not(.hidden-conversation)' ) . length * 50 ;
2015-07-02 02:15:31 +09:00
2014-01-29 03:42:47 +09:00
// set transition time (needs to be delayed, otherwise webkit animates the height-setting above)
2015-07-02 02:15:31 +09:00
setTimeout ( function ( ) {
2014-01-29 03:42:47 +09:00
q . children ( '.queet' ) . css ( '-moz-transition-duration' , Math . round ( collapseTime / 1000 * 10 ) / 10 + 's' ) ;
q . children ( '.queet' ) . css ( '-o-transition-duration' , Math . round ( collapseTime / 1000 * 10 ) / 10 + 's' ) ;
q . children ( '.queet' ) . css ( '-webkit-transition-duration' , Math . round ( collapseTime * 1000 * 10 ) / 10 + 's' ) ;
q . children ( '.queet' ) . css ( 'transition-duration' , Math . round ( collapseTime / 1000 * 10 ) / 10 + 's' ) ;
2015-06-10 06:42:41 +09:00
q . children ( '.queet' ) . find ( '.expanded-content, .queet-thumbs.thumb-num-1, .queet-thumbs.thumb-num-1 .thumb-container' ) . css ( '-moz-transition-duration' , Math . round ( collapseTime / 1000 * 10 ) / 10 + 's' ) ;
q . children ( '.queet' ) . find ( '.expanded-content, .queet-thumbs.thumb-num-1, .queet-thumbs.thumb-num-1 .thumb-container' ) . css ( '-o-transition-duration' , Math . round ( collapseTime / 1000 * 10 ) / 10 + 's' ) ;
q . children ( '.queet' ) . find ( '.expanded-content, .queet-thumbs.thumb-num-1, .queet-thumbs.thumb-num-1 .thumb-container' ) . css ( '-webkit-transition-duration' , Math . round ( collapseTime * 1000 * 10 ) / 10 + 's' ) ;
q . children ( '.queet' ) . find ( '.expanded-content, .queet-thumbs.thumb-num-1, .queet-thumbs.thumb-num-1 .thumb-container' ) . css ( 'transition-duration' , Math . round ( collapseTime / 1000 * 10 ) / 10 + 's' ) ;
2014-01-29 03:42:47 +09:00
q . css ( '-moz-transition-duration' , Math . round ( collapseTime / 1000 * 10 ) / 10 + 's' ) ;
q . css ( '-o-transition-duration' , Math . round ( collapseTime / 1000 * 10 ) / 10 + 's' ) ;
q . css ( '-webkit-transition-duration' , Math . round ( collapseTime * 1000 * 10 ) / 10 + 's' ) ;
q . css ( 'transition-duration' , Math . round ( collapseTime / 1000 * 10 ) / 10 + 's' ) ;
// set new heights and margins to animate to
2015-06-10 06:42:41 +09:00
var animateToHeight = q . children ( '.queet' ) . outerHeight ( ) - q . find ( '.inline-reply-queetbox' ) . outerHeight ( ) - q . children ( '.queet' ) . find ( '.expanded-content' ) . outerHeight ( ) - Math . max ( 0 , q . children ( '.queet' ) . find ( '.queet-thumbs.thumb-num-1' ) . outerHeight ( ) - 250 ) - 2 ;
2014-11-24 21:47:45 +09:00
if ( animateToHeight < 73 ) { // no less than this
animateToHeight = 73 ;
}
q . css ( 'height' , animateToHeight + 'px' ) ;
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . css ( 'margin-top' , '-' + ( q . children ( '.queet' ) . offset ( ) . top - q . offset ( ) . top ) + 'px' ) ;
2015-06-10 06:42:41 +09:00
q . children ( '.queet' ) . find ( '.expanded-content' ) . css ( 'height' , '0' ) ;
q . children ( '.queet' ) . find ( '.queet-thumbs.thumb-num-1, .queet-thumbs.thumb-num-1 .thumb-container' ) . css ( 'max-height' , '250px' ) ;
2015-07-02 02:15:31 +09:00
2015-06-10 06:42:41 +09:00
if ( doScrolling ) {
setTimeout ( function ( ) {
backToMyScrollPos ( q , qid , 500 , function ( ) {
cleanUpAfterCollapseQueet ( q ) ;
} ) ;
} , collapseTime ) ;
}
else {
setTimeout ( function ( ) {
2015-07-02 02:15:31 +09:00
cleanUpAfterCollapseQueet ( q ) ;
} , collapseTime ) ;
2015-06-10 06:42:41 +09:00
}
2015-07-02 02:15:31 +09:00
} , 50 ) ;
}
2013-11-23 08:31:04 +09:00
}
2013-08-19 22:30:57 +09:00
}
2014-09-25 06:20:35 +09:00
else if ( ! q . hasClass ( 'collapsing' ) ) {
2015-07-02 02:15:31 +09:00
2014-09-25 06:20:35 +09:00
// not for acitivity or notifications
if ( ! q . hasClass ( 'activity' ) && ! q . hasClass ( 'repeat' ) && ! q . hasClass ( 'like' ) && ! q . hasClass ( 'follow' ) ) {
2015-07-02 02:15:31 +09:00
2014-11-24 21:47:45 +09:00
q . addClass ( 'expanded' ) ;
q . prev ( ) . addClass ( 'next-expanded' ) ;
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
// get full html, if shortened
getFullUnshortenedHtmlForQueet ( q ) ;
2015-07-02 02:15:31 +09:00
2013-08-19 22:30:57 +09:00
// add expanded container
var longdate = parseTwitterLongDate ( q . find ( '.created-at' ) . attr ( 'data-created-at' ) ) ;
var qurl = q . find ( '.created-at' ) . find ( 'a' ) . attr ( 'href' ) ;
2015-07-02 02:15:31 +09:00
2013-11-23 08:31:04 +09:00
var metadata = '<span class="longdate" title="' + longdate + '">' + longdate + ' · ' + unescape ( q . attr ( 'data-source' ) ) + '</span> · <a href="' + qurl + '" class="permalink-link">' + window . sL . details + '</a>' ;
2015-07-02 02:15:31 +09:00
2013-08-19 22:30:57 +09:00
// show expanded content
2015-07-02 02:15:31 +09:00
q . find ( '.stream-item-footer' ) . before ( '<div class="expanded-content"><div class="queet-stats-container"></div><div class="client-and-actions"><span class="metadata">' + metadata + '</span></div></div>' ) ;
2015-06-17 07:44:18 +09:00
// "play" gif image on expand if there's only one attachment (switch to full gif from thumb)
var gifToPlay = q . children ( '.queet' ) . find ( '.queet-thumbs.thumb-num-1' ) . children ( '.thumb-container.play-button' ) . children ( '.attachment-thumb[data-mime-type="image/gif"]' ) ;
if ( gifToPlay . length > 0 ) {
gifToPlay . attr ( 'src' , gifToPlay . attr ( 'data-full-image-url' ) ) ;
gifToPlay . parent ( '.thumb-container' ) . css ( 'background-image' , 'url(\'' + gifToPlay . attr ( 'data-full-image-url' ) + '\')' ) ;
2015-02-25 04:44:48 +09:00
}
2015-06-17 07:44:18 +09:00
// if there's only one thumb and it's a youtube video, show it inline
if ( q . children ( '.queet' ) . find ( '.queet-thumbs.thumb-num-1' ) . children ( '.thumb-container.play-button.youtube' ) . length == 1 ) {
2015-11-17 01:40:04 +09:00
var youtubeURL = q . children ( '.queet' ) . find ( '.queet-thumbs.thumb-num-1' ) . children ( '.thumb-container.play-button.youtube' ) . children ( '.attachment-thumb' ) . attr ( 'data-full-image-url' ) ;
if ( q . children ( '.queet' ) . find ( '.expanded-content' ) . children ( '.media' ) . children ( 'iframe[src="' + youTubeEmbedLinkFromURL ( youtubeURL ) + '"]' ) . length < 1 ) { // not if already showed
2016-01-24 02:10:24 +09:00
q . children ( '.queet' ) . find ( '.queet-thumbs' ) . addClass ( 'hide-thumbs' ) ;
2015-06-17 07:44:18 +09:00
// show video
2015-11-17 01:40:04 +09:00
q . children ( '.queet' ) . find ( '.expanded-content' ) . prepend ( '<div class="media"><iframe width="510" height="315" src="' + youTubeEmbedLinkFromURL ( youtubeURL ) + '" frameborder="0" allowfullscreen></iframe></div>' ) ;
2015-07-02 02:15:31 +09:00
}
2015-06-17 07:44:18 +09:00
}
2015-07-02 02:15:31 +09:00
2015-06-17 07:44:18 +09:00
// show certain attachments in expanded content
2016-01-22 00:15:56 +09:00
if ( q . children ( 'script.attachment-json' ) . length > 0
&& q . children ( 'script.attachment-json' ) . text ( ) != 'undefined' ) {
2015-11-26 03:51:51 +09:00
try {
2016-01-22 00:15:56 +09:00
var attachmentsParsed = JSON . parse ( q . children ( 'script.attachment-json' ) . text ( ) ) ;
2015-11-26 03:51:51 +09:00
}
catch ( e ) {
var attachmentsParsed = false ;
2016-01-22 00:15:56 +09:00
console . log ( 'could not parse attachment json when expanding the notice: ' + e ) ;
console . log ( "attachment-json: " + q . children ( 'script.attachment-json' ) . text ( ) ) ;
2015-11-26 03:51:51 +09:00
}
2016-01-22 00:15:56 +09:00
2015-11-26 03:51:51 +09:00
if ( attachmentsParsed !== false ) {
$ . each ( attachmentsParsed , function ( ) {
2015-07-02 02:15:31 +09:00
2015-11-26 03:51:51 +09:00
var attachment _mimetype = this . mimetype ;
var attachment _title = this . url ;
2015-07-02 02:15:31 +09:00
2015-11-26 03:51:51 +09:00
// filename extension
var attachment _title _extension = attachment _title . substr ( ( ~ - attachment _title . lastIndexOf ( "." ) >>> 0 ) + 2 ) ;
2014-05-12 07:23:57 +09:00
2015-11-26 03:51:51 +09:00
// attachments in the content link to /attachment/etc url and not direct to image/video, link is in title
if ( typeof attachment _title != 'undefined' ) {
2015-07-02 02:15:31 +09:00
2015-11-26 03:51:51 +09:00
// hack to make remote webm-movies load
if ( attachment _title _extension == 'webm' ) {
attachment _mimetype = 'video/webm' ;
}
2015-02-25 10:11:32 +09:00
2015-11-26 03:51:51 +09:00
// videos
if ( $ . inArray ( attachment _mimetype , [ 'video/mp4' , 'video/ogg' , 'video/quicktime' , 'video/webm' ] ) >= 0 ) {
if ( q . children ( '.queet' ) . find ( '.expanded-content' ) . children ( '.media' ) . children ( 'video' ) . children ( 'source[href="' + attachment _title + '"]' ) . length < 1 ) { // not if already showed
// local attachment with a thumbnail
var attachment _poster = '' ;
if ( typeof this . thumb _url != 'undefined' ) {
attachment _poster = ' poster="' + this . thumb _url + '"' ;
}
if ( q . children ( '.queet' ) . find ( '.expanded-content' ) . children ( '.media' ) . length > 0 ) {
q . children ( '.queet' ) . find ( '.media' ) . last ( ) . after ( '<div class="media"><video class="u-video" controls="controls"' + attachment _poster + '><source type="' + attachment _mimetype + '" src="' + attachment _title + '" /></video></div>' ) ;
}
else {
q . children ( '.queet' ) . find ( '.expanded-content' ) . prepend ( '<div class="media"><video class="u-video" controls="controls"' + attachment _poster + '><source type="' + attachment _mimetype + '" src="' + attachment _title + '" /></video></div>' ) ;
}
}
2015-06-17 09:16:03 +09:00
}
2015-11-26 03:51:51 +09:00
else {
// other plugins, e.g. gotabulo, can check for other attachment file formats to expand
window . currentlyExpanding = {
"attachment_title" : attachment _title ,
"attachment_mimetype" : attachment _mimetype ,
"attachment_title_extension" : attachment _title _extension ,
"streamItem" : q ,
"thisAttachmentLink" : $ ( this )
} ;
$ ( document ) . trigger ( 'qvitterExpandOtherAttachments' ) ;
}
2015-06-17 09:16:03 +09:00
}
2015-11-26 03:51:51 +09:00
} ) ;
}
2015-06-17 09:16:03 +09:00
}
2013-08-19 22:30:57 +09:00
2015-02-26 05:16:24 +09:00
// get and show favs and repeats
2015-07-02 02:15:31 +09:00
getFavsAndRequeetsForQueet ( q , qid ) ;
2013-08-19 22:30:57 +09:00
// show conversation and reply form (but not if already in conversation)
if ( ! q . hasClass ( 'conversation' ) ) {
2015-07-02 02:15:31 +09:00
2015-11-20 05:22:59 +09:00
// show conversation
getConversation ( q , qid ) ;
2015-07-02 02:15:31 +09:00
2013-09-03 01:13:15 +09:00
// show inline reply form if logged in
2015-07-02 02:15:31 +09:00
if ( typeof window . loggedIn . screen _name != 'undefined' ) {
q . children ( '.queet' ) . append ( replyFormHtml ( q , qid ) ) ;
2015-11-23 08:24:33 +09:00
maybePrefillQueetBoxWithCachedText ( q . children ( '.queet' ) . find ( '.queet-box' ) ) ;
2015-07-02 23:57:51 +09:00
}
2015-07-02 02:15:31 +09:00
}
2013-08-19 22:30:57 +09:00
}
}
2015-07-02 02:15:31 +09:00
}
2016-01-10 05:01:34 +09:00
function cleanUpAfterCollapseQueet ( streamItem ) {
var queet = streamItem . children ( '.queet' ) ;
streamItem . css ( 'height' , 'auto' ) ;
2016-01-10 05:27:02 +09:00
queet . css ( 'margin-top' , '0' ) ;
2016-01-10 05:01:34 +09:00
streamItem . removeClass ( 'expanded' ) ;
streamItem . prev ( ) . removeClass ( 'next-expanded' ) ;
streamItem . removeClass ( 'collapsing' ) ;
streamItem . find ( '.expanded-content' ) . remove ( ) ;
streamItem . find ( '.view-more-container-top' ) . remove ( ) ;
streamItem . find ( '.view-more-container-bottom' ) . remove ( ) ;
streamItem . find ( '.inline-reply-queetbox' ) . remove ( ) ;
streamItem . find ( '.stream-item.conversation' ) . remove ( ) ;
streamItem . find ( '.show-full-conversation' ) . remove ( ) ;
streamItem . removeAttr ( 'style' ) ;
queet . removeAttr ( 'style' ) ;
queet . find ( '.queet-thumbs.thumb-num-1' ) . removeAttr ( 'style' ) ;
queet . find ( '.queet-thumbs.thumb-num-1 .thumb-container' ) . css ( 'max-height' , '' ) ;
2014-11-24 21:47:45 +09:00
}
2014-05-28 03:40:51 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-11-23 08:24:33 +09:00
· Get a popup queet box
2015-07-02 02:15:31 +09:00
·
2014-05-28 03:40:51 +09:00
· @ return the html for the queet box
·
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
function queetBoxPopUpHtml ( ) {
// if we have cached text in localstorage
var data = localStorageObjectCache _GET ( 'queetBoxInput' , 'pop-up-queet-box' ) ;
if ( data ) {
var cachedText = encodeURIComponent ( data ) ;
}
2014-05-28 03:40:51 +09:00
var startText = encodeURIComponent ( window . sL . compose ) ;
2015-11-23 08:24:33 +09:00
return '<div class="inline-reply-queetbox"><div id="pop-up-queet-box" class="queet-box queet-box-syntax" data-start-text="' + startText + '" data-cached-text="' + cachedText + '">' + decodeURIComponent ( startText ) + '</div><div class="syntax-middle"></div><div class="syntax-two" contenteditable="true"></div><div class="mentions-suggestions"></div><div class="queet-toolbar toolbar-reply"><div class="queet-box-extras"><button data-tooltip="' + window . sL . tooltipAttachImage + '" class="upload-image"></button><button data-tooltip="' + window . sL . tooltipShortenUrls + '" class="shorten disabled">URL</button></div><div class="queet-button"><span class="queet-counter"></span><button>' + window . sL . queetVerb + '</button></div></div></div>' ;
2014-05-28 03:40:51 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Get a reply form
2015-07-02 02:15:31 +09:00
·
2013-08-19 22:30:57 +09:00
· @ param q : the stream item to open reply form in
· @ param qid : queet id
· @ return the html for the reply form
·
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2016-01-19 03:30:29 +09:00
function replyFormHtml ( streamItem , qid ) {
var q = streamItem . children ( '.queet' ) ;
2015-07-02 23:57:51 +09:00
// if we have cached text in localstorage
var data = localStorageObjectCache _GET ( 'queetBoxInput' , 'queet-box-' + qid ) ;
if ( data ) {
var cachedText = encodeURIComponent ( data ) ;
}
2016-01-19 03:30:29 +09:00
var screenNamesToAdd = { } ;
// add the screen name to the one we're replying to (if it's not me)
if ( ! thisIsALinkToMyProfile ( q . find ( '.account-group' ) . attr ( 'href' ) ) ) {
var replyToScreenName = q . find ( '.account-group span.screen-name' ) . html ( ) . replace ( '@' , '' ) ;
screenNamesToAdd [ q . find ( '.account-group' ) . attr ( 'href' ) ] = replyToScreenName ;
2013-08-19 22:30:57 +09:00
}
2016-01-19 03:30:29 +09:00
// add the screen name to the one who the one we're replying to is replying to (if it's not me)
if ( q . find ( 'i.addressees > span.reply-to' ) . length > 0
&& ! thisIsALinkToMyProfile ( q . find ( 'i.addressees > span.reply-to > a' ) . attr ( 'href' ) ) ) {
var replyToScreenName = q . find ( 'i.addressees > span.reply-to > a' ) . html ( ) . replace ( '@' , '' ) ;
if ( typeof screenNamesToAdd [ q . find ( 'i.addressees > span.reply-to > a' ) . attr ( 'href' ) ] == 'undefined' ) {
screenNamesToAdd [ q . find ( 'i.addressees > span.reply-to > a' ) . attr ( 'href' ) ] = replyToScreenName ;
}
}
// get all other mentions (if it's not me)
$ . each ( q . find ( '.queet-text' ) . find ( '.mention' ) , function ( key , obj ) {
if ( ! thisIsALinkToMyProfile ( $ ( obj ) . attr ( 'href' ) ) ) {
if ( typeof screenNamesToAdd [ $ ( obj ) . attr ( 'href' ) ] == 'undefined' ) {
var thisMention = $ ( obj ) . html ( ) . replace ( '@' , '' ) ;
screenNamesToAdd [ $ ( obj ) . attr ( 'href' ) ] = thisMention ;
}
2013-08-19 22:30:57 +09:00
}
} ) ;
2015-07-02 02:15:31 +09:00
2016-01-19 03:30:29 +09:00
// build reply/rant strings
var repliesText = '' ;
if ( Object . keys ( screenNamesToAdd ) . length < 1
&& q . find ( 'strong.name' ) . attr ( 'data-user-id' ) == window . loggedIn . id ) {
if ( streamItem . attr ( 'data-in-reply-to-status-id' ) == 'null' || streamItem . attr ( 'data-in-reply-to-status-id' ) == 'false' || streamItem . attr ( 'data-in-reply-to-status-id' ) == 'undefined' || streamItem . attr ( 'data-in-reply-to-status-id' ) == '' ) {
var startText = window . sL . startRant + ' ' ;
}
else {
var startText = window . sL . continueRant + ' ' ;
}
}
else {
var startText = window . sL . replyTo + ' ' ;
var repliesArray = [ ] ;
$ . each ( screenNamesToAdd , function ( url , screenName ) {
repliesArray . push ( screenName ) ;
} ) ;
if ( repliesArray . length > 0 ) {
startText = '<a>@' + repliesArray . join ( '</a> <a>@' ) + '</a> <br>' ;
repliesText = '@' + repliesArray . join ( ' @' ) + ' ' ;
}
}
2015-07-02 23:57:51 +09:00
startText = encodeURIComponent ( startText ) ;
repliesText = encodeURIComponent ( repliesText ) ;
2015-09-01 06:53:09 +09:00
return '<div class="inline-reply-queetbox"><span class="inline-reply-caret"><span class="caret-inner"></span></span><img class="reply-avatar" src="' + $ ( '#user-avatar' ) . attr ( 'src' ) + '" /><div class="queet-box queet-box-syntax" id="queet-box-' + qid + '" data-start-text="' + startText + '" data-replies-text="' + repliesText + '" data-cached-text="' + cachedText + '">' + decodeURIComponent ( startText ) + '</div><div class="syntax-middle"></div><div class="syntax-two" contenteditable="true"></div><div class="mentions-suggestions"></div><div class="queet-toolbar toolbar-reply"><div class="queet-box-extras"><button data-tooltip="' + window . sL . tooltipAttachImage + '" class="upload-image"></button><button data-tooltip="' + window . sL . tooltipShortenUrls + '" class="shorten disabled">URL</button></div><div class="queet-button"><span class="queet-counter"></span><button>' + window . sL . queetVerb + '</button></div></div></div>' ;
2013-08-19 22:30:57 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Popup for replies , deletes , etc
2015-07-02 02:15:31 +09:00
·
2013-08-19 22:30:57 +09:00
· @ param popupId : popups id
· @ param heading : popops header
2015-07-02 02:15:31 +09:00
· @ param bodyHtml : popups body html
· @ param footerHtml : popups footer html
·
· · · · · · · · · · · · · * /
2013-08-19 22:30:57 +09:00
2015-06-10 06:42:41 +09:00
function popUpAction ( popupId , heading , bodyHtml , footerHtml , popUpWidth ) {
2014-11-24 21:47:45 +09:00
$ ( '.modal-container' ) . remove ( ) ; // remove any open popups
2013-08-19 22:30:57 +09:00
var allFooterHtml = '' ;
if ( footerHtml ) {
allFooterHtml = '<div class="modal-footer">' + footerHtml + '</div>' ;
}
2015-07-02 02:15:31 +09:00
$ ( 'body' ) . prepend ( '<div id="' + popupId + '" class="modal-container"><div class="modal-draggable"><div class="modal-content"><button class="modal-close" type="button"><span class="icon"></span></button><div class="modal-header"><h3 class="modal-title">' + heading + '</h3></div><div class="modal-body">' + bodyHtml + '</div>' + allFooterHtml + '</div></div></div>' ) ;
2015-06-10 06:42:41 +09:00
var thisPopUp = $ ( '#' + popupId ) . children ( '.modal-draggable' ) ;
if ( typeof popUpWidth != 'undefined' ) {
thisPopUp . width ( popUpWidth ) ;
}
centerPopUp ( thisPopUp ) ;
2015-07-02 02:15:31 +09:00
}
2015-06-10 06:42:41 +09:00
function centerPopUp ( thisPopUp ) {
thisPopUp . css ( 'margin-top' , '' ) ;
2015-07-02 02:15:31 +09:00
thisPopUp . css ( 'margin-left' , '' ) ;
2015-06-10 06:42:41 +09:00
var this _modal _height = thisPopUp . height ( ) ;
var this _modal _width = thisPopUp . width ( ) ;
var popupPos = thisPopUp . offset ( ) . top - $ ( window ) . scrollTop ( ) ;
2013-08-19 22:30:57 +09:00
if ( ( popupPos - ( this _modal _height / 2 ) ) < 5 ) {
var marginTop = 5 - popupPos ;
}
else {
var marginTop = 0 - this _modal _height / 2 ;
}
2015-06-10 06:42:41 +09:00
thisPopUp . css ( 'margin-top' , marginTop + 'px' ) ;
2015-07-02 02:15:31 +09:00
thisPopUp . css ( 'margin-left' , '-' + ( this _modal _width / 2 ) + 'px' ) ;
2015-06-10 06:42:41 +09:00
thisPopUp . draggable ( { handle : ".modal-header" } ) ;
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-02-26 05:16:24 +09:00
· Get and show conversation
2013-08-19 22:30:57 +09:00
·
2015-07-02 02:15:31 +09:00
· This function has grown into a monster , needs fixing
·
· · · · · · · · · · · · · * /
2013-08-19 22:30:57 +09:00
2015-02-26 05:16:24 +09:00
function getConversation ( q , qid ) {
2015-01-27 09:57:08 +09:00
2015-02-26 05:16:24 +09:00
// check if we have a conversation for this notice cached in localstorage
2015-07-02 23:57:51 +09:00
var cacheData = localStorageObjectCache _GET ( 'conversation' , q . attr ( 'data-conversation-id' ) ) ;
if ( cacheData ) {
2015-11-20 05:22:59 +09:00
showConversation ( q , qid , cacheData , 8 ) ;
2015-07-02 23:57:51 +09:00
}
2015-02-26 05:16:24 +09:00
// always get most recent conversation from server
2015-07-02 02:15:31 +09:00
getFromAPI ( 'statusnet/conversation/' + q . attr ( 'data-conversation-id' ) + '.json?count=100' , function ( data ) { if ( data ) {
2014-11-24 21:47:45 +09:00
2015-02-26 05:16:24 +09:00
// cache in localstorage
2015-03-06 07:35:08 +09:00
localStorageObjectCache _STORE ( 'conversation' , q . attr ( 'data-conversation-id' ) , data ) ;
2013-11-23 08:31:04 +09:00
2015-11-20 05:22:59 +09:00
showConversation ( q , qid , data , 0 ) ;
2015-02-26 05:16:24 +09:00
} } ) ;
}
2015-07-02 02:15:31 +09:00
2015-11-20 05:22:59 +09:00
function showConversation ( q , qid , data , offsetScroll ) {
2015-07-02 02:15:31 +09:00
if ( data && ! q . hasClass ( 'collapsing' ) ) {
2014-05-12 20:20:26 +09:00
2015-02-26 05:16:24 +09:00
if ( data . length > 1 ) {
var before _or _after = 'before' ;
$ . each ( data . reverse ( ) , function ( key , obj ) {
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
// switch to append after original queet
2015-07-02 02:15:31 +09:00
if ( obj . id == qid ) {
2015-02-26 05:16:24 +09:00
before _or _after = 'after' ;
}
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
// don't add clicked queet to DOM, but all else
// note: first we add the full conversation, but hidden
if ( obj . id != qid ) {
2015-07-02 02:15:31 +09:00
var queetTime = parseTwitterDate ( obj . created _at ) ;
2015-11-23 08:24:33 +09:00
var queetHtml = buildQueetHtml ( obj , obj . id , 'hidden-conversation' , false , true ) ;
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
if ( q . hasClass ( 'expanded' ) ) { // add queet to conversation only if still expanded
2015-07-02 02:15:31 +09:00
2015-11-23 20:44:36 +09:00
// don't add if already exist in conversation
2015-11-23 08:24:33 +09:00
if ( q . children ( '.stream-item.conversation[data-quitter-id="' + obj . id + '"]' ) . length > 0 ) {
2015-11-23 20:44:36 +09:00
// the data in the existing notice is updated automatically in searchForUpdatedNoticeData invoked from getFromAPI
2014-11-24 21:47:45 +09:00
}
2015-02-26 05:16:24 +09:00
else if ( before _or _after == 'before' ) {
q . children ( '.queet' ) . before ( queetHtml ) ;
2013-08-19 22:30:57 +09:00
}
else {
2015-02-26 05:16:24 +09:00
if ( q . children ( '.queet' ) . nextAll ( '.conversation' ) . length < 1 ) {
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . after ( queetHtml ) ;
2013-08-19 22:30:57 +09:00
}
else {
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . nextAll ( '.conversation' ) . last ( ) . after ( queetHtml ) ;
2013-08-19 22:30:57 +09:00
}
}
2015-07-02 02:15:31 +09:00
2013-08-19 22:30:57 +09:00
}
}
2015-07-02 02:15:31 +09:00
convertAttachmentMoreHref ( ) ;
2015-02-26 05:16:24 +09:00
} ) ;
2013-08-19 22:30:57 +09:00
}
else {
remove _spinner ( ) ;
2015-02-26 05:16:24 +09:00
}
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
// loop trough this stream items conversation and show the "strict" line of replies
2015-11-20 05:22:59 +09:00
rememberMyScrollPos ( q . children ( '.queet' ) , qid , offsetScroll ) ;
2015-02-26 05:16:24 +09:00
findInReplyToStatusAndShow ( q , qid , q . attr ( 'data-in-reply-to-status-id' ) , true , false ) ;
backToMyScrollPos ( q . children ( '.queet' ) , qid , false ) ;
findAndMarkLastVisibleInConversation ( q ) ;
}
else {
remove _spinner ( ) ;
2015-07-02 02:15:31 +09:00
}
2013-08-19 22:30:57 +09:00
}
2014-11-26 08:58:56 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2014-11-26 08:58:56 +09:00
· Add last visible class , since that ' s not possible to select in pure css
·
2015-07-02 02:15:31 +09:00
· · · · · · · · · · · · · * /
2014-11-26 08:58:56 +09:00
function findAndMarkLastVisibleInConversation ( streamItem ) {
streamItem . children ( ) . removeClass ( 'last-visible' ) ;
2015-07-02 02:15:31 +09:00
streamItem . children ( ) . removeClass ( 'first-visible-after-parent' ) ;
2014-11-26 09:51:07 +09:00
streamItem . children ( ) . not ( '.hidden-conversation' ) . last ( ) . addClass ( 'last-visible' ) ;
2015-07-02 02:15:31 +09:00
streamItem . children ( '.queet' ) . nextAll ( ) . not ( '.hidden-conversation' ) . first ( ) . addClass ( 'first-visible-after-parent' ) ;
2014-11-26 08:58:56 +09:00
}
2013-08-19 22:30:57 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-11-23 08:24:33 +09:00
· Recursive walker functions to view only reyplies to replies , not full conversation
2013-08-19 22:30:57 +09:00
·
2015-07-02 02:15:31 +09:00
· · · · · · · · · · · · · * /
2015-02-26 05:16:24 +09:00
function findInReplyToStatusAndShow ( q , qid , reply , only _first , onlyINreplyto ) {
2015-11-23 08:24:33 +09:00
var reply _found = q . find ( '.stream-item[data-quitter-id="' + reply + '"]' ) ;
var reply _found _reply _to = q . find ( '.stream-item[data-quitter-id="' + reply _found . attr ( 'data-in-reply-to-status-id' ) + '"]' ) ;
2013-08-19 22:30:57 +09:00
if ( reply _found . length > 0 ) {
reply _found . removeClass ( 'hidden-conversation' ) ;
2014-01-29 03:42:47 +09:00
reply _found . css ( 'opacity' , '1' ) ;
2013-08-19 22:30:57 +09:00
if ( only _first && reply _found _reply _to . length > 0 ) {
2015-02-26 05:16:24 +09:00
if ( q . children ( '.view-more-container-top' ) . length < 1 ) {
2015-07-02 02:15:31 +09:00
if ( q . children ( '.queet' ) . prevAll ( '.hidden-conversation' ) . length > 0 ) {
q . prepend ( '<div class="view-more-container-top" data-trace-from="' + reply + '"><a>' + window . sL . viewMoreInConvBefore + '</a></div>' ) ;
2015-03-30 06:38:45 +09:00
}
2015-02-26 05:16:24 +09:00
}
findReplyToStatusAndShow ( q , qid , qid , true ) ;
2013-08-19 22:30:57 +09:00
}
else {
2015-07-02 02:15:31 +09:00
findInReplyToStatusAndShow ( q , qid , reply _found . attr ( 'data-in-reply-to-status-id' ) , false , onlyINreplyto ) ;
2013-08-19 22:30:57 +09:00
}
}
else if ( ! onlyINreplyto ) {
2015-02-26 05:16:24 +09:00
findReplyToStatusAndShow ( q , qid , qid , true ) ;
2013-08-19 22:30:57 +09:00
}
else {
2015-07-02 02:15:31 +09:00
checkForHiddenConversationQueets ( q , qid ) ;
}
2013-08-19 22:30:57 +09:00
}
// recursive function to find the replies to a status
2015-02-26 05:16:24 +09:00
function findReplyToStatusAndShow ( q , qid , this _id , only _first ) {
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
var replies _found = q . find ( '.stream-item[data-in-reply-to-status-id="' + this _id + '"]' ) ;
$ . each ( replies _found , function ( k , reply _found ) {
var reply _founds _reply = q . find ( '.stream-item[data-in-reply-to-status-id="' + $ ( reply _found ) . attr ( 'data-quitter-id' ) + '"]' ) ;
$ ( reply _found ) . removeClass ( 'hidden-conversation' ) ;
$ ( reply _found ) . css ( 'opacity' , '1' ) ;
2015-07-02 02:15:31 +09:00
2014-01-29 03:42:47 +09:00
if ( ! only _first ) {
2015-02-26 05:16:24 +09:00
findReplyToStatusAndShow ( q , qid , $ ( this ) . attr ( 'data-quitter-id' ) , false ) ;
2015-07-02 02:15:31 +09:00
}
2013-08-19 22:30:57 +09:00
if ( only _first && reply _founds _reply . length > 0 ) {
2015-02-26 05:16:24 +09:00
if ( q . children ( '.view-more-container-bottom' ) . length < 1 ) {
2015-07-02 02:15:31 +09:00
if ( q . children ( '.queet' ) . nextAll ( '.hidden-conversation' ) . length > 0 ) {
q . append ( '<div class="view-more-container-bottom" data-replies-after="' + qid + '"><a>' + window . sL . viewMoreInConvAfter + '</a></div>' ) ;
}
}
2013-08-19 22:30:57 +09:00
}
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
} ) ;
2015-07-02 02:15:31 +09:00
checkForHiddenConversationQueets ( q , qid ) ;
2013-08-19 22:30:57 +09:00
}
// helper function for the above recursive functions
2015-02-26 05:16:24 +09:00
function checkForHiddenConversationQueets ( q , qid ) {
2013-08-19 22:30:57 +09:00
// here we check if there are any remaining hidden queets in conversation, if there are, we put a "show full conversation"-link
2015-02-26 05:16:24 +09:00
if ( q . find ( '.hidden-conversation' ) . length > 0 ) {
if ( q . children ( '.queet' ) . find ( '.show-full-conversation' ) . length == 0 ) {
2015-07-02 02:15:31 +09:00
q . children ( '.queet' ) . find ( '.stream-item-footer' ) . append ( '<span class="show-full-conversation" data-stream-item-id="' + qid + '">' + window . sL . expandFullConversation + '</span>' ) ;
2013-08-19 22:30:57 +09:00
}
}
else {
2015-02-26 05:16:24 +09:00
q . children ( '.queet' ) . find ( '.show-full-conversation' ) . remove ( ) ;
2015-07-02 02:15:31 +09:00
}
2013-08-19 22:30:57 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Build stream items and add them to feed
·
· Also a function that has grown out of control ... Needs total makeover
·
2015-07-02 02:15:31 +09:00
· · · · · · · · · · · · · * /
2015-11-23 08:24:33 +09:00
function addToFeed ( feed , after , extraClasses ) {
2015-07-02 02:15:31 +09:00
2014-09-25 06:20:35 +09:00
// some streams, e.g. /statuses/show/1234.json is not enclosed in an array, make sure it is
if ( ! $ . isArray ( feed ) ) {
2015-07-02 02:15:31 +09:00
feed = [ feed ] ;
2014-09-25 06:20:35 +09:00
}
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
var addedToTopOfFeedBodyNum = 0 ;
2015-11-19 05:15:43 +09:00
2013-08-19 22:30:57 +09:00
$ . each ( feed . reverse ( ) , function ( key , obj ) {
2015-07-02 02:15:31 +09:00
var extraClassesThisRun = extraClasses ;
2015-11-23 08:24:33 +09:00
// if this is the notifications feed
if ( window . currentStreamObject . name == 'notifications' ) {
2015-07-02 02:15:31 +09:00
2014-11-14 08:48:02 +09:00
// don't show any notices with object_type "activity"
2016-01-03 00:11:46 +09:00
if ( typeof obj . notice != 'undefined' && obj . notice !== null && obj . notice . is _post _verb === false ) {
2014-11-14 08:48:02 +09:00
return true ;
2015-07-02 02:15:31 +09:00
}
2014-09-25 06:20:35 +09:00
// only if this notification isn't already in stream
2015-07-02 02:15:31 +09:00
if ( $ ( '#feed-body > .stream-item[data-quitter-id-in-stream="' + obj . id + '"]' ) . length == 0 ) {
2014-09-25 06:20:35 +09:00
obj . from _profile . description = obj . from _profile . description || '' ;
2015-07-02 02:15:31 +09:00
var notificationTime = parseTwitterDate ( obj . created _at ) ;
2014-09-25 06:20:35 +09:00
if ( obj . is _seen == '0' ) {
2015-11-23 08:24:33 +09:00
extraClassesThisRun += ' not-seen'
2014-09-25 06:20:35 +09:00
}
2015-07-02 02:15:31 +09:00
2014-11-24 21:47:45 +09:00
// external
var ostatusHtml = '' ;
if ( obj . from _profile . is _local === false ) {
2015-09-01 06:53:09 +09:00
ostatusHtml = '<a target="_blank" data-tooltip="' + window . sL . goToOriginalNotice + '" class="ostatus-link" href="' + obj . from _profile . statusnet _profile _url + '"></a>' ;
2014-11-24 21:47:45 +09:00
}
2015-07-02 02:15:31 +09:00
if ( obj . ntype == 'like' ) {
2014-09-25 06:20:35 +09:00
var noticeTime = parseTwitterDate ( obj . notice . created _at ) ;
2015-09-15 10:58:11 +09:00
var notificationHtml = '<div data-quitter-id-in-stream="' + obj . id + '" id="stream-item-n-' + obj . id + '" class="stream-item ' + extraClassesThisRun + ' notification like " > \
< div class = "queet" > \
< div class = "dogear" > < / d i v > \
' + ostatusHtml + ' \
< div class = "queet-content" > \
< div class = "stream-item-header" > \
2015-12-04 05:41:00 +09:00
< a class = "account-group" href = "' + obj.from_profile.statusnet_profile_url + '" data - user - id = "' + obj.from_profile.id + '" > \
< img class = "avatar standard-size" src = "' + obj.from_profile.profile_image_url + '" data - user - id = "' + obj.from_profile.id + '" / > \
2015-09-15 10:58:11 +09:00
< strong class = "name" data - user - id = "' + obj.from_profile.id + '" title = "@' + obj.from_profile.screen_name + '" > \
' + obj.from_profile.name + ' \
< / s t r o n g > \
< / a > \
2016-01-19 03:30:29 +09:00
' + window.sL.xFavedYourQueet + ' < small class = "created-at" data - created - at = "' + obj.created_at + '" data - tooltip = "' + parseTwitterLongDate(obj.created_at) + '" > < a > ' + notificationTime + ' < / a > < / s m a l l > \
2015-09-15 10:58:11 +09:00
< / d i v > \
< div class = "small-grey-notice" > \
2015-12-04 00:19:03 +09:00
< a data - created - at = "' + obj.notice.created_at + '" data - tooltip = "' + parseTwitterLongDate(obj.notice.created_at) + '" href = "' + window.siteInstanceURL + 'notice/' + obj.notice.id + '" > \
2015-09-15 10:58:11 +09:00
' + noticeTime + ' \
< / a > : \
' + $.trim(obj.notice.statusnet_html) + ' \
< / d i v > \
< / d i v > \
< / d i v > \
< / d i v > ' ;
2014-09-25 06:20:35 +09:00
}
2015-07-02 02:15:31 +09:00
else if ( obj . ntype == 'repeat' ) {
2014-09-25 06:20:35 +09:00
var noticeTime = parseTwitterDate ( obj . notice . created _at ) ;
2015-09-15 10:58:11 +09:00
var notificationHtml = '<div data-quitter-id-in-stream="' + obj . id + '" id="stream-item-n-' + obj . id + '" class="stream-item ' + extraClassesThisRun + ' notification repeat " > \
< div class = "queet" > \
2015-11-23 08:24:33 +09:00
< div class = "dogear" > < / d i v > \
' + ostatusHtml + ' \
2015-09-15 10:58:11 +09:00
< div class = "queet-content" > \
< div class = "stream-item-header" > \
2015-12-04 05:41:00 +09:00
< a class = "account-group" href = "' + obj.from_profile.statusnet_profile_url + '" data - user - id = "' + obj.from_profile.id + '" > \
< img class = "avatar standard-size" src = "' + obj.from_profile.profile_image_url + '" data - user - id = "' + obj.from_profile.id + '" / > \
2015-09-15 10:58:11 +09:00
< strong class = "name" data - user - id = "' + obj.from_profile.id + '" title = "@' + obj.from_profile.screen_name + '" > \
' + obj.from_profile.name + ' \
< / s t r o n g > \
< / a > \
2016-01-19 03:30:29 +09:00
' + window.sL.xRepeatedYourQueet + ' < small class = "created-at" data - created - at = "' + obj.created_at + '" data - tooltip = "' + parseTwitterLongDate(obj.created_at) + '" > < a > ' + notificationTime + ' < / a > < / s m a l l > \
2015-09-15 10:58:11 +09:00
< / d i v > \
< div class = "small-grey-notice" > \
2015-12-04 00:19:03 +09:00
< a data - created - at = "' + obj.notice.created_at + '" data - tooltip = "' + parseTwitterLongDate(obj.notice.created_at) + '" href = "' + window.siteInstanceURL + 'notice/' + obj.notice.id + '" > \
2015-09-15 10:58:11 +09:00
' + noticeTime + ' \
< / a > : \
' + $.trim(obj.notice.statusnet_html) + ' \
< / d i v > \
< / d i v > \
< / d i v > \
< / d i v > ' ;
2014-09-25 06:20:35 +09:00
}
2015-07-02 02:15:31 +09:00
else if ( obj . ntype == 'mention' ) {
2014-09-25 06:20:35 +09:00
var notificationHtml = buildQueetHtml ( obj . notice , obj . id , extraClassesThisRun + ' notification mention' ) ;
2015-07-02 02:15:31 +09:00
}
else if ( obj . ntype == 'reply' ) {
2014-09-25 06:20:35 +09:00
var notificationHtml = buildQueetHtml ( obj . notice , obj . id , extraClassesThisRun + ' notification reply' ) ;
2015-07-02 02:15:31 +09:00
}
else if ( obj . ntype == 'follow' ) {
2015-11-26 04:11:59 +09:00
var notificationHtml = '<div data-quitter-id-in-stream="' + obj . id + '" id="stream-item-n-' + obj . id + '" class="stream-item ' + extraClassesThisRun + ' notification follow " > \
< div class = "queet" > \
< div class = "queet-content" > \
' + ostatusHtml + ' \
< div class = "stream-item-header" > \
2015-12-04 05:41:00 +09:00
< a class = "account-group" href = "' + obj.from_profile.statusnet_profile_url + '" data - user - id = "' + obj.from_profile.id + '" > \
< img class = "avatar standard-size" src = "' + obj.from_profile.profile_image_url + '" data - user - id = "' + obj.from_profile.id + '" / > \
2015-11-26 04:11:59 +09:00
< strong class = "name" data - user - id = "' + obj.from_profile.id + '" title = "@' + obj.from_profile.screen_name + '" > \
' + obj.from_profile.name + ' \
< / s t r o n g > \
< / a > \
2016-01-19 03:30:29 +09:00
' + window.sL.xStartedFollowingYou + ' < small class = "created-at" data - created - at = "' + obj.created_at + '" title = "' + obj.created_at + '" > < a > ' + notificationTime + ' < / a > < / s m a l l > \
2015-11-26 04:11:59 +09:00
< / d i v > \
< / d i v > \
< / d i v > \
< / d i v > ' ;
2015-07-02 02:15:31 +09:00
}
2014-09-25 06:20:35 +09:00
if ( after ) {
2015-07-02 02:15:31 +09:00
$ ( '#' + after ) . after ( notificationHtml ) ;
2014-09-25 06:20:35 +09:00
}
else {
2015-07-02 02:15:31 +09:00
$ ( '#feed-body' ) . prepend ( notificationHtml ) ;
2015-11-23 08:24:33 +09:00
addedToTopOfFeedBodyNum ++ ;
2014-09-25 06:20:35 +09:00
}
}
}
2013-08-19 22:30:57 +09:00
// if this is a user feed
2015-11-23 08:24:33 +09:00
else if ( window . currentStreamObject . type == 'users' ) {
2015-07-02 02:15:31 +09:00
2013-08-20 22:26:25 +09:00
// only if not user is already in stream
2015-07-02 02:15:31 +09:00
if ( $ ( '#stream-item-' + obj . id ) . length == 0 ) {
2016-01-19 03:30:29 +09:00
var userHtml = buildUserStreamItemHtml ( obj ) ;
2015-07-02 02:15:31 +09:00
2013-08-20 22:26:25 +09:00
if ( after ) {
2015-07-02 02:15:31 +09:00
$ ( '#' + after ) . after ( userHtml ) ;
2013-08-20 22:26:25 +09:00
}
else {
2015-07-02 02:15:31 +09:00
$ ( '#feed-body' ) . prepend ( userHtml ) ;
2015-11-23 08:24:33 +09:00
addedToTopOfFeedBodyNum ++ ;
2013-08-19 22:30:57 +09:00
}
}
}
2015-07-02 02:15:31 +09:00
2013-08-20 22:26:25 +09:00
// if this is a list of groups
2015-11-23 08:24:33 +09:00
else if ( window . currentStreamObject . type == 'groups' ) {
2013-08-19 22:30:57 +09:00
2013-08-20 22:26:25 +09:00
// only if not group is already in stream
2015-07-02 02:15:31 +09:00
if ( $ ( '#stream-item-' + obj . id ) . length == 0 ) {
2013-08-20 22:26:25 +09:00
obj . description = obj . description || '' ;
2015-08-26 06:27:08 +09:00
obj . stream _logo = obj . stream _logo || window . defaultAvatarStreamSize ;
2015-07-02 02:15:31 +09:00
2015-03-06 09:34:02 +09:00
// rtl or not
var rtlOrNot = '' ;
if ( $ ( 'body' ) . hasClass ( 'rtl' ) ) {
rtlOrNot = 'rtl' ;
}
2015-07-02 02:15:31 +09:00
2013-08-20 22:26:25 +09:00
// show group actions if logged in
var memberClass = '' ;
if ( obj . member ) {
memberClass = 'member' ;
2015-07-02 02:15:31 +09:00
}
2013-08-20 22:26:25 +09:00
var memberButton = '' ;
2015-07-02 02:15:31 +09:00
if ( typeof window . loggedIn . screen _name != 'undefined' ) {
var memberButton = '<div class="user-actions"><button data-group-id="' + obj . id + '" type="button" class="member-button ' + memberClass + '"><span class="button-text join-text"><i class="join"></i>' + window . sL . joinGroup + '</span><span class="button-text ismember-text">' + window . sL . isMemberOfGroup + '</span><span class="button-text leave-text">' + window . sL . leaveGroup + '</span></button></div>' ;
}
2014-01-29 03:42:47 +09:00
var groupAvatar = obj . stream _logo ;
if ( obj . homepage _logo != null ) {
groupAvatar = obj . homepage _logo ;
}
2015-03-06 09:34:02 +09:00
var groupHtml = '<div id="stream-item-' + obj . id + '" class="stream-item user"><div class="queet ' + rtlOrNot + '">' + memberButton + '<div class="queet-content"><div class="stream-item-header"><a class="account-group" href="' + obj . url + '"><img class="avatar" src="' + groupAvatar + '" /><strong class="name" data-group-id="' + obj . id + '">' + obj . fullname + '</strong> <span class="screen-name">!' + obj . nickname + '</span></a></div><div class="queet-text">' + obj . description + '</div></div></div></div>' ;
2015-07-02 02:15:31 +09:00
2013-08-20 22:26:25 +09:00
if ( after ) {
2015-07-02 02:15:31 +09:00
$ ( '#' + after ) . after ( groupHtml ) ;
2013-08-20 22:26:25 +09:00
}
else {
2015-07-02 02:15:31 +09:00
$ ( '#feed-body' ) . prepend ( groupHtml ) ;
2015-11-23 08:24:33 +09:00
addedToTopOfFeedBodyNum ++ ;
2015-07-02 02:15:31 +09:00
}
2013-08-20 22:26:25 +09:00
}
2015-07-02 02:15:31 +09:00
}
2013-08-19 22:30:57 +09:00
// if this is a retweet
2015-11-23 08:24:33 +09:00
// (note the difference between "the repeat-notice" and "the repeated notice")
// but the unrepeat delete activity notices have retweeted_status added to them, so check this is not a delete notice
else if ( typeof obj . retweeted _status != 'undefined'
&& ( typeof obj . qvitter _delete _notice == 'undefined' || obj . qvitter _delete _notice === false ) ) {
2013-08-19 22:30:57 +09:00
2015-11-23 08:24:33 +09:00
// if repeat-notice doesn't already exist in feed
if ( $ ( '#stream-item-' + obj . id ) . length == 0 ) {
2016-01-17 08:21:30 +09:00
// if the repeated notice already exist in feed, we add this, but hidden
if ( $ ( '.stream-item[data-quitter-id="' + obj . retweeted _status . id + '"]' ) . length > 0 ) {
extraClassesThisRun += ' hidden-repeat' ;
}
2015-07-02 02:15:31 +09:00
var queetHtml = buildQueetHtml ( obj . retweeted _status , obj . id , extraClassesThisRun , obj ) ;
2013-08-19 22:30:57 +09:00
if ( after ) {
2015-07-02 02:15:31 +09:00
$ ( '#' + after ) . after ( queetHtml ) ;
2013-08-19 22:30:57 +09:00
}
else {
2015-07-02 02:15:31 +09:00
$ ( '#feed-body' ) . prepend ( queetHtml ) ;
2015-11-23 08:24:33 +09:00
addedToTopOfFeedBodyNum ++ ;
2013-08-19 22:30:57 +09:00
}
}
}
2015-07-02 02:15:31 +09:00
2013-08-19 22:30:57 +09:00
// ordinary tweet
else {
2015-07-02 02:15:31 +09:00
2013-08-19 22:30:57 +09:00
// only if not already exist
2015-11-23 08:24:33 +09:00
if ( $ ( '#stream-item-' + obj . id ) . length == 0 ) {
2015-07-02 02:15:31 +09:00
2015-11-23 10:23:44 +09:00
// remove any matching temp post
if ( typeof obj . user != 'undefined'
&& obj . user . id == window . loggedIn . id
&& $ ( '#feed-body > .temp-post' ) . length > 0
&& after === false ) {
$ ( '#feed-body > .temp-post' ) . each ( function ( ) {
if ( $ ( this ) . children ( '.queet' ) . find ( '.queet-text' ) . text ( ) == obj . text ) {
$ ( this ) . remove ( ) ;
extraClassesThisRun = 'visible' ;
}
} ) ;
}
2015-11-23 08:24:33 +09:00
var queetHtml = buildQueetHtml ( obj , obj . id , extraClassesThisRun ) ;
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
if ( after ) {
$ ( '#' + after ) . after ( queetHtml ) ;
2013-08-19 22:30:57 +09:00
}
else {
2015-11-23 10:23:44 +09:00
2015-11-23 08:24:33 +09:00
$ ( '#feed-body' ) . prepend ( queetHtml ) ;
addedToTopOfFeedBodyNum ++ ;
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
// if this is a single notice, we expand it
if ( window . currentStreamObject . name == 'notice' ) {
expand _queet ( $ ( '#stream-item-' + obj . id ) ) ;
2013-08-19 22:30:57 +09:00
}
}
}
}
2015-07-02 02:15:31 +09:00
} ) ;
2015-11-23 08:24:33 +09:00
convertAttachmentMoreHref ( ) ;
// if we've added stuff to the top of feed-body, we update our stream cache
if ( addedToTopOfFeedBodyNum > 0 ) {
rememberStreamStateInLocalStorage ( ) ;
}
2013-08-19 22:30:57 +09:00
$ ( '.stream-selection' ) . removeAttr ( 'data-current-user-stream-name' ) ; // don't remeber user feeds
2015-07-02 02:15:31 +09:00
}
2015-11-23 08:24:33 +09:00
2016-01-19 03:30:29 +09:00
/ * ·
·
· Build HTML for a user stream item from an object
·
· @ param obj : a user object
·
· · · · · · · · · · · · · * /
function buildUserStreamItemHtml ( obj ) {
obj . description = obj . description || '' ;
// external
var ostatusHtml = '' ;
if ( obj . is _local === false ) {
ostatusHtml = '<a target="_blank" title="' + window . sL . goToTheUsersRemoteProfile + '" class="ostatus-link" href="' + obj . statusnet _profile _url + '"></a>' ;
}
// rtl or not
var rtlOrNot = '' ;
if ( $ ( 'body' ) . hasClass ( 'rtl' ) ) {
rtlOrNot = 'rtl' ;
}
// show user actions
var followingClass = '' ;
if ( obj . following ) {
followingClass = 'following' ;
}
var followButton = '' ;
if ( typeof window . loggedIn . screen _name != 'undefined' // if logged in
&& window . loggedIn . id != obj . id ) { // not if this is me
if ( ! ( obj . statusnet _profile _url . indexOf ( '/twitter.com/' ) > - 1 && obj . following === false ) ) { // only unfollow twitter users
var followButton = ' < div class = "user-actions" > \
< button data - follow - user - id = "' + obj.id + '" data - follow - user = "' + obj.statusnet_profile_url + '" type = "button" class = "qvitter-follow-button ' + followingClass + '" > \
< span class = "button-text follow-text" > < i class = "follow" > < / i > ' + w i n d o w . s L . u s e r F o l l o w + ' < / s p a n > \
< span class = "button-text following-text" > ' + window.sL.userFollowing + ' < / s p a n > \
< span class = "button-text unfollow-text" > ' + window.sL.userUnfollow + ' < / s p a n > \
< / b u t t o n > \
< / d i v > ' ;
}
}
return '<div id="stream-item-' + obj . id + ' " class=" stream - item user " > \
< div class = "queet ' + rtlOrNot + '" > \
' + followButton + ' \
< div class = "queet-content" > \
< div class = "stream-item-header" > \
< a class = "account-group" href = "' + obj.statusnet_profile_url + '" data - user - id = "' + obj.id + '" > \
< img class = "avatar profile-size" src = "' + obj.profile_image_url_profile_size + '" data - user - id = "' + obj.id + '" / > \
< strong class = "name" data - user - id = "' + obj.id + '" > ' + obj.name + ' < / s t r o n g > \
< span class = "screen-name" data - user - id = "' + obj.id + '" > @ ' + obj.screen_name + ' < / s p a n > \
< / a > \
' + ostatusHtml + ' \
< / d i v > \
< div class = "queet-text" > ' + obj.description + ' < / d i v > \
< / d i v > \
< / d i v > \
< / d i v > ' ;
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2014-09-25 06:20:35 +09:00
· Build HTML for a queet from an object
2015-07-02 02:15:31 +09:00
·
2014-09-25 06:20:35 +09:00
· @ param obj : a queet object
· @ param requeeted _by : if this is a requeet
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · · · · · * /
2013-08-19 22:30:57 +09:00
2015-11-23 08:24:33 +09:00
function buildQueetHtml ( obj , idInStream , extraClasses , requeeted _by , isConversation ) {
2015-07-02 02:15:31 +09:00
2015-11-19 00:38:19 +09:00
// if we've blocked this user, but it has slipped through anyway
2015-11-19 06:14:12 +09:00
var blockingTooltip = '' ;
2015-11-19 05:15:43 +09:00
if ( typeof window . allBlocking != 'undefined' ) {
$ . each ( window . allBlocking , function ( ) {
if ( this == obj . user . id ) {
2015-11-23 08:24:33 +09:00
extraClasses += ' profile-blocked-by-me' ;
2015-11-19 06:14:12 +09:00
blockingTooltip = ' data-tooltip="' + window . sL . thisIsANoticeFromABlockedUser + '"' ;
2015-11-19 05:15:43 +09:00
return false ; // break
}
} ) ;
}
2015-11-19 00:38:19 +09:00
2015-11-23 08:24:33 +09:00
// deleted?
2015-11-26 03:51:51 +09:00
if ( typeof window . knownDeletedNotices [ obj . uri ] != 'undefined' ) {
extraClasses += ' deleted always-hidden' ;
2015-11-23 08:24:33 +09:00
}
// unrepeated?
2015-11-26 03:51:51 +09:00
if ( typeof requeeted _by != 'undefined'
&& requeeted _by !== false
2015-12-01 09:03:07 +09:00
&& typeof window . knownDeletedNotices [ requeeted _by . uri ] != 'undefined' ) {
2015-11-26 03:51:51 +09:00
extraClasses += ' unrepeated always-hidden' ;
2015-11-23 08:24:33 +09:00
}
// activity? (hidden with css)
2016-01-03 00:11:46 +09:00
if ( obj . source == 'activity' || obj . is _post _verb === false ) {
2015-11-26 03:51:51 +09:00
extraClasses += ' activity always-hidden' ;
2015-11-23 08:24:33 +09:00
// because we had an xss issue with activities, the obj.statusnet_html of qvitter-deleted-activity-notices can contain unwanted html, so we escape, they are hidden anyway
obj . statusnet _html = replaceHtmlSpecialChars ( obj . statusnet _html ) ;
}
2015-11-19 00:38:19 +09:00
2015-02-26 07:46:32 +09:00
// if we have the full html for a truncated notice cached in localstorage, we use that
2015-07-02 23:57:51 +09:00
var cacheData = localStorageObjectCache _GET ( 'fullQueetHtml' , obj . id ) ;
if ( cacheData ) {
obj . statusnet _html = cacheData ;
}
2015-07-02 02:15:31 +09:00
2014-09-25 06:20:35 +09:00
// we don't want to print 'null' in in_reply_to_screen_name-attribute, someone might have that username!
var in _reply _to _screen _name = '' ;
if ( obj . in _reply _to _screen _name != null ) {
in _reply _to _screen _name = obj . in _reply _to _screen _name ;
2015-02-26 05:16:24 +09:00
}
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
// conversations has some slightly different id's
var idPrepend = '' ;
if ( typeof isConversation != 'undefined' && isConversation === true ) {
var idPrepend = 'conversation-' ;
2015-11-23 08:24:33 +09:00
extraClasses += ' conversation'
2015-02-26 05:16:24 +09:00
}
2014-09-25 06:20:35 +09:00
2015-01-27 09:57:08 +09:00
// is this mine?
2015-07-02 02:15:31 +09:00
var isThisMine = 'not-mine' ;
2015-11-05 21:10:11 +09:00
if ( obj . user . id == window . loggedIn . id ) {
2015-01-27 09:57:08 +09:00
var isThisMine = 'is-mine' ;
2014-09-25 06:20:35 +09:00
}
2015-07-02 02:15:31 +09:00
2015-02-26 05:16:24 +09:00
// requeeted by me?
2015-07-02 02:15:31 +09:00
var requeetedByMe = '' ;
2015-02-26 05:16:24 +09:00
if ( obj . repeated _id ) {
requeetedByMe = ' data-requeeted-by-me-id="' + obj . repeated _id + '" ' ;
}
2015-07-02 02:15:31 +09:00
2015-01-27 09:57:08 +09:00
// requeet html
if ( obj . repeated ) {
2014-11-24 21:47:45 +09:00
var requeetHtml = '<li class="action-rt-container"><a class="with-icn done"><span class="icon sm-rt" title="' + window . sL . requeetedVerb + '"></span></a></li>' ;
2015-11-23 08:24:33 +09:00
extraClasses += ' requeeted' ;
2014-09-25 06:20:35 +09:00
}
else {
2015-01-27 09:57:08 +09:00
var requeetHtml = '<li class="action-rt-container"><a class="with-icn"><span class="icon sm-rt ' + isThisMine + '" title="' + window . sL . requeetVerb + '"></span></a></li>' ;
2014-09-25 06:20:35 +09:00
}
// favorite html
if ( obj . favorited ) {
2014-11-24 21:47:45 +09:00
var favoriteHtml = '<a class="with-icn done"><span class="icon sm-fav" title="' + window . sL . favoritedVerb + '"></span></a>' ;
2015-11-23 08:24:33 +09:00
extraClasses += ' favorited' ;
2014-09-25 06:20:35 +09:00
}
else {
2014-11-24 21:47:45 +09:00
var favoriteHtml = '<a class="with-icn"><span class="icon sm-fav" title="' + window . sL . favoriteVerb + '"></span></a>' ;
2015-07-02 02:15:31 +09:00
}
2014-09-25 06:20:35 +09:00
// actions only for logged in users
var queetActions = '' ;
2015-07-02 02:15:31 +09:00
if ( typeof window . loggedIn . screen _name != 'undefined' ) {
2015-06-24 22:34:44 +09:00
queetActions = '<ul class="queet-actions"><li class="action-reply-container"><a class="with-icn"><span class="icon sm-reply" title="' + window . sL . replyVerb + '"></span></a></li>' + requeetHtml + '<li class="action-rq-num" data-rq-num="' + obj . repeat _num + '">' + obj . repeat _num + '</li><li class="action-fav-container">' + favoriteHtml + '</li><li class="action-fav-num" data-fav-num="' + obj . fave _num + '">' + obj . fave _num + '</li><li class="action-ellipsis-container"><a class="with-icn"><span class="icon sm-ellipsis" title="' + window . sL . ellipsisMore + '"></span></a></li></ul>' ;
2014-09-25 06:20:35 +09:00
}
2015-07-02 02:15:31 +09:00
// reply-to html
var reply _to _html = '' ;
2014-11-24 21:47:45 +09:00
if ( obj . in _reply _to _screen _name !== null && obj . in _reply _to _profileurl !== null && obj . in _reply _to _screen _name != obj . user . screen _name ) {
reply _to _html = '<span class="reply-to"><a class="h-card mention" href="' + obj . in _reply _to _profileurl + '">@' + obj . in _reply _to _screen _name + '</a></span> ' ;
2014-09-25 06:20:35 +09:00
}
2015-07-02 02:15:31 +09:00
2014-09-25 06:20:35 +09:00
// in-groups html
2015-07-02 02:15:31 +09:00
var in _groups _html = '' ;
2014-11-25 00:10:59 +09:00
if ( typeof obj . statusnet _in _groups != 'undefined' && obj . statusnet _in _groups !== false && typeof obj . statusnet _in _groups === 'object' ) {
2014-11-24 21:47:45 +09:00
$ . each ( obj . statusnet _in _groups , function ( ) {
2015-08-26 07:11:31 +09:00
in _groups _html = in _groups _html + ' <span class="in-groups"><a class="h-card group" href="' + this . url + '">!' + this . nickname + '</a></span>' ;
2014-11-24 21:47:45 +09:00
} ) ;
2015-07-02 02:15:31 +09:00
}
2015-11-23 08:24:33 +09:00
// requeets get's a context element and a identifying class
// uri used is the repeate-notice's uri for repeats, not the repetED notice's uri (necessary if someone deletes a repeat)
var URItoUse = obj . uri ;
2014-09-25 06:20:35 +09:00
var requeetHtml = '' ;
2015-02-26 05:16:24 +09:00
if ( typeof requeeted _by != 'undefined' && requeeted _by !== false ) {
2014-11-24 21:47:45 +09:00
var requeetedByHtml = '<a data-user-id="' + requeeted _by . user . id + '" href="' + requeeted _by . user . statusnet _profile _url + '"> <b>' + requeeted _by . user . name + '</b></a>' ;
2015-11-05 21:10:11 +09:00
requeetHtml = '<div class="context" id="requeet-' + requeeted _by . id + '"><span class="with-icn"><i class="badge-requeeted" data-tooltip="' + parseTwitterDate ( requeeted _by . created _at ) + '"></i><span class="requeet-text"> ' + window . sL . requeetedBy . replace ( '{requeeted-by}' , requeetedByHtml ) + '</span></span></div>' ;
2015-11-23 08:24:33 +09:00
var URItoUse = requeeted _by . uri ;
extraClasses += ' is-requeet' ;
2014-09-25 06:20:35 +09:00
}
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
// the URI for delete activity notices are the same as the notice that is to be deleted
// so we make the URI for the (hidden) actitity notice unique, otherwise we might remove
// the activity notice from DOM when we remove the deleted notice
if ( typeof obj . qvitter _delete _notice != 'undefined' && obj . qvitter _delete _notice == true ) {
URItoUse += '-activity-notice' ;
}
2016-01-07 11:03:18 +09:00
// attachment html and attachment url's to hide
var attachmentBuild = buildAttachmentHTML ( obj . attachments ) ;
var statusnetHTML = $ ( '<div/>' ) . html ( obj . statusnet _html ) ;
2016-01-11 05:31:19 +09:00
$ . each ( statusnetHTML . find ( 'a' ) , function ( k , aElement ) {
$ . each ( attachmentBuild . urlsToHide , function ( k , urlToHide ) {
var urlToHideWithoutProtocol = removeProtocolFromUrl ( urlToHide ) ;
if ( urlToHideWithoutProtocol == removeProtocolFromUrl ( $ ( aElement ) . attr ( 'href' ) )
|| urlToHideWithoutProtocol == removeProtocolFromUrl ( $ ( aElement ) . text ( ) ) ) {
2016-01-23 05:19:08 +09:00
$ ( aElement ) . addClass ( 'hidden-embedded-link-in-queet-text' )
2016-01-11 05:31:19 +09:00
}
} ) ;
2016-01-07 11:03:18 +09:00
} ) ;
2016-01-21 10:47:34 +09:00
// if statusnetHTML is contains <p>:s, unwrap those (diaspora..)
statusnetHTML . children ( 'p' ) . each ( function ( ) {
$ ( this ) . contents ( ) . unwrap ( ) ;
} ) ;
2016-01-21 10:16:39 +09:00
// find a place in the queet-text for the quoted notices
statusnetHTML = placeQuotedNoticesInQueetText ( attachmentBuild . quotedNotices , statusnetHTML ) ;
2016-01-07 11:03:18 +09:00
statusnetHTML = statusnetHTML . html ( ) ;
2016-01-21 11:00:17 +09:00
// remove trailing <br>s from queet text
while ( statusnetHTML . slice ( - 4 ) == '<br>' ) {
statusnetHTML = statusnetHTML . slice ( 0 , - 4 ) ;
}
2014-11-24 21:47:45 +09:00
// external
var ostatusHtml = '' ;
if ( obj . is _local === false ) {
2015-09-01 06:53:09 +09:00
ostatusHtml = '<a target="_blank" data-tooltip="' + window . sL . goToOriginalNotice + '" class="ostatus-link" href="' + obj . external _url + '"></a>' ;
2014-11-24 21:47:45 +09:00
}
2015-07-02 02:15:31 +09:00
var queetTime = parseTwitterDate ( obj . created _at ) ;
2014-09-25 06:20:35 +09:00
var queetHtml = ' < div \
2015-11-23 08:24:33 +09:00
id = "' + idPrepend + 'stream-item-' + idInStream + '" \
data - uri = "' + URItoUse + '" \
2015-12-04 05:41:00 +09:00
class = "stream-item notice ' + extraClasses + '" \
2014-09-25 06:20:35 +09:00
data - source = "' + escape(obj.source) + '" \
data - quitter - id = "' + obj.id + '" \
data - conversation - id = "' + obj.statusnet_conversation_id + '" \
data - quitter - id - in - stream = "' + idInStream + '" \
data - in - reply - to - screen - name = "' + in_reply_to_screen_name + '" \
2015-02-26 05:16:24 +09:00
data - in - reply - to - status - id = "' + obj.in_reply_to_status_id + '" \
' + requeetedByMe + ' > \
2015-11-23 08:24:33 +09:00
< div class = "queet" id = "' + idPrepend + 'q-' + idInStream + '" ' + blockingTooltip + ' > \
2016-01-22 00:15:56 +09:00
< script class = "attachment-json" type = "application/json" > ' + JSON.stringify(obj.attachments) + ' < / s c r i p t > \
2014-11-24 21:47:45 +09:00
' + requeetHtml + ' \
' + ostatusHtml + ' \
2014-09-25 06:20:35 +09:00
< div class = "queet-content" > \
< div class = "stream-item-header" > \
2015-12-04 05:41:00 +09:00
< a class = "account-group" href = "' + obj.user.statusnet_profile_url + '" data - user - id = "' + obj.user.id + '" > \
< img class = "avatar profile-size" src = "' + obj.user.profile_image_url_profile_size + '" data - user - id = "' + obj.user.id + '" / > \
2014-09-25 06:20:35 +09:00
< strong class = "name" data - user - id = "' + obj.user.id + '" > ' + obj.user.name + ' < / s t r o n g > \
2015-12-04 05:41:00 +09:00
< span class = "screen-name" data - user - id = "' + obj.user.id + '" > @ ' + obj.user.screen_name + ' < / s p a n > ' +
2015-09-18 08:42:52 +09:00
'</a>' +
'<i class="addressees">' + reply _to _html + in _groups _html + '</i>' +
'<small class="created-at" data-created-at="' + obj . created _at + ' " > \
2015-09-15 10:58:11 +09:00
< a data - tooltip = "' + parseTwitterLongDate(obj.created_at) + '" href = "' + window.siteInstanceURL + 'notice/' + obj.id + '" > ' + queetTime + ' < / a > \
2014-09-25 06:20:35 +09:00
< / s m a l l > \
< / d i v > \
2016-01-07 11:03:18 +09:00
< div class = "queet-text" > ' + $.trim(statusnetHTML) + ' < / d i v > \
' + attachmentBuild.html + ' \
2014-09-25 06:20:35 +09:00
< div class = "stream-item-footer" > \
' + queetActions + ' \
< / d i v > \
< / d i v > \
< / d i v > \
< / d i v > ' ;
// detect rtl
2015-07-02 02:15:31 +09:00
queetHtml = detectRTL ( queetHtml ) ;
2014-09-25 06:20:35 +09:00
return queetHtml ;
}
2015-11-26 03:51:51 +09:00
2016-01-21 10:16:39 +09:00
/ * ·
·
2016-01-23 05:19:08 +09:00
· Place or update quoted notices in the queet text
2016-01-21 10:16:39 +09:00
·
· @ param quotedNotices : object returned by buildAttachmentHTML ( )
· @ param queetText : jQuery object for queet text
·
· · · · · · · · · * /
function placeQuotedNoticesInQueetText ( quotedNotices , queetText ) {
$ . each ( quotedNotices , function ( k , qoutedNotice ) {
if ( typeof qoutedNotice . url != 'undefined' ) {
2016-01-23 05:19:08 +09:00
var quoteLinkFound = queetText . find ( 'a[href*="' + removeProtocolFromUrl ( qoutedNotice . url ) + '"]:not(.quote-link-container)' ) ;
2016-01-21 10:16:39 +09:00
// if we can't found it in a href, we might find it in data-quote-url attribute!
if ( quoteLinkFound . length == 0 ) {
2016-01-23 05:19:08 +09:00
quoteLinkFound = queetText . find ( 'a[data-quote-url*="' + removeProtocolFromUrl ( qoutedNotice . url ) + '"]:not(.quote-link-container)' ) ;
2016-01-21 10:16:39 +09:00
}
if ( quoteLinkFound . length > 0 ) {
2016-01-21 10:47:34 +09:00
$ . each ( quoteLinkFound , function ( ) {
2016-01-23 05:19:08 +09:00
// restore old style links (this can be removed later)
if ( $ ( this ) . children ( '.quoted-notice-header, .oembed-item-header' ) . length > 0 ) {
$ ( this ) . html ( $ ( this ) . attr ( 'href' ) ) ;
$ ( this ) . removeAttr ( 'style' ) ;
$ ( this ) . removeClass ( 'quoted-notice' ) ;
$ ( this ) . removeClass ( 'oembed-item' ) ;
2016-01-21 10:16:39 +09:00
}
2016-01-23 05:19:08 +09:00
// place a container if we don't have it
if ( ! $ ( this ) . next ( ) . is ( '.quote-link-container' ) ) {
$ ( this ) . after ( '<a class="quote-link-container"></a>' ) ;
}
// update the link and the (maybe newly added) quote container after the link
if ( $ ( this ) . next ( ) . is ( '.quote-link-container' ) ) {
$ ( this ) . addClass ( 'hidden-quote-link-in-queet-text' ) ;
$ ( this ) . attr ( 'data-quote-url' , qoutedNotice . url ) ;
$ ( this ) . next ( ) . addClass ( qoutedNotice . class ) ;
$ ( this ) . next ( ) . attr ( 'href' , qoutedNotice . href ) ;
$ ( this ) . next ( ) . html ( qoutedNotice . html ) ;
// remove unnecessary line breaks, i.e. remove br between two quoted notices
if ( $ ( this ) . prev ( ) . is ( 'br' ) ) {
$ ( this ) . prev ( ) . remove ( ) ;
}
if ( ! $ ( this ) . next ( ) . next ( ) . is ( 'br' ) ) {
$ ( this ) . next ( ) . after ( '<br>' ) ;
}
2016-01-21 10:16:39 +09:00
}
} ) ;
}
}
} ) ;
return queetText ;
}
2015-11-26 03:51:51 +09:00
/ * ·
·
· Build HTML for the attachments to a queet
·
· @ param attachments : attachment object returned by the api
·
· · · · · · · · · · · · · * /
function buildAttachmentHTML ( attachments ) {
2016-01-21 01:39:34 +09:00
var attachmentHTML = '' ;
var oembedHTML = '' ;
2016-01-11 01:19:47 +09:00
var quotedNotices = [ ] ;
2015-11-26 03:51:51 +09:00
var attachmentNum = 0 ;
2016-01-21 01:39:34 +09:00
var oembedNum = 0 ;
2016-01-07 11:03:18 +09:00
var urlsToHide = [ ] ;
2015-11-26 03:51:51 +09:00
if ( typeof attachments != "undefined" ) {
$ . each ( attachments , function ( ) {
2016-01-07 11:03:18 +09:00
// quoted notices
if ( typeof this . quoted _notice != 'undefined' ) {
var quotedContent = this . quoted _notice . content ;
// quoted notice's attachments' thumb urls
var quotedAttachmentsHTML = '' ;
var quotedAttachmentsHTMLbefore = '' ;
var quotedAttachmentsHTMLafter = '' ;
if ( typeof this . quoted _notice . attachments != 'undefined' && this . quoted _notice . attachments . length > 0 ) {
quotedAttachmentsHTML += '<div class="quoted-notice-attachments quoted-notice-attachments-num-' + this . quoted _notice . attachments . length + '">'
$ . each ( this . quoted _notice . attachments , function ( k , qAttach ) {
quotedAttachmentsHTML += '<div class="quoted-notice-img-container" style="background-image:url(\'' + qAttach . thumb _url + '\')"><img class="quoted-notice-img" src="' + qAttach . thumb _url + '" /></div>' ;
// remove attachment string from content
quotedContent = quotedContent . split ( window . siteInstanceURL + 'attachment/' + qAttach . attachment _id ) . join ( '' ) ;
} ) ;
quotedAttachmentsHTML += '</div>' ;
// if there is only one attachment, it goes before, otherwise after
if ( this . quoted _notice . attachments . length == 1 ) {
quotedAttachmentsHTMLbefore = quotedAttachmentsHTML ;
}
else {
quotedAttachmentsHTMLafter = quotedAttachmentsHTML ;
}
}
2016-01-21 10:16:39 +09:00
var quotedNoticeHTML = quotedAttachmentsHTMLbefore + ' \
< div class = "quoted-notice-header" > \
< span class = "quoted-notice-author-fullname" > ' + this.quoted_notice.fullname + ' < / s p a n > \
< span class = "quoted-notice-author-nickname" > ' + this.quoted_notice.nickname + ' < / s p a n > \
< / d i v > \
< div class = "quoted-notice-body" > ' + $.trim(quotedContent) + ' < / d i v > \
' + quotedAttachmentsHTMLafter ;
quotedNotices . push ( {
url : this . url ,
html : quotedNoticeHTML ,
href : window . siteInstanceURL + 'notice/' + this . quoted _notice . id ,
class : 'quoted-notice'
} ) ;
2016-01-07 11:03:18 +09:00
}
2016-01-21 10:16:39 +09:00
// if we have Twitter oembed data, we add is as quotes
else if ( typeof this . oembed != 'undefined'
&& this . oembed !== false
&& this . oembed . provider == 'Twitter' ) {
var twitterHTML = ' < div class = "oembed-item-header" > \
2016-01-21 10:47:34 +09:00
< span class = "oembed-item-title" > ' + this.oembed.author_name + ' < / s p a n > \
2016-01-22 04:01:43 +09:00
< span class = "oembed-username" > ' + this.oembed.title + ' < / s p a n > \
2016-01-21 10:47:34 +09:00
< / d i v > \
< div class = "oembed-item-body" > ' + this.oembed.oembedHTML + ' < / d i v > \
< div class = "oembed-item-footer" > \
< span class = "oembed-item-provider" > ' + this.oembed.provider + ' < / s p a n > \
< / d i v > ' ;
2016-01-21 10:16:39 +09:00
quotedNotices . push ( {
url : this . url ,
html : twitterHTML ,
href : this . url ,
class : 'oembed-item'
} ) ;
}
// if we have other oembed data (but not for photos and youtube, we handle those later)
2016-01-21 01:39:34 +09:00
else if ( typeof this . oembed != 'undefined'
&& this . oembed !== false
2016-01-21 02:40:27 +09:00
&& this . oembed . title !== null
2016-01-21 10:16:39 +09:00
&& this . oembed . provider != 'YouTube'
&& this . oembed . type != 'photo' ) {
2016-01-21 01:39:34 +09:00
var oembedImage = '' ;
// not if stripped from html it's the same as the title (wordpress does this..)
if ( typeof this . thumb _url != 'undefined' && this . thumb _url !== null ) {
oembedImage = '<div class="oembed-img-container" style="background-image:url(\'' + this . thumb _url + '\')"><img class="oembed-img" src="' + this . thumb _url + '" /></div>' ;
}
var oembedBody = '' ;
2016-01-22 00:15:56 +09:00
// don't add body if html it's too similar (80%) to the title (wordpress does this..)
2016-01-21 01:39:34 +09:00
if ( this . oembed . oembedHTML !== null
2016-01-22 00:15:56 +09:00
&& this . oembed . oembedHTML . length > 0 ) {
2016-01-21 03:06:21 +09:00
if ( this . oembed . oembedHTML . length > 200 ) {
this . oembed . oembedHTML = this . oembed . oembedHTML . substring ( 0 , 200 ) + '…' ;
2016-01-21 01:39:34 +09:00
}
2016-01-22 00:15:56 +09:00
if ( stringSimilarity ( this . oembed . oembedHTML , this . oembed . title . substring ( 0 , 200 ) ) < 80 ) {
oembedBody = this . oembed . oembedHTML ;
}
2016-01-21 01:39:34 +09:00
}
if ( this . oembed . provider === null ) {
var oembedProvider = this . url ;
var oembedProviderURL = '' ;
}
else {
var oembedProvider = this . oembed . provider ;
var oembedProviderURL = removeProtocolFromUrl ( this . oembed . provider _url ) ;
2016-01-22 04:01:43 +09:00
// remove trailing /
if ( oembedProviderURL . slice ( - 1 ) == '/' ) {
oembedProviderURL = oembedProviderURL . slice ( 0 , - 1 ) ;
}
2016-01-21 01:39:34 +09:00
}
2016-01-22 04:01:43 +09:00
// If the oembed data is generated by Qvitter, we know a better way of showing the title
var oembedTitle = this . oembed . title ;
var oembedTitleHTML = '<span class="oembed-item-title">' + oembedTitle + '</span>' ;
if ( oembedTitle . slice ( - 10 ) == ' (Qvitter)' ) {
var oembedTimePosted = parseTwitterLongDate ( oembedTitle . slice ( 0 , - 10 ) ) ;
var oembedGNUsocialUsername = this . oembed . author _name . substring ( this . oembed . author _name . lastIndexOf ( '(' ) + 1 , this . oembed . author _name . lastIndexOf ( ')' ) ) ;
var oembedGNUsocialFullname = this . oembed . author _name . slice ( 0 , - ( oembedGNUsocialUsername . length + 3 ) ) ;
oembedTitleHTML = '<span class="oembed-item-title">' + oembedGNUsocialFullname + ' < / s p a n > \
< span class = "oembed-username" > @ ' + oembedGNUsocialUsername + ' < / s p a n > ' ;
}
2016-01-21 01:39:34 +09:00
oembedHTML += '<a href="' + this . url + ' " class=" oembed - item " > \
' + oembedImage + ' \
< div class = "oembed-item-header" > \
2016-01-22 04:01:43 +09:00
' + oembedTitleHTML + ' \
2016-01-21 01:39:34 +09:00
< / d i v > \
< div class = "oembed-item-body" > ' + oembedBody + ' < / d i v > \
< div class = "oembed-item-footer" > \
2016-01-21 02:21:03 +09:00
< span class = "oembed-item-provider" > ' + oembedProvider + ' < / s p a n > \
2016-01-21 01:39:34 +09:00
< span class = "oembed-item-provider-url" > ' + oembedProviderURL + ' < / s p a n > \
< / d i v > \
< / a > ' ;
oembedNum ++ ;
}
2016-01-07 11:03:18 +09:00
// if there's a local thumb_url we assume this is a image or video
else if ( typeof this . thumb _url != 'undefined'
2015-12-11 01:36:28 +09:00
&& this . thumb _url !== null
2016-01-07 11:03:18 +09:00
&& isLocalURL ( this . thumb _url ) ) {
2015-11-26 03:51:51 +09:00
var bigThumbW = 1000 ;
var bigThumbH = 3000 ;
if ( bigThumbW > window . siteMaxThumbnailSize ) {
bigThumbW = window . siteMaxThumbnailSize ;
}
if ( bigThumbH > window . siteMaxThumbnailSize ) {
bigThumbH = window . siteMaxThumbnailSize ;
}
// very long landscape images should not have background-size:cover
var noCoverClass = '' ;
if ( this . width / this . height > 2 ) {
noCoverClass = ' no-cover' ;
}
// play button for videos and animated gifs
var playButtonClass = '' ;
if ( ( this . url . indexOf ( '://www.youtube.com' ) > - 1 || this . url . indexOf ( '://youtu.be' ) > - 1 )
|| ( typeof this . animated != 'undefined' && this . animated === true ) ) {
var playButtonClass = ' play-button' ;
}
// youtube class
var youTubeClass = '' ;
if ( this . url . indexOf ( '://www.youtube.com' ) > - 1 || this . url . indexOf ( '://youtu.be' ) > - 1 ) {
youTubeClass = ' youtube' ;
}
2016-01-11 02:14:34 +09:00
// gif-class
var animatedGifClass = '' ;
if ( typeof this . animated != 'undefined' && this . animated === true ) {
var animatedGifClass = ' animated-gif' ;
}
2015-11-26 03:51:51 +09:00
// animated gifs always get default small non-animated thumbnail
if ( this . animated === true ) {
var img _url = this . thumb _url ;
}
// if no dimensions are set, go with default thumb
else if ( this . width === null && this . height === null ) {
var img _url = this . thumb _url ;
}
// large images get large thumbnail
else if ( this . width > 1000 ) {
var img _url = this . large _thumb _url ;
}
2016-01-10 20:29:56 +09:00
// no thumbnails for small local images
else if ( this . url . indexOf ( window . siteInstanceURL ) === 0 ) {
2015-11-26 03:51:51 +09:00
var img _url = this . url ;
}
2016-01-11 01:19:47 +09:00
// small thumbnail for small remote images
2016-01-10 20:29:56 +09:00
else {
var img _url = this . thumb _url ;
}
2015-11-26 03:51:51 +09:00
2016-01-21 01:39:34 +09:00
attachmentHTML += '<a style="background-image:url(\'' + img _url + '\')" class="thumb-container' + noCoverClass + playButtonClass + youTubeClass + animatedGifClass + '" href="' + this . url + '"><img class="attachment-thumb" data-mime-type="' + this . mimetype + '" src="' + img _url + '"/ data-width="' + this . width + '" data-height="' + this . height + '" data-full-image-url="' + this . url + '" data-thumb-url="' + img _url + '"></a>' ;
2016-01-07 11:03:18 +09:00
urlsToHide . push ( window . siteInstanceURL + 'attachment/' + this . id ) ; // hide this attachment url from the queet text
2015-11-26 03:51:51 +09:00
attachmentNum ++ ;
}
else if ( this . mimetype == 'image/svg+xml' ) {
2016-01-21 01:39:34 +09:00
attachmentHTML += '<a style="background-image:url(\'' + this . url + '\')" class="thumb-container" href="' + this . url + '"><img class="attachment-thumb" data-mime-type="' + this . mimetype + '" src="' + this . url + '"/></a>' ;
2016-01-07 11:03:18 +09:00
urlsToHide . push ( window . siteInstanceURL + 'attachment/' + this . id ) ; // hide this attachment url from the queet text
2015-11-26 03:51:51 +09:00
attachmentNum ++ ;
}
} ) ;
}
2016-01-21 01:39:34 +09:00
return { html : '<div class="oembed-data oembed-num-' + oembedNum + '">' + oembedHTML + '</div><div class="queet-thumbs thumb-num-' + attachmentNum + '">' + attachmentHTML + '</div>' ,
2016-01-11 01:19:47 +09:00
urlsToHide : urlsToHide ,
2016-01-21 01:39:34 +09:00
quotedNotices : quotedNotices
} ;
2015-11-26 03:51:51 +09:00
}