Tuesday, January 7, 2025

Toggling Region Behavior in Foundry VTT

I'm using Foundry VTT and wanted to create a map with a manhole (could easily be a trapdoor) that leads to another scene. I came across this Reddit discussion and adapted it for my own use.

The underlying map has an open manhole and I have a separate tile on top to act as the cover. I have a region with a Teleport Tile behavior, initially set to disabled. While the Teleport Token Behavior dialog is open, click the Copy Document UUID button in the title bar to copy the UUID of the behavior (not the region itself) and save it for later.

Set up a Script macro (I called it "Toggle Region Behavior Enabled") and use the following script:

const behavior = args[0] && fromUuidSync(args[0]);
if(behavior)
await behavior.update({disabled: !behavior.disabled});

Using args[0] lets you reuse this macro for multiple things.

I have a tile for the manhole cover that I place over the open hole to cover it. In the tile properties (while in Tile Controls, double-click the manhole cover), go to the Triggers tab, change Controlled By to "GM Only" (unless you want your players to be able to do this) and change the When to "Double Click".

Then switch over to the Actions tab and add an action to "Show/Hide", click the button that looks like Stacked Boxes for "this tile" and choose "Toggle" as the State - this will show/hide the tile itself, revealing the open hole. Then add another "Run Macro" action, select the macro you've created, and use your Region Behavior's UUID as the Args.

Now, when you double-click the tile (make sure to switch back to Token Controls or you'll just open the properties again), the tile will hide and activate the region behavior. When you double-click again, it will re-show the tile and deactivate the behavior.

Saturday, April 29, 2023

When to Charge Your Apple Watch

I have heard individuals complain about the battery life of their Apple Watch (or lack thereof) because they feel they need to charge it overnight, but then they don't get their sleep data.

A routine that works well for me is to charge it twice. I charge in the morning as I get ready for the day (shave, shower, etc.) then again at night when I get ready for bed (brush my teeth, wash my face, etc.).

It's always charged and I get my sleep data.

I hope this helps!

Thursday, March 7, 2019

Drinking Diet Soda During Pregnancy and the Link to Autism

There is as much evidence linking diet soda to autism as there is vaccines. By this, I mean there is none. Being a parent is hard and I know you want to protect your kid, but part of protecting your kid is protecting them from preventable diseases. I also know that you wouldn't like it if your kid got something because another kid wasn't protected.

Diet soda doesn't cause autism, nor do vaccinations. The difference is that a vaccine will actually save your kid's life.

Get your kids vaccinated!

Wednesday, May 23, 2018

How to Disable Browser Autocomplete in a Password Box

Most modern browsers are coded to ignore autocomplete="off" on an <input type="password"> field, with the assumption that the site is just trying to be annoying and that the user should be allowed to save and autocomplete their password if they want to.

Most of the time, this is what the user wants. Most sites only have one place and one use case for entering a password: signing in. Some sites also require verification of the password for sensitive operations. Again, the assumption of "if the user wants to save the password, let them" might be OK for these situations as well.

Where this really breaks down is administrative uses.

If I'm an admin and I want to create users, in some applications, I need to set their password. Having the site autocomplete my password, is at best annoying, and could be a security concern.

There are also other uses of <input type="password">: credit card CCV codes, security question/answers, cryptographic keys, etc. and having the browser autocomplete your password is not a good idea.

In these cases, you need/want to disable autocomplete. This is the way I've found that seems to work in the latest version of IE, Firefox, Chrome, and Edge.

<input id="password" name="password" type="password"
 autocomplete="new-password"
 readonly="readonly"
 style="background-color:#fff" />

<script>
setTimeout(function() {
 $("#password").prop("readonly", false).css("background-color", "");
}, 50);
</script>

Starting out in readonly mode is enough for IE, Firefox, and Edge, but has no effect in Chrome. The background-color style just resets the style so it doesn't appear grayed out when the page first loads. The <script> is needed to enable the field after the page has loaded. I tried enabling it when the document loads (e.g. $(function() { ... })), but in Firefox, it triggered the autocomplete anyway. Introducing the delay was the only way to get it to work (50 ms seems to work in my testing - a 10 ms delay didn't work reliably).

I like the autocomplete="new-password" method and wish browsers would standardize around this (since they don't want to use the current standard of "off"). This attribute value works in current versions of Chrome (66.0.3359.181 at the time of this writing) and was the only thing that worked in Chrome, but has no effect in other browsers.

Thursday, August 4, 2016

World of Warcraft Error # 132 after installing Windows 10 Anniversary Update

I recently installed the Windows 10 Anniversary Update. After doing so however, my World of Warcraft installation started crashing during launch with a message stating Error 132 memory something or other referenced such and such.

I searched online for a solution and tried a bunch of things listed on this helpful Youtube link - the DX9 fix worked, but I found a better fix. I happened across a post listing what's new in the update and found that they "added support" for World of Warcraft in the Game DVR. On a hunch, I turned it off (in the settings of the X-Box app). Voila, WoW started working again with DX11.

Edit: After posting I found that Blizzard has an official post with the same resolution: http://us.battle.net/forums/en/wow/topic/20748004624#post-1.

Tuesday, January 26, 2016

SQL to de-dupe overlapping ranges of dates

I came across a situation today where I was given a set of date ranges (begin date to end date) and needed to de-duplicate the ranges so any overlapping dates are "collapsed" so the final result would only contain the distinct range(s) of dates. There are probably a number of solutions to this problem, but I rather liked what I came up with so I decided to post it here for posterity. Code first, explanation after:

-- Declare a table variable for test data
DECLARE @Data TABLE
(
 BeginDate DATETIME,
 EndDate  DATETIME
);
-- Fill the test data
INSERT INTO @Data
SELECT '2016-01-01', '2016-01-05'
UNION ALL SELECT '2016-01-03', '2016-01-10'
UNION ALL SELECT '2016-01-12', '2016-01-15'
UNION ALL SELECT '2016-01-14', '2016-01-18'
UNION ALL SELECT '2016-01-16', '2016-01-25'
UNION ALL SELECT '2016-01-16', '2016-01-20'
UNION ALL SELECT '2016-01-16', '2016-01-20'
UNION ALL SELECT '2016-02-01', '2016-03-13'
UNION ALL SELECT '2016-05-01', '2016-05-30'
UNION ALL SELECT '2016-05-28', '2016-06-10';

WITH S AS (
 SELECT BeginDate, MAX(EndDate) AS EndDate -- Select the starting ranges
 FROM @Data D
 WHERE NOT EXISTS(SELECT * FROM @Data WHERE BeginDate < D.BeginDate AND EndDate >= D.BeginDate)
 GROUP BY BeginDate
UNION ALL
 SELECT S.BeginDate, D.EndDate -- recursively expand the ranges
 FROM S
 INNER JOIN @Data D ON D.BeginDate BETWEEN S.BeginDate AND S.EndDate
   AND D.EndDate > S.EndDate
)
SELECT BeginDate, MAX(EndDate)
FROM S
GROUP BY BeginDate;

In this example, I used a common table expression (CTE) for my test data. The work is done by the "S" CTE. It starts by selecting any date range that doesn't have another date range that overlaps its begin date, grouping by that begin date, to select the max end date (to eliminate any ranges that start on the same day). It then recursively joins back to the original data, bringing in any date range that overlaps and has an end date further out. In the final output, you select the BeginDate and MAX(EndDate) to get your distinct list of date ranges.

If you have additional columns you need to group by, you'll need to add them in. For example:

-- Declare a table variable for test data
DECLARE @Data TABLE
(
 ID   INT,
 BeginDate DATETIME,
 EndDate  DATETIME
);
-- Fill the test data
INSERT INTO @Data
SELECT 1, '2016-01-01', '2016-01-05'
UNION ALL SELECT 1, '2016-01-03', '2016-01-10'
UNION ALL SELECT 1, '2016-01-12', '2016-01-15'
UNION ALL SELECT 1, '2016-01-14', '2016-01-18'
UNION ALL SELECT 1, '2016-01-16', '2016-01-25'
UNION ALL SELECT 2, '2016-01-16', '2016-01-20'
UNION ALL SELECT 2, '2016-01-16', '2016-01-20'
UNION ALL SELECT 2, '2016-02-01', '2016-03-13'
UNION ALL SELECT 2, '2016-05-01', '2016-05-30'
UNION ALL SELECT 2, '2016-05-28', '2016-06-10';

WITH S AS (
 SELECT ID, BeginDate, MAX(EndDate) AS EndDate -- Select the starting ranges
 FROM @Data D
 WHERE NOT EXISTS(SELECT * FROM @Data WHERE ID = D.ID AND BeginDate < D.BeginDate AND EndDate >= D.BeginDate)
 GROUP BY ID, BeginDate
UNION ALL
 SELECT S.ID, S.BeginDate, D.EndDate -- recursively expand the ranges
 FROM S
 INNER JOIN @Data D ON S.ID = D.ID
   AND D.BeginDate BETWEEN S.BeginDate AND S.EndDate
   AND D.EndDate > S.EndDate
)
SELECT ID, BeginDate, MAX(EndDate)
FROM S
GROUP BY ID, BeginDate;

Thursday, December 3, 2015

Starting a numbered list at a different number in Gmail or other web email client

I recently was sending a colleague a series of questions across multiple emails and I wanted to continue numbering the list where the previous email left off. The rich text editor used by Gmail doesn't have this built-in (not that I can find at least), but I figured out a way using my trusty browser tools.

I'm a web developer so using the browser tools is perfectly normal for me on an average day, but although this might be unfamiliar, it's fairly easy to accomplish.

  1. If you haven't done so already, open your formatting tools by clicking the A button in the toolbar, then insert the ordered list by clicking the "1 2 3" icon.
  2. Right-click your list and choose "Inspect element". If your browser doesn't have "Inspect element" or another "Inspect" option, you can press F12 to open the browser tools then click the button in the top left - this lets you then click the list to select it.
  3. You'll see the HTML of the current page. The <ol> tag is what you're looking for. Your tools might have auto-selected an <li> instead, in which case you'd look for the parent.
  4. Right-click the <ol> tag and choose "Add Attribute".
  5. Type "start=N" (no quotes) where N is the number you want to start with

Your browser should update immediately and you'll see the new number as your starting point.

Monday, August 10, 2015

David Hasselhoff Screensaver

It's good sense to lock your computer when you're away. My co-workers have a habit of punishing those who don't by changing their desktop background to a picture of David Hasselhoff (usually the one where he's naked holding a puppy), a practice known as "Hoffing". I decided to go one further and create a screensaver that displays mister Hasselhoff in all his glory.

This is the result. Extract the file on your victim's computer, right-click the .scr file and choose "Install". And here is the source code in case you're worried about malicious code (or just want to see how it works). I leaned heavily on the code from Frank McCown at http://www.harding.edu/fmccown/screensaver/screensaver.html.

Update 2015-09-17 - I found that the previous version was crashing when trying to move the image. I've uploaded new versions of the .scr and source code (same links) to resolve the issue.

Sunday, September 14, 2014

Star Wars: Commander

I've Meanwhile, the non-optional PvP element is basically just stealing the hard-earned resources of other players. he real goal of these games though is to get you to buy whatever points they're selling in order to speed things up. This goal results in gameplay that just isn't fun.

Don't get me wrong, they're totally addicting. That's the whole point. Each stage opens up a new thing that makes you a little bit more powerful so you keep thinking "now I can do This", but each stage of the game takes longer and longer and ultimately, nothing really changes. You're still do the same thing over and over again. It's addicting, but I get no joy in the experience. It's just hard to stop.

Meanwhile, the non-optional PvP element is basically just stealing the hard-earned resources of other players. You log into the game to find another player has stolen resources it took you hours or even days to gather. Sure, you can attack them back, but them you lose the temporary protection obtained by having your base destroyed. And what's the point? Now they've lost time and resources.

This experience could be totally turned around by adding some additional strategy or RTS elements. As is, your attack units are completely mindless. After dropping them on the battlefield, you have no control over their behavior. They'll attack random (and strategically worthless) walls while being fired upon (and destroyed) by enemy units. Your defenses seem woefully underpowered (at least that's my experience) at repelling invading players. A single enemy unit was capable of taking down both shield generators and three turrets before being reinforced by the rest of the army for wiping out the rest of the base.

OK, rant over. And so is my time with this game.

Monday, September 8, 2014

Dynamically Changing the Color of SVG Using an ASP.NET HttpHandler

I have an SVG file that I'm using as the background image of elements on the page (using sprites). I wanted to dynamically update the color of the shapes and paths when theming the application, but I could not find a way to do this using CSS when the SVG is loaded as a background image (I have heard that inline <svg> elements will inherit style rules).

The way I solved the issue was to use a custom HttpHandler in my ASP.NET application that allows me to replace parts of the SVG with other content. Here's the code I used:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;

namespace MyNamespace
{
 public class DynamicSVGHandler : IHttpHandler
 {
  public bool IsReusable
  {
   get
   {
    return true;
   }
  }

  public void ProcessRequest( HttpContext context )
  {
   var request = context.Request;
   var response = context.Response;

   var file = context.Server.MapPath( Path.ChangeExtension( request.Url.LocalPath, ".svg" ) );

   if( File.Exists( file ) )
   {
    var lastWrite = File.GetLastWriteTimeUtc( file );
    response.Clear();
    response.ContentType = "image/svg+xml";
    response.Cache.SetExpires( DateTime.Now.AddMinutes( 5d ) );
    response.Cache.SetCacheability( HttpCacheability.Public );

    List<KeyValuePair<string, string>> replacements = new List<KeyValuePair<string, string>>();

    string value;

    foreach( string key in request.QueryString )
    {
     if( key.Length > 3 && !string.IsNullOrEmpty( value = request.QueryString[key] ) && value.Length > 3 )//Implement any custom validation here.
      replacements.Add( new List<KeyValuePair<string, string>>( key, value ) );
    }

    if( replacements.Count > 0 )
    {
     string line;
     using( StreamReader reader = new StreamReader( File.OpenRead( file ) ) )
     {
      while( (line = reader.ReadLine()) != null )
      {
       foreach( var replacement in replacements )
        line = line.Replace( replacement.Key, replacement.Value );

       response.Output.WriteLine( line );
      }
     }
    }
    else
     response.WriteFile( file );
   }
   else
   {
    response.Clear();
    response.StatusCode = 404;
    response.End();
   }
  }
 }
}

Make sure to register your handler in your web.config (This example is for IIS 7+, integrated mode and I used the extension .dsvg):

<configuration>
 <system.webServer>
  <handlers>
   <add name="*.dsvg" path="*.dsvg" preCondition="integratedMode" verb="*" type="MyNamespace.DynamicSVGHandler, MyAssembly"/>
  </handlers>
 </system.webServer>
</configuration>

You can now reference any svg file in your application with the extension .dsvg instead of .svg and specify replacements in the URL (e.g. mysvg.dsvg?white=red to change "white" to "red").

In my SVG, I found it easiest to define the color using a style section rather than setting the fill of each shape, then add the class="svg-shape" to all the shapes.

Thursday, March 13, 2014

Changing the Color of Table and Column Names in SQL Server Management Studio 2012

If you've upgraded to SQL Server 2012 and used the new Management Studio, you've probably noticed that your table and column names are now a teal color that looks exactly like the color of your comments. Previous versions of Management Studio would use the color specified for "Plain Text", but the new version uses a new setting.

To change the color, open the Tools menu and choose Options.... In the Environment settings click Fonts and Colors. Locate and click "Identifier" in the Display Items list box then change the Item foreground to something other than Default. That should do the trick.

Monday, February 24, 2014

How to Restore Files from a Restore Point

In my last post, I mentioned how I had used Windows System Restore, only to find out that JavaScript (*.js) files are considered to be "system files" and are restored back to the state they existed when the restore point was created. I tried restoring back to the previous state, but no-go. System Restore failed with every other restore point.

I thought all was lost, until I found this article by the How-to Geek that has a batch file letting you mount the latest VSS (Volume Shadow Copy) volume and view the files contained there-in (when you create a Restore Point, Windows creates a VSS volume containing your files at that point in time). I didn't want the most recent volume, but I looked at the batch file and figured out how to list the available volumes and mount the one I wanted. I figured I'd document the steps in case this helps anyone else.

  1. First off, you need to open a command prompt as an administrator (Right-click the "Command Prompt" and choose "Run as Administrator").
  2. At the prompt, type
    VSSAdmin List Shadows
    This will list the available VSS volumes on your system. This can be confusing, but each one displays a creation time. What you're looking for is the the "Shadow Copy Volume" (e.g. \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1).
  3. Once you find out the name of the shadow copy volumne, you can mount the volume using MKLINK just as you would any other folder (if you haven't used MKLINK, I'd recommend you check it out - it's a pretty useful tool). For example:
    MKLINK /D C:\ShadowBackup \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1
    In this example, you'll see a new folder named ShadowBackup on your C: drive, containing the contents of the VSS volume.

Now you can view the contents of the VSS volume and view/copy any of the files within. This can also be useful if you accidentally delete a file or want to see what changes you've made.

To "unmount" the volume, you can simply delete the folder in Windows Explorer. This won't delete the volume, just the link to view the files.

Warning: System Restore will Modify/Delete JavaScript Files!

I discovered too late that Windows System Restore considers JavaScript (*.js) files to be system files. Apparently, the way System Restore works is it uses the Volume Shadow Copy service to back up your files, then during the restore process, it restores "system" files to their state at the time the restore point was created.

As a developer, this is bad for me, since I regularly create and edit JS files. After restoring my system, I opened up my code and found, to my surprise that all my JS changes had been reverted. If this happens to you, in my next post, I'll detail how I managed to restore the files.

Tuesday, January 14, 2014

Munchkin Rules Removed by Request.

The page of Munchkin rules has been removed by request. No hard feelings on this end.

Monday, January 6, 2014

Performance of a shortcut to document.getElementById

I don't like typing document.getElementById (or document.createElement, etc.) repeatedly so for a while now, I've used my own function, I call byId (I know jQuery has $get, but I don't use jQuery, for reasons I won't go into here). This function started out as a simple:

function byId(id)
{
 return document.getElementById(id);
}

In Internet Explorer, I could use var byId = document.getElementById, but this didn't work in other browsers. I then tried, what I though was a more elegant solution of binding byId to the document object (var byId = document.getElementById.bind(document)). This works, but it winds up being slower then then simple method in all browsers, but IE. I decided to publish my results on jsperf for anyone interested.

What's really maddening is that the best method is different in each browser. I'm sure any web developers have experienced this themselves.

Sneaky Christmas Music

More and more, it seems that every year the holidays sneak up on me and suddenly it's Christmas. I was thinking about it last night, and I think I figured out why.

For starters, I'm older and time seems to go faster as you get older because a defined period of time is a smaller and smaller percentage of your memories. But it's more than that. I think it's the music.

Back when I was younger, the radio was constantly playing Christmas music. We had to go outside to shop and all the stores were replaying Christmas songs (driving their employees crazy).

This year, I listened to very little Christmas music. Due to the digital age, I had Pandora, podcasts, and my iTunes music library. I rarely ever turn on the radio anymore. I think next year I might try and mix in a little holiday cheer to my auditory experience and see how that affects me.

Friday, January 3, 2014

Munchkin Rules in HTML Format

I love playing Munchkin so much, I decided to translate the rules of Munchkin into HTML format to make them more searchable/linkable/viewable on other devices and post them here as a good way to access them anywhere. Note that I am in no way affiliated with Steve Jackson Games and if they ask me to take these down, I will without hesitation (though I'd ask them to make them available on their own site).

*** Edit: Removed by request ***

Wednesday, September 26, 2012

The Deception of Capital Gains

I could rant on this topic for hours, but for now, I'll focus on one point. Capital Gains tax rates are lower, typically far lower, than the average tax rate. This has been rationalized as being acceptable by saying that since you're investing in the growth of businesses, you should be rewarded or by saying that it encourages investment which in turn fuels business growth.

Don't believe it

What it actually does is reduce the tax rates for wealthy people. Now before you immediately stop reading and think I'm just spreading propaganda or anti-republican party BS, think about it. The only people that could really benefit from the lower rates are people that have extra disposable income. If you're living paycheck to paycheck and worrying about putting food on the table, you don't have extra money to invest in another business. In order to invest, you have to have at least some disposable income. Many or most middle class families make enough to invest some of their money, but most are investing in retirement plans and saving for the future. Retirement plans have their own tax incentives, but don't benefit from capital gains rates because they're separate. The beneficiaries of capital gains rates are those that have enough money to "make their money work for them" and use money to make money.

Don't you wish you could do that? Sounds like a great deal.

So basically, capital gains rates only apply to money that is made using money that you already have, and theoretically could afford to lose. Considering this type of income can only be made if your wealth increases and you only get taxed on the gain, not what you put in, do we really need to incentivize this behavior?

Wednesday, August 1, 2012

Initial Thoughts on Windows 8

I recently installed the Windows 8 consumer preview on a virtual machine and thought I would post my initial reaction.

I actually like the new approach to the start screen and having information update in real-time and I think the UI will be really nice with a touchscreen. However there are drawbacks to this approach. I am mostly talking from a perspective of using a keyboard and mouse.

  1. When using a mouse, I realize I can hover in the bottom left and the button will appear, but this takes more time than simply clicking a button. Please just give me the option to show a button. The hover just takes too much time. I've also inadvertently launched IE from the desktop twice now because I hovered to bring up the button, then thought I clicked the button but the click went through to the pinned Internet Explorer icon. It might be possible to pin a short-cut to the start screen on the taskbar...
    1. I'm running the preview in a virtual machine and since my virtual machine display is itself in a Window, it's actually relatively hard to move the mouse to the corners of the display without going into my host environment. This will cause problems with users using Remote Desktop.
    2. Once I new about the "hot" areas, it became usable, but it's not obvious. It reduces the number of UI elements on the screen, but new users will have trouble.
    3. I know that I can click the Windows button to bring up the start screen, but again, since I'm using Remote Desktop to a virtual machine, the Windows button is keyed to my host machine. I might not mind the lack of start button as much when I'm using it as my main OS.
  2. The buttons being so big makes it nice for a touchscreen because you have more click-able area, but when using a mouse, it winds up taking much longer because you now have to move your mouse a much greater distance. e.g. you might have to move across the entire screen instead of just a few inches.
  3. I'm a developer and I use a lot of programs in my day-to-day life. By a lot, I mean I have about 8 programs I run every day (Visual Studio, 3 different browsers, Outlook, Messenger, SQL Management Studio, Notepad, etc) plus a large number of others that i run situationally. The old start menu gave me a way to organize those programs by function and was pretty good at simply listing them all. Everyone seems to be moving towards Search to find your app, but if you don't remember the name, you're basically browsing for the right app so you want to browse by category and/or simply view everything you have so you can find what you're looking for.
  4. The buttons on the start screen are not very visually distinctive. I can appreciate the look you're going for and seeing the screenshots, I really liked it, but as I said on item #3, I use a lot of different programs and having to search through them all when all the buttons look alike is not a prospect I look forward to.
  5. I still haven't figured out how to turn it off.

Thursday, March 1, 2012

Campaign Contributions Must Die

Tell me if you disagree, but I think the biggest problem with our current political system is campaign contributions. Basically, the problem is that in order to be elected, you have to fund your campaign. Since most funding comes from corporations or special interest groups, politicians become beholden to said corporations or special interest groups and therefore work in their best interest rather than the interest of the public at large. This also encourages extremism in the beliefs of the candidates and what they propose because they're trying to get the support of a group that supports that belief (a problem with the political party system in general).

I can see two ways of fixing this:
1. Fund campaigns using government money.
2. Require all campaign contributions to be anonymous.

Option 1 should theoretically be more effective because each candidate would have an even playing field and voters would support the person with beliefs most in tune with their own. Since candidates run on money from the people, they're beholden to the people, not a faceless entity. Option 1 also evens the power between the wealthy and the poor since campaigns aren't dependent on the amount of money each supporter has available to donate.

Option 2 is more moderate. People, corporations, and groups could still donate to help ensure their candidate wins, but the candidate is theoretically unaware of where these funds are coming from and is therefore less beholden to these groups. However, there is a large potential for abuse by off-the-record phone calls and the like. This option also has the downside of wealthier people having more influence.

Or there is a third option:
3. Allow people to vote on the issues themselves rather than having a representative do it for them.

This would be the theoretical ideal but it runs into the problem of informing people about all the issues in a simple enough way that everyone could understand and then actually getting them to go out and vote. This is already hard for some people and increasing the number of things they're voting for might make it worse for some, though it might make it better for others since they would feel like they're part of the real process and not just voting between bad candidate A and bad candidate B.