Friday, February 4, 2011

Facebook APR (application post remover)


// ==UserScript==
// @name            Facebook APR (application post remover)
// @description     remove old posts from FB applications (games, quizes, etc ...)
// @author          Sigi_cz
// @version         2.13
// @homepage        http://userscripts.org/scripts/show/92953
// @licence         http://creativecommons.org/licenses/by-nc-nd/3.0/
// @include         http://userscripts.org/scripts/source/92953.meta.js*
// @include         http://www.facebook.com/*
// ==/UserScript==

// some code parts are inspired or borroved from many great scripts (why reinvent wheel?)
// thanks to:
// "FFixer" http://userscripts.org/scripts/show/8861
// "FB MafiaWars Addon" http://userscripts.org/scripts/show/90615
// "Facebook Recent Activity" http://userscripts.org/scripts/show/68225
// "google.com"
// "stackoverflow.com"

/*jslint browser: true, devel: true, evil: true*/
/*global unsafeWindow:true, window:false, self:false, chrome:false, localStorage:true, XPathResult:false,
  GM_addStyle:true, GM_log:true, GM_getValue:true, GM_setValue:true, GM_deleteValue:true */

(function () {


//------------------------------------------------
// custom settings
//------------------------------------------------

var cfg_defaults = {
  appIDs: [], // ID's of applications to remove
  all_apps: false, // remove all application posts
  time_limit: 12, // remove posts older then X hours
  autorun: false, // start automatically (works only in Opera)
  only_user_posts: false, // false: remove all app. post; true: remove only your app. posts
  continue_proc: true, // search last known post and continue processing (disabled if hide_posts = true)
  hide_posts: false, // don't remove posts, only hide
  xhr_delay: 300, // XMLHttpRequest delay X in ms (you don't want to remove all posts at once)
  updateInterval: 6, // hours
  language: 'auto',
  isDebug: false // debug mode
};
var appsArr = [['10979261223','Mafia Wars'],
               ['234860566661','Treasure Isle'],
               ['46755028429','Castle Age'],
               ['102452128776','FarmVille'],
               ['130402594779','Kingdoms of Camelot']];
cfg_defaults.appIDs = appsArr;
var cfg = cfg_defaults;


//------------------------------------------------
// script informations
//------------------------------------------------

var APRinf = {
  name: 'Facebook Application Post Remover',
  name_med: 'FB Application Post Remover',
  name_short: 'FB APR',
  id: '92953',
  ver: '2.13',
  url: 'http://userscripts.org/scripts/show/92953',
  meta: 'http://userscripts.org/scripts/source/92953.meta.js'
};


//------------------------------------------------
// unsafeWindow
//------------------------------------------------

if (typeof unsafeWindow === 'undefined') { unsafeWindow = window; }

// don't run in frames, except updater
if (!unsafeWindow.location.href.match(APRinf.meta)) {
 if (self !== window.top) { return; }
}

// detect Chromium browser - finally working code
var getDOMWindow = function () {
 var elt = document.createElement('div');
 elt.setAttribute('onclick', 'return window;');
 return elt.onclick();
};
if (typeof chrome  !== 'undefined' && chrome.extension) {
 unsafeWindow = getDOMWindow();
}


//------------------------------------------------
// text strings
//------------------------------------------------

var text_strings = {
 languages : ['cs', 'nl', 'en', 'es'],
 // English by Sigi_cz - default
 en : {
  '_language' : 'English',
  'cfg-language' : 'language: ',
  'cfg-config' : 'Configuration',
  'cfg-author' : 'author',
  'cfg-author_email' : '%1send e-mail to author%2',
  'cfg-licence' : 'is licensed under a',
  'cfg-appIDs' : 'appID\'s to remove:',
  'cfg-appIDs-hlp' : 'each row contains one appID, optional text behind ID is comment',
  'cfg-all_apps' : 'all applications',
  'cfg-all_apps-hlp' : 'remove posts from all applications',
  'cfg-time_limit' : '(hours) time limit',
  'cfg-time_limit-hlp' : 'remove posts older then X hours',
  'cfg-continue_proc' : 'continue processing',
  'cfg-continue_proc-hlp' : 'search last known post and continue processing',
  'cfg-autorun' : 'start automatically',
  'cfg-autorun-hlp' : 'processing starts when page is loaded',
  'cfg-only_user_posts' : 'only own posts',
  'cfg-only_user_posts-hlp' : 'check only your app. posts or also from friends',
  'cfg-hide_posts' : 'hide posts',
  'cfg-hide_posts-hlp' : 'don\'t remove posts, only hide',
  'cfg-xhr_delay' : '(ms) delay for removing',
  'cfg-xhr_delay-hlp' : 'you don\'t want to remove all posts at once',
  'cfg-isDebug' : 'debug mode',
  'gui-runbtn' : 'APR',
  'gui-runbtn-hlp' : 'start posts processing',
  'gui-cfgbtn' : 'Configure APR',
  'gui-cfgbtn-hlp' : 'show APR configuration window',
  'gui-close' : 'Close',
  'gui-save' : 'Save',
  'gui-add' : 'Add',
  'gui-remove' : 'Remove',
  'inf-error' : 'error - see console for details.',
  'inf-search' : 'searching last known old post %1 - page %2.',
  'inf-process' : 'processing visible posts.',
  'inf-xremoved' : '%1 posts total, %2 old removed.',
  'inf-xhidden' : '%1 posts total, %2 old hidden.',
  'str-update_long' : ' UPDATE FOUND%1%1 %2%1 installed version: %3%1 actual version: %4%1 update URL: %5%1%1 Do you want to update now?%1%1',
  'str-addremove' : 'add/remove application',
  'str-unknown' : 'unknown page',
  'str-unknown_long' : 'I don\'t know this page. Should I do something here?%1Contact me with suggestions: ',
  'str-old_profile' : 'old profile',
  'str-old_profile_long' : 'Old profile page layout is unsupported by APR.%1(I can\'t debug new features on it anymore.)%1%1Please switch to %2new profile layout%3.',
  'str-APRhome' : 'homepage',
  'str-APRhome_long' : 'Home sweet home :)',
  '_language_end' : 'English'
 },
 // Czech by Sigi_cz
 cs : {
  '_language' : 'Česky (Czech)',
  'cfg-language' : 'jazyk: ',
  'cfg-config' : 'Nastavení',
  'cfg-author' : 'autor',
  'cfg-author_email' : '%1poslat e-mail autorovi%2',
  'cfg-licence' : 'je licencován pod',
  'cfg-appIDs' : 'ID aplikací k odstranění:',
  'cfg-appIDs-hlp' : 'každý řádek obsahuje jedno ID, text za ID je komentář',
  'cfg-all_apps' : 'všechny aplikace',
  'cfg-all_apps-hlp' : 'odstranit příspěvky ze všech aplikací',
  'cfg-time_limit' : '(hodin) časový limit',
  'cfg-time_limit-hlp' : 'odstranit příspěvky starší než X hodin',
  'cfg-continue_proc' : 'pokračovat ve zpracování',
  'cfg-continue_proc-hlp' : 'vyhledat poslední nezpracovaný příspěvek a pokračovat ve zpracování',
  'cfg-autorun' : 'automatické spouštění',
  'cfg-autorun-hlp' : 'zpracování se spustí ihned po načtení stránky',
  'cfg-only_user_posts' : 'pouze vlastní příspěvky',
  'cfg-only_user_posts-hlp' : 'kontrolovat pouze vlastní příspěvky, nebo i od přátel',
  'cfg-hide_posts' : 'skrývat příspěvky',
  'cfg-hide_posts-hlp' : 'příspěvky nemazat ale pouze skrývat',
  'cfg-xhr_delay' : '(ms) prodleva pro mazání',
  'cfg-xhr_delay-hlp' : 'nechceme odstraňovat všechny příspěvky najednou',
  'cfg-isDebug' : 'režim ladění',
  'gui-runbtn' : 'APR',
  'gui-runbtn-hlp' : 'spustit zpracování příspěvků',
  'gui-cfgbtn' : 'Nastavit APR',
  'gui-cfgbtn-hlp' : 'zobrazit konfigurační okno APR',
  'gui-close' : 'Zavřít',
  'gui-save' : 'Uložit',
  'gui-add' : 'Přidat',
  'gui-remove' : 'Odebrat',
  'inf-error' : 'chyba - více informací v chybové konzoli.',
  'inf-search' : 'hledám poslední známý nezpracovaný příspěvek.',
  'inf-process' : 'zpracovávám zobrazené příspěvky.',
  'inf-xremoved' : '%1 příspěvků celkem, %2 starých odstraněno.',
  'inf-xhidden' : '%1 příspěvků celkem, %2 starých skryto.',
  'str-update_long' : ' NALEZENA AKTUALIZACE%1%1 %2%1 nainstalovaná verze: %3%1 aktuální verze: %4%1 adresa aktualizace: %5%1%1 Přejete si provést aktualizaci?%1%1',
  'str-addremove' : 'přidat/odebrat aplikaci',
  'str-unknown' : 'neznámá stránka',
  'str-unknown_long' : 'Tady to neznám. Měl bych tu něco dělat?%1Kontaktujte mě s nápady: ',
  'str-old_profile' : 'starý profil',
  'str-old_profile_long' : 'APR nepodporuje starý layout profilu%1(Už na něm nemůžu testovat nové věci.)%1%1Prosím přejděte na %2nový layout%3.',
  'str-APRhome' : 'homepage',
  'str-APRhome_long' : 'Domov sladký domov :)',
  '_language_end' : 'Česky (Czech)'
 },
 // Dutch translation by Stievydude
 nl : {
  '_language' : 'Nederlands (Dutch)',
  'cfg-language' : 'taal: ',
  'cfg-config' : 'Configuratie',
  'cfg-author' : 'auteur',
  'cfg-author_email' : '%1verstuur een e-mail naar de auteur%2',
  'cfg-licence' : 'is gelicentieerd onder een',
  'cfg-appIDs' : 'te verwijderen toepassingID\'s:',
  'cfg-appIDs-hlp' : 'elke rij bevat één toepassingID, optionele tekst achter het ID is commentaar',
  'cfg-all_apps' : 'alle toepassingen',
  'cfg-all_apps-hlp' : 'verwijder posts van alle toepassingen',
  'cfg-time_limit' : '(uren) tijdslimiet',
  'cfg-time_limit-hlp' : 'verwijder posts ouder dan X uren',
  'cfg-continue_proc' : 'verdergaan met verwerken',
  'cfg-continue_proc-hlp' : 'zoek laatst gekende post en ga verder met verwerken',
  'cfg-autorun' : 'start automatisch',
  'cfg-autorun-hlp' : 'verwerken start wanneer de pagina is geladen',
  'cfg-only_user_posts' : 'alleen eigen posts',
  'cfg-only_user_posts-hlp' : 'controleer alleen eigen toepassingposts of ook van vrienden',
  'cfg-hide_posts' : 'verberg posts',
  'cfg-hide_posts-hlp' : 'verwijder geen posts, alleen verbergen',
  'cfg-xhr_delay' : '(ms) vertraging voor verwijderen ',
  'cfg-xhr_delay-hlp' : 'Je wil niet alle posts ineens verwijderen',
  'cfg-isDebug' : 'debug modus',
  'gui-runbtn' : 'APR',
  'gui-runbtn-hlp' : 'start met verwerken posts',
  'gui-cfgbtn' : 'Configureer APR',
  'gui-cfgbtn-hlp' : 'toon APR configuratievenster',
  'gui-close' : 'Sluit',
  'gui-save' : 'Bewaar',
  'gui-add' : 'Voeg toe',
  'gui-remove' : 'Verwijder',
  'inf-error' : 'fout - zie console voor details.',
  'inf-search' : 'bezig met laatst bekende post te zoeken %1 - pagina %2.',
  'inf-process' : 'zichtbare posts verwerken.',
  'inf-xremoved' : 'totaal aantal posts: %1, %2 oude verwijderd.',
  'inf-xhidden' : 'totaal aantal posts: %1, %2 oude verborgen.',
  'str-update_long' : ' NIEUWE VERSIE GEVONDEN%1%1 %2%1 geïnstalleerde versie: %3%1 huidige versie: %4%1 update URL: %5%1%1 Nu updaten?%1%1',
  'str-addremove' : 'toepassing toevoegen/verwijderen',
  'str-unknown' : 'onbekende pagina',
  'str-unknown_long' : 'Ik ken deze pagina niet. Zou ik hier iets moeten doen? %1Contacteer me met suggesties: ',
  'str-old_profile' : 'oud profiel',
  'str-old_profile_long' : 'Oude profielpagina. De layout wordt niet ondersteund door APR.%1(Ik kan de nieuwe functies op deze pagina niet meer debuggen.)%1%1Stap a.u.b over naar het %2nieuw profiel%3.',
  'str-APRhome' : 'startpagina',
  'str-APRhome_long' : 'Zoals het klokje thuis tikt, tikt het nergens :)',
  '_language_end' : 'Nederlands (Dutch)'
 },
 // Spanish translation by Néstor
 es : {
  '_language' : 'Español (Spanish)',
  'cfg-language' : 'idioma: ',
  'cfg-config' : 'Configuración',
  'cfg-author' : 'autor',
  'cfg-author_email' : '%1enviar e-mail al autor%2',
  'cfg-licence' : 'está bajo una licencia de',
  'cfg-appIDs' : 'appID\'s a borrar:',
  'cfg-appIDs-hlp' : 'cada linea contiene un appID, texto opcional detrás del ID es comentario',
  'cfg-all_apps' : 'Todas las aplicaciones',
  'cfg-all_apps-hlp' : 'borrar posts de todas aplicaciones',
  'cfg-time_limit' : '(horas) tiempo límite',
  'cfg-time_limit-hlp' : 'borrar posts más viejos que x horas',
  'cfg-continue_proc' : 'continua procesando',
  'cfg-continue_proc-hlp' : 'buscar último post y continuar procesando',
  'cfg-autorun' : 'comenzar automáticamente',
  'cfg-autorun-hlp' : 'proceso comienza cuando la página está cargada',
  'cfg-only_user_posts' : 'solo posts propios',
  'cfg-only_user_posts-hlp' : 'revisar sólo los posts de tus propias apps o tambien de amigos',
  'cfg-hide_posts' : 'ocultar posts',
  'cfg-hide_posts-hlp' : 'no borrar posts, sólo esconderlos',
  'cfg-xhr_delay' : '(ms) demora para el borrado',
  'cfg-xhr_delay-hlp' : 'no querrás borrar todos los posts al mismo tiempo',
  'cfg-isDebug' : 'debug mode',
  'gui-runbtn' : 'APR',
  'gui-runbtn-hlp' : 'Comenzar a procesar posts',
  'gui-cfgbtn' : 'Configurar APR',
  'gui-cfgbtn-hlp' : 'Mostrar configuración APR',
  'gui-close' : 'Cerrar',
  'gui-save' : 'Guardar',
  'gui-add' : 'Agregar',
  'gui-remove' : 'Borrar',
  'inf-error' : 'error - mira la consola para detalles.',
  'inf-search' : 'buscando último post viejo conocido %1 - página %2.',
  'inf-process' : 'procesando posts visibles.',
  'inf-xremoved' : '%1 posts total, %2 viejos borrados.',
  'inf-xhidden' : '%1 posts total, %2 viejos ocultados.',
  'str-update_long' : ' ACTUALIZACIÓN ENCONTRADA%1%1 %2%1 versión instalada: %3%1 versión actual: %4%1 URL de actualiación: %5%1%1 ¿Quieres actualizar ahora?%1%1',
  'str-addremove' : 'agregar/quitar aplicación',
  'str-unknown' : 'página desconocida',
  'str-unknown_long' : 'No conozco esta página. ¿Debería hacer algo aquí?%1Contáctame con sugerencias: ',
  'str-old_profile' : 'viejo perfil',
  'str-old_profile_long' : 'El viejo diseño de página de perfil no es soportado por APR.%1(Ya no se puede depurar nuevas características en este.)%1%1Por favor cambie al %2nuevo diseño de perfil%3.',
  'str-APRhome' : 'homepage',
  'str-APRhome_long' : 'Hogar dulce hogar :)',
  '_language_end' : 'Español (Spanish)'
 }
};


//------------------------------------------------
// some helpfull functions
//------------------------------------------------

function doClick(elm) { var e = document.createEvent('MouseEvents'); e.initEvent('click', true, true, window, 0); elm.dispatchEvent(e); }

function destroy(elm) { if (elm && elm.parentNode) { elm.parentNode.removeChild(elm); } }

function testClass(elm, cls) { if (typeof cls === 'string') { cls = new RegExp('(^|\\s)' + cls + '(\\s|$)'); } return (elm.className && cls.test(elm.className)); }

function insertBefore(elm, ref) { if (ref && ref.parentNode) { ref.parentNode.insertBefore(elm, ref); } }

// select elements by 'tagname' '#ID' '.class' './xpath' '//xpath'
function $(q, root, single) {
 if (root && typeof root === 'string') {
  root = $(root, null, true);
  if (!root) { return null; }
 }
 root = root || document;
 if (q[0]==='#') { return root.getElementById(q.substr(1)); }
 else if (q[0]==='/' || (q[0]==='.' && q[1]==='/')) {
  if (single) { return document.evaluate(q, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; }
  return document.evaluate(q, root, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
 }
 else if (q[0]==='.') { return root.getElementsByClassName(q.substr(1)); }
 return root.getElementsByTagName(q);
}

// add event listeners
function on(type, elm, func) {
 if (type instanceof Array) { var ii=type.length; while (ii--) { on(type[ii], elm, func); } return; }
 if (elm instanceof Array) { var jj=elm.length; while (jj--) { on(type, elm[jj], func); } return; }
 (typeof elm === 'string' ? $(elm) : elm).addEventListener(type, func, false);
}

// GM_addStyle emulation
function addStyle(css) {
 if (typeof GM_addStyle !== 'undefined') { return GM_addStyle(css); }
 else {
  var ishead = document.getElementsByTagName('head')[0];
  var document_element = (!ishead ? document.documentElement : null);
  var link_stylesheet = document.createElement('link');
  link_stylesheet.rel = 'stylesheet';
  link_stylesheet.type = 'text/css';
  link_stylesheet.href = 'data:text/css,' + encodeURIComponent(css);
  if (ishead) { ishead.appendChild(link_stylesheet); }
  else { document_element.insertBefore(link_stylesheet, document_element.firstChild); }
 }
}

// check if string is part of array
function contains(arr, str, identical) {
 var ii = arr.length;
 while (ii--) {
  if (!identical) { if (arr[ii].indexOf(str) >= 0) { return ii; } }
  else { if (arr[ii] === str) { return ii; } }
 }
 return ii;
}

// send XMLHttpRequest
function xhr(url, callback_fn, data) {
 var res = new XMLHttpRequest();
 res.onreadystatechange = function () { if (typeof callback_fn === 'function' && res.readyState === 4 && res.status === 200) { callback_fn(res.responseText); } };
 res.open(data ? 'POST' : 'GET', url, true);
 res.setRequestHeader('User-agent', window.navigator.userAgent);
 if (data) {
  res.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  res.setRequestHeader('Connection', 'close');
  res.setRequestHeader('Content-length', data.length);
 }
 res.send(data||null);
}

// delay script processing
function delay(millis) {
 var date = new Date();
 var curDate = null;
 do { curDate = new Date(); }
 while (curDate-date < millis);
}

// write debug info to console
function debugMsg(msg, force) {
 if (!force && !cfg.isDebug) { return false; }
 if (force === 0) { return false; }
 msg = APRinf.name_short + ': ' + msg;
 if (typeof GM_log !== 'undefined') { GM_log(msg); return true; }
 if (typeof opera !== 'undefined' && opera.postError) { opera.postError(msg); return true; }
 if (typeof console !== 'undefined' && console.log) { console.log(msg); return true; }
 alert(msg);
 return false;
}


//------------------------------------------------
// Data storage
//------------------------------------------------

// implement JSON functions if they're not already defined - modified from
// http://www.sitepoint.com/blogs/2009/08/19/javascript-json-serialization/
if (!this.JSON) { this.JSON = {}; }
if (typeof JSON.parse !== 'function') {
 JSON.stringify = function (obj) {
  var t = typeof (obj);
  if (t !== 'object' || obj === null) {
   if (t === 'string') { obj = '"' + obj.replace('"','\\"') + '"'; }
   return String(obj);
  } else {
   var n, v, json = [], arr = (obj && obj.constructor === Array);
   for (n in obj) {
    v = obj[n]; t = typeof(v);
    if (t !== 'function') {
     if (t === 'string') { v = '"' + v.replace('"','\\"') + '"'; }
     if ( t === 'object' && v !== null) { v = JSON.stringify(v); }
     json.push((arr ? '' : '"' + n + '":') + String(v));
    }
   }
   return (arr ? '[' : '{') + String(json) + (arr ? ']' : '}');
  }
 };
}
if (typeof JSON.parse !== 'function') {
 JSON.parse = function (str) {
  if (str === '') { str = '""'; }
  eval('var p=' + str + ';');
  return p;
 };
}

// prepare data storage functions
function storageInit() {
 debugMsg('initialize data storage');

 // detect available storage type
 var storage = 'none';
 try {
  if (typeof GM_getValue === 'function' && typeof GM_setValue === 'function') {
   // test if greasemonkey's functions work.
   var GM_testkey = 'GM_testkey',
       GM_testvalue = 'GM_testvalue-' + (new Date() *1);
   GM_setValue(GM_testkey, GM_testvalue);
   if (GM_getValue(GM_testkey, false) === GM_testvalue) { storage = 'greasemonkey'; }
   GM_deleteValue(GM_testkey);
  }
 } catch(x) { debugMsg('storageInit error: '+x,1); }
 if (storage === 'none' && typeof localStorage === 'object') { storage = 'localstorage'; }
 debugMsg('storage type=' + storage);

 // some shitty browser detected
 if (storage === 'none') {
  setTimeout(function () {
   alert('Sorry but your browser sux ...\n' +
         'No permanent storage detected - initiating temporary storage.\n' +
         'All changes will be lost after page refresh!');
  }, 0);
  localStorage = (function () {
   var instance = {},
       tmp_array = [];
   instance.setItem = function (name, value){
    tmp_array[name] = value;
   };
   instance.getItem = function (name) {
    return tmp_array[name] || null;
   };
   instance.removeItem = function (name) {
    tmp_array[name] = null;
   };
   return instance;
  })();
  storage = 'fakestorage';
 }

 // fake GM_ functions
 if (storage === 'localstorage' || storage === 'fakestorage') {
  GM_setValue = function (name, value) {
   localStorage.setItem(name, value);
  };
  GM_getValue = function (name, defaultValue) {
   return localStorage.getItem(name) || defaultValue;
  };
  GM_deleteValue = function (name) {
   localStorage.removeItem(name);
  };
 }

 return storage;
}

// function to handle configuration data
var config = (function () {
 var prefix = 'FB_APR-' + 123456 + '-', //to do - user id
     instance = {};

 instance.set = function (key, value){
  //setTimeout(function () { GM_setValue(prefix + key, value); }, 0);
  GM_setValue(prefix + key, value);
 };
 instance.get = function (key, defaultValue) {
  return GM_getValue(prefix + key) || defaultValue;
 };
 instance.remove = function (key) {
  GM_deleteValue(prefix + key);
 };
 instance.save = function (key, value){
  GM_setValue(prefix + key, JSON.stringify(value));
 };
 instance.load = function (key, defaultValue) {
  return JSON.parse(GM_getValue(prefix + key) || defaultValue);
 };
 instance.init = function (key, defaultValue) {
  var cfginit;
  cfginit = instance.load(key, '"none"');
  if (cfginit === 'none') {
   instance.save(key, defaultValue);
   cfginit = instance.load(key, '"none"');
  }
  return cfginit;
 };
 return instance;
})();

// Opera Greasemonkey support
function operaGM() {
 setTimeout(function () {
  var msgNoOperaGM = config.get('msgNoOperaGM', 1 ) *1;
  if ( msgNoOperaGM === 1) {
   config.set('msgNoOperaGM', 0 +'');
   var GMemu_url = 'http://userscripts.org/scripts/show/88932';
   if (confirm('Opera have no built in Greasemonkey suport.\n' +
               'Please install Greasemonkey Emulation script.\n' +
               'URL: ' + GMemu_url +  '\n\n' +
               'Do you want to install now?')) {
    unsafeWindow.open(GMemu_url, '_blank');
   }
  }
 }, 1000);
}


//------------------------------------------------
// APR "GUI"
//------------------------------------------------

var apr_language = 'en';

// set current language
function aprGuiLanguage() {
 debugMsg('GUI select language');

 // try read document locale or default to english
 var loc = document.body.className.match(/locale_([^ ]+)/i) || ['','en'];
 if (cfg.language === 'auto') {
  apr_language = loc[1].toLowerCase();
  if (!text_strings[apr_language]) {
   apr_language = apr_language.split('_')[0];
   if (!text_strings[apr_language]) { apr_language = 'en'; }
  }
 } else { apr_language = cfg.language; }
}

// Get a string in the current language, default to english or return empty.
// Replace %x with text from repl array
function str(key, repl) {
 var out_string, lng = apr_language;
 if (!text_strings[lng][key]) { lng = 'en'; }
 if (text_strings[lng][key]) { out_string = text_strings[lng][key]; }
 else { out_string = ''; }
 if (repl) {
  if (typeof repl === 'number') { repl = repl + ''; }
  if (typeof repl === 'string') { repl = repl.split('#&@'); }
  if (repl instanceof Array) { var rgx, ii = repl.length; while (ii--) { rgx = new RegExp('%' + (ii + 1),'g'); out_string = out_string.replace(rgx, repl[ii]); } }
 }
 return out_string;
}

// show info in apr_info div
function aprInfo(text) {
 debugMsg('info: "'+text+'"',1);

 try {
  var result = $('#apr_info');
  result.firstChild.innerHTML = 'APR: ' + text;
  result.style.height = 'auto';
  result.style.overflow = 'visible';
  result.style.visibility = 'visible';
 } catch(x) { debugMsg('aprInfo error: '+x,1); }
}

// hide apr_info div
function aprInfoHide() {
 debugMsg('GUI hide info div');

 try {
  var result = $('#apr_info');
  result.style.height = '0px';
  result.style.overflow = 'hidden';
  result.style.visibility = 'hidden';
 } catch(x) { debugMsg('aprInfo error: '+x,1); }
}

// create div for APR informations
function aprCreateInfoDiv(elm) {
 debugMsg('GUI create info div');

 if ($('#apr_info')) { return; }
 try {
  addStyle([
           '#apr_info { height:0px; padding:0 5px; display:block; border:1px dotted grey; overflow:hidden; visibility:hidden; }',
           '#apr_info:hover #apr_info_close { color:red; }',
           ''].join(''));

  var newAPRinfo = document.createElement('div');
  newAPRinfo.id = 'apr_info';
  newAPRinfo.innerHTML = [
    '<span style="float:left;">APR: information area.</span>',
    '<span id="apr_info_close" style="float:right;" title="' + str('gui-close') + '"> [x]</span>',
    '<div style="clear:both;"></div>',
    ''].join('');
  insertBefore(newAPRinfo,$(elm).firstChild);
  on('click', '#apr_info', aprInfoHide);
 } catch(x) { debugMsg('aprCreateInfoDiv error: '+x,1); }
}

// create button on right side of top menu
function aprCreateRunButton() {
 debugMsg('GUI create run button');

 if ($('#apr_runbtn')) { return; }
 try {
  var newAPRbutton = document.createElement('li');
  newAPRbutton.id = 'apr_runbtn';
  newAPRbutton.innerHTML = '<a onclick="javascript:return(void);" title="' + str('gui-runbtn-hlp') + '">' + str('gui-runbtn') + '</a>';
  $('#pageNav').appendChild(newAPRbutton);
  on('click', '#apr_runbtn', function () { aprProcess(); });
 } catch(x) { debugMsg('aprCreateRunButton error: '+x,1); }
}

// create menu item inside FB account menu
function aprCreateConfigButton() {
 debugMsg('GUI create configuration button');

 if ($('#apr_cfgbtn')) { return; }
 try {
  var newAPRconfig = document.createElement('li');
  newAPRconfig.id = 'apr_cfgbtn';
  newAPRconfig.innerHTML = '<a onclick="javascript:return(void);" title=\'' + str('gui-cfgbtn-hlp') + '\'>' + str('gui-cfgbtn') + '</a>';
  insertBefore(newAPRconfig, $('//li[@id="navAccount"]/ul', null, true).childNodes[2]);
  on('click', '#apr_cfgbtn', aprShowConfig);
 } catch(x) { debugMsg('aprCreateConfigButton error: '+x,1); }
}

var apr_popup_level = 0;

// create popup
function aprCreatePopup() {
 debugMsg('GUI create popup');

 try {
  if (apr_popup_level === 0) {
   addStyle([
            '.apr_popupanchor { display:none; top:0; right:0; bottom:0; left:0; }',
            '.apr_popupshadow { display:none; position:fixed; top:0; left:0; right:0; bottom:0; background:black; opacity:0.3; }',
            '.apr_popup { position:relative; margin:auto; padding:10px; border:3px double #666666; background:#f6f6f6; }',
            '.apr_fll { float:left; }',
            '.apr_flr { float:right; }',
            '.apr_3t { position:relative; top:3px; }',
            ''].join(''));
  }

  var newAPRpopupanchor = document.createElement('div');
  newAPRpopupanchor.id = 'apr_popupanchor' + apr_popup_level;
  newAPRpopupanchor.className = 'apr_popupanchor';
  document.body.appendChild(newAPRpopupanchor);

  var newAPRpopupshadow = document.createElement('div');
  newAPRpopupshadow.id = 'apr_popupshadow' + apr_popup_level;
  newAPRpopupshadow.className = 'apr_popupshadow';
  document.body.appendChild(newAPRpopupshadow);

 } catch(x) { debugMsg('aprCreatePopup error: '+x,1); }
}

// hide popup created with aprShowPopup
function aprHidePopup() {
 debugMsg('GUI hide popup');

 try {
  $('#apr_popupanchor' + apr_popup_level).style.display = 'none';
  $('#apr_popupshadow' + apr_popup_level).style.display = 'none';
  apr_popup_level--;
  if (apr_popup_level > 0) { $('#apr_popupshadow' + apr_popup_level).style.display = 'block'; }
 } catch(x) { debugMsg('aprHidePopup error: '+x,1); }
}

// show popup div with a shadow background
function aprShowPopup(id, width, top, fixedPosition, title, body, footer_l, footer_r, autoclose_delay) {
 debugMsg('GUI show popup');

 try {
  apr_popup_level++;
  aprCreatePopup();

  var anch = $('#apr_popupanchor' + apr_popup_level),
      shdw = $('#apr_popupshadow' + apr_popup_level),
      close_btn_id = '#apr_popup_msg_close' + apr_popup_level;

  if (typeof id !== 'string') { id = 'apr_popup-' + apr_popup_level; }
  if (typeof width !== 'string') { width = '400px'; }
  if (typeof top !== 'string') { top = '200px'; }
  if (typeof fixedPosition !== 'boolean') { fixedPosition = true; }
  if (typeof title !== 'string') { title = ''; }
  if (typeof body !== 'string') { body = ''; }
  if (typeof footer_l !== 'string') { footer_l = ''; }
  if (typeof footer_r !== 'string') { footer_r = ''; }

  anch.innerHTML = [
   '<div id="' + id + '" class="apr_popup" style="width:' + width + '; top:' + top + ';">',
    // "title"
    '<span style="font-size:14px;"><b>FB Application Post Remover</b></span> - ' + title + '<br /><hr />',
    // "body"
    '<div>',
     body,
    '</div>',
    // "footer"
    '<div style="clear:both;"></div><hr />',
    '<div>',
     '<span class="apr_fll apr_3t">',
      footer_l,
     '</span>',
     '<span class="apr_flr">',
      footer_r,
      '<input type="button" id="' + close_btn_id + '" value="' + str('gui-close') + '" />',
     '</span>',
    '</div>',
    '<div style="clear:both;"></div>',
   '</div>'].join('');

  shdw.style.zIndex = (1000 + (apr_popup_level * 2)) + '';
  anch.style.zIndex = (1001 + (apr_popup_level * 2)) + '';
  shdw.style.display = 'block';
  anch.style.display = 'block';
  $('#apr_popupshadow' + (apr_popup_level - 1)).style.display = 'none';
  anch.style.position = fixedPosition ? 'fixed' : 'absolute';
  if (!fixedPosition) { window.scroll(0,0); }
  doClick(anch); // click on popup to get rid of FB menu
  on('click', '#' + close_btn_id, aprHidePopup);
  if (typeof autoclose_delay === 'number') {
   var aprmsgtimeout = setTimeout(aprHidePopup, autoclose_delay);
   on('click', '#' + close_btn_id, function () { clearTimeout(aprmsgtimeout); });
  }

 } catch(x) { debugMsg('aprShowPopup error: '+x,1); }
}

// messagebox based on aprShowPopup
function aprShowMessage(title, body, buttons, autoclose_delay) {
 debugMsg('GUI show message');

 aprShowPopup(null, null, null, true, title, body, null, buttons, autoclose_delay);
}


//------------------------------------------------
// Configuration
//------------------------------------------------

// show config window
function aprShowConfig() {
 debugMsg('show configuration window');

 var apr_option = 'apr_cfg_o_',
     cfgID = 'apr_popup-config';
 cfg = config.load('cfg', cfg_defaults);

 addStyle([
          '.apr_in-row { overflow:hidden; clear:both; }',
          '.apr_in-row label{ margin-left:4px; position:relative; top:3px; }',
          '.apr_in-row_cb { float:left; width:36px; border:1px solid #f6f6f6; }',
          '.apr_in-row_ni { float:left; width:32px; margin-right:2px; border:1px solid grey; }',
          '#apr_cfg_leftcol { float:left; width:290px; }',
          '#apr_cfg_rightcol { float:right; width:290px; }',
          ''].join(''));

 // create checkbox
 function inChb(opt) {
  return ['<div title="' + str('cfg-' + opt + '-hlp') + '" class="apr_in-row">',
       '<div class="apr_in-row_cb"><input type="checkbox" id="' + apr_option + opt + '" class="apr_flr" /></div>',
       '<label for="' + apr_option + opt + '">' + str('cfg-' + opt) + '</label>',
      '</div>'].join('');
 }

 // create inputbox
 function inVal(opt) {
  return ['<div title="' + str('cfg-' + opt + '-hlp') + '" class="apr_in-row">',
       '<input type="text" id="' + apr_option + opt + '" class="apr_in-row_ni" />',
       '<label for="' + apr_option + opt + '">' + str('cfg-' + opt) + '</label>',
      '</div>'].join('');
 }

 // create language selector
 function selLang(opt) {
  var opts = '', ii, lngl = text_strings.languages.length;
  for (ii = 0; ii < lngl; ii++) {
   opts = opts + '<option value="' + text_strings.languages[ii] + '">' + text_strings[text_strings.languages[ii]]._language + '</option>';
  }
  return ['<div title="' + str('cfg-' + opt + '-hlp') + '" class="apr_in-row">',
       str('cfg-' + opt),
      '<select id="' + apr_option + opt + '">',
       '<option value="auto">Automatic</option>',
       opts,
      '</select>',
      '</div>'].join('');
 }

 // show config window
 aprShowPopup(cfgID, '600px', '100px', true, str('cfg-config'),
   // body
   ['<div id="apr_cfg_leftcol">',
     selLang('language'),
     '<br />',
     inVal('time_limit'),
     inChb('continue_proc'),
     inChb('autorun'),
     inChb('only_user_posts'),
     inChb('hide_posts'),
     '<br /><br />',
     inChb('isDebug'),
     inVal('xhr_delay'),
    '</div>',
    '<div id="apr_cfg_rightcol">',
     inChb('all_apps'),
     '<span title="' + str('cfg-appIDs-hlp') + '">' + str('cfg-appIDs') + '</span><br />',
     '<textarea id="' + apr_option + 'appIDs" style="width:97%; height:150px;"></textarea>',
    '</div>',
   ''].join(''),
   // about
   ['<a href="http://userscripts.org/scripts/show/92953" target="_blank" title="FaceBook Application Post Remover">FB APR</a>',
    ' v' + APRinf.ver + ' (' + str('cfg-author') + ' ',
    str('cfg-author_email', ['<a href="mailto:sigi_cz@centrum.cz" title="','">Sigi_cz</a>']) + ') ',
    str('cfg-licence') + ' ',
    '<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/" title="Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License">',
    '<span class="apr_3t"><img alt="cc-by-nc-nd-3.0" style="border-width:0" src="http://i.creativecommons.org/l/by-nc-nd/3.0/80x15.png" /></span></a>',
   ''].join(''),
   '<input type="button" id="apr_cfg_save" value="' + str('gui-save') + '" /> '
   );

 // set/get config values
 function setgetInputs(mode) {
  var elm, opt,
      opts = $('//*[@id="' + cfgID + '"]//input[starts-with(@id,"' + apr_option + '")]'),
      ii = opts.snapshotLength;

  while (ii--) {
   elm = opts.snapshotItem(ii);
   opt = elm.id.replace(apr_option,'');

   if (mode === 'set') {
    if (elm.type === 'checkbox') { elm.checked = cfg[opt]; }
    if (elm.type === 'text') { elm.value = cfg[opt]; }
   }
   if (mode === 'get') {
    if (elm.type === 'checkbox') { cfg[opt] = elm.checked; }
    if (elm.type === 'text') { cfg[opt] = elm.value; }
   }
  }
 }

 // set/get appIDs from textarea
 function setgetAppIDs(mode) {
  var ii, val, opt = 'appIDs',
      elm = $('//*[@id="' + cfgID + '"]//*[@id="' + apr_option + opt + '"]').snapshotItem(0);

  function sortByName(a, b) {
    var x = ('str' + a[1]).toLowerCase(), y = ('str' + b[1]).toLowerCase();
    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  }

  if (mode === 'set') {
   //duplicate array, sort by name and join
   val = cfg[opt].slice().sort(sortByName); ii = val.length;
   while (ii--) { val[ii] = val[ii].join(' '); }
   elm.value = val.join('\n');
  }
  if (mode === 'get') {
   //remove empty lines and split into array ...
   val = elm.value.replace(/[\r\n][\s]+[\r\n]/g,'\n').replace(/^[\r\n]+|[\r\n]+$/g,'').split(/[\r\n]+/); ii = val.length;
   //... trim whitespaces from each element and split (2D array)
   while (ii--) { val[ii] = val[ii].replace(/^\s+|\s+$/g, '').split(/\s+(.+)/,2); }
   cfg[opt] = val;
  }
 }

 // set/get selected language option
 function setgetLanguage(mode) {
  var opt = 'language',
      elm = $('//*[@id="' + cfgID + '"]//*[@id="' + apr_option + opt + '"]').snapshotItem(0);

  if (mode === 'set') { elm.value = cfg[opt]; }
  if (mode === 'get') { cfg[opt] = elm.value; }
 }

 function saveCfg() {
  setgetInputs('get');
  setgetAppIDs('get');
  setgetLanguage('get');
  aprGuiLanguage();
  config.save('cfg', cfg);
  aprHidePopup();
  aprShowConfig();
 }

 setgetInputs('set');
 setgetAppIDs('set');
 setgetLanguage('set');

 on('click', '#apr_cfg_save', saveCfg);
}


//------------------------------------------------
// Updater
//------------------------------------------------

// check for script update
function updaterStart(scriptInfo, metadata) {
 debugMsg('updater - check update',1);

 try {
  var metaID = metadata.match(/@uso:script\s*(.+)/)[1],
      metaVer = metadata.match(/@version\s*(.+)/)[1];
  debugMsg('' +
           'scriptInfo.id=' + scriptInfo.id + '\n' +
           'metaID=' + metaID + '\n' +
           'scriptInfo.ver=' + scriptInfo.ver + '\n' +
           'metaVer=' + metaVer + '\n' +
           '');
  if (metaID === scriptInfo.id && metaVer !== scriptInfo.ver) {
   setTimeout(function () {
    if (confirm(str('str-update_long', ['\n', scriptInfo.name, scriptInfo.ver, metaVer, scriptInfo.url]))) {
     unsafeWindow.open(scriptInfo.url, '_blank');
    }
   }, 0);
  }
 } catch(x) { debugMsg('updaterStart error: '+x,1); return; }
}

// add iframe and load metadata
function updaterFrame(updateSrc) {
 debugMsg('updater - create iFrame');

 if ($('#apr_updater')) { return; }
 try {
  var newAPRupdater = document.createElement('iframe');
  newAPRupdater.id = 'apr_updater';
  newAPRupdater.setAttribute('style', 'display: none;');
  //newAPRupdater.setAttribute('style', 'display: block; width:500px; height:300px; border:1px solid red;');
  newAPRupdater.src = updateSrc;
  $('body')[0].appendChild(newAPRupdater);
 } catch(x) { debugMsg('updaterFrame error: '+x,1); }
}

// initialize
function updaterInit() {
 debugMsg('updater - initialize');

 setTimeout(function () {
  var now = new Date().getTime();
  var updateInterval = cfg.updateInterval * 60 * 60 * 1000;
  var lastUpdateCheck = config.get('lastUpdateCheck', 0)*1;
  debugMsg('' +
           'now.toLocaleString()=' + now.toLocaleString() + '\n' +
           'lastUpdateCheck.toLocaleString()=' + lastUpdateCheck.toLocaleString() + '\n' +
           'updateInterval=' + updateInterval + '\n' +
           '',0);
  if (now > (lastUpdateCheck + updateInterval)) {
   updaterFrame(APRinf.meta + '?' + now);
   config.set('lastUpdateCheck', now+'');
  }
 }, 3000);
}


//------------------------------------------------
// Process
//------------------------------------------------

// process posts on profile page
function processProfilePage() {
 debugMsg('process profile page',1);

 // FB profile page elements
 var elmStream = '#profile_minifeed',
     elmTime = './/@data-date',
     elmClBtn = '.uiCloseButton',
     elmPosts = './/*[@class="uiStreamSource"]',
     elmMore = '.uiMorePagerPrimary',
     elmMoreAn = '.uiMorePager';
 cfg = config.load('cfg', cfg_defaults);

 aprCreateInfoDiv(elmStream);

 // test access to unsafeWindow.Env
 if (typeof unsafeWindow.Env.user === 'undefined') {
  aprInfo(str('inf-error'));
  debugMsg('no access to unsafeWindow.Env.user',1);
  return;
 }

 //------------------------------------------------
 // functions - search last known post
 //------------------------------------------------

 function postsClickOlder(rpt) {
  debugMsg('click "Older Posts"');

  var clicked = false;
  var a = $(elmMore)[0];
  try {
   if (a) {
    try {
     if (a.onclick) {
      a.onclick();
      clicked = true;
     }
    } catch(xx) { }
    if (!clicked) {
     doClick(a);
    }
   }
  } catch(x) {
   debugMsg('clickOlderPosts error: '+x,1);
   if (rpt) { return; } // return on repeated error
   setTimeout(function (){ postsClickOlder(true); },1000);
  }
 }

 function postsLoadOlder(callback_func) {
  debugMsg('load older posts',1);

  var ii=0;
  function ni() {
   ii++;
   debugMsg('' +
            'xx=' + ii + '\n' +
            'profile_pager=' + $(elmMoreAn)[0].className + '\n' +
            '',0);
   if (!testClass($(elmMoreAn)[0],'async_saving')) {
    debugMsg('load older posts - finished');
    $(elmStream).removeEventListener('DOMNodeInserted', ni, false);
    callback_func();
   }
  }

  $(elmStream).addEventListener('DOMNodeInserted', ni, false);

  postsClickOlder();

 }

 //------------------------------------------------
 // functions - process visible posts
 //------------------------------------------------

 // get "post" date
 function postDate(post) {
  if (typeof post === 'string') {
   debugMsg('get \'' + post + '\' post date');

   var elms = $(elmPosts, $(elmStream));
   if (post === 'first') { post = elms.snapshotItem(0); }
   if (post === 'last') { post = elms.snapshotItem(elms.snapshotLength - 1); }
  }
  return Date.parse($(elmTime, post).snapshotItem(0).textContent);
 }

 //unsafeWindow.Env, cfg(appIDs, all_apps, only_user_posts)
 function postsGet() {
  debugMsg('get posts',1);

  var user2check = cfg.only_user_posts ? 'contains(@data-ft,\'"actrs":"' + unsafeWindow.Env.user + '"\') and ' : '',
      apps2check = '', ii;
  if (!cfg.all_apps) { ii = cfg.appIDs.length; while (ii--) { apps2check = apps2check + 'contains(@data-ft,\'"app_id":' + cfg.appIDs[ii][0] + '}\') or '; } }
  else { apps2check = 'contains(@data-ft,\'"app_id":\')'; }
  apps2check = ('(' + apps2check + ')').replace(' or )', ')');

  debugMsg('' +
           'appIDs=' + cfg.appIDs + '\n' +
           'user2check=' + user2check + '\n' +
           'apps2check=' + apps2check + '\n' +
           '');

  // select matching elements
  var elms = $('.//*[' + user2check + apps2check + ']', $(elmStream));
  debugMsg('' +
           'elms.snapshotLength=' + elms.snapshotLength + '\n' +
           //'elms.snapshotItem(0).innerHTML=' + elms.snapshotItem(0).innerHTML + '\n' +
           '');
  return elms;
 }

 //cfg(time_limit)
 function postsFirstOld(elms) {
  debugMsg('find first old post',1);

  var ii, tDiff,
      elms_cnt = elms.snapshotLength,
      old = elms_cnt, // first old post
      tStory = new Date(), // story time
      tNow = new Date(); // current time
      tNow.getTime();

  // find first expired item
  for (ii=0; ii<elms_cnt; ii++) {

   tStory.setTime(postDate(elms.snapshotItem(ii)));
   tDiff = Math.floor((tNow-tStory)/1000/60/60); // difference in full hours

   debugMsg('' +
            'ii=' + ii + '\n' +
            'time_limit=' + cfg.time_limit + '\n' +
            'tStory.toLocaleString()=' + tStory.toLocaleString() + '\n' +
            'tNow.toLocaleString()=' + tNow.toLocaleString() + '\n' +
            'tDiff=' + tDiff + '\n' +
            '',0);

   if (tDiff >= cfg.time_limit) {
    old = ii;
    break;
   }
  }

  debugMsg('' +
           'first old=' + old + '\n' +
           '');
  return old;
 }

 //save last known post date
 function postsSaveLastKnown(post) {
  var lkp = postDate(post);
  debugMsg('' +
           'postsSaveLastKnown ' + new Date(lkp).toLocaleString() + '\n' +
           '');
  config.set('lastKnownPost', lkp+'');
 }

 //unsafeWindow.Env, cfg(xhr_delay, hide_posts)
 function postsRemove(elms, from) {
  debugMsg('remove posts',1);

  var ii, story_type, story_key, clbtn,
      elms_cnt = elms.snapshotLength,
      xhr_userID = unsafeWindow.Env.user || 0,
      xhr_pfID = unsafeWindow.Env.post_form_id || 0,
      xhr_fbdtsg = unsafeWindow.Env.fb_dtsg || 0;

  // check xhr variables
  if (xhr_userID===0 || xhr_pfID===0 || xhr_fbdtsg===0) {
   aprInfo(str('inf-error'));
   debugMsg('' +
            'XHR - no access to unsafeWindow.Env\n' +
            'xhr_userID=' + xhr_userID + '\n' +
            'xhr_pfID=' + xhr_pfID + '\n' +
            'xhr_fbdtsg=' + xhr_fbdtsg + '\n' +
            '',1);
   return;
  }

  debugMsg('' +
           'elms_cnt=' + elms_cnt + '\n' +
           'from=' + from + '\n' +
           'xhr_userID=' + xhr_userID + '\n' +
           'xhr_pfID=' + xhr_pfID + '\n' +
           'xhr_fbdtsg=' + xhr_fbdtsg + '\n' +
           'xhr_delay=' + cfg.xhr_delay + '\n' +
           '');

  // remove all expired (from last)
  for (ii=elms_cnt-1; ii>=from; ii--) {

   clbtn = $(elmClBtn, elms.snapshotItem(ii))[0];
   story_type = clbtn.getAttribute('ajaxify').match(/story_type=(\d+)/i)[1];
   story_key = clbtn.getAttribute('ajaxify').match(/story_key=(\d+)/i)[1];

   debugMsg('' +
            'ii=' + ii + '\n' +
            'story_type=' + story_type + '\n' +
            'story_key=' + story_key + '\n' +
            '',0);

   if (!cfg.hide_posts && cfg.xhr_delay > 0) {
    delay(cfg.xhr_delay); // don't send all requests at once ...
    xhr('http://www.facebook.com/ajax/minifeed.php?__a=1', '',
        'profile_fbid=' + xhr_userID +
        '&ministory_key=' + story_key +
        '&story_type=' + story_type +
        '&post_form_id=' + xhr_pfID +
        '&post_form_id_source=AsyncRequest' +
        '&fb_dtsg=' + xhr_fbdtsg
       );
    postsSaveLastKnown(elms.snapshotItem(ii-1) || elms.snapshotItem(ii));
   }
   destroy(elms.snapshotItem(ii));
  }

  // save last post date
  if ((!cfg.hide_posts && cfg.xhr_delay > 0) && (elms_cnt === from)) {
   postsSaveLastKnown(elms_cnt === 0 ? 'first' : elms.snapshotItem(elms_cnt-1));
  }

  aprInfo(str(((!cfg.hide_posts && cfg.xhr_delay > 0) ? 'inf-xremoved' : 'inf-xhidden'), [elms_cnt, (elms_cnt - from)]));
 }

 // select all visible app posts, find first expired and remove old
 function postsProcessVisible() {
  aprInfo(str('inf-process'));
  var posts, firstOld;
  posts = postsGet();
  firstOld = postsFirstOld(posts);
  postsRemove(posts, firstOld);
 }

 //------------------------------------------------
 // processing start
 //------------------------------------------------

 if (cfg.continue_proc && !cfg.hide_posts) {

  var ii = 1, last_visible_post = 0,
      last_known_post = config.get('lastKnownPost', new Date())*1,
      last_known_post_str = new Date(last_known_post).toLocaleString();

  function gogo() {
   last_visible_post = postDate('last');
   aprInfo(str('inf-search', [last_known_post_str, ii++]));

   debugMsg('' +
            'last_known_post_str=' + last_known_post_str + '\n' +
            'last_visible_post=' + last_visible_post + '\n' +
            'last_visible_post=' + new Date(last_visible_post).toLocaleString() + '\n' +
            '');

   if (last_known_post < last_visible_post) {
    debugMsg('last known post not found - load next page');
    postsLoadOlder(gogo);
   } else {
    debugMsg('last known post found');
    postsProcessVisible();
   }

  }
  gogo();

 } else {
  debugMsg('continue processing disabled');
  postsProcessVisible();
 }

 // processing finished
}

// add/remove appIDs on application pages
function processApplicationPage() {
 debugMsg('process application page',1);

 var app_name = $('//*[@id="profile_name"]').snapshotItem(0).innerHTML,
     app_link = $('//*[contains(@id,"left_column")]//a[contains(@href,"/ajax/report.php")]'),
     app_ID = app_link.snapshotItem(0).getAttribute('href').match(/cid=(\d+)/i)[1],
     elm_action_btn, itm, is_added;
 cfg = config.load('cfg', cfg_defaults);

 aprShowMessage(str('str-addremove'),
   '<span style="font-size:12px;"><b>' + app_ID + ' ' + app_name + '</b></span>',
   '<input type="button" id="apr_app_action" value="action" /> ');

 elm_action_btn = $('#apr_app_action');

 function checkApp() {
  itm = contains(cfg.appIDs, app_ID);
  is_added = itm >= 0 ? true : false;
  elm_action_btn.value = is_added ? str('gui-remove') : str('gui-add');
 }

 function addremoveApp() {
  if (!is_added) { cfg.appIDs.push([app_ID,app_name]); }
  else { cfg.appIDs.splice(itm,1); }
  config.save('cfg', cfg);
  checkApp();
 }

 checkApp();
 on('click', '#apr_app_action', addremoveApp);
}

// identify current page and start
function aprProcess(autorun) {
 debugMsg('determine current page');

 var user_profile_new = $('//a[contains(@class,\'edit_profilepicture\')]').snapshotLength;
 if (user_profile_new) {
  debugMsg('current page is user profile (new)');
  processProfilePage();
  return;
 }

 // stop if autorun missed profile page
 if (autorun) { return; }

 var app_page = $('//*[contains(@id,"left_column")]//a[contains(@href,"/ajax/report.php") or contains(@href,"/apps/block.php?")]').snapshotLength;
 if (app_page >= 2) {
  debugMsg('current page is application page');
  processApplicationPage();
  return;
 }

 var apr_homepage = $('//*[contains(@id,"profile_name") and contains(text(),"FB Application Post Remover")]').snapshotLength;
 if (apr_homepage) {
  debugMsg('current page is FB APR homepage');
  aprShowMessage(str('str-APRhome'), str('str-APRhome_long'));
  return;
 }

 var user_profile_old = $('//div[contains(@class,\'can_edit\')]', $('#left_column')).snapshotLength;
 if (user_profile_old) {
  debugMsg('current page is user profile (old)');
  aprShowMessage(str('str-old_profile'), str('str-old_profile_long', ['<br />', '<a href="/about/profile/">', '</a>']));
  return;
 }

 debugMsg('current page is unknown');
 aprShowMessage(str('str-unknown'), [str('str-unknown_long', '<br />'),
   str('cfg-author_email', ['<a href="mailto:sigi_cz@centrum.cz" title="','">Sigi_cz</a>']),
   ''].join(''), '', 10000);
 return;
}


//------------------------------------------------
// start script
//------------------------------------------------

function aprStart() {
 debugMsg('start - DOMContentLoaded\n'+
          'unsafeWindow.location.href= '+unsafeWindow.location.href,1);

 // metadata found, check for script update and quit
 if (unsafeWindow.location.href.match(APRinf.meta)) {
  updaterStart(APRinf,document.getElementsByTagName('body')[0].innerHTML);
  return;
 }

 var storage = storageInit();
 // ask for Opera Greasemonkey support
 if (storage !== 'greasemonkey' && typeof opera !== 'undefined') { operaGM(); }
 cfg = config.init('cfg', cfg_defaults);
 aprGuiLanguage();
 aprCreatePopup();
 updaterInit();

 aprCreateRunButton();
 aprCreateConfigButton();

 if (cfg.autorun === true) {
  debugMsg('autorun enabled');
  aprProcess(true);
 }
}
document.addEventListener('DOMContentLoaded', aprStart(), false);
}());

0 comments:

Post a Comment