I still can not draw …

… so I had to cheat.
Person drawn with Harmony Keymod
I imported a photo as backdrop image in Harmony Keymod, then set the brush to “shaded”, the “offset-scale” to about 80 (I reeally need a better name for that), and started following the contours or shadows with long continuous strokes. To avoid interactions between the strokes I occasionally pressed the “B” key to break the relationship between the strokes. The occasional “U” key (undo! How could we only live without that in the dark ages) was of course involved too.

The image was drawn with a trackball, which is definitely not the best thing to use for drawing. A real artist with a tablet and a pen would have done it much better, especially the face, but I am quite content with the result.

Usual disclaimer stuff:

Harmony was originally written by Ricardo “mrdoob” Cabello, I only did some heavy modifications to the code, see help in the application.

You can download the whole application by clicking on the
V-Menu > File > “save as…”: “Webpage, Complete”.
It is perfectly self-contained and works locally if you open the stored HTML file in Vivaldi. It does not need any server and stores no data on the server if you use the online version.

PS: There is a chromium bug which causes a massive RAM explosion with one of the Javascript commands I used for undo and redo on some hardware – see https://bugs.chromium.org/p/chromium/issues/detail?id=242215 – which hits Vivaldi too in combination with some hardware. If you did overwrite chrome://flags/#ignore-gpu-blacklist please consider to disable the overwrite. If you didn’t overwrite it and are still hit by it, you sadly have to wait until the chromium authors have fixed the bug.

Hunting Down Audio Bugs – a view from behind the scenes

All what should become a great mess later started with a single bug report some time back:

“[SlowAudio] HTML5 Live stream has bad sound and video”

(dramatic music effect)
found by Gwendragon, who in the following events tested, logged and categorized a lot of audio stuff too.

This bug happened only on a single site, which is well frequented in one country, but otherwise rather irrelevant.
Back then we could only check if it works for us – which it did for those who tested it after spoofing the user agent – so this bug was set to “cannot reproduce” (which means: Closed) after contacting the site owner because at that time it appeared to be a problem for the site to fix.

Usually this is the end of the story, the developers can only fix what they see and what they can analyze, and if it works for the testers and the developers, it can’t be broken, right?

Sadly not.

Somehow we managed to forget one operating system (OS) during the check, which was partly my fault, because usually I use that OS while testing.
All was good until the next report came in for exactly the same site. We checked it again (usual procedure, no bug gets closed unchecked), this time with the right OS too and the result was: [insert some heavy cussing and swearing here – be creative, ’cause I was]!

Not only that – suddenly reports about dozens of other sites started popping up!


Well, no panic, but no real solution in sight either, which is quite frustrating because at that time all of the developers, who can fix this kind of bugs, were heavily burdened with really important stuff like frozen UI or crashes, which of course have a higher priority, especially because super traffic-heavy sites like YT, Vimeo etc. worked well – but of course those audio bugs were constantly nagging in the background.

Then, one day, it was finally possible to assign a developer to the problem, but the fix was everything but easy.

One of the first things was to find out where it breaks and what exactly breaks – which sounds easy enough but isn’t, especially if you have to deal with the codecs inside of the OS because prohibitive license fees from the MPEG-LA and the Fraunhofer institute forbid to bundle the full ffmpeg+aac codec in binary form to Vivaldi, no thanks to them.
(Linux users might have noticed by now, that they are not hit by this because they can steal re-purpose the original Chrome codecs hosted in the usual repositories – a luxury the owners of other OSs don’t have)

The first thing to do was to add some sophisticated debugging code to the internal builds which can provide log files for the sites that were affected by the issue.
Said and done, we testers (both the employed and we volunteers) went through all of the bugs and created tons of big log files which we dumped on the poor Developer. After some time some of us became experienced enough to identify some common problems so that we could “Duplicate” bugs (meaning link together bugs that have the identical cause) on some kind of master bugs (usually the bug where the exact problem was first seen).

One thing all of those bugs had in common was the interaction between Vivaldi and the OS codecs. First Vivaldi needed to parse out the correct information from the media stream to get the correct settings for the audio decoder, then it needed to deliver it to the OS in exactly the right way or everything would break, which it still did at that time. This meant digging deep into the OS – one of the developers even went so far to read through The Windows Bible (a set of 1k+ page books that describe the internal workings in detail) to find “the right MS way(TM)” to do that.

This resulted in the first set of patches, or, to be more precise: One single patch which fixed a lot of the problems that existed.
(Here my part of hero worshiping: Patricia did this patch “blind” – i.e. without working on the OS directly! And it worked on the first try! That is quite a huge feat!)

End of story … not …

… because this fixed only the most visible bugs, or audible, in this case.

There was a different bunch of similar bugs – which audibly sounded the same but internally weren’t – we saw that they were there but at that point we could not set see what exactly caused it, so back to writing log files, but this time with the improved “media bug logger 3000(TM)” to get even more data and again we dumped it on the developer: Set me on the watch-list for ALL of those bugs! (slightly misquoted, but that was the essence of what Patricia said)

Those bugs were related with the way the servers send the data. To get a grip on those, the log files alone were not enough, because they told us only what the browser thinks it has seen – which might not be and was not always the same as is really there.

Side note: To understand that one must know that the involved AAC files can be delivered in multiple ways: With different profiles, with the profile announcing what is inside (the easier case), some with implicit announcement (which means we need to look inside of the data), some without any headers at all and some of those even encrypted (more about those later).

We had to find a way to get the raw “files” as delivered by the server. Sadly they are not delivered as files, but in little chunks that temporarily exist in the RAM. Downloading the requested data with the dev tools was no option1, so Tarquin came up with the genius idea to grab the stuff with an extension (no, even that will not give you a working file, don’t bother to ask for a downloader. I can hear your thoughts from here, the extension doesn’t work that way 😛 ) and finally we were able to “download” small bunches of the raw data, but when we tried to look into those, a lot of what was inside looked like a garbled mess. We are no audio decoding experts, usually the codecs “just do it” and we all hope they do it right, so we definitely needed to wise up, quick.

At some point, when I got totally frustrated about that pile of horse manure some servers delivered, I remembered that I have a friend who “is into that codec stuff”. Usually SagaraS works with video codecs and does all kinds of fancy things with them to improve the output quality but hey, it was worth a shot. So I dumped some of the grabbed files we couldn’t analyze at that point on him and asked him to find out what all of that stuff means. It took him some hours or so to get a grip on the audio stuff. When he tried to explain to me what it does and how we can analyze it – in purest AV Geek language 2, it could have been Swahili and I couldn’t have told the difference – my mind boggled. In the end we set up a teamviewer session the next day so that I could look over his shoulder while he showed me what to watch out for and how to do it. It wasn’t that easy, he is armed with an Hex Editor and knows how to use it, but he gave us the necessary clues. Additionally he pointed us to tools we can use to analyze that stuff, which was of great help too.

So back to logging and grabbing and analyzing it was, while the developers started fixing the next bunch of issues, this time in a teamed effort between Patricia and Julien because of the sheer amount, resulting in what you now know as More HTML5 audio fixes – Vivaldi Browser snapshot 1.15.1147.23.

End of story? Not quite …

Some web pages strictly resisted to give us meaningful content to grab with the above mentioned extension: All we got was seriously garbled junk.

In the end we had to dive deep into the code of the website player – cough, not really “we”, the developer of the extension. I could only confirm from the code (which was a PITA of its own: >60k lines of JavaScript is a lot to look through) that they encrypt the stuff on the server and decrypt it in the browser on the fly and dynamically add the header to it, but not what it exactly does. Finally the Tarquin found the exact place where to grab the necessary information needed for the decryption module, so be prepared for the next bunch of fixes.

Oh, btw: While doing all of that he wrote a gigantic test suite for all kinds of audio issues with several hundred tests. Yes, Vivaldi still fails some of them (but so do other browsers too, interestingly not all on the same tests) but it is still a work in progress, so stay tuned and watch the snapshot blog and the team blog 🙂

1) You can find it in the network tab of the developer tools. No need to try, it won’t give you a working file for streams.
2) Not really geek language, he isn’t like that – but understanding the correct technical terms is quite hard if you don’t have much expertise in an area. Seeing him pointing at the code blocks while he was explaining the detail was much easier (for me) than only getting a description of it.

Here the usual disclaimer:
This blog post reflects my personal opinion and views. For brevity (and dramatic reasons) I left out some of the boring parts, but everything else happened more or less as described here and as good as I could memorize what has happened. All errors and mistakes are mine and nothing in here is binding Vivaldi ASA or any person I mentioned in any way. It is only my personal, private view of the events I took part in as a “Soprano”. I have asked for permission to publish this (’cause NDA, ya know) to give a small look behind the scene because I thought it might be interesting for some of you, or maybe even entertaining, and I can only “Thank You!” for granting the request.

Harmony Keymod – Update

I got a bit spare time and once again added a small bit of functionality to the script: Flood fill – or maybe better known as “bucket” tool to most people. The basic function is in, it fills enclosed areas with the selected foreground color and and it can even fill semi-transparent, depending on the opacity setting in the menu. Sadly I still miss the tolerance settings for it, the code is in, but the UI is my old and well known problem. I need a design.

The same goes for Dynadraw, which can can have 4 settings – something like elasticity, weight, velocity and if the angle is fixed or not. The code is ready for that, but I have absolutely no clue how to integrate it into the UI without making it too complex to handle.

Still to be done:
Combining stringy, curvy and web to one brush. I did not yet check how compatible they are to each other, but I believe it should work in some way.

… and finally I could need some help:
I would really appreciate if someone who knows UI graphics stuff – meaning design – can help me with some mock-ups and ideas for a design rebuild. I am running out of space with the new settings I am planning.

A native English speaker who can help me to find better fitting words for the settings would be welcome too.

You can try out the little program on my web-page quhno.internetstrahlen.de or visit the previous Harmony Keymod blog entry here.

PS: The image above was “drawn” with this application while testing things. but I am still proud of it because it is up until today the most realistic cloud I ever managed to draw – and It took less than 5 minutes 😀

( I know it sucks, but as I wrote before, I am no artist at all 😛 )

PS: There is a chromium bug which causes a massive RAM explosion with one of the Javascript commands I used for undo and redo on some hardware – see https://bugs.chromium.org/p/chromium/issues/detail?id=242215 – which hits Vivaldi too in combination with some hardware. If you did overwrite chrome://flags/#ignore-gpu-blacklist please consider to disable the overwrite. If you didn’t overwrite it and are still hit by it, you sadly have to wait until the chromium authors have fixed the bug.

Rest in Peace, KartOO

Back then …

Whenever I knew what information I needed but didn’t know which search terms would lead me to this information, you were there and helped me reliably.

I only needed to enter a search term and you showed me a cloud of pictures with lines in between, showing the interlinks with added keywords connecting these pictures. You allowed to hover over one of the pictures to get a short summary of the content of the page.

I could click on one of the terms on the connecting lines and you provided me with a new, refined cloud until there were only 10 or 20 pages left, all of which were filled with very good results.

If I clicked on one of the preview pictures, you opened the page in a new tab in the browser and flagged that I have visited the page in your search window.

Also, it was possible to set which additional information I wanted to have and you even showed me groups of possible search results…

Unfortunately, you are no longer.


I have no idea.

Maybe you were just too innovative for the “I don’t have time” searchers who just want quick information tidbits, 1,000,000 hits in 0.23s, who are not interested in the quality of the results.

But maybe you were just too unknown and therefore didn’t get any advertising revenue – although I did a lot of advertising for you…

… but what’s a single voice against the overwhelming advertising power of the data kraken?

The above eulogy was originally posted February 12, 2010 on my.opera, it is still available in the Web Archive.

The first part of this article is a translated and slightly rewritten version – I am still moaning about its too early demise. I wonder if we will ever see a public search engine like this again.

… and today

Advertising money is what makes the web spin. Cases like the above, where new ideas and technologies were starved to death, maybe without noticing or maybe even deliberately, are still a thing, maybe even more than ever, as we can see with e.g. www.foundem.co.uk which is a vertical search engine. The technology and mathematics behind vertical search engines like those is really amazing, and they can solve problems the usual horizontal search engine suspects like Google, bing etc.pp. can’t, but have you ever heard about it? No? I guessed so …

The story behind it?

Read it in this New York Times Magazine article: The case against Google.

Also available in the waybackmachine. Make sure to press the stop button as soon as you see the article content to avoid the paywall redirection on the archived page.

Almost the same happened some years earlier to Cuil – which ironically was founded by some former Google employees.

Side note: Ignore the “criticism” part in the Wikipedia article – Cuil was still new and all new search engines I have ever seen had the same problems and much worse in their early days – yes, Google back then in 1997 too. Been there, seen that.

Not only did they have their own crawler that maintained a gigantic database of web pages, which at that time was about the same size as the ones of Google, Microsoft Search (now bing) and Altavista (while it was still a thing) combined, but beside the usual search result page they had a really interesting and working approach to make results actually digestible:

It was called “cpedia” but in opposition to the well known Wikipedia, it was based purely on their data mining agents which collected information all over the web and – now the really interesting part – wrote executive summaries (backed up with a links so that you could check the sources) in a length of about 1000 words, which were astonishingly well “written”, often almost indistinguishable to the work of a human author. If possible they backed up the results with meta data, such as statistics or analytic tools, linked to related videos or images too. This was a great way to get a cursory overview over almost everything you could imagine and sometimes even showed astonishing insights that were really hard to come by otherwise.

What happened to them?

Dead after a few rounds of fund raising and sold to Google (If you can’t squash them, buy them?) and because the advertising marked at that time was already taken over by Google, so this kind of business model was no longer viable.

The odyssey of finding a certain video

This is only a short rant about the stupidity of a bunch of search engines that basically all use the same search database.

I knew that I had once seen a video of Brendan Eich (more than a year ago) that dealt with classes in JavaScript, especially ES6 and I wanted to watch it again because I had some hunch that in that video he addressed a problem I have right now.

(Sometimes my brain works in weird ways – I usually don’t remember the things I have watched a long time ago, only that I have and roughly about where I have seen them)

I don’t keep a ginormous history in Vivaldi because I have noticed that too many history results might lead to a slow down of the browser under certain circumstances and especially because all kinds of C**P are stored in the history (e.g. search engine result pages from yesterday – which will definitely not give the same results today), so I had to find that video again. I still knew that I didn’t watch it on YouTube, but somewhere else, so I started a search at the usual non-Google-ish search engines first.

A standard search “classes in javascript ES6” gave almost exclusively YT results (yes, I forgot to add “Brendan Eich” – didn’t remember who gave the talk, could have been Douglas Crockford too, so what?) so I looked up the parameters of my favorite search engines and tried to exclude YT from the results pages by adding some.
(Yes I know that it was still a very unrefined search – but hey – I’ve got results 😉 )

All links to the result pages open in a new tab.





And finally I added Google as a “control group”:

I leave the results open to interpretation by you, maybe you get different results, or maybe you can tell me what was wrong with my searches – but I found some search engines lacking.

How To add Search Engines that use POST to Vivaldi

There is a long standing problem with Vivaldi not adding every kind of search engines, e.g. the current implementation still does not add search engines like SearX

This happens because those engines use POST instead of GET in their search forms and Vivaldi does not (yet) check for that.

To mitigate the problem you can use the following bookmarklet:

  • Create a new bookmark in the panel and copy/paste the line above in the URL field of the Bookmarks panel.
  • Give it a name like e.g. “GET the POST search”.
  • Optional add a shortcut like “getit” too.

Now you can go to one of the search engine pages linked above and either click on the bookmarklet or type the shortcut in the URL bar and hit enter.

An alert-box with additional instructions will pop up. Follow the instructions.

Warning: The site will be altered by the bookmarklet. Don’t use it for a search before you have reloaded the page or else it might break!

How it works

The bookmarklet changes all FORM elements on the page to use GET instead of POST. This allows right click > “Add as Search Engine” to detect it and enter the corresponding values it needs. Don’t forget to set the “Use POST Method” checkbox for the newly created search in “Vivaldi > Settings > Search”.

Here the decoded and unminified “source code”:

javascript: (function () {
    var x, i;
    x = document.forms;
    for (i = 0; i < x.length; ++i) {
        x[i].method = "get"; 
        alert("Changed " + x.length + " forms to use the GET method.\n\nDON'T USE THE ALTERED PAGE NOW!\n\n - Right-click in the search field\n - select \"Add as search engine\"\n - Open \"Settings\" > \"Search\"\n - Click the edit button for the new search\n - Check \"Use Post Method\"\n\nEnjoy!");

CSS beautifier

I confess that am a messy coder when it comes to formatting. I edit, insert and delete until the code runs like I want to. This often leads to perfectly readable code – for machines, but not for other humans – so if I decide to publish it, I shouldn’t publish it in the same form I write it.


When I was editing my userCSS for vivaldi once again, I messed up the well formatted code the same way as usual. Of course I could have used an editor that formats on the fly, but I don’t like editors that change the formatting while I am coding, all that moving around makes me dizzy. While I was looking for a solution for that problem I found this:


CSS Beautifier – http://html.fwpolice.com/css/


Simple to use, you can even download it to run it offline in your browser, with a few clicks the code looks just fine and you can change the way it is formatted in a blink. Exactly the simple thing I wanted 😀


Helps with the usual one-line-we-save-every-bit code that is presented in some webpages too (Google, I am looking at you!)

Google custom search, the simple side of google

“Everyone” uses Google, so do I (among other search engines) but the Google page got a bit heavy over the years and I felt watched. If you look into the page source you see all kind of stuff that monitors your behavior while you are on the page, where you hover, where you click and if you click, it redirects you over one of their servers. That way they collect a complete browsing history (admit it: Apart from some pages you have bookmarked, all of your web visits somehow start with Google ;)) which is not what I want. 

Some day I found out that Google offers a special search, called Google Custom.

The result page already looks quite clean, (see the source code of this “test” search:  http://www.google.com/custom?q=test&btnG=Search ) but it could be improved a bit. During an ICQ session BS-Harou and I had the idea to clean it up a little bit more and to “pimp” it a bit by adding some extras like a search for related YT videos and images.

This is the result:


and this is the code of the userJS and CSS we created:

// ==UserScript== // @name           Nicer Google/custom search // @author         BS-Harou, QuHno // @description    prettifies www.google.com/custom?q=%s  searches, adds links to the top 8 query related images and top 3 related YouTube Videos // @include        http://www.google.com/custom* // @version        1.0.12 // ==/UserScript==  document.addEventListener('DOMContentLoaded', function() { 	var block = document.createElement('div'); 	block.id = 'userjs-block'; 	 	// QuHno: No link replacement for me. 	var theA = document.querySelectorAll('A'); 	for (var i = theA.length - 1; i >= 0; i--) { 		theA[i].onmousedown = null; 	} 	window['curwt'] = null; 	  	function getQueryVariable(variable) { 		var query = window.location.search.substring(1); 		var vars = query.split('&'); 		for (var i = 0; i < vars.length; i++) { 			var pair = vars[i].split('='); 			if (decodeURIComponent(pair[0]) == variable) { 				return pair[1]; 			} 		} 	}  	function insertAfter(newNode, referenceNode) { 		referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); 	}  	function addImage(url, realUrl, title) { 		var link = document.createElement('a'); 		link.href = realUrl; 		link.className = 'userjs-image-link'; 		link.title = title.replace(/<\/?b>/g,''); // QuHno: remove ugly <B> tags  		var tmp = new Image(url); 		tmp.src = url; 		tmp.className = 'userjs-image'; 		tmp.width = 100; 		tmp.height = 100;  		link.appendChild(tmp); 		block.appendChild(link); 	}  	function addVideo(url, realUrl, title, desc) { 		var link = document.createElement('a'); 		link.href = realUrl; 		link.className = 'userjs-video-link'; 		link.title = title;  		var tmp = new Image(url); 		tmp.src = url; 		tmp.className = 'userjs-video'; 		tmp.width = 120; 		tmp.height = 90;  		var text = document.createElement('span'); 		text.className = 'userjs-video-text'; 		text.innerHTML = title;  		var descEle = document.createElement('span'); 		descEle.className = 'userjs-video-desc'; 		descEle.innerHTML = desc;  		link.appendChild(tmp); 		link.appendChild(text); 		link.appendChild(descEle);  		block.appendChild(link); 	} 	 	// QuHno: add search for more videos or images. Should this be inlined? 	function searchMore(what) {         var theHref, theInnerHTML, theQuery = getQueryVariable('q');                  switch(what) {             case 'images':                 theHref = 'https://encrypted.google.com/search?q=' + theQuery + '&ie=UTF-8&tbm=isch&sa=N';                 theInnerHTML = 'Google Images: <span class="link">' + decodeURIComponent(theQuery).replace(/[+]/g,' ') + '</span>';                 break;             case 'videos':                 theHref = 'https://www.youtube.com/results?q=' + theQuery;                 theInnerHTML = 'YouTube: <span class="link">' + decodeURIComponent(theQuery).replace(/[+]/g,' ') + '</span>';                 break;             default: return;         }                  var link = document.createElement('a');         link.href = theHref;         link.innerHTML = theInnerHTML;         link.className = 'userjs-more-link';                  block.appendChild(link);     }      window.imageHandler = function(data) {         var tmp;         if (data && data.hasOwnProperty('responseData') && data.responseData && typeof data.responseData == 'object' && data.responseData.hasOwnProperty('results')) {             for (var i=0; i < 8 && i < data.responseData.results.length; i++) {                 tmp = data.responseData.results[i];                 addImage(decodeURIComponent(tmp.tbUrl), tmp.unescapedUrl, tmp.title);             }             searchMore('images'); // QuHno: add direct link to Google image search                          //document.querySelector('#res').appendChild(block);             //document.querySelector('#res').insertBefore(block, document.querySelector('#navbar'));             insertAfter(block, document.querySelector('#res div')); // QuHno: changed because of "Did you mean"         }          var videoLoader = document.createElement('script');         videoLoader.src = 'https://gdata.youtube.com/feeds/api/videos?q=' + getQueryVariable('q') + '&v=2&alt=jsonc&callback=videoHandler&max-results=3';          document.body.appendChild(videoLoader);     };      window.videoHandler = function(data) {         var tmp;         if (data && data.hasOwnProperty('data') && data.data.hasOwnProperty('items')) {             for (var i=0; i < 3 && i < data.data.items.length; i++) {                 tmp = data.data.items[i];                 addVideo(tmp.thumbnail.sqDefault, tmp.player.default, tmp.title, tmp.description);             }             searchMore('videos'); // QuHno: add direct link to YouTube video search                          //document.querySelector('#res').appendChild(block);             //document.querySelector('#res').insertBefore(block, document.querySelector('#res div'));         }     };      var imageLoader = document.createElement('script');     imageLoader.src = 'https://ajax.googleapis.com/ajax/services/search/images?q=' + getQueryVariable('q') + '&v=1.0&callback=imageHandler&rsz=8';     document.body.appendChild(imageLoader);          // QuHno: embedded the CSS in the script. No need to set up a site specific userCSS.     var css = '\r\n\ html { \r\n\     color: #000;\r\n\     background: #f2f2f2;\r\n\ }\r\n\ #navbar, #res > p { \r\n\     clear: both;\r\n\ } \r\n\ .tc img { \r\n\     height: 60px;\r\n\     width: 130px;\r\n\ }\r\n\ #res li:first-of-type .g { \r\n\     margin: 0;\r\n\ }\r\n\ body { \r\n\     padding: 0 40px;\r\n\     max-width: 80rem;\r\n\     margin: 0 auto;\r\n\     box-sizing: border-box;\r\n\ }\r\n\ table.ra, body > table + table { \r\n\     display: none;\r\n\ }\r\n\ #res > p:first-child + div, #res > div:first-child { \r\n\     padding: 20px 0;\r\n\     float: left;\r\n\ }\r\n\ /*#res li*/.g { \r\n\     break-inside: avoid;\r\n\     margin: 20px 0;\r\n\ }\r\n\ input[name=q] { \r\n\     padding: 4px 8px;\r\n\     font-size: 1rem;\r\n\     vertical-align: middle;\r\n\     border: 1px solid #bbb;\r\n\     width: 500px;\r\n\ }\r\n\ input[name=q]:hover, input[name=q]:focus { \r\n\     border: 1px solid #777;\r\n\ }\r\n\ input[name=btnG] { \r\n\     border: 1px solid rgba(0, 0, 0, 0.400);\r\n\     box-shadow: inset -10px -15px 30px rgba(0, 0, 0, 0.102), inset 1px 1px 1px rgba(255, 255, 255, 0.506);\r\n\     transition: background-color 0.1s;\r\n\     background-color: #1133F7;\r\n\     color: #FFF;\r\n\     font-size: 1rem;\r\n\     padding: 4px 12px 4px 12px;\r\n\     box-sizing: border-box;\r\n\     line-height: 1.24;\r\n\     vertical-align: middle;\r\n\     cursor: pointer;\r\n\ }\r\n\ input[name=btnG]:hover { \r\n\     color: #FFF;\r\n\     background-color: #14F;\r\n\ }\r\n\ #userjs-block { \r\n\     float: right;\r\n\     box-sizing: border-box;\r\n\     padding: 15px 30px;\r\n\     width: 45%;\r\n\ }\r\n\ .userjs-image { \r\n\     -o-object-fit: cover;\r\n\     -o-object-position: top left;\r\n\     color: #00C;\r\n\     background: #111;\r\n\     overflow: hidden;\r\n\     margin: 3px;\r\n\ }\r\n\ .userjs-image-link { \r\n\     margin: 2px;\r\n\     border-bottom: 1px solid #666;\r\n\     border-left: 1px solid #999;\r\n\     border-right: 1px solid #666;\r\n\     border-top: 1px solid #999;\r\n\     overflow: hidden;\r\n\     display: inline-block;\r\n\ }\r\n\ .userjs-video { \r\n\     -o-object-fit: cover;\r\n\     float: left;\r\n\     -o-object-position: top left;\r\n\     border-bottom: 1px solid #666;\r\n\     border-left: 1px solid #999;\r\n\     border-right: 1px solid #666;\r\n\     border-top: 1px solid #999;\r\n\     padding: 2px;\r\n\     color: #00C;\r\n\     background: #FFF;\r\n\     overflow: hidden;\r\n\     margin: 3px 3px 3px 0;\r\n\     vertical-align: top;\r\n\ }\r\n\ .userjs-video-link { \r\n\     margin: 2px;\r\n\     margin-top: 20px;\r\n\     overflow: hidden;\r\n\     display: block;\r\n\     text-decoration: none;\r\n\ }\r\n\ .userjs-video-text { \r\n\     padding: 10px 0 0 5px;\r\n\     display: inline-block;\r\n\     color: #000;\r\n\     font-size: 14px;\r\n\     font-weight: bold;\r\n\     width: 300px;\r\n\     white-space: nowrap;\r\n\     overflow: hidden;\r\n\     text-overflow: ellipsis;\r\n\ }\r\n\ .userjs-video-link:hover, .userjs-video-text:hover, .userjs-more-link:hover { \r\n\     text-decoration: underline;\r\n\ } \r\n\ .userjs-video-desc { \r\n\     display: block;\r\n\     padding: 10px 0 0 5px;\r\n\     font-weight: normal;\r\n\     font-size: 12px;\r\n\     line-height: 1;\r\n\     text-decoration: none;\r\n\     color: #000;\r\n\     float: left;\r\n\     width: 300px;\r\n\ }\r\n\ .userjs-more-link { \r\n\     overflow: hidden;\r\n\     color: #000;\r\n\     font-size: 14px;\r\n\     font-weight: bold;\r\n\     display: block;\r\n\     text-decoration: none;\r\n\     text-overflow: ellipsis;\r\n\ }\r\n\ @media all and (max-width: 69rem) {\r\n\     #userjs-block { \r\n\     display: none;\r\n\     }\r\n\ }\r\n\ @media all and (max-width: 55rem) {\r\n\     .tc img { \r\n\         display: none;\r\n\     }\r\n\     input[name=q] { \r\n\         width: 300px;\r\n\     }\r\n\ }\r\n\ @media all and (max-width: 80rem) { \r\n\     body { \r\n\         padding: 20px;\r\n\     }\r\n\ }\r\n',         head = document.getElementsByTagName('head')[0],         style = document.createElement('style');          if (style.styleSheet){      style.styleSheet.cssText = css;     } else {      style.appendChild(document.createTextNode(css));     }     head.appendChild(style); }); 

Just install it in your userJS capable browser (Opera

(PS: the WOT rings don’t belong to this script, that’s another one – not the official extension, but something lightweight.)

IE, generated select boxes, change event and WTF?!

While I was writing a bigger JS project, I needed a generated select box that was filled with its options from somewhere. I tested it in Opera 11 and all was good. Then I opened Firefox and it worked, as it did in Chromium.

Did you notice something?

Yeah – IE is still missing. So fired up IE – 11 even – and, almost needless to say: It did not work 🙁

Normally the select box should fire a change event as soon as an option finally gets selected. While this works as intended in this example taken from MSDN:

<!DOCTYPE html> <html>  <head>   <title>onchange Event Sample</title> </head>  <body>   <h1>onchange Event Sample</h1>   <form>     <p>Select a different option in the drop-down listbox to trigger the <strong>onchange</strong> event.</p> 		<select name="selTest" onchange="alert('Index: ' + this.selectedIndex + '\nValue: ' + this.options[this.selectedIndex].value)">       <option value="Books">Books</option>       <option value="Clothing">Clothing</option>       <option value="Housewares">Housewares</option>     </select>   </form> </body>  </html> 

… it definitely does not work when the whole thing is set up a bit more complex like this (short excerpt, imagine the missing rest):

===================================================== menu.js =====================================================   function Menu() {         this.init(); }  Menu.prototype =  {     menucontainer: null,          foregroundColor: null,     backgroundColor: null,          selector: null,     save: null,     clear: null,     about: null,          init: function()     {         var option, space, separator, color_width = 15, color_height = 15;          this.menucontainer = document.createElement("div");         this.menucontainer.className = 'gui';         this.menucontainer.id = 'gui';         this.menucontainer.id = "menucontainer_menu";         this.menucontainer.style.position = 'absolute';         this.menucontainer.style.top = '0px';                  this.selector = document.createElement("select");          for (i = 0; i < BRUSHES.length; i++)         {             option = document.createElement("option");             option.id = i;             option.value = BRUSHES[i];             option.text = BRUSHES[i].toUpperCase();             this.selector.appendChild(option);         }         this.menucontainer.appendChild(this.selector);     } } ========================================== main.js  ========================================== var brush, BRUSHES = ["foo", "bar",  "", "baz", "bam"];  menu = new Menu(); menu.selector.onchange = onMenuSelectorChange;  /* ...  */ if (!brush)	{ 	brush = new window[BRUSHES[0]](context); }  /* ... */ function onMenuSelectorChange() {     if (BRUSHES[menu.selector.selectedIndex] == "")         return;      brush.destroy();     brush = new window[BRUSHES[menu.selector.selectedIndex]](context);      window.location.hash = BRUSHES[menu.selector.selectedIndex];     menu.selector.blur(); } 

So, what happens with IE here?

If the index was once set programatically, IE simply “forgets” to fire the change event, you can’t even select an option with the mouse. Opening the select box, moving to the option with the mouse and then pressing the [Enter] key surprisingly worked, so the select box was created and the event listener was attached to the select box. It seems that the select box only has a slight focus problem if you want to select something by using the mouse only. In the end it came down to a missing focus problem on the select element.


I added some extra code for IE – yes I used browser sniffing because I don’t know how to detect b0rked behavior of a browser.

menu = new Menu(); if(navigator.userAgent.indexOf(' MSIE ') > -1 || navigator.userAgent.indexOf(' Trident/') > -1){ menu.selector.onclick = (function() {     this.blur();     this.focus();   });  } menu.selector.onchange = onMenuSelectorChange; 

Not perfect, but it works – which is good enough for me now. If anyone has a better solution: I am willing to learn, please post it in the comments. Thank You!

PS: No, I don’t know what the MS devs were smoking when they implemented that behavior …

Lightweight Javascript SVG Editor

Always when I needed an SVG file for something I fired up Inkscape, even if I only needed a relatively simple image. While Inkscape is quite powerful when it comes to editing importing and exporting vector graphics, it is a memory and CPU hog too, so I was looking for a lightweight variant for simpler tasks.

Check this out:


Fully written in HTML, CSS and Javascript and includes all the necessary tools I need for “normal” SVGs. It even works local.


You can download the latest version it in various forms, as zip file for local use, as Chromium extension and even as Opera 11 Widget: