1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896 |
- /*· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
- · ·
- · ·
- · 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. ·
- · ·
- · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · */
- /* ·
- ·
- · Trigger click on <input type="file"> elements
- ·
- · @param inputFile: jQuery element to trigger click on
- ·
- · · · · · · · · · */
- function triggerClickOnInputFile(inputFile) {
- if(typeof bowser != 'undefined') {
- var bowserIntVersion = parseInt(bowser.version,10);
- if(typeof bowser.chrome != 'undefined' && bowser.chrome === true && bowserIntVersion < 53
- || typeof bowser.opera != 'undefined' && bowser.opera === true && bowserIntVersion < 39
- || typeof bowser.safari != 'undefined' && bowser.safari === true && bowserIntVersion < 9) {
- var evt = document.createEvent("HTMLEvents");
- evt.initEvent("click", true, true);
- inputFile[0].dispatchEvent(evt);
- console.log('triggering click on on input file element with the old trigger hack for older browsers...');
- }
- else {
- inputFile.trigger('click');
- console.log('regular click triggered on input file element');
- }
- }
- else {
- inputFile.trigger('click');
- console.log('no bowser object found: regular click triggered on input file element');
- }
- console.log(bowser);
- }
- /* ·
- ·
- · 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() === false) {
- return false;
- }
- 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];
- }
- return false;
- }
- var dataToSave = {};
- dataToSave.modified = Date.now();
- dataToSave.cdata = LZString.compressToUTF16(JSON.stringify(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');
- }
- }
- }
- /* ·
- ·
- · 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 ' + i + ' old localstorage items');
- if(i>0) {
- callback();
- return true;
- }
- else {
- return false;
- }
- }
- /* ·
- ·
- · 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
- ·
- · · · · · · · · · */
- function localStorageObjectCache_GET(name, unique_id) {
- 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;
- }
- catch(e) {
- return false;
- }
- }
- }
- // 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' ;
- }
- }
- else {
- return name;
- }
- }
- function checkLocalStorage() {
- if(localStorageIsEnabled() === false) {
- console.log('localstorage disabled');
- return false;
- }
- console.log('checking localStorage for invalid entries');
- var dateNow = Date.now()
- var corrected = 0;
- var deleted = 0;
- var compressed = 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',
- 'queetBoxInput',
- 'streamState',
- 'languageErrorMessageDiscarded'
- ];
- 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.cdata = 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));
- });
- }
- }
- entryParsed = newEntry;
- corrected++;
- }
- // 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;
- }
- var dataCompressed = LZString.compressToUTF16(JSON.stringify(entryParsed.data));
- var newCompressedEntry = {};
- newCompressedEntry.modified = entryParsed.modified;
- newCompressedEntry.cdata = dataCompressed;
- localStorage.setItem(k, JSON.stringify(newCompressedEntry));
- compressed++;
- }
- }
- });
- console.log(corrected + ' entries corrected, ' + deleted + ' entries deleted, ' + compressed + ' entries compressed');
- }
- /* ·
- ·
- · 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') {
- // 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();
- });
- if(successfulRemoval === false) {
- return false;
- }
- }
- else {
- return false;
- }
- }
- }
- /* ·
- ·
- · Block/unblock user and do necessary stuff
- ·
- · · · · · · · · · */
- function blockUser(arg, callback) {
- $('body').click(); // a click somewhere hides any open menus
- // 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) {
- $('body').click(); // a click somewhere hides any open menus
- // 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
- if(window.currentStreamObject.name == 'user blocks' && window.currentStreamObject.nickname == window.loggedIn.screen_name) {
- $.each($('.stream-item.user[data-user-id="' + userId + '"]'),function(){
- slideUpAndRemoveStreamItem($(this));
- });
- }
- // 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;
- }
- }
- /* ·
- ·
- · 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;
- }
- /* ·
- ·
- · 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')));
- }
- });
- }
- /* ·
- ·
- · Is this a local URL?
- ·
- · · · · · · · · · */
- function isLocalURL(url) {
- if(url.substring(0,window.siteInstanceURL.length) == window.siteInstanceURL // site url
- || url.substring(0,window.siteAttachmentURLBase.length) == window.siteAttachmentURLBase // attachment url
- || url.substring(0,window.siteAvatarURLBase.length) == window.siteAvatarURLBase // avatar url
- ) {
- return true;
- }
- return false;
- }
- /* ·
- ·
- · Check for hidden items and show the new queets bar if there are any
- ·
- · · · · · · · · · */
- function maybeShowTheNewQueetsBar() {
- 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) {
- $('#new-queets-bar').parent().removeClass('hidden');
- // bar label
- if(newQueetsNum == 1) { var q_txt = window.sL.newQueet; }
- else { var q_txt = window.sL.newQueets; }
- if(window.currentStreamObject.name == 'notifications') {
- if(newQueetsNum == 1) { var q_txt = window.sL.newNotification; }
- else { var q_txt = window.sL.newNotifications; }
- }
- $('#new-queets-bar').html(q_txt.replace('{new-notice-count}',newQueetsNum));
- }
- }
- /* ·
- ·
- · Align tooltips to the hovered element
- ·
- · · · · · · · · · */
- function alignTooltipTohoveredElement(tooltipElement,tooltipCaret,hovered) {
- var tooltipWidth = tooltipElement.outerWidth();
- var tooltipHeight = tooltipElement.outerHeight();
- var windowWidth = $(window).width();
- var windowScrollPosY = $(window).scrollTop();
- var targetPosX = hovered.offset().left;
- var targetPosY = hovered.offset().top;
- var targetHeight = hovered.outerHeight();
- var targetWidth = hovered.outerWidth();
- // 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;
- var tooltipCaretPosY = targetPosY-8;
- // 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');
- }
- }
- /* ·
- ·
- · Cache the unicode compatible regexps for the syntax highlighting
- ·
- · · · · · · · · · */
- function cacheSyntaxHighlighting() {
- window.syntaxHighlightingRegexps = Object();
- 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|su|sucks|supplies|supply|support|surf|surgery|suzuki|sv|swiss|sx|sy|sydney|systems|sz|taipei|tatar|tattoo|tax|taxi|tc|td|team|tech|technology|tel|temasek|tennis|tf|tg|th|thd|theater|tickets|tienda|tips|tires|tirol|tj|tk|tl|tm|tn|to|today|tokyo|tools|top|toray|toshiba|tours|town|toys|tp|tr|trade|trading|training|travel|trust|tt|tui|tv|tw|tz|ua|ug|uk|um|university|uno|uol|us|uy|uz|va|vacations|vc|ve|vegas|ventures|versicherung|vet|vg|vi|viajes|video|villas|vision|vlaanderen|vn|vodka|vote|voting|voto|voyage|vu|wales|walter|wang|watch|webcam|website|wed|wedding|weir|wf|whoswho|wien|wiki|williamhill|win|wme|work|works|world|ws|wtc|wtf|xbox|xerox|xin|测试|परीक्षा|佛山|慈善|集团|在线|한국|ভারত|八卦|موقع|বাংলা|公益|公司|移动|我爱你|москва|испытание|қаз|онлайн|сайт|срб|бел|时尚|테스트|淡马锡|орг|삼성|சிங்கப்பூர்|商标|商店|商城|дети|мкд|טעסט|工行|中文网|中信|中国|中國|娱乐|谷歌|భారత్|ලංකා|測試|ભારત|भारत|آزمایشی|பரிட்சை|网店|संगठन|餐厅|网络|укр|香港|δοκιμή|飞利浦|إختبار|台湾|台灣|手机|мон|الجزائر|عمان|ایران|امارات|بازار|پاکستان|الاردن|بھارت|المغرب|السعودية|سودان|عراق|مليسيا|澳門|政府|شبكة|გე|机构|组织机构|健康|ไทย|سورية|рус|рф|تونس|みんな|グーグル|ελ|世界|ਭਾਰਤ|网址|游戏|vermögensberater|vermögensberatung|企业|信息|مصر|قطر|广东|இலங்கை|இந்தியா|հայ|新加坡|فلسطين|テスト|政务|xxx|xyz|yachts|yandex|ye|yodobashi|yoga|yokohama|youtube|yt|za|zip|zm|zone|zuerich|zw|oracle|xn--1qqw23a|xn--30rr7y|xn--3bst00m|xn--3ds443g|xn--3e0b707e|xn--45brj9c|xn--45q11c|xn--4gbrim|xn--55qw42g|xn--55qx5d|xn--6frz82g|xn--6qq986b3xl|xn--80adxhks|xn--80ao21a|xn--80asehdb|xn--80aswg|xn--90a3ac|xn--90ais|xn--9et52u|xn--b4w605ferd|xn--c1avg|xn--cg4bki|xn--clchc0ea0b2g2a9gcd|xn--czr694b|xn--czrs0t|xn--czru2d|xn--d1acj3b|xn--d1alf|xn--estv75g|xn--fiq228c5hs|xn--fiq64b|xn--fiqs8s|xn--fiqz9s|xn--fjq720a|xn--flw351e|xn--fpcrj9c3d|xn--fzc2c9e2c|xn--gecrj9c|xn--h2brj9c|xn--hxt814e|xn--i1b6b1a6a2e|xn--imr513n|xn--io0a7i|xn--j1amh|xn--j6w193g|xn--kcrx77d1x4a|xn--kprw13d|xn--kpry57d|xn--kput3i|xn--l1acc|xn--lgbbat1ad8j|xn--mgb9awbf|xn--mgba3a4f16a|xn--mgbaam7a8h|xn--mgbab2bd|xn--mgbayh7gpa|xn--mgbbh1a71e|xn--mgbc0a9azcg|xn--mgberp4a5d4ar|xn--mgbpl2fh|xn--mgbx4cd0ab|xn--mxtq1m|xn--ngbc5azd|xn--node|xn--nqv7f|xn--nqv7fs00ema|xn--nyqy26a|xn--o3cw4h|xn--ogbpf8fl|xn--p1acf|xn--p1ai|xn--pgbs0dh|xn--q9jyb4c|xn--qcka1pmc|xn--rhqv96g|xn--s9brj9c|xn--ses554g|xn--unup4y|xn--vermgensberater-ctb|xn--vermgensberatung-pwb|xn--vhquv|xn--vuq861b|xn--wgbh1c|xn--wgbl6a|xn--xhq521b|xn--xkc2al3hye2a|xn--xkc2dl3a5ee0h|xn--y9a3aq|xn--yfro4i67o|xn--ygbi2ammx|xn--zfr164b)';
- 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|\\.|\\,|\\:|\\-|\\<|\\!|\\?|\\&|\\)|\\\')');
- cacheSyntaxHighlightingGroups();
- }
- /* ·
- ·
- · Cache syntax highlighting for groups
- ·
- · · · · · · · · · */
- function cacheSyntaxHighlightingGroups() {
- if(window.groupNicknamesAndLocalAliases.length > 0) {
- var allGroupNicknamesAndLocalAliases = '(' + window.groupNicknamesAndLocalAliases.join('|') + ')';
- window.syntaxHighlightingRegexps.group = XRegExp.cache('(^|\\s|\\.|<br>| |\\()(\\!)' + allGroupNicknamesAndLocalAliases + '($|\\s|\\.|\\,|\\:|\\-|\\<|\\!|\\?|\\&|\\)|\\\')');
- }
- }
- /* ·
- ·
- · User array cache (called array because it's an array in php)
- ·
- · Stored in window.userArrayCache with unique key like instance_url/nickname
- · with protocol (http:// or https://) trimmed off, e.g. "quitter.se/hannes2peer"
- ·
- · · · · · · · · · */
- window.userArrayCache = new Object();
- window.convertUriToUserArrayCacheKey = new Object();
- window.convertStatusnetProfileUrlToUserArrayCacheKey = new Object();
- window.convertLocalIdToUserArrayCacheKey = 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.local.screen_name);
- var key = instanceUrlWithoutProtocol + '/' + data.local.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 localOrExternal = detectLocalOrExternalUserObject(data);
- // local
- if(localOrExternal == 'local'){
- 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;
- window.userArrayCache[key].modified = Date.now();
- // easy conversion between URI and statusnet_profile_url and the key we're using in window.userArrayCache
- window.convertLocalIdToUserArrayCacheKey[parseInt(dataToStore.local.id, 10)] = key;
- window.convertUriToUserArrayCacheKey[dataToStore.local.ostatus_uri] = key;
- window.convertStatusnetProfileUrlToUserArrayCacheKey[dataToStore.local.statusnet_profile_url] = key;
- }
- 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;
- // easy conversion between URI and statusnet_profile_url and the key we're using in window.userArrayCache
- window.convertLocalIdToUserArrayCacheKey[dataToStore.local.id] = key;
- window.convertUriToUserArrayCacheKey[dataToStore.local.ostatus_uri] = key;
- window.convertStatusnetProfileUrlToUserArrayCacheKey[dataToStore.local.statusnet_profile_url] = key;
- }
- if(dataToStore.external) {
- window.userArrayCache[key].external = dataToStore.external;
- // easy conversion between URI and the key we're using in window.userArrayCache
- window.convertUriToUserArrayCacheKey[dataToStore.external.ostatus_uri] = key;
- window.convertStatusnetProfileUrlToUserArrayCacheKey[dataToStore.external.statusnet_profile_url] = key;
- }
- // store the time when this record was modified
- if(dataToStore.local || dataToStore.external) {
- window.userArrayCache[key].modified = Date.now();
- }
- }
- }
- function userArrayCacheGetByLocalNickname(localNickname) {
- if(localNickname.substring(0,1) == '@') {
- localNickname = localNickname.substring(1);
- }
- if(typeof window.userArrayCache[window.siteRootDomain + '/' + localNickname] != 'undefined') {
- return window.userArrayCache[window.siteRootDomain + '/' + localNickname];
- }
- else {
- return false;
- }
- }
- function userArrayCacheGetByProfileUrlAndNickname(profileUrl, nickname) {
- var possibleLocalId = false;
- if(nickname.substring(0,1) == '@') {
- nickname = nickname.substring(1);
- }
- if(profileUrl.indexOf(window.siteInstanceURL + 'user/') == 0) {
- possibleLocalId = parseInt(profileUrl.substring(window.siteInstanceURL.length+5),10);
- }
- // 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 the local id might match a known id
- else if(typeof window.convertLocalIdToUserArrayCacheKey[possibleLocalId] != 'undefined') {
- if(typeof window.userArrayCache[window.convertLocalIdToUserArrayCacheKey[possibleLocalId]] != 'undefined') {
- return window.userArrayCache[window.convertLocalIdToUserArrayCacheKey[possibleLocalId]];
- }
- }
- // 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
- else {
- return false;
- }
- }
- 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;
- }
- /* ·
- ·
- · Detect if the supplied user object is from the local server or external
- ·
- · · · · · · · · · */
- function detectLocalOrExternalUserObject(userObject) {
- if(isLocalURL(userObject.profile_image_url) ) {
- return 'local';
- } else {
- return 'external';
- }
- }
- /* ·
- ·
- · 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);
- }
- // fix new mastodon style profile urls
- if(guessedInstanceUrl.indexOf('/@') > -1) {
- guessedInstanceUrl = guessedInstanceUrl.substring(0, guessedInstanceUrl.indexOf('/@'));
- }
- return guessedInstanceUrl;
- }
- /* ·
- ·
- · Remove the protocol (e.g. "http://") from an URL
- ·
- · · · · · · · · · */
- function removeProtocolFromUrl(url) {
- if(typeof url == 'undefined'
- || url === null
- || url === false
- || url == '') {
- return '';
- }
- if(url.indexOf('://') == -1) {
- return url;
- }
- return url.substring(url.indexOf('://')+3);
- }
- /* ·
- ·
- · Get host from URL
- ·
- · · · · · · · · · */
- function getHost(url) {
- var a = document.createElement('a');
- a.href = url;
- return a.hostname;
- }
- /* ·
- ·
- · 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;
- }
- /* ·
- ·
- · 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);
- }
- }
- }
- }
- /* ·
- ·
- · 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) {
- // 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');
- $('.user-menu-cog[data-user-id=' + userArray.local.id + ']').addClass('silenced');
- }
- else {
- $('.stream-item[data-user-id=' + userArray.local.id + ']').removeClass('silenced')
- $('.profile-card .profile-header-inner[data-user-id=' + userArray.local.id + ']').removeClass('silenced');
- $('.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');
- }
- // 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(userArray.local.is_local === true && $('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(){
- if($(this).css('background-image') != 'url("' + userArray.local.cover_photo + '")' && userArray.local.cover_photo != false) {
- $(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;
- }
- }
- }
- });
- }
- /* ·
- ·
- · 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) {
- var streamItemsUpdated = false;
- 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);
- streamItemsUpdated = true;
- }
- // 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');
- streamItemsUpdated = true;
- }
- // 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
- if(obj.is_post_verb === false) {
- streamItemFoundInFeed.addClass('activity always-hidden');
- streamItemsUpdated = true;
- }
- // 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);
- }
- }
- // attachments might have been added/changed/have had time to be processed
- 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));
- }
- var attachmentsHTMLBuild = buildAttachmentHTML(obj.attachments);
- var thumbsIsHidden = false;
- if(queetFoundInFeed.find('.queet-thumbs').hasClass('hide-thumbs')) {
- var thumbsIsHidden = true;
- }
- queetFoundInFeed.find('.queet-thumbs').remove();
- queetFoundInFeed.find('.oembed-data').remove();
- placeQuotedNoticesInQueetText(attachmentsHTMLBuild.quotedNotices,queetFoundInFeed.find('.queet-text'));
- // 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) {
- $(this).removeAttr('style'); // temporary fix
- $(this).addClass('hidden-embedded-link-in-queet-text');
- }
- });
- queetFoundInFeed.find('.queet-text').after(attachmentsHTMLBuild.html);
- if(thumbsIsHidden) {
- queetFoundInFeed.find('.queet-thumbs').addClass('hide-thumbs');
- }
- streamItemsUpdated = true;
- }
- // 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));
- }
- }
- // 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);
- streamItemsUpdated = true;
- }
- 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);
- streamItemsUpdated = true;
- }
- // 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);
- streamItemsUpdated = true;
- }
- 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');
- streamItemsUpdated = true;
- }
- }
- }
- }
- }
- if(streamItemsUpdated) {
- // 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();
- }
- }
- /* ·
- ·
- · Removes a deleted stream item from the feed gracefully, if not already hidden
- ·
- · · · · · · · · · */
- function slideUpAndRemoveStreamItem(streamItem,callback) {
- if(streamItem.length>0 && !streamItem.hasClass('always-hidden')) {
- streamItem.animate({opacity:'0.2'},1000,'linear',function(){
- $(this).css('height',$(this).height() + 'px');
- $(this).animate({height:'0px'},500,'linear',function(){
- $(this).addClass('deleted always-hidden');
- rememberStreamStateInLocalStorage();
- if(typeof callback == 'function') {
- callback();
- }
- });
- });
- }
- }
- /* ·
- ·
- · Store the current stream's state (html) in localStorage (if we're logged in)
- ·
- · · · · · · · · · */
- function rememberStreamStateInLocalStorage() {
- if(typeof window.currentStreamObject != 'undefined') {
- // don't store expanded content, only store profile card and the top 20 visible stream-items
- var firstTwentyVisibleHTML = '';
- var i = 0;
- $.each($('#feed-body').children('.stream-item'),function(k,streamItem){
- firstTwentyVisibleHTML += $(streamItem).outerHTML();
- if(!$(streamItem).hasClass('always-hidden')) {
- i++;
- }
- if(i>20) {
- return false;
- }
- });
- var feed = $('<div/>').append(firstTwentyVisibleHTML);
- // we add some of these things again when the notices are fetched from the cache
- cleanStreamItemsFromClassesAndConversationElements(feed);
- var feedHtml = feed.html();
- var profileCardHtml = $('#feed').siblings('.profile-card').outerHTML();
- var streamData = {
- card: profileCardHtml,
- feed: feedHtml
- };
- localStorageObjectCache_STORE('streamState',window.currentStreamObject.path, streamData);
- }
- }
- /* ·
- ·
- · 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));
- });
- }
- /* ·
- ·
- · 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;
- }
- /* ·
- ·
- · Gets the full unshortened HTML for a queet
- ·
- · · · · · · · · · */
- function getFullUnshortenedHtmlForQueet(streamItem, cacheOnly) {
- if(typeof cacheOnly == 'undefined') {
- var cacheOnly = false;
- }
- var queet = streamItem.children('.queet');
- var queetId = streamItem.attr('data-quitter-id');
- var attachmentMore = queet.find('span.attachment.more');
- // only if actually shortened
- if(attachmentMore.length>0
- && queet.children('script.attachment-json').length > 0
- && queet.children('script.attachment-json').text() != 'undefined') {
- // first try localstorage cache
- var cacheData = localStorageObjectCache_GET('fullQueetHtml',queetId);
- if(cacheData) {
- queet.find('.queet-text').html(cacheData);
- queet.outerHTML(detectRTL(queet.outerHTML()));
- }
- // then try static html file attachment, that we should have in the attachment-json script element
- else if(cacheOnly === false){
- var attachmentId = attachmentMore.attr('data-attachment-id');
- $.each(JSON.parse(queet.children('script.attachment-json').text()), function(k,attachment) {
- 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;
- }
- });
- }
- }
- }
- /* ·
- ·
- · Appends a user to the array containing the mentions suggestions to show when typing a notice
- ·
- · · · · · · · · · */
- function appendUserToMentionsSuggestionsArray(user) {
- if(typeof window.following[user.id] == 'undefined') {
- // 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);
- }
- var userToAdd = {
- avatar: user.profile_image_url,
- id: user.id,
- name: user.name,
- url: url,
- username: user.screen_name
- };
- window.following[user.id] = userToAdd;
- }
- }
- /* ·
- ·
- · 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;
- }
- }
- /* ·
- ·
- · Display unread notifications
- ·
- · · · · · · · · · */
- function displayOrHideUnreadNotifications(notifications) {
- var data = $.parseJSON(notifications);
- var totNotif = 0;
- if(data !== null && typeof data != 'undefined') {
- $.each(data,function(k,v){
- totNotif = totNotif + parseInt(v,10);
- });
- }
- if(window.currentStreamObject.name == 'notifications') {
- var hiddenNotifications = $('#feed-body').find('.stream-item.hidden:not(.always-hidden)').length;
- if(hiddenNotifications>0) {
- totNotif = totNotif + hiddenNotifications;
- }
- }
- if(totNotif>0) {
- $('#unseen-notifications').html(totNotif);
- document.title = '(' + totNotif + ') ' + window.siteTitle; // update html page title
- $('#unseen-notifications').show();
- }
- else {
- $('#unseen-notifications').hide();
- document.title = window.siteTitle;
- }
- }
- /* ·
- ·
- · Removes HTML special chars recursively from strings in objects
- · 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
- ·
- · @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]);
- }
- else if(typeof obj[property] == 'string'
- && property != 'statusnet_html'
- && property != 'oembedHTML' // we trust this to be cleaned server side
- && property != 'source') {
- obj[property] = replaceHtmlSpecialChars(obj[property]);
- }
- }
- }
- return obj;
- }
- function replaceHtmlSpecialChars(text) {
- // don't do anything if the text is undefined
- if(typeof text == 'undefined') {
- return text;
- }
- var map = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": '''
- };
- return text.replace(/[&<>"']/g, function(m) { return map[m]; });
- }
- /* ·
- ·
- · 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(validEmail(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;
- }
- 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;
- }
- }
- /* ·
- ·
- · 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;
- }
- /* ·
- ·
- · 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;
- }
- /**
- * Change profile design
- * @param {Object} obj - user object that should contain one, two or all of backgroundimage, backgroundcolor and linkcolor false or empty string unsets the parameter to default
- */
- function changeDesign(obj) {
- if (!window.currentStreamObject) {
- window.currentStreamObject = {
- name: '',
- nickname: '',
- };
- }
- if (!window.defaultBackgroundColor) {
- window.defaultBackgroundColor = '000';
- }
- if (!window.defaultLinkColor) {
- window.defaultLinkColor = '000';
- }
- // if we're logged out and this is the front page, we use the default design
- if (!window.loggedIn &&
- (window.currentStreamObject.name === 'public timeline' || window.currentStreamObject.name === 'public and external timeline')) {
- obj.backgroundimage = window.fullUrlToThisQvitterApp + window.siteBackground;
- obj.backgroundcolor = window.defaultBackgroundColor;
- obj.linkcolor = window.defaultLinkColor;
- }
- // if no object is defined, abort
- if (!obj) {
- return;
- }
- // remember the design for this stream
- if (typeof window.oldStreamsDesigns[window.currentStreamObject.nickname] === 'undefined') {
- window.oldStreamsDesigns[window.currentStreamObject.nickname] = new Object();
- }
- // change design elements
- if (obj.backgroundimage === false) {
- obj.backgroundimage = '';
- }
- $('body').css('background-image', "url('" + obj.backgroundimage + "')");
- window.oldStreamsDesigns[window.currentStreamObject.nickname].backgroundimage = obj.backgroundimage;
- if (!obj.backgroundcolor) {
- obj.backgroundcolor = window.defaultBackgroundColor;
- }
- changeBackgroundColor(obj.backgroundcolor);
- window.oldStreamsDesigns[window.currentStreamObject.nickname].backgroundcolor = obj.backgroundcolor;
- if (!obj.linkcolor) {
- obj.linkcolor = window.defaultLinkColor;
- }
- changeLinkColor(obj.linkcolor);
- window.oldStreamsDesigns[window.currentStreamObject.nickname].linkcolor = obj.linkcolor;
- }
- // create object to remember designs on page load
- window.oldStreamsDesigns = new Object();
- /* ·
- ·
- · 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);
- }
- /* ·
- ·
- · Change link color
- ·
- · @param newLinkColor: hex value with or without #
- ·
- · · · · · · · · · */
- function changeLinkColor(newLinkColor) {
- // check hex value
- var validHexColor = isValidHexColor(newLinkColor);
- if(!validHexColor) {
- console.log('invalid hex value for linkcolor: ' + newLinkColor);
- return false;
- }
- var lighterColor08 = blendRGBColors(hex2rgb(validHexColor),'rgb(255,255,255)',0.8);
- var lighterColor06 = blendRGBColors(hex2rgb(validHexColor),'rgb(255,255,255)',0.6)
- var headStyle = $('#dynamic-styles').children('style');
- 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));
- }
- 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 + ')';
- }
- /* ·
- ·
- · 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
- var $hcard = $queetText.find('.h-card'); $hcard.remove(); // remove users, groups
- 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
- // or if no chars (that we are interested, but body is set to rtl)
- if(RTLnum > LTRnum
- || ($queetText.html().length==0 && $('body').hasClass('rtl'))) {
- $streamItem.children('.stream-item').children('.queet').addClass('rtl');
- }
- else {
- // for ltr languages we move @, ! and # to inside (only because it looks better)
- prependCharIfItDoesntAlreadyExist($streamItem.find('.queet-text').find('.h-card.mention'),'@');
- prependCharIfItDoesntAlreadyExist($streamItem.find('.queet-text').find('.h-card.group'),'!');
- prependCharIfItDoesntAlreadyExist($streamItem.find('.queet-text').find('.vcard .fn.nickname:not(.group)'),'@'); // very old style
- prependCharIfItDoesntAlreadyExist($streamItem.find('.queet-text').find('.vcard .nickname.mention:not(.fn)'),'@'); // old style
- prependCharIfItDoesntAlreadyExist($streamItem.find('.queet-text').find('.vcard .nickname.group'),'!'); // old style
- prependCharIfItDoesntAlreadyExist($streamItem.find('.queet-text').find('a[rel="tag"]'),'#');
- }
- // we remove @, ! and #, they are added as pseudo elements, or have been moved to the inside
- 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">');
- }
- function prependCharIfItDoesntAlreadyExist(jQueryObject,char) {
- if(jQueryObject.text().substring(0,1) != char) {
- jQueryObject.prepend(char);
- }
- }
- /* ·
- ·
- · 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 = parseDate(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 = parseDate(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());
- }
- 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;
- }
- function parseDate(str) {
- if(typeof str != 'undefined') {
- var v=str.split(' ');
- return new Date(Date.parse(v[1]+" "+v[2]+", "+v[5]+" "+v[3]+" "+v[4]));
- }
- }
- /* ·
- ·
- · If we want to make sure we have empty arrays, not empty objects
- ·
- · · · · · · · · · · */
- function convertEmptyObjectToEmptyArray(data) {
- // empty object? return empty array instead...
- if($.isEmptyObject(data)) {
- return [];
- }
- // leave data unchanged if we don't recognize it
- else {
- return data;
- }
- }
- /* ·
- ·
- · Functions to show and remove the spinner
- ·
- · · · · · · · · · · · · */
- function display_spinner(parent) {
- if($('.loader').length<1) {
- if(typeof parent == 'undefined') {
- $('.global-nav').removeClass('show-logo');
- var parent = 'body';
- }
- $(parent).prepend('\
- <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.503c-6.413,0-11.612,5.199-11.612,11.612s5.199,11.611,11.612,11.611\
- c6.412,0,11.611-5.198,11.611-11.611S26.613,8.503,20.201,8.503z M20.201,29.153c-4.992,0-9.039-4.046-9.039-9.038\
- s4.047-9.039,9.039-9.039c4.991,0,9.038,4.047,9.038,9.039S25.192,29.153,20.201,29.153z"/>\
- <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"/>\
- </path>\
- </svg>\
- </div>\
- ');
- }
- }
- function remove_spinner() {
- $('.loader').remove();
- $('.global-nav').addClass('show-logo');
- }
- /* ·
- ·
- · Converts ...-attachment-links to spans
- ·
- · (Attachments are loaded when queets expand)
- ·
- · · · · · · · · · · · · · · · · · */
- function convertAttachmentMoreHref() {
- $('a.attachment.more').each(function() {
- if(typeof $(this).attr('href') != 'undefined') {
- 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>'));
- }
- }
- });
- }
- /* ·
- ·
- · 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');
- bookmarkContainer[i].dataStreamHeader = $(obj).text();
- i++;
- });
- postUpdateBookmarks(bookmarkContainer);
- $('#bookmark-container').sortable({delay: 100});
- $('#bookmark-container').disableSelection();
- }
- /* ·
- ·
- · Append all bookmarks to the bookmark container
- ·
- · · · · · · · · · · · · · */
- function appendAllBookmarks(bookmarkContainer) {
- if(typeof bookmarkContainer != 'undefined' && bookmarkContainer) {
- $('#bookmark-container').html('');
- var bookmarkContainerParsed = JSON.parse(bookmarkContainer);
- $.each(bookmarkContainerParsed, function(key,obj) {
- $('#bookmark-container').append('<a class="stream-selection" href="' + obj.dataStreamHref + '">' + obj.dataStreamHeader + '<i class="chev-right" data-tooltip="' + window.sL.tooltipRemoveBookmark + '"></i></a>');
- });
- }
- $('#bookmark-container').sortable({delay: 100});
- $('#bookmark-container').disableSelection();
- }
- /* ·
- ·
- · Change the header of an item in the history container by href attribute
- ·
- · · · · · · · · · · · · · */
- function updateHistoryContainerItemByHref(href, streamHeader) {
- $('#history-container .stream-selection[href="' + href + '"]').html(streamHeader + '<i class="chev-right" data-tooltip="' + window.sL.tooltipBookmarkStream + '"></i>');
- updateHistoryLocalStorage();
- }
- /* ·
- ·
- · Remove items in the history container by href attribute
- ·
- · · · · · · · · · · · · · */
- function removeHistoryContainerItemByHref(href) {
- $('#history-container .stream-selection[href="' + href + '"]').remove();
- updateHistoryLocalStorage();
- }
- /* ·
- ·
- · Updates the browsing history local storage
- ·
- · · · · · · · · · · · · · */
- function updateHistoryLocalStorage() {
- if(localStorageIsEnabled()) {
- var i=0;
- var historyContainer = new Object();
- $.each($('#history-container .stream-selection'), function(key,obj) {
- historyContainer[i] = new Object();
- historyContainer[i].dataStreamHref = $(obj).attr('href');
- historyContainer[i].dataStreamHeader = $(obj).text();
- i++;
- });
- localStorageObjectCache_STORE('browsingHistory', window.loggedIn.screen_name,historyContainer);
- if($('#history-container .stream-selection').length==0) {
- $('#history-container').css('display','none');
- }
- else {
- $('#history-container').css('display','block');
- }
- }
- }
- /* ·
- ·
- · Loads history from local storage to menu
- ·
- · · · · · · · · · · · · · */
- function loadHistoryFromLocalStorage() {
- if(localStorageIsEnabled()) {
- var cacheData = localStorageObjectCache_GET('browsingHistory', window.loggedIn.screen_name);
- if(cacheData) {
- $('#history-container').css('display','block');
- $('#history-container').html('');
- $.each(cacheData, function(key,obj) {
- 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>');
- });
- }
- updateHistoryLocalStorage();
- }
- }
- /* ·
- ·
- · Does stream need a ? or a &
- ·
- · · · · · · · · · · · · · */
- function qOrAmp(stream) {
- if(stream.substr(-5) == '.json') {
- return '?';
- }
- else {
- return '&';
- }
- }
- /* ·
- ·
- · Count chars in queet box
- ·
- · @param src: the queetbox's value
- · @param trgt: the counter
- · @param btn: the button
- ·
- · · · · · · · · · · · · · */
- function countCharsInQueetBox(src,trgt,btn) {
- // 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, '')));
- var numchars = ($src_txt.text()).length;
- // 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');
- // limited
- 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');
- btn.removeClass('too-long');
- // deactivate button if it's equal to the start text
- 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())) {
- btn.removeClass('enabled');
- btn.addClass('disabled');
- }
- }
- }
- else if(numchars > window.textLimit){
- btn.removeClass('enabled');
- btn.addClass('disabled');
- btn.addClass('too-long');
- }
- else {
- btn.removeClass('enabled');
- btn.addClass('disabled');
- btn.removeClass('too-long');
- }
- // counter color
- if((window.textLimit-numchars) < 0) {
- trgt.css('color','#D40D12');
- }
- else {
- trgt.removeAttr('style');
- }
- }
- // unlimited
- else {
- if(numchars > 0) {
- btn.removeClass('disabled');
- btn.addClass('enabled');
- }
- else {
- btn.removeClass('enabled');
- btn.addClass('disabled');
- }
- }
- }
- /* ·
- ·
- · 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');
- }
- }
- /* ·
- ·
- · 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) {
- if(animate == 'animate' || animate === true) {
- animate = 1000;
- }
- if(typeof callback !== 'undefined'){
- $('html, body').animate({ scrollTop: pos}, animate, 'swing',function(){
- callback();
- });
- }
- else {
- $('html, body').animate({ scrollTop: pos }, animate, 'swing');
- }
- }
- else {
- $('html, body').scrollTop(pos);
- }
- }
- /* ·
- ·
- · Scroll to a stream item
- ·
- · @param streamItem: jQuery object to scroll to
- ·
- · · · · · · · · · · · · · */
- 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);
- }
- /* ·
- ·
- · 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;
- }
- /* ·
- ·
- · outerHTML
- ·
- · · · · · · · · · · · · · */
- jQuery.fn.outerHTML = function(s) {
- return s
- ? this.before(s).remove()
- : jQuery("<p>").append(this.eq(0).clone()).html();
- };
- /* ·
- ·
- · 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; }
- }
- /* ·
- ·
- · 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);
- if(typeof range != 'undefined') {
- range.deleteContents();
- }
- }
- /* ·
- ·
- · 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){
- var url = $.trim($(obj).text());
- display_spinner();
- $.ajax({ url: window.urlShortenerAPIURL + '?format=' + window.urlshortenerFormat + '&action=shorturl&signature=' + window.urlShortenerSignature + '&url=' + encodeURIComponent(url), type: "GET", dataType: window.urlshortenerFormat,
- success: function(data) {
- if(typeof data.shorturl != 'undefined') {
- 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();
- }
- });
- });
- }
- /* ·
- ·
- · Youtube ID from Youtube URL
- ·
- · · · · · · · · · · · · · */
- function youTubeIDFromYouTubeURL(url) {
- return url.replace('https://youtube.com/watch?v=','').replace('http://youtube.com/watch?v=','').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);
- }
- /* ·
- ·
- · Youtube embed link from youtube url
- ·
- · · · · · · · · · · · · · */
- function youTubeEmbedLinkFromURL(url, autoplay) {
- // get start time hash
- var l = document.createElement("a");
- l.href = url;
- var addStart = '';
- if(l.hash.substring(0,3) == '#t=') {
- addStart = '&start=' + l.hash.substring(3);
- }
- var addAutoplay = '';
- if(typeof autoplay != 'undefined' && autoplay === true) {
- addAutoplay = '&autoplay=1';
- }
- return '//www.youtube.com/embed/' + youTubeIDFromYouTubeURL(url) + '?enablejsapi=1&version=3&playerapiid=ytplayer' + addStart + addAutoplay;
- }
- /* ·
- ·
- · Vimeo ID from Vimeo URL
- ·
- · · · · · · · · · · · · · */
- function vimeoIDFromVimeoURL(url) {
- id = url.replace('http://vimeo.com/','').replace('https://vimeo.com/','');
- if(id.indexOf('#') > -1) {
- id = id.substring(0,id.indexOf('#'));
- }
- return id;
- }
- /* ·
- ·
- · Vimeo embed link from vimeo url
- ·
- · · · · · · · · · · · · · */
- function vimeoEmbedLinkFromURL(url, autoplay) {
- // get start time hash
- var l = document.createElement("a");
- l.href = url;
- var addStart = '';
- if(l.hash.substring(0,3) == '#t=') {
- addStart = l.hash;
- }
- var addAutoplay = '&autoplay=0';
- if(typeof autoplay != 'undefined' && autoplay === true) {
- addAutoplay = '&autoplay=1';
- }
- return 'https://player.vimeo.com/video/' + vimeoIDFromVimeoURL(url) + '?api=1' + addAutoplay + addStart;
- }
- /* ·
- ·
- · CSS class name from URL
- ·
- · · · · · · · · · · · · · */
- function CSSclassNameByHostFromURL(url) {
- var host = getHost(url);
- if(host.indexOf('www.') === 0) {
- host = host.substring(4);
- }
- host = host.toLowerCase().replace(/\./g, "-");
- host = host.replace(/[^a-zA-Z0-9-]+/g, "_");
- if(host == 'youtu-be') {
- host = 'youtube-com';
- }
- return 'host-' + host;
- }
- /* ·
- ·
- · 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;
- }());
- function mock() {
- changeDesign();
- }
|