Find & Replace Text in batches of files at once

Yo, I wrote this application called “FindReplace” and here is the code:



(* 
    	This application is a droplet for batch-processing text files.  You can also drag folders onto it, and it will process 
	the items inside.
   	(IMPORTANT:  It does not check first if the files are text files -- technically, it will process ANY item that is not a 
	disk, application, or folder.  But it includes an option allowing you to only process specific file types, or to 
	ignore specific file types).
	
	What it's intended to do:  Look inside each file, find strings of text the User specifies, and replace them with 
	strings of text the User specifies, creating a new file that includes the changes.  The original file is kept the same.
	The User is allowed to specify multiple find/replace combinations in a single process.  Example:
	"The Man" is replaced with "The Woman",
	"Elephant" is replaced with "Rhinoceros", and
	"Disneyland" is replaced with "Knott's Berry Farm", all done at once.

*)

--These variables are global simply because it's a pain to pass them all as arguments to functions.
property exclusionChoice : ""
property exclusionChoices : {}
property extList : {}
property choiceList : {}
property choiceNums : {}
property searchTexts : {}
property replaceTexts : {}
property cOrSs : {}
property caseBools : {}

-- SECTION 1:  the handler that runs when you drag items onto the app icon:
on open draggedItems
	mainRoutine(draggedItems)
end open --application quits.


-- SECTION 2:  the handler that runs when you double-click the app:
on run
	set theResult to display dialog "Do you want to select Files or Folders?" buttons ¬
		{"Cancel", "Folders", "Files"} default button 3
	tell application "Finder"
		--Make a dialog window open and ask user to select item(s) to process.
		if button returned of theResult is "Folders" then
			set chosenItems to choose folder with prompt ¬
				"Choose Folder(s) to process" default location desktop as alias with multiple selections allowed
		else if button returned of theResult is "Files" then -- if button returned is "File"
			set chosenItems to choose file with prompt "Choose File(s) to process" default location ¬
				desktop as alias with multiple selections allowed
		end if
	end tell
	mainRoutine(chosenItems)
end run --application quits.



--  SECTION 3:  Define functions.

on mainRoutine(itemsToProcess)
	set useMostRecent to (choose from list {"Yes", "No"} with title ¬
		"Use Most Recent Settings?" with prompt "Choose Yes or No:" OK button name "Proceed") as text
	if useMostRecent is "false" then return
	if useMostRecent is "No" then
	--Note that all even-numbered items in list are case-sensitive options:
	set choiceList to {"Specific", "Specific (case-sensitive)", "Begin(anything)End", ¬
		"Begin(anything)End (case-sensitive)"}
	
	
	repeat --until User reviews and approves his specified settings.
		--Create arrays to store user settings for each inner loop iteration:
		set {choiceNums, searchTexts, replaceTexts, cOrSs, caseBools} to ¬
			{{}, {}, {}, {}, {}}
		
		--Inner loop
		repeat -- until User chooses to not add more find/replace pairs.
			
			set listChoice to (choose from list choiceList ¬
				with title "”””” What Shall I Do Sir? ””””" with prompt ¬
				"Click an option:" OK button name "Proceed") as text
			--If user hits 'cancel' when choosing from list it returns false as string, not boolean.
			if listChoice is "false" then return
			set end of choiceNums to getIndex(listChoice, choiceList) as integer
			
			--Get current choiceNum from choiceNums:
			set choiceNum to LI(-1, choiceNums)
			
			--All even choiceNums will be case-sensitive searches:
			if (choiceNum mod 2 = 0) then
				set end of caseBools to true
			else
				set end of caseBools to false
			end if
			
			
			--Assign the default context setting:
			set end of cOrSs to "All"
			
			if choiceNum < 3 then
				set end of searchTexts to text returned of ¬
					(display dialog "Enter the text you want to find:" default answer "")
				--Deal specifically with the context choice:
				set cOrSList to {"” where it is Contained inside a longer word", ¬
					"” where it is Separate from other words, ", "” in All instances"}
				set cOrSchoice to ¬
					(choose from list cOrSList with title "”””” In What Context? ””””" with prompt ¬
						"Replace the total search text ”" OK button name "Proceed") as text
				if cOrSchoice is "false" then return
				set cOrSNum to getIndex(cOrSchoice, cOrSList) as integer
				if cOrSNum = 1 then
					setLI(-1, cOrSs, "Contained")
				else if cOrSNum = 2 then
					setLI(-1, cOrSs, "Separate")
				end if
				
			else -- If listChoice is not item 1 or 2:
				--If user chose Begin(anything)End you don't give them the context choice:
					display dialog "Enter the beginning of the text to be replaced:" default answer ""
					set begin_search_string to (text returned of result)
					display dialog "Enter the ending of the text to be replaced:" default answer ""
					set end_search_string to (text returned of result)
					set end of searchTexts to {begin_search_string, end_search_string}
				
			end if
			
			
			display dialog "Enter the text to replace it all with:" default answer ""
			set end of replaceTexts to (text returned of result)
			
			display dialog "Would you like to do more after that?" buttons {"Cancel", "Yes", "No"} ¬
				default button 3
			if button returned of result is "No" then exit repeat
		end repeat
		
		set exclusionChoices to {"Process all file types except....      ", ¬
			"Skip all file types except....      ", "Skip this option"}
		set exclusionChoice to (choose from list exclusionChoices with title ¬
			"Any file types to exclude, or focus on?" with prompt ¬
			"Click an option:" OK button name "Proceed") as text
		
		if exclusionChoice is not LI(3, exclusionChoices) then
			if exclusionChoice is LI(1, exclusionChoices) then
				set extList to text returned of (display dialog ¬
					"Enter name extensions of files you want to skip" & return & ¬
					"(separate each with commas):" default answer "")
			else if exclusionChoice is LI(2, exclusionChoices) then
				set extList to text returned of (display dialog ¬
					"Enter name extensions of files you want to process" & return & ¬
					"(separate each with commas).  All other types will be skipped:" default answer "")
			end if
			--Remove periods from extList if user added any:
			set extList to replace(".", "", extList)
			--Separate the different extensions into a list of items, using either a comma or space
			--as the separator:
			set extList to explode(extList, {",", " "})
			set extList to removeEmptyStrings(extList) -- in case there are any empty strings.
		else
			set extList to {}
		end if
		
		--Display a 'summary of settings' dialog:
		if userApprovesSettings() then exit repeat
	end repeat
	end if
	set theLists to {choiceNums, searchTexts, replaceTexts, cOrSs, caseBools}
	
	--This condition check is only for debugging:
	if countCheck(theLists) is true then
		formatSearchAndReplaceTexts()
		--Begin the loop that processes each item:
		repeat with i from 1 to (count of itemsToProcess)
			processItem(LI(i, itemsToProcess))
		end repeat -- Item processing ends.
		display dialog "Finished." buttons {"OK"} default button "OK" giving up after 3
		--Application quits.
		
	else --for debugging:
		return theLists
	end if
end mainRoutine


on processItem(theItem)
	--Check what kind of item the draggedItem is:
	set theItem to (theItem as text)
	tell application "System Events"
		set theProperties to properties of (theItem as alias)
		set {theKind, itemID, nameExt} to {kind of theProperties, ¬
			name of theProperties, name extension of theProperties}
	end tell
	--Protect the "versions" folders so the contents don't get modified:
	if (theKind is "Folder" and itemID does not contain "versions") then
		processFolder(theItem)
	else if (theKind is not in {"Volume", "Application", "Folder"}) then
		if (exclusionChoice is LI(1, exclusionChoices) and nameExt is not in extList) ¬
			or (exclusionChoice is LI(2, exclusionChoices) and nameExt is in extList) or ¬
			(exclusionChoice is LI(3, exclusionChoices)) then
			--process the file once for every item in choiceNums:
			repeat with i from 1 to (count of choiceNums)
				processFile(theItem, LI(i, choiceNums), LI(i, searchTexts), LI(i, replaceTexts), ¬
					LI(i, cOrSs), LI(i, caseBools))
			end repeat
		end if
	end if
end processItem


--theFolder must be an alias.  This is a recursive function that will call itself
--if it finds a folder inside of theFolder.
on processFolder(theFolder)
	tell application "System Events"
		set everyItem to path of every item of (theFolder as alias) whose visible is true
	end tell
	repeat with i from 1 to (count of everyItem)
		set theItem to (item i of everyItem) as text
		processItem(theItem)
	end repeat
end processFolder


on processFile(theFile, choiceNum, searchText, replaceText, cOrS, caseBool)
	--Replace the old text with the new and write it to a new file in the same location:
	set theResult to findReplaceToNewFile(choiceNum, searchText, replaceText, theFile, ¬
		cOrS, caseBool)
	if theResult is not false then --Then the modifying of the text is now done.
		-- Move the original file into the 'versions' folder:
		makeFolderAndMoveOriginalFile(theFile, theResult)
	end if
end processFile


(*
findReplaceToNewFile() uses perl inside shell scripts to perform the find&replace
and write the changes to a new file.  theFile must be the file's entire path as a string.
searchStrings can be either a string or list of strings.  Returns false if error occurs and no new file is made.
If not false, it returns the most recent modification date of theFile, which
is used by processFile() to rename theFile with the date&time prepended to it.
*)
on findReplaceToNewFile(choiceNum, searchString, replaceString, theFile, cOrS, caseBool)
	try
		set {fileName, uniqueString, newFile, shellString} to ¬
			{itemName(theFile), "¢§-_0_¢R0çç--§§Ã¥¢", (theFile & ".new"), ""}
		--use system events to get last-modified property from theFile:
		tell application "System Events" to set modDate to (modification date of item theFile)
		--Make a copy of theFile with the extension ".new" .  
		--Make new copy of newFile with return chars replaced with uniqueString.
		set shellList to {copyItem(theFile, newFile, false), (perlSubstitution("\n", uniqueString, ¬
			newFile, newFile & ".new", caseBool, false) & ¬
			renameItem(newFile & ".new", fileName & ".new", false))}
		repeat with i from 1 to (count of shellList)
			set shellString to (shellString & LI(i, shellList))
		end repeat
		do shell script shellString
		
		--Check if searchString contains newline chars.  If so, replace with uniqueString:
		if (searchString contains "\r") or (searchString contains "\n") then
			set searchString to replace({"\r", "\n"}, uniqueString, searchString)
		end if
		--Replace searchString with replaceString and place changes in new file.
		--Replace uniqueString with newline chars in newFile.
		do shell script (perlSubstitution(searchString, replaceString, newFile, newFile & ".new", ¬
			caseBool, false) & perlSubstitution(uniqueString, "\n", newFile & ".new", newFile, ¬
			caseBool, false) & deleteItem(newFile & ".new", false))
	on error
		return false
	end try
	return modDate
end findReplaceToNewFile


--Formats and escapes each item in searchTexts into a regex pattern based on user's choiceNumber.
--Escapes special characters in each item in replaceTexts.
on formatSearchAndReplaceTexts()
	repeat with i from 1 to (count of searchTexts)
		--Format searchText into a regex pattern based on user's choice number:
		setLI(i, searchTexts, formattedSearchString(LI(i, choiceNums), LI(i, searchTexts), LI(i, cOrSs)))
		--Escape special characters in replaceText:
		setLI(i, replaceTexts, escapedStrings(LI(i, replaceTexts)))
	end repeat
end formatSearchAndReplaceTexts


-- This function decides which find&replace pattern to use, based on user's choice number.
--Returns the string pattern.  containedOrSeparate is the option of whether
--to replace searchString only in instances where it's a separate word, only
--in instances where it's contained inside a larger word, or all instances.  Values can be "Contained",
--"Separate", or "All".
on formattedSearchString(choiceNum, searchStrings, containedOrSeparate)
	if choiceNum = 0 then return false
	if containedOrSeparate is "All" then
		set cOrS to {"", ""}
	else if containedOrSeparate is "Separate" then
		--Use negative look-behind and negative look-ahead to
		--make sure there's no alphanumeric character just before
		--or after the searchString:
		set cOrS to {"(?<![a-zA-Z0-9])", "(?![a-zA-Z0-9])"}
	end if
	--Escape the strings in case they contain special characters:
	set searchStrings to escapedStrings(searchStrings)
	if (choiceNum < 3) then
		if (containedOrSeparate is "Contained") then return ¬
			containedPattern(searchStrings as text) --function stops.
		--Else, it's either "Separate" or "All", and the code is same for both:
		return (LI(1, cOrS) & (searchStrings as text) & LI(2, cOrS))
	end if
	--|begin(middle)end| find&replacing doesn't include the containedOrSeparate
	--option, since the |begin(middle)end| searchString can end up very long, and
	--it's unlikely you would ever need the precise control those options give you.
	--Using those options for this type of find&replace method can also
	--cause you to end up with unexpected results which are hard to control.
	if (choiceNum < 5) then return (LI(1, searchStrings) & "(.*?)" & LI(2, searchStrings))
	--returns a |begin(middle)end|Replace pattern.	
	if (choiceNum < 8) then
		if (choiceNum = 5) then return #Add a string pattern here
		if (choiceNum = 6) then return #Add a string pattern here
		if (choiceNum = 7) then return #Add a string pattern here
	end if
end formattedSearchString


--This returns a regex pattern containing the user-submitted searchString,
--and is needed when user wants to replace only instances of searchString
--that are contained inside longer words.
on containedPattern(searchString)
	set {negLkBehind, negLkAhead} to {"(?<![a-zA-Z0-9])", "(?![a-zA-Z0-9])"}
	set {posLkBehind, posLkAhead} to {"(?<=[a-zA-Z0-9])", "(?=[a-zA-Z0-9])"}
	return "((" & posLkBehind & "|" & negLkBehind & ")" & (searchString) & posLkAhead & "|" & ¬
		posLkBehind & (searchString) & "(" & posLkAhead & "|" & negLkAhead & "))"
end containedPattern




--Escape any special characters that might be in searchStrings. Returns a list of strings
--containing escaped characters.
on escapedStrings(searchStrings)
	set {specialChars, theList} to {{"$", "^", "*", "(", ")", "?", "+", "[", "|", ".", "/"}, {}}
	--If searchStrings is just a literal string then convert it to a list item:
	if class of searchStrings is text then
		set end of theList to searchStrings
		set searchStrings to theList
	end if
	--If the user includes backslashes in the searchStrings, applescript automatically
	--escapes them for us. But they need to be escaped again for the program to work,
	--meaning they're quadrupled in total:
	repeat with n from 1 to (count of searchStrings)
		setLI(n, searchStrings, replace("\\", "\\\\", LI(n, searchStrings)))
	end repeat
	--Create the shell string that will perform escaping:
	repeat with n from 1 to (count of searchStrings)
		set escapeScript to "echo " & quoted form of (LI(n, searchStrings) as text) & " | sed -E "
		--Append a command to deal specially with escaping the backslash character:
		set escapeScript to (escapeScript & "-e 's#\\\\#\\\\\\\\#g' ")
		--Use a loop to create the rest of the shell script:
		repeat with i from 1 to (count of specialChars)
			set escapeScript to (escapeScript & "-e 's#\\" & LI(i, specialChars)) ¬
				& "#\\\\" & LI(i, specialChars) & "#g' "
		end repeat
		--Run the shell and put resulting string inside searchStrings:
		setLI(n, searchStrings, do shell script escapeScript)
	end repeat
	return searchStrings
end escapedStrings



--This function returns a specially formatted date-time string, i.e, "140211-201412",
--named after the current date and time.
on currentDateTime()
	set curDate to (current date)
	set shortString to short date string of curDate
	set theList to explode(shortString, "/")
	if (count of (LI(1, theList))) = 1 then
		setLI(1, theList, ("0" & (item 1 of theList)))
	end if
	if (count of (item 2 of theList)) = 1 then
		setLI(2, theList, ("0" & (item 2 of theList)))
	end if
	set theDate to (item 3 of theList) & (item 1 of theList) & (item 2 of theList)
	set AppleScript's text item delimiters to ""
	set theTime to time of curDate
	set theHour to (round (theTime / 3600) rounding down) as string
	set hourInt to theHour as integer
	set hourSecs to (hourInt * 3600)
	set minSecs to ((theTime - hourSecs) / 60)
	set theMin to (round minSecs rounding down) as string
	set minInt to theMin as integer
	set minSecs to (minInt * 60)
	set theSec to (theTime - hourSecs - minSecs) as string
	if ((count of theHour) = 1) then set theHour to ("0" & theHour)
	if ((count of theMin) = 1) then set theMin to ("0" & theMin)
	if ((count of theSec) = 1) then set theSec to ("0" & theSec)
	return (theDate & "-" & theHour & theMin & theSec)
end currentDateTime




on getIndex(theItem, theList)
	if class of theList is not in {integer, real, text, list} then return false --function stops.
	--If theList is a number then coerce into text:
	if (count of theList) is 0 then set theList to (theList as text)
	if theItem is not in theList then return false -- function stops.
	--Else, theItem must be in theList, so:	
	set indexList to {}
	set itemLength to (count of (theItem as text))
	if (count of theList) is 1 then -- Then theItem IS theList.
		set end of indexList to 1
		return indexList -- function stops.
	end if
	if class of theList is list then
		repeat with i from 1 to count of theList
			if (theItem is (LI(i, theList))) then set end of indexList to i -- Appends number to end of list.
		end repeat
	else if class of theList is text then -- Then theItem is also text.
		set {theLimit, x, i} to {count of theList, 1, 1}
		set theItem to (theItem as text)
		repeat while theLimit > (itemLength - 1)
			if theItem is (characters i thru (i + itemLength - 1) of theList as text) then
				set end of indexList to i
			end if
			set i to (i + 1)
			set theLimit to (theLimit - 1)
		end repeat
	end if
	if indexList is {} then return false
	--Else:
	return indexList
end getIndex



--Replaces searchString with replaceString inside theString:
on replace(searchString, replaceString, theString)
	set item_list to explode(theString, searchString)
	set theResult to implode(item_list, replaceString)
	return theResult -- returns a new, modified string.
end replace



-- This function separates pieces of a string into list items, using theDelimit
-- as the separator. theDelimit can be either string or list of strings.
on explode(theString, theDelimit)
	set origDelimit to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimit
	set theResult to every text item of theString
	set AppleScript's text item delimiters to origDelimit
	return theResult
end explode


--This function re-assembles a list of strings into a single string,
--using theDelimit as glue to reconnect each string.  theDelimit must be a string.
on implode(textlist, theDelimit)
	set origDelimit to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimit
	set theString to (textlist as string)
	set AppleScript's text item delimiters to origDelimit
	return theString
end implode


--This function is just for creating a short-hand way of accessing a list item.
--ItemNum can be a single integer, or a list of two integers for accessing a range of items:
on LI(itemNum, theList)
	if class of itemNum is integer then
		return (item itemNum of theList)
	else if class of itemNum is list then
		return (items (item 1 of itemNum as integer) thru ¬
			(item 2 of itemNum as integer) of theList)
	end if
end LI


--This function is for assigning a value to a list item:
on setLI(itemNum, theList, theValue)
	set item itemNum of theList to theValue
end setLI



--Removes items that are empty strings from theList.
on removeEmptyStrings(theList)
	set newList to {}
	repeat with i from 1 to (count of theList)
		if LI(i, theList) is not "" then
			set end of newList to LI(i, theList)
		end if
	end repeat
	return newList
end removeEmptyStrings



--theItem must be a colon-delimited path string. 
--Example: "Macintosh HD:Users:Username:Desktop:Filename.txt"
--Returns theItem's name (without the full path) as string.
on itemName(theItem)
	set theParts to explode(theItem, ":")
	if theParts ends with "" then set theParts to LI({1, -2}, theParts)
	return LI(count of theParts, theParts) as text
end itemName


--Returns a shell string changing the working directory to the one 
--containing theItem.  theItem must be a colon-delimited path string. 
--Example: "Macintosh HD:Users:Username:Desktop:Filename.txt"
on itemLocation(theItem)
	set theItem to POSIX path of theItem
	if theItem ends with "/" then set theItem to (characters 1 thru -2 of theItem)
	set theParts to explode(theItem, "/")
	set theDirectory to (implode(LI({1, -2}, theParts), "/"))
	return "cd " & quoted form of theDirectory & " \n "
end itemLocation



--theItem and newLocationName must both be colon-delimited path strings.
on moveItem(theItem, newLocationName, runBool)
	set newLocationName to POSIX path of newLocationName
	if newLocationName ends with "/" then ¬
		set newLocationName to (characters 1 thru -2 of newLocationName)
	set theName to itemName(theItem)
	set shellString to (itemLocation(theItem) & "mv " & (quoted form of theName) & ¬
		" " & (quoted form of (newLocationName)) & " \n ")
	if runBool is false then return shellString
	do shell script shellString
end moveItem


--theItem must be colon-delimited path string.
on deleteItem(theItem, runBool)
	set theItem to POSIX path of theItem
	set shellString to ("rm " & (quoted form of theItem) & " \n ")
	if runBool is false then return shellString
	do shell script shellString
end deleteItem


--theItem and newLocationName must both be colon-delimited path strings.
on copyItem(theItem, newLocationName, runBool)
	set theName to itemName(theItem)
	set theItem to POSIX path of theItem
	set newLocationName to POSIX path of newLocationName
	set shellString to ("cp " & (quoted form of theItem) & ¬
		" " & (quoted form of newLocationName) & " \n ")
	if runBool is false then return shellString
	do shell script shellString
end copyItem


--theItem must be colon-delimited path to theItem.  newName is just the name without the path.
on renameItem(theItem, newName, runBool)
	set theName to itemName(theItem)
	set shellString to (itemLocation(theItem) & "mv " & (quoted form of theName) & " " & ¬
		(quoted form of newName) & " \n ")
	if runBool is false then return shellString
	do shell script shellString
end renameItem


--newFolderPath must be entire string path.
on makeFolder(newFolderPath)
	set newFolderPath to POSIX path of newFolderPath
	do shell script "mkdir " & (quoted form of newFolderPath)
end makeFolder


on perlSubstitution(searchString, replaceString, oldFile, newFile, caseBool, runBool)
	set {oldFileName, newFileName, perlCase} to {itemName(oldFile), itemName(newFile), "i"}
	if caseBool is true then set perlCase to ""
	set shellString to itemLocation(oldFile) & "perl -pe " & (quoted form of ("s/" & ¬
		searchString & "/" & replaceString & "/" & perlCase & "g")) & " " & ¬
		(quoted form of oldFileName) & " > " & (quoted form of newFileName) & " \n "
	if runBool is false then return shellString
	do shell script shellString
end perlSubstitution



--This function returns a specially formatted date-time string, i.e, "140211-201412".
--It requires a date object as argument, which you can get from item properties
--in Finder and System Events.
on makeDateTime(dateObject)
	set shortString to short date string of dateObject
	set theList to explode(shortString, "/")
	if (count of (LI(1, theList))) = 1 then
		setLI(1, theList, ("0" & (item 1 of theList)))
	end if
	if (count of (item 2 of theList)) = 1 then
		setLI(2, theList, ("0" & (item 2 of theList)))
	end if
	set theDate to (item 3 of theList) & (item 1 of theList) & (item 2 of theList)
	set AppleScript's text item delimiters to ""
	set theTime to time of dateObject
	set theHour to (round (theTime / 3600) rounding down) as string
	set hourInt to theHour as integer
	set hourSecs to (hourInt * 3600)
	set minSecs to ((theTime - hourSecs) / 60)
	set theMin to (round minSecs rounding down) as string
	set minInt to theMin as integer
	set minSecs to (minInt * 60)
	set theSec to (theTime - hourSecs - minSecs) as string
	if ((count of theHour) = 1) then set theHour to ("0" & theHour)
	if ((count of theMin) = 1) then set theMin to ("0" & theMin)
	if ((count of theSec) = 1) then set theSec to ("0" & theSec)
	return (theDate & "-" & theHour & theMin & theSec) as string
end makeDateTime




--originalFile is full path string.  dateObject is a date string created by System Events.
on makeFolderAndMoveOriginalFile(originalFile, dateObject)
	set origFileName to itemName(originalFile)
	set locationPath to implode(LI({1, -2}, explode(originalFile as text, ":")), ":")
	set versionsFolder to locationPath & ":" & origFileName & "__versions"
	tell application "System Events"
		--First get the location of originalFile:
		set theLocation to container of (originalFile as alias)
		--Make sure folder "versions" exists in same folder as specified item.
		if not (exists folder (origFileName & "__versions") of theLocation) ¬
			then my makeFolder(versionsFolder)
	end tell
	set fileToMove to makeDateTime(dateObject) & "__" & origFileName
	--Rename original file so it's preprended with date and time saved,
	--and rename new item so it now has original file's name:
	do shell script (renameItem(originalFile, fileToMove, false) & ¬
		renameItem(originalFile & ".new", origFileName, false))
	--Move original file into the versions folder:
	moveItem(locationPath & ":" & fileToMove, versionsFolder, true)
end makeFolderAndMoveOriginalFile


on countCheck(theLists)
	set x to count of LI(1, theLists)
	repeat with i from 2 to count of theLists
		if ((count of LI(i, theLists)) ≠ x) then return false
	end repeat
	return true
end countCheck


on userApprovesSettings()
	--format the summary into a string by looping thru settingsList:
	set summaryString to ""
	repeat with i from 1 to (count of choiceNums)
		if LI(i, choiceNums) < 3 then --then search is specific.
			set searchText to "\"" & LI(i, searchTexts) & "\""
			set containedOrSeparate to (return & "Context: " & LI(i, cOrSs))
		else --then it's a begin(anything)end search.
			set searchText to "\"" & LI(1, LI(i, searchTexts)) & "\"  to  \"" & LI(2, LI(i, searchTexts)) & "\""
			set containedOrSeparate to ""
		end if
		set replaceText to "\"" & LI(i, replaceTexts) & "\""
		set theLine to "Choice: " & LI(LI(i, choiceNums), choiceList) & return & "Replace  " & searchText & ¬
			"  with  " & replaceText & containedOrSeparate & return & return
		
		set summaryString to (summaryString & theLine)
		
	end repeat
	display dialog "Is this what you wanted?" & return & return & summaryString buttons ¬
		{"Cancel", "No, Start Again", "Yes"} default button 3
	if button returned of result is "Yes" then
		return true
	else
		return false
	end if
end userApprovesSettings


Model: 2009 mac mini
AppleScript: 2.5
Browser: Safari 9.1.2
Operating System: Mac OS X (10.10)