http://www.perlmonks.org?node_id=530323

For props

use warnings; use strict; sub menu { print <<'EOM'; What would you like to do? 1) FLASHCARD MODE 2) TEST MODE 3) OPTIONS 4) Exit EOM } sub really { print "Are you sure? "; my $input = <STDIN>; return $input =~ /^y/i; } sub prompt { print "> "; } sub main { print "Welcome to Multiplication Challenge\n\n"; menu(); prompt(); while (my $input = <STDIN>) { print "$1\n" if $input =~ /^([123])/; last if $input =~ /^4/ && really(); menu(); prompt(); } print "done.\n"; } main();

FreeNodeLet hack for QUOTE button in comment nodes.

Notes:
<script type="text/javascript"> (function(){ /* TODO: 1. In IE7, a strange document selection remains (harmless) after + selection-less document quote. 2. Keybindings ? 3. Configuration ? 4. Nicer looking error dialog. */ // browser identification variables // (some unused for now, but will inevitably be used in future bug +-fixes) // var isOpera = !!window.opera; / +/ is the browser Opera ? var isSafari = (typeof(window.devicePixelRatio) != 'undefined'); / +/ is the browser Safari ? var isIE = /*@cc_on!@*/false; / +/ is the browser Internet Explorer ? var isIE7 = false /*@cc_on || @_jscript_version >= 5.7 @*/; / +/ is the browser IE version 7 (or later) ? // filter a list (for older javascript implmentations) var filter = function (l,f) { var a = `[`]; for (var i = 0; i<l.length; i++) if (f(l`[i`])) a.push(l`[i`]); return a; }; // check a node has the given className var hasClass = function(node, cname) { var cnre = new RegExp('\\b'+cname+'\\b'); return cnre.test(""+node.className) ? 1 : 0; }; // does this node have a parentNode (somewhere up the chain) with +the given class var hasParentWithClass = function (node, cname) { var pnode = node.parentNode; if (!pnode || (typeof(pnode.className)!='string')) return false; return hasClass(pnode, cname) || hasParentWithClass(pnode, cname +); }; // cross-platform event handler attacher var addEvent = !isIE ? function (obj, type, fn) { obj.addEventListener(type, fn, fals +e); } : function (obj, type, fn) { obj`['e'+type+fn`] = fn; obj`[type+fn`] = function(){ obj`['e'+type+fn`](window.event +); }; obj.attachEvent('on'+type,obj`[type+fn`]); }; // translate entity-encoded html back to original form var unescapeHTML = function (html) { return html.replace('&lt;', '<') .replace('&gt;', '>') .replace('&quot;', '"') .replace('&amp;', '&'); }; // save/restore scroll offsets for specified elements... this help +s in IE, but is overridden by the 'focus' on the textarea if that com +es into play. var saveScrolls, restoreScrolls; (function(){ var _elems; saveScrolls = function(list) { _elems = `[`]; for (var i=0; i<l +ist.length; i++) _elems.push(`[list`[i`],+list`[i`].scrollTop`]); }; restoreScrolls = function() { for (var i=0; i<_elems.length; i++ +) _elems`[i`]`[0`].scrollTop = _elems`[i`]`[1`]; }; })(); // get top-level user selection object var getSel = window.getSelection ? function () { return window.getSelection(); } : function () { if (!document.selection) return null; return document.selection.createRange(); }; // convert selection object to (Text/W3C)Range object var sel2Range = isIE ? function (sel) { return sel; } : function (sel) { if (sel.getRangeAt) return sel.getRangeAt(0); var r = document.createRange(); r.setStart(sel.anchorNode,sel.anchorOffset); r.setEnd(sel.focusNode,sel.focusOffset); return r; }; // get HTML of document selection var getDocSelHTML = isIE ? function (s) { return s.htmlText } : function (s) { var t = document.createElement('DIV'); t.appendChild(sel2Range(s).cloneContents()); return t.innerHTML; }; // get inner HTML of selected fragment var getSelHTML = function () { var s = getSel(); // nothing selected ? if (((''+s.htmlText) == '') || !s || s == '') { // get the whole parent post var previews = filter(document.getElementsByTagName('DIV'), fu +nction(n){ return n.className == 'preview'; }); if (!previews || !previews.length) return ''; return previews`[0`].innerHTML.replace(/\s*<hr\s*\/?>\s*<p>\s* +<i>In reply to<\/i>`[\s\S`]+/i,''); } else { // otherwise, get only the (document) selection var inCode = selectionCompletelyInsideCodeBlock(s); return (inCode ? '<c>\n' : '') + getDocSelHTML(s) + (inCode ? '\n</'+'c>' : ''); } }; // test if the selection is completely inside a <code> block. var selectionCompletelyInsideCodeBlock = isIE ? function (sel) { var pnode = sel.parentElement(); return ((pnode != null) && (hasClass(pnode, 'codeblock') || hasParentWithClass(pnode, 'codeblock'))) ? 1 : 0; } : function (sel) { return ((sel.anchorNode != null) && (sel.focusNode != null) && hasParentWithClass(sel.anchorNode, 'codeblock') && hasParentWithClass(sel.focusNode, 'codeblock')) ? 1 +: 0; }; // get the caret (cursor) position in the supplied textarea/text f +ield (for Internet Explorer) // XXX: should we minus selection length from this value ? var getRealIECaretIdx = function (fld){ // formula to decode microsoft selection bookmark string var calcBmrk = function(bk) { return (bk.charCodeAt(0)-1) + (bk.charCodeAt(3)-1) * 65536 + (bk.charCodeAt(2)-1); }; // count number of newlines in str var countNL = function(str) { var m = (/\r\n/g).exec(str); if (!m || !m.length) return 0; return m.length-1; }; // get caret index from the text selection by way of the nasty M +S-selection bookmark string var p = -1; fld.focus(); if (fld && fld.createTextRange) { var r = document.selection.createRange().duplicate(); r.setEndPoint('StartToEnd',r); var s = document.body.createTextRange(); s.moveToElementText(fld); p = calcBmrk(r.getBookmark())-calcBmrk(s.getBookmark()); var rLen = 0; do { var BrLen = rLen; rLen = countNL(fld.value.substring(0,p+rLen+1)); } while(BrLen!=rLen); p += rLen; } return p; }; // IE-specific code to get/set simulated textarea caret index var mkUpdateCaret,getIECaretIdx; (function(){ // variable to monitor caret index in comment textarea var cIdx = 0; // (build) generic textarea event handler mkUpdateCaret = function (fld) { var _fld = fld; return function() { // close over _fld and cIdx cIdx = getRealIECaretIdx(_fld); }; }; // simple getter for simulated caret index getIECaretIdx = function () { return cIdx; }; })(); // get the caret (cursor) position in the supplied text/textarea f +ield var getCaretIdx = isIE ? getIECaretIdx : function (fld) { return (fld.selectionStart || fld.selectionStart == '0') ? fld.selectionStart : 0; }; // set the caret (cursor) position in the supplied textarea (or te +xt) field var setCaretIdx = isIE ? function(fld, idx) { fld.focus(); var sel = document.selection.createRange(); sel.moveStart ('character', -fld.value.length); sel.moveStart ('character', idx); sel.moveEnd ('character', 0); sel.select(); } : function(fld, idx) { if (!(fld.selectionStart || fld.selectionStart == '0')) retu +rn; // why? fld.selectionStart = idx; fld.selectionEnd = idx; fld.focus(); }; // insert text at cursor (in textarea) var insertAtCursor = function(fld, text) { var idx = getCaretIdx(fld); var fln = fld.value.length - idx; var val = fld.value; fld.value = val.substr(0, idx) + text + val.substr(idx, fln); setCaretIdx(fld, idx + text.length); }; // turn supplied HTML into text suitable for pasting into Perlmonk +s comment textarea var quoteHTML = function (html) { var sig = 0; return html.replace( /* build translation regexp */ (new RegExp(`[ '<pre\\s+class=`[\'"`]?code`[\'"`]?>`[\\s\\S`]*?<tt\\sclass= +`[\'"`]?codetext`[\'"`]?>(`[\\s\\S`]+?)<\\/tt>`[\\s\\S`]*?<\/pre>', / +* codeblock */ '<tt\\sclass=`[\'"`]?inlinecode`[\'"`]?>(`[\\s\\S`]*?)<\\/tt +>', / +* inlinecode */ '`[^<>`]+', + / +* plain text */ '<a\\s+href=`[\'"`]?(`[^\'">`]+)`[\'"`]?>(.+?)<\\/a>', + / +* anchor tag */ '<'+'!--`[\\s\\S`]*?-->', + / +* comment tag */ '<`[^<>`]*>' + / +* other tag */ `].join('|'), "ig")), /* pass matches to this function */ function (m, c, c2, href, t) { if (m.match(/^<div\sclass=`["'`]?pmsig`["'`]?>/i)) { sig++; return ""; } if (sig) { if (m.match(/<div\b/i)) sig++; else if (m.match(/<\/div\b/i)) sig--; return ""; } if (m.match(/<{1}!--/)) return ""; if (c2 != null && c2 != "") c = c2; if (c != null && c!= "") return (function(cc){ return cc.match(/<\/c>/i) ? '<code>'+cc+'<'+'/code>' : '<c>'+cc+'<'+'/c>'; })(c.replace(/\n<font color=`['"`]?red`['"`]?>\+<\/font>/i +g, '') .replace(/&gt;/g, '>') .replace(/&lt;/g, '<') .replace(/&amp;/g, '&')); if (href != "" && href != null && t != "" && t != null) retu +rn monkLink(unescapeHTML(href), unescapeHTML(t)) || m; return m; }); }; // reverse-engineer PM markup from the supplied link (and optional + title) var monkLink = function (url, text) { var A,L; if (A = /^(?:http:\/\/(?:www\.)?perlmonks\.(?:org|com|net)\/|\/) +?(?:index\.pl)?\?(node(_id)?=(`[^;&`]+)|.+)$/.exec(url)) { L = (A`[3`] == null) ? `['href://','?'+A`[1`]`] : (/^\d+$/.tes +t(A`[3`])) ? `['id://',A`[3`]`] : `['',unescape(A`[3`])`]; // normal + PM-Link } else if (A = /^http:\/\/perlmonks\.thepen\.com\/(.+)\.html$/.e +xec(url)) { L = (/^\d+$/.test(A`[1`])) ? `['id://',A`[1`]`] : `['',unescap +e(A`[1`])`]; // the pe +n } else if (A = /^http:\/\/search\.cpan\.org\/search\?mode=module +\&query=(.+)$/.exec(url)) { L = `['cpan://',unescape(A`[1`])`]; + // search + cpan link } else if (A = /^http:\/\/search\.cpan\.org\/perldoc\?(.+)$/.exe +c(url)) { L = `['module://',unescape(A`[1`])`]; + // cpan d +ocumentation link } else if (A = /^http:\/\/search\.cpan\.org\/dist\/(`[^\/`]+)\/? +$/.exec(url)) { L = `['dist://',unescape(A`[1`])`]; + // cpan d +istribution link } else if (A = /^http:\/\/cpan\.uwinnipeg\.ca\/search\?query=(. ++)&mode=module$/.exec(url)) { L = `['kobes://',unescape(A`[1`])`]; + // kobes +cpan link } else if (A = /^http:\/\/perldoc\.perl\.org\/(?:functions\/)?(. ++)\.html$/.exec(url)) { L = `['doc://',unescape(A`[1`]).replace(/\//g, '::')`]; + // perldo +c link } else if (/^https?:\/\//.test(url)) { L = `['',url`]; + // other +link } return (!L) ? null : "`["+L`[0`]+L`[1`]+((text == L`[1`]) ? '' : + '|'+text)+"`]"; }; // (build) quote button click event handler var qfunc = function(fs) { var _fs = fs; return function() { // close over _fs try { saveScrolls(document.body, _fs); insertAtCursor(_fs, ('\n<blockquote>\n' + quoteHTML(getSelHT +ML()) + '\n</blockquote>\n').replace(/\n+/g,'\n').replace(/<p>\s*<\/p +>/g,'')); restoreScrolls(); } catch (e) { alert('quote error: '+e); // FIXME } return false; }; }; // IE 6/7 doesn't have per-element selection, so you can't remembe +r the textarea cursor location when selecting text in the // page to be quoted into the textarea. This workaround records th +e caret index (in the textarea) after every useful event. var monitorCaret = function (fld) { var up = mkUpdateCaret(fld); var events = `['keydown', 'keyup', 'paste', 'change', 'mouseup', + 'mousedown', 'dragstart', 'dragend'`]; for (var x=0; x<events.length; x++) addEvent(fld, events`[x`], u +p); }; // on document load, create the 'quote' button and attach its han +dler function. addEvent(window,'load',function(e){ // only do the following for the 'Comment on' page if (document.body.id != 'id-3333') return; // if we've not already been run... if (document.getElementById('fnh_quote')) return; // find the comment textarea var nodes = document.getElementsByName('note_doctext'); if (!nodes || !nodes.length) return; var tArea = nodes`[0`]; // add the 'quote' button to the UI var q = document.createElement('BUTTON'); q.appendChild(document.createTextNode('quote')); tArea.parentNode.appendChild(q); q.onclick = qfunc(tArea); q.id = 'fnh_quote'; // work around IE selection deficiences if (isIE) monitorCaret(tArea); }); })(); </script>


IDEA: DownEmAll-like perl library

- properties
  - select-based
  - make use of HTTP::Range
  - use LWP::Parallel::UA so that encodings, compression work ootb.
  - optional pre-alloc, otherwise join & move

- features
  - throw exception if range not supported, unless conf'd otherwise
  - per-file pause/resume/cancel & in-active timeout
  - split/merge downloaders to add/remove "threads"
  - auto detect site maxconn, reassign range segments to active downloaders
  - smart per-file/site "maximise speed" setting
  - "thread"/file/site/total -> speed, bytes, progress
  - per "thread"/file/site/total throttling
  - per-file md5 check
  - configurable: buffer, max "threads", speed-sample, select-timeout, etc
  - daemon mode (per user?), get-opt support
  - renaming masks / overwriting, dta-like renaming options
  - progress event API
  - batch url's (ranges, functions), all (file-type / link-file-type) in URL
  - understand "main" video/picture in linked HTML ?