Reboot?

January 7th, 2010

I’m thinking about rebooting this blog: Create a new .com site with my jQuery stuff and posting about technical/programming stuff there, while relaunching this site into a german blog, posting about non-technical stuff, music, politics…

If you read this, please let me know what you think: leave a comment, or send me a mail at joern.zaefferer@gmail.com

Wallraff Interview, Sozialdebatte, Religion

December 21st, 2009

Hier sind drei Artikel auf zeit.de, ich die allesamt sehe empfehlen kann.

Zum einen ein Streitgespräch mit Günter Wallraff, zu dessen Buch Aus der schönen neuen Welt (ebenfalls sehr empfehlenswert) und den zugehörigen Film, Schwarz auf Weiss. Auch die am Ende erwähnte Titanic-Satire dazu lesen!

Dann wäre da ein Artikel zur Sozialdebatte, auch wenn in der URL Armutsdebatte steht. Es sollte schwer fallen, dem uneingeschränkt zuzustimmen, aber als Denkanstoß sehr gut.

Wieder was ganz anderes: Der Bericht von Jemand, der wegen das Papstes aus der Kirche austreten will, und dies zunächst mit Pfarrern aus ganz Deutschland bei der Beichte bespricht.

Technical issues

December 20th, 2009

The auto-update to 2.9 didn’t went that well, seems like Wordpress is choking on my wp-content folder. At least I managed to restore it to the default theme. That’ll have to do until I manage to reproduce and fix the error locally.

Regex search-and-replace to add script elements to a lot of html files

December 16th, 2009

I needed to add a script tag in every file that included jquery.ui.core.js, directly after that that existing element, with the same indentation. This is what worked quite nicely for me:

Search for: (\s+)<script type=”text/javascript” src=”(.+)/ui/jquery.ui.core.js”></script>

Replace with: $1<script type=”text/javascript” src=”$2/ui/jquery.ui.core.js”></script>$1<script type=”text/javascript” src=”$2/ui/jquery.ui.widget.js”></script>

It groups the whitespace in front of the script element as well as the “../../”-part, and then just duplicates it, inserting the matched groups.

Release: Validation Plugin 1.6

November 30th, 2009

Update update: Files are now availble via Microsoft’s AJAX CDN.

An update for the jQuery validation plugin is available. There are five new localizations (now at 26 in total), including Arabic and Farsi (both right-to-left). Validation of selects is now more interactive, click events are properly handled to provide quicker feedback (also a bug related to selects in IE8 is fixed). The equalTo-method is now aware of changes to the target field, so a change to a password field will be reflected in a confirm-password field. And more…

Download this release.

The only not fully backwards-compatible change (if you spot anything else, please report it as a bug): The dateDE and numberDE methods were replaced with a localization/methods_de.js file, with localized versions of both the date and number methods. There are also localized methods for the NL and PT locales. The goal is to gather more localized versions of these, similar to the localized messages.

The full changelog:

As usual, support is available via:

  • Please post questions to the jQuery discussion list, putting “(validate)” into the subject of your post, making it easier to spot it and respond quickly. Keep your question short and succinct and provide code when possible; a testpage makes it much more likely that you get an useful answer in no time.
  • Please post bug reports and other contributions (enhancements, features, eg. new validation methods) to the plugins.jQuery.com bug tracker (requires registration).

Enjoy!

Undo/redo command stack in Java

November 18th, 2009

Another fragment that turned out to be useless for the project, but may come handy in the future. A port to JavaScript should be easy enough, too:

import java.util.ArrayList;
import java.util.List;

public class CommandStack {

	/**
	 * Implement for each individual change that can occur in an editor.
	 *


	 * Both {@link #execute()} and {@link #undo()} should fire property change
	 * events for notifying the editor of an update.
	 */
	public static abstract class Command {

		private String name;

		public Command(String name) {
			this.name = name;
		}

		@Override
		public String toString() {
			return name;
		}

		/**
		 * Implements the actual change.
		 *


		 * Any state-saving for {@link #undo()}
		 * has to occur elsewhere, eg. in the constructor.
		 */
		public abstract void execute();

		/**
		 * Reverts any changes applied by {@link #execute()}.
		 */
		public abstract void undo();

	}

	private final List commands = new ArrayList();
	private int currentLocation = -1;
	private int saveLocation = currentLocation;

	public void add(Command command) {
		clearInFrontOfCurrent();
		command.execute();
		commands.add(command);
		currentLocation++;
	}

	public void undo() {
		commands.get(currentLocation).undo();
		currentLocation--;
	}

	public boolean undoEnabled() {
		return currentLocation >= 0;
	}

	public void redo() {
		currentLocation++;
		commands.get(currentLocation).execute();
	}

	public boolean redoEnabled() {
		return currentLocation < commands.size() - 1;
	}

	public boolean dirty() {
		return currentLocation != saveLocation;
	}

	private void clearInFrontOfCurrent() {
		while (currentLocation < commands.size() - 1) {
			commands.remove(currentLocation + 1);
		}
	}

	public void markSaveLocation() {
		saveLocation = currentLocation;
	}

	@Override
	public String toString() {
		return commands.toString();
	}

}

And to illustrate the usage, the JUnit test:

import junit.framework.TestCase;

public class CommandStackTest extends TestCase {

	CommandStack stack = new CommandStack();

	private String name;
	private int age;

	class EditName extends Command {

		private String oldName = name;
		private String newName;

		public EditName(String newName) {
			super("Update name to " + newName);
			this.newName = newName;
		}

		public void execute() {
			name = newName;
		}

		public void undo() {
			name = oldName;
		}
	};

	class EditAge extends Command {

		private int oldAge = age;
		private int newAge;

		public EditAge(int newAge) {
			super("Update age to " + newAge);
			this.newAge = newAge;
		}

		public void execute() {
			age = newAge;
		}

		public void undo() {
			age = oldAge;
		}
	};

	public void test_basics() {
		assertNull(name);
		assertEquals(0, age);
		assertFalse(stack.dirty());
		assertFalse(stack.undoEnabled());
		assertFalse(stack.redoEnabled());

		stack.add(new EditName("Peter"));

		assertTrue(stack.dirty());
		assertEquals("Peter", name);
		assertTrue(stack.undoEnabled());
		assertFalse(stack.redoEnabled());

		stack.undo();

		assertNull(name);
		assertFalse(stack.undoEnabled());
		assertTrue(stack.redoEnabled());
		assertFalse(stack.dirty());

		stack.redo();

		assertTrue(stack.dirty());
		assertEquals("Peter", name);
		assertTrue(stack.undoEnabled());
		assertFalse(stack.redoEnabled());

		stack.markSaveLocation();

		assertFalse(stack.dirty());
		assertEquals("Peter", name);
		assertTrue(stack.undoEnabled());
		assertFalse(stack.redoEnabled());

		stack.add(new EditAge(10));

		assertTrue(stack.dirty());
		assertEquals(10, age);
		assertTrue(stack.undoEnabled());
		assertFalse(stack.redoEnabled());

		stack.undo();

		assertFalse(stack.dirty());
		assertEquals(0, age);
		assertTrue(stack.undoEnabled());
		assertTrue(stack.redoEnabled());

		stack.add(new EditName("Pan"));

		assertTrue(stack.dirty());
		assertEquals("Pan", name);
		assertEquals(0, age);
		assertTrue(stack.undoEnabled());
		assertFalse(stack.redoEnabled());

		stack.undo();
		stack.undo();

		assertTrue(stack.dirty());
		assertNull(name);
		assertEquals(0, age);
		assertFalse(stack.undoEnabled());
		assertTrue(stack.redoEnabled());

		stack.redo();
		stack.redo();

		assertTrue(stack.dirty());
		assertEquals("Pan", name);
		assertEquals(0, age);
		assertTrue(stack.undoEnabled());
		assertFalse(stack.redoEnabled());
	}

}

Eclipse Dev: Custom Search Page

November 17th, 2009

This is for the archive and due to lack of useful tutorials on writing custom search pages in your own Eclipse plugin. It turned out that the default text search is good enough for my purposes, so I’ll stick it here as a future reference.

To start, your Eclipse plugin needs to add org.eclipse.search as a dependency, and, if you don’t have it already, org.eclipse.jface.text, too. With that, the Require-Bundle part of your MANIFEST.MF should look something like this:

Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.ui.views,
org.eclipse.core.resources,
org.eclipse.ui.ide,
org.eclipse.ui.workbench.texteditor,
org.eclipse.text,
org.eclipse.ui.editors,
org.eclipse.search,
org.eclipse.jface.text

Next, we’ll specify the extension for our custom search page, using the plugin.xml editor or just pasting and modifying this snippet:

<extension point="org.eclipse.search.searchPages">
  <page
    class="com.acm.your.product.CustomSearchPage"
    id="yourplugin.page1"
    label="Your Custom Search, rename this">
  </page>
</extension>

And the actual implementation, providing a textfield as input, initialized with whatever text selection is currently active (thats where the org.eclipse.jface.text dependency comes from), and doing a text search across the full workspace:

package com.acm.your.product;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.TextSearchQueryProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class CustomSearchPage extends DialogPage implements ISearchPage {

	private String selected;
	private Text idText;

	public IdSearchPage() {
		super();
	}

	public IdSearchPage(String title) {
		super(title);
	}

	public boolean performAction() {
		if (idText.getText().length() == 0)
			return false;
		try {
			NewSearchUI.runQueryInBackground(TextSearchQueryProvider.getPreferred().createQuery(idText.getText()));
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (CoreException e) {
			throw new RuntimeException(e);
		}
        return true;
	}

	public void setContainer(ISearchPageContainer container) {
		if (container.getSelection() instanceof TextSelection) {
			selected = ((TextSelection) container.getSelection()).getText();
		}
	}

	public void createControl(Composite parent) {
		GridLayout layout = new GridLayout(1, false);
		layout.horizontalSpacing = 5;
		layout.verticalSpacing = 5;
		parent.setLayout(layout);
		new Label(parent, 0).setText("Containing text:");
		idText = new Text(parent, SWT.BORDER);
		idText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		if (selected != null) {
			idText.setText(selected);
			idText.setSelection(0, selected.length());
		}
		setControl(parent);
	}

	@Override
	public void setVisible(boolean visible) {
		super.setVisible(visible);
		idText.setFocus();
	}

}

You can also restrict the files to search by creating a list first, and pass that to the search provider:

package com.acm.your.product;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.TextSearchQueryProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class CustomSearchPage2 extends DialogPage implements ISearchPage {

	private String selected;
	private Text idText;

	public IdSearchPage() {
		super();
	}

	public IdSearchPage(String title) {
		super(title);
	}

	public boolean performAction() {
		if (idText.getText().length() == 0)
			return false;
		try {
			final List files = new ArrayList();
			ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceProxyVisitor() {
				public boolean visit(IResourceProxy proxy) throws CoreException {
					if (proxy.getType() == IResource.FILE) {
						IFile file = (IFile) proxy.requestResource();
			            if (file.getLocation().getFileExtension() != null && file.getLocation().getFileExtension().matches("xml|html|js|css")) {
			            	files.add(file);
			            }
			         }
			         return true;
				}
			}, IResource.DEPTH_INFINITE);
			NewSearchUI.runQueryInBackground(TextSearchQueryProvider.getPreferred().createQuery(idText.getText(), files.toArray(new IResource[0])));
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (CoreException e) {
			throw new RuntimeException(e);
		}
        return true;
	}

	public void setContainer(ISearchPageContainer container) {
		if (container.getSelection() instanceof TextSelection) {
			selected = ((TextSelection) container.getSelection()).getText();
		}
	}

	public void createControl(Composite parent) {
		GridLayout layout = new GridLayout(1, false);
		layout.horizontalSpacing = 5;
		layout.verticalSpacing = 5;
		parent.setLayout(layout);
		new Label(parent, 0).setText("Containing text:");
		idText = new Text(parent, SWT.BORDER);
		idText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		if (selected != null) {
			idText.setText(selected);
			idText.setSelection(0, selected.length());
		}
		setControl(parent);
	}

	@Override
	public void setVisible(boolean visible) {
		super.setVisible(visible);
		idText.setFocus();
	}

}

This in itself doesn’t yet provide a lot of value, but if you actually need to build a custom search, its a good start.

Roughly based on this sort-of tutorial on the Eclipse wiki.

Doener Grenade

September 3rd, 2009

Crawling some old backups, I found this:

doener

Can you guess what it is? Well, along other files, there’s also a readme:

Author:    [BIA]_Enchos (TNT)
Email:  Enchos@gmx.net

HE DOENER
———————-
Built to replace the HE grenade in Counter-Strike Halflife.
———————-

This pack Include:

New Hud Sprites
All new models, v_, p_, and w_
Completly new skins
———————-
Installation: Just extract the files to your cstrike-folder.
Be sure that the given pathes are used!
(MDL-files in the models-folder and SPR-files in sprites-folder)
———————-
This is a replacement for the HE Grenade, but you can also you
it as a flashbang. Make a copy of the 3 mdl´s and rename them to:
p_flashbang.mdl
v_flashbang.mdl
w_flashbang.mdl

If you don´t want to use the new explosion-sprites, just delete
eexplo.spr, fexplo1.spr and fexplo.spr! :-(
———————-
Note:
The “DOENER” is a kind of burger, but in combination with
“TOMATO KETCHUP” it makes “BOOM”. Hurt your enemies
around the corner and they will hate you :-) . Shoot through the
corner and they will kill you .-|


Still to do:
I also wanted to make new sounds, but this is not as easy as I thought.
If anybody knows how to do it, please mail me (Enchos@gmx.net).
<->
Have a LOT of fun!!!
<->
Suggestions, ideas, anything at all is welcome. Just e-mail me.

———————————————————————-

Be careful with hostages, they are allergic against doener´s.
What? Your enemies too?!?

The highlighted section “explains” the purpose of the tomato ketchup… so yeah, what was I thinking when creating that? I need to figure out how to show you the custom animation; the ketchup was actually poured over the undefined thing in the left hand! Also look at the awesome custom hand-texture…

Looking at the custom grenade models of a random Counterstrike site – this one is still way cooler then those!

Getting started with Git and GitHub and importing from Subversion

August 20th, 2009

If you’re working an open-source projects, these days you can barely get around Git and GitHub. Actually getting started, especially on Windows, isn’t trivial. In my case I had some projects I need to transfer from a Google Code subversion repository to a new repository at GitHub. Actually getting started with GitHub needs to wait for another post, but here are some commands for importing from Subversion:

git svn clone http://[yourproject].googlecode.com/svn/trunk/path/to/code/
git remote add origin git@github.com:[youraccount]/[yourrepository].git
git push origin master

This will create a new folder, based on the svn url you provide (the last part will provide the folder name) and get the files from that path, including the history(!). The second command tells the local Git repository where the next command should go to. You have to replace that URL with “Your clone URL” from your GitHub project.
The last command actually pushes the code to the repository. For that to work, the GitHub repository should be brand-new and empty. If you tried GitHub’s SVN import and got nothing, the repository may be filled with something that GitHub doesn’t show. In that case, create a new repository and import into that. You can remove/delete later.

The following code should do mostly the same, but instead of creating a folder, will initialize an empty repository in the current folder, then do the checkout from svn into that folder (need to test that again):

git init
git svn clone http://[yourproject].googlecode.com/svn/trunk/path/to/code/ .
git remote add origin git@github.com:[youraccount]/[yourrepository].git
git push origin master

Note the dot after the svn clone command.

Thanks to Kelvin Luck for the ideas and commands!

Demon Hunter

August 10th, 2009

A few weeks ago I mentioned a current music favorite, The Dillinger Escape Plan. Today I’ve got another one I’d like to share: Demon Hunter.

While a bit more straight-foward in terms of instruments, vocals are again a great mix between melodic-catchy and slightly over-the-top. See for yourself, from the Storm The Gates Of Hell album:

httpv://www.youtube.com/watch?v=sXktCbZzh8E
4. Fading Away

Probably not an official video, but a great song anyway:
httpvh://www.youtube.com/watch?v=tBrNh6Y9Sw0
8. Incision

Update: Trying a different plugin to get the video into the RSS feed