The geeky AppleScript christmas tree

I know, I am a little bit late (or too early) with this fun project, but if you liked Niftee-Tron’s Hamlet as much as I did, than you should definitely try out the geeky AppleScript Christmas Tree :lol:

The AppleScript Christmas Tree (ca. 265 KB)

The illuminated AppleScript christmas tree in action

The AppleScript Christmas Tree was successfully tested on:
¢ Mac OS X 10.5.1
¢ Mac OS X 10.4

To start the animation, just double-click the AppleScript icon at the end of the tree trunk. There are several illumination types to discover, so lay back and watch the show :cool:

The script code can be viewed in the Script Editor and with a little bit of AppleScript-knowledge you can easily create your own illumination routines.

Happy X-Mas (2008)!

P.S.: I really tried my best to preserve the folder view on the disk image to display the christmas tree properly. If it’s broken on your Mac, please note accordingly.
P.S.S.: The AppleScript code is far from being perfect, but it works :wink:

Imporant: Opening and saving the below script code in Script Editor won’t result in a usable AppleScript! Therefor please download the complete script here.


-- created: 06.02.2008

property mytitle : "AppleScript Christmas Tree"
property myversion : "0.1b"

on run
	my main()
end run

-- I am the main routine controlling the script
on main()
	-- ask the user how many times we should illuminate the tree
	set rounds to my askforrounds()
	-- user hit 'Cancel'-button
	if rounds is missing value then
		return
	end if
	-- let's start the illumination
	my illuminatetree(rounds)
end main

-- I am asking the user how many times we should illuminate the tree
-- and return this value as an integer.
-- If the user hits the 'Cancel'-button, I return «missing value»
on askforrounds()
	set msg to "How many times should we illuminate the christmas tree?"
	tell me
		activate
		display dialog msg default answer "10" buttons {"Cancel", "Enter"} default button 2 with icon note with title mytitle
		set dlgresult to result
		if button returned of dlgresult is "Cancel" then
			return missing value
		else
			try
				-- did the user enter a number?
				set rounds to ((text returned of dlgresult) as integer)
				-- yes, but he entered a zero, so we ask again!
				if rounds is 0 then
					my askforrounds()
					-- yes, a valid integer!
				else
					return rounds
				end if
				-- some people just don't know what a number is...
			on error
				my askforrounds()
			end try
		end if
	end tell
end askforrounds

-- I am responsible for controlling the tree illumination
on illuminatetree(rounds)
	set illufunctions to {"random", "row", "edge"}
	tell application "Finder"
		set treefolder to (container of (path to me)) as Unicode text
		-- resetting the colour of all light folders
		set label index of every item in folder treefolder to 0
	end tell
	repeat rounds times
		set illufunction to some item of illufunctions
		if illufunction is "random" then
			my randomillumination(treefolder)
		else if illufunction is "row" then
			my rowillumination(treefolder)
		else if illufunction is "edge" then
			my edgeillumination(treefolder)
		end if
	end repeat
end illuminatetree

-- I am illuminating random lights on the christmas tree
on randomillumination(treefolder)
	set colours to {"1", "2", "3", "4", "5", "6"}
	tell application "Finder"
		set countlightfolders to 15
		set lightfolderpaths to {}
		repeat with i from 1 to countlightfolders
			repeat
				set rnum to random number from 2 to 70
				set lightfolderpath to treefolder & rnum & ":"
				if lightfolderpath is not in lightfolderpaths then
					set lightfolderpaths to lightfolderpaths & lightfolderpath
					exit repeat
				end if
			end repeat
		end repeat
		repeat with lightfolderpath in lightfolderpaths
			set label index of folder lightfolderpath to ((some item of colours) as integer)
			delay 0.1
		end repeat
		delay 1.5
		repeat with lightfolderpath in (reverse of lightfolderpaths)
			set label index of folder lightfolderpath to 0
			delay 0.1
		end repeat
		delay 1.0
	end tell
end randomillumination

-- I am illuminating horizontal rows of light folders
on rowillumination(treefolder)
	set colours to {"1", "2", "3", "4", "5", "6"}
	set lightrows to {{"1"}, {"2", "3", "4"}, {"5", "6", "7", "8", "9"}, {"10", "11", "12"}, {"13", "14", "15", "16", "17", "18", "19"}, {"20", "21", "22", "23", "24", "25", "26", "27", "28"}, {"29", "30", "31", "32", "33", "34", "35"}, {"36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46"}, {"47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59"}, {"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70"}}
	tell application "Finder"
		set directions to {"up", "down"}
		set direction to some item of directions
		if direction is "down" then
			repeat with lightrow in lightrows
				repeat with lightrownum in lightrow
					set lightfolderpath to treefolder & lightrownum & ":"
					set label index of folder lightfolderpath to ((some item of colours) as integer)
				end repeat
				delay 0.4
				repeat with lightrownum in lightrow
					set lightfolderpath to treefolder & lightrownum & ":"
					set label index of folder lightfolderpath to 0
				end repeat
				delay 0.1
			end repeat
		else if direction is "up" then
			repeat with lightrow in (reverse of lightrows)
				repeat with lightrownum in lightrow
					set lightfolderpath to treefolder & lightrownum & ":"
					set label index of folder lightfolderpath to ((some item of colours) as integer)
				end repeat
				delay 0.4
				repeat with lightrownum in lightrow
					set lightfolderpath to treefolder & lightrownum & ":"
					set label index of folder lightfolderpath to 0
				end repeat
				delay 0.1
			end repeat
		end if
	end tell
end rowillumination

-- I am illuminating the light folders on the edge of the tree
on edgeillumination(treefolder)
	set colours to {"1", "2", "3", "4", "5", "6"}
	set lightrows to {{"1"}, {"2", "3", "4"}, {"5", "6", "7", "8", "9"}, {"10", "11", "12"}, {"13", "14", "15", "16", "17", "18", "19"}, {"20", "21", "22", "23", "24", "25", "26", "27", "28"}, {"29", "30", "31", "32", "33", "34", "35"}, {"36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46"}, {"47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59"}, {"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70"}}
	tell application "Finder"
		set directions to {"up", "down"}
		set direction to some item of directions
		if direction is "down" then
			repeat with lightrow in lightrows
				set lightfolderpathfirst to treefolder & (first item of lightrow) & ":"
				set lightfolderpathlast to treefolder & (last item of lightrow) & ":"
				set label index of folder lightfolderpathfirst to ((some item of colours) as integer)
				set label index of folder lightfolderpathlast to ((some item of colours) as integer)
				delay 0.4
				set lightfolderpathfirst to treefolder & (first item of lightrow) & ":"
				set lightfolderpathlast to treefolder & (last item of lightrow) & ":"
				set label index of folder lightfolderpathfirst to 0
				set label index of folder lightfolderpathlast to 0
				delay 0.1
			end repeat
		else if direction is "up" then
			repeat with lightrow in (reverse of lightrows)
				set lightfolderpathfirst to treefolder & (first item of lightrow) & ":"
				set lightfolderpathlast to treefolder & (last item of lightrow) & ":"
				set label index of folder lightfolderpathfirst to ((some item of colours) as integer)
				set label index of folder lightfolderpathlast to ((some item of colours) as integer)
				delay 0.4
				set lightfolderpathfirst to treefolder & (first item of lightrow) & ":"
				set lightfolderpathlast to treefolder & (last item of lightrow) & ":"
				set label index of folder lightfolderpathfirst to 0
				set label index of folder lightfolderpathlast to 0
				delay 0.1
			end repeat
		end if
	end tell
end edgeillumination

Merry Christmas Martin! :slight_smile: That was fun, thanks.

Lol :d

Hey, is anyone interested in hearing something about this Christmas tree 3 months after Christmas, in March? :smiley: I can wait 9 months, if no one responds.

Maybe I should release an Easter Egg version :wink:

Improved:
Changed folder names to " " with different amounts of the zero-width-space character, so that they all look the same when lit. Also, close the Finder window to stop the script (in less then 20 seconds). Also new ‘settings’ property and repeat clauses (instead of repeating code) make script more customizable.
Color and illufunction settings have been changed to show how to change probability; time delay settings are the same.
Enable the large comment block to convert old folders to new ones with icons. You just need a “Applescript Christmas Tree:src:folder” folder with an icon. You can get the new folder here (already converted):
act.dmg (Link may change; someone tell me how to host this on MacScripter.)
When is Easter?

-- author: Martin Michel; eMail: martin@joyofscripting.com
-- modified by Sean Zhu; http://interestinglythere.com/
-- created: 06.02.2008
-- last modified: 03.24.2008

property mytitle : "AppleScript Christmas Tree"
property myversion : "0.1b modified"

--Changes:
--	closing the window eventually exits the program
--	got rid of ANY repeating code in favor of a repeat clause
--	made it more customizable
--	changed variable treefolder to rootfolder to make it more generic
--Help: Settings:
--	info: display info dialogs.
--	colours:  Turn colours on/off and adjust probability.
--		colors 0-7: {None, R, O, Y, G, B, V, Gray}.
--	illumination: Turn illumination schemes on/off and adjust probability
--		illumination 1-3: {my randomillumination, my rowillumination, my edgeillumination}.
--	delay:
--		<element>delay<on/off>: delay after <element> is turned <on/off>.

property settings : ¬
	{defaultfolder:"AppleScript Christmas Tree:" as Unicode text ¬
		, info:(false) ¬
		, colours:{1, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6} ¬
		, illumination:{1, 1, 2, 3} ¬
		, random:{lightsdelayon:0.1, lightsdelayoff:0.1, alldelayon:1.5, alldelayoff:1.0, countlightfolders:15} ¬
		, row:{lightsdelayon:0.0, lightsdelayoff:0.0, rowsdelayon:0.4, rowsdelayoff:0.1} ¬
		, edge:{delayon:0.1, delayoff:0.1}}

on run
	global rootfolder
	set rootfolder to missing value
	tell application "Finder"
		set my rootfolder to (container of (path to me)) as Unicode text
		try -- for running withing Script Editor (applets don't have a scriptable name, hence the try statement)
			if my name = "Script Editor" then ¬
				set my rootfolder to defaultfolder of my settings
		end try
		my main()
	end tell
end run

on open {item1} -- If a folder is dragged to the applet, the applet works with that folder.
	global rootfolder
	set rootfolder to item1 as Unicode text
end open

on main() -- I am the main routine controlling the script
	my clear(my rootfolder)
	if info of my settings then
		tell application "Finder"
			activate
			display dialog "Christmas Tree is initializing..." buttons {"Quit", "OK"} cancel button 1 default button 2 giving up after 1
		end tell
	end if
	global lights
	set lights to {}
	repeat with i from 1 to 72
		set s to ""
		repeat i times
			set s to s & («data utxt200B» as Unicode text)
			-- Unicode character 200B is a zero-width space.
		end repeat
		set lights to lights & {("  " & s)}
	end repeat
	(* 	-- NOTE: Enable the following to change old folders to new folders.  Must have src folder in rootdir.
	my waitforfinder()
	repeat with i from 1 to 72
		set s to ""
		repeat i times
			set s to s & ((«data utxt200B» as Unicode text))
		end repeat
		if i ≤ 70 then set sp to "  "
		if i > 70 then set sp to ""
		-- next line renames folders from number style to space style
		--tell application "Finder" to set name of alias (((my rootfolder as alias) as string) & i as string) to (sp & s)
		-- next 2 lines set icons
		do shell script "cp '" & POSIX path of ((characters 1 thru -2 of my rootfolder) as Unicode text) & "/src/folder/Icon" & («data utxt000D» as Unicode text) & "' " & quoted form of POSIX path of (((my rootfolder as alias) as string) & (sp & s))
		do shell script "/Developer/Tools/SetFile -a C " & quoted form of POSIX path of (((my rootfolder as alias) as string) & (sp & s))
		-- When the icons are set, click the folders to update the icons.
	end repeat
	return
	*)
	global lightrows -- Item 1 must be made into a list because ususally it will be a string.
	set lightrows to {item 1 of lights as list, ¬
		items 2 thru 4 of lights, ¬
		items 5 thru 9 of lights, ¬
		items 10 thru 12 of lights, ¬
		items 13 thru 19 of lights, ¬
		items 20 thru 28 of lights, ¬
		items 29 thru 35 of lights, ¬
		items 36 thru 46 of lights, ¬
		items 47 thru 59 of lights, ¬
		items 60 thru 70 of lights}
	-- let's start the illumination
	my waitforfinder()
	repeat while not my windowclosed()
		global illufunction
		set illufunction to item (some item of illumination of my settings) of ¬
			[my randomillumination, my rowillumination, my edgeillumination]
		illufunction(my rootfolder)
	end repeat
	my clear(my rootfolder)
end main

on randomillumination(rootfolder) -- I am illuminating random lights on the christmas tree
	my infodialog("Random Illumination")
	tell application "Finder"
		set lightfoldernames to {}
		repeat with i from 1 to 2
			repeat with foldernum from 1 to (countlightfolders of random of my settings)
				if i = 1 then
					repeat
						set randomnum to random number from 2 to 70
						set lightfoldername to (item randomnum of my lights)
						if not (my isin(lightfoldername, lightfoldernames)) then
							set lightfoldernames to lightfoldernames & {lightfoldername}
							exit repeat
						end if
					end repeat
				else
					set lightfoldername to (item foldernum of lightfoldernames)
				end if
				set thecolour to 0
				if i = 1 then set thecolour to my colour()
				set label index of folder (rootfolder & lightfoldername) to thecolour
				if i = 1 then
					delay lightsdelayon of random of my settings --time between ons
				else
					delay lightsdelayoff of random of my settings --time between offs
				end if
				if my windowclosed() then exit repeat
			end repeat
			if i = 1 then
				set lightfoldername to (reverse of lightfoldernames)
				delay alldelayon of random of my settings --time between ons
			else
				delay alldelayoff of random of my settings --time between offs
			end if
		end repeat
	end tell
end randomillumination

on rowillumination(rootfolder) -- I am illuminating horizontal rows of light folders
	my infodialog("Row Illumination")
	tell application "Finder"
		if (some item of [true, false]) then -- true:down ; false:up
			set lightrowswithdirections to my lightrows
		else
			set lightrowswithdirections to (reverse of my lightrows)
		end if
		repeat with lightrow in lightrowswithdirections
			repeat with i from 1 to 2
				set thecolour to 0
				repeat with lightfoldername in lightrow
					if i = 1 then set thecolour to my colour()
					set label index of folder (rootfolder & lightfoldername) to thecolour
					if i = 1 then
						delay lightsdelayon of row of my settings --time between light ons
					else
						delay lightsdelayoff of row of my settings --time between light offs
					end if
					
				end repeat
				if i = 1 then
					delay rowsdelayon of row of my settings --time of row light
				else
					delay rowsdelayoff of row of my settings --time of complete darkness
				end if
				if my windowclosed() then exit repeat
			end repeat
		end repeat
	end tell
end rowillumination

on edgeillumination(rootfolder) -- I am illuminating the light folders on the edge of the tree
	my infodialog("Edge Illumination")
	tell application "Finder"
		if (some item of [true, false]) then -- true:down ; false:up
			set lightrowswithdirections to my lightrows
		else
			set lightrowswithdirections to reverse of my lightrows
		end if
		repeat with lightrow in lightrowswithdirections
			repeat with i from 1 to 2
				set thecolour to 0
				repeat with lightfoldername in {first item of lightrow, last item of lightrow}
					if i = 1 then set thecolour to my colour()
					set label index of folder (rootfolder & lightfoldername) to thecolour
				end repeat
				if i = 1 then
					delay delayon of edge of my settings --time between ons
				else
					delay delayoff of edge of my settings --time between offs
				end if
				if my windowclosed() then exit repeat
			end repeat
		end repeat
	end tell
end edgeillumination

on windowclosed() -- I can tell if the tree folder is open (returns false) or closed (returns true)
	-- usage in repeat loop: 		if my windowclosed() then exit repeat
	-- usage in handler or main: 	if my windowclosed() then return
	-- usage anywhere (don't use): 	if my windowclosed() then quit
	-- DO NOT use the last one, "quit", because if it will quit Script Editor if you are running the script from it.
	tell application "Finder"
		repeat with w from 1 to count Finder windows
			try
				if ((target of Finder window w) as alias) = (my rootfolder as alias) then return false
			end try
		end repeat
		return true
	end tell
end windowclosed

on waitforfinder() -- I wait for the Finder to finish displaying a dialog.
	repeat
		try
			delay 1
			tell application "Finder" to get name
			exit repeat
		end try
	end repeat
end waitforfinder

on isin(i, l) -- I can tell apart folders with different numbers of zero-width spaces in them.
	repeat with i2 in l
		if i as string = i2 as string then return true
	end repeat
	return false
end isin

on colour() -- I choose a label index colour.
	return some item of colours of my settings
end colour

on clear(rootfolder) -- I turn off all the lights.
	tell application "Finder" to set label index of every item in folder (my rootfolder) to 0
end clear

on infodialog(msg)
	if info of my settings then
		activate
		display dialog msg giving up after 1 buttons {"Stop", "OK"} default button 2 cancel button 1
	end if
end infodialog