
/* Browser detection
// Figure out what browser is being used
jQuery.browser = {
	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
	safari: /webkit/.test( userAgent ),
	opera: /opera/.test( userAgent ),
	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
};
*/

// *. General Functions

function to(a, b, step) {
    if (!a)  return [ ];
    var beg = b ? a : 1 , end = b ? b : a , step = step ? step : 1 ;
    var A = [ ];
    for (var i=beg; i <= end; i += step)
        A.push(i);
    return A;
}
// >>> to(10)
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// >>> to(5,10)
// [5, 6, 7, 8, 9, 10]
// >>> to(2, 20, 2)
// [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

function map(fn, A) {
    result = [ ];
    for (var i in A)
        result.push(fn(A[i]));
    return result;
}
// >>> var f = function(e) { return e[0]; }
// >>> var section_titles = map(f, vw_article_sections);

function dictmap(fn, D) {
    result = [ ];
    for (var k in D)
        result.push(fn(k, D[k]));
    return result;
}
// >>> var D = { 'a' : 'b' , 'c' : 'd' }
// >>> dictmap(function(key, value) { alert( key + ' : ' + value ); }, D).join( '\n' );

function mapconcat(fn, A, separator) {
    if (!separator)
        separator = ''
    return map(fn, A).join( separator );
}
// >>> var f = function(s) { return "<li>"+s+"</li>"; }
// >>> var li_s = mapconcat(f, A)

function find_elem(predicate, A) {
    /// If there is an element of A that satisfies predicate, return the element.
    /// Ex: find_if(callable, [3, min, max]) ==> min
    /// find_if(callable, [1, 2, 3]) ==> None
    for (var i in A) {
        if (predicate(A[i]))
            return A[i];
    }
    return null;
}
// >>> target_section_info = find_elem( function(e) {  return (e[0] == section_title); } , this.article_sections );
/// if (jawiki_title = find_elem( function(e) {  return (e['lang'] == 'ja'); } , data.parse.langlinks))
/// jawiki_title = jawiki_title[ '*' ];


// *. String Manipulation

function htmlspecialchars(s, noamp) {    /// noamp : if true, dont do: & -> &amp;
    if (!noamp)
        s = s.replace(/&/g, '&amp;');
    return s.replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\'/g, '&#039;');
}

function unhtmlspecialchars(s) {
    return s.replace(/&quot;/g, '"').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&#0?39;/g, '\'').replace(/&apos;/g, '\'').replace(/&amp;/g, '&');
}

function string_contains(s, substr) {
    return (s.indexOf(substr) >= 0);
}

function rfind(s, substr) {
    return s.lastIndexOf(substr);
}
// >>> rfind('bbaaabbbccc', 'bb');
// 6
// >>> rfind('d', 'a')
// -1

function strip(s) {
    return s.replace(/^\s+|\s+$/g, '') ;
}

function str2number(s) {
    // return parseInt(s);
    return Number(s);
}

/*
 *  Unicode to utf8 by AOK <soft@aokura.com>
*/
function toutf8(s) {
  var c, d = "";
  for (var i = 0; i < s.length; i++) {
    c = s.charCodeAt(i);
    if (c <= 0x7f) {
      d += s.charAt(i);
    } else if (c >= 0x80 && c <= 0x7ff) {
      d += String.fromCharCode(((c >> 6) & 0x1f) | 0xc0);
      d += String.fromCharCode((c & 0x3f) | 0x80);
    } else {
      d += String.fromCharCode((c >> 12) | 0xe0);
      d += String.fromCharCode(((c >> 6) & 0x3f) | 0x80);
      d += String.fromCharCode((c & 0x3f) | 0x80);
    }
  }
  return d;
}

/*
 * utf8 to unicode by AOK <soft@aokura.com>
*/
function tounicode(s) {
  var c, d = "", flag = 0, tmp;
  for (var i = 0; i < s.length; i++) {
    c = s.charCodeAt(i);
    if (flag == 0) {
      if ((c & 0xe0) == 0xe0) {
        flag = 2;
        tmp = (c & 0x0f) << 12;
      } else if ((c & 0xc0) == 0xc0) {
        flag = 1;
        tmp = (c & 0x1f) << 6;
      } else if ((c & 0x80) == 0) {
        d += s.charAt(i);
      } else {
        flag = 0;
      }
    } else if (flag == 1) {
      flag = 0;
      d += String.fromCharCode(tmp | (c & 0x3f));
    } else if (flag == 2) {
      flag = 3;
      tmp |= (c & 0x3f) << 6;
    } else if (flag == 3) {
      flag = 0;
      d += String.fromCharCode(tmp | (c & 0x3f));
    } else {
      flag = 0;
    }
  }
  return d;
}

/*
	Cross-Browser Split 0.2.1a --- i modified the behavior slightly from the original
	// Cross-Browser Split 0.2.1
	By Steven Levithan <http://stevenlevithan.com>
	MIT license
    SEE: http://blog.stevenlevithan.com/archives/cross-browser-split

    Behavior of String.split(...) is different between firefox, ie, and safari.
    This split function for string object works on any of the above browser and
    its behavior should be the same as FireFox2 String.split(...).
*/
var nativeSplit = nativeSplit || String.prototype.split;
String.prototype.split = function (s /* separator */, limit) {
	// If separator is not a regex, use the native split method
	if (!(s instanceof RegExp))
		return nativeSplit.apply(this, arguments);
	/* Behavior for limit: If it's...
	 - Undefined: No limit
	 - NaN or zero: Return an empty array
	 - A positive number: Use limit after dropping any decimal
	 - A negative number: No limit
	 - Other: Type-convert, then use the above rules */
	if (limit === undefined || +limit < 0) {
		limit = false;
	} else {
		limit = Math.floor(+limit);
		if (!limit)
			return [];
	}
	var	flags = (s.global ? "g" : "") + (s.ignoreCase ? "i" : "") + (s.multiline ? "m" : ""),
		s2 = new RegExp("^" + s.source + "$", flags),
		output = [],
		lastLastIndex = 0,
		i = 0,
		match;
	if (!s.global)
		s = new RegExp(s.source, "g" + flags);
	while ((!limit || i++ <= limit) && (match = s.exec(this))) {
		var zeroLengthMatch = !match[0].length;
		// Fix IE's infinite-loop-resistant but incorrect lastIndex
		if (zeroLengthMatch && s.lastIndex > match.index)
			s.lastIndex = match.index; // The same as s.lastIndex--
		if (s.lastIndex > lastLastIndex) {
			// Fix browsers whose exec methods don't consistently return undefined for non-participating capturing groups
			if (match.length > 1) {
				match[0].replace(s2, function () {
					for (var j = 1; j < arguments.length - 2; j++) {
						if (arguments[j] === undefined)
							// match[j] = undefined;
							match[j] = "";    /// !Modified
					}
				});
			}
			output = output.concat(this.slice(lastLastIndex, match.index), (match.index === this.length ? [] : match.slice(1)));
			lastLastIndex = s.lastIndex;
		}
		if (zeroLengthMatch)
			s.lastIndex++;
	}
	return (lastLastIndex === this.length) ?
		(s.test("") ? output : output.concat("")) :
		(limit      ? output : output.concat(this.slice(lastLastIndex)));
};


/**
* Uses some slightly modified version of crs32 from www.webtoolkit.info (MIT license)
* License: MIT
*/
String.prototype.crc32 = function(s) {
    try {
        var c;
        var CRC32Table = new Array(0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,
                                   0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
                                   0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,
                                   0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
                                   0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,
                                   0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
                                   0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,
                                   0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
                                   0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,
                                   0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
                                   0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,
                                   0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
                                   0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,
                                   0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
                                   0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,
                                   0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
                                   0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,
                                   0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
                                   0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,
                                   0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
                                   0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,
                                   0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
                                   0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,
                                   0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
                                   0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,
                                   0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
                                   0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,
                                   0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
                                   0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,
                                   0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
                                   0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,
                                   0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D);
        var crc       = 0xFFFFFFFF;
        var tmp       = (typeof(s) == 'undefined')?(this):(s);
        var tmpLength = tmp.length;
        
        for (var i=0; i<tmpLength; i++){
                c = tmp.charCodeAt(i);
                crc = CRC32Table[(crc ^ c) & 0xFF] ^ ((crc >> 8) & 0xFFFFFF);
            }
        return (crc ^ 0xFFFFFFFF);
    }
    catch(e) { return false; }
};
// >>> ''.crc32()
// 0
// >>> 'hello'.crc32()
// 907060870
// >>> 'ee333'.crc32()
// -157394946

// *. URL-Related functions

/// Return decoded { parameter : value } mapping of given url's query string.
/// '_nonqs' will contain non query string portion of url.
function getQsmap(url) {
    var args = new Object();
    var j = url.indexOf('?');
    var non_qs_part = url.substring(0, j);
    args['_nonqs'] = non_qs_part;
    var query = url.substring(j+1);
    var pairs = query.split("&");
    for(var i = 0; i < pairs.length; i++) {
        var pos = pairs[i].indexOf('=');
        if (pos == -1) continue;                  // If not found, skip
        var argname = pairs[i].substring(0, pos);  // Extract the name
        var value = pairs[i].substring(pos+1);    // Extract the value
        value = decodeURIComponent(value);        // Decode it, if needed
        args[argname] = value;                    // Store as a property
    }
    return args;
}

function recoverQsmap(qsmap) {
    var url = qsmap[ '_nonqs' ];
    var A = [ ];
    for (var name in qsmap) {
        if (name != '_nonqs')
            A.push( name + '=' + encodeURIComponent(qsmap[name]));
    }
    if (A.length < 1)
        return url;
    return url + '?' + A.join( '&' );
}
    
// *. Cookies

function setCookie(name, value, days) {
    /// Note that it doesn't escape value.
    /// name.length + 1 + value.length must be <= 4096 (in ascii).
    var expires = "";
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		expires = "; expires="+date.toGMTString();
	}
	// document.cookie = name+"="+encodeURIComponent(value)+expires+"; path=/";
	document.cookie = name+"="+value+expires+"; path=/";
}


function getCookie(name) {
    /// Note that it doesn't unescape value.
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0; i < ca.length; i++) {
		var c = ca[i];
		while (c.charAt(0)==' ')
            c = c.substring(1, c.length);
		if (c.indexOf(nameEQ) == 0)
            // return decodeURIComponent(c.substring(nameEQ.length,c.length));
            return c.substring(nameEQ.length, c.length);
	}
	return null;
}

function deleteCookie(name) {
	setCookie(name, "", -1);
}

/*
setCookie2 = function(name, value, days, path, domain, secure) {
	var expires = -1;
	if(typeof days == "number" && days >= 0) {
		var d = new Date();
		d.setTime(d.getTime()+(days*24*60*60*1000));
		expires = d.toGMTString();
	}
	value = escape(value);
	document.cookie = name + "=" + value + ";"
		+ (expires != -1 ? " expires=" + expires + ";" : "")
		+ (path ? "path=" + path : "")
		+ (domain ? "; domain=" + domain : "")
		+ (secure ? "; secure" : "");
}

getCookie2 = function(name) {
	// FIXME: Which cookie should we return?
	//        If there are cookies set for different sub domains in the current
	//        scope there could be more than one cookie with the same name.
	//        I think taking the last one in the list takes the one from the
	//        deepest subdomain, which is what we're doing here.
	var idx = document.cookie.lastIndexOf(name+'=');
	if(idx == -1) { return null; }
	var value = document.cookie.substring(idx+name.length+1);
	var end = value.indexOf(';');
	if(end == -1) { end = value.length; }
	value = value.substring(0, end);
	value = unescape(value);
	return value;
}
*/

// *. Math / Computation

function randomIntBetween(min, max) {
    /// Return a random integer between min and max inclusively.
    return min + Math.floor(Math.random() * (max - min + 1));
}
// >>> randomIntBetween(4, 6)
// 6
// >>> randomIntBetween(4, 6)
// 5

function sec2minutes(sec) {    /// 144 or "144" -> "02:24"
    var n = Number(sec);
    var r = (n % 60);
    var m = (n - r)/60;
    return ( (m < 10) ? "0" : "" ) + m + ":" + ( (r < 10) ? "0" : "" ) + r ;
}

// *. Effects

function fadeUp(element, red, green, blue) {
    if (element.fade) {
        clearTimeout(element.fade);
    }
    element.style.backgroundColor = "rgb("+red+","+green+","+blue+")";
    if (red == 255 && green == 255 && blue == 255) {
        return;
    }
    var newred = red + Math.ceil((255 - red)/10);
    var newgreen = green + Math.ceil((255 - green)/10);
    var newblue = blue + Math.ceil((255 - blue)/10);
    var repeat = function() {
        fadeUp(element, newred, newgreen, newblue)
    };
    element.fade = setTimeout(repeat,100);
}
// >>> fadeUp(target_element, 255, 255, 153);
// >>> fadeUp(target_element, 255, 255, 180);

function togglePNdiv(pn_id, text_target_id) {
    //| Toggle `pn_id' (p-box or n-box) element. Content is supposed to be stored
    //| in `text_target_id' element.
    var icon_element = document.getElementById(pn_id);
    var text_target_element = document.getElementById(text_target_id);
    if (icon_element.className == "p-box") {
        icon_element.className = "n-box" ;
      // Set text and style.display="none" to "block"
        text_target_element.style.display = "block";
    }
    else {
        icon_element.className = "p-box" ;
        text_target_element.style.display = "none";
    }
}
/// <div class="pn-container even">
///     <a id="pn2" href="javascript:void(0)" class="p-box" onclick="enphraseTogglePN(this.id, 'pntext2', '/w/api.php?action=query&list=ensentences&essearch=apple and pear&format=json&esoffset=0&eslimit=10', 'apple and pear')">
///     </a><a class="pn-title" href="/wiki/apple_and_pear"><strong>apple</strong> and pear</a><br/>
///     <div id="pntext2" class="pn-content" style="display: none;"> </div>
/// </div>
///   <~/docs/public_html/image/capture//20080523222742.png>
///   <~/docs/public_html/image/capture//20080523222753.png>

function fixIEUnfocus(target_embed_object) {    /// fix the problem of IE where flash objects get unfocused somehow by default. If target_embed_object is not specified, run the fix on all embed objects.
    var userAgent = navigator.userAgent.toLowerCase();
    var msie = /msie/.test( userAgent ) && !/opera/.test( userAgent ) ;
    if (!msie)
        return false;
    var target_embed_object = target_embed_object || false;
    if (target_embed_object) {
        target_embed_object.outerHTML = target_embed_object.outerHTML;
    }
    else {
        var objects = document.getElementsByTagName("embed");
        for (var i=0; i < objects.length; ++i) {
            objects[i].outerHTML = objects[i].outerHTML;
        }
    }
    return true;
}

//___________________________________________________________

/// Dont bother the user with error reports
// onerror = function() { return true; }
