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
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · * /
2015-01-19 04:36:08 +09:00
2015-02-26 07:46:32 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-02-26 07:46:32 +09:00
· Store in localStorage object cache
2015-07-02 02:15:31 +09:00
·
2015-02-26 07:46:32 +09:00
· @ param name : the name of this type of object
· @ param unique _id : some unique _id – the key in localStorage will be name - unique _id
· @ param object : the object to store
2015-07-02 02:15:31 +09:00
·
2015-02-26 07:46:32 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2015-02-26 07:46:32 +09:00
function localStorageObjectCache _STORE ( name , unique _id , object ) {
2015-11-23 08:24:33 +09:00
if ( localStorageIsEnabled ( ) === false ) {
return false ;
}
2015-02-26 07:46:32 +09:00
2015-11-23 08:24:33 +09:00
name = localStorageMaybeAppendIdToKey ( name ) ;
if ( object === false || object === null || object . length < 1 ) {
// false or an empty object means we remove this entry
if ( typeof localStorage [ name + '-' + unique _id ] != 'undefined' && localStorage [ name + '-' + unique _id ] !== null ) {
delete localStorage [ name + '-' + unique _id ] ;
2015-02-26 07:46:32 +09:00
}
2015-11-23 08:24:33 +09:00
return false ;
}
2015-03-06 06:22:48 +09:00
2015-11-23 08:24:33 +09:00
var dataToSave = { } ;
dataToSave . modified = Date . now ( ) ;
dataToSave . cdata = LZString . compressToUTF16 ( JSON . stringify ( object ) ) ;
2015-03-06 06:22:48 +09:00
2015-11-23 08:24:33 +09:00
try {
localStorage . setItem ( name + '-' + unique _id , JSON . stringify ( dataToSave ) ) ;
}
catch ( e ) {
if ( e . name == 'QUOTA_EXCEEDED_ERR' || e . name == 'NS_ERROR_DOM_QUOTA_REACHED' || e . name == 'QuotaExceededError' || e . name == 'W3CException_DOM_QUOTA_EXCEEDED_ERR' ) {
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
removeOldestLocalStorageEntries ( function ( ) {
localStorageObjectCache _STORE ( name , unique _id , object ) ;
} ) ;
2015-07-02 02:15:31 +09:00
2015-11-23 08:24:33 +09:00
}
else {
console . log ( 'could not store in localStorage, unknown error' ) ;
2015-07-02 02:15:31 +09:00
}
}
2015-02-26 07:46:32 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-03-06 06:22:48 +09:00
· Remove the 100 oldest cached items
2015-07-02 02:15:31 +09:00
·
2015-03-06 06:22:48 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
function removeOldestLocalStorageEntries ( callback ) {
2015-03-06 06:22:48 +09:00
// grab the expiry and store the modified-key into an object
var modified = Object . keys ( localStorage ) . reduce ( function ( collection , key ) {
var currentModified = JSON . parse ( localStorage . getItem ( key ) ) . modified ;
collection [ currentModified ] = key ;
return collection ;
} , { } ) ;
2015-07-02 02:15:31 +09:00
2015-03-06 06:22:48 +09:00
delete modified [ 'undefined' ] ; // we don't want those
// get the modified dates into an array
var modifiedDates = Object . keys ( modified ) ;
2015-07-02 02:15:31 +09:00
2015-03-06 06:22:48 +09:00
modifiedDates . sort ( ) ;
2015-07-02 02:15:31 +09:00
2015-03-06 06:22:48 +09:00
var i = 0 ;
$ . each ( modifiedDates , function ( k , v ) {
2015-07-02 02:15:31 +09:00
delete localStorage [ modified [ v ] ] ;
2015-03-06 06:22:48 +09:00
i ++ ;
if ( i >= 100 ) {
return false ;
}
} ) ;
2016-03-04 08:22:25 +09:00
console . log ( 'removed ' + i + ' old localstorage items' ) ;
2015-03-06 06:22:48 +09:00
2016-03-04 08:22:25 +09:00
if ( i > 0 ) {
callback ( ) ;
return true ;
}
else {
return false ;
}
2015-03-06 06:22:48 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-02-26 07:46:32 +09:00
· Get from localStorage object cache
2015-07-02 02:15:31 +09:00
·
2015-02-26 07:46:32 +09:00
· @ param name : the name of this type of object
· @ param unique _id : some unique _id – the key in localStorage will be name - unique _id
2015-07-02 02:15:31 +09:00
·
2015-02-26 07:46:32 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2015-07-02 23:57:51 +09:00
function localStorageObjectCache _GET ( name , unique _id ) {
2015-11-23 08:24:33 +09:00
if ( localStorageIsEnabled ( ) === false ) {
return false ;
}
name = localStorageMaybeAppendIdToKey ( name ) ;
if ( typeof localStorage [ name + '-' + unique _id ] == 'undefined' || localStorage [ name + '-' + unique _id ] === null ) {
return false ;
}
try {
var parsedObject = JSON . parse ( localStorage [ name + '-' + unique _id ] ) ;
}
catch ( e ) {
return false ;
}
if ( typeof parsedObject . modified == 'undefined' || parsedObject . modified === null ) {
// invalid or old localstorage object found, check the whole localstorage!
checkLocalStorage ( ) ;
return false ;
}
else {
try {
var decompressedAndParsed = JSON . parse ( LZString . decompressFromUTF16 ( parsedObject . cdata ) ) ;
return decompressedAndParsed ;
2015-02-26 07:46:32 +09:00
}
2015-11-23 08:24:33 +09:00
catch ( e ) {
2015-07-02 23:57:51 +09:00
return false ;
2015-02-26 07:46:32 +09:00
}
2015-03-06 06:22:48 +09:00
}
2015-11-23 08:24:33 +09:00
}
// to the following data types we add the logged in user's user id,
// since they contain user specific data (0 for logged out)
// selectedLanguage is handled differently, since we want to be able to
// access the logged out user's data if we're logged in
function localStorageMaybeAppendIdToKey ( name ) {
if ( jQuery . inArray ( name , [ 'browsingHistory' , 'conversation' , 'queetBoxInput' , 'streamState' ] ) !== - 1 ) {
if ( window . loggedIn ) {
return name + '-' + window . loggedIn . id ;
}
else {
return name + '-0' ;
}
}
2015-03-06 06:22:48 +09:00
else {
2015-11-23 08:24:33 +09:00
return name ;
2015-07-02 02:15:31 +09:00
}
2015-02-26 07:46:32 +09:00
}
2015-11-23 08:24:33 +09:00
2015-03-06 06:22:48 +09:00
function checkLocalStorage ( ) {
2015-05-19 20:42:54 +09:00
if ( localStorageIsEnabled ( ) === false ) {
console . log ( 'localstorage disabled' ) ;
return false ;
}
2015-03-06 06:22:48 +09:00
console . log ( 'checking localStorage for invalid entries' ) ;
var dateNow = Date . now ( )
var corrected = 0 ;
var deleted = 0 ;
2015-09-22 01:30:37 +09:00
var compressed = 0 ;
2015-03-06 06:22:48 +09:00
$ . each ( localStorage , function ( k , entry ) {
if ( typeof entry == 'string' ) {
// check that entry is valid json
try {
2015-07-02 02:15:31 +09:00
var entryParsed = JSON . parse ( entry ) ;
2015-03-06 06:22:48 +09:00
}
catch ( e ) {
delete localStorage [ k ] ;
deleted ++ ;
return true ;
2015-07-02 02:15:31 +09:00
}
2015-03-06 06:22:48 +09:00
// check that it is a valid/currently used data type
var validDataTypes = [
'browsingHistory' ,
'conversation' ,
'favsAndRequeets' ,
'languageData' ,
'fullQueetHtml' ,
2015-07-02 23:57:51 +09:00
'selectedLanguage' ,
2015-09-22 05:46:15 +09:00
'queetBoxInput' ,
2016-01-18 23:26:54 +09:00
'streamState' ,
'languageErrorMessageDiscarded'
2015-03-06 06:22:48 +09:00
] ;
var thisDataType = k . substring ( 0 , k . indexOf ( '-' ) ) ;
if ( $ . inArray ( thisDataType , validDataTypes ) == - 1 || k . indexOf ( '-' ) == - 1 ) {
delete localStorage [ k ] ;
deleted ++ ;
2015-07-02 02:15:31 +09:00
return true ;
}
// check that it has a modified entry, if not: add one
2015-03-06 06:22:48 +09:00
if ( typeof entryParsed . modified == 'undefined' || entryParsed . modified === null ) {
var newEntry = { } ;
newEntry . modified = dateNow - corrected ; // we want as unique dates as possible
2015-09-22 01:30:37 +09:00
newEntry . cdata = entryParsed ;
2015-03-06 06:22:48 +09:00
try {
localStorage . setItem ( k , JSON . stringify ( newEntry ) ) ;
}
catch ( e ) {
if ( e . name == 'QUOTA_EXCEEDED_ERR' || e . name == 'NS_ERROR_DOM_QUOTA_REACHED' || e . name == 'QuotaExceededError' || e . name == 'W3CException_DOM_QUOTA_EXCEEDED_ERR' ) {
removeOldestLocalStorageEntries ( function ( ) {
localStorage . setItem ( k , JSON . stringify ( newEntry ) ) ;
} ) ;
}
}
2015-09-22 01:30:37 +09:00
entryParsed = newEntry ;
2015-03-06 06:22:48 +09:00
corrected ++ ;
2015-07-02 02:15:31 +09:00
}
2015-09-22 01:30:37 +09:00
// compress uncompressed data
if ( typeof entryParsed . data != 'undefined' ) {
// but not if it's not containing any data (some bug may have saved an empty, false or null value)
if ( entryParsed . data === false || entryParsed . data === null || entryParsed . data . length == 0 ) {
delete localStorage [ k ] ;
deleted ++ ;
return true ;
}
2015-09-26 08:57:40 +09:00
var dataCompressed = LZString . compressToUTF16 ( JSON . stringify ( entryParsed . data ) ) ;
2015-09-22 01:30:37 +09:00
var newCompressedEntry = { } ;
newCompressedEntry . modified = entryParsed . modified ;
newCompressedEntry . cdata = dataCompressed ;
localStorage . setItem ( k , JSON . stringify ( newCompressedEntry ) ) ;
compressed ++ ;
}
2015-07-02 02:15:31 +09:00
}
2015-03-06 06:22:48 +09:00
} ) ;
2015-09-22 01:30:37 +09:00
console . log ( corrected + ' entries corrected, ' + deleted + ' entries deleted, ' + compressed + ' entries compressed' ) ;
2015-03-06 06:22:48 +09:00
}
2015-02-26 07:46:32 +09:00
2015-06-05 09:17:33 +09:00
2015-11-23 08:24:33 +09:00
/ * ·
·
· Checks if localstorage is availible
·
· We can 't just do if(typeof localStorage.selectedLanguage != ' undefined ' )
· because firefox with cookies disabled then freaks out and stops executing js completely
·
· · · · · · · · · * /
function localStorageIsEnabled ( ) {
var mod = 'test' ;
try {
localStorage . setItem ( mod , mod ) ;
localStorage . removeItem ( mod ) ;
return true ;
}
catch ( e ) {
if ( e . name == 'QUOTA_EXCEEDED_ERR' || e . name == 'NS_ERROR_DOM_QUOTA_REACHED' || e . name == 'QuotaExceededError' || e . name == 'W3CException_DOM_QUOTA_EXCEEDED_ERR' ) {
2016-03-04 08:22:25 +09:00
// if localstorage is empty but returns a full error, we assume it's disabled (in an ugly way)
if ( localStorage . length === 0 ) {
return false ;
}
var successfulRemoval = removeOldestLocalStorageEntries ( function ( ) {
return localStorageIsEnabled ( ) ;
2015-11-23 08:24:33 +09:00
} ) ;
2016-03-04 08:22:25 +09:00
if ( successfulRemoval === false ) {
return false ;
}
2015-11-23 08:24:33 +09:00
}
else {
return false ;
}
}
}
2016-03-04 08:22:25 +09:00
2016-02-03 00:25:34 +09:00
/ * ·
·
· Block / unblock user and do necessary stuff
·
· · · · · · · · · * /
function blockUser ( arg , callback ) {
2016-03-04 08:22:25 +09:00
$ ( 'body' ) . click ( ) ; // a click somewhere hides any open menus
2016-02-03 00:25:34 +09:00
// arguments is sent as an object, for easier use with a menu's function-row
var userId = arg . userId ;
var blockButton _jQueryElement = arg . blockButton _jQueryElement ;
display _spinner ( ) ;
APIBlockOrUnblockUser ( 'block' , userId , function ( data ) {
remove _spinner ( ) ;
// activate the button, if we were passed one
if ( typeof blockButton _jQueryElement != 'undefined' ) {
blockButton _jQueryElement . removeClass ( 'disabled' ) ;
}
if ( data && data . statusnet _blocking === true ) {
// success
markUserAsBlockedInDOM ( userId , data . following ) ;
if ( typeof callback == 'function' ) {
callback ( blockButton _jQueryElement ) ;
}
}
else {
// failed!
showErrorMessage ( window . sL . failedBlockingUser ) ;
}
} ) ;
}
function unblockUser ( arg , callback ) {
2016-03-04 08:22:25 +09:00
$ ( 'body' ) . click ( ) ; // a click somewhere hides any open menus
2016-02-03 00:25:34 +09:00
// arguments is sent as an object, for easier use with a menu's function-row
var userId = arg . userId ;
var blockButton _jQueryElement = arg . blockButton _jQueryElement ;
display _spinner ( ) ;
APIBlockOrUnblockUser ( 'unblock' , userId , function ( data ) {
remove _spinner ( ) ;
// activate the button, if we were passed one
if ( typeof blockButton _jQueryElement != 'undefined' ) {
blockButton _jQueryElement . removeClass ( 'disabled' ) ;
}
if ( data && data . statusnet _blocking === false ) {
// success
markUserAsUnblockedInDOM ( userId , data . following ) ;
if ( typeof callback == 'function' ) {
callback ( blockButton _jQueryElement ) ;
}
}
else {
// failed!
showErrorMessage ( window . sL . failedUnblockingUser ) ;
}
} ) ;
}
function markUserAsBlockedInDOM ( userId , following ) {
// display buttons accordingly
$ ( '.qvitter-follow-button[data-follow-user-id="' + userId + '"]' ) . addClass ( 'blocking' ) ;
if ( following ) {
$ ( '.qvitter-follow-button[data-follow-user-id="' + userId + '"]' ) . addClass ( 'following' ) ;
}
else {
$ ( '.qvitter-follow-button[data-follow-user-id="' + userId + '"]' ) . removeClass ( 'following' ) ;
}
// hide notices from the blocked user
$ . each ( $ ( '.stream-item[data-quitter-id-in-stream][data-user-id="' + userId + '"]' ) , function ( ) {
$ ( this ) . addClass ( 'profile-blocked-by-me' ) ;
$ ( this ) . children ( '.queet' ) . attr ( 'data-tooltip' , window . sL . thisIsANoticeFromABlockedUser ) ;
} ) ;
// add to the window.allBlocking array
if ( userIsBlocked ( userId ) === false ) {
window . allBlocking . push ( userId ) ;
}
}
function markUserAsUnblockedInDOM ( userId , following ) {
// display buttons accordingly
$ ( '.qvitter-follow-button[data-follow-user-id="' + userId + '"]' ) . removeClass ( 'blocking' ) ;
if ( following ) {
$ ( '.qvitter-follow-button[data-follow-user-id="' + userId + '"]' ) . addClass ( 'following' ) ;
}
else {
$ ( '.qvitter-follow-button[data-follow-user-id="' + userId + '"]' ) . removeClass ( 'following' ) ;
}
// hide the user from lists of blocked users
2016-03-04 08:22:25 +09:00
if ( window . currentStreamObject . name == 'user blocks' && window . currentStreamObject . nickname == window . loggedIn . screen _name ) {
$ . each ( $ ( '.stream-item.user[data-user-id="' + userId + '"]' ) , function ( ) {
slideUpAndRemoveStreamItem ( $ ( this ) ) ;
} ) ;
}
2016-02-03 00:25:34 +09:00
// unhide notices from the blocked user
$ . each ( $ ( '.stream-item[data-quitter-id-in-stream][data-user-id="' + userId + '"]' ) , function ( ) {
$ ( this ) . removeClass ( 'profile-blocked-by-me' ) ;
$ ( this ) . children ( '.queet' ) . removeAttr ( 'data-tooltip' ) ;
} ) ;
// remove from the window.allBlocking array
var existingBlockIndex = window . allBlocking . indexOf ( userId ) ;
if ( existingBlockIndex > - 1 ) {
window . allBlocking . splice ( existingBlockIndex , 1 ) ;
}
}
/ * ·
·
· Is this user id blocked ?
·
· · · · · · · · · * /
function userIsBlocked ( userId ) {
var existingBlock = window . allBlocking . indexOf ( userId ) ;
if ( existingBlock > - 1 ) {
return true ;
}
else {
return false ;
}
}
2016-03-04 08:22:25 +09:00
/ * ·
·
· Marks all notices from blocked users in an jQuery object as blocked
·
· · · · · · · · · * /
function markAllNoticesFromBlockedUsersAsBlockedInJQueryObject ( obj ) {
$ . each ( window . allBlocking , function ( ) {
obj . find ( '.stream-item[data-user-id="' + this + '"]' ) . addClass ( 'profile-blocked-by-me' ) ;
obj . find ( '.stream-item[data-user-id="' + this + '"]' ) . children ( '.queet' ) . attr ( 'data-tooltip' , window . sL . thisIsANoticeFromABlockedUser ) ;
} ) ;
}
/ * ·
·
· Marks all notices from muted users in an jQuery object as muted
·
· · · · · · · · · * /
function markAllNoticesFromMutedUsersAsMutedInJQueryObject ( obj ) {
$ . each ( obj . find ( '.stream-item' ) , function ( ) {
if ( isUserMuted ( $ ( this ) . attr ( 'data-user-id' ) ) ) {
$ ( this ) . addClass ( 'user-muted' ) ;
$ ( this ) . children ( '.queet' ) . attr ( 'data-tooltip' , window . sL . thisIsANoticeFromAMutedUser ) ;
}
else {
$ ( this ) . children ( '.queet' ) . removeAttr ( 'data-tooltip' ) ;
$ ( this ) . removeClass ( 'user-muted' ) ;
}
} ) ;
}
/ * ·
·
· Marks all profile cards from muted users as muted in DOM
·
· · · · · · · · · * /
function markAllProfileCardsFromMutedUsersAsMutedInDOM ( ) {
$ . each ( $ ( 'body' ) . find ( '.profile-header-inner' ) , function ( ) {
if ( isUserMuted ( $ ( this ) . attr ( 'data-user-id' ) ) ) {
$ ( this ) . parent ( '.profile-card' ) . addClass ( 'user-muted' ) ;
}
else {
$ ( this ) . parent ( '.profile-card' ) . removeClass ( 'user-muted' ) ;
}
} ) ;
}
/ * ·
·
· Function invoked after mute and unmute
·
· · · · · · · · · * /
function hideOrShowNoticesAfterMuteOrUnmute ( ) {
markAllNoticesFromMutedUsersAsMutedInJQueryObject ( $ ( '#feed-body' ) ) ;
markAllProfileCardsFromMutedUsersAsMutedInDOM ( ) ;
}
/ * ·
·
· Sandbox / unsandbox user and do necessary stuff
·
· · · · · · · · · * /
function sandboxCreateOrDestroy ( arg , callback ) {
$ ( 'body' ) . click ( ) ; // a click somewhere hides any open menus
display _spinner ( ) ;
APISandboxCreateOrDestroy ( arg . createOrDestroy , arg . userId , function ( data ) {
remove _spinner ( ) ;
if ( ! data ) {
// failed!
showErrorMessage ( window . sL . ERRORfailedSandboxingUser ) ;
}
} ) ;
}
/ * ·
·
· Sandbox / unsandbox user and do necessary stuff
·
· · · · · · · · · * /
function silenceCreateOrDestroy ( arg , callback ) {
$ ( 'body' ) . click ( ) ; // a click somewhere hides any open menus
display _spinner ( ) ;
APISilenceCreateOrDestroy ( arg . createOrDestroy , arg . userId , function ( data ) {
remove _spinner ( ) ;
if ( ! data ) {
// failed!
showErrorMessage ( window . sL . ERRORfailedSilencingUser ) ;
}
} ) ;
}
/ * ·
·
· Get the logged in user ' s menu array
·
· · · · · · · · · * /
function loggedInUsersMenuArray ( ) {
return [
{
type : 'function' ,
functionName : 'goToEditProfile' ,
label : window . sL . editMyProfile
} ,
{
type : 'link' ,
href : window . siteInstanceURL + 'settings/profile' ,
label : window . sL . settings
} ,
{
type : 'divider'
} ,
{
type : 'link' ,
href : window . siteInstanceURL + window . loggedIn . screen _name + '/mutes' ,
label : window . sL . userMuted
} ,
{
type : 'link' ,
href : window . siteInstanceURL + window . loggedIn . screen _name + '/blocks' ,
label : window . sL . userBlocked
} ] ;
}
/ * ·
·
· Append moderator user actions to menu array
·
· @ param menuArray : array used to build menus in getMenu ( )
· @ param userID : the user id of the user to act on
· @ param userScreenName : the screen _name / nickname / username of the user to act on
· @ param sandboxed : is the user sandboxed ?
· @ param silenced : is the user silenced ?
·
· · · · · · · · · * /
function appendModeratorUserActionsToMenuArray ( menuArray , userID , userScreenName , sandboxed , silenced ) {
// not if it's me
if ( window . loggedIn . id == userID ) {
return menuArray ;
}
if ( window . loggedIn !== false && window . loggedIn . rights . sandbox === true ) {
menuArray . push ( { type : 'divider' } ) ;
if ( sandboxed === true ) {
menuArray . push ( {
type : 'function' ,
functionName : 'sandboxCreateOrDestroy' ,
functionArguments : {
userId : userID ,
createOrDestroy : 'destroy'
} ,
label : window . sL . unSandboxThisUser . replace ( '{nickname}' , '@' + userScreenName )
} ) ;
}
else {
menuArray . push ( {
type : 'function' ,
functionName : 'sandboxCreateOrDestroy' ,
functionArguments : {
userId : userID ,
createOrDestroy : 'create'
} ,
label : window . sL . sandboxThisUser . replace ( '{nickname}' , '@' + userScreenName )
} ) ;
}
}
if ( window . loggedIn !== false && window . loggedIn . rights . silence === true ) {
if ( silenced === true ) {
menuArray . push ( {
type : 'function' ,
functionName : 'silenceCreateOrDestroy' ,
functionArguments : {
userId : userID ,
createOrDestroy : 'destroy'
} ,
label : window . sL . unSilenceThisUser . replace ( '{nickname}' , '@' + userScreenName )
} ) ;
}
else {
menuArray . push ( {
type : 'function' ,
functionName : 'silenceCreateOrDestroy' ,
functionArguments : {
userId : userID ,
createOrDestroy : 'create'
} ,
label : window . sL . silenceThisUser . replace ( '{nickname}' , '@' + userScreenName )
} ) ;
}
}
return menuArray ;
}
2016-02-03 00:25:34 +09:00
2015-12-22 21:36:44 +09:00
/ * ·
·
· Updates the times for all queets loaded to DOM
·
· · · · · · · · · * /
function updateAllQueetsTimes ( ) {
$ ( '[data-created-at]' ) . each ( function ( ) {
// if the element with the data-created-at doesn't have an a-child, we change the html of the element
if ( $ ( this ) . children ( 'a' ) . length == 0 ) {
$ ( this ) . html ( parseTwitterDate ( $ ( this ) . attr ( 'data-created-at' ) ) ) ;
}
// otherwise the change the child's html
else {
$ ( this ) . children ( 'a' ) . html ( parseTwitterDate ( $ ( this ) . attr ( 'data-created-at' ) ) ) ;
}
} ) ;
}
2015-12-11 01:36:28 +09:00
/ * ·
·
· Is this a local URL ?
·
· · · · · · · · · * /
function isLocalURL ( url ) {
if ( url . substring ( 0 , window . siteInstanceURL . length ) == window . siteInstanceURL ) {
return true ;
}
else {
return false ;
}
}
2015-11-23 08:24:33 +09:00
/ * ·
·
· Check for hidden items and show the new queets bar if there are any
·
· · · · · · · · · * /
function maybeShowTheNewQueetsBar ( ) {
2016-03-04 08:22:25 +09:00
var newQueetsNum = $ ( '#feed-body' ) . find ( '.stream-item.hidden:not(.always-hidden):not(.hidden-repeat)' ) . length ;
// subtract the number of hidden notices from muted users if this isn't the notifications stream,
// or if this is the notifications stream but the user has opted out of seeing notifications from muted users
var mutedHiddenNum = 0 ;
if ( window . currentStreamObject . name == 'notifications' ) {
if ( $ ( '#feed-body' ) . hasClass ( 'hide-notifications-from-muted-users' ) ) {
mutedHiddenNum = $ ( '#feed-body' ) . find ( '.stream-item.hidden.user-muted:not(.always-hidden):not(.hidden-repeat)' ) . length ;
}
}
else {
var mutedHiddenNum = $ ( '#feed-body' ) . find ( '.stream-item.hidden.user-muted:not(.always-hidden):not(.hidden-repeat)' ) . length ;
}
newQueetsNum = newQueetsNum - mutedHiddenNum ;
if ( newQueetsNum > 0 ) {
2015-11-23 08:24:33 +09:00
$ ( '#new-queets-bar' ) . parent ( ) . removeClass ( 'hidden' ) ;
// bar label
2016-03-04 08:22:25 +09:00
if ( newQueetsNum == 1 ) { var q _txt = window . sL . newQueet ; }
2015-11-23 08:24:33 +09:00
else { var q _txt = window . sL . newQueets ; }
if ( window . currentStreamObject . name == 'notifications' ) {
2016-03-04 08:22:25 +09:00
if ( newQueetsNum == 1 ) { var q _txt = window . sL . newNotification ; }
2015-11-23 08:24:33 +09:00
else { var q _txt = window . sL . newNotifications ; }
}
2016-03-04 08:22:25 +09:00
$ ( '#new-queets-bar' ) . html ( q _txt . replace ( '{new-notice-count}' , newQueetsNum ) ) ;
2015-11-23 08:24:33 +09:00
}
}
2015-09-15 01:04:27 +09:00
/ * ·
·
2015-09-18 08:42:52 +09:00
· Align tooltips to the hovered element
2015-09-15 01:04:27 +09:00
·
· · · · · · · · · * /
2015-09-18 08:42:52 +09:00
function alignTooltipTohoveredElement ( tooltipElement , tooltipCaret , hovered ) {
2015-09-15 01:04:27 +09:00
var tooltipWidth = tooltipElement . outerWidth ( ) ;
var tooltipHeight = tooltipElement . outerHeight ( ) ;
var windowWidth = $ ( window ) . width ( ) ;
var windowScrollPosY = $ ( window ) . scrollTop ( ) ;
2015-09-18 08:42:52 +09:00
var targetPosX = hovered . offset ( ) . left ;
var targetPosY = hovered . offset ( ) . top ;
var targetHeight = hovered . outerHeight ( ) ;
var targetWidth = hovered . outerWidth ( ) ;
2015-09-15 01:04:27 +09:00
// too little space on top of element, show tooltip at bottom
if ( ( targetPosY - windowScrollPosY - tooltipHeight - 10 ) < 0 ) {
var tooltipCaretPosX = targetPosX + targetWidth / 2 - 5 ;
var tooltipCaretPosY = targetPosY + targetHeight + 2 ;
// caret always directly under element
tooltipCaret . css ( 'left' , tooltipCaretPosX + 'px' ) ;
tooltipCaret . css ( 'top' , tooltipCaretPosY + 'px' ) ;
tooltipCaret . addClass ( 'top' ) ;
// tooltip itself might bleed over the window edges, and need moving
var tooltipPosX = targetPosX + targetWidth / 2 - tooltipWidth / 2 ;
var tooltipPosY = targetPosY + targetHeight + 7 ;
if ( ( tooltipPosX + tooltipWidth ) > windowWidth ) {
tooltipPosX = windowWidth - tooltipWidth - 5 ;
}
if ( tooltipPosX < 5 ) {
tooltipPosX = 5 ;
}
tooltipElement . css ( 'left' , tooltipPosX + 'px' ) ;
tooltipElement . css ( 'top' , tooltipPosY + 'px' ) ;
}
// tooltip at top
else {
var tooltipCaretPosX = targetPosX + targetWidth / 2 - 5 ;
2016-01-25 06:03:33 +09:00
var tooltipCaretPosY = targetPosY - 8 ;
2015-09-15 01:04:27 +09:00
// caret always directly on top of element
tooltipCaret . css ( 'left' , tooltipCaretPosX + 'px' ) ;
tooltipCaret . css ( 'top' , tooltipCaretPosY + 'px' ) ;
tooltipCaret . addClass ( 'bottom' ) ;
// tooltip itself might bleed over the window edges, and need moving
var tooltipPosX = targetPosX + targetWidth / 2 - tooltipWidth / 2 ;
var tooltipPosY = targetPosY - 7 - tooltipHeight ;
if ( ( tooltipPosX + tooltipWidth ) > windowWidth ) {
tooltipPosX = windowWidth - tooltipWidth - 5 ;
}
if ( tooltipPosX < 5 ) {
tooltipPosX = 5 ;
}
tooltipElement . css ( 'left' , tooltipPosX + 'px' ) ;
tooltipElement . css ( 'top' , tooltipPosY + 'px' ) ;
}
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-06-21 08:29:14 +09:00
· Cache the unicode compatible regexps for the syntax highlighting
2015-07-02 02:15:31 +09:00
·
2015-06-21 08:29:14 +09:00
· · · · · · · · · * /
function cacheSyntaxHighlighting ( ) {
window . syntaxHighlightingRegexps = Object ( ) ;
2015-08-26 06:27:08 +09:00
var allDomains = ' ( abb | abbott | abogado | ac | academy | accenture | accountant | accountants | active | actor | ad | ads | adult | ae | aero | af | afl | ag | agency | ai | aig | airforce | al | allfinanz | alsace | am | amsterdam | an | android | ao | apartments | aq | aquarelle | ar | archi | army | arpa | as | asia | associates | at | attorney | au | auction | audio | auto | autos | aw | ax | axa | az | ba | band | bank | bar | barclaycard | barclays | bargains | bauhaus | bayern | bb | bbc | bbva | bd | be | beer | berlin | best | bf | bg | bh | bi | bible | bid | bike | bingo | bio | biz | bj | bl | black | blackfriday | bloomberg | blue | bm | bmw | bn | bnpparibas | bo | boats | bond | boo | boutique | bq | br | bridgestone | broker | brother | brussels | bs | bt | budapest | build | builders | business | buzz | bv | bw | by | bz | bzh | ca | cab | cafe | cal | camera | camp | cancerresearch | canon | capetown | capital | caravan | cards | care | career | careers | cars | cartier | casa | cash | casino | cat | catering | cbn | cc | cd | center | ceo | cern | cf | cfa | cfd | cg | ch | channel | chat | cheap | chloe | christmas | chrome | church | ci | cisco | citic | city | ck | cl | claims | cleaning | click | clinic | clothing | club | cm | cn | co | coach | codes | coffee | college | cologne | com | community | company | computer | condos | construction | consulting | contractors | cooking | cool | coop | corsica | country | coupons | courses | cr | credit | creditcard | cricket | crs | cruises | cu | cuisinella | cv | cw | cx | cy | cymru | cyou | cz | dabur | dad | dance | date | dating | datsun | day | dclk | de | deals | degree | delivery | democrat | dental | dentist | desi | design | dev | diamonds | diet | digital | direct | directory | discount | dj | dk | dm | dnp | do | docs | dog | doha | domains | doosan | download | durban | dvag | dz | earth | eat | ec | edu | education | ee | eg | eh | email | emerck | energy | engineer | engineering | enterprises | epson | equipment | er | erni | es | esq | estate | et | eu | eurovision | eus | events | everbank | exchange | expert | exposed | express | fail | faith | fan | fans | farm | fashion | feedback | fi | film | finance | financial | firmdale | fish | fishing | fit | fitness | fj | fk | flights | florist | flowers | flsmidth | fly | fm | fo | foo | football | forex | forsale | foundation | fr | frl | frogans | fund | furniture | futbol | fyi | ga | gal | gallery | garden | gb | gbiz | gd | gdn | ge | gent | gf | gg | ggee | gh | gi | gift | gifts | gives | gl | glass | gle | global | globo | gm | gmail | gmo | gmx | gn | gold | goldpoint | golf | goo | goog | google | gop | gov | gp | gq | gr | graphics | gratis | green | gripe | gs | gt | gu | guge | guide | guitars | guru | gw | gy | hamburg | hangout | haus | healthcare | help | here | hermes | hiphop | hitachi | hiv | hk | hm | hn | hockey | holdings | holiday | homedepot | homes | honda | horse | host | hosting | house | how | hr | ht | hu | ibm | icbc | icu | id | ie | ifm | il | im | immo | immobilien | in | industries | infiniti | info | ing | ink | institute | insure | int | international | investments | io | iq | ir | irish | is | it | iwc | java | jcb | je | jetzt | jewelry | jll | jm | jo | jobs | joburg | jp | juegos | kaufen | kddi | ke | kg | kh | ki | kim | kitchen | kiwi | km | kn | koeln | komatsu | kp | kr | krd | kred | kw | ky | kyoto | kz | la | lacaixa | land | lat | latrobe | lawyer | lb | lc | lds | lease | leclerc | legal | lgbt | li | liaison | lidl | life | lighting | limited | limo | link | lk | loan | loans | lol | london | lotte | lotto | love | lr | ls | lt | ltda | lu | lupin | luxe | luxury | lv | ly | ma | madrid | maif | maison | management | mango | market | marketing | markets | marriott | mba | mc | md | me | media | meet | melbourne | meme | memorial | men | menu | mf | mg | mh | miami | mil | mini | mk | ml | mm | mma | mn | mo | mobi | moda | moe | monash | money | montblanc | mormon | mortgage | moscow | motorcycles | mov | movie | mp | mq | mr | ms | mt | mtn | mtpc | mu | museum | mv | mw | mx | my | mz | na | nadex | nagoya | name | navy | nc | ne | nec | net | network | neustar | new | news | nexus | nf | ng | ngo | nhk | ni | nico | ninja | nissan | nl | no | np | nr | nra | nrw | ntt | nu | nyc | nz | okinawa | om | one | ong | onl | online | ooo | org | organic | osaka | otsuka | ovh | pa | page | panerai | paris | partners | parts | party | pe | pf | pg | ph | pharmacy | philips | photo | photography | photos | physio | piaget | pics | pictet | pictures | pink | pizza | pk | pl | place | plumbing | plus | pm | pn | pohl | poker | porn | post | pr | praxi | press | pro | prod | productions | prof | properties | property | ps | pt | pub | pw | py | qa | qpon | quebec | racing | re | realtor | recipes | red | redstone | rehab | reise | reisen | reit | ren | rent | rentals | repair | report | republican | rest | restaurant | review | reviews | rich | rio | rip | ro | rocks | rodeo | rs | rsvp | ru | ruhr | run | rw | ryukyu | sa | saarland | sale | samsung | sandvik | sandvikcoromant | sap | sarl | saxo | sb | sc | sca | scb | schmidt | scholarships | school | schule | schwarz | science | scot | sd | se | seat | sener | services | sew | sex | sexy | sg | sh | shiksha | shoes | show | shriram | si | singles | site | sj | sk | ski | sky | sl | sm | sn | sncf | so | soccer | social | software | sohu | solar | solutions | sony | soy | space | spiegel | spreadbetting | sr | ss | st | study | style
2016-02-25 19:10:55 +09:00
window . syntaxHighlightingRegexps . externalMention = XRegExp . cache ( '(^|\\s|\\.|<br>| |\\()(@)[a-zA-Z0-9]+(@)[\\p{L}\\p{N}\\-\\.]+(\\.)(' + allDomains + ')($|\\s|\\.|\\,|\\:|\\-|\\<|\\!|\\?|\\&|\\)|\\\')' ) ;
window . syntaxHighlightingRegexps . mention = /(^|\s|\.|<br>| |\()(@)[a-zA-Z0-9]+($|\s|\.|\,|\:|\-|\<|\!|\?|\&|\)|\')/ ;
window . syntaxHighlightingRegexps . tag = XRegExp . cache ( '(^|\\s|\\.|<br>| |\\()(\\#)[\\p{L}\\p{N}\\-\\.]+($|\\s|\\,|\\:|\\<|\\!|\\?|\\&|\\)|\\\')' ) ;
window . syntaxHighlightingRegexps . url = XRegExp . cache ( '(^|\\s|\\.|<br>| |\\()(http\\:\\/\\/|https\:\\/\\/)([\\p{L}\\p{N}\\-\\.]+)?(\\.)(' + allDomains + ')(\\/[\\p{L}\\p{N}\\%\\!\\*\\\'\\(\\)\\;\\:\\@\\&\\=\\+\\$\\,\\/\\?\\#\\[\\]\\-\\_\\.\\~]+)?(\\/)?($|\\s|\\,|\\:|\\-|\\<|\\!|\\?|\\&|\\)|\\\')' ) ;
window . syntaxHighlightingRegexps . urlWithoutProtocol = XRegExp . cache ( '(^|\\s|\\.|<br>| |\\()[\\p{L}\\p{N}\\-\\.]+(\\.)(' + allDomains + ')(\\/[\\p{L}\\p{N}\\%\\!\\*\\\'\\(\\)\\;\\:\\@\\&\\=\\+\\$\\,\\/\\?\\#\\[\\]\\-\\_\\.\\~]+)?(\\/)?($|\\s|\\.|\\,|\\:|\\-|\\<|\\!|\\?|\\&|\\)|\\\')' ) ;
window . syntaxHighlightingRegexps . email = XRegExp . cache ( '(^|\\s|\\.|<br>| |\\()([a-zA-Z0-9\\!\\#\\$\\%\\&\\\'\\*\\+\\-\\/\\=\\?\\^\\_\\`\\{\\|\\}\\~\\.]+)?(@)[\\p{L}\\p{N}\\-\\.]+(\\.)(' + allDomains + ')($|\\s|\\.|\\,|\\:|\\-|\\<|\\!|\\?|\\&|\\)|\\\')' ) ;
2015-08-26 06:27:08 +09:00
cacheSyntaxHighlightingGroups ( ) ;
}
/ * ·
·
· Cache syntax highlighting for groups
·
· · · · · · · · · * /
function cacheSyntaxHighlightingGroups ( ) {
if ( window . groupNicknamesAndLocalAliases . length > 0 ) {
var allGroupNicknamesAndLocalAliases = '(' + window . groupNicknamesAndLocalAliases . join ( '|' ) + ')' ;
2016-02-25 19:10:55 +09:00
window . syntaxHighlightingRegexps . group = XRegExp . cache ( '(^|\\s|\\.|<br>| |\\()(\\!)' + allGroupNicknamesAndLocalAliases + '($|\\s|\\.|\\,|\\:|\\-|\\<|\\!|\\?|\\&|\\)|\\\')' ) ;
2015-08-26 06:27:08 +09:00
}
2015-06-21 08:29:14 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2016-03-05 22:51:01 +09:00
· User array cache ( called array because it ' s an array in php )
2015-07-02 02:15:31 +09:00
·
2015-12-04 00:06:02 +09:00
· Stored in window . userArrayCache with unique key like instance _url / nickname
2015-06-05 09:17:33 +09:00
· with protocol ( http : // or https://) trimmed off, e.g. "quitter.se/hannes2peer"
2015-07-02 02:15:31 +09:00
·
2015-06-05 09:17:33 +09:00
· · · · · · · · · * /
window . userArrayCache = new Object ( ) ;
2015-09-15 00:49:06 +09:00
window . convertUriToUserArrayCacheKey = new Object ( ) ;
2015-09-15 10:58:11 +09:00
window . convertStatusnetProfileUrlToUserArrayCacheKey = new Object ( ) ;
2015-06-05 09:17:33 +09:00
function userArrayCacheStore ( data ) {
if ( typeof data == 'undefined' ) {
return false ;
}
2015-07-02 02:15:31 +09:00
2015-06-05 09:17:33 +09:00
// if we are passed a data object with both local and external data, use external data as key
2015-07-02 02:15:31 +09:00
if ( typeof data . local != 'undefined'
&& typeof data . local . statusnet _profile _url != 'undefined'
&& typeof data . external != 'undefined'
2015-06-05 09:17:33 +09:00
&& typeof data . external . statusnet _profile _url != 'undefined' ) {
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( data . external . statusnet _profile _url , data . external . screen _name ) ;
2015-07-02 02:15:31 +09:00
var key = instanceUrlWithoutProtocol + '/' + data . external . screen _name ;
2015-06-05 09:17:33 +09:00
var dataToStore = data ;
}
// we can also get either local...
else if ( typeof data . local != 'undefined' && typeof data . local . statusnet _profile _url != 'undefined' ) {
2015-09-15 00:49:06 +09:00
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( data . local . statusnet _profile _url , data . local . screen _name ) ;
var key = instanceUrlWithoutProtocol + '/' + data . local . screen _name ;
2015-06-05 09:17:33 +09:00
data . external = false ;
var dataToStore = data ;
}
// ...or external...
else if ( typeof data . external != 'undefined' && typeof data . external . statusnet _profile _url != 'undefined' ) {
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( data . external . statusnet _profile _url , data . external . screen _name ) ;
2015-07-02 02:15:31 +09:00
var key = instanceUrlWithoutProtocol + '/' + data . external . screen _name ;
2015-06-05 09:17:33 +09:00
data . local = false ;
var dataToStore = data ;
}
// ...or an unspecified data object, in which case we check the avatar urls to see if it's local or external
else if ( typeof data . statusnet _profile _url != 'undefined' ) {
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( data . statusnet _profile _url , data . screen _name ) ;
2015-07-02 02:15:31 +09:00
var key = instanceUrlWithoutProtocol + '/' + data . screen _name ;
2015-12-04 05:41:00 +09:00
var localOrExternal = detectLocalOrExternalUserObject ( data ) ;
2015-07-02 02:15:31 +09:00
2015-06-05 09:17:33 +09:00
// local
2015-12-04 05:41:00 +09:00
if ( localOrExternal == 'local' ) {
2015-06-05 09:17:33 +09:00
var dataToStore = { local : data , external : false } ;
}
// external
else {
2015-07-02 02:15:31 +09:00
var dataToStore = { external : data , local : false } ;
2015-06-05 09:17:33 +09:00
}
}
else {
return false ;
}
2015-07-02 02:15:31 +09:00
2015-06-05 09:17:33 +09:00
// store
if ( typeof window . userArrayCache [ key ] == 'undefined' ) {
2015-07-02 02:15:31 +09:00
window . userArrayCache [ key ] = dataToStore ;
2015-12-04 05:41:00 +09:00
window . userArrayCache [ key ] . modified = Date . now ( ) ;
2015-09-17 05:41:48 +09:00
// easy conversion between URI and statusnet_profile_url and the key we're using in window.userArrayCache
window . convertUriToUserArrayCacheKey [ dataToStore . local . ostatus _uri ] = key ;
window . convertStatusnetProfileUrlToUserArrayCacheKey [ dataToStore . local . statusnet _profile _url ] = key ;
2015-06-05 09:17:33 +09:00
}
else {
if ( dataToStore . local ) {
2015-07-02 02:15:31 +09:00
2015-06-05 09:17:33 +09:00
// keep old status if newer data doesn't have any
if ( typeof dataToStore . local . status == 'undefined' && typeof window . userArrayCache [ key ] . local . status != 'undefined' ) {
dataToStore . local . status = window . userArrayCache [ key ] . local . status ;
}
2015-07-02 02:15:31 +09:00
window . userArrayCache [ key ] . local = dataToStore . local ;
2015-09-15 00:49:06 +09:00
2015-09-15 10:58:11 +09:00
// easy conversion between URI and statusnet_profile_url and the key we're using in window.userArrayCache
2015-09-15 00:49:06 +09:00
window . convertUriToUserArrayCacheKey [ dataToStore . local . ostatus _uri ] = key ;
2015-09-15 10:58:11 +09:00
window . convertStatusnetProfileUrlToUserArrayCacheKey [ dataToStore . local . statusnet _profile _url ] = key ;
2015-06-05 09:17:33 +09:00
}
if ( dataToStore . external ) {
2015-07-02 02:15:31 +09:00
window . userArrayCache [ key ] . external = dataToStore . external ;
2015-09-15 00:49:06 +09:00
// easy conversion between URI and the key we're using in window.userArrayCache
window . convertUriToUserArrayCacheKey [ dataToStore . external . ostatus _uri ] = key ;
2015-09-15 10:58:11 +09:00
window . convertStatusnetProfileUrlToUserArrayCacheKey [ dataToStore . external . statusnet _profile _url ] = key ;
2015-07-02 02:15:31 +09:00
}
2015-12-04 05:41:00 +09:00
// store the time when this record was modified
if ( dataToStore . local || dataToStore . external ) {
window . userArrayCache [ key ] . modified = Date . now ( ) ;
}
2015-06-05 09:17:33 +09:00
}
}
2015-07-02 02:15:31 +09:00
2015-06-05 09:17:33 +09:00
function userArrayCacheGetByLocalNickname ( localNickname ) {
2015-09-15 10:58:11 +09:00
if ( localNickname . substring ( 0 , 1 ) == '@' ) {
localNickname = localNickname . substring ( 1 ) ;
}
2015-06-05 09:17:33 +09:00
if ( typeof window . userArrayCache [ window . siteRootDomain + '/' + localNickname ] != 'undefined' ) {
return window . userArrayCache [ window . siteRootDomain + '/' + localNickname ] ;
}
else {
return false ;
}
}
2015-07-02 02:15:31 +09:00
2015-06-05 09:17:33 +09:00
function userArrayCacheGetByProfileUrlAndNickname ( profileUrl , nickname ) {
2015-09-15 10:58:11 +09:00
if ( nickname . substring ( 0 , 1 ) == '@' ) {
nickname = nickname . substring ( 1 ) ;
2015-06-05 09:17:33 +09:00
}
2015-09-15 10:58:11 +09:00
// the url might match a known profile uri
if ( typeof window . convertUriToUserArrayCacheKey [ profileUrl ] != 'undefined' ) {
if ( typeof window . userArrayCache [ window . convertUriToUserArrayCacheKey [ profileUrl ] ] != 'undefined' ) {
return window . userArrayCache [ window . convertUriToUserArrayCacheKey [ profileUrl ] ] ;
}
}
// or the href attribute might match a known statusnet_profile_url
else if ( typeof window . convertStatusnetProfileUrlToUserArrayCacheKey [ profileUrl ] != 'undefined' ) {
if ( typeof window . userArrayCache [ window . convertStatusnetProfileUrlToUserArrayCacheKey [ profileUrl ] ] != 'undefined' ) {
return window . userArrayCache [ window . convertStatusnetProfileUrlToUserArrayCacheKey [ profileUrl ] ] ;
}
}
// or we try to guess the instance url, and see if we have a match in our cache
else if ( typeof window . userArrayCache [ guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( profileUrl , nickname ) + '/' + nickname ] != 'undefined' ) {
return window . userArrayCache [ guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( profileUrl , nickname ) + '/' + nickname ] ;
}
// we couldn't find any cached user array
2015-06-05 09:17:33 +09:00
else {
2015-09-15 10:58:11 +09:00
return false ;
2015-06-05 09:17:33 +09:00
}
2015-07-02 02:15:31 +09:00
}
2015-06-05 09:17:33 +09:00
2015-09-18 08:42:52 +09:00
function userArrayCacheGetUserNicknameById ( id ) {
var possibleUserURI = window . siteInstanceURL + 'user/' + id ;
var key = window . convertUriToUserArrayCacheKey [ possibleUserURI ] ;
if ( typeof key != 'undefined' ) {
if ( typeof window . userArrayCache [ key ] != 'undefined' ) {
return window . userArrayCache [ key ] . local . screen _name ;
}
}
return false ;
}
2015-06-05 09:17:33 +09:00
2015-12-04 05:41:00 +09:00
/ * ·
·
· Detect if the supplied user object is from the local server or external
·
· · · · · · · · · * /
function detectLocalOrExternalUserObject ( userObject ) {
var dataProfileImageUrlWithoutProtocol = removeProtocolFromUrl ( userObject . profile _image _url ) ;
var siteInstanceURLWithoutProtocol = removeProtocolFromUrl ( window . siteInstanceURL ) ;
if ( dataProfileImageUrlWithoutProtocol . substring ( 0 , siteInstanceURLWithoutProtocol . length ) == siteInstanceURLWithoutProtocol ) {
return 'local' ;
}
else {
return 'external' ;
}
}
2015-06-05 09:17:33 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-06-05 09:17:33 +09:00
· Guess instance ' s base installation url without protocol from a profile url
2015-07-02 02:15:31 +09:00
·
2015-06-05 09:17:33 +09:00
· · · · · · · · · * /
function guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( profileUrl , nickname ) {
// remove protocol
var guessedInstanceUrl = removeProtocolFromUrl ( profileUrl )
// user/id-style profile urls
if ( guessedInstanceUrl . indexOf ( '/user/' ) > - 1 &&
2015-07-02 02:15:31 +09:00
$ . isNumeric ( guessedInstanceUrl . substring ( guessedInstanceUrl . lastIndexOf ( '/user/' ) + 6 ) ) ) {
2015-06-05 09:17:33 +09:00
guessedInstanceUrl = guessedInstanceUrl . substring ( 0 , guessedInstanceUrl . lastIndexOf ( '/user/' ) ) ;
}
2015-07-02 02:15:31 +09:00
2015-06-05 09:17:33 +09:00
// nickname-style profile urls
else if ( guessedInstanceUrl . substring ( guessedInstanceUrl . lastIndexOf ( '/' ) + 1 ) == nickname ) {
guessedInstanceUrl = guessedInstanceUrl . substring ( 0 , guessedInstanceUrl . lastIndexOf ( '/' ) ) ;
}
2015-07-02 02:15:31 +09:00
2015-06-05 09:17:33 +09:00
// remove trailing "index.php" if the instance doesn't use mod_rewrite
if ( guessedInstanceUrl . substring ( guessedInstanceUrl . lastIndexOf ( '/' ) ) == '/index.php' ) {
2015-07-02 02:15:31 +09:00
guessedInstanceUrl = guessedInstanceUrl . substring ( 0 , guessedInstanceUrl . lastIndexOf ( '/' ) ) ;
}
2015-06-05 09:17:33 +09:00
// there was a bug once that made some instances have multiple /:s in their url,
// so make sure there's no trailing /:s
while ( guessedInstanceUrl . slice ( - 1 ) == '/' ) {
guessedInstanceUrl = guessedInstanceUrl . slice ( 0 , - 1 ) ;
}
return guessedInstanceUrl ;
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-06-05 09:17:33 +09:00
· Remove the protocol ( e . g . "http://" ) from an URL
2015-07-02 02:15:31 +09:00
·
2015-06-05 09:17:33 +09:00
· · · · · · · · · * /
function removeProtocolFromUrl ( url ) {
2016-01-21 01:39:34 +09:00
if ( typeof url == 'undefined'
|| url === null
|| url === false
|| url == '' ) {
return '' ;
}
2015-06-05 09:17:33 +09:00
if ( url . indexOf ( '://' ) == - 1 ) {
return url ;
}
return url . substring ( url . indexOf ( '://' ) + 3 ) ;
}
2016-02-25 19:10:55 +09:00
/ * ·
·
· Get host from URL
·
· · · · · · · · · * /
function getHost ( url ) {
var a = document . createElement ( 'a' ) ;
a . href = url ;
return a . hostname ;
}
2015-06-05 09:17:33 +09:00
2016-01-19 03:30:29 +09:00
/ * ·
·
· Is this url a link to my profile ?
·
· · · · · · · · · * /
function thisIsALinkToMyProfile ( url ) {
if ( typeof url == 'undefined' ) {
return false ;
}
if ( ! window . loggedIn ) {
return false ;
}
if ( url . slice ( - 1 ) == '/' ) { // remove trailing '/'
url = url . slice ( 0 , - 1 ) ;
}
var urlWithoutProtocol = removeProtocolFromUrl ( url ) ;
if ( removeProtocolFromUrl ( window . loggedIn . statusnet _profile _url ) == urlWithoutProtocol ) {
return true ;
}
var userIdUrlWithoutProtocol = removeProtocolFromUrl ( window . siteInstanceURL ) + 'user/' + window . loggedIn . id ;
if ( userIdUrlWithoutProtocol == urlWithoutProtocol ) {
return true ;
}
return false ;
}
2015-06-05 09:17:33 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-06-05 09:17:33 +09:00
· Iterates recursively through an API response in search for user data to cache
2015-07-02 02:15:31 +09:00
· If we find a "statusnet_profile_url" key we assume the parent is a user array / object
·
· · · · · · · · · · · · · * /
2015-06-05 09:17:33 +09:00
function searchForUserDataToCache ( obj ) {
for ( var property in obj ) {
if ( obj . hasOwnProperty ( property ) ) {
if ( typeof obj [ property ] == "object" ) {
searchForUserDataToCache ( obj [ property ] ) ;
}
else if ( typeof obj [ property ] == 'string' && property == 'statusnet_profile_url' ) {
userArrayCacheStore ( obj ) ;
}
}
2015-07-02 02:15:31 +09:00
}
2015-06-05 09:17:33 +09:00
}
2015-12-04 05:41:00 +09:00
/ * ·
·
· Updates user data loaded into the stream with the latest data from the user array cache
· This function should therefor always be invoked _after _ searchForUserDataToCache ( )
·
· · · · · · · · · · · · · * /
function updateUserDataInStream ( ) {
var timeNow = Date . now ( ) ;
$ . each ( window . userArrayCache , function ( k , userArray ) {
// if the cache record was updated the latest second, we assume this is brand new info that we haven't
// updated the stream with
if ( typeof userArray . local != 'undefined'
&& userArray . local !== false
&& typeof userArray . modified != 'undefined'
&& ( timeNow - userArray . modified ) < 1000 ) {
2016-03-01 00:08:06 +09:00
// add/remove silenced class to stream items and profile cards
if ( userArray . local . is _silenced === true ) {
$ ( '.stream-item[data-user-id=' + userArray . local . id + ']' ) . addClass ( 'silenced' ) ;
$ ( '.profile-card .profile-header-inner[data-user-id=' + userArray . local . id + ']' ) . addClass ( 'silenced' ) ;
2016-03-04 08:22:25 +09:00
$ ( '.user-menu-cog[data-user-id=' + userArray . local . id + ']' ) . addClass ( 'silenced' ) ;
2016-03-01 00:08:06 +09:00
}
else {
$ ( '.stream-item[data-user-id=' + userArray . local . id + ']' ) . removeClass ( 'silenced' )
$ ( '.profile-card .profile-header-inner[data-user-id=' + userArray . local . id + ']' ) . removeClass ( 'silenced' ) ;
2016-03-04 08:22:25 +09:00
$ ( '.user-menu-cog[data-user-id=' + userArray . local . id + ']' ) . removeClass ( 'silenced' ) ;
}
// add/remove sandboxed class to stream items and profile cards
if ( userArray . local . is _sandboxed === true ) {
$ ( '.stream-item[data-user-id=' + userArray . local . id + ']' ) . addClass ( 'sandboxed' ) ;
$ ( '.profile-card .profile-header-inner[data-user-id=' + userArray . local . id + ']' ) . addClass ( 'sandboxed' ) ;
$ ( '.user-menu-cog[data-user-id=' + userArray . local . id + ']' ) . addClass ( 'sandboxed' ) ;
}
else {
$ ( '.stream-item[data-user-id=' + userArray . local . id + ']' ) . removeClass ( 'sandboxed' )
$ ( '.profile-card .profile-header-inner[data-user-id=' + userArray . local . id + ']' ) . removeClass ( 'sandboxed' ) ;
$ ( '.user-menu-cog[data-user-id=' + userArray . local . id + ']' ) . removeClass ( 'sandboxed' ) ;
2016-03-01 00:08:06 +09:00
}
2015-12-04 05:41:00 +09:00
// profile size avatars (notices, users)
$ . each ( $ ( 'img.avatar.profile-size[data-user-id="' + userArray . local . id + '"]' ) , function ( ) {
if ( $ ( this ) . attr ( 'src' ) != userArray . local . profile _image _url _profile _size ) {
$ ( this ) . attr ( 'src' , userArray . local . profile _image _url _profile _size ) ;
}
} ) ;
// standard size avatars (notifications)
$ . each ( $ ( 'img.avatar.standard-size[data-user-id="' + userArray . local . id + '"]' ) , function ( ) {
if ( $ ( this ) . attr ( 'src' ) != userArray . local . profile _image _url ) {
$ ( this ) . attr ( 'src' , userArray . local . profile _image _url ) ;
}
} ) ;
// full names
$ . each ( $ ( 'strong.name[data-user-id="' + userArray . local . id + ' " ] , \
. fullname [ data - user - id = "' + userArray.local.id + '" ] ' ) , function ( ) {
if ( $ ( this ) . html ( ) != userArray . local . name ) {
$ ( this ) . html ( userArray . local . name ) ;
}
} ) ;
// user/screen names
$ . each ( $ ( '.screen-name[data-user-id="' + userArray . local . id + '"]' ) , function ( ) {
if ( $ ( this ) . html ( ) . substring ( 1 ) != userArray . local . screen _name ) {
$ ( this ) . html ( '@' + userArray . local . screen _name ) ;
}
} ) ;
// profile urls
// try to find the last account group with this id, if the statusnet_profile_url seems to
// be changed we replace it wherever we can find it, even in list urls etc that starts with statusnet_profile_url
if ( $ ( 'a.account-group[data-user-id="' + userArray . local . id + '"]' ) . last ( ) . attr ( 'href' ) != userArray . local . statusnet _profile _url ) {
var oldStatusnetProfileURL = $ ( 'a.account-group[data-user-id="' + userArray . local . id + '"]' ) . last ( ) . attr ( 'href' ) ;
// all links with the exact statusnet_profile_url
$ . each ( $ ( '[href="' + oldStatusnetProfileURL + '"]' ) , function ( ) {
$ ( this ) . attr ( 'href' , userArray . local . statusnet _profile _url ) ;
} ) ;
// links starting with statusnet_profile_url
$ . each ( $ ( '[href*="' + oldStatusnetProfileURL + '/"]' ) , function ( ) {
$ ( this ) . attr ( 'href' , $ ( this ) . attr ( 'href' ) . replace ( oldStatusnetProfileURL + '/' , userArray . local . statusnet _profile _url + '/' ) ) ;
} ) ;
}
// cover photos
$ . each ( $ ( '.profile-header-inner[data-user-id="' + userArray . local . id + '"]' ) , function ( ) {
2016-01-24 22:02:58 +09:00
if ( $ ( this ) . css ( 'background-image' ) != 'url("' + userArray . local . cover _photo + '")' && userArray . local . cover _photo != false ) {
2015-12-04 05:41:00 +09:00
$ ( this ) . css ( 'background-image' , 'url("' + userArray . local . cover _photo + '")' ) ;
}
} ) ;
// the window.following object might need updating also
if ( typeof window . following != 'undefined' && typeof window . following [ userArray . local . id ] != 'undefined' ) {
if ( window . following [ userArray . local . id ] . avatar != userArray . local . profile _image _url ) {
window . following [ userArray . local . id ] . avatar = userArray . local . profile _image _url ;
}
if ( window . following [ userArray . local . id ] . name != userArray . local . name ) {
window . following [ userArray . local . id ] . name = userArray . local . name ;
}
if ( window . following [ userArray . local . id ] . username != userArray . local . screen _name ) {
window . following [ userArray . local . id ] . username = userArray . local . screen _name ;
}
}
}
} ) ;
}
2015-11-23 08:24:33 +09:00
/ * ·
·
· Iterates recursively through an API response in search for updated notice data
· If we find a "repeated" key we assume the parent is a notice object ( chosen arbitrary )
·
· · · · · · · · · · · · · * /
window . knownDeletedNotices = new Object ( ) ;
function searchForUpdatedNoticeData ( obj ) {
2016-01-24 02:10:24 +09:00
var streamItemsUpdated = false ;
2015-11-23 08:24:33 +09:00
for ( var property in obj ) {
if ( obj . hasOwnProperty ( property ) ) {
if ( typeof obj [ property ] == "object" ) {
searchForUpdatedNoticeData ( obj [ property ] ) ;
}
else if ( typeof obj [ property ] == 'boolean' && property == 'repeated' ) {
var streamItemFoundInFeed = $ ( '.stream-item[data-conversation-id][data-quitter-id="' + obj . id + '"]' ) ; // data-conversation-id identifies it as a notice, not a user or something
// if this is a special qvitter-delete-notice activity notice it means we try to hide
// the deleted notice from our stream
// the uri is in the obj.text var, between the double curly brackets
if ( typeof obj . qvitter _delete _notice != 'undefined' && obj . qvitter _delete _notice == true ) {
var uriToHide = obj . text . substring ( obj . text . indexOf ( '{{' ) + 2 , obj . text . indexOf ( '}}' ) ) ;
window . knownDeletedNotices [ uriToHide ] = true ;
var streamItemToHide = $ ( '.stream-item[data-uri="' + uriToHide + '"]' ) ;
slideUpAndRemoveStreamItem ( streamItemToHide ) ;
2016-01-24 02:10:24 +09:00
streamItemsUpdated = true ;
2015-11-23 08:24:33 +09:00
}
2015-12-01 17:13:59 +09:00
// if this is not a delete notice it means the notice exists and is not deleted,
// correct any notices that are marked as unrepeated, they might have
// been marked like that by mistake (i.e. a bug...)
else if ( streamItemFoundInFeed . hasClass ( 'unrepeated' ) ) {
streamItemFoundInFeed . removeClass ( 'unrepeated always-hidden' ) ;
2016-01-24 02:10:24 +09:00
streamItemsUpdated = true ;
2015-12-01 17:13:59 +09:00
}
2015-11-23 08:24:33 +09:00
// ordinary notices
else if ( streamItemFoundInFeed . length > 0 ) {
var queetFoundInFeed = streamItemFoundInFeed . children ( '.queet' ) ;
var queetID = streamItemFoundInFeed . attr ( 'data-quitter-id' ) ;
// sometimes activity notices don't get the is_activity flag set to true
// maybe because they were in the process of being saved when
// we first got them
2016-01-03 00:11:46 +09:00
if ( obj . is _post _verb === false ) {
2015-11-26 03:51:51 +09:00
streamItemFoundInFeed . addClass ( 'activity always-hidden' ) ;
2016-01-24 02:10:24 +09:00
streamItemsUpdated = true ;
2015-11-23 08:24:33 +09:00
}
// update the avatar row if the queet is expanded and the numbers are not the same
if ( streamItemFoundInFeed . hasClass ( 'expanded' ) ) {
var oldFavNum = parseInt ( queetFoundInFeed . find ( '.action-fav-num' ) . text ( ) , 10 ) ;
var oldRQNum = parseInt ( queetFoundInFeed . find ( '.action-rq-num' ) . text ( ) , 10 ) ;
if ( oldFavNum != obj . fave _num || oldRQNum != obj . repeat _num ) {
getFavsAndRequeetsForQueet ( streamItemFoundInFeed , queetID ) ;
}
}
2015-11-26 05:00:30 +09:00
// attachments might have been added/changed/have had time to be processed
2016-01-22 00:15:56 +09:00
if ( queetFoundInFeed . children ( 'script.attachment-json' ) . text ( ) != JSON . stringify ( obj . attachments ) ) {
if ( queetFoundInFeed . children ( 'script.attachment-json' ) . length == 0 ) {
queetFoundInFeed . prepend ( '<script class="attachment-json" type="application/json">' + JSON . stringify ( obj . attachments ) + '</script>' ) ;
}
else {
queetFoundInFeed . children ( 'script.attachment-json' ) . text ( JSON . stringify ( obj . attachments ) ) ;
}
2015-12-11 01:36:28 +09:00
var attachmentsHTMLBuild = buildAttachmentHTML ( obj . attachments ) ;
2016-01-24 02:10:24 +09:00
var thumbsIsHidden = false ;
if ( queetFoundInFeed . find ( '.queet-thumbs' ) . hasClass ( 'hide-thumbs' ) ) {
var thumbsIsHidden = true ;
}
2015-12-11 01:36:28 +09:00
queetFoundInFeed . find ( '.queet-thumbs' ) . remove ( ) ;
2016-01-21 01:39:34 +09:00
queetFoundInFeed . find ( '.oembed-data' ) . remove ( ) ;
2016-01-21 10:16:39 +09:00
placeQuotedNoticesInQueetText ( attachmentsHTMLBuild . quotedNotices , queetFoundInFeed . find ( '.queet-text' ) ) ;
2016-01-08 00:07:15 +09:00
// we might want to hide urls (rendered as attachments) in the queet text
$ . each ( queetFoundInFeed . find ( '.queet-text' ) . find ( 'a' ) , function ( ) {
if ( attachmentsHTMLBuild . urlsToHide . indexOf ( $ ( this ) . text ( ) ) > - 1 ) {
2016-01-23 05:19:08 +09:00
$ ( this ) . removeAttr ( 'style' ) ; // temporary fix
$ ( this ) . addClass ( 'hidden-embedded-link-in-queet-text' ) ;
2016-01-08 00:07:15 +09:00
}
} ) ;
2016-01-21 01:54:35 +09:00
queetFoundInFeed . find ( '.queet-text' ) . after ( attachmentsHTMLBuild . html ) ;
2016-01-24 02:10:24 +09:00
if ( thumbsIsHidden ) {
queetFoundInFeed . find ( '.queet-thumbs' ) . addClass ( 'hide-thumbs' ) ;
}
streamItemsUpdated = true ;
2015-11-23 10:23:44 +09:00
}
2016-02-25 19:10:55 +09:00
// attentions might have been added to a notice
if ( queetFoundInFeed . children ( 'script.attentions-json' ) . text ( ) != JSON . stringify ( obj . attentions ) ) {
if ( queetFoundInFeed . children ( 'script.attentions-json' ) . length == 0 ) {
queetFoundInFeed . prepend ( '<script class="attentions-json" type="application/json">' + JSON . stringify ( obj . attentions ) + '</script>' ) ;
}
else {
queetFoundInFeed . children ( 'script.attentions-json' ) . text ( JSON . stringify ( obj . attentions ) ) ;
}
}
2015-11-23 08:24:33 +09:00
// set favorite data
queetFoundInFeed . find ( '.action-fav-num' ) . attr ( 'data-fav-num' , obj . fave _num ) ;
queetFoundInFeed . find ( '.action-fav-num' ) . html ( obj . fave _num ) ;
if ( obj . favorited ) {
streamItemFoundInFeed . addClass ( 'favorited' ) ;
queetFoundInFeed . find ( '.action-fav-container' ) . children ( '.with-icn' ) . addClass ( 'done' ) ;
queetFoundInFeed . find ( '.action-fav-container' ) . find ( '.icon.sm-fav' ) . attr ( 'data-tooltip' , window . sL . favoritedVerb ) ;
2016-01-24 02:10:24 +09:00
streamItemsUpdated = true ;
2015-11-23 08:24:33 +09:00
}
else {
streamItemFoundInFeed . removeClass ( 'favorited' ) ;
queetFoundInFeed . find ( '.action-fav-container' ) . children ( '.with-icn' ) . removeClass ( 'done' ) ;
queetFoundInFeed . find ( '.action-fav-container' ) . find ( '.icon.sm-fav' ) . attr ( 'data-tooltip' , window . sL . favoriteVerb ) ;
2016-01-24 02:10:24 +09:00
streamItemsUpdated = true ;
2015-11-23 08:24:33 +09:00
}
// set repeat data
queetFoundInFeed . find ( '.action-rq-num' ) . attr ( 'data-rq-num' , obj . repeat _num ) ;
queetFoundInFeed . find ( '.action-rq-num' ) . html ( obj . repeat _num ) ;
if ( obj . repeated ) {
streamItemFoundInFeed . addClass ( 'requeeted' ) ;
queetFoundInFeed . find ( '.action-rt-container' ) . children ( '.with-icn' ) . addClass ( 'done' ) ;
queetFoundInFeed . find ( '.action-rt-container' ) . find ( '.icon.sm-rt' ) . attr ( 'data-tooltip' , window . sL . requeetedVerb ) ;
streamItemFoundInFeed . attr ( 'data-requeeted-by-me-id' , obj . repeated _id ) ;
2016-01-24 02:10:24 +09:00
streamItemsUpdated = true ;
2015-11-23 08:24:33 +09:00
}
else {
streamItemFoundInFeed . removeClass ( 'requeeted' ) ;
queetFoundInFeed . find ( '.action-rt-container' ) . children ( '.with-icn' ) . removeClass ( 'done' ) ;
queetFoundInFeed . find ( '.action-rt-container' ) . find ( '.icon.sm-rt' ) . attr ( 'data-tooltip' , window . sL . requeetVerb ) ;
streamItemFoundInFeed . removeAttr ( 'data-requeeted-by-me-id' ) ;
2016-01-24 02:10:24 +09:00
streamItemsUpdated = true ;
2015-11-23 08:24:33 +09:00
}
}
}
}
}
2016-01-24 02:10:24 +09:00
if ( streamItemsUpdated ) {
2016-01-24 05:04:30 +09:00
// TODO, create a queue that runs with setInterval instead, say every 5 s,
// that way we can run rememberStreamStateInLocalStorage() in the background,
// and don't slow down stream change etc
// rememberStreamStateInLocalStorage();
2016-01-24 02:10:24 +09:00
}
2015-11-23 08:24:33 +09:00
}
/ * ·
·
2015-11-26 03:51:51 +09:00
· Removes a deleted stream item from the feed gracefully , if not already hidden
2015-11-23 08:24:33 +09:00
·
· · · · · · · · · * /
function slideUpAndRemoveStreamItem ( streamItem , callback ) {
2015-11-26 03:51:51 +09:00
if ( streamItem . length > 0 && ! streamItem . hasClass ( 'always-hidden' ) ) {
2015-11-23 08:24:33 +09:00
streamItem . animate ( { opacity : '0.2' } , 1000 , 'linear' , function ( ) {
$ ( this ) . css ( 'height' , $ ( this ) . height ( ) + 'px' ) ;
$ ( this ) . animate ( { height : '0px' } , 500 , 'linear' , function ( ) {
2015-11-26 03:51:51 +09:00
$ ( this ) . addClass ( 'deleted always-hidden' ) ;
2016-01-21 01:39:34 +09:00
rememberStreamStateInLocalStorage ( ) ;
2015-11-23 08:24:33 +09:00
if ( typeof callback == 'function' ) {
callback ( ) ;
}
} ) ;
} ) ;
}
}
2015-06-05 09:17:33 +09:00
2015-11-20 05:22:59 +09:00
/ * ·
·
2015-11-23 08:24:33 +09:00
· Store the current stream 's state (html) in localStorage (if we' re logged in )
2015-11-20 05:22:59 +09:00
·
· · · · · · · · · * /
function rememberStreamStateInLocalStorage ( ) {
if ( typeof window . currentStreamObject != 'undefined' ) {
2015-11-26 03:51:51 +09:00
// don't store expanded content, only store profile card and the top 20 visible stream-items
2015-11-23 08:24:33 +09:00
var firstTwentyVisibleHTML = '' ;
var i = 0 ;
$ . each ( $ ( '#feed-body' ) . children ( '.stream-item' ) , function ( k , streamItem ) {
firstTwentyVisibleHTML += $ ( streamItem ) . outerHTML ( ) ;
2015-11-26 03:51:51 +09:00
if ( ! $ ( streamItem ) . hasClass ( 'always-hidden' ) ) {
2015-11-23 08:24:33 +09:00
i ++ ;
}
if ( i > 20 ) {
return false ;
}
2015-11-20 05:22:59 +09:00
} ) ;
2016-02-03 00:25:34 +09:00
2015-11-23 08:24:33 +09:00
var feed = $ ( '<div/>' ) . append ( firstTwentyVisibleHTML ) ;
2016-02-03 00:25:34 +09:00
2016-03-04 08:22:25 +09:00
// we add some of these things again when the notices are fetched from the cache
cleanStreamItemsFromClassesAndConversationElements ( feed ) ;
2015-11-20 05:22:59 +09:00
var feedHtml = feed . html ( ) ;
var profileCardHtml = $ ( '#feed' ) . siblings ( '.profile-card' ) . outerHTML ( ) ;
var streamData = {
card : profileCardHtml ,
feed : feedHtml
} ;
localStorageObjectCache _STORE ( 'streamState' , window . currentStreamObject . path , streamData ) ;
}
}
2016-03-04 08:22:25 +09:00
/ * ·
·
· Clean stream items from classes and conversation elements ,
· to use e . g . for caching and including in popup footers
·
· @ param streamItems : jQuery object with stream items as children
·
· · · · · · · · · * /
function cleanStreamItemsFromClassesAndConversationElements ( streamItems ) {
streamItems . children ( '.stream-item' ) . removeClass ( 'profile-blocked-by-me' ) ;
streamItems . children ( '.stream-item' ) . children ( '.queet' ) . removeAttr ( 'data-tooltip' ) ; // can contain tooltip about blocked user
streamItems . find ( '.temp-post' ) . remove ( ) ;
streamItems . children ( '.stream-item' ) . removeClass ( 'not-seen' ) ;
streamItems . children ( '.stream-item' ) . removeClass ( 'hidden-repeat' ) ; // this means we need hide repeats when adding cached notices to feed later
streamItems . children ( '.stream-item' ) . removeClass ( 'selected-by-keyboard' ) ;
streamItems . find ( '.dropdown-menu' ) . remove ( ) ;
streamItems . find ( '.stream-item' ) . removeClass ( 'expanded' ) . removeClass ( 'next-expanded' ) . removeClass ( 'hidden' ) . removeClass ( 'collapsing' ) . addClass ( 'visible' ) ;
streamItems . children ( '.stream-item' ) . each ( function ( ) {
cleanUpAfterCollapseQueet ( $ ( this ) ) ;
} ) ;
}
2015-11-20 05:22:59 +09:00
2016-01-17 08:21:30 +09:00
/ * ·
·
· Hide all instances ( repeats ) of a notice but the first / oldest one
·
· @ param streamItems : jQuery object with stream items as children
·
· · · · · · · · · * /
function hideAllButOldestInstanceOfStreamItem ( streamItemContainer ) {
streamItemContainer . children ( '.stream-item' ) . each ( function ( ) {
// if this stream item have siblings _after_ it, with the same id, hide it!
if ( $ ( this ) . nextAll ( '.stream-item[data-quitter-id="' + $ ( this ) . attr ( 'data-quitter-id' ) + '"]' ) . length > 0 ) {
$ ( this ) . addClass ( 'hidden-repeat' ) ;
}
} ) ;
return streamItemContainer ;
}
2015-11-23 08:24:33 +09:00
/ * ·
·
· Gets the full unshortened HTML for a queet
·
· · · · · · · · · * /
2015-11-26 04:36:28 +09:00
function getFullUnshortenedHtmlForQueet ( streamItem , cacheOnly ) {
if ( typeof cacheOnly == 'undefined' ) {
var cacheOnly = false ;
}
var queet = streamItem . children ( '.queet' ) ;
2015-11-23 08:24:33 +09:00
var queetId = streamItem . attr ( 'data-quitter-id' ) ;
var attachmentMore = queet . find ( 'span.attachment.more' ) ;
// only if actually shortened
2016-01-22 00:15:56 +09:00
if ( attachmentMore . length > 0
&& queet . children ( 'script.attachment-json' ) . length > 0
&& queet . children ( 'script.attachment-json' ) . text ( ) != 'undefined' ) {
2015-11-23 08:24:33 +09:00
// first try localstorage cache
var cacheData = localStorageObjectCache _GET ( 'fullQueetHtml' , queetId ) ;
if ( cacheData ) {
queet . find ( '.queet-text' ) . html ( cacheData ) ;
queet . outerHTML ( detectRTL ( queet . outerHTML ( ) ) ) ;
}
2016-01-22 00:15:56 +09:00
// then try static html file attachment, that we should have in the attachment-json script element
2015-11-26 04:36:28 +09:00
else if ( cacheOnly === false ) {
2015-11-23 08:24:33 +09:00
var attachmentId = attachmentMore . attr ( 'data-attachment-id' ) ;
2016-01-22 00:15:56 +09:00
$ . each ( JSON . parse ( queet . children ( 'script.attachment-json' ) . text ( ) ) , function ( k , attachment ) {
2015-11-23 08:24:33 +09:00
if ( attachment . id == attachmentId ) {
$ . get ( attachment . url , function ( data ) {
if ( data ) {
// get body and store in localStorage
var bodyHtml = $ ( '<html/>' ) . html ( data ) . find ( 'body' ) . html ( ) ;
localStorageObjectCache _STORE ( 'fullQueetHtml' , queetId , bodyHtml ) ;
queet . find ( '.queet-text' ) . html ( $ . trim ( bodyHtml ) ) ;
queet . outerHTML ( detectRTL ( queet . outerHTML ( ) ) ) ;
}
} ) ;
return false ;
}
} ) ;
}
}
}
2015-07-02 20:58:45 +09:00
/ * ·
·
· Appends a user to the array containing the mentions suggestions to show when typing a notice
·
· · · · · · · · · * /
function appendUserToMentionsSuggestionsArray ( user ) {
2015-09-18 10:04:28 +09:00
if ( typeof window . following [ user . id ] == 'undefined' ) {
2015-07-02 20:58:45 +09:00
// in the window.following array, we use "false" as url if it's a user from this instance
if ( user . is _local ) {
var url = false ;
}
else {
var url = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( user . statusnet _profile _url , user . screen _name ) ;
}
2015-09-18 10:04:28 +09:00
var userToAdd = {
avatar : user . profile _image _url ,
id : user . id ,
name : user . name ,
url : url ,
username : user . screen _name
} ;
2015-07-02 20:58:45 +09:00
2015-09-18 10:04:28 +09:00
window . following [ user . id ] = userToAdd ;
2015-07-02 20:58:45 +09:00
}
}
2016-03-04 08:22:25 +09:00
/ * ·
·
· Is a profile pref in the qvitter namespace enabled ?
·
· · · · · · · · · * /
function isQvitterProfilePrefEnabled ( topic ) {
if ( typeof window . qvitterProfilePrefs != 'undefined' && typeof window . qvitterProfilePrefs [ topic ] != 'undefined'
&& window . qvitterProfilePrefs [ topic ] !== null
&& window . qvitterProfilePrefs [ topic ] != ''
&& window . qvitterProfilePrefs [ topic ] !== false
&& window . qvitterProfilePrefs [ topic ] != 0
&& window . qvitterProfilePrefs [ topic ] != '0' ) {
return true ;
}
return false ;
}
/ * ·
·
· Is this user muted ?
·
· · · · · · · · · * /
function isUserMuted ( userID ) {
if ( isQvitterProfilePrefEnabled ( 'mute:' + userID ) ) {
return true ;
}
else {
return false ;
}
}
2015-07-02 20:58:45 +09:00
2015-06-05 09:17:33 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-01-27 01:13:34 +09:00
· Display unread notifications
2015-07-02 02:15:31 +09:00
·
2015-01-27 01:13:34 +09:00
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
function displayOrHideUnreadNotifications ( notifications ) {
2015-01-27 01:13:34 +09:00
var data = $ . parseJSON ( notifications ) ;
2015-11-18 06:48:28 +09:00
var totNotif = 0 ;
2015-07-02 02:15:31 +09:00
2015-11-18 06:48:28 +09:00
if ( data !== null && typeof data != 'undefined' ) {
$ . each ( data , function ( k , v ) {
totNotif = totNotif + parseInt ( v , 10 ) ;
} ) ;
}
2015-07-02 02:15:31 +09:00
2015-11-18 06:48:28 +09:00
if ( window . currentStreamObject . name == 'notifications' ) {
2015-11-26 03:51:51 +09:00
var hiddenNotifications = $ ( '#feed-body' ) . find ( '.stream-item.hidden:not(.always-hidden)' ) . length ;
2015-11-18 06:48:28 +09:00
if ( hiddenNotifications > 0 ) {
totNotif = totNotif + hiddenNotifications ;
2015-01-27 01:13:34 +09:00
}
2015-07-02 02:15:31 +09:00
}
2015-11-18 06:48:28 +09:00
if ( totNotif > 0 ) {
$ ( '#unseen-notifications' ) . html ( totNotif ) ;
2016-02-25 19:10:55 +09:00
document . title = '(' + totNotif + ') ' + window . siteTitle ; // update html page title
2015-11-18 06:48:28 +09:00
$ ( '#unseen-notifications' ) . show ( ) ;
}
else {
2015-01-27 01:13:34 +09:00
$ ( '#unseen-notifications' ) . hide ( ) ;
2015-07-02 02:15:31 +09:00
document . title = window . siteTitle ;
2015-01-27 01:13:34 +09:00
}
2015-11-18 06:48:28 +09:00
}
2015-01-27 01:13:34 +09:00
2015-11-18 06:48:28 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-01-19 04:36:08 +09:00
· Removes HTML special chars recursively from strings in objects
2015-01-19 22:07:46 +09:00
· with exceptions : "statusnet_html" found in notices , which we assume
· gnusocial already stripped from xss , and the "source" which should be
· html rendered by gnusocial itself and not open for attacks
2015-01-19 04:36:08 +09:00
·
· @ param obj : the object to search and replace in
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · · · · · * /
2015-01-19 04:36:08 +09:00
function iterateRecursiveReplaceHtmlSpecialChars ( obj ) {
for ( var property in obj ) {
if ( obj . hasOwnProperty ( property ) ) {
if ( typeof obj [ property ] == "object" ) {
iterateRecursiveReplaceHtmlSpecialChars ( obj [ property ] ) ;
}
2016-01-21 01:39:34 +09:00
else if ( typeof obj [ property ] == 'string'
&& property != 'statusnet_html'
2016-01-21 18:05:45 +09:00
&& property != 'oembedHTML' // we trust this to be cleaned server side
2016-01-21 01:39:34 +09:00
&& property != 'source' ) {
2015-06-05 09:17:33 +09:00
obj [ property ] = replaceHtmlSpecialChars ( obj [ property ] ) ;
2015-01-19 04:36:08 +09:00
}
}
}
2015-07-02 02:15:31 +09:00
return obj ;
2015-01-19 04:36:08 +09:00
}
function replaceHtmlSpecialChars ( text ) {
2016-03-04 08:22:25 +09:00
// don't do anything if the text is undefined
if ( typeof text == 'undefined' ) {
return text ;
}
2015-01-19 04:36:08 +09:00
var map = {
'&' : '&' ,
'<' : '<' ,
'>' : '>' ,
'"' : '"' ,
"'" : '''
} ;
return text . replace ( /[&<>"']/g , function ( m ) { return map [ m ] ; } ) ;
}
2013-08-20 04:15:15 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-09-03 01:13:15 +09:00
· Checks if register form is valid
2015-07-02 02:15:31 +09:00
·
2013-09-03 01:13:15 +09:00
· @ returns true or false
·
2015-07-02 02:15:31 +09:00
· · · · · · · · · * /
2013-09-03 01:13:15 +09:00
function validateRegisterForm ( o ) {
2015-07-02 02:15:31 +09:00
2013-09-03 01:13:15 +09:00
var nickname = o . find ( '#signup-user-nickname-step2' ) ;
2015-07-02 02:15:31 +09:00
var fullname = o . find ( '#signup-user-name-step2' ) ;
var email = o . find ( '#signup-user-email-step2' ) ;
2013-09-03 01:13:15 +09:00
var homepage = o . find ( '#signup-user-homepage-step2' ) ;
var bio = o . find ( '#signup-user-bio-step2' ) ;
var loc = o . find ( '#signup-user-location-step2' ) ;
var password1 = o . find ( '#signup-user-password1-step2' ) ;
var password2 = o . find ( '#signup-user-password2-step2' ) ;
2015-07-02 02:15:31 +09:00
var passwords = o . find ( '#signup-user-password1-step2,#signup-user-password2-step2' ) ;
2013-09-03 01:13:15 +09:00
var allFieldsValid = true ;
2015-07-02 02:15:31 +09:00
2013-09-03 01:13:15 +09:00
if ( nickname . val ( ) . length > 1 && /^[a-zA-Z0-9]+$/ . test ( nickname . val ( ) ) ) {
nickname . removeClass ( 'invalid' ) ; } else { nickname . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2015-07-02 02:15:31 +09:00
2013-09-03 01:13:15 +09:00
if ( fullname . val ( ) . length < 255 ) {
2015-07-02 02:15:31 +09:00
fullname . removeClass ( 'invalid' ) ; } else { fullname . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2013-09-03 01:13:15 +09:00
2015-12-15 05:01:27 +09:00
if ( validEmail ( email . val ( ) ) ) {
2015-07-02 02:15:31 +09:00
email . removeClass ( 'invalid' ) ; } else { email . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2013-09-03 01:13:15 +09:00
if ( $ . trim ( homepage . val ( ) ) . length == 0 || /^(ftp|http|https):\/\/[^ "]+$/ . test ( homepage . val ( ) ) ) {
2015-07-02 02:15:31 +09:00
homepage . removeClass ( 'invalid' ) ; } else { homepage . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2013-09-03 01:13:15 +09:00
if ( bio . val ( ) . length < 140 ) {
2015-07-02 02:15:31 +09:00
bio . removeClass ( 'invalid' ) ; } else { bio . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2013-09-03 01:13:15 +09:00
if ( loc . val ( ) . length < 255 ) {
2015-07-02 02:15:31 +09:00
loc . removeClass ( 'invalid' ) ; } else { loc . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2013-09-03 01:13:15 +09:00
if ( password1 . val ( ) . length > 5 && password2 . val ( ) . length > 5 && password1 . val ( ) == password2 . val ( ) ) {
2015-07-02 02:15:31 +09:00
passwords . removeClass ( 'invalid' ) ; } else { passwords . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2013-09-03 01:13:15 +09:00
return allFieldsValid ;
2015-07-02 02:15:31 +09:00
}
2014-06-02 04:51:28 +09:00
2015-12-15 05:01:27 +09:00
function validEmail ( email ) {
if ( /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ . test ( email ) ) {
return true ;
}
else {
return false ;
}
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2014-06-02 04:51:28 +09:00
· Checks if edit profile form is valid
2015-07-02 02:15:31 +09:00
·
2014-06-02 04:51:28 +09:00
· @ returns true or false
·
2015-07-02 02:15:31 +09:00
· · · · · · · · · * /
2014-06-02 04:51:28 +09:00
function validateEditProfileForm ( o ) {
2015-07-02 02:15:31 +09:00
var fullname = o . find ( 'input.fullname' ) ;
2014-06-02 04:51:28 +09:00
var homepage = o . find ( 'input.url' ) ;
var bio = o . find ( 'textarea.bio' ) ;
var loc = o . find ( 'input.location' ) ;
2015-07-02 02:15:31 +09:00
2014-06-02 04:51:28 +09:00
var allFieldsValid = true ;
if ( fullname . val ( ) . length < 255 ) {
2015-07-02 02:15:31 +09:00
fullname . removeClass ( 'invalid' ) ; } else { fullname . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2014-06-02 04:51:28 +09:00
if ( $ . trim ( homepage . val ( ) ) . length == 0 || /^(ftp|http|https):\/\/[^ "]+$/ . test ( homepage . val ( ) ) ) {
2015-07-02 02:15:31 +09:00
homepage . removeClass ( 'invalid' ) ; } else { homepage . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2014-06-02 04:51:28 +09:00
if ( bio . val ( ) . length < 140 ) {
2015-07-02 02:15:31 +09:00
bio . removeClass ( 'invalid' ) ; } else { bio . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2014-06-02 04:51:28 +09:00
if ( loc . val ( ) . length < 255 ) {
2015-07-02 02:15:31 +09:00
loc . removeClass ( 'invalid' ) ; } else { loc . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
2014-06-02 04:51:28 +09:00
return allFieldsValid ;
2015-07-02 02:15:31 +09:00
}
2015-05-30 00:30:03 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-05-30 00:30:03 +09:00
· Validate a hex color and add # if missing
2015-07-02 02:15:31 +09:00
·
2015-05-30 00:30:03 +09:00
· @ returns hex color with # or false
·
2015-07-02 02:15:31 +09:00
· · · · · · · · · * /
2015-05-30 00:30:03 +09:00
function isValidHexColor ( maybeValidHexColor ) {
2015-07-02 02:15:31 +09:00
2015-05-30 00:30:03 +09:00
if ( maybeValidHexColor . substring ( 0 , 1 ) != '#' ) {
maybeValidHexColor = '#' + maybeValidHexColor ;
}
2015-07-02 02:15:31 +09:00
2015-05-30 00:30:03 +09:00
var validHexColor = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i . test ( maybeValidHexColor ) ;
if ( validHexColor ) {
validHexColor = maybeValidHexColor ;
}
return validHexColor ;
}
2013-08-26 05:34:09 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-26 05:34:09 +09:00
· Change profile design
·
2015-05-30 00:30:03 +09:00
· @ param obj : user object that should contain one , two or all of backgroundimage , backgroundcolor and linkcolor
· false or empty string unsets the parameter to default
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · * /
2013-08-26 05:34:09 +09:00
function changeDesign ( obj ) {
2015-07-02 02:15:31 +09:00
2015-05-30 00:30:03 +09:00
// if we're logged out and this is the front page, we use the default design
2015-08-25 21:00:07 +09:00
if ( ! window . loggedIn &&
2015-11-23 08:24:33 +09:00
( window . currentStreamObject . name == 'public timeline' || window . currentStreamObject . name == 'public and external timeline' ) ) {
2015-05-30 00:30:03 +09:00
obj . backgroundimage = window . fullUrlToThisQvitterApp + window . siteBackground ;
obj . backgroundcolor = window . defaultBackgroundColor ;
obj . linkcolor = window . defaultLinkColor ;
}
2015-07-02 02:15:31 +09:00
2015-05-30 00:30:03 +09:00
// if no object is defined, abort
if ( typeof obj == 'undefined' ) {
return false ;
2015-07-02 02:15:31 +09:00
}
2015-05-30 00:30:03 +09:00
// remember the design for this stream
2015-11-23 08:24:33 +09:00
if ( typeof window . oldStreamsDesigns [ window . currentStreamObject . nickname ] == 'undefined' ) {
window . oldStreamsDesigns [ window . currentStreamObject . nickname ] = new Object ( ) ;
2015-05-30 00:30:03 +09:00
}
2015-07-02 02:15:31 +09:00
2015-05-30 00:30:03 +09:00
// change design elements
2015-07-02 02:15:31 +09:00
if ( typeof obj . backgroundimage != 'undefined' ) {
2015-05-30 00:30:03 +09:00
if ( obj . backgroundimage === false || obj . backgroundimage == '' ) {
$ ( 'body' ) . css ( 'background-image' , 'url(\'\')' ) ;
2013-08-26 05:34:09 +09:00
}
2015-05-30 00:30:03 +09:00
else if ( obj . backgroundimage . length > 4 ) {
2015-07-02 02:15:31 +09:00
$ ( 'body' ) . css ( 'background-image' , 'url(\'' + obj . backgroundimage + '\')' ) ;
2013-08-26 05:34:09 +09:00
}
2015-11-23 08:24:33 +09:00
window . oldStreamsDesigns [ window . currentStreamObject . nickname ] . backgroundimage = obj . backgroundimage ;
2015-07-02 02:15:31 +09:00
}
2015-05-30 00:30:03 +09:00
if ( typeof obj . backgroundcolor != 'undefined' ) {
if ( obj . backgroundcolor === false || obj . backgroundcolor == '' ) {
2015-05-19 22:22:52 +09:00
obj . backgroundcolor = window . defaultBackgroundColor ;
2013-08-26 05:34:09 +09:00
}
2015-05-30 00:30:03 +09:00
changeBackgroundColor ( obj . backgroundcolor ) ;
2015-11-23 08:24:33 +09:00
window . oldStreamsDesigns [ window . currentStreamObject . nickname ] . backgroundcolor = obj . backgroundcolor ;
2015-05-30 00:30:03 +09:00
}
if ( typeof obj . linkcolor != 'undefined' ) {
if ( obj . linkcolor === false || obj . linkcolor == '' ) {
obj . linkcolor = window . defaultLinkColor ;
2013-08-26 05:34:09 +09:00
}
2015-05-30 00:30:03 +09:00
changeLinkColor ( obj . linkcolor ) ;
2015-11-23 08:24:33 +09:00
window . oldStreamsDesigns [ window . currentStreamObject . nickname ] . linkcolor = obj . linkcolor ;
2015-05-30 00:30:03 +09:00
}
2013-08-26 05:34:09 +09:00
}
2015-05-19 22:22:52 +09:00
// create object to remember designs on page load
window . oldStreamsDesigns = new Object ( ) ;
2015-05-30 00:30:03 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-05-30 00:30:03 +09:00
· Change background color
·
· @ param newLinkColor : hex value with or without #
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · * /
2015-05-30 00:30:03 +09:00
function changeBackgroundColor ( newBackgroundColor ) {
// check hex value
var validHexColor = isValidHexColor ( newBackgroundColor ) ;
if ( ! validHexColor ) {
console . log ( 'invalid hex value for backgroundcolor: ' + newBackgroundColor ) ;
return false ;
}
2015-07-02 02:15:31 +09:00
$ ( 'body' ) . css ( 'background-color' , validHexColor ) ;
2015-05-30 00:30:03 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-26 03:11:53 +09:00
· Change link color
·
2015-05-30 00:30:03 +09:00
· @ param newLinkColor : hex value with or without #
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · * /
2013-08-26 03:11:53 +09:00
function changeLinkColor ( newLinkColor ) {
2015-05-30 00:30:03 +09:00
// check hex value
var validHexColor = isValidHexColor ( newLinkColor ) ;
if ( ! validHexColor ) {
console . log ( 'invalid hex value for linkcolor: ' + newLinkColor ) ;
return false ;
}
2015-07-02 02:15:31 +09:00
2015-06-06 23:20:26 +09:00
var lighterColor08 = blendRGBColors ( hex2rgb ( validHexColor ) , 'rgb(255,255,255)' , 0.8 ) ;
var lighterColor06 = blendRGBColors ( hex2rgb ( validHexColor ) , 'rgb(255,255,255)' , 0.6 )
2015-07-02 02:15:31 +09:00
2015-06-07 17:52:38 +09:00
var headStyle = $ ( '#dynamic-styles' ) . children ( 'style' ) ;
2015-06-06 23:20:26 +09:00
var headStyleText = headStyle . text ( ) ;
headStyleText = replaceFromStringEndToStringStart ( headStyleText , '/*COLORSTART*/' , '/*COLOREND*/' , validHexColor ) ;
headStyleText = replaceFromStringEndToStringStart ( headStyleText , '/*BACKGROUNDCOLORSTART*/' , '/*BACKGROUNDCOLOREND*/' , validHexColor ) ;
headStyleText = replaceFromStringEndToStringStart ( headStyleText , '/*BORDERCOLORSTART*/' , '/*BORDERCOLOREND*/' , validHexColor ) ;
headStyleText = replaceFromStringEndToStringStart ( headStyleText , '/*LIGHTERBACKGROUNDCOLORSTART*/' , '/*LIGHTERBACKGROUNDCOLOREND*/' , lighterColor08 ) ;
headStyleText = replaceFromStringEndToStringStart ( headStyleText , '/*LIGHTERBORDERCOLORSTART*/' , '/*LIGHTERBORDERCOLOREND*/' , lighterColor06 ) ;
2015-07-02 02:15:31 +09:00
headStyleText = replaceFromStringEndToStringStart ( headStyleText , '/*LIGHTERBORDERBOTTOMCOLORSTART*/' , '/*LIGHTERBORDERBOTTOMCOLOREND*/' , lighterColor08 ) ;
2015-06-06 23:20:26 +09:00
headStyle . text ( headStyleText ) ;
}
function replaceFromStringEndToStringStart ( string , fromStringEnd , toStringStart , withString ) {
2015-07-02 02:15:31 +09:00
return string . substring ( 0 , string . indexOf ( fromStringEnd ) + fromStringEnd . length ) + withString + string . substring ( string . indexOf ( toStringStart ) ) ;
2014-11-24 21:47:45 +09:00
}
function blendRGBColors ( c0 , c1 , p ) {
var f = c0 . split ( "," ) , t = c1 . split ( "," ) , R = parseInt ( f [ 0 ] . slice ( 4 ) ) , G = parseInt ( f [ 1 ] ) , B = parseInt ( f [ 2 ] ) ;
return "rgb(" + ( Math . round ( ( parseInt ( t [ 0 ] . slice ( 4 ) ) - R ) * p ) + R ) + "," + ( Math . round ( ( parseInt ( t [ 1 ] ) - G ) * p ) + G ) + "," + ( Math . round ( ( parseInt ( t [ 2 ] ) - B ) * p ) + B ) + ")" ;
}
function hex2rgb ( hexStr ) {
// note: hexStr should be #rrggbb
var hex = parseInt ( hexStr . substring ( 1 ) , 16 ) ;
var r = ( hex & 0xff0000 ) >> 16 ;
var g = ( hex & 0x00ff00 ) >> 8 ;
var b = hex & 0x0000ff ;
return 'rgb(' + r + ',' + g + ',' + b + ')' ;
2013-08-26 03:11:53 +09:00
}
2013-11-23 08:31:04 +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
· Right - to - left language detection < o
· ( //
2015-07-02 02:15:31 +09:00
· @ param s : the stream - item to detect rtl in
·
· @ return a stream - item that might have rtl - class added
2013-08-19 22:30:57 +09:00
·
· · · · · · · · · * /
2015-07-02 02:15:31 +09:00
function detectRTL ( s ) {
2013-08-19 22:30:57 +09:00
var $streamItem = $ ( '<div>' ) . append ( s ) ;
var $queetText = $ ( '<div>' ) . append ( $streamItem . find ( '.queet-text' ) . html ( ) ) ; // create an jquery object
var $a = $queetText . find ( 'a' ) ; $a . remove ( ) ; // remove links
var $vcard = $queetText . find ( '.vcard' ) ; $vcard . remove ( ) ; // remove users, groups
2015-07-02 02:15:31 +09:00
var $hcard = $queetText . find ( '.h-card' ) ; $hcard . remove ( ) ; // remove users, groups
2013-08-19 22:30:57 +09:00
var $tag = $queetText . find ( '.tag' ) ; $tag . remove ( ) ; // remove tags
if ( $queetText . find ( '.rtl' ) . length > 0 ) { $queetText . html ( $queetText . find ( '.rtl' ) . html ( ) ) ; } // remove rtl container if there is one
// remove chars we're not interested in
$queetText . html ( $queetText . html ( ) . replace ( /\@/gi , '' ) . replace ( /\#/gi , '' ) . replace ( /\!/gi , '' ) . replace ( /\(/gi , '' ) . replace ( /\)/gi , '' ) . replace ( /\:D/gi , '' ) . replace ( /D\:/gi , '' ) . replace ( /\:/gi , '' ) . replace ( /\-/gi , '' ) . replace ( /\s/gi , '' ) ) ;
2015-07-02 02:15:31 +09:00
// count ltr and rtl chars
2013-08-19 22:30:57 +09:00
var ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF' + '\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF' ,
rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC' ,
rtlDirCheck = new RegExp ( '^[^' + ltrChars + ']*[' + rtlChars + ']' ) ,
RTLnum = 0 ,
LTRnum = 0 ,
RTLorLTR = $queetText . html ( ) ;
for ( var i = 0 , len = RTLorLTR . length ; i < len ; i ++ ) {
if ( rtlDirCheck . test ( RTLorLTR [ i ] ) ) { RTLnum ++ ; }
else { LTRnum ++ ; }
2015-07-02 02:15:31 +09:00
}
2015-06-17 08:37:35 +09:00
2013-08-19 22:30:57 +09:00
// if there are more rtl chars than ltr
2015-06-17 08:37:35 +09:00
// or if no chars (that we are interested, but body is set to rtl)
if ( RTLnum > LTRnum
|| ( $queetText . html ( ) . length == 0 && $ ( 'body' ) . hasClass ( 'rtl' ) ) ) {
2013-08-19 22:30:57 +09:00
$streamItem . children ( '.stream-item' ) . children ( '.queet' ) . addClass ( 'rtl' ) ;
2015-06-17 08:37:35 +09:00
}
else {
// for ltr languages we move @, ! and # to inside
$streamItem . find ( '.queet-text' ) . find ( '.h-card.mention' ) . prepend ( '@' ) ;
2015-06-18 07:15:44 +09:00
$streamItem . find ( '.queet-text' ) . find ( '.h-card.group' ) . prepend ( '!' ) ;
2015-08-07 04:53:24 +09:00
$streamItem . find ( '.queet-text' ) . find ( '.vcard .fn.nickname:not(.group)' ) . prepend ( '@' ) ; // very old style
2015-11-23 08:24:33 +09:00
$streamItem . find ( '.queet-text' ) . find ( '.vcard .nickname.mention:not(.fn)' ) . prepend ( '@' ) ; // old style
2015-07-02 02:15:31 +09:00
$streamItem . find ( '.queet-text' ) . find ( '.vcard .nickname.group' ) . prepend ( '!' ) ; // old style
$streamItem . find ( '.queet-text' ) . find ( 'a[rel="tag"]' ) . prepend ( '#' ) ;
2015-06-17 08:37:35 +09:00
}
// we remove @, ! and #, they are added as pseudo elements, or have been moved to the inside
2015-07-02 02:15:31 +09:00
return $streamItem . html ( ) . replace ( /@<a/gi , '<a' ) . replace ( /!<a/gi , '<a' ) . replace ( /@<span class="vcard">/gi , '<span class="vcard">' ) . replace ( /!<span class="vcard">/gi , '<span class="vcard">' ) . replace ( /#<span class="tag">/gi , '<span class="tag">' ) ;
2013-08-19 22:30:57 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Takes twitter style dates and converts them
·
2015-07-02 02:15:31 +09:00
· @ param tdate : date in the form of e . g . 'Mon Aug 05 16:30:22 +0200 2013'
·
· @ return user friendly dates . . M _
· W
2013-08-19 22:30:57 +09:00
· Needs global language object window . sL to be populated
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
function parseTwitterDate ( tdate ) {
var month _names = new Array ( ) ;
month _names [ month _names . length ] = window . sL . shortmonthsJanuary ;
month _names [ month _names . length ] = window . sL . shortmonthsFebruary
month _names [ month _names . length ] = window . sL . shortmonthsMars
month _names [ month _names . length ] = window . sL . shortmonthsApril
month _names [ month _names . length ] = window . sL . shortmonthsMay
month _names [ month _names . length ] = window . sL . shortmonthsJune
month _names [ month _names . length ] = window . sL . shortmonthsJuly
month _names [ month _names . length ] = window . sL . shortmonthsAugust
month _names [ month _names . length ] = window . sL . shortmonthsSeptember
month _names [ month _names . length ] = window . sL . shortmonthsOctober
month _names [ month _names . length ] = window . sL . shortmonthsNovember
month _names [ month _names . length ] = window . sL . shortmonthsDecember
2015-08-18 04:42:32 +09:00
var system _date = parseDate ( tdate ) ;
2013-08-19 22:30:57 +09:00
var user _date = new Date ( ) ;
var diff = Math . floor ( ( user _date - system _date ) / 1000 ) ;
if ( diff <= 10 ) { return window . sL . now ; }
if ( diff < 60 ) { return window . sL . shortDateFormatSeconds . replace ( '{seconds}' , Math . round ( diff / 10 ) * 10 ) ; }
if ( diff <= 3540 ) { return window . sL . shortDateFormatMinutes . replace ( '{minutes}' , Math . round ( diff / 60 ) ) ; }
if ( diff <= 86400 ) { return window . sL . shortDateFormatHours . replace ( '{hours}' , Math . round ( diff / 3600 ) ) ; }
if ( diff <= 31536000 ) { return window . sL . shortDateFormatDate . replace ( '{day}' , system _date . getDate ( ) ) . replace ( '{month}' , month _names [ system _date . getMonth ( ) ] ) ; }
if ( diff > 31536000 ) { return window . sL . shortDateFormatDateAndY . replace ( '{day}' , system _date . getDate ( ) ) . replace ( '{month}' , month _names [ system _date . getMonth ( ) ] ) . replace ( '{year}' , system _date . getFullYear ( ) ) ; }
return system _date ;
}
function parseTwitterLongDate ( tdate ) {
var month _names = new Array ( ) ;
month _names [ month _names . length ] = window . sL . longmonthsJanuary ;
month _names [ month _names . length ] = window . sL . longmonthsFebruary
month _names [ month _names . length ] = window . sL . longmonthsMars
month _names [ month _names . length ] = window . sL . longmonthsApril
month _names [ month _names . length ] = window . sL . longmonthsMay
month _names [ month _names . length ] = window . sL . longmonthsJune
month _names [ month _names . length ] = window . sL . longmonthsJuly
month _names [ month _names . length ] = window . sL . longmonthsAugust
month _names [ month _names . length ] = window . sL . longmonthsSeptember
month _names [ month _names . length ] = window . sL . longmonthsOctober
month _names [ month _names . length ] = window . sL . longmonthsNovember
month _names [ month _names . length ] = window . sL . longmonthsDecember
2015-08-18 04:42:32 +09:00
var system _date = parseDate ( tdate ) ;
2013-08-19 22:30:57 +09:00
var hours = system _date . getHours ( ) ;
var minutes = ( '0' + system _date . getMinutes ( ) ) . slice ( - 2 ) ;
var ampm = hours >= 12 ? 'pm' : 'am' ;
var time24hours = hours + ':' + minutes ;
var time12hours = hours % 12 ;
time12hours = time12hours ? time12hours : 12 ; // the hour '0' should be '12'
if ( ampm == 'am' ) { time12hours = window . sL . time12am . replace ( '{time}' , time12hours + ':' + minutes ) ; }
else { time12hours = window . sL . time12pm . replace ( '{time}' , time12hours + ':' + minutes ) ; }
return window . sL . longDateFormat . replace ( '{time24}' , time24hours ) . replace ( '{hours}' , hours ) . replace ( '{minutes}' , minutes ) . replace ( '{time12}' , time12hours ) . replace ( '{day}' , system _date . getDate ( ) ) . replace ( '{month}' , month _names [ system _date . getMonth ( ) ] ) . replace ( '{year}' , system _date . getFullYear ( ) ) ;
2015-07-02 02:15:31 +09:00
}
2014-05-23 21:13:38 +09:00
function timestampToTwitterDate ( timestamp ) {
var a = new Date ( timestamp * 1000 ) ;
var months = [ 'Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' , 'Jul' , 'Aug' , 'Sep' , 'Oct' , 'Nov' , 'Dec' ] ;
2015-07-02 02:15:31 +09:00
var days = [ 'Sun' , 'Mon' , 'Tue' , 'Wed' , 'Thu' , 'Fri' , 'Sat' ] ;
2014-05-23 21:13:38 +09:00
var day = days [ a . getUTCDay ( ) ] ;
var year = a . getUTCFullYear ( ) ;
var month = months [ a . getUTCMonth ( ) ] ;
var date = ( a . getUTCDate ( ) < 10 ? '0' : '' ) + a . getUTCDate ( ) ;
var hour = ( a . getUTCHours ( ) < 10 ? '0' : '' ) + a . getUTCHours ( ) ;
var min = ( a . getUTCMinutes ( ) < 10 ? '0' : '' ) + a . getUTCMinutes ( ) ;
var sec = ( a . getUTCSeconds ( ) < 10 ? '0' : '' ) + a . getUTCSeconds ( ) ;
return day + ' ' + month + ' ' + date + ' ' + hour + ':' + min + ':' + sec + ' +0000 ' + year ;
2015-07-02 02:15:31 +09:00
}
2015-08-18 04:42:32 +09:00
function parseDate ( str ) {
2015-08-26 06:27:08 +09:00
if ( typeof str != 'undefined' ) {
var v = str . split ( ' ' ) ;
return new Date ( Date . parse ( v [ 1 ] + " " + v [ 2 ] + ", " + v [ 5 ] + " " + v [ 3 ] + " " + v [ 4 ] ) ) ;
}
2015-08-18 04:42:32 +09:00
}
2015-07-02 02:15:31 +09:00
2014-05-23 21:13:38 +09:00
/ * ·
2015-07-02 02:15:31 +09:00
·
2014-10-03 02:24:54 +09:00
· If we want to make sure we have empty arrays , not empty objects
2015-07-02 02:15:31 +09:00
·
2014-05-23 21:13:38 +09:00
· · · · · · · · · · * /
2015-07-02 02:15:31 +09:00
function convertEmptyObjectToEmptyArray ( data ) {
2014-05-28 03:40:51 +09:00
// empty object? return empty array instead...
if ( $ . isEmptyObject ( data ) ) {
return [ ] ;
}
2014-05-23 21:13:38 +09:00
// leave data unchanged if we don't recognize it
2014-10-03 02:24:54 +09:00
else {
2014-05-23 21:13:38 +09:00
return data ;
2015-07-02 02:15:31 +09:00
}
2014-05-28 03:40:51 +09:00
2014-05-23 21:13:38 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Functions to show and remove the spinner
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 display _spinner ( parent ) {
2014-09-25 06:20:35 +09:00
if ( $ ( '.loader' ) . length < 1 ) {
2015-07-02 02:15:31 +09:00
2014-11-24 21:47:45 +09:00
if ( typeof parent == 'undefined' ) {
$ ( '.global-nav' ) . removeClass ( 'show-logo' ) ;
var parent = 'body' ;
2015-07-02 02:15:31 +09:00
}
2014-11-24 21:47:45 +09:00
$ ( parent ) . prepend ( ' \
2014-09-25 06:20:35 +09:00
< div class = "loader" > \
< svg version = "1.1" xmlns = "http://www.w3.org/2000/svg" xmlns : xlink = "http://www.w3.org/1999/xlink" x = "0px" y = "0px" \
width = "40px" height = "40px" viewBox = "0 0 40 40" enable - background = "new 0 0 40 40" xml : space = "preserve" > \
< path opacity = "0.2" enable - background = "new " d = " M20 . 201 , 8.503 c - 6.413 , 0 - 11.612 , 5.199 - 11.612 , 11.612 s5 . 199 , 11.611 , 11.612 , 11.611 \
c6 . 412 , 0 , 11.611 - 5.198 , 11.611 - 11.611 S26 . 613 , 8.503 , 20.201 , 8.503 z M20 . 201 , 29.153 c - 4.992 , 0 - 9.039 - 4.046 - 9.039 - 9.038 \
s4 . 047 - 9.039 , 9.039 - 9.039 c4 . 991 , 0 , 9.038 , 4.047 , 9.038 , 9.039 S25 . 192 , 29.153 , 20.201 , 29.153 z " / > \
< path d = "M24.717,12.293l1.285-2.227c-1.708-0.988-3.686-1.563-5.801-1.563l0,0v2.573l0,0C21.848,11.076,23.386,11.524,24.717,12.293 z" > \
< animateTransform attributeType = "xml" \
attributeName = "transform" \
type = "rotate" \
from = "0 20 20" \
to = "360 20 20" \
dur = "1s" \
repeatCount = "indefinite" / > \
< / p a t h > \
< / s v g > \
< / d i v > \
' ) ;
}
2015-07-02 02:15:31 +09:00
}
function remove _spinner ( ) {
2014-09-20 09:53:10 +09:00
$ ( '.loader' ) . remove ( ) ;
2015-07-02 02:15:31 +09:00
$ ( '.global-nav' ) . addClass ( 'show-logo' ) ;
}
2013-08-19 22:30:57 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Converts ... - attachment - links to spans
·
· ( Attachments are loaded when queets expand )
2015-07-02 02:15:31 +09:00
·
2013-08-19 22:30:57 +09:00
· · · · · · · · · · · · · · · · · * /
function convertAttachmentMoreHref ( ) {
$ ( 'a.attachment.more' ) . each ( function ( ) {
if ( typeof $ ( this ) . attr ( 'href' ) != 'undefined' ) {
2015-02-26 07:46:32 +09:00
var attachment _href = $ ( this ) . attr ( 'href' ) ;
var attachment _id = attachment _href . substr ( ( ~ - attachment _href . lastIndexOf ( "/" ) >>> 0 ) + 2 ) ;
if ( attachment _id . length > 0 ) {
2015-07-02 02:15:31 +09:00
$ ( this ) . replaceWith ( $ ( '<span class="attachment more" data-attachment-id="' + attachment _id + '">…</span>' ) ) ;
2015-02-26 07:46:32 +09:00
}
2013-08-19 22:30:57 +09:00
}
} ) ;
}
2015-09-01 06:53:09 +09:00
/ * ·
·
· Saves the user ' s bookmarks to the server
·
· · · · · · · · · · · · · * /
function saveAllBookmarks ( ) {
var i = 0 ;
var bookmarkContainer = new Object ( ) ;
$ . each ( $ ( '#bookmark-container .stream-selection' ) , function ( key , obj ) {
bookmarkContainer [ i ] = new Object ( ) ;
bookmarkContainer [ i ] . dataStreamHref = $ ( obj ) . attr ( 'href' ) ;
2015-11-19 23:38:25 +09:00
bookmarkContainer [ i ] . dataStreamHeader = $ ( obj ) . text ( ) ;
2015-09-01 06:53:09 +09:00
i ++ ;
} ) ;
postUpdateBookmarks ( bookmarkContainer ) ;
$ ( '#bookmark-container' ) . sortable ( { delay : 100 } ) ;
$ ( '#bookmark-container' ) . disableSelection ( ) ;
}
/ * ·
·
· Append all bookmarks to the bookmark container
·
· · · · · · · · · · · · · * /
function appendAllBookmarks ( bookmarkContainer ) {
2015-11-18 06:48:28 +09:00
if ( typeof bookmarkContainer != 'undefined' && bookmarkContainer ) {
2015-09-01 06:53:09 +09:00
$ ( '#bookmark-container' ) . html ( '' ) ;
2015-11-18 06:48:28 +09:00
var bookmarkContainerParsed = JSON . parse ( bookmarkContainer ) ;
$ . each ( bookmarkContainerParsed , function ( key , obj ) {
2015-11-19 23:38:25 +09:00
$ ( '#bookmark-container' ) . append ( '<a class="stream-selection" href="' + obj . dataStreamHref + '">' + obj . dataStreamHeader + '<i class="chev-right" data-tooltip="' + window . sL . tooltipRemoveBookmark + '"></i></a>' ) ;
2015-09-01 06:53:09 +09:00
} ) ;
}
$ ( '#bookmark-container' ) . sortable ( { delay : 100 } ) ;
$ ( '#bookmark-container' ) . disableSelection ( ) ;
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2015-03-06 06:22:48 +09:00
· Updates the browsing history local storage
2013-08-19 22:30:57 +09:00
·
· · · · · · · · · · · · · * /
2015-07-02 02:15:31 +09:00
2013-08-19 22:30:57 +09:00
function updateHistoryLocalStorage ( ) {
2013-08-20 04:15:15 +09:00
if ( localStorageIsEnabled ( ) ) {
var i = 0 ;
var historyContainer = new Object ( ) ;
$ . each ( $ ( '#history-container .stream-selection' ) , function ( key , obj ) {
historyContainer [ i ] = new Object ( ) ;
2013-11-23 08:31:04 +09:00
historyContainer [ i ] . dataStreamHref = $ ( obj ) . attr ( 'href' ) ;
2015-11-19 23:38:25 +09:00
historyContainer [ i ] . dataStreamHeader = $ ( obj ) . text ( ) ;
2013-08-20 04:15:15 +09:00
i ++ ;
} ) ;
2015-03-06 06:22:48 +09:00
localStorageObjectCache _STORE ( 'browsingHistory' , window . loggedIn . screen _name , historyContainer ) ;
2013-08-20 04:15:15 +09:00
if ( $ ( '#history-container .stream-selection' ) . length == 0 ) {
$ ( '#history-container' ) . css ( 'display' , 'none' ) ;
}
else {
$ ( '#history-container' ) . css ( 'display' , 'block' ) ;
}
2013-08-19 22:30:57 +09:00
}
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Loads history from local storage to menu
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
function loadHistoryFromLocalStorage ( ) {
2013-08-20 04:15:15 +09:00
if ( localStorageIsEnabled ( ) ) {
2015-07-02 23:57:51 +09:00
var cacheData = localStorageObjectCache _GET ( 'browsingHistory' , window . loggedIn . screen _name ) ;
if ( cacheData ) {
$ ( '#history-container' ) . css ( 'display' , 'block' ) ;
$ ( '#history-container' ) . html ( '' ) ;
$ . each ( cacheData , function ( key , obj ) {
2016-02-07 10:23:38 +09:00
var streamHeader = replaceHtmlSpecialChars ( obj . dataStreamHeader ) ; // because we're pulling the header with jQuery.text() before saving in localstorage, which unescapes our escaped html
$ ( '#history-container' ) . append ( '<a class="stream-selection" href="' + obj . dataStreamHref + '">' + streamHeader + '<i class="chev-right" data-tooltip="' + window . sL . tooltipBookmarkStream + '"></i></a>' ) ;
2015-07-02 23:57:51 +09:00
} ) ;
}
2013-08-20 04:15:15 +09:00
updateHistoryLocalStorage ( ) ;
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
/ * ·
·
2013-08-19 22:30:57 +09:00
· Does stream need a ? or a &
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
function qOrAmp ( stream ) {
if ( stream . substr ( - 5 ) == '.json' ) {
return '?' ;
}
else {
return '&' ;
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
· Count chars in queet box
·
2013-11-23 08:31:04 +09:00
· @ param src : the queetbox ' s value
2013-08-19 22:30:57 +09:00
· @ param trgt : the counter
2015-07-02 02:15:31 +09:00
· @ param btn : the button
·
· · · · · · · · · · · · · * /
2013-08-19 22:30:57 +09:00
function countCharsInQueetBox ( src , trgt , btn ) {
2014-05-17 05:41:45 +09:00
2015-01-27 03:22:05 +09:00
// count linebreaks by converting them to spaces
var $src _txt = $ ( '<div/>' ) . append ( src . html ( ) . replace ( /<br>/g , ' ' ) ) ;
$src _txt = $ ( '<div/>' ) . append ( $ . trim ( $src _txt . text ( ) . replace ( /\n/g , '' ) . replace ( /^\s+|\s+$/g , '' ) ) ) ;
2014-10-18 00:08:14 +09:00
var numchars = ( $src _txt . text ( ) ) . length ;
2014-05-17 05:41:45 +09:00
2014-09-20 09:53:10 +09:00
// check for long urls and disable/enable url shorten button if present
var longurls = 0 ;
$ . each ( src . siblings ( '.syntax-middle' ) . find ( 'span.url' ) , function ( key , obj ) {
if ( $ . trim ( $ ( obj ) . html ( ) . replace ( / /gi , '' ) . replace ( /<br>/gi , '' ) ) . length > 20 ) {
longurls ++ ;
}
} ) ;
if ( longurls > 0 ) src . siblings ( '.queet-toolbar' ) . find ( 'button.shorten' ) . removeClass ( 'disabled' ) ;
else src . siblings ( '.queet-toolbar' ) . find ( 'button.shorten' ) . addClass ( 'disabled' ) ;
2014-05-17 05:41:45 +09:00
// limited
2014-05-16 11:07:30 +09:00
if ( window . textLimit > 0 ) {
trgt . html ( window . textLimit - numchars ) ;
// activate/deactivare button
if ( numchars > 0 && numchars < window . textLimit + 1 ) {
btn . removeClass ( 'disabled' ) ;
btn . addClass ( 'enabled' ) ;
2015-07-02 02:15:31 +09:00
btn . removeClass ( 'too-long' ) ;
2014-05-16 11:07:30 +09:00
// deactivate button if it's equal to the start text
2014-05-28 03:40:51 +09:00
var queetBox = btn . closest ( '.inline-reply-queetbox' ) . children ( '.queet-box-syntax' ) ;
if ( typeof queetBox . attr ( 'data-replies-text' ) != 'undefined' ) {
var $startText = $ ( '<div/>' ) . append ( decodeURIComponent ( queetBox . attr ( 'data-replies-text' ) ) ) ;
if ( $ . trim ( $startText . text ( ) ) == $ . trim ( $src _txt . text ( ) ) ) {
2014-05-16 11:07:30 +09:00
btn . removeClass ( 'enabled' ) ;
2015-07-02 02:15:31 +09:00
btn . addClass ( 'disabled' ) ;
2014-05-16 11:07:30 +09:00
}
2013-11-23 08:31:04 +09:00
}
2013-08-19 22:30:57 +09:00
}
2014-10-18 00:08:14 +09:00
else if ( numchars > window . textLimit ) {
btn . removeClass ( 'enabled' ) ;
btn . addClass ( 'disabled' ) ;
2015-07-02 02:15:31 +09:00
btn . addClass ( 'too-long' ) ;
2014-10-18 00:08:14 +09:00
}
2014-05-16 11:07:30 +09:00
else {
btn . removeClass ( 'enabled' ) ;
btn . addClass ( 'disabled' ) ;
2015-07-02 02:15:31 +09:00
btn . removeClass ( 'too-long' ) ;
}
// counter color
2014-05-16 11:07:30 +09:00
if ( ( window . textLimit - numchars ) < 0 ) {
trgt . css ( 'color' , '#D40D12' ) ;
}
else {
2015-07-02 02:15:31 +09:00
trgt . removeAttr ( 'style' ) ;
2014-05-16 11:07:30 +09:00
}
2013-08-19 22:30:57 +09:00
}
2014-05-17 05:41:45 +09:00
// unlimited
else {
if ( numchars > 0 ) {
btn . removeClass ( 'disabled' ) ;
2015-07-02 02:15:31 +09:00
btn . addClass ( 'enabled' ) ;
2014-05-17 05:41:45 +09:00
}
else {
btn . removeClass ( 'enabled' ) ;
2015-07-02 02:15:31 +09:00
btn . addClass ( 'disabled' ) ;
2014-05-17 05:41:45 +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-11-23 08:24:33 +09:00
/ * ·
·
· Prefill the queet box with cached text , if there is any in an attribute
·
· @ param queetBox : jQuery object for the queet box
·
· · · · · · · · · · · · · * /
function maybePrefillQueetBoxWithCachedText ( queetBox ) {
var cachedText = decodeURIComponent ( queetBox . attr ( 'data-cached-text' ) ) ;
var cachedTextText = $ ( '<div/>' ) . html ( cachedText ) . text ( ) ;
if ( cachedText != 'undefined' && cachedText != 'false' ) {
queetBox . click ( ) ;
queetBox . html ( cachedText ) ;
setSelectionRange ( queetBox [ 0 ] , cachedTextText . length , cachedTextText . length ) ;
queetBox . trigger ( 'input' ) ;
}
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· Remember my scroll position
·
· @ param obj : jQuery object which position we want to remember
· @ param id : id for position to remember
· @ param offset : we might want to offset our remembered scroll , e . g . when stream - item gets margin after expand
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · · · · · * /
2013-08-19 22:30:57 +09:00
function rememberMyScrollPos ( obj , id , offset ) {
if ( typeof offset == 'undefined' ) {
var offset = 0 ;
2015-07-02 02:15:31 +09:00
}
2013-08-19 22:30:57 +09:00
if ( typeof window . scrollpositions == 'undefined' ) { window . scrollpositions = new Object ( ) ; }
window . scrollpositions [ id ] = obj . offset ( ) . top - $ ( window ) . scrollTop ( ) - offset ;
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
· Go back to my scroll po
·
· @ param obj : jQuery object to put in the remebered position
2015-07-02 02:15:31 +09:00
· @ param id : id for remembered position
2013-08-19 22:30:57 +09:00
· @ param animate : if we want to animate the scroll
· @ param callback : function to run when animation stops
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · · · · · * /
2013-08-19 22:30:57 +09:00
function backToMyScrollPos ( obj , id , animate , callback ) {
var pos = obj . offset ( ) . top - window . scrollpositions [ id ] ;
if ( animate ) {
2014-01-29 03:42:47 +09:00
if ( animate == 'animate' || animate === true ) {
animate = 1000 ;
}
2013-08-19 22:30:57 +09:00
if ( typeof callback !== 'undefined' ) {
2014-11-24 21:47:45 +09:00
$ ( 'html, body' ) . animate ( { scrollTop : pos } , animate , 'swing' , function ( ) {
2013-08-19 22:30:57 +09:00
callback ( ) ;
2015-07-02 02:15:31 +09:00
} ) ;
2013-08-19 22:30:57 +09:00
}
else {
2014-11-24 21:47:45 +09:00
$ ( 'html, body' ) . animate ( { scrollTop : pos } , animate , 'swing' ) ;
2013-08-19 22:30:57 +09:00
}
}
else {
$ ( 'html, body' ) . scrollTop ( pos ) ;
2015-07-02 02:15:31 +09:00
}
}
2013-08-19 22:30:57 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
· Scroll to a stream item
·
· @ param streamItem : jQuery object to scroll to
·
· · · · · · · · · · · · · * /
2013-08-19 22:30:57 +09:00
2015-07-02 02:15:31 +09:00
function scrollToQueet ( streamItem ) {
var streamItemPos = streamItem . offset ( ) . top ;
var windowHeight = $ ( window ) . height ( ) ;
var streamItemHeight = streamItem . outerHeight ( ) ;
// console.log(streamItemHeight);
// console.log(windowHeight);
var newScrollPos = Math . round ( streamItemPos - windowHeight / 2 + streamItemHeight / 2 ) ;
$ ( 'html, body' ) . scrollTop ( newScrollPos ) ;
}
/ * ·
·
2014-06-02 04:51:28 +09:00
· Clean up user object , remove null etc
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · · · · · * /
2014-06-02 04:51:28 +09:00
function cleanUpUserObject ( data ) {
data . name = data . name || '' ;
data . profile _image _url = data . profile _image _url || '' ;
data . profile _image _url _profile _size = data . profile _image _url _profile _size || '' ;
2015-07-02 02:15:31 +09:00
data . profile _image _url _original = data . profile _image _url _original || '' ;
data . screen _name = data . screen _name || '' ;
2014-06-02 04:51:28 +09:00
data . description = data . description || '' ;
data . location = data . location || '' ;
data . url = data . url || '' ;
data . statusnet _profile _url = data . statusnet _profile _url || '' ;
data . statuses _count = data . statuses _count || 0 ;
data . followers _count = data . followers _count || 0 ;
2015-07-02 02:15:31 +09:00
data . groups _count = data . groups _count || 0 ;
2014-06-02 04:51:28 +09:00
data . friends _count = data . friends _count || 0 ;
2015-07-02 02:15:31 +09:00
return data ;
}
2014-06-02 04:51:28 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
2013-08-19 22:30:57 +09:00
· outerHTML
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · · · · · * /
2013-08-19 22:30:57 +09:00
jQuery . fn . outerHTML = function ( s ) {
return s
? this . before ( s ) . remove ( )
: jQuery ( "<p>" ) . append ( this . eq ( 0 ) . clone ( ) ) . html ( ) ;
2014-05-28 03:40:51 +09:00
} ;
2015-11-23 08:24:33 +09:00
/ * ·
·
· Sort divs by attribute descending
·
· · · · · · · · · · · · · * /
jQuery . fn . sortDivsByAttrDesc = function sortDivsByAttrDesc ( attr ) {
$ ( "> div" , this [ 0 ] ) . sort ( dec _sort ) . appendTo ( this [ 0 ] ) ;
function dec _sort ( a , b ) { return parseInt ( $ ( b ) . attr ( attr ) , 10 ) > parseInt ( $ ( a ) . attr ( attr ) , 10 ) ? 1 : - 1 ; }
}
2014-05-28 03:40:51 +09:00
2015-07-02 02:15:31 +09:00
/ * ·
·
· Stuff to get and set selection / caret in contenteditables
·
· · · · · · · · · · · · · * /
2014-05-28 03:40:51 +09:00
function getSelectionInElement ( element ) {
var caretOffset = Array ( 0 , 0 ) ;
var doc = element . ownerDocument || element . document ;
var win = doc . defaultView || doc . parentWindow ;
var sel ;
var range = win . getSelection ( ) . getRangeAt ( 0 ) ;
var preCaretRangeEnd = range . cloneRange ( ) ;
preCaretRangeEnd . selectNodeContents ( element ) ;
preCaretRangeEnd . setEnd ( range . endContainer , range . endOffset ) ;
caretOffset [ 1 ] = preCaretRangeEnd . toString ( ) . length ;
var preCaretRangeStart = range . cloneRange ( ) ;
preCaretRangeStart . selectNodeContents ( element ) ;
preCaretRangeStart . setEnd ( range . startContainer , range . startOffset ) ;
2015-07-02 02:15:31 +09:00
caretOffset [ 0 ] = preCaretRangeStart . toString ( ) . length ;
2014-05-28 03:40:51 +09:00
return caretOffset ;
2015-07-02 02:15:31 +09:00
}
2014-05-28 03:40:51 +09:00
function getTextNodesIn ( node ) {
var textNodes = [ ] ;
if ( node . nodeType == 3 ) {
textNodes . push ( node ) ;
}
else {
var children = node . childNodes ;
for ( var i = 0 , len = children . length ; i < len ; ++ i ) {
textNodes . push . apply ( textNodes , getTextNodesIn ( children [ i ] ) ) ;
}
}
return textNodes ;
}
function setSelectionRange ( el , start , end ) {
if ( document . createRange && window . getSelection ) {
var range = document . createRange ( ) ;
range . selectNodeContents ( el ) ;
var textNodes = getTextNodesIn ( el ) ;
var foundStart = false ;
var charCount = 0 , endCharCount ;
for ( var i = 0 , textNode ; textNode = textNodes [ i ++ ] ; ) {
endCharCount = charCount + textNode . length ;
if ( endCharCount == start && endCharCount == end ) {
endCharCount = endCharCount + 1 ;
2015-07-02 02:15:31 +09:00
}
2014-05-28 03:40:51 +09:00
if ( ! foundStart && start >= charCount
&& ( start < endCharCount ||
( start == endCharCount && i < textNodes . length ) ) ) {
range . setStart ( textNode , start - charCount ) ;
foundStart = true ;
}
if ( foundStart && end <= endCharCount ) {
range . setEnd ( textNode , end - charCount ) ;
break ;
}
charCount = endCharCount ;
}
var sel = window . getSelection ( ) ;
sel . removeAllRanges ( ) ;
sel . addRange ( range ) ;
} else if ( document . selection && document . body . createTextRange ) {
var textRange = document . body . createTextRange ( ) ;
textRange . moveToElementText ( el ) ;
textRange . collapse ( true ) ;
textRange . moveEnd ( "character" , end ) ;
textRange . moveStart ( "character" , start ) ;
textRange . select ( ) ;
}
}
function createRangeFromCharacterIndices ( containerEl , start , end ) {
var charIndex = 0 , range = document . createRange ( ) , foundStart = false , stop = { } ;
range . setStart ( containerEl , 0 ) ;
range . collapse ( true ) ;
function traverseTextNodes ( node ) {
if ( node . nodeType == 3 ) {
var nextCharIndex = charIndex + node . length ;
if ( ! foundStart && start >= charIndex && start <= nextCharIndex ) {
range . setStart ( node , start - charIndex ) ;
foundStart = true ;
}
if ( foundStart && end >= charIndex && end <= nextCharIndex ) {
range . setEnd ( node , end - charIndex ) ;
throw stop ;
}
charIndex = nextCharIndex ;
} else {
for ( var i = 0 , len = node . childNodes . length ; i < len ; ++ i ) {
traverseTextNodes ( node . childNodes [ i ] ) ;
}
}
}
try {
traverseTextNodes ( containerEl ) ;
} catch ( ex ) {
if ( ex == stop ) {
return range ;
} else {
throw ex ;
}
}
}
function deleteBetweenCharacterIndices ( el , from , to ) {
var range = createRangeFromCharacterIndices ( el , from , to ) ;
2015-01-22 03:15:22 +09:00
if ( typeof range != 'undefined' ) {
2015-07-02 02:15:31 +09:00
range . deleteContents ( ) ;
2015-01-22 03:15:22 +09:00
}
2014-09-20 09:53:10 +09:00
}
2015-07-02 02:15:31 +09:00
/ * ·
·
2014-09-20 09:53:10 +09:00
· Shorten urls in a queet - box
2015-07-02 02:15:31 +09:00
·
· · · · · · · · · · · · · * /
2014-09-20 09:53:10 +09:00
function shortenUrlsInBox ( shortenButton ) {
shortenButton . addClass ( 'disabled' ) ;
2015-07-02 02:15:31 +09:00
2014-09-20 09:53:10 +09:00
$ . each ( shortenButton . parent ( ) . parent ( ) . siblings ( '.syntax-middle' ) . find ( 'span.url' ) , function ( key , obj ) {
2014-09-25 21:02:36 +09:00
var url = $ . trim ( $ ( obj ) . text ( ) ) ;
2015-07-02 02:15:31 +09:00
2014-09-20 09:53:10 +09:00
display _spinner ( ) ;
2015-07-02 02:15:31 +09:00
2015-07-11 04:35:16 +09:00
$ . ajax ( { url : window . urlShortenerAPIURL + '?format=jsonp&action=shorturl&signature=' + window . urlShortenerSignature + '&url=' + encodeURIComponent ( url ) , type : "GET" , dataType : "jsonp" ,
success : function ( data ) {
2014-09-20 09:53:10 +09:00
2015-07-11 04:35:16 +09:00
if ( typeof data . shorturl != 'undefined' ) {
2015-07-02 02:15:31 +09:00
2015-07-11 04:35:16 +09:00
shortenButton . closest ( '.queet-toolbar' ) . siblings ( '.upload-image-container' ) . children ( 'img[data-shorturl="' + data . url . url + '"]' ) . attr ( 'data-shorturl' , data . shorturl ) ;
shortenButton . parent ( ) . parent ( ) . siblings ( '.queet-box-syntax' ) . html ( shortenButton . parent ( ) . parent ( ) . siblings ( '.queet-box-syntax' ) . html ( ) . replace ( $ ( '<div/>' ) . text ( data . url . url ) . html ( ) , data . shorturl ) ) ;
shortenButton . parent ( ) . parent ( ) . siblings ( '.queet-box-syntax' ) . trigger ( 'keyup' ) ;
shortenButton . addClass ( 'disabled' ) ; // make sure the button is disabled right after
}
remove _spinner ( ) ;
} ,
error : function ( data ) {
console . log ( data ) ;
remove _spinner ( ) ;
2014-09-20 09:53:10 +09:00
}
2015-07-11 04:35:16 +09:00
} ) ;
2014-09-20 09:53:10 +09:00
} ) ;
2015-05-30 00:30:03 +09:00
}
2015-11-17 01:40:04 +09:00
2015-11-23 08:24:33 +09:00
2015-11-17 01:40:04 +09:00
/ * ·
·
· Youtube embed link from youtube url
·
· · · · · · · · · · · · · * /
function youTubeEmbedLinkFromURL ( url ) {
var youtubeId = url . replace ( 'http://www.youtube.com/watch?v=' , '' ) . replace ( 'https://www.youtube.com/watch?v=' , '' ) . replace ( 'http://youtu.be/' , '' ) . replace ( 'https://youtu.be/' , '' ) . substr ( 0 , 11 ) ;
// get start time hash
var l = document . createElement ( "a" ) ;
l . href = url ;
if ( l . hash . substring ( 0 , 3 ) == '#t=' ) {
return '//www.youtube.com/embed/' + youtubeId + '?start=' + l . hash . substring ( 3 ) ;
}
else {
return '//www.youtube.com/embed/' + youtubeId ;
}
}
2016-01-22 00:15:56 +09:00
/ * ·
·
· String similarity
·
· @ params string1 , string2 :
· @ returns ( int ) percent similarity
·
· · · · · · · · · · · · · * /
function stringSimilarity ( string1 , string2 ) {
if ( typeof string1 != 'string' || typeof string2 != 'string' ) {
return 0 ;
}
// trim and strip html tags
string1 = $ ( '<div/>' ) . html ( $ . trim ( string1 ) ) . text ( ) ;
string2 = $ ( '<div/>' ) . html ( $ . trim ( string2 ) ) . text ( ) ;
var longestStringLength = string1 . length ;
if ( string2 . length > string1 . length ) {
longestStringLength = string2 . length ;
}
var distanceArray = levenshteinenator ( string1 , string2 ) ;
var distance = distanceArray [ distanceArray . length - 1 ] [ distanceArray [ distanceArray . length - 1 ] . length - 1 ] ;
var percentSimilarity = 100 - Math . round ( distance / longestStringLength * 100 ) ;
return percentSimilarity ;
}
// from http://andrew.hedges.name/experiments/levenshtein/
var levenshteinenator = ( function ( ) {
/ * *
* @ param String a
* @ param String b
* @ return Array
* /
function levenshteinenator ( a , b ) {
var cost ;
var m = a . length ;
var n = b . length ;
// make sure a.length >= b.length to use O(min(n,m)) space, whatever that is
if ( m < n ) {
var c = a ; a = b ; b = c ;
var o = m ; m = n ; n = o ;
}
var r = [ ] ; r [ 0 ] = [ ] ;
for ( var c = 0 ; c < n + 1 ; ++ c ) {
r [ 0 ] [ c ] = c ;
}
for ( var i = 1 ; i < m + 1 ; ++ i ) {
r [ i ] = [ ] ; r [ i ] [ 0 ] = i ;
for ( var j = 1 ; j < n + 1 ; ++ j ) {
cost = a . charAt ( i - 1 ) === b . charAt ( j - 1 ) ? 0 : 1 ;
r [ i ] [ j ] = minimator ( r [ i - 1 ] [ j ] + 1 , r [ i ] [ j - 1 ] + 1 , r [ i - 1 ] [ j - 1 ] + cost ) ;
}
}
return r ;
}
/ * *
* Return the smallest of the three numbers passed in
* @ param Number x
* @ param Number y
* @ param Number z
* @ return Number
* /
function minimator ( x , y , z ) {
if ( x <= y && x <= z ) return x ;
if ( y <= x && y <= z ) return y ;
return z ;
}
return levenshteinenator ;
} ( ) ) ;