2013-08-19 22:30:57 +09:00
2015-06-02 01:27:36 +09:00
/ * · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
· ·
· ·
· Q V I T T E R ·
· ·
· ·
· < o ) ·
· / _ //// ·
· ( _ _ _ _ / ·
· ( o < ·
· o > \ \ \ \ _ \ ·
· \ \ ) \ _ _ _ _ ) ·
· ·
· ·
· @ licstart The following is the entire license notice for the ·
· JavaScript code in this page . ·
· ·
· Copyright ( C ) 2015 Hannes Mannerheim and other contributors ·
· ·
· ·
· 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/>. ·
· ·
· @ licend The above is the entire license notice ·
· for the JavaScript code in this page . ·
· ·
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · * /
2015-01-19 04:36:08 +09:00
2015-02-26 07:46:32 +09:00
/ * ·
·
· Store in localStorage object cache
·
· @ 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
·
· · · · · · · · · * /
function localStorageObjectCache _STORE ( name , unique _id , object ) {
if ( localStorageIsEnabled ( ) ) {
if ( object . length < 1 ) {
// 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 ] ;
}
}
else {
2015-03-06 06:22:48 +09:00
var dataToSave = { } ;
dataToSave . modified = Date . now ( ) ;
dataToSave . data = object ;
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' ) {
removeOldestLocalStorageEntries ( function ( ) {
localStorageObjectCache _STORE ( name , unique _id , object ) ;
} ) ;
}
else {
console . log ( 'could not store in localStorage, unknown error' ) ;
}
}
2015-02-26 07:46:32 +09:00
}
}
}
2015-03-06 06:22:48 +09:00
/ * ·
·
· Remove the 100 oldest cached items
·
· · · · · · · · · * /
function removeOldestLocalStorageEntries ( callback ) {
// 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 ;
} , { } ) ;
delete modified [ 'undefined' ] ; // we don't want those
// get the modified dates into an array
var modifiedDates = Object . keys ( modified ) ;
modifiedDates . sort ( ) ;
var i = 0 ;
$ . each ( modifiedDates , function ( k , v ) {
delete localStorage [ modified [ v ] ] ;
i ++ ;
if ( i >= 100 ) {
return false ;
}
} ) ;
console . log ( 'removed 100 old localstorage items' ) ;
callback ( ) ;
}
2015-02-26 07:46:32 +09:00
/ * ·
·
· Get from localStorage object cache
·
· @ 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 callback : callback function , returns false if not found
·
· · · · · · · · · * /
function localStorageObjectCache _GET ( name , unique _id , callback ) {
if ( localStorageIsEnabled ( ) ) {
if ( typeof localStorage [ name + '-' + unique _id ] != 'undefined' && localStorage [ name + '-' + unique _id ] !== null ) {
2015-03-06 06:22:48 +09:00
var parsedObject = JSON . parse ( localStorage [ name + '-' + unique _id ] ) ;
if ( typeof parsedObject . modified == 'undefined' || parsedObject . modified === null ) {
// invalid or old localstorage object found, check the whole localstorage!
checkLocalStorage ( ) ;
callback ( false ) ;
}
else {
callback ( parsedObject . data ) ;
}
2015-02-26 07:46:32 +09:00
}
else {
callback ( false ) ;
}
2015-03-06 06:22:48 +09:00
}
else {
callback ( false ) ;
2015-02-26 07:46:32 +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 ;
$ . each ( localStorage , function ( k , entry ) {
if ( typeof entry == 'string' ) {
// check that entry is valid json
try {
var entryParsed = JSON . parse ( entry ) ;
}
catch ( e ) {
delete localStorage [ k ] ;
deleted ++ ;
return true ;
}
// check that it is a valid/currently used data type
var validDataTypes = [
'browsingHistory' ,
'conversation' ,
'favsAndRequeets' ,
'languageData' ,
'fullQueetHtml' ,
'selectedLanguage'
] ;
var thisDataType = k . substring ( 0 , k . indexOf ( '-' ) ) ;
if ( $ . inArray ( thisDataType , validDataTypes ) == - 1 || k . indexOf ( '-' ) == - 1 ) {
delete localStorage [ k ] ;
deleted ++ ;
return true ;
}
// check that it has a modified entry, if not: add one
if ( typeof entryParsed . modified == 'undefined' || entryParsed . modified === null ) {
var newEntry = { } ;
newEntry . modified = dateNow - corrected ; // we want as unique dates as possible
newEntry . data = entryParsed ;
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 ) ) ;
} ) ;
}
}
corrected ++ ;
}
}
} ) ;
console . log ( corrected + ' entries corrected, ' + deleted + ' entries deleted' ) ;
}
2015-02-26 07:46:32 +09:00
2015-06-05 09:17:33 +09:00
/ * ·
·
· User array cache
·
· Stored in window . userArrayCache as instance _url / nickname
· with protocol ( http : // or https://) trimmed off, e.g. "quitter.se/hannes2peer"
·
· · · · · · · · · * /
window . userArrayCache = new Object ( ) ;
function userArrayCacheStore ( data ) {
if ( typeof data == 'undefined' ) {
return false ;
}
// if we are passed a data object with both local and external data, use external data as key
if ( typeof data . local != 'undefined'
&& typeof data . local . statusnet _profile _url != 'undefined'
&& typeof data . external != 'undefined'
&& typeof data . external . statusnet _profile _url != 'undefined' ) {
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( data . external . statusnet _profile _url , data . external . screen _name ) ;
var key = instanceUrlWithoutProtocol + '/' + data . external . screen _name ;
var dataToStore = data ;
}
// we can also get either local...
else if ( typeof data . local != 'undefined' && typeof data . local . statusnet _profile _url != 'undefined' ) {
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( data . local . statusnet _profile _url , data . external . screen _name ) ;
var key = instanceUrlWithoutProtocol + '/' + data . external . screen _name ;
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 ) ;
var key = instanceUrlWithoutProtocol + '/' + data . external . screen _name ;
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 ) ;
var key = instanceUrlWithoutProtocol + '/' + data . screen _name ;
var dataProfileImageUrlWithoutProtocol = removeProtocolFromUrl ( data . profile _image _url ) ;
var siteInstanceURLWithoutProtocol = removeProtocolFromUrl ( window . siteInstanceURL ) ;
// local
if ( dataProfileImageUrlWithoutProtocol . substring ( 0 , siteInstanceURLWithoutProtocol . length ) == siteInstanceURLWithoutProtocol ) {
var dataToStore = { local : data , external : false } ;
}
// external
else {
var dataToStore = { external : data , local : false } ;
}
}
else {
return false ;
}
// store
if ( typeof window . userArrayCache [ key ] == 'undefined' ) {
window . userArrayCache [ key ] = dataToStore ;
}
else {
if ( dataToStore . local ) {
// 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 ;
}
window . userArrayCache [ key ] . local = dataToStore . local ;
}
if ( dataToStore . external ) {
window . userArrayCache [ key ] . external = dataToStore . external ;
}
}
}
function userArrayCacheGetByLocalNickname ( localNickname ) {
if ( typeof window . userArrayCache [ window . siteRootDomain + '/' + localNickname ] != 'undefined' ) {
return window . userArrayCache [ window . siteRootDomain + '/' + localNickname ] ;
}
else {
return false ;
}
}
function userArrayCacheGetByProfileUrlAndNickname ( profileUrl , nickname ) {
var guessedInstanceUrl = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( profileUrl , nickname ) ;
if ( typeof window . userArrayCache [ guessedInstanceUrl + '/' + nickname ] == 'undefined' ) {
return false ;
}
else {
return window . userArrayCache [ guessedInstanceUrl + '/' + nickname ] ;
}
}
/ * ·
·
· Guess instance ' s base installation url without protocol from a profile url
·
· · · · · · · · · * /
function guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname ( profileUrl , nickname ) {
// remove protocol
var guessedInstanceUrl = removeProtocolFromUrl ( profileUrl )
// user/id-style profile urls
if ( guessedInstanceUrl . indexOf ( '/user/' ) > - 1 &&
$ . isNumeric ( guessedInstanceUrl . substring ( guessedInstanceUrl . lastIndexOf ( '/user/' ) + 6 ) ) ) {
guessedInstanceUrl = guessedInstanceUrl . substring ( 0 , guessedInstanceUrl . lastIndexOf ( '/user/' ) ) ;
}
// nickname-style profile urls
else if ( guessedInstanceUrl . substring ( guessedInstanceUrl . lastIndexOf ( '/' ) + 1 ) == nickname ) {
guessedInstanceUrl = guessedInstanceUrl . substring ( 0 , guessedInstanceUrl . lastIndexOf ( '/' ) ) ;
}
// remove trailing "index.php" if the instance doesn't use mod_rewrite
if ( guessedInstanceUrl . substring ( guessedInstanceUrl . lastIndexOf ( '/' ) ) == '/index.php' ) {
guessedInstanceUrl = guessedInstanceUrl . substring ( 0 , guessedInstanceUrl . lastIndexOf ( '/' ) ) ;
}
// 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 ;
}
/ * ·
·
· Remove the protocol ( e . g . "http://" ) from an URL
·
· · · · · · · · · * /
function removeProtocolFromUrl ( url ) {
if ( url . indexOf ( '://' ) == - 1 ) {
return url ;
}
return url . substring ( url . indexOf ( '://' ) + 3 ) ;
}
/ * ·
·
· Iterates recursively through an API response in search for user data to cache
· If we find a "statusnet_profile_url" key we assume the parent is a user array / object
·
· · · · · · · · · · · · · * /
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-01-27 01:13:34 +09:00
/ * ·
·
· Display unread notifications
·
· · · · · · · · · * /
function displayOrHideUnreadNotifications ( notifications ) {
var data = $ . parseJSON ( notifications ) ;
// if this is notifications page, we use the info from the hidden items in the feed
if ( window . currentStream == 'qvitter/statuses/notifications.json' ) {
2015-03-06 00:04:40 +09:00
var new _queets _num = $ ( '#feed-body' ) . find ( '.stream-item.notification.hidden' ) . length ;
2015-01-27 01:13:34 +09:00
if ( new _queets _num == 0 ) {
document . title = window . siteTitle ;
$ ( '#unseen-notifications' ) . hide ( ) ;
}
else {
document . title = window . siteTitle + ' (' + new _queets _num + ')' ;
$ ( '#unseen-notifications' ) . html ( new _queets _num ) ;
$ ( '#unseen-notifications' ) . show ( ) ;
}
}
// all other pages use the header info
else if ( data === null || typeof data == 'undefined' || data . length == 0 ) {
$ ( '#unseen-notifications' ) . hide ( ) ;
document . title = window . siteTitle ;
}
else {
var totNotif = 0 ;
$ . each ( data , function ( k , v ) {
totNotif = totNotif + parseInt ( v , 10 ) ;
} ) ;
if ( totNotif > 0 ) {
$ ( '#unseen-notifications' ) . html ( totNotif ) ;
document . title = window . siteTitle + ' (' + totNotif + ')' ; // update html page title
$ ( '#unseen-notifications' ) . show ( ) ;
}
else {
$ ( '#unseen-notifications' ) . hide ( ) ;
document . title = window . siteTitle ;
}
}
}
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
·
· · · · · · · · · · · · · * /
function iterateRecursiveReplaceHtmlSpecialChars ( obj ) {
for ( var property in obj ) {
if ( obj . hasOwnProperty ( property ) ) {
if ( typeof obj [ property ] == "object" ) {
iterateRecursiveReplaceHtmlSpecialChars ( obj [ property ] ) ;
}
2015-06-05 09:17:33 +09:00
else if ( typeof obj [ property ] == 'string' && property != 'statusnet_html' && property != 'source' ) {
obj [ property ] = replaceHtmlSpecialChars ( obj [ property ] ) ;
2015-01-19 04:36:08 +09:00
}
}
}
return obj ;
}
function replaceHtmlSpecialChars ( text ) {
var map = {
'&' : '&' ,
'<' : '<' ,
'>' : '>' ,
'"' : '"' ,
"'" : '''
} ;
return text . replace ( /[&<>"']/g , function ( m ) { return map [ m ] ; } ) ;
}
2013-08-20 04:15:15 +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 ) {
return false ;
}
}
2013-09-03 01:13:15 +09:00
/ * ·
·
· Checks if register form is valid
·
· @ returns true or false
·
· · · · · · · · · * /
function validateRegisterForm ( o ) {
var nickname = o . find ( '#signup-user-nickname-step2' ) ;
var fullname = o . find ( '#signup-user-name-step2' ) ;
var email = o . find ( '#signup-user-email-step2' ) ;
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' ) ;
var passwords = o . find ( '#signup-user-password1-step2,#signup-user-password2-step2' ) ;
var allFieldsValid = true ;
if ( nickname . val ( ) . length > 1 && /^[a-zA-Z0-9]+$/ . test ( nickname . val ( ) ) ) {
nickname . removeClass ( 'invalid' ) ; } else { nickname . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
if ( fullname . val ( ) . length < 255 ) {
fullname . removeClass ( 'invalid' ) ; } else { fullname . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
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 . val ( ) ) ) {
email . removeClass ( 'invalid' ) ; } else { email . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
if ( $ . trim ( homepage . val ( ) ) . length == 0 || /^(ftp|http|https):\/\/[^ "]+$/ . test ( homepage . val ( ) ) ) {
homepage . removeClass ( 'invalid' ) ; } else { homepage . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
if ( bio . val ( ) . length < 140 ) {
bio . removeClass ( 'invalid' ) ; } else { bio . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
if ( loc . val ( ) . length < 255 ) {
loc . removeClass ( 'invalid' ) ; } else { loc . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
if ( password1 . val ( ) . length > 5 && password2 . val ( ) . length > 5 && password1 . val ( ) == password2 . val ( ) ) {
passwords . removeClass ( 'invalid' ) ; } else { passwords . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
return allFieldsValid ;
}
2014-06-02 04:51:28 +09:00
/ * ·
·
· Checks if edit profile form is valid
·
· @ returns true or false
·
· · · · · · · · · * /
function validateEditProfileForm ( o ) {
var fullname = o . find ( 'input.fullname' ) ;
var homepage = o . find ( 'input.url' ) ;
var bio = o . find ( 'textarea.bio' ) ;
var loc = o . find ( 'input.location' ) ;
var allFieldsValid = true ;
if ( fullname . val ( ) . length < 255 ) {
fullname . removeClass ( 'invalid' ) ; } else { fullname . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
if ( $ . trim ( homepage . val ( ) ) . length == 0 || /^(ftp|http|https):\/\/[^ "]+$/ . test ( homepage . val ( ) ) ) {
homepage . removeClass ( 'invalid' ) ; } else { homepage . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
if ( bio . val ( ) . length < 140 ) {
bio . removeClass ( 'invalid' ) ; } else { bio . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
if ( loc . val ( ) . length < 255 ) {
loc . removeClass ( 'invalid' ) ; } else { loc . addClass ( 'invalid' ) ; if ( allFieldsValid ) allFieldsValid = false ; }
return allFieldsValid ;
}
2015-05-30 00:30:03 +09:00
/ * ·
·
· Validate a hex color and add # if missing
·
· @ returns hex color with # or false
·
· · · · · · · · · * /
function isValidHexColor ( maybeValidHexColor ) {
if ( maybeValidHexColor . substring ( 0 , 1 ) != '#' ) {
maybeValidHexColor = '#' + maybeValidHexColor ;
}
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
/ * ·
·
· 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
2013-08-26 05:34:09 +09:00
·
· · · · · · · · · * /
function changeDesign ( obj ) {
2015-05-30 00:30:03 +09:00
// if we're logged out and this is the front page, we use the default design
if ( ! window . loggedIn && window . currentStream == 'statuses/public_timeline.json' ) {
obj . backgroundimage = window . fullUrlToThisQvitterApp + window . siteBackground ;
obj . backgroundcolor = window . defaultBackgroundColor ;
obj . linkcolor = window . defaultLinkColor ;
}
// if no object is defined, abort
if ( typeof obj == 'undefined' ) {
return false ;
}
// remember the design for this stream
if ( typeof window . oldStreamsDesigns [ theUserOrGroupThisStreamBelongsTo ( window . currentStream ) ] == 'undefined' ) {
window . oldStreamsDesigns [ theUserOrGroupThisStreamBelongsTo ( window . currentStream ) ] = new Object ( ) ;
}
// change design elements
if ( typeof obj . backgroundimage != 'undefined' ) {
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 ) {
$ ( 'body' ) . css ( 'background-image' , 'url(\'' + obj . backgroundimage + '\')' ) ;
2013-08-26 05:34:09 +09:00
}
2015-05-30 00:30:03 +09:00
window . oldStreamsDesigns [ theUserOrGroupThisStreamBelongsTo ( window . currentStream ) ] . backgroundimage = obj . backgroundimage ;
}
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 ) ;
window . oldStreamsDesigns [ theUserOrGroupThisStreamBelongsTo ( window . currentStream ) ] . backgroundcolor = obj . backgroundcolor ;
}
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 ) ;
window . oldStreamsDesigns [ theUserOrGroupThisStreamBelongsTo ( window . currentStream ) ] . linkcolor = obj . linkcolor ;
}
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
/ * ·
·
· Change background color
·
· @ param newLinkColor : hex value with or without #
·
· · · · · · · · · * /
function changeBackgroundColor ( newBackgroundColor ) {
// check hex value
var validHexColor = isValidHexColor ( newBackgroundColor ) ;
if ( ! validHexColor ) {
console . log ( 'invalid hex value for backgroundcolor: ' + newBackgroundColor ) ;
return false ;
}
$ ( 'body' ) . css ( 'background-color' , validHexColor ) ;
}
2013-08-20 04:15:15 +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 #
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-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-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 ) ;
headStyleText = replaceFromStringEndToStringStart ( headStyleText , '/*LIGHTERBORDERBOTTOMCOLORSTART*/' , '/*LIGHTERBORDERBOTTOMCOLOREND*/' , lighterColor08 ) ;
headStyle . text ( headStyleText ) ;
}
function replaceFromStringEndToStringStart ( string , fromStringEnd , toStringStart , withString ) {
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
/ * ·
·
· Right - to - left language detection < o
· ( //
· @ param s : the stream - item to detect rtl in
·
· @ return a stream - item that might have rtl - class added
·
· · · · · · · · · * /
function detectRTL ( s ) {
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-01-19 04:36:08 +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 , '' ) ) ;
// count ltr and rtl chars
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 ++ ; }
}
// if there are more rtl chars than ltr
if ( RTLnum > LTRnum ) { $streamItem . children ( '.stream-item' ) . children ( '.queet' ) . addClass ( 'rtl' ) ; }
// if no chars (that we are interested, but body is set to rtl)
else if ( $queetText . html ( ) . length == 0 && $ ( 'body' ) . hasClass ( 'rtl' ) ) {
$streamItem . children ( '.stream-item' ) . children ( '.queet' ) . addClass ( 'rtl' ) ;
}
2015-01-19 04:36:08 +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">' ) ; // hacky way to get @#! into mention tags to stop bidirection (css sets an @ with before content method)
2013-08-19 22:30:57 +09:00
}
/ * ·
·
· Takes twitter style dates and converts them
·
· @ param tdate : date in the form of e . g . 'Mon Aug 05 16:30:22 +0200 2013'
·
· @ return user friendly dates . . M _
· W
· Needs global language object window . sL to be populated
·
· · · · · · · · · · · · · * /
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
var system _date = new Date ( Date . parse ( tdate ) ) ;
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
var system _date = new Date ( Date . parse ( tdate ) ) ;
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 ( ) ) ;
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' ] ;
var days = [ 'Sun' , 'Mon' , 'Tue' , 'Wed' , 'Thu' , 'Fri' , 'Sat' ] ;
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 ;
}
/ * ·
·
2014-10-03 02:24:54 +09:00
· If we want to make sure we have empty arrays , not empty objects
2014-05-23 21:13:38 +09:00
·
· · · · · · · · · · * /
2014-10-03 02:24:54 +09:00
function convertEmptyObjectToEmptyArray ( data ) {
2014-05-23 21:13:38 +09:00
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 ;
2014-05-28 03:40:51 +09:00
}
2014-05-23 21:13:38 +09:00
}
2013-08-19 22:30:57 +09:00
/ * ·
·
· Return all URL : s in a string
·
· @ param string : the string to search
·
· @ return an array with the found urls
·
· · · · · · · · · · * /
function findUrls ( text ) {
var source = ( text || '' ) . toString ( ) ;
var urlArray = [ ] ;
var url ;
var matchArray ;
var regexToken = /(((ftp|https?):\/\/)[\-\w@:%_\+.~#?,&\/\/=]+)|((mailto:)?[_.\w-]+@([\w][\w\-]+\.)+[a-zA-Z]{2,3})/g ;
while ( ( matchArray = regexToken . exec ( source ) ) !== null ) {
var token = matchArray [ 0 ] ;
urlArray . push ( token ) ;
}
return urlArray ;
}
/ * ·
·
· Functions to show and remove the spinner
·
· · · · · · · · · · · · * /
2014-11-24 21:47:45 +09:00
function display _spinner ( parent ) {
2014-09-25 06:20:35 +09:00
if ( $ ( '.loader' ) . length < 1 ) {
2014-11-24 21:47:45 +09:00
if ( typeof parent == 'undefined' ) {
$ ( '.global-nav' ) . removeClass ( 'show-logo' ) ;
var parent = 'body' ;
}
$ ( 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 > \
' ) ;
}
2013-08-19 22:30:57 +09:00
}
function remove _spinner ( ) {
2014-09-20 09:53:10 +09:00
$ ( '.loader' ) . remove ( ) ;
2014-11-24 21:47:45 +09:00
$ ( '.global-nav' ) . addClass ( 'show-logo' ) ;
2013-08-19 22:30:57 +09:00
}
2014-09-20 09:53:10 +09:00
2013-08-19 22:30:57 +09:00
/ * ·
·
· Converts ... - attachment - links to spans
·
· ( Attachments are loaded when queets expand )
·
· · · · · · · · · · · · · · · · · * /
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 ) {
$ ( this ) . replaceWith ( $ ( '<span class="attachment more" data-attachment-id="' + attachment _id + '">…</span>' ) ) ;
}
2013-08-19 22:30:57 +09:00
}
} ) ;
}
/ * ·
·
2015-03-06 06:22:48 +09:00
· Updates the browsing history local storage
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' ) ;
2013-08-20 04:15:15 +09:00
historyContainer [ i ] . dataStreamHeader = $ ( obj ) . attr ( 'data-stream-header' ) ;
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' ) ;
}
$ ( '#history-container' ) . sortable ( { delay : 100 } ) ;
$ ( '#history-container' ) . disableSelection ( ) ;
2013-08-19 22:30:57 +09:00
}
}
/ * ·
·
· Loads history from local storage to menu
·
· · · · · · · · · · · · · * /
function loadHistoryFromLocalStorage ( ) {
2013-08-20 04:15:15 +09:00
if ( localStorageIsEnabled ( ) ) {
2015-03-06 06:22:48 +09:00
localStorageObjectCache _GET ( 'browsingHistory' , window . loggedIn . screen _name , function ( data ) {
if ( data ) {
$ ( '#history-container' ) . css ( 'display' , 'block' ) ;
$ ( '#history-container' ) . html ( '' ) ;
$ . each ( data , function ( key , obj ) {
$ ( '#history-container' ) . append ( '<a class="stream-selection" data-stream-header="' + obj . dataStreamHeader + '" href="' + obj . dataStreamHref + '">' + obj . dataStreamHeader + '</i><i class="chev-right"></i></a>' ) ;
} ) ;
}
} ) ;
2013-08-20 04:15:15 +09:00
updateHistoryLocalStorage ( ) ;
2013-08-19 22:30:57 +09:00
}
}
/ * ·
·
· Does stream need a ? or a &
·
· · · · · · · · · · · · · * /
function qOrAmp ( stream ) {
if ( stream . substr ( - 5 ) == '.json' ) {
return '?' ;
}
else {
return '&' ;
}
}
/ * ·
·
· 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
· @ param btn : the button
·
· · · · · · · · · · · · · * /
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' ) ;
2014-10-18 00:08:14 +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' ) ;
btn . addClass ( 'disabled' ) ;
}
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' ) ;
btn . addClass ( 'too-long' ) ;
}
2014-05-16 11:07:30 +09:00
else {
btn . removeClass ( 'enabled' ) ;
btn . addClass ( 'disabled' ) ;
2014-10-18 00:08:14 +09:00
btn . removeClass ( 'too-long' ) ;
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-05-16 11:07:30 +09:00
// counter color
if ( ( window . textLimit - numchars ) < 0 ) {
trgt . css ( 'color' , '#D40D12' ) ;
}
else {
trgt . removeAttr ( 'style' ) ;
}
2013-08-19 22:30:57 +09:00
}
2014-05-17 05:41:45 +09:00
// unlimited
else {
if ( numchars > 0 ) {
btn . removeClass ( 'disabled' ) ;
btn . addClass ( 'enabled' ) ;
}
else {
btn . removeClass ( 'enabled' ) ;
btn . addClass ( 'disabled' ) ;
}
}
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
·
· · · · · · · · · · · · · * /
function rememberMyScrollPos ( obj , id , offset ) {
if ( typeof offset == 'undefined' ) {
var offset = 0 ;
}
if ( typeof window . scrollpositions == 'undefined' ) { window . scrollpositions = new Object ( ) ; }
window . scrollpositions [ id ] = obj . offset ( ) . top - $ ( window ) . scrollTop ( ) - offset ;
}
/ * ·
·
· Go back to my scroll po
·
· @ param obj : jQuery object to put in the remebered position
· @ param id : id for remembered position
· @ param animate : if we want to animate the scroll
· @ param callback : function to run when animation stops
·
· · · · · · · · · · · · · * /
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 ( ) ;
} ) ;
}
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 ) ;
}
}
2014-06-02 04:51:28 +09:00
/ * ·
·
· Clean up user object , remove null etc
·
· · · · · · · · · · · · · * /
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 || '' ;
data . profile _image _url _original = data . profile _image _url _original || '' ;
data . screen _name = data . screen _name || '' ;
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 ;
data . groups _count = data . groups _count || 0 ;
data . friends _count = data . friends _count || 0 ;
return data ;
}
2013-08-19 22:30:57 +09:00
/ * ·
·
· outerHTML
·
· · · · · · · · · · · · · * /
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
} ;
/ * ·
·
· Stuff to get and set selection / caret in contenteditables
·
· · · · · · · · · · · · · * /
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 ) ;
caretOffset [ 0 ] = preCaretRangeStart . toString ( ) . length ;
return caretOffset ;
}
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 ;
}
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' ) {
range . deleteContents ( ) ;
}
2014-09-20 09:53:10 +09:00
}
/ * ·
·
· Shorten urls in a queet - box
·
· · · · · · · · · · · · · * /
function shortenUrlsInBox ( shortenButton ) {
shortenButton . addClass ( 'disabled' ) ;
$ . 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 ( ) ) ;
2014-09-20 09:53:10 +09:00
display _spinner ( ) ;
$ . ajax ( { url : window . urlShortenerAPIURL + '?format=jsonp&action=shorturl&signature=' + window . urlShortenerSignature + '&url=' + encodeURIComponent ( url ) , type : "GET" , dataType : "jsonp" , success : function ( data ) {
if ( typeof data . shorturl != 'undefined' ) {
2014-09-25 21:02:36 +09:00
2014-11-24 21:47:45 +09:00
shortenButton . closest ( '.queet-toolbar' ) . siblings ( '.upload-image-container' ) . children ( 'img[data-shorturl="' + data . url . url + '"]' ) . attr ( 'data-shorturl' , data . shorturl ) ;
2014-11-05 01:32:40 +09:00
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 ) ) ;
2014-09-20 09:53:10 +09:00
shortenButton . parent ( ) . parent ( ) . siblings ( '.queet-box-syntax' ) . trigger ( 'keyup' ) ;
shortenButton . addClass ( 'disabled' ) ; // make sure the button is disabled right after
}
remove _spinner ( ) ;
} } ) ;
} ) ;
2015-05-30 00:30:03 +09:00
}
/ * ·
·
· Return the user screen name that this stream belongs to . last resort just return the stream
·
· · · · · · · · · · · · · * /
function theUserOrGroupThisStreamBelongsTo ( stream ) {
// if screen_name is given as get-var, use that
if ( stream . indexOf ( 'screen_name=' ) > - 1 ) {
var thisUsersScreenName = stream . substring ( stream . indexOf ( 'screen_name=' ) + 12 ) ;
if ( thisUsersScreenName . indexOf ( '&=' ) > - 1 ) {
thisUsersScreenName = thisUsersScreenName . substring ( 0 , stream . indexOf ( '&' ) ) ;
}
return thisUsersScreenName ;
}
// groups
else if ( stream . indexOf ( 'statusnet/groups/timeline/' ) > - 1
|| stream . indexOf ( 'statusnet/groups/membership/' ) > - 1
|| stream . indexOf ( 'statusnet/groups/admins/' ) > - 1 ) {
var groupName = '!' + stream . substring ( stream . lastIndexOf ( '/' ) + 1 , stream . indexOf ( '.json' ) ) ;
return groupName ;
}
// otherwise, and if we're logged in, we assume this is my stream
else if ( window . loggedIn ) {
return window . loggedIn . screen _name ;
}
else {
return stream ;
}
}