2014-11-04

Cyrillic to Latin

Tired of unreadable Russian characters? Try this JavaScript:
var Cyrillic_Latin = {
 "А":"A", "а":"a",
 "Б":"B", "б":"b",
 "В":"V", "в":"v",
 "Г":"G", "г":"ɡ",
 "Д":"D", "д":"d",
 "Е":"Je", "е":"e",
 "Ё":"Jo", "ё":"jo",
 "Ж":"S", "ж":"s",
 "З":"Z", "з":"z",
 "И":"I", "и":"i",
 "Й":"J", "й":"j",
 "К":"K", "к":"k",
 "Л":"L", "л":"l",
 "М":"M", "м":"m",
 "Н":"N", "н":"n",
 "О":"O", "о":"o",
 "П":"P", "п":"p",
 "Р":"R", "р":"r",
 "С":"S", "с":"s",
 "Т":"T", "т":"t",
 "У":"U", "у":"u",
 "Ф":"F", "ф":"f",
 "Х":"X", "х":"x",
 "Ц":"Ts", "ц":"ts",
 "Ч":"Tj", "ч":"tj",
 "Ш":"Sh", "ш":"sh",
 "Щ":"Sh", "щ":"sh",
 "Ъ":"", "ъ":"",
 "Ы":"E", "ы":"e",
 "Ь":"ʲ", "ь":"ʲ",
 "Э":"E", "э":"e",
 "Ю":"Ju", "ю":"ju",
 "Я":"Ja", "я":"ja",
 "І":"I", "і":"i",
 "Ѳ":"F", "ѳ":"f",
 "Ѣ":"E", "ѣ":"e",
 "Ѵ":"I", "ѵ":"i",
 "Ѕ":"Z", "ѕ":"z",
 "Ѯ":"Ks", "ѯ":"ks",
 "Ѱ":"Ps", "ѱ":"ps",
 "Ѡ":"O", "ѡ":"o",
 "Ѫ":"Ju", "ѫ":"ju",
 "Ѧ":"Ja", "ѧ":"ja",
 "Ѭ":"Ju", "ѭ":"ju",
 "Ѩ":"Ja", "ѩ":"ja"
}

function Cyr2Lat(text){
 var out = ""
 for(i in text) out += Cyrillic_Latin[text[i]] || text[i]
 return out
}
Example:
Cyr2Lat("чем")
"tjem"
Cyr2Lat("форма")
"forma"

Here it is as a bookmarklet that converts the current page (thanks to this snippet):

javascript:(function(){function t(t){var n="";for(var i in t)n+=e[t[i]]||t[i];return n}function n(e){if(e.nodeType==3&&e.nodeValue.trim()!=""){e.nodeValue=t(e.nodeValue)}else{for(var i=e.childNodes.length-1;i>=0;--i){n(e.childNodes[i])}}}var e={"А":"A","а":"a","Б":"B","б":"b","В":"V","в":"v","Г":"G","г":"ɡ","Д":"D","д":"d","Е":"Je","е":"e","Ё":"Jo","ё":"jo","Ж":"S","ж":"s","З":"Z","з":"z","И":"I","и":"i","Й":"J","й":"j","К":"K","к":"k","Л":"L","л":"l","М":"M","м":"m","Н":"N","н":"n","О":"O","о":"o","П":"P","п":"p","Р":"R","р":"r","С":"S","с":"s","Т":"T","т":"t","У":"U","у":"u","Ф":"F","ф":"f","Х":"X","х":"x","Ц":"Ts","ц":"ts","Ч":"Tj","ч":"tj","Ш":"Sh","ш":"sh","Щ":"Sh","щ":"sh","Ъ":"","ъ":"","Ы":"E","ы":"e","Ь":"ʲ","ь":"ʲ","Э":"E","э":"e","Ю":"Ju","ю":"ju","Я":"Ja","я":"ja","І":"I","і":"i","Ѳ":"F","ѳ":"f","Ѣ":"E","ѣ":"e","Ѵ":"I","ѵ":"i","Ѕ":"Z","ѕ":"z","Ѯ":"Ks","ѯ":"ks","Ѱ":"Ps","ѱ":"ps","Ѡ":"O","ѡ":"o","Ѫ":"Ju","ѫ":"ju","Ѧ":"Ja","ѧ":"ja","Ѭ":"Ju","ѭ":"ju","Ѩ":"Ja","ѩ":"ja"};n(document)})()

2014-10-03

Useful comments

While the comments in my Assembly post were useful to beginners, experts who can read the code might find them annoying. Here's an example of helpful comments for experienced programmers:

function delete_month(id, index){
 load_branch(id)
 $(".row[id^="+id+"] .cell:nth-child("+(index+1)+")").each(function(i,e){
  if(i==0 && e.className.indexOf("draggable")<0) return  // only change sandbox budget.
  $(this).nextAll().each(function(i,e){
   this.style.left = (parseInt(this.style.left)-mw)+"px"
   cellBlur(this)  // update availability
  }).addClass("changed")
  $(this).prevAll().addClass("changed")  // needed in case there is no next cell.
  $(this).remove()
 })
 //updateTotals()  // too slow here
 btnSave.value = "Save*"
 setBusy(0)
}
Note that i'm describing WHY the code is there, not HOW it works (which should be obvious to any decent JavaScript coder, who also should have an editor capable of wrapping lines to their preferred width).

2014-09-10

How to freeze a Google Chrome tab

Chrome cares more about speed than user interaction. This simple bug just hung my tab while hogging one CPU core:
function isOverflowed(element){
    return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}

function resize_to_fit(el){
 var size = parseFloat(el.style.fontSize) || 55
 log("resizing from " + size)
 while(isOverflowed(el) && size > 8){
  el.style.fontSize = size - 1
 }
}
Press Shift+Escape, sort by CPU descending, and end the offending process to recover. Still better than Firefox.

This is what i meant to write, to resize text content to fit an HTML element like a div:
function isOverflowed(element){
 return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth
}

function resizeToFit(el){
 var size = parseFloat(el.style.fontSize) || 55
 while(isOverflowed(el) && size > 8){
  size -= 1
  el.style.fontSize = size + "px"
 }
}

2014-08-26

How to manually mount a USB storage device in GNU/Linux

After creating a bootable (possibly smaller than 16 GB for BIOS compatibility) USB stick, using Universal USB Installer or equivalent, you may find that some distros, like System Rescue CD do not automount your other/larger USB storage devices.

To solve that, run these commands from your terminal:
mkdir stick
fdisk -l
mount /dev/sdc1 stick
That will:

1. create a mountpoint
2. allow you to find the device that matches your storage size, e.g. /dev/sdc
3. mount the 1st partition of that device on your mountpoint

Before removing the device you may want to unmount it, lest OSs like Windows complain on insert:
umount stick
You can possibly save keystrokes by pressing the up arrow key a few times so you only have to prepend a "u" to the mount command in your command history:
umount /dev/sdc1 stick

2014-06-12

Learn how to program in Assembly code in 2 minutes using debug!

A computer is a calculator with more features and commands to use them.

To program it (tell it what to do) at the lowest human-readable level (one step above the zeroes and ones of binary), you can use Assembly code, as shown here:




A neater, yet perhaps slightly more complex version of "Hello World" in Assembly can be found here. Annotated version:
A                  ; enter assembly mode
MOV AH,9           ; set High byte of Accumulator register to display a string of text terminated by the '$' character
MOV DX,108         ; set Data register to the location of the string to display
INT 21             ; execute an I/O function set in AH register
RET                ; return
DB 'HELLO WORLD$'  ; store a string

R CX               ; set Counter register (switches from 16-bit mode to 32-bit mode on a 80386 CPU or better)
14                 ; current code position in hexadecimal to specify program length (14 is 20 in decimal)
N MYHELLO.COM      ; name your command file
W                  ; write the program to the file
Q                  ; quit debug
Sadly, Windows 2003 was the last OS that shipped with the debug command. There are other assemblers and operating systems that are free and actively supported. This tutorial site appears to focus on the NASM assembler on Linux.

Simple assembly code runs very fast (640x200 video on a 4.77MHz 8088 IBM PC Model 5150 with 640K RAM, SoundBlaster, and 20 MB HDD), but takes a lot of work to write, and each system tends to have different opcodes, so programmers in the 1970s invented and largely moved to more abstract, higher level languages like C. In the 1990s, another such step occurred, to Java, the most business-like among new '90s languages. Lately, though, since the 2000s, high-level scripting languages like Python have become more popular among people tired of typing a lot, for code that may run milliseconds slower, depending on flavor. Remember: First get it right, then make it fast.

2014-04-17

Using SharePoint 2010's REST API

Get 100 items after the first 100 items from the items of this year and month:

http://site.company.tld/_vti_bin/ListData.svc/YourList?$skip=100&$top=100&$select=Year,Month,ID&$filter=Year%20eq%20%272014%27%20and%20Month%20eq%20%27Mar%27


Here's a complete example in Python, TestSharePoint2010REST.py:

"""
SharePoint2010 REST test app by Cees Timmerman, 24jun14.
"""
# Fix Python 2.
from __future__ import print_function
try: input = raw_input
except: pass
try: import urllib.request as urllib2
except: import urllib2
try: from ntlm3 import HTTPNtlmAuthHandler
except: from ntlm import HTTPNtlmAuthHandler

import json, re, time

web_domain = "http://subdomain.company.tld/"
user = "company\\user"
password = "secret"
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, web_domain, user, password)
auth_NTLM = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(passman)
opener = urllib2.build_opener(auth_NTLM)
urllib2.install_opener(opener)

url = web_domain + "_vti_bin/ListData.svc/TimeRegistration?$filter=Modified%20gt%20datetime\'2014-04-30T00:00:00.000Z\'&$orderby=Modified%20asc&$select=Id,Year,Month,Hours,Project,Resource&$top=2050"

months = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(",")
count = 0
while 1:
  print(url)
  request = urllib2.Request(url)
  request.add_header("accept", "application/json;odata=verbose")  # The default format is application/atom+xml. Example: "accept":"application/json;odata=verbose"
  response = urllib2.urlopen(request)
  headers = response.info()
  body = response.read().decode("utf-8")
  body = re.sub(r"(?<!\\)\\'", "'", body)  # Fix MicrosoftSharePointTeamServices 14.0.0.6029's incorrect JSON output. http://stackoverflow.com/questions/24390258/json-module-bug-in-python-3-4-1/24390987#comment37724338_24390290
  print("Headers: {}".format(headers))
  print("Body start: " + body[:800])
  print("==========================\nBody end: " + body[-800:])
  #err_loc=314020; print(">>>>>>Parse Error: {}<<<<<<".format(body[err_loc-40:err_loc+40])) # Debug output using value from thrown error.
  j = json.loads(body)
  results = j['d']['results'] if 'results' in j['d'] else j['d']
  print("Len: %r" % len(results))
  for item in results:
    count += 1
    print(item['Resource'].replace("company\\", ""), item['Project'], float(item['Hours']), "{}-{:02}-01".format(item['Year'], 1+months.index(item['Month'])), item['Id'])
  print("Count: %s" % count)
  
  # End loop or process next batch of 1000.
  url = None
  try: url = j['d']['__next']
  except: pass
  if not url: break

2014-03-21

Patching bugs with SVN

I'm using Suds to work with SharePoint, but it was terribly slow due to broken caching, so i patched it:
I:\>cd temp

I:\temp>mkdir sudssvn

I:\temp>cd sudssvn

I:\temp\sudssvn>svn co http://svn.fedorahosted.org/svn/suds/trunk
A    trunk\LICENSE
A    trunk\tests
A    trunk\tests\rhq.py
A    trunk\tests\public.py
...

I:\temp\sudssvn>"C:\Program Files (x86)\Notepad++\notepad++.exe" trunk\suds\cache.py

I:\temp\sudssvn>svn di trunk
Index: trunk/suds/cache.py
===================================================================
--- trunk/suds/cache.py (revision 712)
+++ trunk/suds/cache.py (working copy)
@@ -190,7 +190,7 @@
     def put(self, id, bfr):
         try:
             fn = self.__fn(id)
-            f = self.open(fn, 'w')
+            f = self.open(fn, 'wb')
             f.write(bfr)
             f.close()
             return bfr
@@ -223,7 +223,7 @@
         try:
             fn = self.__fn(id)
             self.validate(fn)
-            return self.open(fn)
+            return self.open(fn, 'rb')
         except:
             pass

I:\temp\sudssvn>svn ci trunk
svn: E205007: Commit failed (details follow):
svn: E205007: Could not use external editor to fetch log message; consider setting the $SVN_EDITOR environment variable or using the --message (-m) or --file (-F) options
svn: E205007: None of the environment variables SVN_EDITOR, VISUAL or EDITOR are set, and no 'editor-cmd' run-time configuration option was found

I:\temp\sudssvn> ...TortoiseSVN GUI:

Commit
Commit failed (details follow):
Can't create directory '/svn/suds/db/transactions/712-1.txn': Permission denied
Alas, a lack of project time/leadership stifles progress, but at least open source and free speech allows one to fork unmaintained projects. Torvalds' Git (simple guide) was made for decentralized development like that.

A Google result (and on closer inspection, previous error) tells me that instead of the GUI, i could've used the -m switch of svn:
I:\temp\sudssvn>svn ci trunk -m "fix caching on Windows"
svn: E000013: Commit failed (details follow):
svn: E000013: Can't create directory '/svn/suds/db/transactions/712-1.txn': Permission denied
The system environment variable PATH gives me easy access to "C:\Program Files\TortoiseSVN\bin\svn.exe".

2014-03-01

Genderswap any webpage!

After trying to read the dismal The Claiming of Sleeping Beauty again, i thought it might be interesting to see whether it'd be more palatable with reversed genders. A difficult experience. Here's the code, which you can set as a bookmark location, then click the bookmark to transform the current page:

javascript:(function(){function swap(txt, a, b){return txt.split(a).join("SWPTMP").split(b).join(a).split("SWPTMP").join(b)};var subs=[[' he ',' she '],[' he\'',' she\''],['He ','She '],[' man ',' woman '],[' men',' women'],['boy','girl'],['King','Queen'],['Lord','Lady'],[' daughter',' son'],['father','mother'],['uncle','aunt'],['Prince','Princess'],[' herself',' himself'],[' her.',' him.'],[' her,',' him,'],[' her a ',' him a '],[' her an ',' him an '],[' her and ',' him and '],[' him to',' her to'],[' him up',' her up'],[' him down',' her down'],[' him again',' her again'],[' him until',' her until'],[' him w',' her w'],[' him by',' her by'],[' her ',' his '],['His ', 'Her ']];t=document.body.innerHTML;for(var i=0;i<subs.length;++i)t=swap(t, subs[i][0], subs[i][1]);document.body.innerHTML=t.replace('Princessss','Prince')})()

2014-02-28

T-SQL: Work Days per Month

Based on http://stackoverflow.com/a/252532/819417

ALTER FUNCTION dbo.fn_WorkDays(@StartDate DATETIME) RETURNS INT
AS
BEGIN
    IF @StartDate IS NULL
        RETURN NULL

    --Strip the time element from both dates (just to be safe) by converting to whole days and back to a date.
    --Usually faster than CONVERT.
    --0 is a date (01/01/1900 00:00:00.000)
    SELECT @StartDate = DATEADD(dd, DATEDIFF(dd, 0, @StartDate), 0)
   
    DECLARE @EndDate DATETIME
    SELECT @EndDate = dateadd(day, -1, dateadd(month, 1, @StartDate))

    RETURN (SELECT        --Start with total number of days including weekends
        (DATEDIFF(dd, @StartDate, @EndDate)+1)
        --Subtract 2 days for each full weekend
        -(DATEDIFF(wk, @StartDate, @EndDate)*2)
        -(CASE WHEN DATEPART(dw, @StartDate) = 1 THEN 1 ELSE 0 END)
        -(CASE WHEN DATEPART(dw, @EndDate) = 7 THEN 1 ELSE 0 END))
END