// Copyright (c) 2007-2008 Fark, Inc (except as noted)
// $Id: farktools.js 8065 2009-08-18 21:02:47Z mandrews $

// Fark.com Javascript tools.  We try to keep the Javascript to a minimum 'round
// these here parts...  mostly because Mike sucks at it.


// Surrounds any highlighted text in taObj with newText1 and newText2.
// If there isn't any, put noselText in if it exists.  If that is blank
// then do nothing at all.
// Much of this code is cribbed from multiple sources, such as Alex King's
// LGPL-licensed "QuickTags" code (which in turn is based on GPL-licensed
// phpMyAdmin code), plus several other sources found on Google, plus several
// Fark-specific customizations (such as the detection of whether the selected
// text is a URL or not)
// TODO: When appending to a large block of text in a textarea, the textarea
// does not scroll to the end of the newly inserted text in anything but IE,
// though the cursor is in the right spot (except sometimes Opera's off by 2
// and iCab is waaaay off) and it will scroll at the first keystroke.

function farkTextboxSurroundSelect(taObj,newText1,newText2,noselText) {
  var selStart = taObj.selectionStart;
  var selEnd = taObj.selectionEnd;
  if (typeof(selStart) == 'undefined' && document.selection) {
    // IE.  Might work in Opera too.
    taObj.focus();
    var sel = document.selection.createRange();
    if (sel.parentElement() == taObj) {
      if (sel.text) {
        var urltest = farkIsThisURL(sel.text);
        // "img"; if selected text is not a URL then the selected text
        // stays unmodified and we insert an img tag with a dummy URL
        if (newText1.substring(0,5) == '<img ' && urltest[0] == 0) {
          newText1 = '<img src="http://">';
          newText2 = '';
        }
        // "a href"; if selected text is not a URL then the selected text
        // is used as the anchor text with a dummy URL; otherwise use the
        // selected text as the URL with dummy anchor text
        if (newText1.substring(0,4) == '<a h' && urltest[0] == 0) {
          newText1 = '<a href="http://">';
          newText2 = '</a>';
        } else if (newText1.substring(0,4) == '<a t' && urltest[0] == 0) {
          newText1 = '<a target="_blank" href="http://">';
          newText2 = newText2.substr(6);  // preserve "(new window)" text on end
        }
        // Wrap tags around selected text.  Move cursor to after the tags.
        sel.text = newText1 + urltest[1] + newText2;
        sel.select();
      } else {
        // Nothing selected, insert probably-empty tags and noSelText if needed.
        // Move cursor to inbetween the tags.
        if (noselText == '+') noselText = '';
        sel.text = newText1 + noselText + newText2;
        sel.moveStart('character', -noselText.length-newText2.length);
        sel.moveEnd('character', -noselText.length-newText2.length);
        sel.select();
      }
    }
  } else {
    // Gecko, Webkit, Opera (if Opera doesn't pick the IE path)
    if (selStart == selEnd) {
      // Nothing selected, insert probably-empty tags, and noSelText if needed.
      // Move cursor to inbetween the tags.
      if (noselText == '+') noselText = '';
      var scrollLoc = taObj.scrollTop;
      taObj.value = taObj.value.substring(0, selStart) + newText1 + noselText + newText2 + taObj.value.substring(selEnd, taObj.value.length);
      taObj.focus();   // TODO consider removing this so page doesn't scroll down to textarea on click
      taObj.setSelectionRange(selEnd + newText1.length + noselText.length, selEnd + newText1.length + noselText.length);
      taObj.scrollTop = scrollLoc;  // workaround Firefox scrolling to top of textarea
    } else {
      var urltest = farkIsThisURL(taObj.value.substring(selStart,selEnd));
      // "img"; if selected text is not a URL then the selected text
      // stays unmodified and we insert an img tag with a dummy URL
      if (newText1.substring(0,5) == '<img ' && urltest[0] == 0) {
        newText1 = '<img src="http://">';
        newText2 = '';
      }
      // "a href"; if selected text is not a URL then the selected text
      // is used as the anchor text with a dummy URL; otherwise use the
      // selected text as the URL with dummy anchor text
      if (newText1.substring(0,4) == '<a h' && urltest[0] == 0) {
        newText1 = '<a href="http://">';
        newText2 = '</a>';
      } else if (newText1.substring(0,4) == '<a t' && urltest[0] == 0) {
        newText1 = '<a target="_blank" href="http://">';
        newText2 = newText2.substr(6);  // preserve "(new window)" text on end
      }
      // Wrap tags around selected text.  Move cursor to after the tags.
      var selgrowth = urltest[1].length-(selEnd-selStart);
      var scrollLoc = taObj.scrollTop;
      taObj.value = taObj.value.substring(0, selStart) + newText1 + urltest[1] + newText2 + taObj.value.substring(selEnd, taObj.value.length);
      taObj.focus();   // TODO consider removing this so page doesn't scroll down to textarea on click
      taObj.setSelectionRange(selEnd+newText1.length+newText2.length+selgrowth, selEnd+newText1.length+newText2.length+selgrowth);
      taObj.scrollTop = scrollLoc;  // workaround Firefox scrolling to top of textarea
    }
  }
  return false;
}

// Grab all text inside an ID element.  Basically same as innerHTML property
// (as returned by getElementById for example) but with the HTML stripped out.
// This is needed because not all browsers handle the textContent or innerText
// properties the same way.  The basic idea is in Chapter 17 of O'Reilly's
// "Javascript: The Definitive Guide" 4th ed but has a few additions per
// code found at http://www.thescripts.com/forum/post1719279-5.html

function farkGetTextContent(el) {
  if (el.innerText) return el.innerText;        // IE, WebKit, Opera -- preserves \n
  // if (el.textContent) return el.textContent; // OK in Firefox but strips \n -- don't use
  var cNode, cNodes = el.childNodes;
  var txt = '';
  if (el.tagName == 'BR') txt = '\n';  // Hack around Firefox stripping \n
  for (var i=0, len=cNodes.length; i<len; i++) {
    cNode = cNodes[i];
    if (cNode.nodeType == 1) txt += farkGetTextContent(cNode);
    if (cNode.nodeType == 3) txt += cNode.data;
  }
  return txt;
}

// Do minor cleanup on text.  Some of this comes from the "Farkit" extension for Firefox
// (not to be confused with our FarkIt link submission feature).  Similar cleanup is done
// on the server-side (where it belongs!) after submission.

function farkSelCleanup(s) {
  if (typeof(s) == 'undefined') return '';
  s = s.replace(/\s+$/, '');    // remove stray whitespace
  s = s.replace(/^\s+/, '');
  s = s.replace(/>/g, '&gt;');  // make sure escaped html stays that way
  s = s.replace(/</g, '&lt;');
  if (s.length > 4096) s = s.substring(0,4096) + ' ...';  // truncate huge cut/paste
  return s;
}

// Determine if string looks somewhat like a URL or not.
// Return an ARRAY with the first element either 1 or 0, and the
// second element being a modified version of the original string
// if it's necessary, and the unmodified string if not.  For example:
//   "click here"         => [0, "click here"]
//   "http://www.cnn.com" => [1, "http://www.cnn.com"]
//   "fark.com/farq"      => [1, "http://fark.com/farq"]  (prepends http://)
//   "fark.com wtf lol"   => [0, "fark.com wtf lol"]
//   "fark.com"           => [1, "http://fark.com"]
// TODO: We can do much better than this -- these regexps suck

function farkIsThisURL(s) {
  if (s.match(/^https?:\/\//)) return [1,s];
  if (s.match(/^\d+\.\d+\.\d+\.\d+/)) return [1,'http://'+s+'/'];
  if (s.match(/^[\w\.\-]+\.[a-zA-Z][a-zA-Z]+$/)) return [1,'http://'+s+'/'];
  if (s.match(/^[\w\.\-]+\.[a-zA-Z][a-zA-Z]+\//)) return [1,'http://'+s];
  return [0,s];
}

// Find the highlighted text on the page and return it.  Also, figure out what ID
// block it's in, and see if the block corresponds to a single comment.  If so,
// also return the comment number, if not, return a blank string.  Either way this
// function returns an ARRAY containing two strings (even tho the 2nd string is
// really an int).

// TODO: Safari 2.x strips all the \n from the selected text.  3.0 is fine.
// So does Opera unless you force the IE codepath by doing the IE test first,
// so that's our workaround for Opera.

function farkCmtFindSelection() {
  var par = undefined;
  var quotestr = '';
  if (document.selection && document.selection.createRange) {
     // IE.  Opera can use this path too
     var range = document.selection.createRange();
     quotestr = range.text;
     par = range.parentElement().id;
  } else if (window.getSelection) {
     // Gecko, WebKit.  Opera can use this path too
     quotestr = window.getSelection();
     var range = undefined;
     if (typeof(quotestr.rangeCount) == 'undefined') {
        // y helo thar Safari user with your pesky broken getRangeAt() (TODO: is this fixed in 3.x?)
        if (quotestr.anchorNode == null) return ['',''];
        range = document.createRange();
        range.setStart(quotestr.anchorNode,quotestr.anchorOffset);
        range.setEnd(quotestr.focusNode,quotestr.focusOffset);
     } else if (quotestr.rangeCount == 0) {
        return ['',''];
     } else if (quotestr.getRangeAt) {
        range = quotestr.getRangeAt(0);
     } else {
        return ['',''];  // shouldn't happen
     }
     if (range.parentElement) {
        par = range.parentElement;
     } else {
        par = range.commonAncestorContainer;
        if (par) {
           var count=20;  // Iterate upwards to find parent, but not infinitely
           while (count-- && (par.nodeType != 1 || par.id == '')) par = par.parentNode;
        }
     }
     par = par.id;
     quotestr = quotestr+'';  // quotestr.toString() fails on Safari for some reason
  } else return ['',''];
  if (typeof(quotestr) == 'undefined') return ['',''];
  if (quotestr == '') return ['',''];
  var comnum = '';
  if (typeof(par) != 'undefined' && par.match(/^ct\d+$/)) comnum = par.substring(2); 
  return [quotestr,comnum];
}

// Build a string from document ID's "ct$commentnum" (text) and "cu$commentnum" (login)
// then call farkTextboxSurroundSelect with that text to quote the entire message.
// If there is selected text AND it's in the SAME comment, then just quote that part,
// otherwise ignore the selection and quite the whole thing.

function farkCmtQuoteNum(taObj,commentNum,idLink) {
  var quocom = farkCmtFindSelection();
  if (quocom[0] != '' && quocom[1] == commentNum) return farkCmtQuoteSelected(taObj,idLink);
  var quotestr = farkSelCleanup(farkGetTextContent(document.getElementById('ct'+commentNum)));
  if (quotestr == '') return;
  var usrProtocol = location.protocol;
  var usrHost = location.host;
  var usrPath = location.pathname;
  if (usrHost == 'ssl.fark.com') {
     usrProtocol = 'http:';
     usrHost = 'www.fark.com';
     usrPath = '/cgi/comments.pl';
  }
  var usrTxt = farkGetTextContent(document.getElementById('cu'+commentNum));
  if (usrTxt != '') usrTxt = '<b><a target="_blank" href="'+usrProtocol+'//'+usrHost+usrPath+'?IDLink='+idLink+'&amp;IDComment='+commentNum+'#c'+commentNum+'">'+usrTxt+'</a>:</b> ';
  //if (usrTxt != '') usrTxt = '<b>'+usrTxt+':</b> ';
  return farkTextboxSurroundSelect(taObj, usrTxt+'<i>'+quotestr+'</i>\n\n', '', '');
}

// Build a string from the selected text on the page.  Find out if that string
// is contained inside a "ct$commentnum" (text) block, and if it is, grab the login
// from the associated "cu$commentnum" block.  Then call farkTextboxSurroundSelect
// to quote the partial message.  If the selection spans multiple comments or isn't
// part of a comment, just insert the quoted text with no login.

function farkCmtQuoteSelected(taObj,idLink) {
  var quocom = farkCmtFindSelection();
  var quotestr = farkSelCleanup(quocom[0]);
  if (quotestr == '') return;
  var commentNum = quocom[1];
  var usrTxt = '';
  if (commentNum != '') {
    var usrProtocol = location.protocol;
    var usrHost = location.host;
    var usrPath = location.pathname;
    if (usrHost == 'ssl.fark.com') {
      usrProtocol = 'http:';
      usrHost = 'www.fark.com';
      usrPath = '/cgi/comments.pl';
    }
    var usrTxt = farkGetTextContent(document.getElementById('cu'+commentNum));
    if (usrTxt != '') usrTxt = '<b><a target="_blank" href="'+usrProtocol+'//'+usrHost+usrPath+'?IDLink='+idLink+'&amp;IDComment='+commentNum+'#c'+commentNum+'">'+usrTxt+'</a>:</b> ';
    //if (usrTxt != '') usrTxt = '<b>'+usrTxt+':</b> ';
  }
  return farkTextboxSurroundSelect(taObj, usrTxt+'<i>'+quotestr+'</i>\n\n', '', '');
}

// Make every element of class 'jsonly' visible.  It's set 'display:none' in the CSS
// and this changes it to 'display:inline'.  This makes <span class="jsonly"> kind
// of the opposite of <noscript>.
// See http://www.thescripts.com/forum/showthread.php?t=155402
// Modifying document.styleSheet[0] in place doesn't work because Firefox doesn't
// allow JS loaded from cgi.fark.com to modify a DOM in www.fark.com (nor should it),
// though now that we aren't using cgi.fark.com anymore it shouldn't be an issue...

function farkEnaCSSjsonly() {
  var style = document.createElement('style');
  style.type = 'text/css';
  var selector = '.jsonly';
  var properties = 'display:inline;';
  document.getElementsByTagName('head').item(0).appendChild(style);
  if (document.styleSheets) {
    var lastSheet = document.styleSheets[document.styleSheets.length - 1];
    if (lastSheet && typeof(lastSheet.addRule) == 'object') { // ie
      lastSheet.addRule(selector,properties);
      return;
    }
  }
  style.appendChild(document.createTextNode(selector+' { '+properties+' }'));
}

// Old functions for updating status line; browsers don't support this anymore

function w(link) { window.status=link; return true; }
function c() { window.status=''; return true; }

// Make elements (in)visible or (en|dis)abled depending on if 'expr' is true.

function farkToggleTR(elementId,expr) {
  var el = document.getElementById(elementId);
  if (expr) {
    try {
      el.style.display = 'table-row';
    } catch (e) {
      el.style.display = 'block';
    }
  } else {
    el.style.display = 'none';
  }
  return true;
}
function farkToggleBlock(elementId,expr) {
  var el = document.getElementById(elementId);
  if (expr) {
    el.style.display = 'block';
  } else {
    el.style.display = 'none';
  }
}
function farkToggleInline(elementId,expr) {
  var el = document.getElementById(elementId);
  if (expr) {
    el.style.display = 'inline';
  } else {
    el.style.display = 'none';
  }
}
function farkEnableElement(elementId,expr) {
  var el = document.getElementById(elementId);
  if (expr) {
     el.disabled = false;
  } else {
     el.disabled = true;
  }
  return true;
}

// users.pl + Gecko only: Tweak background color on <option> items

function farkTweakOptionColor(foobar) {
  foobar.style.backgroundColor = foobar.options[foobar.selectedIndex].style.backgroundColor;
}

// Swap image.  The "change images to links" option is done by simply setting
// the img src to blank and the onclick handler to this routine.  This routine
// blasts the onclick handler back to nothing, so that if the image is inside
// a link tag, the second click will follow the link.

function farkImgSwap(imgEl,altText,url,w,h) {
  imgEl.src=url;
  imgEl.onclick=false;
  imgEl.alt=altText;
  imgEl.title=altText;
  imgEl.width=w;
  imgEl.height=h;
  return false;
}

// Add onload handler.

function farkAddOnloadEvent(func) {
  var oldonload = window.onload;
  if (typeof(window.onload) != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}
              
// Make elements in the "jsonly" CSS class visible at page load time.

// farkAddOnloadEvent(farkEnaCSSjsonly);
farkEnaCSSjsonly();

