Improve Website or CD DVD HTML.fh_lua

--[[
@Title:			Improve Website or CD DVD HTML
@Type:				Standard
@Author:			Mike Tate
@Contributors:	
@Version:			3.2
@Keywords:		
@LastUpdated:	18 Nov 2021
@Licence:			This plugin is copyright (c) 2020 Mike Tate & contributors and is licensed under the MIT License which is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@Description:	Ensure user URL are hyperlinks, abbreviate long URL, offer alternative image popups, add Table of Contents pages, add alternate & married names and ids to Index of Names, and create GenDex file.
@Version Log:	See end of file.
]]

if fhGetAppVersion() > 5 then fhSetStringEncoding("UTF-8") end

--[[
@Title:			aa Library Functions Preamble
@Author:			Mike Tate
@Version:			3.1
@LastUpdated:	22 Jun 2021
@Description:	All the library functions prototype closures for Plugins.
]]

--[[
@Module:			+fh+stringx_v3
@Author:			Mike Tate
@Version:			3.0
@LastUpdated:	19 Sep 2020
@Description:	Extended string functions to supplement LUA string library.
@V3.0:				Function Prototype Closure version with Lua 5.1 & 5.3 comaptibility; Added inert(strTxt) function;
@V2.5:				Support FH V6 Encoding = UTF-8;
@V2.4:				Tolerant of integer & nil parameters just link match & gsub;
@V1.0:				Initial version.
]]

local function stringx_v3()

	local fh = {}									-- Local environment table

	-- Supply current file encoding format --
	function fh.encoding()
		if fhGetAppVersion() > 5 then return fhGetStringEncoding() end
		return "ANSI"
	end -- function encoding

	-- Split a string using "," or chosen separator --
	function fh.split(strTxt,strSep)
		local tblFields = {}
		local strPattern = string.format("([^%s]+)", strSep or ",")
		strTxt = tostring(strTxt or "")
		strTxt:gsub(strPattern, function(strField) tblFields[#tblFields+1] = strField end)
		return tblFields
	end -- function split

	-- Split a string into numbers using " " or "," or "x" separators	-- Any non-number remains as a string
	function fh.splitnumbers(strTxt)
		local tblNum = {}
		strTxt = tostring(strTxt or "")
		strTxt:gsub("([^ ,x]+)", function(strNum) tblNum[#tblNum+1] = tonumber(strNum) or strNum end)
		return tblNum
	end -- function splitnumbers

	local strMagic = "([%^%$%(%)%%%.%[%]%*%+%-%?])"							-- UTF-8 replacement for "(%W)"

	-- Hide magic pattern symbols	^ $ ( ) % . [ ] * + - ?
	function fh.plain(strTxt)
		-- Prefix every magic pattern character with a % escape character,
		-- where %% is the % escape, and %1 is the original character capture.
		strTxt = tostring(strTxt or ""):gsub(strMagic,"%%%1")
		return strTxt
	end -- function plain

	-- matches is plain text version of string.match()
	function fh.matches(strTxt,strFind,intInit)
		strFind = tostring(strFind or ""):gsub(strMagic,"%%%1")			-- Hide magic pattern symbols
		return tostring(strTxt or ""):match(strFind,tonumber(intInit))
	end -- function matches

	-- replace is plain text version of string.gsub()
	function fh.replace(strTxt,strOld,strNew,intNum)
		strOld = tostring(strOld or ""):gsub(strMagic,"%%%1")				-- Hide magic pattern symbols
		return tostring(strTxt or ""):gsub(strOld,function() return strNew end,tonumber(intNum))	-- Hide % capture symbols
	end -- function replace

	-- Hide % escape/capture symbols in replacement so they are inert
	function fh.inert(strTxt)
		strTxt = tostring(strTxt or ""):gsub("%%","%%%%")					-- Hide all % symbols
		return strTxt
	end -- function inert

	-- convert is pattern without captures version of string.gsub()
	function fh.convert(strTxt,strOld,strNew,intNum)
		return tostring(strTxt or ""):gsub(tostring(strOld or ""),function() return strNew end,tonumber(intNum))	-- Hide % capture symbols
	end -- function convert

	local dicUpper = { }
	local dicLower = { }
	local dicCaseX = { }
	-- ASCII unaccented letter translations for Upper, Lower, and Case Insensitive
	for intUpper = string.byte("A"), string.byte("Z") do
		local strUpper = string.char(intUpper)
		local strLower = string.char(intUpper - string.byte("A") + string.byte("a"))
		dicUpper[strLower] = strUpper
		dicLower[strUpper] = strLower
		local strCaseX = "["..strUpper..strLower.."]"
		dicCaseX[strLower] = strCaseX
		dicCaseX[strUpper] = strCaseX
	end

	-- Supply character length of ANSI text --
	function fh.length(strTxt)
		return string.len(strTxt or "")
	end -- function length

	-- Supply character substring of ANSI text --
	function fh.substring(strTxt,i,j)
		return string.sub(strTxt or "",i,j)
	end -- function substring

	-- Translate upper/lower case ANSI letters to pattern that matches both --
	function fh.caseless(strTxt)
		strTxt = tostring(strTxt or ""):gsub("[A-Za-z]",dicCaseX)
		return strTxt
	end -- function caseless

	if fh.encoding() == "UTF-8" then

		-- Supply character length of UTF-8 text --
		function fh.length(strTxt)
			isFlag = fhIsConversionLossFlagSet()
			strTxt = fhConvertUTF8toANSI(strTxt or "")
			fhSetConversionLossFlag(isFlag)
			return string.len(strTxt)
		end -- function length

		local strUTF8 = "([%z\1-\127\194-\244][\128-\191]*)"			-- Cater for Lua 5.1 %z or Lua 5.3 \0
		if fhGetAppVersion() > 6 then
			strUTF8 = "([\0-\127\194-\244][\128-\191]*)"
		end

		-- Supply character substring of UTF-8 text --
		function fh.substring(strTxt,i,j)
			local strSub = ""
			j = j or -1
			if j < 0 then j = j + length(strTxt) + 1 end
			if i < 0 then i = i + length(strTxt) + 1 end
			for strChr in string.gmatch(strTxt or "",strUTF8) do
				if j <= 0 then break end
				j = j - 1
				i = i - 1
				if i <= 0 then strSub = strSub..strChr end
			end
			return strSub
		end -- function substring

		-- Translate lower case to upper case UTF-8 letters --
		function fh.upper(strTxt)
			strTxt = tostring(strTxt or ""):gsub("([a-z\194-\244][\128-\191]*)",dicUpper)
			return strTxt
		end -- function upper

		-- Translate upper case to lower case UTF-8 letters --
		function fh.lower(strTxt)
			strTxt = tostring(strTxt or ""):gsub("([A-Z\194-\244][\128-\191]*)",dicLower)
			return strTxt
		end -- function lower

		-- Translate upper/lower case UTF-8 letters to pattern that matches both --
		function fh.caseless(strTxt)
			strTxt = tostring(strTxt or ""):gsub("([A-Za-z\194-\244][\128-\191]*)",dicCaseX)
			return strTxt
		end -- function caseless

		-- Following tables use ASCII numeric coding to be immune from ANSI/UTF-8 encoding --

		local arrPairs =	-- Upper & Lower case groups of UTF-8 letters with same prefix --
		{--	{ Prefix; Beg ; End ; Inc; Offset Upper > Lower };			-- These include all ANSI letters and many more
			{ "\195"; 0x80; 0x96;  1 ; 32 };								-- 195=0xC3 À U+00C0 to Ö U+00D6 and à U+00E0 to ö U+00F6
			{ "\195"; 0x98; 0x9E;  1 ; 32 };								-- 195=0xC3 Ø U+00D8 to Þ U+00DE and ø U+00F8 to þ U+00FE
			{ "\196"; 0x80; 0xB6;  2 ;  1 };								-- 196=0xC4 A U+0100 to k U+0137 in pairs
			{ "\196"; 0xB9; 0xBD;  2 ;  1 };								-- 196=0xC4 L U+0139 to l U+013E in pairs
			{ "\197"; 0x81; 0x87;  2 ;  1 };								-- 197=0xC5 L U+0141 to n U+0148 in pairs
			{ "\197"; 0x8A; 0xB6;  2 ;  1 };								-- 197=0xC5 ? U+014A to y U+0177 in pairs
			{ "\197"; 0xB9; 0xBD;  2 ;  1 };								-- 197=0xC5 Z U+0179 to ž U+017E in pairs
			{ "\198"; 0x82; 0x84;  2 ;  1 };								-- 198=0xC6 ?  U+0182 to ?  U+0185 in pairs
			-- Add more Unicode groups here as usage increases --
		}
		local dicPairs =	-- Upper v Lower case UTF-8 letters that don't fit groups above --
		{	[string.char(0xC4,0xBF)] = string.char(0xC5,0x80);			-- ? U+013F and ? U+0140
			[string.char(0xC5,0xB8)] = string.char(0xC3,0xBF);			-- Ÿ U+0178 and ÿ U+00FF
		}

		local intBeg1 = string.byte(string.sub("À",1))
		local intBeg2 = string.byte(string.sub("À",2))
		local intEnd1 = string.byte(string.sub("Z",1))
		local intEnd2 = string.byte(string.sub("Z",2))
	--	print(string.format("%#x %#x %#x %#x",intBeg1,intBeg2,intEnd1,intEnd2)) -- Useful to work out numeric coding

		-- Populate the UTF-8 letter translation dictionaries --
		for intGroup, tblGroup in ipairs ( arrPairs ) do				-- UTF-8 accented letter groups
			local strPrefix = tblGroup[1]
			for intUpper = tblGroup[2], tblGroup[3], tblGroup[4] do
				local strUpper = string.char(intUpper)
				local strLower = string.char(intUpper + tblGroup[5])
				local strCaseX = strPrefix.."["..strUpper..strLower.."]"
				strUpper = strPrefix..strUpper
				strLower = strPrefix..strLower
				dicUpper[strLower] = strUpper
				dicLower[strUpper] = strLower
				dicCaseX[strLower] = strCaseX
				dicCaseX[strUpper] = strCaseX
			end
		end
		for strUpper, strLower in pairs ( dicPairs ) do					-- UTF-8 accented letters where upper & lower have different prefix
			dicUpper[strLower] = strUpper
			dicLower[strUpper] = strLower
			local strCaseX = ""
			for intByte = 1, #strUpper do									-- Matches more than just the two letters, but can't do any better
				strCaseX = strCaseX.."["..strUpper:sub(intByte,intByte)..strLower:sub(intByte,intByte).."]"
			end
			dicCaseX[strLower] = strCaseX
			dicCaseX[strUpper] = strCaseX
		end

	end

	-- overload fh functions into string table
	for strIndex, anyValue in pairs(fh) do
		if type(anyValue) == "function" then
			string[strIndex] = anyValue
		end
	end

	return fh

end -- local function stringx_v3

local stringx = stringx_v3()													-- To access FH string extension module

--[[
@Module:			+fh+tablex_v3
@Author:			Mike Tate
@Version:			3.0
@LastUpdated:	25 Aug 2020
@Description:	A Table Load Save Module.
@V3.0:				Function Prototype Closure version.
@V1.2:				Added local definitions of _ to ensure nil gets returned on error.
@V1.1:				?
@V1.0:				Initial version 0.94 is Lua 5.1 compatible.
]]

local function tablex_v3()

	local fh = {}									-- Local environment table

	------------------------------------------------------ Start Table Load Save
	-- require "_tableloadsave"
	--[[
   Save Table to File/Stringtable
   Load Table from File/Stringtable
   v 0.94
   
   Lua 5.1 compatible
   
   Userdata and indices of these are not saved
   Functions are saved via string.dump, so make sure it has no upvalues
   References are saved
   ----------------------------------------------------
   table.save( table [, filename] )
   
   Saves a table so it can be called via the table.load function again
   table must a object of type 'table'
   filename is optional, and may be a string representing a filename or true/1
   
   table.save( table )
      on success: returns a string representing the table (stringtable)
      (uses a string as buffer, ideal for smaller tables)
   table.save( table, true or 1 )
      on success: returns a string representing the table (stringtable)
      (uses io.tmpfile() as buffer, ideal for bigger tables)
   table.save( table, "filename" )
      on success: returns 1
      (saves the table to file "filename")
   on failure: returns as second argument an error msg
   ----------------------------------------------------
   table.load( filename or stringtable )
   
   Loads a table that has been saved via the table.save function
   
   on success: returns a previously saved table
   on failure: returns as second argument an error msg
   ----------------------------------------------------
   
   chillcode, http://lua-users.org/wiki/SaveTableToFile
   Licensed under the same terms as Lua itself.
	]]--

   -- declare local variables
   --// exportstring( string )
   --// returns a "Lua" portable version of the string
   local function exportstring( s )
      s = string.format( "%q",s )
      -- to replace
      s = string.gsub( s,"\\\n","\\n" )
      s = string.gsub( s,"\r","\\r" )
      s = string.gsub( s,string.char(26),"\"..string.char(26)..\"" )
      return s
   end
--// The Save Function
function fh.save( tbl,filename )
   local charS,charE = "   ","\n"
   local file,err,_ 					-- V1.2 -- Added _
   -- create a pseudo file that writes to a string and return the string
   if not filename then
      file =  { write = function( self,newstr ) self.str = self.str..newstr end, str = "" }
      charS,charE = "",""
   -- write table to tmpfile
   elseif filename == true or filename == 1 then
      charS,charE,file = "","",io.tmpfile()
   -- write table to file
   -- use io.open here rather than io.output, since in windows when clicking on a file opened with io.output will create an error
   else
      file,err = io.open( filename, "w" )
      if err then return _,err end
   end
   -- initiate variables for save procedure
   local tables,lookup = { tbl },{ [tbl] = 1 }
   file:write( "return {"..charE )
   for idx,t in ipairs( tables ) do
      if filename and filename ~= true and filename ~= 1 then
         file:write( "-- Table: {"..idx.."}"..charE )
      end
      file:write( "{"..charE )
      local thandled = {}
      for i,v in ipairs( t ) do
         thandled[i] = true
         -- escape functions and userdata
         if type( v ) ~= "userdata" then
            -- only handle value
            if type( v ) == "table" then
               if not lookup[v] then
                  table.insert( tables, v )
                  lookup[v] = #tables
               end
               file:write( charS.."{"..lookup[v].."},"..charE )
            elseif type( v ) == "function" then
               file:write( charS.."loadstring("..exportstring(string.dump( v )).."),"..charE )
            else
               local value =  ( type( v ) == "string" and exportstring( v ) ) or tostring( v )
               file:write(  charS..value..","..charE )
            end
         end
      end
      for i,v in pairs( t ) do
         -- escape functions and userdata
         if (not thandled[i]) and type( v ) ~= "userdata" then
            -- handle index
            if type( i ) == "table" then
               if not lookup[i] then
                  table.insert( tables,i )
                  lookup[i] = #tables
               end
               file:write( charS.."[{"..lookup[i].."}]=" )
            else
               local index = ( type( i ) == "string" and "["..exportstring( i ).."]" ) or string.format( "[%d]",i )
               file:write( charS..index.."=" )
            end
            -- handle value
            if type( v ) == "table" then
               if not lookup[v] then
                  table.insert( tables,v )
                  lookup[v] = #tables
               end
               file:write( "{"..lookup[v].."},"..charE )
            elseif type( v ) == "function" then
               file:write( "loadstring("..exportstring(string.dump( v )).."),"..charE )
            else
               local value =  ( type( v ) == "string" and exportstring( v ) ) or tostring( v )
               file:write( value..","..charE )
            end
         end
      end
      file:write( "},"..charE )
   end
   file:write( "}" )
   -- Return Values
   -- return stringtable from string
   if not filename then
      -- set marker for stringtable
      return file.str.."--|"
   -- return stringttable from file
   elseif filename == true or filename == 1 then
      file:seek ( "set" )
      -- no need to close file, it gets closed and removed automatically
      -- set marker for stringtable
      return file:read( "*a" ).."--|"
   -- close file and return 1
   else
      file:close()
      return 1
   end
end

--// The Load Function
function fh.load( sfile )
   local tables,err,_ 					-- V1.2 -- Added _
   -- catch marker for stringtable
   if string.sub( sfile,-3,-1 ) == "--|" then
      tables,err = loadstring( sfile )
   else
      tables,err = loadfile( sfile )
   end
   if err then return _,err
   end
   tables = tables()
   for idx = 1,#tables do
      local tolinkv,tolinki = {},{}
      for i,v in pairs( tables[idx] ) do
         if type( v ) == "table" and tables[v[1]] then
            table.insert( tolinkv,{ i,tables[v[1]] } )
         end
         if type( i ) == "table" and tables[i[1]] then
            table.insert( tolinki,{ i,tables[i[1]] } )
         end
      end
      -- link values, first due to possible changes of indices
      for _,v in ipairs( tolinkv ) do
         tables[idx][v[1]] = v[2]
      end
      -- link indices
      for _,v in ipairs( tolinki ) do
         tables[idx][v[2]],tables[idx][v[1]] =  tables[idx][v[1]],nil
      end
   end
   return tables[1]
end

------------------------------------------------------ End Table Load Save

	-- overload fh functions into table
	for strIndex, anyValue in pairs(fh) do
		if type(anyValue) == "function" then
			table[strIndex] = anyValue
		end
	end

	return fh

end -- local function tablex_v3

local tablex  = tablex_v3 ()													-- To access FH table extension module

--[[
@Module:			+fh+iterate_v3
@Author:			Mike Tate
@Version:			3.0
@LastUpdated:	25 Aug 2020
@Description:	An iterater functions module to supplement LUA functions.
@V3.0:				Function Prototype Closure version.
@V1.2:				RecordTypes() includes HEAD tag.
@V1.1:				?
@V1.0:				Initial version.
]]

local function iterate_v3()

	local fh = {}																-- Local environment table

	-- Iterator for all records of one chosen type --
	function fh.Records(strType)
		local ptrAll = fhNewItemPtr()										-- Pointer to all records in turn
		local ptrRec = fhNewItemPtr()										-- Pointer to record returned to user
		ptrAll:MoveToFirstRecord(strType)
		return function ()
			ptrRec:MoveTo(ptrAll)
			ptrAll:MoveNext()
			if ptrRec:IsNotNull() then return ptrRec end
		end
	end -- function Records

	-- Iterator for all the record types --
	function fh.RecordTypes()
		local intNext = -1														-- Next record type number
		local intLast = fhGetRecordTypeCount()								-- Last record type number
		return function()
			intNext = intNext + 1
			if intNext == 0 then												-- Includes HEAD tag -- V1.2
				return "HEAD"
			elseif intNext <= intLast then
				return fhGetRecordTypeTag(intNext)							-- Return record type tag
			end
		end
	end -- function RecordTypes

	-- Iterator for all items in all records of chosen types --
	function fh.Items(...)
		local arg = {...}
		local intType = 1														-- Integer record type number
		local tblType = {}														-- Table of record type tags
		local ptrNext = fhNewItemPtr()										-- Pointer to next item in turn
		local ptrItem = fhNewItemPtr()										-- Pointer to item returned to user

		if #arg == 0 then
			for intType = 1, fhGetRecordTypeCount() do					-- No parameters so use all record types
				tblType[intType] = fhGetRecordTypeTag(intType)
			end
		else
			tblType = arg														-- Got parameters so use them instead
		end
	--	print(tblType[intType],intType)
		ptrNext:MoveToFirstRecord(tblType[intType])						-- Get first record of first type

		return function()
			repeat
				while ptrNext:IsNotNull() do									-- Loop through all items
					ptrItem:MoveTo(ptrNext)
					ptrNext:MoveNextSpecial()
					if ptrItem:IsNotNull() then return ptrItem end
				end
				intType = intType + 1											-- Loop through each record type
				if intType <= #tblType then
					ptrNext:MoveToFirstRecord(tblType[intType])
				end
			until intType > #tblType
		end
	end -- function Items

	-- Iterator for all facts of an individual --
	function fh.Facts(ptrIndi)
		local ptrItem = fhNewItemPtr()										-- Pointer to each item at level 1
		local ptrFact = fhNewItemPtr()										-- Pointer to each fact returned to user
		ptrItem:MoveToFirstChildItem(ptrIndi)
		return function ()
			while ptrItem:IsNotNull() do
				ptrFact:MoveTo(ptrItem)
				ptrItem:MoveNext()
				if fhIsFact(ptrFact) then return ptrFact end
			end
		end
	end -- function Facts

	return fh

end -- local function iterate_v3

local iterate = iterate_v3()													-- To access FH iterate items module

--[[
@Module:			+fh+general_v3
@Author:			Mike Tate
@Version:			3.0
@LastUpdated:	25 Aug 2020
@Description:	A general functions module to supplement LUA functions, where all filenames are in ANSI.
@V3.0:				Function Prototype Closure version; GetDayNumber() error message reasons;
@V1.5:				Revised SplitFilename(strFilename) for missing extension.
@V1.4:				Revised EstimatedBirthDates() & EstimatedDeathDates() to fix null Dates.
@V1.3:				Add GetDayNumber(), EstimatedBirthDates(), EstimatedDeathDates().
@V1.2:				SplitFilename() updated for directory only paths, and MakeFolder() added.
@V1.1:				pl.path experiment revoked. New DirTree with omit branch option. Avoid using stringx_v2.
@V1.0:				Initial version.
]]

local function general_v3()

	local fh = {}													-- Local environment table

	require "lfs"													-- To access LUA filing system

	if fhGetAppVersion() > 6 then
		unpack = table.unpack
	end

	-- Check if file exists --
	function fh.FlgFileExists(strFileName)
		return lfs.attributes(strFileName,"mode") == "file"
		--[=[
		if lfs.attributes(strFileName,"mode") == "file" then
			return true
		else
			return false
		end
		--]=]
	end -- function FlgFileExists

	-- Check if folder exists --
	function fh.FlgFolderExists(strFolderName)
		return lfs.attributes(strFolderName:gsub("\\$",""),"mode") == "directory"
		--[=[
		if lfs.attributes(strFolderName:gsub("\\$",""),"mode") == "directory" then
			return true
		else
			return false
		end
		--]=]
	end -- function FlgFolderExists

	-- Check if folder writable --
	function fh.FlgFolderWrite(strFolderName)
		if fh.FlgFolderExists(strFolderName) then
			local fileHandle, strError = io.open(strFolderName.."\\xyz.xyz","w")
			if fileHandle ~= nil then
				fileHandle:close()
				os.remove(strFolderName.."\\xyz.xyz")
				return true
			end
		end
		return false
	end -- function FlgFolderWrite

	-- Open File and return Handle --
	function fh.OpenFile(strFileName,strMode)
		local fileHandle, strError = io.open(strFileName,strMode)
		if fileHandle == nil then
			error("\n Unable to open file in \""..strMode.."\" mode. \n "..strFileName.." \n "..strError.." \n")
		end
		return fileHandle
	end -- function OpenFile

	-- Save string to file --
	function fh.SaveStringToFile(strString,strFileName)
		local fileHandle = fh.OpenFile(strFileName,"w")
		fileHandle:write(strString)
		assert(fileHandle:close())
	end -- function SaveStringToFile

	-- Load string from file --
	function fh.StrLoadFromFile(strFileName)
		local fileHandle = fh.OpenFile(strFileName,"r")
		local strString = fileHandle:read("*all")
		assert(fileHandle:close())
		return strString
	end -- function StrLoadFromFile

	-- Returns the Path, Filename, and Extension as 3 values
	function fh.SplitFilename(strFilename)
		if lfs.attributes(strFilename,"mode") == "directory" then
			local strPath = strFilename:gsub("[\\/]$","")
			return strPath.."\\","",""
		end
		strFilename = strFilename.."."
		return strFilename:match("^(.-)([^\\/]-%.([^\\/%.]-))%.?$")
	end -- function SplitFilename

	-- Return a Directory Tree entry & attributes on each iteration --
	function fh.DirTree(strDir,...)
		local arg = {...}
		assert(strDir and strDir ~= "", "directory parameter is missing or empty")
		if strDir:sub(-1) == "/"
		or strDir:sub(-1) == "\\" then
			strDir = strDir:sub(1,-2)								-- Remove trailing "/" or "\"
		end
    
		local function doYieldTree(strDir)
			for strEntry in lfs.dir(strDir) do
				if strEntry ~= "." and strEntry ~= ".." then
					strEntry = strDir.."\\"..strEntry
					local tblAttr, strError = lfs.attributes(strEntry)
					if not tblAttr then tblAttr = { mode="attrfail"; error=strError; } end 
					coroutine.yield(strEntry,tblAttr)
					if tblAttr.mode == "directory" then
						local isOK = true
						for intOmit, strOmit in ipairs (arg) do
							if strEntry:matches(strOmit) then	-- Omit tree branch
								isOK = false
								break
							end
						end
						if isOK then doYieldTree(strEntry) end
					end
				end
			end
		end -- local function doYieldTree

		return coroutine.wrap(function() doYieldTree(strDir) end)
	end -- function DirTree

	local function strErrorText(strError,strFileName,intRepeat)
		return strError:gsub(strFileName:match("(.+\\).+"),"Del#"..tostring(intRepeat)..":")
	end -- local function strErrorText

	-- Delete file if it exists --
	function fh.DeleteFile(strFileName,errFunction)
		if fh.FlgFileExists(strFileName) then
			local fileHandle, strError = os.remove(strFileName)
			if fileHandle == nil then
				local intRepeat = 1
				repeat
					if intRepeat > 1 and type(errFunction) == "function" then
						errFunction(strErrorText(strError,strFileName,intRepeat))
					end
					fhSleep(300,100)
					if fh.FlgFileExists(strFileName) then
						fileHandle, strError = os.remove(strFileName)
					end
					intRepeat = intRepeat + 1
				until fileHandle ~= nil or intRepeat > 10
				if intRepeat > 10 then error(strErrorText(strError,strFileName,intRepeat)) end
			end
		end
	end -- function DeleteFile

	-- Make subfolder if does not exist --
	function fh.MakeFolder(strFolder,errFunction)
		if not fh.FlgFolderExists(strFolder) then
			local isOK, strError = lfs.mkdir(strFolder)
			if not isOK then
				local strMessage = "Cannot Make Folder: "..tostring(strError)..".\n"..strFolder.."\n"
				if type(errFunction) == "function" then
					errFunction(strMessage)
				else
					error(strMessage)
				end
				return false
			end
		end
		return true
	end -- function MakeFolder

	-- Invoke FH Shell Execute API --
	function fh.DoExecute(strExecutable,...)
		local arg = {...}
		local errFunction = fhMessageBox
		if type(arg[#arg]) == 'function' then
			errFunction = arg[#arg]
			table.remove(arg)
		end
		if fhGetAppVersion() > 5 and fhGetStringEncoding() == "UTF-8" then
			strExecutable = fhConvertANSItoUTF8(strExecutable)
		end
		local isOK, intErrorCode, strErrorText = fhShellExecute(strExecutable,unpack(arg))
		if not isOK then
			errFunction(tostring(strErrorText).." ("..tostring(intErrorCode)..")")
		end
		return isOK
	end -- function DoExecute

	-- Obtain the Day Number for any Date Point --									-- Fix problems with invalid dates in DayNumber function
	function fh.GetDayNumber(datDate)
		if datDate:IsNull() then return 0 end
		local intDay = fhCallBuiltInFunction("DayNumber",datDate) 				-- Only works for Gregorian dates that were not skipped nor BC dates
		if not intDay then
			local strError = "because "													-- Error message reason	-- V3.0
			local calendar = datDate:GetCalendar()
			local oldMonth = datDate:GetMonth()
			local oldDayNo = datDate:GetDay()
			local intMonth = math.min( oldMonth, 12 ) 								-- Limit month to 12, and day to last of each month
			local intDayNo = math.min( oldDayNo, ({0;31;28;31;30;31;30;31;31;30;31;30;31;})[intMonth+1] )
			local intYear  = datDate:GetYear()
			if oldDayNo > intDayNo then strError = strError.."day "..oldDayNo.." too big "   end
			if oldMonth > intMonth then strError = strError.."month "..oldMonth.." too big " end
			if calendar == "Hebrew" and intYear > 3761 then
				intYear = intYear - 3761
				strError = strError.."Hebrew year > 3761 "
			elseif calendar ~= "Gregorian" then
				strError = strError..calendar.." disallowed "
			end
			if     intYear == 1752 and intMonth ==  9 and intDayNo <= 13 then	-- Use 2 Sep 1752 for 3 - 13 Sep 1752 dates skipped
				intDayNo = 2
				strError = strError.."3 - 13 Sep 1752 skipped "
			elseif intYear == 1582 and intMonth == 10 and intDayNo <= 14 then	-- Use 4 Oct 1582 for 5 - 14 Oct 1582 dates skipped
				intDayNo = 4
				strError = strError.."5 - 14 Oct 1582 skipped "
			end	
			local setDate = fhNewDatePt(intYear,intMonth,intDayNo,datDate:GetYearDD())
			intDay = fhCallBuiltInFunction("DayNumber",setDate) 					-- Remove BC and Julian, Hebrew, French calendars
			if not intDay then intDay = 0 end
			local oldDate = fhNewDate()		oldDate:SetSimpleDate(datDate)		-- Report problem to user
			local newDate = fhNewDate()		newDate:SetSimpleDate(setDate)
			local strIsBC = ""
			if datDate:GetBC() then
				strError = strError.." B.C. disallowed "
				intDay = -intDay
				strIsBC = "and Day Number negated"
			end
			fhMessageBox("\n Get Day Number issue for date \n "..oldDate:GetDisplayText().." \n "..strError.." \n So replaced it with date \n "..newDate:GetDisplayText().." \n "..strIsBC)
		end
		return intDay
	end -- function GetDayNumber

	local dtpYearMin = fhNewDatePt(1000)												-- Minimum year to use when earliest estimate is null
	local dtpYearMax = fhNewDatePt(2000)												-- Maximum year to use when latest estimate is null

	function fh.GetYearToday()																-- Get the Year for Today
		local intYearToday = fhCallBuiltInFunction("Year",fhCallBuiltInFunction("Today"))
		dtpYearMax = fhNewDatePt(intYearToday)											-- Set maximum year date point
		return intYearToday
	end -- function GetYearToday()

	local function getDeathFacts(ptrIndi)												-- Iterate Death, Burial, Cremation facts
		local arrFact = { "~.DEAT"; "~.BURI"; "~.CREM"; }
		local intFact = 0
		local ptrFact = fhNewItemPtr()													-- Pointer to each fact returned to user
		return function ()
			while intFact < #arrFact do
				intFact = intFact + 1
				ptrFact = fhGetItemPtr(ptrIndi,arrFact[intFact])
				if ptrFact:IsNotNull() then return ptrFact end
			end
		end
	end -- local function getDeathFacts

	-- Ensure Estimated Date EARLIEST <= LATEST <= Fact Date --					-- Fix errors in EstimatedBirth/DeathDate function
	local function estimatedDates(strFunc,ptrIndi,intGens,getFact,intYrs)
		-- strFunc	"EstimatedBirthDate" or "EstimatedDeathDate"
		-- ptrIndi	Individual of interest
		-- intGens	Number of generations (may be nil)
		-- getFact	Iterator function for facts
		-- intYrs 	Years to add to After dates
		intGens = intGens or 2
		local dtpMin = fhCallBuiltInFunction(strFunc,ptrIndi,"EARLIEST",intGens)
		local dtpMax = fhCallBuiltInFunction(strFunc,ptrIndi,"LATEST",intGens)
		local dtpMid = fhNewDatePt()
		if not ( dtpMin:IsNull() and dtpMax:IsNull() ) then 						-- Skip if both null	
			if dtpMax:IsNull() then dtpMax = dtpYearMax elseif dtpMin:IsNull() then dtpMin = dtpYearMin end
			for ptrFact in getFact(ptrIndi) do
				local datFact = fhGetValueAsDate(fhGetItemPtr(ptrFact,"~.DATE"))
				if not datFact:IsNull() then												-- Find 1st Fact Date
					local dtpLast = datFact:GetDatePt1()								-- Last date = DatePt1 for Simple, Range, and Before
					local strType = datFact:GetSubtype()								-- Between = DatePt2 and After = DatePt1 + intYrs
					if   strType == "Between" then dtpLast = datFact:GetDatePt2()
					elseif strType == "After" then dtpLast = fhNewDatePt(dtpLast:GetYear()+intYrs,dtpLast:GetMonth(),dtpLast:GetDay()) end		-- Compare only uses Year, Month, Day so omitted	,dtpLast:GetYearDD(),dtpLast:GetBC(),dtpLast:GetCalendar()
					if dtpMax:Compare(dtpLast) > 0 then dtpMax = dtpLast end
					if dtpMin:Compare(dtpMax)  > 0 then dtpMin = dtpMax  end
					if strType ~= "After" then break end								-- Now EARLIEST <= LATEST <= Last date
				end
			end	
			local intDays = ( fh.GetDayNumber(dtpMax) - fh.GetDayNumber(dtpMin) ) / 2
			local intYear,remYear = math.modf( intDays / 365.2422 )				-- Offset year @ 365.2422 days per year, and remainder fraction
			local intMnth = math.floor( ( remYear * 12 ) + 0.1 )					-- Offset month is remainder fraction of year * 12
			dtpMid = fhCallBuiltInFunction("CalcDate",dtpMin,intYear,intMnth)	-- Need approximate MID year & month
		end
		return { Min=dtpMin; Mid=dtpMid; Max=dtpMax; }								-- Return EARLIEST, MID, LATEST dates
	end -- local function estimatedDates

	-- Make EstimatedBirthDate EARLIEST <= LATEST <= 1st Fact Date --			-- Fix errors in EstimatedBirthDate function
	function fh.EstimatedBirthDates(ptrIndi,intGens)
		return estimatedDates("EstimatedBirthDate",ptrIndi,intGens,iterate.Facts,10)
	end -- function EstimatedBirthDates

	-- Make EstimatedDeathDate EARLIEST <= LATEST <= DEAT/BURI/CREM Date --	-- Fix errors in EstimatedDeathDate function
	function fh.EstimatedDeathDates(ptrIndi,intGens)
		return estimatedDates("EstimatedDeathDate",ptrIndi,intGens,getDeathFacts,100)
	end -- function EstimatedDeathDates

	--[[
	@function:		BuildDataRef
	@description:	Get Full Data Reference for Pointer
	@parameters:		Item Pointer
	@returns:			Data Reference String, Record Id Integer, Record Type Tag String
	@requires:		None
	]]
	function fh.BuildDataRef(ptrRef)

		local strDataRef = ""										-- Data Reference with instance indices e.g. INDI.RESI[2].ADDR
		local intRecId   = 0										-- Record Id for associated Record
		local strRecTag  = ""										-- Record Tag of associated Record type i.e. INDI, FAM, NOTE, SOUR, etc

		-- getDataRef() is called recursively per level of the Data Ref
		-- ptrRef points to the upper Data Ref levels yet to be analysed
		-- strRef compiles the lower Data Ref levels including instances

		local function getDataRef(ptrRef,strRef)
			local ptrTag = ptrRef:Clone()
			local strTag = fhGetTag(ptrTag)						-- Current level Tag
			ptrTag:MoveToParentItem(ptrTag)
			if ptrTag:IsNotNull() then							-- Parent level exists
				local intSib = 1
				local ptrSib = ptrRef:Clone()					-- Pointer to siblings with same Tag
				ptrSib:MovePrev("SAME_TAG")
				while ptrSib:IsNotNull() do						-- Count previous siblings with same Tag
					intSib = intSib + 1
					ptrSib:MovePrev("SAME_TAG")
				end
				if intSib > 1 then 	strTag = strTag.."["..intSib.."]" end
				getDataRef(ptrTag,"."..strTag..strRef)			-- Now analyse the parent level
			else
				strDataRef = strTag..strRef						-- Record level reached, so set return values
				intRecId   = fhGetRecordId(ptrRef)
				strRecTag  = strTag
				if not fhIsValidDataRef(strDataRef) then print("BuildDataRef: "..strDataRef.." is Invalid") end
			end
		end -- local function getDataRef

		if type(ptrRef) == "userdata" then getDataRef(ptrRef,"") end

		return strDataRef, intRecId, strRecTag

	end -- function BuildDataRef

	--[[
	@function:		GetDataRefPtr
	@description:	Get Pointer for Full Data Reference
	@parameters:		Data Reference String, Record Id Integer, Record Type Tag String (optional)
	@returns:			Item Pointer which IsNull() if any parameters are invalid
	@requires:		None
	]]
	function fh.GetDataRefPtr(strDataRef,intRecId,strRecTag)
		strDataRef = strDataRef or ""
		if not strRecTag then
			strRecTag = strDataRef:gsub("^(%u+).*$","%1")	-- Extract Record Tag from Data Ref
		end
		local ptrRef = fhNewItemPtr()
		ptrRef:MoveToRecordById(strRecTag,intRecId or 0)	-- Lookup the Record by Id
		ptrRef:MoveTo(ptrRef,strDataRef)						-- Move to the Data Ref
		return ptrRef
	end -- function GetDataRefPtr

	function fh.TblDataRef(ptrRef)
		local tblRef = {}
		tblRef.DataRef, tblRef.RecId, tblRef.RecTag = BuildDataRef(ptrRef)
		return tblRef
	end -- function TblDataRef

	function fh.PtrDataRef(tblRef)
		local tblRef = tblRef or {}								-- Ensure table and its fields exist
		return GetDataRefPtr(tblRef.DataRef or "",tblRef.RecId or 0,tblRef.RecTag or "")
	end -- function PtrDataRef

	return fh

end -- local function general_v3

local general = general_v3()										-- To access FH general tools module

--[[
@Module:			+fh+encoder_v3
@Author:			Mike Tate
@Version:			3.5
@LastUpdated:	25 Aug 2020
@Description:	Text encoder module for HTML XHTML XML URI UTF8 UTF16 ISO CP1252/ANSI character codings.
@V3.5:				Function Prototype Closure version with Lua 5.1 & 5.3 comaptibility.
@V3.4:				Ensure expressions involving gsub return just text parameter.
@V3.3:				Adds UNICODE U+10000 to U+10FFFF UTF-16 Supplementary Planes.
@V3.2:				Update for ANSI & Unicode to ASCII for sorting, Soundex, etc.
@V3.1:				Update for Unicode UTF-16 & UTF-8 and fhConvertANSItoUTF8 & fhConvertUTF8toANSI, name change UTF to UTF8 & CP to ANSI.
@V2.0:				StrUTF8_Encode() replaced by StrUTF_CP1252() for entire UTF-8 range, plus new StrCP1252_ISO().
@V1.0:				Initial version.
]]

local function encoder_v3()

	local fh = {}													-- Local environment table

	local fhVersion = fhGetAppVersion()

	local br_Tag = "
" -- Markup language break tag default local br_Lua = "
" -- Lua pattern for break tag recognition local tblCodePage = {} -- Code Page to XML/XHTML/HTML/URI/UTF8 encodings: http://en.wikipedia.org/wiki/Windows-1252 & 1250 & etc -- Control characters "\000" to "\031" for URI & Markup "[%c]" encodings are disallowed except for "\t" to "\r" tblCodePage["\000"] = "" -- NUL tblCodePage["\001"] = "" -- SOH tblCodePage["\002"] = "" -- STX tblCodePage["\003"] = "" -- ETX tblCodePage["\004"] = "" -- EOT tblCodePage["\005"] = "" -- ENQ tblCodePage["\006"] = "" -- ACK tblCodePage["\a"] = "" -- BEL tblCodePage["\b"] = "" -- BS tblCodePage["\t"] = "+" -- HT space in Markup see setURIEncodings() and setMarkupEncodings() below tblCodePage["\n"] = "%0A" -- LF br_Tag in Markup tblCodePage["\v"] = "%0A" -- VT br_Tag in Markup tblCodePage["\f"] = "%0A" -- FF br_Tag in Markup tblCodePage["\r"] = "%0D" -- CR br_Tag in Markup tblCodePage["\014"] = "" -- SO tblCodePage["\015"] = "" -- SI tblCodePage["\016"] = "" -- DLE tblCodePage["\017"] = "" -- DC1 tblCodePage["\018"] = "" -- DC2 tblCodePage["\019"] = "" -- DC3 tblCodePage["\020"] = "" -- DC4 tblCodePage["\021"] = "" -- NAK tblCodePage["\022"] = "" -- SYN tblCodePage["\023"] = "" -- ETB tblCodePage["\024"] = "" -- CAN tblCodePage["\025"] = "" -- EM tblCodePage["\026"] = "" -- SUB tblCodePage["\027"] = "" -- ESC tblCodePage["\028"] = "" -- FS tblCodePage["\029"] = "" -- GS tblCodePage["\030"] = "" -- RS tblCodePage["\031"] = "" -- US -- ASCII characters "\032" to "\127" for URI "[%s%p]" encodings: http://en.wikipedia.org/wiki/URL and http://en.wikipedia.org/wiki/Percent-encoding tblCodePage[" "] = "+" -- or "%20" Space tblCodePage["!"] = "%21" -- Reserved character tblCodePage['"'] = "%22" -- """ in Markup see setURIEncodings() and setMarkupEncodings() below tblCodePage["#"] = "%23" -- Reserved character tblCodePage["$"] = "%24" -- Reserved character tblCodePage["%"] = "%25" -- Must be encoded tblCodePage["&"] = "%26" -- Reserved character -- "&" in Markup see setURIEncodings() and setMarkupEncodings() below tblCodePage["'"] = "%27" -- Reserved character -- "'" in Markup see setURIEncodings() and setMarkupEncodings() below tblCodePage["("] = "%28" -- Reserved character tblCodePage[")"] = "%29" -- Reserved character tblCodePage["*"] = "%2A" -- Reserved character tblCodePage["+"] = "%2B" -- Reserved character tblCodePage[","] = "%2C" -- Reserved character -- tblCodePage["-"] = "%2D" -- Unreserved character not encoded -- tblCodePage["."] = "%2E" -- Unreserved character not encoded tblCodePage["/"] = "%2F" -- Reserved character -- Digits 0 to 9 -- Unreserved characters not encoded tblCodePage[":"] = "%3A" -- Reserved character tblCodePage[";"] = "%3B" -- Reserved character tblCodePage["<"] = "%3C" -- "<" in Markup see setURIEncodings() and setMarkupEncodings() below tblCodePage["="] = "%3D" -- Reserved character tblCodePage[">"] = "%3E" -- ">" in Markup see setURIEncodings() and setMarkupEncodings() below tblCodePage["?"] = "%3F" -- Reserved character tblCodePage["@"] = "%40" -- Reserved character -- Letters A to Z -- Unreserved characters not encoded tblCodePage["["] = "%5B" -- Reserved character tblCodePage["\\"]= "%5C" tblCodePage["]"] = "%5D" -- Reserved character tblCodePage["^"] = "%5E" -- tblCodePage["_"] = "%5F" -- Unreserved character not encoded tblCodePage["`"] = "%60" -- Letters a to z -- Unreserved characters not encoded tblCodePage["{"] = "%7B" tblCodePage["|"] = "%7C" tblCodePage["}"] = "%7D" -- tblCodePage["~"] = "%7E" -- Unreserved character not encoded tblCodePage["\127"] = "" -- DEL -- Code Page 1252 Unicode characters "\128" to "\255" for UTF-8 scheme "[€-ÿ]" encodings: http://en.wikipedia.org/wiki/UTF-8 tblCodePage["€"] = string.char(0xE2,0x82,0xAC) -- "€" tblCodePage["\129"] = "" -- Undefined tblCodePage["‚"] = string.char(0xE2,0x80,0x9A) tblCodePage["ƒ"] = string.char(0xC6,0x92) tblCodePage["„"] = string.char(0xE2,0x80,0x9E) tblCodePage["…"] = string.char(0xE2,0x80,0xA6) tblCodePage["†"] = string.char(0xE2,0x80,0xA0) tblCodePage["‡"] = string.char(0xE2,0x80,0xA1) tblCodePage["ˆ"] = string.char(0xCB,0x86) tblCodePage["‰"] = string.char(0xE2,0x80,0xB0) tblCodePage["Š"] = string.char(0xC5,0xA0) tblCodePage["‹"] = string.char(0xE2,0x80,0xB9) tblCodePage["Œ"] = string.char(0xC5,0x92) tblCodePage["\141"] = "" -- Undefined tblCodePage["Ž"] = string.char(0xC5,0xBD) tblCodePage["\143"] = "" -- Undefined tblCodePage["\144"] = "" -- Undefined tblCodePage["‘"] = string.char(0xE2,0x80,0x98) tblCodePage["’"] = string.char(0xE2,0x80,0x99) tblCodePage["“"] = string.char(0xE2,0x80,0x9C) tblCodePage["”"] = string.char(0xE2,0x80,0x9D) tblCodePage["•"] = string.char(0xE2,0x80,0xA2) tblCodePage["–"] = string.char(0xE2,0x80,0x93) tblCodePage["—"] = string.char(0xE2,0x80,0x94) tblCodePage["\152"] = string.char(0xCB,0x9C) -- Small Tilde tblCodePage["™"] = string.char(0xE2,0x84,0xA2) tblCodePage["š"] = string.char(0xC5,0xA1) tblCodePage["›"] = string.char(0xE2,0x80,0xBA) tblCodePage["œ"] = string.char(0xC5,0x93) tblCodePage["\157"] = "" -- Undefined tblCodePage["ž"] = string.char(0xC5,0xBE) tblCodePage["Ÿ"] = string.char(0xC5,0xB8) tblCodePage["\160"] = string.char(0xC2,0xA0) -- " " No Break Space tblCodePage["¡"] = string.char(0xC2,0xA1) -- "¡" tblCodePage["¢"] = string.char(0xC2,0xA2) -- "¢" tblCodePage["£"] = string.char(0xC2,0xA3) -- "£" tblCodePage["¤"] = string.char(0xC2,0xA4) -- "¤" tblCodePage["¥"] = string.char(0xC2,0xA5) -- "¥" tblCodePage["¦"] = string.char(0xC2,0xA6) tblCodePage["§"] = string.char(0xC2,0xA7) tblCodePage["¨"] = string.char(0xC2,0xA8) tblCodePage["©"] = string.char(0xC2,0xA9) tblCodePage["ª"] = string.char(0xC2,0xAA) tblCodePage["«"] = string.char(0xC2,0xAB) tblCodePage["¬"] = string.char(0xC2,0xAC) tblCodePage["­"] = string.char(0xC2,0xAD) -- "­" Soft Hyphen tblCodePage["®"] = string.char(0xC2,0xAE) tblCodePage["¯"] = string.char(0xC2,0xAF) tblCodePage["°"] = string.char(0xC2,0xB0) tblCodePage["±"] = string.char(0xC2,0xB1) tblCodePage["²"] = string.char(0xC2,0xB2) tblCodePage["³"] = string.char(0xC2,0xB3) tblCodePage["´"] = string.char(0xC2,0xB4) tblCodePage["µ"] = string.char(0xC2,0xB5) tblCodePage["¶"] = string.char(0xC2,0xB6) tblCodePage["·"] = string.char(0xC2,0xB7) tblCodePage["¸"] = string.char(0xC2,0xB8) tblCodePage["¹"] = string.char(0xC2,0xB9) tblCodePage["º"] = string.char(0xC2,0xBA) tblCodePage["»"] = string.char(0xC2,0xBB) tblCodePage["¼"] = string.char(0xC2,0xBC) tblCodePage["½"] = string.char(0xC2,0xBD) tblCodePage["¾"] = string.char(0xC2,0xBE) tblCodePage["¿"] = string.char(0xC2,0xBF) tblCodePage["À"] = string.char(0xC3,0x80) tblCodePage["Á"] = string.char(0xC3,0x81) tblCodePage["Â"] = string.char(0xC3,0x82) tblCodePage["Ã"] = string.char(0xC3,0x83) tblCodePage["Ä"] = string.char(0xC3,0x84) tblCodePage["Å"] = string.char(0xC3,0x85) tblCodePage["Æ"] = string.char(0xC3,0x86) tblCodePage["Ç"] = string.char(0xC3,0x87) tblCodePage["È"] = string.char(0xC3,0x88) tblCodePage["É"] = string.char(0xC3,0x89) tblCodePage["Ê"] = string.char(0xC3,0x8A) tblCodePage["Ë"] = string.char(0xC3,0x8B) tblCodePage["Ì"] = string.char(0xC3,0x8C) tblCodePage["Í"] = string.char(0xC3,0x8D) tblCodePage["Î"] = string.char(0xC3,0x8E) tblCodePage["Ï"] = string.char(0xC3,0x8F) tblCodePage["Ð"] = string.char(0xC3,0x90) tblCodePage["Ñ"] = string.char(0xC3,0x91) tblCodePage["Ò"] = string.char(0xC3,0x92) tblCodePage["Ó"] = string.char(0xC3,0x93) tblCodePage["Ô"] = string.char(0xC3,0x94) tblCodePage["Õ"] = string.char(0xC3,0x95) tblCodePage["Ö"] = string.char(0xC3,0x96) tblCodePage["×"] = string.char(0xC3,0x97) tblCodePage["Ø"] = string.char(0xC3,0x98) tblCodePage["Ù"] = string.char(0xC3,0x99) tblCodePage["Ú"] = string.char(0xC3,0x9A) tblCodePage["Û"] = string.char(0xC3,0x9B) tblCodePage["Ü"] = string.char(0xC3,0x9C) tblCodePage["Ý"] = string.char(0xC3,0x9D) tblCodePage["Þ"] = string.char(0xC3,0x9E) tblCodePage["ß"] = string.char(0xC3,0x9F) tblCodePage["à"] = string.char(0xC3,0xA0) tblCodePage["á"] = string.char(0xC3,0xA1) tblCodePage["â"] = string.char(0xC3,0xA2) tblCodePage["ã"] = string.char(0xC3,0xA3) tblCodePage["ä"] = string.char(0xC3,0xA4) tblCodePage["å"] = string.char(0xC3,0xA5) tblCodePage["æ"] = string.char(0xC3,0xA6) tblCodePage["ç"] = string.char(0xC3,0xA7) tblCodePage["è"] = string.char(0xC3,0xA8) tblCodePage["é"] = string.char(0xC3,0xA9) tblCodePage["ê"] = string.char(0xC3,0xAA) tblCodePage["ë"] = string.char(0xC3,0xAB) tblCodePage["ì"] = string.char(0xC3,0xAC) tblCodePage["í"] = string.char(0xC3,0xAD) tblCodePage["î"] = string.char(0xC3,0xAE) tblCodePage["ï"] = string.char(0xC3,0xAF) tblCodePage["ð"] = string.char(0xC3,0xB0) tblCodePage["ñ"] = string.char(0xC3,0xB1) tblCodePage["ò"] = string.char(0xC3,0xB2) tblCodePage["ó"] = string.char(0xC3,0xB3) tblCodePage["ô"] = string.char(0xC3,0xB4) tblCodePage["õ"] = string.char(0xC3,0xB5) tblCodePage["ö"] = string.char(0xC3,0xB6) tblCodePage["÷"] = string.char(0xC3,0xB7) tblCodePage["ø"] = string.char(0xC3,0xB8) tblCodePage["ù"] = string.char(0xC3,0xB9) tblCodePage["ú"] = string.char(0xC3,0xBA) tblCodePage["û"] = string.char(0xC3,0xBB) tblCodePage["ü"] = string.char(0xC3,0xBC) tblCodePage["ý"] = string.char(0xC3,0xBD) tblCodePage["þ"] = string.char(0xC3,0xBE) tblCodePage["ÿ"] = string.char(0xC3,0xBF) -- Set XML/XHTML/HTML "[%c\"&'<>]" Markup encodings: http://en.wikipedia.org/wiki/XML and http://en.wikipedia.org/wiki/HTML local function setMarkupEncodings() tblCodePage["\t"] = " " -- HT "\t" to "\r" are treated as white space in Markup Languages by default tblCodePage["\n"] = br_Tag -- LF tblCodePage["\v"] = br_Tag -- VT line break tag "
" or "
" or "
" or "
" is better tblCodePage["\f"] = br_Tag -- FF tblCodePage["\r"] = br_Tag -- CR tblCodePage['"'] = """ tblCodePage["&"] = "&" tblCodePage["'"] = "'" tblCodePage["<"] = "<" tblCodePage[">"] = ">" end -- local function setMarkupEncodings -- Set URI/URL/URN "[%s%p]" encodings: http://en.wikipedia.org/wiki/URL and http://en.wikipedia.org/wiki/Percent-encoding local function setURIEncodings() tblCodePage["\t"] = "+" -- HT space tblCodePage["\n"] = "%0A" -- LF newline tblCodePage["\v"] = "%0A" -- VT newline tblCodePage["\f"] = "%0A" -- FF newline tblCodePage["\r"] = "%0D" -- CR return tblCodePage['"'] = "%22" tblCodePage["&"] = "%26" tblCodePage["'"] = "%27" tblCodePage["<"] = "%3C" tblCodePage[">"] = "%3E" end -- local function setURIEncodings -- Encode characters according to gsub pattern & lookup table -- local function strEncode(strText,strPattern,tblPattern) return ( (strText or ""):gsub(strPattern,tblPattern) ) -- V3.4 end -- local function strEncode -- Encode CP1252/ANSI characters into UTF-8 codes -- function fh.StrANSI_UTF8(strText) if fhVersion > 5 then strText = fhConvertANSItoUTF8(strText) else strText = strEncode(strText,"[\127-ÿ]",tblCodePage) end return strText end -- function StrANSI_UTF8 function fh.StrCP_UTF(strText) -- Legacy return fh.StrANSI_UTF8(strText) end -- function StrCP1252_UTF8 function fh.StrCP1252_UTF(strText) -- Legacy return fh.StrANSI_UTF8(strText) end -- function StrCP1252_UTF -- Encode CP1252/ANSI or UTF-8 characters into UTF-8 -- function fh.StrEncode_UTF8(strText) if stringx.encoding() == "ANSI" then return fh.StrANSI_UTF8(strText) else return strText end end -- function StrEncode_UTF8 -- Encode CP1252/ANSI characters into XML/XHTML/HTML/UTF8 codes -- local strANSI_XML = "[%z\001-\031\"&'<>\127-ÿ]" if fhVersion > 6 then -- Cater for Lua 5.1 %z or Lua 5.3 \0 strANSI_XML = "[\000-\031\"&'<>\127-ÿ]" end function fh.StrANSI_XML(strText) setMarkupEncodings() strText = (strText or ""):gsub(br_Lua,"\n") -- Convert
&
&
&
to \n that becomes br_Tag strText = strEncode(strText,strANSI_XML,tblCodePage) return strText end -- function StrANSI_XML function StrCP_XML(strText) -- Legacy return fh.StrANSI_XML(strText) end -- function StrCP_XML function StrCP1252_XML(strText) -- Legacy return fh.StrANSI_XML(strText) end -- function StrCP1252_XML -- Encode UTF-8 ASCII characters into XML/XHTML/HTML codes -- local strUTF8_XML = "[%z\001-\031\"&'<>\127]" if fhVersion > 6 then -- Cater for Lua 5.1 %z or Lua 5.3 \0 strUTF8_XML = "[\000-\031\"&'<>\127]" end function fh.StrUTF8_XML(strText) setMarkupEncodings() strText = (strText or ""):gsub(br_Lua,"\n") -- Convert
&
&
&
to \n that becomes br_Tag strText = strEncode(strText,strUTF8_XML,tblCodePage) return strText end -- function StrUTF8_XML -- Encode CP1252/ANSI or UTF-8 ASCII characters into XML/XHTML/HTML codes -- function fh.StrEncode_XML(strText) if stringx.encoding() == "ANSI" then return fh.StrANSI_XML(strText) else return fh.StrUTF8_XML(strText) end end -- function StrEncode_XML -- Encode Item Text characters into XML/HTML/UTF-8 codes -- function fh.StrGetItem_XML(ptrItem,strTags) return fh.StrEncode_XML(fhGetItemText(ptrItem,strTags)) end -- function StrGetItem_XML -- Encode CP1252/ANSI characters into URI codes -- function fh.StrANSI_URI(strText) setURIEncodings() strText = (strText or ""):gsub(br_Lua,"\n") -- Convert
&
&
&
to \n that becomes %0A strText = strEncode(strText,"[^0-9A-Za-z]",tblCodePage) return strText end -- function StrANSI_URI function fh.StrCP_URI(strText) return fh.StrANSI_URI(strText) end -- function StrCP_URI function fh.StrCP1252_URI(strText) return fh.StrANSI_URI(strText) end -- function StrCP1252_URI -- Encode UTF-8 ASCII characters into URI codes -- local strUTF8_URI = "[%z\001-\127]" if fhVersion > 6 then -- Cater for Lua 5.1 %z or Lua 5.3 \0 strUTF8_URI = "[\000-\127]" end function fh.StrUTF8_URI(strText) setURIEncodings() strText = (strText or ""):gsub(br_Lua,"\n") -- Convert
&
&
&
to \n that becomes br_Tag strText = strEncode(strText,strUTF8_URI,tblCodePage) return strText end -- function StrUTF8_URI -- Encode CP1252/ANSI or UTF-8 ASCII characters into URI codes -- function fh.StrEncode_URI(strText) if stringx.encoding() == "ANSI" then return fh.StrANSI_URI(strText) else return fh.StrUTF8_URI(strText) end end -- function StrEncode_URI function fh.StrUTF8_Encode(strText) -- Legacy from V1.0 return fh.StrUTF8_ANSI(strText) end -- function StrUTF8_Encode -- Encode UTF-8 bytes into single CP1252/ANSI character V2.0 upvalues -- local strByteRange = "["..string.char(0xC0).."-"..string.char(0xFF).."]" local tblBytePoint = {0xC0;0xE0;0xF0;0xF8;0xFC;} -- Byte codes for 2-byte, 3-byte, 4-byte, 5-byte, 6-byte UTF-8 local tblUTF8 = {} for strByte = string.byte("€"), string.byte("ÿ") do local strChar = string.char(strByte) -- Use CodePage to UTF-8 table to populate UTF-8 to CodePage table local strCode = tblCodePage[strChar] tblUTF8[strCode] = strChar end -- Encode UTF-8 bytes into single CP1252/ANSI character -- function fh.StrUTF8_ANSI(strText) strText = strText or "" if fhVersion > 5 then return fhConvertUTF8toANSI(strText) end if strText:match(strByteRange) then -- If text contains characters that need translating then local intChar = 0 -- Input character index local strChar = "" -- Current character local strCode = "" -- UTF-8 multi-byte code local tblLine = {} -- Translated output line repeat intChar = intChar + 1 -- Step through each character in text strChar = strText:sub(intChar,intChar) if strChar:match(strByteRange) then -- Convert UTF-8 bytes into CP character strCode = strChar -- First UTF-8 byte code, whose top bits say how many bytes to append for intByte, strByte in ipairs(tblBytePoint) do if string.byte(strChar) >= strByte then intChar = intChar + 1 -- Append next UTF-8 byte code character strCode = strCode..strText:sub(intChar,intChar) else break end end strChar = tblUTF8[strCode] or "¿" -- Translate UTF-8 code into CP character end table.insert(tblLine,strChar) -- Accumulate output char by char until intChar >= #strText strText = table.concat(tblLine) end return strText end -- function StrUTF8_ANSI function fh.StrUTF_CP(strText) -- Legacy return fh.StrUTF8_ANSI(strText) end -- function StrUTF_CP function fh.StrUTF_CP1252(strText) -- Legacy return fh.StrUTF8_ANSI(strText) end -- function StrUTF_CP1252 -- Encode CP1252/ANSI or UTF-8 characters into ANSI -- function fh.StrEncode_ANSI(strText) if stringx.encoding() == "ANSI" then return strText or "" else return fh.StrUTF8_ANSI(strText) end end -- function StrEncode_ANSI -- Set ISO-8859-1 "[\127-Ÿ]" encodings: http://en.wikipedia.org/wiki/ISO/IEC_8859-1 local tblISO8859 = { } tblISO8859["\127"]="" -- DEL tblISO8859["€"] = "EUR" tblISO8859["\129"]="" -- Undefined tblISO8859["‚"] = "¸" tblISO8859["ƒ"] = "f" tblISO8859["„"] = "¸¸" tblISO8859["…"] = "..." tblISO8859["†"] = "+" tblISO8859["‡"] = "±" tblISO8859["ˆ"] = "^" tblISO8859["‰"] = "%" tblISO8859["Š"] = "S" tblISO8859["‹"] = "<" tblISO8859["Œ"] = "OE" tblISO8859["\141"]="" -- Undefined tblISO8859["Ž"] = "Z" tblISO8859["\143"]="" -- Undefined tblISO8859["\144"]="" -- Undefined tblISO8859["‘"] = "'" tblISO8859["’"] = "'" tblISO8859["“"] = '"' tblISO8859["”"] = '"' tblISO8859["•"] = "º" tblISO8859["–"] = "-" tblISO8859["—"] = "-" tblISO8859["\152"]="~" -- Small Tilde tblISO8859["™"] = "TM" tblISO8859["š"] = "s" tblISO8859["›"] = ">" tblISO8859["œ"] = "oe" tblISO8859["\157"]="" -- Undefined tblISO8859["ž"] = "z" tblISO8859["Ÿ"] = "Y" -- Encode CP1252/ANSI characters into ISO-8859-1 codes -- function fh.StrANSI_ISO(strText) return strEncode(strText,"[\127-Ÿ]",tblISO8859) end -- function StrANSI_ISO function fh.StrCP_ISO(strText) -- Legacy return fh.StrANSI_ISO(strText) end -- function StrCP_ISO function fh.StrCP1252_ISO(strText) -- Legacy return fh.StrANSI_ISO(strText) end -- function StrCP1252_ISO function fh.StrUTF8_ISO(strText) return fh.StrANSI_ISO(fh.StrUTF8_ANSI(strText)) end -- function StrUTF8_ISO -- Encode CP1252/ANSI or UTF-8 ASCII characters into ISO-8859-1 codes -- function fh.StrEncode_ISO(strText) if stringx.encoding() == "ANSI" then return fh.StrANSI_ISO(strText) else return fh.StrUTF8_ISO(strText) end end -- function StrEncode_ISO -- Convert UTF-8 bytes to a UTF-16 word or pair -- local tblByte = {} local tblLead = { 0x80; 0xC0; 0xE0; 0xF0; 0xF8; 0xFC; } function fh.StrUtf8toUtf16(strChar) -- Convert any UTF-8 multibytes to UTF-16 -- local function strUtf8() if #tblByte > 0 then local intUtf16 = 0 for intIndex, intByte in ipairs (tblByte) do -- Convert UTF-8 bytes to UNICODE U+0080 to U+10FFFF if intIndex == 1 then intUtf16 = intByte - tblLead[#tblByte] else intUtf16 = intUtf16 * 0x40 + intByte - 0x80 end end if intUtf16 > 0xFFFF then -- U+10000 to U+10FFFF Supplementary Planes -- V2.6 tblByte = {} intUtf16 = intUtf16 - 0x10000 local intLow10 = 0xDC00 + ( intUtf16 % 0x400 ) -- Low 16-bit Surrogate local intTop10 = 0xD800 + math.floor( intUtf16 / 0x400 ) -- High 16-bit Surrogate local intChar1 = intTop10 % 0x100 local intChar2 = math.floor( intTop10 / 0x100 ) local intChar3 = intLow10 % 0x100 local intChar4 = math.floor( intLow10 / 0x100 ) return string.char(intChar1,intChar2,intChar3,intChar4) -- Surrogate 16-bit Pair end if intUtf16 < 0xD800 -- U+0080 to U+FFFF (except U+D800 to U+DFFF) -- V2.6 or intUtf16 > 0xDFFF then -- Basic Multilingual Plane tblByte = {} local intChar1 = intUtf16 % 0x100 local intChar2 = math.floor( intUtf16 / 0x100 ) return string.char(intChar1,intChar2) -- BPL 16-bit end local strUtf8 = "" -- U+D800 to U+DFFF Reserved Code Points -- V2.6 for intIndex, intByte in ipairs (tblByte) do strUtf8 = strUtf8..string.format("%.2X ",intByte) end local strUtf16 = string.format("%.4X ",intUtf16) fhMessageBox("\n UTF-16 Reserved Code Point U+D800 to U+DFFF \n UTF-16 = "..strUtf16.." UTF-8 = "..strUtf8.."\n Character will be replaced by a question mark. \n") tblByte = {} return "?\0" end return "" end -- local function strUtf8 local intUtf8 = string.byte(strChar) if intUtf8 < 0x80 then -- U+0000 to U+007F (ASCII) return strUtf8()..strChar.."\0" -- Previous UTF-8 multibytes + current ASCII char end if intUtf8 >= 0xC0 then -- Next UTF-8 multibyte start local strUtf16 = strUtf8() table.insert(tblByte,intUtf8) return strUtf16 -- Previous UTF-8 multibytes end table.insert(tblByte,intUtf8) return "" end -- function StrUtf8toUtf16 -- Encode UTF-8 bytes into UTF-16 words -- function fh.StrUTF8_UTF16(strText) tblByte = {} -- (0xFF) flushes last UTF-8 character return ( ((strText or "")..string.char(0xFF)):gsub("(.)",fh.StrUtf8toUtf16) ) -- V3.4 end -- function StrUTF8_UTF16 -- Encode CP1252/ANSI or UTF-8 characters into UTF-16 words -- function fh.StrEncode_UTF16(strText) if stringx.encoding() == "ANSI" then strText = fh.StrANSI_UTF8(strText) end return fh.StrUTF8_UTF16(strText) end -- function StrEncode_UTF16 local intTop10 = 0 -- Convert a UTF-16 word or pair to UTF-8 bytes -- function fh.StrUtf16toUtf8(strChar1,strChar2) local intUtf16 = string.byte(strChar2) * 0x100 + string.byte(strChar1) if intUtf16 < 0x80 then -- U+0000 to U+007F (ASCII) return string.char(intUtf16) end if intUtf16 < 0x800 then -- U+0080 to U+07FF local intByte1 = intUtf16 % 0x40 intUtf16 = math.floor( intUtf16 / 0x40 ) local intByte2 = intUtf16 return string.char( intByte2 + 0xC0, intByte1 + 0x80 ) end if intUtf16 < 0xD800 -- U+0800 to U+FFFF or intUtf16 > 0xDFFF then local intByte1 = intUtf16 % 0x40 intUtf16 = math.floor( intUtf16 / 0x40 ) local intByte2 = intUtf16 % 0x40 intUtf16 = math.floor( intUtf16 / 0x40 ) local intByte3 = intUtf16 return string.char( intByte3 + 0xE0, intByte2 + 0x80, intByte1 + 0x80 ) end if intUtf16 < 0xDC00 then -- U+10000 to U+10FFFF High 16-bit Surrogate Supplementary Planes -- V2.6 intTop10 = ( intUtf16 - 0xD800 ) * 0x400 + 0x10000 return "" end intUtf16 = intUtf16 - 0xDC00 + intTop10 -- U+10000 to U+10FFFF Low 16-bit Surrogate Supplementary Planes -- V2.6 local intByte1 = intUtf16 % 0x40 intUtf16 = math.floor( intUtf16 / 0x40 ) local intByte2 = intUtf16 % 0x40 intUtf16 = math.floor( intUtf16 / 0x40 ) local intByte3 = intUtf16 % 0x40 intUtf16 = math.floor( intUtf16 / 0x40 ) local intByte4 = intUtf16 return string.char( intByte4 + 0xF0, intByte3 + 0x80, intByte2 + 0x80, intByte1 + 0x80 ) end -- function StrUtf16toUtf8 -- Encode UTF-16 words into UTF-8 bytes -- function fh.StrUTF16_UTF8(strText) return ( (strText or ""):gsub("(.)(.)",fh.StrUtf16toUtf8) ) -- V3.4 end -- function StrUTF16_UTF8 -- Encode UTF-16 words into ANSI characters -- function fh.StrUTF16_ANSI(strText) return fh.StrUTF8_ANSI(fh.StrUTF16_UTF8(strText)) end -- function StrUTF16_ANSI -- Read UTF-16/UTF-8/ANSI file converted to chosen encoding via line iterator -- local strUtf16 = "^.%z" if fhVersion > 6 then -- Cater for Lua 5.1 %z or Lua 5.3 \0 strUtf16 = "^.\0" end function fh.FileLines(strFileName,strEncoding) -- Derived from http://lua-users.org/wiki/EnhancedFileLines local bomUtf16= "^"..string.char(0xFF,0xFE) -- "ÿþ" local bomUtf8 = "^"..string.char(0xEF,0xBB,0xBF) -- "" local fncConv = tostring -- Function to convert input to current encoding local intHead = 1 -- Index to start of current text line local intLump = 1024 local fHandle = general.OpenFile(strFileName,"rb") local strText = fHandle:read(1024) -- Read first lump from file local intBOM = 0 strEncoding = strEncoding or string.encoding() if strText:match(bomUtf16) or strText:match(strUtf16) then strText,intBOM = strText:gsub(bomUtf16,"") -- Strip UTF-16 BOM if strEncoding == "ANSI" then -- Define UTF-16 conversion to current encoding fncConv = fh.StrUTF16_ANSI else fncConv = fh.StrUTF16_UTF8 end elseif strText:match(bomUtf8) then strText,intBOM = strText:gsub(bomUtf8,"") -- Strip UTF-8 BOM if strEncoding == "ANSI" then -- Define UTF-8 conversion to current encoding fncConv = fh.StrUTF8_ANSI end else if strEncoding == "UTF-8" then -- Define ANSI conversion to current encoding fncConv = fh.StrANSI_UTF8 end end strText = fncConv(strText) -- Convert first lump of text return function() -- Iterator function local intTail,strTail -- Index to end of current text line, and terminating characters while true do intTail, strTail = strText:match("()([\r\n].)",intHead) if intTail or not fHandle then if intHead > 1 then intLump = 0 end break -- End of line or end of file elseif fHandle then local strLump = fHandle:read(1024) -- Read next lump from file if strLump then -- Strip old text and add converted lump strText = strText:sub(intHead)..fncConv(strLump) intHead = 1 intLump = 1024 else assert(fHandle:close()) -- End of file fHandle = nil end end end if not intTail then intTail = #strText -- Last fragment of file elseif strTail == "\r\n" then intTail = intTail + 1 -- Adjust tail for both \r & \n end local strLine = strText:sub(intHead,intTail) -- Extract line from text intHead = intTail + 1 if #strLine > 0 then -- Return pruned line, tail chars, lump bytes read local strBody, strTail = strLine:match("^(.-)([\r\n]+)$") return strBody, strTail, intLump end end end -- function FileLines -- Set "[€-ÿ]" ASCII encodings same as Unidecode below local tblASCII = { } tblASCII["€"] = "=E" tblASCII["\129"]="" -- Undefined tblASCII["‚"] = "," tblASCII["ƒ"] = "f" tblASCII["„"] = ",," tblASCII["…"] = "..." tblASCII["†"] = "|+" tblASCII["‡"] = "|++" tblASCII["ˆ"] = "^" tblASCII["‰"] = "%0" tblASCII["Š"] = "S" tblASCII["‹"] = "<" tblASCII["Œ"] = "OE" tblASCII["\141"]="" -- Undefined tblASCII["Ž"] = "Z" tblASCII["\143"]="" -- Undefined tblASCII["\144"]="" -- Undefined tblASCII["‘"] = "'" tblASCII["’"] = "'" tblASCII["“"] = "\"" tblASCII["”"] = "\"" tblASCII["•"] = "*" tblASCII["–"] = "-" tblASCII["—"] = "--" tblASCII["\152"]="~" -- Small Tilde tblASCII["™"] = "TM" tblASCII["š"] = "s" tblASCII["›"] = ">" tblASCII["œ"] = "oe" tblASCII["\157"]="" -- Undefined tblASCII["ž"] = "z" tblASCII["Ÿ"] = "Y" tblASCII["\160"]=" " -- " " No Break Space tblASCII["¡"] = "!" -- "¡" tblASCII["¢"] = "=c" -- "¢" tblASCII["£"] = "=L" -- "£" tblASCII["¤"] = "=$" -- "¤" tblASCII["¥"] = "=Y" -- "¥" tblASCII["¦"] = "|" tblASCII["§"] = "=SS" tblASCII["¨"] = "\"" tblASCII["©"] = "(C)" tblASCII["ª"] = "a" tblASCII["«"] = "<<" tblASCII["¬"] = "-" tblASCII["­"] = "-" -- "­" Soft Hyphen tblASCII["®"] = "(R)" tblASCII["¯"] = "-" tblASCII["°"] = "=o" tblASCII["±"] = "+-" tblASCII["²"] = "2" tblASCII["³"] = "3" tblASCII["´"] = "'" tblASCII["µ"] = "=u" tblASCII["¶"] = "=p" tblASCII["·"] = "*" tblASCII["¸"] = "," tblASCII["¹"] = "1" tblASCII["º"] = "o" tblASCII["»"] = ">>" tblASCII["¼"] = "1/4" tblASCII["½"] = "1/2" tblASCII["¾"] = "3/4" tblASCII["¿"] = "?" tblASCII["À"] = "A" tblASCII["Á"] = "A" tblASCII["Â"] = "A" tblASCII["Ã"] = "A" tblASCII["Ä"] = "A" tblASCII["Å"] = "A" tblASCII["Æ"] = "AE" tblASCII["Ç"] = "C" tblASCII["È"] = "E" tblASCII["É"] = "E" tblASCII["Ê"] = "E" tblASCII["Ë"] = "E" tblASCII["Ì"] = "I" tblASCII["Í"] = "I" tblASCII["Î"] = "I" tblASCII["Ï"] = "I" tblASCII["Ð"] = "D" tblASCII["Ñ"] = "N" tblASCII["Ò"] = "O" tblASCII["Ó"] = "O" tblASCII["Ô"] = "O" tblASCII["Õ"] = "O" tblASCII["Ö"] = "O" tblASCII["×"] = "*" tblASCII["Ø"] = "O" tblASCII["Ù"] = "U" tblASCII["Ú"] = "U" tblASCII["Û"] = "U" tblASCII["Ü"] = "U" tblASCII["Ý"] = "Y" tblASCII["Þ"] = "TH" tblASCII["ß"] = "ss" tblASCII["à"] = "a" tblASCII["á"] = "a" tblASCII["â"] = "a" tblASCII["ã"] = "a" tblASCII["ä"] = "a" tblASCII["å"] = "a" tblASCII["æ"] = "ae" tblASCII["ç"] = "c" tblASCII["è"] = "e" tblASCII["é"] = "e" tblASCII["ê"] = "e" tblASCII["ë"] = "e" tblASCII["ì"] = "i" tblASCII["í"] = "i" tblASCII["î"] = "i" tblASCII["ï"] = "i" tblASCII["ð"] = "d" tblASCII["ñ"] = "n" tblASCII["ò"] = "o" tblASCII["ó"] = "o" tblASCII["ô"] = "o" tblASCII["õ"] = "o" tblASCII["ö"] = "o" tblASCII["÷"] = "/" tblASCII["ø"] = "o" tblASCII["ù"] = "u" tblASCII["ú"] = "u" tblASCII["û"] = "u" tblASCII["ü"] = "u" tblASCII["ý"] = "y" tblASCII["þ"] = "th" tblASCII["ÿ"] = "y" -- Encode CP1252/ANSI characters into ASCII codes [\000-\127] -- function fh.StrANSI_ASCII(strText) return strEncode(strText,"[€-ÿ]",tblASCII) end -- function StrANSI_ASCII --[=[ Unidecode converts each codepoint into a few ASCII characters. Lookup table indexed by codepoint [0x0000]-[0xFFFF] gives an ASCII string. i.e. strASCII = Unidecode[intByte2][intByte1] or "=?" allowing for partially populated table. See http://search.cpan.org/dist/Text-Unidecode/ and follow Browse to: See http://cpansearch.perl.org/src/SBURKE/Text-Unidecode-1.22/lib/Text/Unidecode/ where each x??.pm gives 256 ASCII conversions. Start with the first few European accented characters, and add the others later. --]=] local Unidecode = { } function fh.StrUnidecode(strChar1,strChar2) -- Decode UTF-16 byte pair into ASCII characters return Unidecode[string.byte(strChar2)][string.byte(strChar1)] or "=?" end -- function StrUnidecode -- Encode UTF-8 characters into ASCII codes [\000-\126] -- function fh.StrUTF8_ASCII(strText) strText = fh.StrUTF8_UTF16(strText) -- Convert to UTF-16 Unicode and then to ASCII return ( strText:gsub("(.)(.)",fh.StrUnidecode) ) end -- function StrUTF8_ASCII -- Encode CP1252/ANSI or UTF-8 into ASCII codes [\000-\126] -- function fh.StrEncode_ASCII(strText) if stringx.encoding() == "ANSI" then return fh.StrANSI_ASCII(strText) else return fh.StrUTF8_ASCII(strText) end end -- function StrEncode_ASCII -- Set markup language break tag -- function fh.SetBreakTag(br_New) if not (br_New or ""):match(br_Lua) then -- Ensure new break tag is "
" or "
" or "
" or "
" br_New = "
" end br_Tag = br_New end -- function SetBreakTag for intByte = 0x00, 0xFF do Unidecode[intByte] = { } end Unidecode[0x00] = {[0]="\00";"\01";"\02";"\03";"\04";"\05";"\06";"\a";"\b";"\t";"\n";"\v";"\f";"\r";"\14";"\15";"\16";"\17";"\18";"\19";"\20";"\21";"\22";"\23";"\24";"\25";"\26";"\27";"\28";"\29";"\30";"\31"; " ";"!";'"';"#";"$";"%";"&";"'";"(";")";"*";"+";",";"-";".";"/";"0";"1";"2";"3";"4";"5";"6";"7";"8";"9";":";";";"<";"=";">";"?"; -- 0x20 to 0x3F "@";"A";"B";"C";"D";"E";"F";"G";"H";"I";"J";"K";"L";"M";"N";"O";"P";"Q";"R";"S";"T";"U";"V";"W";"X";"Y";"Z";"[";"\\";"]";"^";"_"; -- 0x40 to 0x5F "`";"a";"b";"c";"d";"e";"f";"g";"h";"i";"j";"k";"l";"m";"n";"o";"p";"q";"r";"s";"t";"u";"v";"w";"x";"y";"z";"{";"|";"}";"~";"\127"; -- 0x60 to 0x7F ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; ""; -- 0x80 to 0x9F " ";"!";"=c";"=L";"=$";"=Y";"|";"=SS";'"';"(C)";"a";"<<";"-";"-";"(R)";"-";"=o";"+-";"2";"3";"'";"=u";"=P";"*";",";"1";"o";">>";"1/4";"1/2";"3/4";"?"; -- 0xA0 to 0xBF "A";"A";"A";"A";"A";"A";"AE";"C";"E";"E";"E";"E";"I";"I";"I";"I";"D";"N";"O";"O";"O";"O";"O";"*";"O";"U";"U";"U";"U";"Y";"TH";"ss"; -- 0xC0 to 0xDF "a";"a";"a";"a";"a";"a";"ae";"c";"e";"e";"e";"e";"i";"i";"i";"i";"d";"n";"o";"o";"o";"o";"o";"/";"o";"u";"u";"u";"u";"y";"th";"y"; -- 0xE0 to 0xFF } Unidecode[0x01] = {[0]="A";"a";"A";"a";"A";"a";"C";"c";"C";"c";"C";"c";"C";"c";"D";"d";"D";"d";"E";"e";"E";"e";"E";"e";"E";"e";"E";"e";"G";"g";"G";"g"; -- 0x00 to 0x1F "G";"g";"G";"g";"H";"h";"H";"h";"I";"i";"I";"i";"I";"i";"I";"i";"I";"i";"IJ";"ij";"J";"j";"K";"k";"k";"L";"l";"L";"l";"L";"l";"L"; -- 0x20 to 0x3F "l";"L";"l";"N";"n";"N";"n";"N";"n";"'n";"ng";"NG";"O";"o";"O";"o";"O";"o";"OE";"oe";"R";"r";"R";"r";"R";"r";"S";"s";"S";"s";"S";"s"; -- 0x40 to 0x5F "S";"s";"T";"t";"T";"t";"T";"t";"U";"u";"U";"u";"U";"u";"U";"u";"U";"u";"U";"u";"W";"w";"Y";"y";"Y";"Z";"z";"Z";"z";"Z";"z";"s"; -- 0x60 to 0x7F "b";"B";"B";"b";"6";"6";"O";"C";"c";"D";"D";"D";"d";"d";"3";"@";"E";"F";"f";"G";"G";"hv";"I";"I";"K";"k";"l";"l";"W";"N";"n";"O"; -- 0x80 to 0x9F "O";"o";"OI";"oi";"P";"p";"YR";"2";"2";"SH";"sh";"t";"T";"t";"T";"U";"u";"Y";"V";"Y";"y";"Z";"z";"ZH";"ZH";"zh";"zh";"2";"5";"5";"ts";"w"; -- 0xA0 to 0xBF "|";"||";"|=";"!";"DZ";"Dz";"dz";"LJ";"Lj";"lj";"NJ";"Nj";"nj";"A";"a";"I";"i";"O";"o";"U";"u";"U";"u";"U";"u";"U";"u";"U";"u";"@";"A";"a"; -- 0xC0 to 0xDF "A";"a";"AE";"ae";"G";"g";"G";"g";"K";"k";"O";"o";"O";"o";"ZH";"zh";"j";"DZ";"Dz";"dz";"G";"g";"HV";"W";"N";"n";"A";"a";"AE";"ae";"O";"o"; -- 0xE0 to 0xFF } Unidecode[0x02] = {[0]="A";"a";"A";"a";"E";"e";"E";"e";"I";"i";"I";"i";"O";"o";"O";"o";"R";"r";"R";"r";"U";"u";"U";"u";"S";"s";"T";"t";"Y";"y";"H";"h"; -- 0x00 to 0x1F "N";"d";"OU";"ou";"Z";"z";"A";"a";"E";"e";"O";"o";"O";"o";"O";"o";"O";"o";"Y";"y";"l";"n";"t";"j";"db";"qp";"A";"C";"c";"L";"T";"s"; -- 0x20 to 0x3F "z";"[?]";"[?]";"B";"U";"^";"E";"e";"J";"j";"q";"q";"R";"r";"Y";"y";"a";"a";"a";"b";"o";"c";"d";"d";"e";"@";"@";"e";"e";"e";"e";"j"; -- 0x40 to 0x5F "g";"g";"g";"g";"u";"Y";"h";"h";"i";"i";"I";"l";"l";"l";"lZ";"W";"W";"m";"n";"n";"n";"o";"OE";"O";"F";"r";"r";"r";"r";"r";"r";"r"; -- 0x60 to 0x7F "R";"R";"s";"S";"j";"S";"S";"t";"t";"u";"U";"v";"^";"w";"y";"Y";"z";"z";"Z";"Z";"?";"?";"?";"C";"@";"B";"E";"G";"H";"j";"k";"L"; -- 0x80 to 0x9F "q";"?";"?";"dz";"dZ";"dz";"ts";"tS";"tC";"fN";"ls";"lz";"WW";"]]";"h";"h";"h";"h";"j";"r";"r";"r";"r";"w";"y";"'";'"';"`";"'";"`";"`";"'"; -- 0xA0 to 0xBF "?";"?";"<";">";"^";"V";"^";"V";"'";"-";"/";"\\";",";"_";"\\";"/";":";".";"`";"'";"^";"V";"+";"-";"V";".";"@";",";"~";'"';"R";"X"; -- 0xC0 to 0xDF "G";"l";"s";"x";"?";"";"";"";"";"";"";"";"V";"=";'"';"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]"; -- 0xE0 to 0xFF } Unidecode[0x03] = { } Unidecode[0x04] = { } Unidecode[0x20] = {[0]=" ";" ";" ";" ";" ";" ";" ";" ";" ";" ";" ";" ";"";"";"";"";"-";"-";"-";"-";"--";"--";"||";"_";"'";"'";",";"'";'"';'"';",,";'"'; -- 0x00 to 0x1F "|+";"|++";"*";"*>";".";"..";"...";".";"\n";"\n\n";"";"";"";"";"";" ";"%0";"%00";"'";"''";"'''";"`";"``";"```";"^";"<";">";"*";"!!";"!?";"-";"_"; -- 0x20 to 0x3F "-";"^";"***";"--";"/";"-[";"]-";"[?]";"?!";"!?";"7";"PP";"(]";"[)";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]"; -- 0x40 to 0x5F "[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"";"";"";"";"";"";"0";"";"";"";"4";"5";"6";"7";"8";"9";"+";"-";"=";"(";")";"n"; -- 0x60 to 0x7F "0";"1";"2";"3";"4";"5";"6";"7";"8";"9";"+";"-";"=";"(";")";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]"; -- 0x80 to 0x9F "ECU";"CL";"Cr";"FF";"L";"mil";"N";"Pts";"Rs";"W";"NS";"D";"=E";"K";"T";"Dr";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]"; -- 0xA0 to 0xBF "[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";""; -- 0xC0 to 0xDF "";"";"";"";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]";"[?]"; -- 0xE0 to 0xFF } Unidecode[0x21] = {[34]="TM"; } return fh end -- local function encoder_v3 local encoder = encoder_v3() -- To access FH encoder chars module --[[ @Module: +fh+progbar_v3 @Author: Mike Tate @Version: 3.0 @LastUpdated: 27 Aug 2020 @Description: Progress Bar library module. @V3.0: Function Prototype Closure version. @V1.0: Initial version. ]] local function progbar_v3() local fh = {} -- Local environment table require "iuplua" -- To access GUI window builder iup.SetGlobal("CUSTOMQUITMESSAGE","YES") -- Needed for IUP 3.28 local tblBars = {} -- Table for optional external attributes local strBack = "255 255 255" -- Background colour default is white local strBody = "0 0 0" -- Body text colour default is black local strFont = nil -- Font dialogue default is current font local strStop = "255 0 0" -- Stop button colour default is red local intPosX = iup.CENTER -- Show window default position is central local intPosY = iup.CENTER local intMax, intVal, intPercent, intStart, intDelta, intScale, strClock, isBarStop local lblText, barGauge, lblDelta, btnStop, dlgGauge local function doFocus() -- Bring the Progress Bar window into Focus dlgGauge.BringFront="YES" -- If used too often, inhibits other windows scroll bars, etc end -- local function doFocus local function doUpdate() -- Update the Progress Gauge and the Delta % with clock barGauge.Value = intVal lblDelta.Title = string.format("%4d %% %s ",math.floor(intPercent),strClock) end -- local function doUpdate local function doReset() -- Reset all dialogue variables and Update display intVal = 0 -- Current value of Progress Bar intPercent= 0.01 -- Percentage of progress intStart = os.time() -- Start time of progress intDelta = 0 -- Delta time of progress intScale = math.ceil( intMax / 1000 ) -- Scale of percentage per second of progress (initial guess is corrected in Step function) strClock = "00 : 00 : 00" -- Clock delta time display isBarStop = false -- Stop button pressed signal doUpdate() doFocus() end -- local function doReset function fh.Start(strTitle,intMaximum) -- Create & start Progress Bar window if not dlgGauge then strTitle = strTitle or "" -- Dialogue and button title intMax = intMaximum or 100 -- Maximun range of Progress Bar, default is 100 local strSize = tostring( math.max( 100, string.len(" Stop "..strTitle) * 8 ) ).."x30" -- Adjust Stop button size to Title lblText = iup.label { Title=" "; Expand="YES"; Alignment="ACENTER"; Tip="Progress Message"; } barGauge = iup.progressbar { RasterSize="400x30"; Value=0; Max=intMax; Tip="Progress Bar"; } lblDelta = iup.label { Title=" "; Expand="YES"; Alignment="ACENTER"; Tip="Percentage and Elapsed Time"; } btnStop = iup.button { Title=" Stop "..strTitle; RasterSize=strSize; FgColor=strStop; Tip="Stop Progress Button"; action=function() isBarStop = true end; } -- Signal Stop button pressed return iup.CLOSE -- Often caused main GUI to close !!! dlgGauge = iup.dialog { Title=strTitle.." Progress "; Font=strFont; FgColor=strBody; Background=strBack; DialogFrame="YES"; -- Remove Windows minimize/maximize menu iup.vbox{ Alignment="ACENTER"; Gap="10"; Margin="10x10"; lblText; barGauge; lblDelta; btnStop; }; move_cb = function(self,x,y) tblBars.X = x tblBars.Y = y end; close_cb = btnStop.action; -- Windows Close button = Stop button } if type(tblBars.GUI) == "table" and type(tblBars.GUI.ShowDialogue) == "function" then dlgGauge.move_cb = nil -- Use GUI library to show & move window tblBars.GUI.ShowDialogue("Bars",dlgGauge,btnStop,"showxy") else dlgGauge:showxy(intPosX,intPosY) -- Show the Progress Bar window end doReset() -- Reset the Progress Bar display end end -- function Start function fh.Message(strText) -- Show the Progress Bar message if dlgGauge then lblText.Title = strText end end -- function Message function fh.Step(intStep) -- Step the Progress Bar forward if dlgGauge then intVal = intVal + ( intStep or 1 ) -- Default step is 1 local intNew = math.ceil( intVal / intMax * 100 * intScale ) / intScale if intPercent ~= intNew then -- Update progress once per percent or per second, whichever is smaller intPercent = math.max( 0.1, intNew ) -- Ensure percentage is greater than zero if intVal > intMax then intVal = intMax intPercent = 100 end -- Ensure values do not exceed maximum intNew = os.difftime(os.time(),intStart) if intDelta < intNew then -- Update clock of elapsed time intDelta = intNew intScale = math.ceil( intDelta / intPercent ) -- Scale of seconds per percentage step local intHour = math.floor( intDelta / 3600 ) local intMins = math.floor( intDelta / 60 - intHour * 60 ) local intSecs = intDelta - intMins * 60 - intHour * 3600 strClock = string.format("%02d : %02d : %02d",intHour,intMins,intSecs) end doUpdate() -- Update the Progress Bar display end iup.LoopStep() end end -- function Step function fh.Focus() -- Bring the Progress Bar window to front if dlgGauge then doFocus() end end -- function Focus function fh.Reset() -- Reset the Progress Bar display if dlgGauge then doReset() end end -- function Reset function fh.Stop() -- Check if Stop button pressed iup.LoopStep() return isBarStop end -- function Stop function fh.Close() -- Close the Progress Bar window isBarStop = false if dlgGauge then dlgGauge:destroy() dlgGauge = nil end end -- function Close function fh.Setup(tblSetup) -- Setup optional table of external attributes if tblSetup then tblBars = tblSetup strBack = tblBars.Back or strBack -- Background colour strBody = tblBars.Body or strBody -- Body text colour strFont = tblBars.Font or strFont -- Font dialogue strStop = tblBars.Stop or strStop -- Stop button colour intPosX = tblBars.X or intPosX -- Window position intPosY = tblBars.Y or intPosY end end -- function Setup return fh end -- local function progbar_v3 local progbar = progbar_v3() -- To access FH progress bars module --[[ @Module: +fh+iup_gui_v3 @Author: Mike Tate @Version: 3.9 @LastUpdated: 22 Jun 2021 @Description: Graphical User Interface Library Module @V3.9: ShowDialogue() popup closure fhSleep() added; CheckVersionInStore() at monthly intervals; @V3.8: Function Prototype Closure version. @V3.7: AssignAttributes(tblControls) now allows any string attribute to invoke a function. @V3.6: anyMemoDialogue() sets TopMost attribute. @V3.5: Replace IsNormalWindow(iupDialog) with SetWindowCoord(tblName) and update CheckWindowPosition(tblName) to prevent negative values freezing main dialog. @V3.4: Use general.MakeFolder() to ensure key folders exist, add Get/PutRegKey(), check Registry IE Shell Version in HelpDialogue(), better error handling in LoadSettings(). @V3.3: LoadFolder() and SaveFolder() use global folder as default for local folder to improve synch across PC. @V3.2: Load & Save settings now use a single clipboard so Local PC settings are preserved across synchronised PC. @V3.1: IUP 3.11.2 iup.GetGlobal("VERSION") to top, HelpDialogue conditional ExpandChildren="YES/NO", RefreshDialogue uses NaturalSize, SetUtf8Mode(), Load/SaveFolder(), etc @V3.0: ShowDialogue "dialog" mode for Memo, new DestroyDialogue, NewHelpDialogue tblAttr for Font, AssignAttributes intSkip, CustomDialogue iup.CENTERPARENT+, IUP Workaround, BalloonToggle, Initialise test Plugin file exists. @V2.0: Support for Plugin Data scope, new FontDialogue, RefreshDialogue, AssignAttributes, httpRequest handler, keep "dialog" mode. @V1.0: Initial version. ]] local function iup_gui_v3() local fh = {} -- Local environment table require "iuplua" -- To access GUI window builder require "iupluacontrols" -- To access GUI window controls require "lfs" -- To access LUA filing system require "iupluaole" -- To access OLE subsystem require "luacom" -- To access COM subsystem iup.SetGlobal("CUSTOMQUITMESSAGE","YES") -- Needed for IUP 3.28 local iupVersion = iup.GetGlobal("VERSION") -- Obtain IUP module version -- "iuplua" Omitted Constants Workaround -- iup.TOP = iup.LEFT iup.BOTTOM = iup.RIGHT iup.RED = iup.RGB(1,0,0) iup.GREEN = iup.RGB(0,1,0) iup.BLUE = iup.RGB(0,0,1) iup.BLACK = iup.RGB(0,0,0) iup.WHITE = iup.RGB(1,1,1) iup.YELLOW = iup.RGB(1,1,0) -- Shared Interface Attributes & Functions -- fh.Version = " " -- Plugin Version fh.History = fh.Version -- Version History fh.Red = "255 0 0" -- Color attributes (must exclude leading zeros & spaces to allow value comparisons) fh.Maroon = "128 0 0" fh.Amber = "250 160 0" fh.Orange = "255 165 0" fh.Yellow = "255 255 0" fh.Olive = "128 128 0" fh.Lime = "0 255 0" fh.Green = "0 128 0" fh.Cyan = "0 255 255" fh.Teal = "0 128 128" fh.Blue = "0 0 255" fh.Navy = "0 0 128" fh.Magenta = "255 0 255" fh.Purple = "128 0 128" fh.Black = "0 0 0" fh.Gray = "128 128 128" fh.Silver = "192 192 192" fh.Smoke = "240 240 240" fh.White = "255 255 255" fh.Risk = fh.Red -- Risk colour for hazardous controls such as Close/Delete buttons fh.Warn = fh.Magenta -- Warn colour for caution controls and warnings fh.Safe = fh.Green -- Safe colour for active controls such as most buttons fh.Info = fh.Black -- Info colour for text controls such as labels/tabs fh.Head = fh.Black -- Head colour for headings fh.Body = fh.Black -- Body colour for body text fh.Back = fh.White -- Background colour for all windows fh.Gap = "8" -- Layout attributes Gap was "10" fh.Border = "8x8" -- was BigMargin="10x10" fh.Margin = "1x1" -- was MinMargin fh.Balloon = "NO" -- Tooltip balloon mode fh.FontSet = 0 -- Legacy GUI font set assigned by FontAssignment but used globally fh.FontHead = "" fh.FontBody = "" local GUI = { } -- Sub-table for GUI Dialogue attributes to allow any "Name" --[[ GUI.Name table of dialogue attributes, where Name is Font, Help, Main, Memo, Bars, etc GUI.Name.CoordX x co-ordinate ( Loaded & Saved by default ) GUI.Name.CoordY y co-ordinate ( Loaded & Saved by default ) GUI.Name.Dialog dialogue handle GUI.Name.Focus focus button handle GUI.Name.Frame dialogframe mode, "normal" = dialogframe="NO" else "YES", "showxy" = showxy(), "popup" or "keep" = popup(), default is "normal & showxy" GUI.Name.Height height GUI.Name.Raster rastersize ( Loaded & Saved by default ) GUI.Name.Width width GUI.Name.Back ProgressBar background colour GUI.Name.Body ProgressBar body text colour GUI.Name.Font ProgressBar font style GUI.Name.Stop ProgressBar Stop button colour GUI.Name.GUI Module table usable by other modules e.g. progbar.Setup Help dialogue Window attributes :- GUI.Help.GetHelp Parent dialogue GetHelp button GUI.Help.RootURL Wiki Help & Advice root URL GUI.Help.TblAttr Table of button attributes GUI.Help[n] Help dialogue nth button :- GUI.Help[n].Name Name for title attribute GUI.Help[n].Tip Tooltip for tip attribute GUI.Help[n].URL Page URL to append to root URL GUI.Help[n].Page Page order for intTabPosn --]] -- tblScrn[1] = origin x, tblScrn[2] = origin y, tblScrn[3] = width, tblScrn[4] = height local tblScrn = stringx.splitnumbers(iup.GetGlobal("VIRTUALSCREEN")) -- Used by CustomDialogue() and CheckWindowPosition() and ShowDialogue() below local intMaxW = tblScrn[3] local intMaxH = tblScrn[4] function fh.BalloonToggle() -- Toggle tooltips Balloon mode local tblToggle = { YES="NO"; NO="YES"; } fh.Balloon = tblToggle[fh.Balloon] fh.SaveSettings() end -- function BalloonToggle iup.SetGlobal("UTF8MODE","NO") function fh.SetUtf8Mode() -- Set IUP into UTF-8 display mode if iupVersion == "3.5" or stringx.encoding() == "ANSI" then return false end iup.SetGlobal("UTF8MODE","YES") iup.SetGlobal("UTF8MODE_FILE","NO") return true end -- function SetUtf8Mode local function tblOfNames(...) -- Get table of dialogue Names including "Font","Help","Main" by default local arg = {...} local tblNames = {"Font";"Help";"Main";} for intName, strName in ipairs(arg) do if type(strName) == "string" and strName ~= "Font" and strName ~= "Help" and strName ~= "Main" then table.insert(tblNames,strName) end end return tblNames end -- local function tblOfNames local function tblNameFor(strName) -- Get table of parameters for chosen dialogue Name strName = tostring(strName) if not GUI[strName] then -- Need new table with default minimum & raster size, and X & Y co-ordinates GUI[strName] = { } local tblName = GUI[strName] tblName.Raster = "x" tblName.CoordX = iup.CENTER tblName.CoordY = iup.CENTER end return GUI[strName] end -- local function tblNameFor local function intDimension(intMin,intVal,intMax) -- Return a number bounded by intMin and intMax if not intVal then return 0 end -- Except if no value then return 0 intVal = tonumber(intVal) or (intMin+intMax)/2 return math.max(intMin,math.min(intVal,intMax)) end -- local function intDimension function fh.CustomDialogue(strName,strRas,intX,intY) -- GUI custom window raster size, and X & Y co-ordinates -- strRas nil = old size, "x" or "0x0" = min size, "999x999" = new size -- intX/Y nil = central, "99" = co-ordinate position local tblName = tblNameFor(strName) local tblSize = {} local intWide = 0 local intHigh = 0 strRas = strRas or tblName.Raster if strRas then -- Ensure raster size is between minimum and screen size tblSize = stringx.splitnumbers(strRas) intWide = intDimension(intWide,tblSize[1],intMaxW) intHigh = intDimension(intHigh,tblSize[2],intMaxH) strRas = tostring(intWide.."x"..intHigh) end if intX and intX < iup.CENTERPARENT then intX = intDimension(0,intX,intMaxW-intWide) -- Ensure X co-ordinate positions window on screen end if intY and intY < iup.CENTERPARENT then intY = intDimension(0,intY,intMaxH-intHigh) -- Ensure Y co-ordinate positions window on screen end tblName.Raster = strRas or "x" tblName.CoordX = tonumber(intX) or iup.CENTER tblName.CoordY = tonumber(intY) or iup.CENTER end -- function CustomDialogue function fh.DefaultDialogue(...) -- GUI default window minimum & raster size, and X & Y co-ordinates for intName, strName in ipairs(tblOfNames(...)) do fh.CustomDialogue(strName) end end -- function DefaultDialogue function fh.DialogueAttributes(strName) -- Provide named Dialogue Attributes local tblName = tblNameFor(strName) -- tblName.Dialog = dialog handle, so any other attributes could be retrieved local tblSize = stringx.splitnumbers(tblName.Raster or "x") -- Split Raster Size into width=tblSize[1] and height=tblSize[2] tblName.Width = tblSize[1] tblName.Height= tblSize[2] tblName.Back = fh.Back -- Following only needed for NewProgressBar tblName.Body = fh.Body tblName.Font = fh.FontBody tblName.Stop = fh.Risk tblName.GUI = fh -- Module table return tblName end -- function DialogueAttributes local strDefaultScope = "Project" -- Default scope for Load/Save data is per Project/User/Machine as set by PluginDataScope() local tblClipProj = { } local tblClipUser = { } -- Clipboards of sticky data for each Plugin Data scope -- V3.2 local tblClipMach = { } local function doLoadData(strParam,strDefault,strScope) -- Load sticky data for Plugin Data scope strScope = tostring(strScope or strDefaultScope):lower() local tblClipData = tblClipProj if strScope:match("user") then tblClipData = tblClipUser elseif strScope:match("mach") then tblClipData = tblClipMach end return tblClipData[strParam] or strDefault end -- local function doLoadData function fh.LoadGlobal(strParam,strDefault,strScope) -- Load Global Parameter for all PC return doLoadData(strParam,strDefault,strScope) end -- function LoadGlobal function fh.LoadLocal(strParam,strDefault,strScope) -- Load Local Parameter for this PC return doLoadData(fh.ComputerName.."-"..strParam,strDefault,strScope) end -- function LoadLocal local function doLoadFolder(strFolder) -- Use relative paths to let Paths change -- V3.3 strFolder = strFolder:gsub("^FhDataPath",function() return fh.FhDataPath end) -- Full path to .fh_data folder strFolder = strFolder:gsub("^PublicPath",function() return fh.PublicPath end) -- Full path to Public folder strFolder = strFolder:gsub("^FhProjPath",function() return fh.FhProjPath end) -- Full path to project folder return strFolder end -- local function doLoadFolder function fh.LoadFolder(strParam,strDefault,strScope) -- Load Folder Parameter for this PC -- V3.3 local strFolder = doLoadFolder(fh.LoadLocal(strParam,"",strScope)) if not general.FlgFolderExists(strFolder) then -- If no local folder try global folder strFolder = doLoadFolder(fh.LoadGlobal(strParam,strDefault,strScope)) end return strFolder end -- function LoadFolder function fh.LoadDialogue(...) -- Load Dialogue Parameters for "Font","Help","Main" by default for intName, strName in ipairs(tblOfNames(...)) do local tblName = tblNameFor(strName) --# tblName.Raster = tostring(fh.LoadLocal(strName.."S",tblName.Raster)) -- Legacy of "S" becomes "R" tblName.Raster = tostring(fh.LoadLocal(strName.."R",tblName.Raster)) tblName.CoordX = tonumber(fh.LoadLocal(strName.."X",tblName.CoordX)) tblName.CoordY = tonumber(fh.LoadLocal(strName.."Y",tblName.CoordY)) fh.CheckWindowPosition(tblName) end end -- function LoadDialogue function fh.LoadSettings(...) -- Load Sticky Settings from File for strFileName, tblClipData in pairs ({ ProjectFile=tblClipProj; PerUserFile=tblClipUser; MachineFile=tblClipMach; }) do strFileName = fh[strFileName] if general.FlgFileExists(strFileName) then -- Load Settings File in table lines with key & val fields local tblField = {} for strLine in io.lines(strFileName) do if #tblField == 0 and strLine == "return {" -- Unless entire Sticky Data table was saved and type(table.load) == "function" then local tblClip, strErr = table.load(strFileName) -- Load Settings File table if strErr then error(strErr.."\n\nMay need to Delete the following Plugin Data .dat file:\n\n"..strFileName.."\n\nError detected.") end for i,j in pairs (tblClip) do tblClipData[i] = tblClip[i] end break end tblField = stringx.split(strLine,"=") if tblField[1] then tblClipData[tblField[1]] = tblField[2] end end end end fh.Safe = tostring(fh.LoadGlobal("SafeColor",fh.Safe)) fh.Warn = tostring(fh.LoadGlobal("WarnColor",fh.Warn)) fh.Risk = tostring(fh.LoadGlobal("RiskColor",fh.Risk)) fh.Head = tostring(fh.LoadGlobal("HeadColor",fh.Head)) fh.Body = tostring(fh.LoadGlobal("BodyColor",fh.Body)) fh.FontHead= tostring(fh.LoadGlobal("FontHead" ,fh.FontHead)) fh.FontBody= tostring(fh.LoadGlobal("FontBody" ,fh.FontBody)) fh.FontSet = tonumber(fh.LoadGlobal("Fonts" ,fh.FontSet)) -- Legacy only fh.FontSet = tonumber(fh.LoadGlobal("FontSet" ,fh.FontSet)) -- Legacy only fh.History = tostring(fh.LoadGlobal("History" ,fh.History)) fh.Balloon = tostring(fh.LoadGlobal("Balloon" ,fh.Balloon, "Machine")) fh.LoadDialogue(...) if fh.FontSet > 0 then fh.FontAssignment(fh.FontSet) end -- Legacy only end -- function LoadSettings local function doSaveData(strParam,anyValue,strScope) -- Save sticky data for Plugin Data scope strScope = tostring(strScope or strDefaultScope):lower() local tblClipData = tblClipProj if strScope:match("user") then tblClipData = tblClipUser elseif strScope:match("mach") then tblClipData = tblClipMach end tblClipData[strParam] = anyValue end -- local function doSaveData function fh.SaveGlobal(strParam,anyValue,strScope) -- Save Global Parameter for all PC doSaveData(strParam,anyValue,strScope) end -- function SaveGlobal function fh.SaveLocal(strParam,anyValue,strScope) -- Save Local Parameter for this PC doSaveData(fh.ComputerName.."-"..strParam,anyValue,strScope) end -- function SaveLocal function fh.SaveFolder(strParam,strFolder,strScope) -- Save Folder Parameter for this PC strFolder = stringx.replace(strFolder,fh.FhDataPath,"FhDataPath") -- Full path to .fh_data folder strFolder = stringx.replace(strFolder,fh.PublicPath,"PublicPath") -- Full path to Public folder strFolder = stringx.replace(strFolder,fh.FhProjPath,"FhProjPath") -- Full path to project folder --# doSaveData(fh.ComputerName.."-"..strParam,strFolder,strScope) -- Uses relative paths to let Paths change fh.SaveGlobal(strParam,strFolder,strScope) -- V3.3 fh.SaveLocal(strParam,strFolder,strScope) -- Uses relative paths to let Paths change end -- function SaveFolder function fh.SaveDialogue(...) -- Save Dialogue Parameters for "Font","Help","Main" by default for intName, strName in ipairs(tblOfNames(...)) do local tblName = tblNameFor(strName) fh.SaveLocal(strName.."R",tblName.Raster) fh.SaveLocal(strName.."X",tblName.CoordX) fh.SaveLocal(strName.."Y",tblName.CoordY) end end -- function SaveDialogue function fh.SaveSettings(...) -- Save Sticky Settings to File fh.SaveDialogue(...) fh.SaveGlobal("SafeColor",fh.Safe) fh.SaveGlobal("WarnColor",fh.Warn) fh.SaveGlobal("RiskColor",fh.Risk) fh.SaveGlobal("HeadColor",fh.Head) fh.SaveGlobal("BodyColor",fh.Body) fh.SaveGlobal("FontHead" ,fh.FontHead) fh.SaveGlobal("FontBody" ,fh.FontBody) fh.SaveGlobal("History" ,fh.History) fh.SaveGlobal("Balloon" ,fh.Balloon, "Machine") for strFileName, tblClipData in pairs ({ ProjectFile=tblClipProj; PerUserFile=tblClipUser; MachineFile=tblClipMach; }) do for i,j in pairs (tblClipData) do -- Check if table has any entries strFileName = fh[strFileName] if type(table.save) == "function" then -- Save entire Settings File table per Project/User/Machine table.save(tblClipData,strFileName) else local fileHandle = general.OpenFile(strFileName,"w") -- Else save Settings File lines with key & val fields for strKey,strVal in pairs(tblClipData) do fileHandle:write(strKey.."="..strVal.."\n") end fileHandle:close() end break end end end -- function SaveSettings function fh.CheckWindowPosition(tblName) -- Ensure dialogue window coordinates are on Screen if tonumber(tblName.CoordX) == nil or tonumber(tblName.CoordX) < 0 -- V3.5 or tonumber(tblName.CoordX) > intMaxW then tblName.CoordX = iup.CENTER end if tonumber(tblName.CoordY) == nil or tonumber(tblName.CoordY) < 0 -- V3.5 or tonumber(tblName.CoordY) > intMaxH then tblName.CoordY = iup.CENTER end end -- function CheckWindowPosition function fh.IsNormalWindow(iupDialog) -- Check dialogue window is not Maximised or Minimised (now redundant) -- tblPosn[1] = origin x, tblPosn[2] = origin y, tblPosn[3] = width, tblPosn[4] = height local tblPosn = stringx.splitnumbers(iupDialog.ScreenPosition) local intPosX = tblPosn[1] local intPosY = tblPosn[2] if intPosX < 0 and intPosY < 0 then -- If origin is negative (-8, -8 = Maximised, -3200, -3200 = Minimised) return false -- then is Maximised or Minimised end return true end -- function IsNormalWindow function fh.SetWindowCoord(tblName) -- Set the Window coordinates if not Maximised or Minimised -- V3.5 -- tblPosn[1] = origin x, tblPosn[2] = origin y, tblPosn[3] = width, tblPosn[4] = height local tblPosn = stringx.splitnumbers(tblName.Dialog.ScreenPosition) local intPosX = tblPosn[1] local intPosY = tblPosn[2] if intPosX < 0 and intPosY < 0 then -- If origin is negative (-8, -8 = Maximised, -3200, -3200 = Minimised) return false -- then is Maximised or Minimised end tblName.CoordX = intPosX -- Otherwise set the Window coordinates tblName.CoordY = intPosY return true end -- function SetWindowCoord function fh.ShowDialogue(strName,iupDialog,btnFocus,strFrame) -- Set standard frame attributes and display dialogue window local tblName = tblNameFor(strName) iupDialog = iupDialog or tblName.Dialog -- Retrieve previous parameters if needed btnFocus = btnFocus or tblName.Focus strFrame = strFrame or tblName.Frame strFrame = strFrame or "show norm" -- Default frame mode is dialog:showxy(X,Y) with DialogFrame="NO" ("normal" to vary size, otherwise fixed size) strFrame = strFrame:lower() -- Other modes are "show", "popup" & "keep" with DialogFrame="YES", or with "normal" for DialogFrame="NO" ("show" for active windows, "popup"/"keep" for modal windows) if strFrame:gsub("%s-%a-map%a*[%s%p]*","") == "" then -- May be prefixed with "map" mode to just map dialogue initially, also may be suffixed with "dialog" to inhibit iup.MainLoop() to allow progress messages strFrame = "map show norm" -- If only "map" mode then default to "map show norm" end if type(iupDialog) == "userdata" then tblName.Dialog = iupDialog tblName.Focus = btnFocus -- Preserve parameters tblName.Frame = strFrame iupDialog.Background = fh.Back -- Background colour iupDialog.Shrink = "YES" -- Sometimes needed to shrink controls to raster size if type(btnFocus) == "userdata" then -- Set button as focus for Esc and Enter keys iupDialog.StartFocus = iupDialog.StartFocus or btnFocus iupDialog.DefaultEsc = iupDialog.DefaultEsc or btnFocus iupDialog.DefaultEnter = iupDialog.DefaultEnter or btnFocus end iupDialog.MaxSize = intMaxW.."x"..intMaxH -- Maximum size is screen size iupDialog.MinSize = "x" -- Minimum size (default "x" becomes nil) iupDialog.RasterSize = tblName.Raster or "x" -- Raster size (default "x" becomes nil) if strFrame:match("norm") then -- DialogFrame mode is "NO" by default for variable size window if strFrame:match("pop") or strFrame:match("keep") then iupDialog.MinBox = "NO" -- For "popup" and "keep" hide Minimize and Maximize icons iupDialog.MaxBox = "NO" else strFrame = strFrame.." show" -- If not "popup" nor "keep" then use "showxy" mode end else iupDialog.DialogFrame = "YES" -- Define DialogFrame mode for fixed size window end iupDialog.close_cb = iupDialog.close_cb or function() return iup.CLOSE end -- Define default window X close, move, and resize actions iupDialog.move_cb = iupDialog.move_cb or function(self) fh.SetWindowCoord(tblName) end -- V3.5 iupDialog.resize_cb = iupDialog.resize_cb or function(self) if fh.SetWindowCoord(tblName) then tblName.Raster=self.RasterSize end end -- V3.5 if strFrame:match("map") then -- Only dialogue mapping is required iupDialog:map() --# tblName.Frame = strFrame:gsub("map","") -- Remove "map" from frame mode ready for subsequent call tblName.Frame = strFrame:gsub("%s-%a-map%a*[%s%p]*","") -- Remove "map" from frame mode ready for subsequent call return end fh.RefreshDialogue(strName) -- Refresh to set Natural Size as Minimum Size if iup.MainLoopLevel() == 0 -- Called from outside Main GUI, so must use showxy() and not popup() or strFrame:match("dialog") or strFrame:match("sho") then -- Use showxy() to dispay dialogue window for "showxy" or "dialog" mode iupDialog:showxy(tblName.CoordX,tblName.CoordY) if fh.History ~= fh.Version then -- Initially show new Version History Help if type(fh.HelpDialogue) == "function" then fh.History = fh.Version fh.HelpDialogue(fh.Version) -- But only after Help dialogue exists iupDialog.BringFront = "YES" end end if not ( strName == "Help" or strFrame:match("dialog") ) -- Inhibit MainLoop if Help dialogue or "dialog" mode and iup.MainLoopLevel() == 0 then iup.MainLoop() end else iupDialog:popup(tblName.CoordX,tblName.CoordY) -- Use popup() to display dialogue window for "popup" or "keep" modes fhSleep(200,150) -- Sometimes needed to prevent MainLoop() closure! -- V3.9 end if not strFrame:match("dialog") and strFrame:match("pop") then tblName.Dialog = nil -- When popup closed, clear key parameters, but not for "keep" mode tblName.Raster = nil tblName.CoordX = nil -- iup.CENTER tblName.CoordY = nil -- iup.CENTER else fh.SetWindowCoord(tblName) -- Set Window coordinate pixel values -- V3.5 end end end -- function ShowDialogue function fh.DestroyDialogue(strName) -- Destroy existing dialogue local tblName = tblNameFor(strName) if tblName then local iupDialog = tblName.Dialog if type(iupDialog) == "userdata" then iupDialog:destroy() tblName.Dialog = nil -- Prevent future misuse of handle -- 22 Jul 2014 end end end -- function DestroyDialogue local function strDialogueArgs(strArgA,strArgB,comp) -- Compare two argument pairs and return matching pair local tblArgA = stringx.splitnumbers(strArgA) local tblArgB = stringx.splitnumbers(strArgB) local strArgX = tostring(comp(tblArgA[1] or 100,tblArgB[1] or 100)) local strArgY = tostring(comp(tblArgA[2] or 100,tblArgB[2] or 100)) return strArgX.."x"..strArgY end -- local function strDialogueArgs function fh.RefreshDialogue(strName) -- Refresh dialogue window size after Font change, etc local tblName = tblNameFor(strName) local iupDialog = tblName.Dialog -- Retrieve the dialogue handle if type(iupDialog) == "userdata" then iupDialog.Size = iup.NULL iupDialog.MinSize = iup.NULL -- V3.1 iup.Refresh(iupDialog) -- Refresh window to Natural Size and set as Minimum Size if not iupDialog.RasterSize then iupDialog:map() iup.Refresh(iupDialog) end local strSize = iupDialog.NaturalSize or iupDialog.RasterSize -- IUP 3.5 NaturalSize = nil, IUP 3.11 needs NaturalSize -- V3.1 iupDialog.MinSize = strDialogueArgs(iupDialog.MaxSize,strSize,math.min) -- Set Minimum Size to smaller of Maximm Size or Natural/Raster Size -- V3.1 iupDialog.RasterSize = strDialogueArgs(tblName.Raster,strSize,math.max) -- Set Current Size to larger of Current Size or Natural/Raster Size -- V3.1 iup.Refresh(iupDialog) tblName.Raster = iupDialog.RasterSize if iupDialog.Visible == "YES" then -- Ensure visible dialogue origin is on screen tblName.CoordX = math.max(tblName.CoordX,10) tblName.CoordY = math.max(tblName.CoordY,10) -- Set both coordinates to larger of current value or 10 pixels if iupDialog.Modal then -- V3.8 if iupDialog.Modal == "NO" then iupDialog.ZOrder = "BOTTOM" -- Ensure dialogue is subservient to any popup iupDialog:showxy(tblName.CoordX,tblName.CoordY) -- Use showxy() to reposition main window else iupDialog:popup(tblName.CoordX,tblName.CoordY) -- Use popup() to reposition modal window end end else iupDialog.BringFront="YES" end end end -- function RefreshDialogue function fh.AssignAttributes(tblControls) -- Assign the attributes of all controls supplied local anyFunction = nil for iupName, tblAttr in pairs ( tblControls or {} ) do if type(iupName) == "userdata" and type(tblAttr) == "table" then-- Loop through each iup control local intSkip = 0 -- Skip counter for attributes same for all controls for intAttr, anyName in ipairs ( tblControls[1] or {} ) do -- Loop through each iup attribute local strName = nil local strAttr = nil local strType = type(anyName) if strType == "string" then -- Attribute is different for each control in tblControls strName = anyName strAttr = tblAttr[intAttr-intSkip] elseif strType == "table" then -- Attribute is same for all controls as per tblControls[1] intSkip = intSkip + 1 strName = anyName[1] strAttr = anyName[2] elseif strType == "function" then intSkip = intSkip + 1 anyFunction = anyName break end if type(strName) == "string" and ( type(strAttr) == "string" or type(strAttr) == "function" ) then local anyRawGet = rawget(fh,strAttr) -- Use rawget() to stop require("pl.strict") complaining if type(anyRawGet) == "string" then strAttr = anyRawGet -- Use internal module attribute such as Head or FontBody elseif type(iupName[strName]) == "string" and type(strAttr) == "function" then -- Allow string attributes to invoke a function -- V3.7 strAttr = strAttr() end iupName[strName] = strAttr -- Assign attribute to control end end end end if anyFunction then anyFunction() end -- Perform any control assignment function end -- function AssignAttributes -- Font Dialogue Attributes and Functions -- fh.FontBody = iup.GetGlobal("DEFAULTFONT") -- Set default font for Body and Head text fh.FontHead = fh.FontBody:gsub(", B?o?l?d?",", Bold ") ---[=[ local intFontPlain = 1 -- Font Face & Style values for legacy FontSet setting local intFontBold = 2 local intArialPlain = 3 local intArialBold = 4 local intTahomaPlain= 5 local intTahomaBold = 6 local strFontFace = fh.FontBody:gsub(",.*","") local tblFontSet = {} -- Lookup table for FontHead and FontBody tblFontSet[intFontPlain] = { Head=strFontFace.."; Bold -16"; Body=strFontFace.."; -15"; } tblFontSet[intFontBold] = { Head=strFontFace.."; Bold -16"; Body=strFontFace.."; Bold -15"; } tblFontSet[intArialPlain] = { Head="Arial; Bold -16"; Body="Arial; -16"; } tblFontSet[intArialBold] = { Head="Arial; Bold -16"; Body="Arial; Bold -15"; } tblFontSet[intTahomaPlain] = { Head="Tahoma; Bold -15"; Body="Tahoma; -16"; } tblFontSet[intTahomaBold] = { Head="Tahoma; Bold -15"; Body="Tahoma; Bold -14"; } function fh.FontAssignment(intFontSet) -- Assign Font Face & Style GUI values for legacy FontSet setting if intFontSet then intFontSet = math.max(intFontSet,1) intFontSet = math.min(intFontSet,#tblFontSet) fh.FontHead = tblFontSet[intFontSet]["Head"] -- Legacy Font for all GUI dialog header text fh.FontBody = tblFontSet[intFontSet]["Body"] -- Legacy Font for all GUI dialog body text end end -- function FontAssignment --]=] function fh.FontDialogue(tblAttr,strName) -- GUI Font Face & Style Dialogue tblAttr = tblAttr or {} strName = strName or "Main" local isFontChosen = false local btnFontHead = iup.button { Title="Choose Headings Font and default Colour"; } local btnFontBody = iup.button { Title="Choose Body text Font and default Colour"; } local btnCol_Safe = iup.button { Title=" Safe Colour "; } local btnCol_Warn = iup.button { Title=" Warning Colour "; } local btnCol_Risk = iup.button { Title=" Risky Colour "; } local btnDefault = iup.button { Title=" Default Fonts "; } local btnMinimum = iup.button { Title=" Minimum Size "; } local btnDestroy = iup.button { Title=" Close Dialogue "; } local frmSetFonts = iup.frame { Title=" Set Window Fonts & Colours "; iup.vbox { Alignment="ACENTER"; Margin=fh.Margin; Homogeneous="YES"; btnFontHead; btnFontBody; iup.hbox { btnCol_Safe; btnCol_Warn; btnCol_Risk; Homogeneous="YES"; }; iup.hbox { btnDefault ; btnMinimum ; btnDestroy ; Homogeneous="YES"; }; } -- iup.vbox end } -- iup.frame end -- Create dialogue and turn off resize, maximize, minimize, and menubox except Close button local dialogFont = iup.dialog { Title=" Set Window Fonts & Colours "; Gap=fh.Gap; Margin=fh.Border; frmSetFonts; } local tblButtons = { } local function setDialogues() -- Refresh the Main and Help dialogues local tblHelp = tblNameFor("Help") if type(tblHelp.Dialog) == "userdata" then -- Help dialogue exists fh.AssignAttributes(tblHelp.TblAttr) -- Assign the Help dialogue attributes fh.RefreshDialogue("Help") -- Refresh the Help window size & position end fh.AssignAttributes(tblAttr) -- Assign parent dialogue attributes fh.RefreshDialogue(strName) -- Refresh parent window size & position and bring infront of Help window fh.RefreshDialogue("Font") -- Refresh Font window size & position and bring infront of parent window end -- local function setDialogues local function getFont(strColor) -- Set font button function local strTitle = " Choose font style & default colour for "..strColor:gsub("Head","Heading").." text " local strValue = "Font"..strColor -- The font codes below are not recognised by iupFontDlg and result in empty font face! local strFont = rawget(fh,strValue):gsub(" Black,",","):gsub(" Light, Bold",","):gsub(" Extra Bold,",","):gsub(" Semibold,",",") local iupFontDlg = iup.fontdlg { Title=strTitle; Color=rawget(fh,strColor); Value=strFont; } iupFontDlg:popup() -- Popup predefined font dialogue if iupFontDlg.Status == "1" then if iupFontDlg.Value:match("^,") then -- Font face missing so revert to original font iupFontDlg.Value = rawget(fh,strValue) end fh[strColor] = iupFontDlg.Color -- Set Head or Body color attribute fh[strValue] = iupFontDlg.Value -- Set FontHead or FontBody font style fh.AssignAttributes(tblButtons) -- Assign the button & frame attributes setDialogues() isFontChosen = true end end -- local function getFont local function getColor(strColor) -- Set colour button function local strTitle = " Choose colour for "..strColor:gsub("Warn","Warning"):gsub("Risk","Risky").." button & message text " local iupColorDlg = iup.colordlg { Title=strTitle; Value=rawget(fh,strColor); ShowColorTable="YES"; } iupColorDlg.DialogFrame="YES" iupColorDlg:popup() -- Popup predefined color dialogue fixed size window if iupColorDlg.Status == "1" then fh[strColor] = iupColorDlg.Value -- Set Safe or Warn or Risk color attribute fh.AssignAttributes(tblButtons) -- Assign the button & frame attributes setDialogues() isFontChosen = true end end -- local function getColor local function setDefault() -- Action for Default Fonts button fh.Safe = fh.Green fh.Warn = fh.Magenta fh.Risk = fh.Red -- Set default colours fh.Body = fh.Black fh.Head = fh.Black fh.FontBody = iup.GetGlobal("DEFAULTFONT") -- Set default fonts for Body and Head text fh.FontHead = fh.FontBody:gsub(", B?o?l?d?",", Bold") fh.AssignAttributes(tblButtons) -- Assign the button & frame attributes setDialogues() isFontChosen = true end -- local function setDefault local function setMinimum() -- Action for Minimum Size button local tblName = tblNameFor(strName) local iupDialog = tblName.Dialog -- Retrieve the parent dialogue handle if type(iupDialog) == "userdata" then tblName.Raster = "10x10" -- Refresh parent window to Minimum Size & adjust position fh.RefreshDialogue(strName) end local tblFont = tblNameFor("Font") tblFont.Raster = "10x10" -- Refresh Font window to Minimum Size & adjust position fh.RefreshDialogue("Font") end -- local function setMinimum tblButtons = { { "Font" ; "FgColor" ; "Tip" ; "action" ; {"TipBalloon";"Balloon";} ; {"Expand";"YES";} ; }; [btnFontHead] = { "FontHead"; "Head"; "Choose the Heading text Font Face, Style, Size, Effects, and default Colour"; function() getFont("Head") end; }; [btnFontBody] = { "FontBody"; "Body"; "Choose the Body text Font Face, Style, Size, Effects, and default Colour" ; function() getFont("Body") end; }; [btnCol_Safe] = { "FontBody"; "Safe"; "Choose the colour for Safe operations" ; function() getColor("Safe") end; }; [btnCol_Warn] = { "FontBody"; "Warn"; "Choose the colour for Warning operations"; function() getColor("Warn") end; }; [btnCol_Risk] = { "FontBody"; "Risk"; "Choose the colour for Risky operations" ; function() getColor("Risk") end; }; [btnDefault ] = { "FontBody"; "Safe"; "Restore default Fonts and Colours"; function() setDefault() end; }; [btnMinimum ] = { "FontBody"; "Safe"; "Reduce window to its minimum size"; function() setMinimum() end; }; [btnDestroy ] = { "FontBody"; "Risk"; "Close this dialogue "; function() return iup.CLOSE end; }; [frmSetFonts] = { "FontHead"; "Head"; }; } fh.AssignAttributes(tblButtons) -- Assign the button & frame attributes fh.ShowDialogue("Font",dialogFont,btnDestroy,"keep normal") -- Popup the Set Window Fonts dialogue: "keep normal" : vary size & posn, and remember size & posn -- fh.ShowDialogue("Font",dialogFont,btnDestroy,"popup normal") -- Popup the Set Window Fonts dialogue: "popup normal" : vary size & posn, but redisplayed centred -- fh.ShowDialogue("Font",dialogFont,btnDestroy,"keep") -- Popup the Set Window Fonts dialogue: "keep" : fixed size, vary posn, and only remember posn -- fh.ShowDialogue("Font",dialogFont,btnDestroy,"popup") -- Popup the Set Window Fonts dialogue: "popup": fixed size, vary posn, but redisplayed centred dialogFont:destroy() return isFontChosen end -- function FontDialogue -- Help Dialogue Attributes and Functions fh.HelpDialogue = "" -- HelpDialogue must be declared for ShowDialogue local strHelpButtonActive = nil -- defaults to "YES" -- Help button active attribute mode used only in NewHelpDialogue function fh.NewHelpDialogue(btnGetHelp,strRootURL) -- Prototype for GUI Help Dialogue, with parent Help button, and web page root/namespace URL local tblHelp = tblNameFor("Help") local oleControl, btnDestroy, hboxHelp, dialogHelp, tblAttr -- Dialogue component upvalues if type(btnGetHelp) == "userdata" then btnGetHelp.Active = strHelpButtonActive if btnGetHelp.Active == "NO" then -- Help button inactive, so Help dialogue exists, so just update parent button tblHelp.GetHelp = btnGetHelp -- Allows successive parent GUI to share one Help dialogue return end end tblHelp.GetHelp = btnGetHelp strRootURL = strRootURL or fh.Plugin:gsub(" ","_"):lower() -- Default to Plugin name as Wiki namespace if strRootURL:match("^[%w_]+$") then -- Append Wiki namespace to Wiki root URL strRootURL = "http://www.fhug.org.uk/wiki/doku.php?id=plugins:help:"..strRootURL..":" end tblHelp.RootURL = strRootURL local intURL = 1 -- Index to Version History help page URL local tblURL = { } -- List of help page URL local tblAttr = { } -- Attribute table primarily for FontDialogue() tblHelp.TblAttr = tblAttr local function doCommonAction() -- Common action when creating/destroying Help dialogue local strMode = "NO" if tblHelp.Dialog then tblHelp.Dialog = nil -- Clear dialog handle strMode = nil -- Defaults to "YES" but more efficient to test else tblAttr = { {"Font";"FgColor";}; } -- Reset attribute table primarily for FontDialogue() tblHelp.TblAttr = tblAttr end if type(tblHelp.GetHelp) == "userdata" then -- Set parent dialogue Help button active mode tblHelp.GetHelp.Active = strMode end strHelpButtonActive = strMode end -- local function doCommonAction -- Save global Help button active mode function fh.HelpDialogue(anyPage) -- GUI Help Dialogue for chosen web page --[=[ Parameter anyPage can be one of several values: 1. Page number from 0 to index tblURL, often equal to intTabPosn. 2. Version to display Version History page for version chosen. 3. String with " "="_" and lowercase substring of a page name in tblURL. --]=] if not fh.GetRegKey("HKLM\\SOFTWARE\\Microsoft\\Internet Explorer\\MAIN\\FeatureControl\\FEATURE_BROWSER_EMULATION\\fh.exe") then fhMessageBox("\n The 'Help and Advice' web page has encountered a problem. \n\n The FH IE Shell version is undefined in the Windows Registry. \n\n So please run the 'Write Reg IE Shell Version' Plugin to rectify. \n") return end if not tblHelp.Dialog then doCommonAction() -- Create the WebBrowser based on its ProgID and connect it to LuaCOM oleControl = iup.olecontrol{ "Shell.Explorer.1"; designmode="NO"; } oleControl:CreateLuaCOM() btnDestroy = iup.button { Title="Close Window"; Tip="Close this Help and Advice window"; TipBalloon=fh.Balloon; Expand="HORIZONTAL"; Size="x10"; FgColor=fh.Risk; action=function() dialogHelp:destroy() doCommonAction() end; } hboxHelp = iup.hbox { Margin=fh.Margin; Homogeneous="NO"; } -- Create each GUI button with title, tooltip, color, action, etc, and table of web page URL for intButton, tblButton in ipairs(tblHelp) do local intPage = tblButton.Page or intButton local strURL = tblButton.URL if strURL:match("ver.-hist") then intURL = intPage end tblURL[intPage] = strURL local btnName = iup.button { Title=tblButton.Name; Tip=tblButton.Tip; TipBalloon=fh.Balloon; Expand=btnDestroy.Expand; Size=btnDestroy.Size; FgColor=fh.Safe; action=function() oleControl.com:Navigate(tblHelp.RootURL..strURL) end; } iup.Append(hboxHelp,btnName) tblAttr[btnName] = { "FontBody"; "Safe"; } end iup.Append(hboxHelp,btnDestroy) tblAttr[btnDestroy] = { "FontBody"; "Risk"; } local strExpChild = "NO" if iupVersion == "3.5" then strExpChild = "YES" end -- V3.1 for IUP 3.11.2 dialogHelp = iup.dialog { Title=fh.Plugin.." Help & Advice"; Font=fh.FontBody; iup.vbox { Alignment="ACENTER"; Gap=fh.Gap; Margin=fh.Border; ExpandChildren=strExpChild; -- V3.1 for IUP 3.11.2 oleControl; hboxHelp; }; close_cb = function() doCommonAction() end; } fh.ShowDialogue("Help",dialogHelp,btnDestroy) -- Show Help dialogue window and set tblHelp.Dialog = dialogHelp end anyPage = anyPage or 0 if type(anyPage) == "number" then -- Select page by Tab = Button = Help page index anyPage = math.max(1,math.min(#tblURL,anyPage+1)) anyPage = tblURL[anyPage] or "" elseif anyPage == fh.Version then -- Select the Version History features section anyPage = anyPage:gsub("[%s%p]","") anyPage = anyPage:gsub("^(%d)","V%1") anyPage = tblURL[intURL].."#features_of_"..anyPage elseif type(anyPage) == "string" then -- Select page by matching name text local strPage = anyPage:gsub(" ","_"):lower() anyPage = tblURL[1] or "" -- Default to first web page for intURL = 1, #tblURL do local strURL = tblURL[intURL] if strURL:match(strPage) then anyPage = strURL break end end else anyPage = tblURL[1] or "" -- Default to first web page end oleControl.com:Navigate(tblHelp.RootURL..anyPage) -- Navigate to chosen web page end -- function HelpDialogue end -- function NewHelpDialogue function fh.AddHelpButton(strName,strTip,strURL,intPage) -- Add button to GUI Help Dialogue local tblHelp = tblNameFor("Help") if tblHelp and not strHelpButtonActive then for intHelp, tblHelp in ipairs(tblHelp) do -- Check button does not already exist if tblHelp.Name == strName then return end end if tonumber(intPage) then intPage = intPage + 1 end -- Optional external intPage number matches intTabPosn table.insert( tblHelp, { Name=strName or "?"; Tip=strTip or "?"; URL=strURL or ""; Page=intPage; } ) end end -- function AddHelpButton local function anyMemoControl(anyName,fgColor) -- Compose any control Title and FgColor local strName = tostring(anyName) -- anyName may be a string, and fgColor is default FgColor local tipText = nil if type(anyName) == "table" then -- anyName may be a table = { Title string ; FgColor string ; ToolTip string (optional); } strName = anyName[1] fgColor = anyName[2]:match("%d* %d* %d*") or fgColor tipText = anyName[3] end return strName, fgColor, tipText end -- local function anyMemoControl local function anyMemoDialogue(strHead,anyHead,strMemo,anyMemo,...) -- Display framed memo dialogue with buttons local arg = {...} -- Fix for Lua 5.2+ local intButt = 0 -- Returned value if "X Close" button is used local tblButt = { [0]="X Close"; } -- Button names lookup table local strHead, fgcHead, tipHead = anyMemoControl(anyHead or "",strHead) local strMemo, fgcMemo, tipMemo = anyMemoControl(anyMemo or "",strMemo) -- Create the GUI labels and buttons local lblMemo = iup.label { Title=strMemo; FgColor=fgcMemo; Tip=tipMemo; TipBalloon=fh.Balloon; Alignment="ACENTER"; Padding=fh.Margin; Expand="YES"; WordWrap="YES"; } local lblLine = iup.label { Separator="HORIZONTAL"; } local iupHbox = iup.hbox { Homogeneous="YES"; } local btnButt = iup.button { } local strTop = "YES" -- Make dialogue TopMost -- V3.6 local strMode = "popup" if arg[1] == "Keep Dialogue" then -- Keep dialogue open for a progress message strMode = "keep dialogue" lblLine = iup.label { } if not arg[2] then strTop = "NO" end -- User chooses TopMost -- V3.6 else if #arg == 0 then arg[1] = "OK" end -- If no buttons listed then default to an "OK" button for intArg, anyButt in ipairs(arg) do local strButt, fgcButt, tipButt = anyMemoControl(anyButt,fh.Safe) tblButt[intArg] = strButt btnButt = iup.button { Title=strButt; FgColor=fgcButt; Tip=tipButt; TipBalloon=fh.Balloon; Expand="NO"; MinSize="80"; Padding=fh.Margin; action=function() intButt=intArg return iup.CLOSE end; } iup.Append( iupHbox, btnButt ) end end -- Create dialogue and turn off resize, maximize, minimize, and menubox except Close button local iupMemo = iup.dialog { Title=fh.Plugin..fh.Version..strHead; TopMost=strTop; -- TopMost added -- V3.6 iup.vbox { Alignment="ACENTER"; Gap=fh.Gap; Margin=fh.Margin; iup.frame { Title=strHead; FgColor=fgcHead; Font=fh.FontHead; iup.vbox { Alignment="ACENTER"; Font=fh.FontBody; lblMemo; lblLine; iupHbox; }; }; }; } fh.ShowDialogue("Memo",iupMemo,btnButt,strMode) -- Show popup Memo dialogue window with righthand button in focus (if any) if strMode == "keep dialogue" then return lblMemo end -- Return label control so message can be changed iupMemo:destroy() return intButt, tblButt[intButt] -- Return button number & title that was pressed end -- local function anyMemoDialogue function fh.MemoDialogue(anyMemo,...) -- Multi-Button GUI like iup.Alarm and fhMessageBox, with "Memo" in frame return anyMemoDialogue(fh.Head,"Memo",fh.Body,anyMemo,...) end -- function MemoDialogue function fh.WarnDialogue(anyHead,anyMemo,...) -- Multi-Button GUI like iup.Alarm and fhMessageBox, with heading in frame return anyMemoDialogue(fh.Warn,anyHead,fh.Warn,anyMemo,...) end -- function WarnDialogue function fh.GetRegKey(strKey) -- Read Windows Registry Key Value local luaShell = luacom.CreateObject("WScript.Shell") local anyValue = nil if pcall( function() anyValue = luaShell:RegRead(strKey) end ) then return anyValue -- Return Key Value if found end return nil end -- function GetRegKey function fh.PutRegKey(strKey,anyValue,strType) -- Write Windows Registry Key Value local luaShell = luacom.CreateObject("WScript.Shell") local strAns = nil if pcall( function() strAns = luaShell:RegWrite(strKey,anyValue,strType) end ) then return true end return nil end -- function PutRegKey local function httpRequest(strRequest) -- Luacom http request protected by pcall() below local http = luacom.CreateObject("winhttp.winhttprequest.5.1") http:Open("GET",strRequest,false) http:Send() return http.Responsebody end -- local function httpRequest function fh.VersionInStore(strPlugin) -- Obtain the Version in Plugin Store by Name only -- V3.9 local strVersion = "0" if strPlugin then local strFile = fh.MachinePath.."\\VersionInStore "..strPlugin..".dat" local intTime = os.time() - 2600000 -- Time in seconds a month ago -- V3.9 local tblAttr, strError = lfs.attributes(strFile) -- Obtain file attributes if not tblAttr or tblAttr.modification < intTime then -- File does not exist or was modified long ago -- V3.9 general.SaveStringToFile(strFile,strFile) -- Update file modified time local strFile = fh.MachinePath.."\\VersionInStoreInternetError.dat" local strRequest ="http://www.family-historian.co.uk/lnk/checkpluginversion.php?name="..strPlugin local isOK, strReturn = pcall(httpRequest,strRequest) if not isOK then -- Problem with Internet access local intTime = os.time() - 36000 -- Time in seconds 10 hours ago local tblAttr, strError = lfs.attributes(strFile) -- Obtain file attributes if not tblAttr or tblAttr.modification < intTime then -- File does not exist or was modified long ago fhMessageBox(strReturn.."\n The Internet appears to be inaccessible. ") end general.SaveStringToFile(strFile,strFile) -- Update file modified time else general.DeleteFile(strFile) -- Delete file if Internet is OK if strReturn ~= nil then strVersion = strReturn:match("([%d%.]*),%d*") -- Version digits & dots then comma and Id digits end end end end return strVersion or "0" end -- function VersionInStore local function intVersion(strVersion) -- Convert version string to comparable integer local intVersion = 0 local tblVersion = stringx.split(strVersion,".") for i=1,5 do intVersion = intVersion * 1000 + tonumber(tblVersion[i] or 0) end return intVersion end -- local function intVersion function fh.CheckVersionInStore() -- Check if later Version available in Plugin Store local strNewVer = fh.VersionInStore(fh.Plugin:gsub(" %- .*","")) local strOldVer = fh.Version if intVersion(strNewVer) > intVersion(strOldVer:match("%D*([%d%.]*)")) then fh.MemoDialogue("Later Version "..strNewVer.." of this Plugin is available from the Family Historian 'Plugin Store'.") end end -- function CheckVersionInStore function fh.PluginDataScope(strScope) -- Set default Plugin Data scope to per-Project, or per-User, or per-Machine strScope = tostring(strScope):lower() if strScope:match("mach") then -- Per-Machine strDefaultScope = "Machine" elseif strScope:match("user") then -- Per-User strDefaultScope = "User" end -- Per-Project is default end -- function PluginDataScope local function strToANSI(strFileName) if stringx.encoding() == "ANSI" then return strFileName end return fhConvertUTF8toANSI(strFileName) end -- local function strToANSI local function getPluginDataFileName(strScope) -- Get plugin data filename for chosen scope local isOK, strDataFile = pcall(fhGetPluginDataFileName,strScope) if not isOK then strDataFile = fhGetPluginDataFileName() end -- Before V5.0.8 parameter is disallowed and default = CURRENT_PROJECT return strToANSI(strDataFile) end -- local function getPluginDataFileName local function getDataFiles(strScope) -- Compose the Plugin Data file & path & root names local strPluginName = strToANSI(fh.Plugin) local strPluginPlain = stringx.plain(strPluginName) local strDataFile = getPluginDataFileName(strScope) -- Allow plugins with variant filenames to use same plugin data files strDataFile = strDataFile:gsub("\\"..strPluginPlain:gsub(" ","_"):lower(),"\\"..strPluginName) strDataFile = strDataFile:gsub("\\"..strPluginPlain..".+%.[D,d][A,a][T,t]$","\\"..strPluginName..".dat") if strDataFile == "" and strScope == "CURRENT_PROJECT" then -- Use standalone GEDCOM path & filename..".fh_data\Plugin Data\" as the folder + the Plugin Filename..".dat" strDataFile = strToANSI(fhGetContextInfo("CI_GEDCOM_FILE")) strDataFile = strDataFile:gsub("%.[G,g][E,e][D,d]",".fh_data") --# lfs.mkdir(strDataFile) general.MakeFolder(strDataFile) -- V3.4 strDataFile = strDataFile.."\\Plugin Data" --# lfs.mkdir(strDataFile) general.MakeFolder(strDataFile) -- V3.4 strDataFile = strDataFile.."\\"..strPluginName..".dat" end local strDataPath = strDataFile:gsub("\\"..strPluginPlain.."%.[D,d][A,a][T,t]$","") -- Plugin data folder path name local strDataRoot = strDataPath.."\\"..strPluginName -- Plugin data file root name general.MakeFolder(strDataPath) -- V3.4 return strDataFile, strDataPath, strDataRoot end -- local function getDataFiles function fh.Initialise(strVersion,strPlugin) -- Initialise the GUI module with optional Version & Plugin name local strAppData = strToANSI(fhGetContextInfo("CI_APP_DATA_FOLDER")) fh.Plugin = fhGetContextInfo("CI_PLUGIN_NAME") -- Plugin Name from file fh.Version = strVersion or " " -- Plugin Version if fh.Version == " " then local strTitle = "\n@Title is missing" local strAuthor = "\n@Author is missing" local strVersion = "\n@Version is missing" local strPlugin = strAppData.."\\Plugins\\"..fh.Plugin..".fh_lua" if general.FlgFileExists(strPlugin) then for strLine in io.lines(strPlugin) do -- Read each line from the Plugin file strPlugin = strLine:match("^@Title:[\t-\r ]*(.*)") if strPlugin then strPlugin = strPlugin:gsub("&&","&") --? if fh.Plugin:match("^"..strPlugin:gsub("(%W)","%%%1")) then if fh.Plugin:match("^"..stringx.plain(strPlugin)) then fh.Plugin = strPlugin -- Prefer Title to Filename if it matches strTitle = nil else strTitle = "\n@Title differs from Filename" -- Report abnormality end end if strLine:match("^@Author:%s*(.*)") then -- Check @Author exists strAuthor = nil end fh.Version = strLine:gsub("^@Version:%D*([%d%.]*)%D*"," %1 ") if fh.Version ~= strLine then -- Obtain the @Version from Plugin file strVersion = nil break end end if strTitle or strAuthor or strVersion then -- Report any header abnormalities fhMessageBox("\nScript Header: "..fh.Plugin..(strTitle or "")..(strAuthor or "")..(strVersion or "")) end else fhMessageBox("\nPlugin has not been saved!") end end fh.History = fh.Version -- Version History fh.Plugin = strPlugin or fh.Plugin -- Plugin Name from argument or default from file fh.CustomDialogue("Help","1020x730") -- Custom "Help" dialogue sizes fh.DefaultDialogue() -- Default "Font","Help","Main" dialogues fh.MachineFile,fh.MachinePath,fh.MachineRoot = getDataFiles("LOCAL_MACHINE") -- Plugin data names per machine fh.PerUserFile,fh.PerUserPath,fh.PerUserRoot = getDataFiles("CURRENT_USER") -- Plugin data names per user fh.ProjectFile,fh.ProjectPath,fh.ProjectRoot = getDataFiles("CURRENT_PROJECT") -- Plugin data names per project fh.FhDataPath = strToANSI(fhGetContextInfo("CI_PROJECT_DATA_FOLDER")) -- Paths used by Load/SaveFolder for relative folders fh.PublicPath = strToANSI(fhGetContextInfo("CI_PROJECT_PUBLIC_FOLDER")) -- Public data folder path name if fh.FhDataPath == "" then fh.FhDataPath = fh.ProjectPath:gsub("\\Plugin Data$","") end if fh.PublicPath == "" then fh.PublicPath = fh.ProjectPath fh.FhProjPath = fh.PublicPath:gsub("^(.+)\\.-\\Plugin Data$","%1") else general.MakeFolder(fh.PublicPath) -- V3.4 fh.FhProjPath = fh.PublicPath:gsub("^(.+)\\.-\\Public$","%1") end fh.CalicoPie = strAppData:gsub("\\Calico Pie\\.*","\\Calico Pie") -- Program Data Calico Pie path name fh.ComputerName = os.getenv("COMPUTERNAME") -- Local PC Computer Name end -- function Initialise fh.Initialise() -- Initialise module with default values return fh end -- local function iup_gui_v3 local iup_gui = iup_gui_v3() -- To access FH IUP GUI build module require "imlua" -- To access digital imaging library to convert Media image frames require "imlua_process" -- Preset Global Data Definitions -- function PresetGlobalData() iup_gui.Gap = "2" --# iup_gui.SetUtf8Mode() -- UTF8 mode not needed as only ASCII text & filepaths displayed StrPublic = iup_gui.PublicPath -- File paths are all always in ANSI StrFolder = StrPublic -- Folder for Website/CD-DVD HTML pages end -- function PresetGlobalData -- Reset Sticky Settings to Default Values -- function ResetDefaultSettings() iup_gui.CustomDialogue("Main","0x0") -- Custom "Main" dialogue minimum size & centralisation iup_gui.CustomDialogue("Font","0x0") -- Custom "Font" dialogue minimum size & centralisation iup_gui.DefaultDialogue("Bars","Memo") -- GUI window rastersize and X & Y co-ordinates for "Main","Font","Bars","Memo" dialogues --# StrFolder = StrPublic -- Folder for Website/CD-DVD HTML pages is NOT reset to default StrHomeHTML = "index.html" -- Home page HTML filename -- V3.0 StrStyleCSS = "custom/mystyle.css" -- Custom style CSS filepath -- V3.0 IntPages = 1 -- Hyperlinks open in same Page IntChars = 1 -- Hyperlinks never abbreviated IntTabSep = 1 -- Column Tab Separator disabled IntPopup = 1 -- Image Popups use default Lytebox v4.0 StrAlso = "OFF" -- 'See also' section removed -- V3.2 StrMini = "OFF" -- Minitrees align in centre -- V3.2 StrToPs = "OFF" -- Table of Pictures disabled StrToRs = "OFF" -- Table of Reports disabled StrToDs = "OFF" -- Table of Downloads disabled StrMenu = "OFF" -- Links in Menu Bar disabled StrFilter = "OFF" -- Filter Search box disabled -- V2.5 IntLength = 30 -- Length of Surnames in 1st column -- V3.0 IntNames = 1 -- Index of Names for Primary Names only IntStyle = 1 -- Index of Names added names in italics StrIdRiD = "ON" -- Record Identity -- V3.2 StrIdPRN = "OFF" -- Perm Record No -- V3.2 StrIdCiD = "OFF" -- Custom Identity -- V3.2 StrIdARI = "OFF" -- Auto Record Id -- V3.2 StrIdUID = "OFF" -- Unique Identity -- V3.2 StrIdFSI = "OFF" -- Family Search Id -- V3.2 IntGenDex = 4 -- GenDex excludes Individuals with Private/Living flags IntTabPosn = 0 -- Initial tab position undefined -- was nil before 31 -July 2013 end -- function ResetDefaultSettings -- Load Sticky Settings from File -- function LoadSettings() iup_gui.LoadSettings("Bars") -- Includes "Main","Font" dialogues and "FontSet" & "History" iup_gui.Balloon = "NO" -- V3.0 for Crossover/PlayOnLinux/Mac StrFolder = tostring(iup_gui.LoadFolder("Folder",StrFolder)) IntPages = tonumber(iup_gui.LoadGlobal("Pages" ,IntPages )) IntChars = tonumber(iup_gui.LoadGlobal("Chars" ,IntChars )) IntTabSep = tonumber(iup_gui.LoadGlobal("TabSep",IntTabSep)) IntPopup = tonumber(iup_gui.LoadGlobal("Popup" ,IntPopup )) StrAlso = tostring(iup_gui.LoadGlobal("Also" ,StrAlso )) -- V3.2 StrMini = tostring(iup_gui.LoadGlobal("Mini" ,StrMini )) -- V3.2 StrToPs = tostring(iup_gui.LoadGlobal("ToPs" ,StrToPs )) StrToRs = tostring(iup_gui.LoadGlobal("ToRs" ,StrToRs )) StrToDs = tostring(iup_gui.LoadGlobal("ToDs" ,StrToDs )) StrMenu = tostring(iup_gui.LoadGlobal("Menu" ,StrMenu )) StrFilter = tostring(iup_gui.LoadGlobal("Filter",StrFilter)) -- V2.5 IntLength = tonumber(iup_gui.LoadGlobal("Length",IntLength)) -- V3.0 IntNames = tonumber(iup_gui.LoadGlobal("Index" ,IntNames )) -- Legacy "Index" setting IntNames = tonumber(iup_gui.LoadGlobal("Names" ,IntNames )) IntStyle = tonumber(iup_gui.LoadGlobal("Style" ,IntStyle )) IntIdent = tonumber(iup_gui.LoadGlobal("Ident" ,IntIdent )) StrIdRiD = tostring(iup_gui.LoadGlobal("IdRiD" ,StrIdRiD )) -- V3.2 StrIdPRN = tostring(iup_gui.LoadGlobal("IdPRN" ,StrIdPRN )) -- V3.2 StrIdCiD = tostring(iup_gui.LoadGlobal("IdCiD" ,StrIdCiD )) -- V3.2 StrIdARI = tostring(iup_gui.LoadGlobal("IdARI" ,StrIdARI )) -- V3.2 StrIdUID = tostring(iup_gui.LoadGlobal("IdUID" ,StrIdUID )) -- V3.2 StrIdFSI = tostring(iup_gui.LoadGlobal("IdFSI" ,StrIdFSI )) -- V3.2 IntGenDex = tonumber(iup_gui.LoadGlobal("GenDex",IntGenDex)) IntTabPosn = tonumber(iup_gui.LoadGlobal("TabPosn",IntTabPosn)) if IntIdent == 2 or IntIdent == 6 then StrIdRiD = "ON" end -- V3.2 -- Legacy conversion if IntIdent == 3 or IntIdent == 6 then StrIdPRN = "ON" end -- V3.2 if IntIdent == 4 or IntIdent == 6 then StrIdCiD = "ON" end -- V3.2 if IntIdent == 5 or IntIdent == 6 then StrIdARI = "ON" end -- V3.2 if IntIdent == 6 then StrIdUID = "ON" StrIdFSI = "ON" end -- V3.2 IntIdent = nil SaveSettings() -- Save sticky data settings g_trans_misc = { Output_Language = ""; } -- Create global Language Pack translation table -- V3.2 if fhGetAppVersion() > 6 then local strLang = iup_gui.GetRegKey("HKCU\\SOFTWARE\\Calico Pie\\Family Historian\\2.0\\Preferences\\Output Language Id") if #strLang > 2 then local strPack = fhGetContextInfo("CI_APP_DATA_FOLDER").."\\Lang\\"..strLang.."\\lang_trans_misc.fh_lua" dofile(strPack) g_trans_misc.Output_Language = " for Output Language "..strLang else g_trans_misc.Output_Language = " for English" end end end -- function LoadSettings -- Translate text to current output language -- -- V3.2 function StrTranslate(strText) -- strText ~ Text to translate return g_trans_misc["~HTML:"..strText] or g_trans_misc[strText] or StrOther(strText) or strText end -- function StrTranslate function StrOther(strText) -- Try translating capitalised text, e.g. For 'father' try 'Father' and convert any translation to lower case -- strText ~ Text to capitalise and translate local strOther = g_trans_misc[strText:sub(1,1):upper()..strText:sub(2,-1)] if strOther then strOther = strOther:lower() end return strOther end -- function StrOther -- Save Sticky Settings to File -- function SaveSettings() iup_gui.SaveFolder("Folder",StrFolder) iup_gui.SaveGlobal("Pages" ,IntPages ) iup_gui.SaveGlobal("Chars" ,IntChars ) iup_gui.SaveGlobal("TabSep",IntTabSep) iup_gui.SaveGlobal("Popup" ,IntPopup ) iup_gui.SaveGlobal("Also" ,StrAlso ) -- V3.2 iup_gui.SaveGlobal("Mini" ,StrMini ) -- V3.2 iup_gui.SaveGlobal("ToPs" ,StrToPs ) iup_gui.SaveGlobal("ToRs" ,StrToRs ) iup_gui.SaveGlobal("ToDs" ,StrToDs ) iup_gui.SaveGlobal("Menu" ,StrMenu ) iup_gui.SaveGlobal("Filter",StrFilter) -- V2.5 iup_gui.SaveGlobal("Length",IntLength) -- V3.0 iup_gui.SaveGlobal("Names" ,IntNames ) iup_gui.SaveGlobal("Style" ,IntStyle ) iup_gui.SaveGlobal("Ident" ,IntIdent ) iup_gui.SaveGlobal("IdRiD" ,StrIdRiD ) -- V3.2 iup_gui.SaveGlobal("IdPRN" ,StrIdPRN ) -- V3.2 iup_gui.SaveGlobal("IdCiD" ,StrIdCiD ) -- V3.2 iup_gui.SaveGlobal("IdARI" ,StrIdARI ) -- V3.2 iup_gui.SaveGlobal("IdUID" ,StrIdUID ) -- V3.2 iup_gui.SaveGlobal("IdFSI" ,StrIdFSI ) -- V3.2 iup_gui.SaveGlobal("TabPosn",IntTabPosn) iup_gui.SaveSettings("Bars") -- Includes "Main","Font" dialogues and "FontSet" & "History" end -- function SaveSettings -- Convert UTF-8 Filenames to ANSI -- -- V2.8 function StrToANSI(strFileName) -- strFileName ~ File path to convert to ANSI if string.encoding() == "ANSI" then return strFileName end return fhConvertUTF8toANSI(strFileName) end -- function StrToANSI -- Copy a file from source to target -- -- V2.8 function CopyFile(strSource,strTarget) -- strSource ~ File path to copy from -- strTarget ~ File path to copy to local strPath = general.SplitFilename(strTarget) -- Does folder path exist? if not general.FlgFolderExists(strPath) then local strDev, strDir = strPath:match("^(%u:)\\(.+)$") -- Such as C:\Path\Path if #strDev == 0 then strDev, strDir = strPath:match("^(\\\\.-)\\(.+)$") -- Such as \\NAME\Path\Path end strPath = strDev for intDir, strDir in ipairs (strDir:split("\\")) do -- Reconstruct path folder by folder strPath = strPath.."\\"..strDir if not general.FlgFolderExists(strPath) then -- Create any missing folder local isOK, strError = lfs.mkdir(strPath) if not isOK then error( "\n\n Cannot make "..tostring(strError)..". \n\n "..strPath.." \n\n Operation cancelled. \n ") end end end end if strSource:match("^%u:\\") or strSource:match("^\\\\.-\\") then local fileSource = assert(io.open(strSource,"rb")) -- Read entire source file strSource = fileSource:read("*all") assert(fileSource:close()) end local fileTarget, strError = io.open(strTarget,"wb") -- Try mode Write Binary (this sometimes asserts a failure, so try other modes) if not fileTarget then fileTarget, strError = io.open(strTarget,"w+b") -- Try mode Write Update Binary if not fileTarget then fileTarget, strError = io.open(strTarget,"w+") -- Try mode Write Update if not fileTarget then fileTarget, strError = io.open(strTarget,"w") -- Try mode Write if not fileTarget then error("\n\n Unable to open target file in \"wb\" or \"w+b\" or \"w+\" or \"w\" mode. \n\n "..strTarget.." \n\n "..tostring(strError).." \n ") end end end end fileTarget:write(strSource) -- Write source file to target assert(fileTarget:close()) end -- function CopyFile -- Load icon files to ProgramData -- -- V2.8 -- V3.0 function DownloadIcons() local tblList = { doc_icon; etc_icon; pdf_icon; txt_icon; } for _, dicList in ipairs (tblList) do local strFile = iup_gui.MachinePath.."\\"..dicList.File if not general.FlgFileExists(strFile) then -- Decode missing icon file from embedded code local arrFile = {} for strByte in dicList.Code:gmatch("%x%x") do table.insert(arrFile,string.char(tonumber(strByte,16))) end local filFile = assert(io.open(strFile,"wb")) filFile:write(table.concat(arrFile)) -- Save icon file to ProgramData\Plugin Data folder assert(filFile:close()) end end end -- function DownloadIcons -- Convert file type to icon filename -- -- V2.8 function StrIconName(strType) -- strType ~ Type of icon file to choose if it exists local strFile = etc_icon.File local strIcon = strFile:gsub("_etc_","_"..strType:sub(1,3):lower().."_") if not general.FlgFileExists(iup_gui.MachinePath.."\\"..strIcon) then strIcon = strFile end return strIcon end -- function StrIconName -- Convert all URL & Hypertext in every required HTML Web/CD/DVD page -- function IntImproveHTML(strFolder,intPages,intChars,strTabSep) -- strFolder ~ Website HTML folder -- intPages ~ Open URL pages option -- intChars ~ Abbrevciate URL option -- strTabSep ~ Tab separator option -- Convert any URL into Hyperlinks and translate any Text placeholders -- local tblPages = { ''; ' target="_blank"'; } -- Target page options local tblChars = { 999; 30; 50; 70; 90; } -- URL length options local tblAnchor = {} -- Table of Anchor parameters tblAnchor.Style = ' class="fhextlink"' -- Set class style anchor attribute as used by FH tblAnchor.Pages = tblPages[intPages] -- Set target page anchor attribute for GUI option tblAnchor.Chars = tonumber(tblChars[intChars]) -- Set abbreviated URL char length for GUI option tblAnchor.Match = '[A-Za-z0-9#&/:;=@_%%%+%-%.%?]+[^\t-\r !-/:-@%[\\%]^_`%]{-~]' -- Set pattern for unquoted URL -- [A-Za-z0-9@:/=&;#_%?%.%+%-%%] allows URL to contain alphanumeric characters A-Z a-z 0-9 and symbols # & / : ; = @ _ % + - . ? -- [^\t-\r !-/:-@%[\\%]^_`%]{-~] prevents last character from being tabs/spaces or punctuation symbols # & / : ; = @ _ % + - . ? , " ' < > ! £ $ ^ * ( ) { } [ ] ~ | \ ¬ ` -- For details see http://en.wikipedia.org/wiki/Uniform_resource_locator and http://en.wikipedia.org/wiki/URI_scheme strTabSep = strTabSep:gsub('[\t-\r ]+','') -- Adjust column table separator strTabSep = strTabSep:gsub('{TAB}','\t') strTabSep = strTabSep:match('(%W+)$') local tblLine = {} -- V2.5 -- Accumuated lines when reverting to original when strTabSep is nil -- Parse any Anchor tag into its component parts and adjust class & target attributes, and link text length local function strParse(strAnchor,tblAnchor) -- strAnchor ~ HTML anchor tag -- tblAnchor ~ Default anchor mode local strStyle = tblAnchor.Style local strPages = tblAnchor.Pages local strMatch = tblAnchor.Match local strAttr, strLink = strAnchor:match('(.-)') -- Split anchor tag into attributes and link text parts -- Parse attributes with value in "double-quotes" or enclosed in 'single-quotes' or without any quotes at all or use default value local strHref = strAttr:match('(%shref=".-")') or strAttr:match("(%shref='.-')") or strAttr:match('(%shref='..strMatch..')') or ' href="http://www.null.com"' local strClass = strAttr:match('(%sclass=".-")') or strAttr:match("(%sclass='.-')") or strAttr:match('(%sclass='..strMatch..')') or strStyle local strTarget = strAttr:match('(%starget=".-")') or strAttr:match("(%starget='.-')") or strAttr:match('(%starget='..strMatch..')') or strPages strAttr = strAttr:replace(strHref,''):replace(strClass,''):replace(strTarget,''):gsub('^[\t-\r ]+',' '):gsub('[\t-\r ]+$','') strHref = strHref:gsub('%shref=([^\'"].-[^\'"])$',' href="%1"') -- Add missing quotes to href=URL strClass = strClass:gsub('%sclass=([^\'"].-[^\'"])$',' class="%1"') -- Add missing quotes to class=style strTarget = strTarget:gsub('%starget=([^\'"].-[^\'"])$',' target="%1"') -- Add missing quotes to target=page if strHref:match('href=%pmailto:') then strTarget = '' -- Remove target attribute from mailto protocol scheme elseif strTarget == tblPages[1] or strTarget == tblPages[2] then strTarget = strPages -- Set target attribute for all other protocol schemes end local intChars = tblAnchor.Chars -- Abbreviate long URL link text to chosen character length local strTail = '...' local strURL = strHref:match('%shref=%p(.-)%p$') -- Extract URL from href="URL" local intLink = strLink:len() - strTail:len() if strURL == strLink -- Abbreviations apply only if URL matches link text or ( strURL:sub(1,intLink) == strLink:sub(1,intLink) and strLink:match(strTail..'$') ) then -- or URL matches abbreviated link text if strURL:len() > intChars then strLink = strURL:sub(1,intChars)..strTail -- Apply abbreviated link text else strLink = strURL -- Cancel abbreviated link text end end return ''..strLink..'' -- Compose entire anchor end -- local function strParse -- Convert all URL protocol schemes, plus class & target attributes, and link text length local function strHyper(strSource,tblAnchor) -- strSource ~ Original URL text -- tblAnchor ~ Default anchor mode local strString = strSource if strString:match('^$') then strString = strString:gsub(' target="_blank"','') -- Adjust non-image Media hyperlinks -- V2.8 strString = strString:gsub(' title="',tblAnchor.Pages..' title="') elseif strString:matches('://') or strString:matches('mailto:') or strString:matches('www.') or strString:matches('<a href=') then -- Recognise user link format and convert to valid XHTML strString = strString:gsub('<a%s(.-href=.-)>(.-)</a>','%2') -- Convert URL from FH text field where < = < and > = > local strPrefix,strAnchor,strSuffix = strString:match('(.-)(]-href=.-)(.*)') -- Split text line into text prefix, anchor tag, and text suffix if strAnchor then -- Existing URL hyperlink anchor tag found strAnchor = strParse(strAnchor,tblAnchor) -- Parse anchor into its component parts and reassemble strString = strHyper(strPrefix,tblAnchor)..strAnchor..strHyper(strSuffix,tblAnchor) -- Recursively call strHyper for prefix and suffix else local strMatch = tblAnchor.Match strString = strString:gsub('(&[lg]t;)','<<%1>>') -- Protect symbols < < and > > for i, strScheme in ipairs( { '(.)(http://'; '(.)(https://'; '(.)(ftp://'; '([^/])(www%.'; '(.)(mailto:'; } ) do strString = strString:gsub(strScheme..strMatch..')','%1%2') -- Repeat for each URL protocol scheme and www. without a scheme end strString = strString:gsub('<<(&[lg]t;)>>','%1') -- Unprotect symbols < < and > > if strString ~= strSource then strString = strHyper(strString,tblAnchor) end -- Prevent indefinite recursion loop end strString = strString:gsub(' href=(%p)www%.',' href=%1http://www.') -- Add the http:// scheme to href="www. -- V3.1 Remove redundant % from www%. replacement text end return strString end -- local function strHyper -- Convert magic {{MEDIA:filepath|caption}} into a non-image file hyperlink and icon -- -- V2.8 local function strMedia(strPre,strMed,strSuf) -- strPre ~ Prefix before MEDIA -- strMed ~ MEDIA non-image details -- strSuf ~ nSuffix after MEDIA local strPref = "_" if general.FlgFolderExists(strFolder.."\\media") then -- Prefer \_media\ unless \media\ sub-folder exists strPref = "" end local strSource, strTarget local strFile, strText = strMed:match("^([^|]+)|?(.-)$") local strPath, strName = general.SplitFilename(strFile) strText = strText:gsub("\\n","
") if strText == "" then strText = strName end -- Use file Name if no caption text local strTitle = strText:gsub("
"," ") strFile = StrToANSI(strFile) -- Ensure file path & name is in ANSI strName = StrToANSI(strName) if strFile:match("^Media\\") then strSource = iup_gui.FhDataPath.."\\"..strFile -- Relative file path needs absolute source else strSource = strFile -- Absolute file path needs relative URL path strFile = "media\\"..strName -- Add media sub-folder to file Name -- V3.2 end strFile = (strPref..strFile):lower( ):gsub("%s","_") -- Convert into URL file path format -- V3.2 strTarget = strFolder.."\\"..strFile -- Optional underscore folder prefix -- V3.2 if general.FlgFileExists(strSource) and not general.FlgFileExists(strTarget) then CopyFile(strSource,strTarget) -- Source file exists but Target file does not end strFile = encoder.StrANSI_URI(strFile) -- or StrANSI_XML ? strFile = strFile:gsub("%%5C","/") local strBef = '\n

' local strAft = '

' if strPre:match('^

') then -- Set XHTML tags for context strBef = '\n

' strAft = '\n
' elseif strPre:match('^$') then strSuf = strSuf:gsub('$','') strAft = strAft..'\n' elseif strSuf:match('$') then strSuf = strSuf:gsub('$','') strAft = strAft..'\n' end elseif strPre:match('^

') then -- Sources list FH V7 -- V3.2 if strSuf:match('

$') then strBef = '\n
' strAft = '\n
' else strBef = '\n
' strAft = '\n
' end end local strLine = strPre..strSuf -- Prune empty components strLine = strLine:gsub('

-

',"") -- Record Note strLine = strLine:gsub(' -',"") -- Fact Note strLine = strLine:gsub('

',"") -- Note in FH V7 -- V3.2 strLine = strLine:gsub(' -, ',"") -- Author strLine = strLine:gsub(' %( -%)%.',"") -- Publication Info strLine = strLine:gsub('Custom Id: -%.',"") -- Custom Id strLine = strLine:gsub('Text From Source: -',"") -- Text From Source strLine = strLine:gsub('%.%.',"."):gsub(' -%.',"") -- Source Note if strLine == "" then strBef = strBef:gsub('^\n',"") end local strIcon = StrIconName(strFile:match("%.(%l+)$") or "etc") CopyFile(iup_gui.MachinePath.."\\"..strIcon,StrFolder.."\\"..strIcon) -- Copy icon file from ProgramData to Web page folder return strLine..strBef..'\n\nmedia\n\n

'..strText..'

'..strAft end -- local function strMedia -- Convert any data line with separators into fixed format table of rows and columns -- V2.4 local function strTable(strLine,tblFile,strSep) -- strLine ~ Line of text -- tblFile ~ Table of converted lines -- strSep ~ Tab separator if strSep then -- V2.5 -- Table separator is defined strSep = strSep:gsub("…","+") if strLine:match('
') and strLine:match(strSep) -- Convert table separated text and not ( strSep == "," and strLine:match('^') ) then -- unless separator is comma and line in main section local int1, int2, strTag, strAll = strLine:find('^()(.-)$') -- Find component parts of table if int1 and int2 and strTag and strAll then local tblTxt = {} -- Text per table cell row & column local intRow = 0 -- Table cell row index local intCol = 0 -- Table cell column index local intMax = 0 -- Maximum column index local intSum = 0 -- Sum of lengths of biggest text per column for strRow in (strAll..'
'):gmatch('(.-)
') do -- Extract each row of text from table intRow = intRow + 1 intCol = 0 tblTxt[intRow] = {} local strPre = "" local strBrk = strSep:replace("+","") for strCol in (strRow..strBrk):gmatch('(.-)'..strSep) do -- Extract each column of text from rows intCol = intCol + 1 tblTxt[intRow][intCol] = strPre..strCol -- Save each text cell per row & column strPre = strBrk intMax = math.max(intCol,intMax) end end tblTxt.Big = {} -- Length of biggest text per column for intRow, tblRow in ipairs(tblTxt) do for intCol, strCol in ipairs(tblRow) do if tblRow[intCol+1] or intCol == intMax then -- Ignore last column in each row, unless rightmost column in table local intLen = math.max(1,#tblRow[intCol]) -- Length of text, but at least 1 local intBig = tblTxt.Big[intCol] or 0 if intLen > intBig then tblTxt.Big[intCol] = intLen -- Save biggest length per column intSum = intSum - intBig + intLen -- Sum all biggest lengths end end end end local strStyle = 'width:'..( 100 / math.ceil( 50 / intSum ) )..'%;' -- V2.4 -- If narrow table, then reduce % width of window for intCol, intWidth in ipairs ( tblTxt.Big ) do intWidth = intWidth / intSum * 100 -- Calculate % width of column strStyle = strStyle..' td.col'..intCol..' {width:'..intWidth..'%}' -- Set style class for column: td.col9 {width:99%} end table.insert(tblFile,strTag) -- Preserve opening tag table.insert(tblFile,' ') -- V2.4 Insert opening tag with style for intRow, tblRow in ipairs(tblTxt) do table.insert(tblFile,' ') -- Insert opening tag per row for intCol, strCol in ipairs(tblRow) do if tblRow[intCol+1] then table.insert(tblFile,' ') -- V2.4 Insert tag per col with style class else if intCol == 1 and strCol:match("^%s-$") then strCol = " " -- If 1st and only column is empty use No Break Space end table.insert(tblFile,' ') -- Allow last column in row to span later columns end end table.insert(tblFile,' ') -- Insert closing tag per row end table.insert(tblFile,'
'..strCol..''..strCol..'
') -- Insert closing tag strLine = '' -- Preserve closing tag end end else -- V2.5 -- Option "None" reverts table to original line if strLine:match('^$') then -- Assumes no other cases where is alone on line tblLine = {} table.insert(tblLine,strLine) -- Opening tag starts accumulation of lines strLine = nil elseif #tblLine > 0 then if strLine:match('^ +.*$') then -- Found column text local strType, strText = strLine:match('^ +(.*)$') table.insert(tblLine,strText) -- Accumulate the column text if strType == "colspan" then table.insert(tblLine,"
") end -- Last column uses colspan so add break tag
strLine = nil elseif strLine == "" then -- Closing tag ends accumulation of lines tblLine[#tblLine] = "" -- Replace last break tag with strLine = table.concat(tblLine) -- Reconstruct the original line tblLine = {} else strLine = nil -- Otherwise remove line end end end return strLine end -- local function strTable -- Process my Locations Database only if 'myloc.html' exists local function strMyLoc(strString,tblAnchor,strFolder,strFile) local strTarget = tblAnchor.Pages local strClass = tblAnchor.Style -- Insert hyperlink Locations into standard menubar e.g. before
  • strString = strString:gsub('
  • ',"
  • Locations
  • \n
  • ") if strFile:match('^fam%d+%.html$') or strFile:match('^ind%d+%.html$') then -- Convert source location entry into locations database hyperlink e.g. ~ Database Address ~. Custom Id: 9999.< allowing for illegally embedded anchor strString = strString:gsub('~ (.-)(.-) ~%. Custom Id: ([0-9]-)%.<','Locations Database Record %1 %3 <') strString = strString:gsub('~ (.-) ~%. Custom Id: ([0-9]-)%.<','Locations Database Record %1<') end if strFile == StrHomeHTML then -- V3.0 -- Change home page ToC hyperlink for Locations database e.g. from "toc[0-9]-.html" to "myloc.html" -- V3.0 strString = strString:gsub('^
  • Locations(.-)
  • $','
  • Locations%1
  • ') end if strFile == 'myloc.html' then -- Convert to 'Locations Database' e.g. <title>~ Database Address ~ strString = strString:gsub('~ .- ~','Locations Database') -- Insert 'Locations Database' heading after menubar section before content e.g. before
    strString = strString:gsub('
    ',"

    Locations Database

    \n
    ") -- Convert >RecordId~ to hyperlink anchor targets e.g.

    [9999] Database Address

    strString = strString:gsub('

    %[([0-9]-)%] (.-)

    ',"\n\n

    %2

    ") local arrStyleCSS = StrStyleCSS:split("/") -- V3.0 local strCustom = strFolder..'\\'..( arrStyleCSS[1] or "custom" ) local strMyStyle = strCustom..'\\'..( arrStyleCSS[2] or "mystyle.css" ) if strString == '' and general.FlgFolderExists(strCustom) then -- V3.0 local strFile = 'myimage.jpg' -- Define 'myimage.jpg' background image file local strSource = StrPublic..'\\'..strFile local strTarget = strCustom..'\\'..strFile if general.FlgFileExists(strSource) and not general.FlgFileExists(strTarget) then -- V3.0 local fileSource = assert(io.open(strSource,'rb')) local fileTarget, strError = io.open(strTarget,'wb') fileTarget:write(fileSource:read('*all')) -- Copy entire 'myimage.jpg' background image assert(fileSource:close()) assert(fileTarget:close()) end if not general.FlgFileExists(strMyStyle) then -- Define custom CSS style sheet -- V3.0 local str_mystyle_css = [=[ body, p, td, li, ul, table {font-family: Arial, sans-serif; } /* Select text font family */ .FhSiteTitle { font-family: Arial, Helvetica, sans-serif; } .FhPageTitle { font-family: Arial, Helvetica, sans-serif; } .FhTOC { font-family: Arial, Helvetica, sans-serif; } .FhHeader { font-family: Arial, Helvetica, sans-serif; } .FhMiniTitle { font-family: Arial, Helvetica, sans-serif; } .FhMiniEntry { font-family: Arial, Helvetica, sans-serif; } .FhHdg1 { font-family: Arial, Helvetica, sans-serif; } .FhHdg2 { font-family: Arial, Helvetica, sans-serif; } .FhLbl2 { font-family: Arial, Helvetica, sans-serif; } .FhDat2 { font-family: Arial, Helvetica, sans-serif; } .FhHdg3 { font-family: Arial, Helvetica, sans-serif; } .FhLbl3 { font-family: Arial, Helvetica, sans-serif; } .FhDat3 { font-family: Arial, Helvetica, sans-serif; } .FhCaption { font-family: Arial, Helvetica, sans-serif; } .bodylink { font-family: Arial, Helvetica, sans-serif; } body { background: url("myimage.jpg") #D8D0BC ; } /* Background image file or colour */ .FhSiteTitle { font-size: 22pt; font-weight: bold; color: green; } /* Title font size, weight & colour */ .FhPageTitle { font-size: 20pt; font-weight: bold; color: green; } .FhPageTitleCentred { font-size: 20pt; font-weight: bold; color: green; } .FhHdg1 { font-size: 16pt; font-weight: bold; color: green; } .FhHdg2 { font-size: 12pt; font-weight: bold; color: green; } .FhHdg3 { font-size: 10pt; font-weight: bold; color: green; } .FhWelcome form { text-align: center; } /* Needed to centre 'Close Window' button */ .FhIndexList ul li { margin-left:10em; } /* Adjust Index of Names surname column */ ]=] general.SaveStringToFile(str_mystyle_css,strMyStyle) -- Create custom CSS style sheet end end end return strString end -- local function strMyLoc -- Determine if my Locations Database needs processing local function isMyToC(strFolder,strFile) -- If Home page HTML file has '
  • Locations...
  • ' then rename "toc[0-9]-.html" to "myloc.html" and set isMyLoc = true local isMyLoc = false if strFile == StrHomeHTML then -- V3.0 for strLine in io.lines(strFolder..'\\'..StrHomeHTML) do -- Search the Home page file (Unicode UTF-8 not needed) local strToC = strLine:match('^
  • Locations.-
  • $') -- toc[0-9]- -- V3.0 if strToC then local isOK, strErr = os.rename(strFolder..'\\'..strToC,strFolder..'\\myloc.html') isMyLoc = true break end end end if strFile == 'myloc.html' then isMyLoc = true end return isMyLoc end -- local function isMyToC -- Determine if file is HTML that needs converting local function isHTML(strFile) -- strFile ~ Filename return strFile == StrHomeHTML or -- Home Welcome page -- V3.0 strFile == '_contact.html' or -- Contact Details page strFile == '_statistics.html' or -- Statistics page strFile == '_nameindex.html' or -- Index of Names page strFile:match('^fam%d+%.html$') or -- 'fam99.html' Family pages strFile:match('^ind%d+%.html$') or -- 'ind99.html' Individual pages strFile:match('^toc%d+%.html$') or -- 'toc99.html' ToC pages strFile == 'myloc.html' -- 'myloc.html' my Locations Database page end -- local function isHTML -- Search each HTML file and convert any URL progbar.Setup(iup_gui.DialogueAttributes("Bars")) -- Pass parameters into new Progress Bar prototype local intLine = 0 -- Count of lines with converted URL local intFile = 0 -- Count of suitable HTML files local isMyLoc = false local datDate = fhNewDate() datDate:SetSimpleDate(fhCallBuiltInFunction('Today')) -- Obtain date for {{DATE:***}} DownloadIcons() -- Load media icons into ProgramData for {{MEDIA:file|text}} -- V2.8 for strFile in lfs.dir(strFolder) do if isMyToC(strFolder,strFile) then isMyLoc = true end -- Is my Locations Database present? if isHTML(strFile) then intFile = intFile + 1 end -- Count suitable HTML files end if intFile > 100 then progbar.Start("Converting URL",intFile) -- Start progress bar end intFile = 0 for strFile in lfs.dir(strFolder) do if isHTML(strFile) then -- Suitable HTML file found intFile = intFile + 1 if intFile % 23 == 0 then progbar.Message("Updating HTML page "..strFile) -- Reduce rate of messages -- V3.2 progbar.Step(23) collectgarbage("step",0) -- Improves run time in loops! -- V3.2 end strPath = strFolder..'\\'..strFile local tblFile = {} -- Table of lines in file local flgSave = false -- Flag if file needs saving local flgLine = false -- Flag if line needs converting -- V2.4 ( false/true in or nil/ in ) for strLine in io.lines(strPath) do -- Read each HTML file line by line (Unicode UTF-8 not needed) if strLine == '~' then flgLine = ( strFile == 'myloc.html' ) end -- V2.4 -- Convert <head> <title>~ in myloc.html if strLine == '<body>' then flgLine = strLine end -- V2.4 -- Convert <body> lines otherwise if flgLine == '<body>' then -- V2.4 :- if strLine:match('^<div class="FhIndexList">') -- Exclude <body> Index List in _nameindex.html or strLine:match('^<a href=".+_full%.jpg" ') -- Exclude <body> popup image title -- V2.8 or strLine:match('<n?o?script') then flgLine = nil end -- Exclude <body> java script, etc end if flgLine then -- In *.html <body> or myloc.html <head> local strLink = strHyper(strLine,tblAnchor) -- Convert anchor hyperlinks if strLink:match('{{%u%u%u-:.+}}') then -- Convert magic DATE & FILE & MEDIA codes -- V2.8 strLink = strLink:gsub('{{DATE:(.-)}}', function(strQual) return datDate:GetDisplayText(strQual) end ) strLink = strLink:gsub('{{FILE:NAME}}', encoder.StrANSI_XML(strFile) ) strLink = strLink:gsub('{{FILE:PATH}}', encoder.StrANSI_XML(strPath) ) repeat local strPrev = strLink strLink = strLink:gsub('^(.-){{MEDIA:(.-)}}(.+)$', strMedia ) -- V2.8 until strPrev == strLink end if strFile == StrHomeHTML then -- Optionally insert 'Close Window' button after <div class="FhWelcome"> and mystyle.css needs .FhWelcome form { text-align: center; } -- V3.0 strLink = strLink:gsub('^<div class="FhWelcome"><p>(.-){{CLOSE}}(.-)</p>$',"<div class='FhWelcome'><p>%1 %2</p>\n<form action='javascript:void(0)'>\n<input type='button' value='"..StrTranslate("Close Window").."' onclick='javascript:window.close()'/>\n</form>") end if isMyLoc then strLink = strMyLoc(strLink,tblAnchor,strFolder,strFile) -- Convert my Locations Database if strLine:match('~') then flgLine = false end -- V2.4 end strLink = strTable(strLink,tblFile,strTabSep) -- V2.5 -- Convert tabulated columns (can return nil to remove line) if strLine ~= strLink then -- Converted/removed line detected strLine = strLink if strLine then intLine = intLine + 1 end -- V2.5 -- Count converted lines but not removed lines flgSave = true end elseif flgLine == nil then -- V2.4 -- Excluding some tags if strLine:match('^
    ') -- Resume after
    at end of List or strLine:match('^ 6 then relFile = "~.FILE" end -- FH V7 -- V3.1 function IntMediaIcons() local function isNonImageType(strFile) -- Check if Media has popular image file type or not -- V3.0 -- strFile ~ File path local dicType = { jpg=1; jpeg=1; png=1; gif=1; tif=1; tiff=1; bmp=1; } local _,_,strType = general.SplitFilename(strFile) return ( dicType[strType:lower()] ~= 1 ) end -- local function isNonImageType local function isInReports(ptrObje) -- Check if Media is included in any Reports -- V3.0 -- ptrObje ~ Pointer to media local isInReports = false local ptrNote = fhGetItemPtr(ptrObje,"~.NOTE2") while ptrNote:IsNotNull() do local strExcl = fhGetValueAsText(fhGetItemPtr(ptrNote,"~._EXCL")) if not strExcl:match("Reports") then isInReports = true end ptrNote:MoveNext("SAME_TAG") end return isInReports or (fhGetAppVersion() > 6) -- FH V7 _EXCL difficult to find -- V3.1 end -- local function isInReports local intFile = 0 -- Count of non-image Media files local ptrObje = fhNewItemPtr() local intObje = 0 -- Count of Media Object records -- V3.0 progbar.Setup(iup_gui.DialogueAttributes("Bars")) -- Pass parameters into new Progress Bar prototype ptrObje:MoveToFirstRecord("OBJE") while ptrObje:IsNotNull() do intObje = intObje + 1 -- Count the Media Object records ptrObje:MoveNext() end if intObje > 100 then progbar.Start("Preparing non-image Media icons",intObje) -- Start progress bar -- V3.0 end DownloadIcons() -- Load icons into ProgramData folder ptrObje:MoveToFirstRecord("OBJE") -- Search every Media record while ptrObje:IsNotNull() do local ptrFile = fhGetItemPtr(ptrObje,relFile) -- FH V7 "~.FILE" or FH V6 "~._FILE" -- V3.1 while ptrFile:IsNotNull() do local strFile = fhGetValueAsText(ptrFile) -- If relative Media path then make absolute local getFile = StrToANSI(strFile):gsub("^Media\\",function() return iup_gui.FhDataPath.."\\Media\\" end) -- V3.2 if #getFile > 0 and general.FlgFileExists(getFile) and isNonImageType(getFile) and isInReports(ptrObje) then -- File exists with non-image extension -- V3.0 local getImage, intErr = im.FileImageLoad(getFile) -- Try to load image from Media file if intErr and intErr ~= im.ERR_NONE then -- If errors then got non-image file local isOK = false local ptrForm = fhGetItemPtr(ptrObje,"~.FORM") -- FH V6 OBJE.FORM -- V3.1 if fhGetAppVersion() > 6 then ptrForm = fhGetItemPtr(ptrFile,"~.FORM") end -- FH V7 OBJE.FILE.FORM -- V3.1 local strForm = fhGetValueAsText(ptrForm) local ptrChan = fhGetItemPtr(ptrObje,"~.CHAN") if ptrChan:IsNull() then ptrChan = fhCreateItem("CHAN",ptrObje,true) end local ptrNote = fhCreateItem("NOTE2",ptrChan,true) -- FH V7 Last Change Note instance per File -- V3.1 if ptrNote:IsNotNull() then -- Save old Format & File in new Last Change Note isOK = fhSetValueAsText(ptrNote,"{{FORM}}"..strForm.."{{FILE}}"..strFile) isOK = fhSetValueAsText(ptrForm,"jpg") -- Substitute ProgramData icon file isOK = fhSetValueAsText(ptrFile,iup_gui.MachinePath.."\\"..StrIconName(strForm)) intFile = intFile + 1 end progbar.Message(" Record Id: "..fhGetRecordId(ptrObje)) -- V3.0 else getImage:Destroy() -- Otherwise it was an image file so remove image buffer end fhSleep(5,5) -- Avoid not responding state -- V3.0 end ptrFile:MoveNext("SAME_TAG") end ptrObje:MoveNext() progbar.Step(1) -- V3.0 if progbar.Stop() then break end end progbar.Close() -- Close the Progress Bar -- V3.0 return intFile end -- function IntMediaIcons -- Convert all image Popups in every required HTML Web/CD/DVD page -- function IntImprovePopup(strFolder,strPopup) -- strFolder ~ Website HTML folder -- strPopup ~ Popup version option -- Update Media records with non-image file and copy to webpage folder -- -- V2.8 local arrMedia = {} local function intNonImageMedia() local strPref = "_" if general.FlgFolderExists(strFolder.."\\media") then -- Prefer \_media\ unless \media\ sub-folder exists strPref = "" end local intObje = 0 local ptrObje = fhNewItemPtr() ptrObje:MoveToFirstRecord("OBJE") -- Search every Media record while ptrObje:IsNotNull() do local ptrFile = fhGetItemPtr(ptrObje,relFile) -- FH V7 "~.FILE" or FH V6 "~._FILE" -- V3.1 while ptrFile:IsNotNull() do local strFile = fhGetValueAsText(ptrFile) if strFile:match(iup_gui.MachinePath.."\\improve_%l+_icon%.jpg$") then -- Found icon image Media local ptrForm = fhGetItemPtr(ptrObje,"~.FORM") -- FH V6 OBJE.FORM -- V3.1 if fhGetAppVersion() > 6 then ptrForm = fhGetItemPtr(ptrFile,"~.FORM") end -- FH V7 OBJE.FILE.FORM -- V3.1 local ptrNote = fhGetItemPtr(ptrObje,"~.CHAN.NOTE2") -- FH V7 Scan Last Change Notes -- V3.1 while ptrNote:IsNotNull() do local strForm,strFile = fhGetValueAsText(ptrNote):match("^{{FORM}}(.+){{FILE}}(.+)") if strForm and strFile then local isOK = fhSetValueAsText(ptrForm,strForm) -- Restore original Format & File local isOK = fhSetValueAsText(ptrFile,strFile) -- FH V7 -- V3.1 local isOK = fhDeleteItem(ptrNote) local strSource, strTarget strFile = StrToANSI(strFile) if strFile:match("^Media\\") then strSource = iup_gui.FhDataPath.."\\"..strFile -- Relative file path needs absolute source else local _, strName = general.SplitFilename(strFile) -- Absolute file path needs relative URL path strSource = strFile strFile = "media\\"..strName -- Add media sub-folder to file Name -- V3.2 end strFile = (strPref..strFile):lower( ):gsub("%s","_") -- Convert into URL file path format -- V3.2 strTarget = strFolder.."\\"..strFile -- Optional underscore folder prefix -- V3.2 strFile = encoder.StrANSI_URI(strFile) -- or StrANSI_XML ? strFile = strFile:gsub("%%5C","/") if general.FlgFileExists(strSource) and not general.FlgFileExists(strTarget) then -- Source file exists but Target file does not if strSource:match("%.url$") then strFile = general.StrLoadFromFile(strSource):match("\nURL=(.-)\n") -- Extract URL from shortcut else CopyFile(strSource,strTarget) end end local intRid = fhGetRecordId(ptrObje) -- Save target File path against Rec Id for strAnchor() arrMedia[tostring(intRid)] = arrMedia[tostring(intRid)] or strFile -- Only save 1st non-image File path in FH V7 -- V3.1 break end ptrNote:MoveNext("SAME_TAG") -- FH V7 Search Last Change Note instances -- V3.1 end intObje = intObje + 1 end ptrFile:MoveNext("SAME_TAG") end ptrObje:MoveNext() end return intObje end -- local function intNonImageMedia -- Convert any anchor tag Popups -- local function strAnchor(strLine,strPopup,strName) -- V2.4 entirely rewritten -- strLine ~ Current text line -- strPopup ~ Popup version option -- strName ~ Name of popup id local intLine = 0 if strLine:match('^<[a!]%-- href="') then -- Found popup anchor/comment tag strLine = strLine:gsub('^ 0 then strLine = strLine:gsub(' href="img_',' href="_h1_') -- Restore the Home Welcome page image prefix -- V3.0 end else local strRid = strLine:match('^picture 0 end -- local function strAnchor -- Determine if file is HTML that needs popup conversion local function isHTML(strFile) -- strFile ~ Filename return strFile == StrHomeHTML or -- Home Welcome page -- V3.0 strFile:match('^fam%d+%.html$') or -- 'fam99.html' Family pages strFile:match('^ind%d+%.html$') or -- 'ind99.html' Individual pages strFile:match('^toc%d+%.html$') or -- 'toc99.html' ToC pages strFile == 'myloc.html' -- 'myloc.html' my Locations Database page end -- local function isHTML -- Write binary image to graphics file such as dojopop.png local function WriteBinaryToGraphicFile(strContent,strGraphicFile) -- strContent ~ File content -- strGraphicFile ~ File path local fileGraphic = general.OpenFile(strGraphicFile,"wb") fileGraphic:write(strContent) assert(fileGraphic:close()) end -- local function WriteBinaryToGraphicFile -- Search each HTML file and convert any Popups -- local str_fhstyle_css = '' -- V2.4 -- Significant lines local str_lytebox_css = '' local str_lytebox_js = '' -- V2.4 local str_fhpopup_js = '' -- V2.4 local tblHead = {} -- V2.4 -- Table & count of lines needed for fhpopup local intHead = 0 local intEdit = 0 -- Count of non-image Media and HTML file changes local intFile = 0 -- Count of suitable HTML files local isBarStop progbar.Setup(iup_gui.DialogueAttributes("Bars")) -- Pass parameters into new Progress Bar prototype for strFile in lfs.dir(strFolder) do if isHTML(strFile) then intFile = intFile + 1 end -- Count suitable HTML files end if intFile > 100 then progbar.Start("Converting Popups",intFile) -- Start progress bar end intFile = 0 intEdit = intNonImageMedia() -- Count of non-image Media records changed -- V2.8 for strFile in lfs.dir(strFolder) do if isHTML(strFile) then -- Suitable HTML file found intFile = intFile + 1 if intFile % 23 == 0 then progbar.Message("Updating HTML page "..strFile) -- Reduce rate of messages -- V3.2 progbar.Step(23) collectgarbage("step",0) -- Improves run time in loops! -- V3.2 end strFile = strFolder..'\\'..strFile local strLoad = general.StrLoadFromFile(strFile) local tblFile = {} -- Table of lines in file local intHref = 0 -- Count of popup per file for href id local flgHead = true -- Flag if line in ( else ) intHead = 0 for strLine in io.lines(strFile) do -- Read each HTML file line by line (Unicode UTF-8 not needed) if strLine == '' then flgHead = false end if flgHead then -- Adjust lines in --# if strLine == str_fhstyle_css then if strLine:match(str_fhstyle_css) then -- V3.1 -- FH V7 CSS line not on new line !!!! intHead = 1 if strPopup:match("Lytebox") then -- V2.4 -- Insert lytebox header lines table.insert(tblFile,str_lytebox_js) table.insert(tblFile,str_lytebox_css) elseif strPopup:match("Custom") then -- V2.4 -- Insert fhpopup header line table.insert(tblFile,str_fhpopup_js) end end if strLine == str_lytebox_css or strLine == str_lytebox_js or strLine == str_fhpopup_js then -- V2.4 -- Remove existing lytebox/fhpopup header lines strLine = '' elseif intHead > #tblHead then table.insert(tblHead," openwindow.document.writeln(' "..strLine.."');") -- V2.4 -- Save line needed for fhpopup intHead = intHead + 1 end else local strHref, flgLine = strAnchor(strLine,strPopup,'"fh'..intHref..'"',strFile) -- Convert popup anchor in if flgLine then strLine = strHref intHref = intHref + 1 -- Count popup per file for href id end end if strLine ~= '' then table.insert(tblFile,strLine) end end if intHref > 0 then -- V2.4 local strSave = table.concat(tblFile,'\n') -- V3.2 if strLoad ~= strSave then intEdit = intEdit + 1 -- Count changed HTML files -- V3.2 general.SaveStringToFile(strSave,strFile) -- Save changed HTML files end end end isBarStop = progbar.Stop() if isBarStop then break end end progbar.Close() -- Close the Progress Bar if intEdit > 0 then -- V2.4 -- Popup lines changed so change JavaScript files local strLytebox = '\\Family Historian\\Web\\Lytebox' -- Locate the C:\Program Files\Family Historian\Web\Lytebox path local strProgram = os.getenv("PROGRAMFILES")..strLytebox if not general.FlgFolderExists(strProgram) then -- Try alternative Program Files OS Path on 64-bit PC strProgram = ( os.getenv("PROGRAMW6432") or os.getenv("PROGRAMFILES") )..strLytebox end if not general.FlgFolderExists(strProgram) then iup_gui.MemoDialogue('Error! Cannot find Lytebox installation folder:\n'..strProgram) return -1 end if not isBarStop then -- V2.4 Delete all the Lytebox & fhpopup files for strFile in lfs.dir(strProgram) do if strFile:match('^[%w_]+%.%w+$') then general.DeleteFile(strFolder..'\\'..strFile) end end general.DeleteFile(strFolder..'\\fhpopup.js') general.DeleteFile(strFolder..'\\dojopop.png') general.DeleteFile(strFolder..'\\dojopop.css') general.DeleteFile(strFolder..'\\dojopop.js') general.DeleteFile(strFolder..'\\dojosrc.js') end if strPopup:match("Lytebox") then for strFile in lfs.dir(strProgram) do -- Copy lytebox v4.0 JavaScript & CSS & image files if strFile:match('^[%w_]+%.%w+$') then local fileSource = assert(io.open(strProgram..'\\'..strFile,'rb')) local fileTarget, strError = io.open(strFolder..'\\'..strFile,'wb') fileTarget:write(fileSource:read('*all')) -- Copy entire file contents assert(fileSource:close()) assert(fileTarget:close()) end end if strPopup:match("5.5") then -- Create lytebox v5.5 JavaScript & CSS files general.SaveStringToFile(Str_lytebox_js,strFolder..'\\lytebox.js') general.SaveStringToFile(Str_lytebox_css,strFolder..'\\lytebox.css') end elseif strPopup:match("Custom") then -- V2.4 local str_fhpopup = "" if strPopup:match("Zoom & Scroll") then -- Create the fhpopup Custom JavaScript file str_fhpopup = Str_fhpopup_custom elseif strPopup:match("Large Screen") then -- Create the fhpopup Screen JavaScript file str_fhpopup = Str_fhpopup_screen elseif strPopup:match("Dojo Zoomer") then -- Create the fhpopup Zoomer JavaScript files str_fhpopup = Str_fhpopup_zoomer WriteBinaryToGraphicFile(Str_dojopop_png,strFolder..'\\dojopop.png') general.SaveStringToFile(Str_dojopop_css,strFolder..'\\dojopop.css') general.SaveStringToFile(Str_dojopop_js ,strFolder..'\\dojopop.js') general.SaveStringToFile(Str_dojosrc_js ,strFolder..'\\dojosrc.js') end str_fhpopup = str_fhpopup:gsub("{HEAD}",table.concat(tblHead,"\n")) -- Insert the lines from HTML files general.SaveStringToFile(str_fhpopup,strFolder..'\\fhpopup.js') -- Create the fhpopup JavaScript file end end return intEdit end -- function IntImprovePopup -- Insert GEDmill minitrees into each Individual/Family featured HTML Web/CD/DVD page -- -- V3.2 function IntAddMiniTrees(strFolder) -- strFolder ~ Website HTML folder local dicSpou = {} -- Spouses for each Family local dicHref = {} -- Minitree href page filename translations (needs in depth efficiency review and cope with same-sex partners, etc) local intFile = 0 -- Count of feature page HTML files local intStep = 0 local strOrig = nil -- Original page file text to identify updates local strTreesANSI = strFolder..'\\_trees' -- Subfolder to hold minitree gif/png files ~ ANSI local strTreesUTF8 = fhConvertANSItoUTF8(strFolder)..'\\_trees' -- Subfolder to hold minitree gif/png files ~ UTF8 local strGEDmill = '' -- GEDmill product output working folder local strProject = StrPublic:gsub('\\Public$','') -- Project folder for strFile, tblAttr in general.DirTree(strProject) do if tblAttr.mode == 'directory' and strFile:match('\\GEDmill_Output$') then -- Find GEDmill_Output folder anywhere in Project strGEDmill = strFile..'\\' break end end -- Transform GEDmill minitree into FH compatible format and insert into feature page HTML file local function addMiniTree(strSource,strFamHref,strPage,strType,intGsub) -- Insert GEDmill minitree and adjust HTML script -- strSource ~ GEDmill indiI%d+.html file holding minitree -- strFamHref~ Current family page Href fam%d+.html featured page or "" -- strPage ~ Content of ind/fam%d+.html featured page -- strType ~ "Individual" or "1stPartner" or "2ndPartner" -- intGsub ~ 1 or 1 or 2 = number of headings to gsub if not strSource then return strPage end strSource = fhConvertUTF8toANSI(strGEDmill..strSource) -- Ensure source html file exists if not general.FlgFileExists(strSource) then return strPage end local strTree = general.StrLoadFromFile(strSource):match('\n +
    ') -- Extract minitree from GEDmill_Output html file local dicType = {["Individual"]="";["1stPartner"]="2ndPartner";["2ndPartner"]="1stPartner";} -- Translate from one strType to the other local dicUseHref = {} -- Href already used per person on page local function getNewHref(strOldHref) -- Lookup appropriate feature page Href -- strOldHref ~ Href to translate local dicOldHref = dicHref[strOldHref] if not dicOldHref then return strOldHref end -- Keep old Href in case of missing data error local strNewHref = dicOldHref['INDI'] -- Use individual Href if possible if not strNewHref then for _, strSpouHref in ipairs ( dicOldHref['SPOU'] or {} ) do -- Use as many different spouse Hrefs as possible for multiple spouse boxes strNewHref = strSpouHref or dicOldHref['CHIL'][1] or strOldHref if strNewHref ~= strFamHref and strNewHref ~= dicUseHref[strOldHref] then -- Use this Href if not current family page nor already used by this spouse break end end if strNewHref then if strNewHref == strFamHref -- Href to current family page? or strNewHref == dicUseHref[strOldHref] then -- Href already used by spouse? strNewHref = '#minitree'..dicType[strType] -- Link to other minitree end dicUseHref[strOldHref] = strNewHref -- Save Href used by this spouse else strNewHref = '#minitree'..strType -- Link to same minitree end end return strNewHref end -- local function getNewHref strTree = strTree:gsub( '\n +', '\n' ) -- Remove leading spaces strTree = strTree:gsub( '(indiI%d+%.html)', getNewHref ) -- Replace each "indiI%d+.html" with feature page Href strTree = strTree:gsub( 'src="treeI', 'src="_trees/treeI' ) -- Adjust path to minitree .gif/.png image file if StrMini == "OFF" then strTree = strTree:gsub( '(
    ', '%1text-align:center;">' ) -- Add text-align:center else strTree = strTree:gsub( '(
    ', '%1">' ) -- Remove text-align:center end if strType == "Individual" then -- Insert Individual minitree into feature page strPage = strPage:gsub( '(

    [^\n]-

    ).-(\n<[dtp]%l- class=.->)', '%1'..strTree:inert()..'%2', intGsub ) -- Cater for
    [^\n]-

    ).-(\n<[dtp]%l- class.->)', '%1'..strTree:inert()..'%2', intGsub ) -- Cater for
    \n(.-)\n
      \n(.-\n
    )\n
    ') if strSee and strRef then -- Found 'See also' section if StrAlso == "OFF" then if not strRef:match( '
  • .-

    %-->') then -- Cater for Map Life Facts
  • .-

    )', '', 1 ) -- Hide

    See also

    section end strRef = strRef:gsub( '(
  • [^<].-
  • \n)(<[^l])', '\n%2', 1 ) -- Hide See also section list items else strSee = strSee:gsub( '.-

    ) %-->', '%1', 1 ) strRef = strRef:gsub( '.-\n) %-->\n', '%1', 1 ) -- Retain See also section list items end strPage = strPage:gsub( '(
    \n).-\n(\n
    )', '%1'..strSee:inert()..'\n
      \n'..strRef:inert()..'%2', 1 ) end return strPage end -- local function addMiniTree if not general.FlgFolderExists(strGEDmill) then -- Check GEDmill_Output folder exists iup_gui.MemoDialogue('Error! Cannot find GEDmill_Output folder:\n'..strGEDmill) return -1 end strGEDmill = fhConvertANSItoUTF8(strGEDmill) for strFile in lfs.dir(strFolder) do if strFile:match( '^ind%d+.html$' ) then -- For each ind%d+.html file create new href entry for indiviudal local strHref = strFile:gsub( '^ind', 'indiI' ) dicHref[strHref] = { INDI=strFile } intFile = intFile + 1 elseif strFile:match( '^fam%d+.html$' ) then -- For each fam%d+.html file create new href entries for HUSB, WIFE & CHIL intFile = intFile + 1 local ptrFam = fhNewItemPtr() ptrFam:MoveToRecordById( 'FAM', tonumber(strFile:match( '^fam(%d+)' )) ) -- Lookup Family record with this Record Id ptrFam:MoveToFirstChildItem(ptrFam) while ptrFam:IsNotNull() do local strTag = fhGetTag(ptrFam) if ('HUSB WIFE CHIL'):match( strTag ) then -- For each HUSB, WIFE, CHIL RecId create href entries local intRid = fhGetRecordId( fhGetValueAsLink(ptrFam) ) local strHref = 'indiI'..tostring(intRid)..'.html' if strTag ~= 'CHIL' then -- Add HUSB/WIFE to spouse list dicSpou[strFile] = dicSpou[strFile] or {} table.insert( dicSpou[strFile], strHref ) -- Copes with single parents and same-sex partners strTag = "SPOU" end if not ( dicHref[strHref] and dicHref[strHref]['INDI'] ) then -- No [indiI%d+.html]['INDI'] Href so no ind%d+.html file dicHref[strHref] = dicHref[strHref] or {} dicHref[strHref][strTag] = dicHref[strHref][strTag] or {} table.insert(dicHref[strHref][strTag],strFile) -- Add SPOU or CHIL Href to person list end end ptrFam:MoveNext() end end end progbar.Setup(iup_gui.DialogueAttributes("Bars")) -- Pass parameters into new Progress Bar prototype if intFile > 100 then progbar.Start( "Inserting Minitrees", intFile + math.floor(intFile/10) ) -- Start progress bar progbar.Message("Managing minitree images (please wait)") progbar.Step( math.floor(intFile/20) ) fhSleep(5,0) end intFile = 0 local FSO = luacom.CreateObject('Scripting.FileSystemObject') -- File System Object if FSO:FolderExists( strTreesUTF8 ) then -- Minitrees _trees subfolder exists for strFile in lfs.dir( strTreesANSI ) do if strFile:match( '^treeI%d+' ) then local intTimeT = lfs.attributes( strTreesANSI..'\\'..strFile, 'modification' ) -- Modified date of _trees file local intTimeG = lfs.attributes( strGEDmill..strFile, 'modification' ) or 0 -- Modified date of GEDmill file or 0 if different file type if intTimeT ~= intTimeG then -- They differ FSO:DeleteFile( strTreesUTF8..'\\treeI*.*' ) -- Delete any old minitrees (fails if no files) intFile = 0 break end intFile = intFile + 1 -- Same dates so avoid copying especially when thousands of minitrees if intFile > 99 then break end -- Halt after checking 100 tree files end end else FSO:CreateFolder( strTreesUTF8 ) -- Create empty _trees subfolder end progbar.Step( math.floor(intFile/20) ) fhSleep(5,0) if intFile == 0 then FSO:CopyFile( strGEDmill..'treeI*.*', strTreesUTF8 ) -- Copy minitrees from GEDmill_Output to _trees subfolder end intFile = 0 for strFile in lfs.dir(strFolder) do if strFile:match( '^ind%d+.html$' ) or strFile:match( '^fam%d+.html$' ) then -- For each feature file update minitrees intStep = intStep + 1 if intStep % 23 == 0 then progbar.Message("Updating feature page "..strFile) progbar.Step(23) collectgarbage("step",0) -- Improves run time in loops! end local strPath = strFolder.."\\"..strFile local strPage = general.StrLoadFromFile(strPath) local strOrig = strPage if strFile:match( '^ind' ) then -- For each ind%d+.html file update individual minitree strPage = addMiniTree( strFile:gsub( '^ind(%d)', 'indiI%1' ), "", strPage, "Individual", 1 ) else if dicSpou[strFile] then -- For each fam%d+.html file update appropriate individual minitree(s) strPage = addMiniTree( dicSpou[strFile][2], strFile, strPage, "2ndPartner", 2 ) -- 2nd Partner or Wife and gsub 2 FhHdg2 entries strPage = addMiniTree( dicSpou[strFile][1], strFile, strPage, "1stPartner", 1 ) -- 1st Partner or Husband, gsub 1 FhHdg2 entry end end if strPage ~= strOrig then -- Page file has changed intFile = intFile + 1 general.SaveStringToFile(strPage,strPath) end if progbar.Stop() then break end end end progbar.Close() -- Close the Progress Bar return intFile end -- function IntAddMiniTrees -- Remove GEDmill minitrees from each Individual/Family featured HTML Web/CD/DVD page -- -- V3.2 function IntRemMiniTrees(strFolder) -- strFolder ~ Website HTML folder local intFile = 0 -- Count of feature page HTML files local intStep = 0 for strFile in lfs.dir(strFolder) do if strFile:match( '^ind%d+.html$' ) or strFile:match( '^fam%d+.html$' ) then -- Count feature page files intFile = intFile + 1 end end progbar.Setup(iup_gui.DialogueAttributes("Bars")) -- Pass parameters into new Progress Bar prototype if intFile > 100 then progbar.Start("Removing Minitrees",intFile) -- Start progress bar end intFile = 0 for strFile in lfs.dir(strFolder) do if strFile:match( '^ind%d+.html$' ) -- For each feature file remove minitrees or strFile:match( '^fam%d+.html$' ) then intStep = intStep + 1 if intStep % 23 == 0 then progbar.Message("Updating feature page "..strFile) progbar.Step(23) collectgarbage("step",0) -- Improves run time in loops! end local strTarget = strFolder.."\\"..strFile local strPage = general.StrLoadFromFile(strTarget) -- Reveal 'See also' and remove minitrees local strOrig = strPage local strSee, strRef = strPage:match('
      \n(.-)\n
        \n(.-)\n
      \n
      ') if strSee and strRef then -- Found 'See also' section to reveal strSee = strSee:gsub( '.-

      ) %-->', '%1', 1 ) strRef = strRef:gsub( '' , '', 1 ) strPage = strPage:gsub( '(
      \n).-(\n
    \n
    )', '%1'..strSee:inert()..'\n$") then for intTable, tblTable in ipairs(tblTables) do -- Insert Table of Pictures/Reports/Downloads links at end of Home HTML page ToC if tblTable.links > 0 then table.insert(tblIndex,"
  • "..StrTranslate("Table of").." "..tblTable.name.."
  • ") -- V3.2 end end isIncluded = "Tail" -- Identify Home HTML page trailing lines in table page files end end else tableInsert(tblTables,strLine) -- Copy all "Head" and "Tail" lines to each table page file end end table.insert(tblIndex,strLine) -- Copy all required lines to Home HTML page end end local strLoad = general.StrLoadFromFile(strHomeHTML) -- V3.2 local strSave = table.concat(tblIndex,"\n") if strLoad ~= strSave then general.SaveStringToFile(strSave,strHomeHTML) -- Update the Home HTML page file -- V3.2 intEdits = intEdits + 1 end for intTable, tblTable in ipairs(tblTables) do local strTableFile = strFolder.."\\"..tblTable.file if tblTable.links > 0 then local strLoad = "" if general.FlgFileExists(strTableFile) then strLoad = general.StrLoadFromFile(strTableFile) -- V3.2 end local strSave = table.concat(tblTable.html,"\n") if strLoad ~= strSave then general.SaveStringToFile(strSave,strTableFile) -- If any links, create table page file -- V3.2 intEdits = intEdits + 1 end else if general.FlgFileExists(strTableFile) then general.DeleteFile(strTableFile) -- Otherwise, delete old table page file -- V3.2 intEdits = intEdits + 1 end end end -- Search each HTML file and convert its menu bar -- progbar.Setup(iup_gui.DialogueAttributes("Bars")) local isBarStop local intHtml = 0 for strFile in lfs.dir(strFolder) do if strFile:match("^.+%.html$") then intHtml = intHtml + 1 end -- Count all HTML files end local intFile = 0 for strFile in lfs.dir(strFolder) do if strFile:match("^.+%.html$") then -- HTML file found intFile = intFile + 1 if intFile % 23 == 0 then progbar.Message("Updating HTML page "..strFile) -- Reduce rate of messages -- V3.2 progbar.Step(23) collectgarbage("step",0) -- Improves run time in loops! -- V3.2 end local tblFile = {} -- Table of lines in file local strOldMenu = "" -- Old menu bar links local strNewMenu = "" -- New menu bar links for comparison strFile = strFolder.."\\"..strFile for strLine in io.lines(strFile) do -- Read each HTML file line by line (Unicode UTF-8 not needed) if strLine:match("^
  • .+
  • $") then -- V3.2 strOldMenu = strOldMenu..strLine -- Accumulate all existing table page menu links if strLine:matches("_nameindex.html") then -- V2.4 -- Ignore all table page menu links except _nameindex.html if isMenu then for intTable, tblTable in ipairs(tblTables) do -- Add any table page menu links if tblTable.links > 0 then local strMenu = "
  • "..tblTable.name.."
  • " table.insert(tblFile,strMenu) strNewMenu = strNewMenu..strMenu -- Accumulate all new table page menu links end end end strNewMenu = strNewMenu..strLine -- Append the _nameindex.html page menu link table.insert(tblFile,strLine) end else table.insert(tblFile,strLine) -- Copy all other lines end end if intFile == 1 then if strOldMenu == strNewMenu then break end -- If menu bar unchanged in first file then skip all file updates if intHtml > 100 then progbar.Start("Converting Menu Bars",intHtml) -- Otherwise, if more than 100 files, start progress bar end end general.SaveStringToFile(table.concat(tblFile,"\n"),strFile) -- Save changed HTML files intEdits = intEdits + 1 end isBarStop = progbar.Stop() if isBarStop then break end end progbar.Close() -- Close the Progress Bar return intEdits end -- function IntImproveTable -- Add alternate & married names and idents to _nameindex.html and optional Surnames Index to _surnames.html -- function IntImproveIndex(strFolder,isFilter,strNames,strStyle) -- V2.5 isFilter -- V3.2 remove intIdent -- strFolder ~ Website HTML folder -- isFilter ~ Search Filter option -- strNames ~ Additional Names option -- strStyle ~ Font style for Names option local strFileHtml = strFolder.."\\_nameindex.html" -- Name index HTML filename -- V2.4 local strFileJava = strFolder.."\\_nameindex.js" -- Name index Java filename -- V2.4 local strFileJmin = strFolder.."\\jquery.min.js" -- Jquery.min Java filename -- V2.4 local tblNameList = {} -- Name list of index page entries local tblShortcut = {} -- Shortcut to Name list entries local strPagelink = '' -- Pagelink to web page for current person local strRecordId = '' -- Record Id of current person used to make Name list entry distinct local strLifedate = '' -- Lifedates of current person in index page format or '( - )' if Living local strAsterisk = '' -- Asterisk indicating has own or shared page in FH V7+ -- V3.1 local strIdentity = '' -- Identity text of current person associated with Identity GUI options local strTitleTxt = '' -- Tooltip title text for Name list entry -- Obtain Individual Lastname & Forenames -- local function strGetNames(ptrIndiName) local strLastname = fhGetItemText(ptrIndiName,'~:SURNAME') -- Get the Surname local strForename = fhGetItemText(ptrIndiName,'~:GIVEN_ALL') -- Get the Forenames etc if strLastname == '' then strLastname = '?' end if strForename == '' then strForename = '?' end strLastname = encoder.StrEncode_XML(strLastname) -- Translate ANSI/UTF-8 characters into XML/HTML characters strForename = encoder.StrEncode_XML(strForename) return strLastname, strForename end -- local function strGetNames -- Obtain Individual Identity Data Conditionally -- local tblIdentity = { -- V3.2 { Control=StrIdRiD; Tag=' ' ; Prefix=' RiD:'; }; -- Record Identity { Control=StrIdPRN; Tag='~.RFN' ; Prefix=' PRN:'; }; -- Permanent Record Number { Control=StrIdCiD; Tag='~.REFN' ; Prefix=' CiD:'; }; -- Custom Identity { Control=StrIdARI; Tag='~.RIN' ; Prefix=' ARI:'; }; -- Automatic Record Identity { Control=StrIdUID; Tag='~._UID' ; Prefix=' UID:'; }; -- Unique Identity { Control=StrIdFSI; Tag='~._FSID'; Prefix=' FSI:'; }; -- Family Search Identity } local function strGetIdentity(ptrIndi) -- V3.2 local strIdentities = '' for intIdentity, tblIdentity in ipairs(tblIdentity) do -- Repeat for each GUI Identity option local strIdentity = strRecordId -- Default to Record Id if #tblIdentity.Tag > 3 then -- Get the Identity text strIdentity = fhGetValueAsText(fhGetItemPtr(ptrIndi,tblIdentity.Tag)) end if strIdentity ~= '' and tblIdentity.Control == "ON" then strIdentity = tblIdentity.Prefix..strIdentity -- Add prefix if required else strIdentity = '' end strIdentities = strIdentities..strIdentity -- Accumulate each required Identity end if strIdentities ~= '' then strIdentities = ''..encoder.StrEncode_XML(strIdentities)..' ' end -- V2.4 -- V2.5

    => return strIdentities end -- local function strGetIdentity -- Find Existing Name list entry and update RecordId & Identity -- local function isExisting(strEntry) local tblEntry = tblShortcut[strEntry] or {} -- Lookup shortcut to Name list entries for i, intEntry in ipairs(tblEntry) do -- Search any matching entries local strRecId = tblNameList[intEntry].RecordId if strRecId == strRecordId or strRecId == '' then -- Record Id matches, or not yet assigned tblNameList[intEntry].RecordId = strRecordId tblNameList[intEntry].Identity = strIdentity return true -- Found entry with matching Pagelink, FullName, Lifedate & RecordId end end return false -- No entry found end -- local function isExisting -- Obtain matching Pagelink for own web page or relative's web page -- local function strGetPageLink(ptrIndi,intFamilyId) local strLastname, strForename = strGetNames(fhGetItemPtr(ptrIndi,'~.NAME')) local strSelfname = strLastname..strForename -- Pagelink + Selfname + Lifedate is Shortcut index to Name list local strPagelink = '' -- Pagelink for own or relative's featured web page local intRecordId = 0 -- RecordId for current Family or Individual record local flagLiving = fhGetItemText(ptrIndi,'~._FLGS.__LIVING') -- 'Y' or '' local ptrLink = fhNewItemPtr() -- Pointer to self and each relative local ptrFams = fhNewItemPtr() -- Pointer to each Family of the above repeat for intTag, strTag in ipairs({ '~'; '~.~FATH[1]>'; '~.~MOTH[1]>'; '~.~SPOU[1]>'; '~.~CHIL[1]>'; }) do local intNext = 1 ptrLink:MoveTo(ptrIndi,strTag) -- Search own and each relative's web page links while ptrLink:IsNotNull() do if intFamilyId ~= 0 then -- Family web page search needed ptrFams:MoveTo(ptrLink,'~.FAMS') while ptrFams:IsNotNull() do -- Check each Family Pagelink intRecordId = fhGetRecordId(fhGetValueAsLink(ptrFams)) strPagelink = '"fam'..tostring(intRecordId)..'.html"' if intFamilyId < 0 then if isExisting(strPagelink..strSelfname..strLifedate) then return strPagelink end elseif intFamilyId == intRecordId then return strPagelink -- Husband entry needs Family Pagelink (could check web page file exists) end ptrFams:MoveNext('SAME_TAG') end end intRecordId = fhGetRecordId(ptrLink) -- Check the Individual Pagelink strPagelink = '"ind'..tostring(intRecordId)..'.html"' if isExisting(strPagelink..strSelfname..strLifedate) then return strPagelink end if strTag == '~' then ptrLink:SetNull() -- Only one instance of self, so close while ptrLink:IsNotNull() loop else intNext = intNext + 1 -- Next instance of relative e.g. '~.~SPOU[2]>' or '~.~CHIL[9]>' ptrLink:MoveTo(ptrIndi,(strTag:gsub('1',intNext))) -- ptrLink:MoveNext('SAME_TAG') does not work end end end if strLifedate == '( - )' then flagLiving = '' end -- After trying blank Lifedate exit repeat loop if flagLiving == 'Y' then strLifedate = '( - )' end -- If Living flag then try blank Lifedate until flagLiving == '' return nil -- No matching entry found end -- local function strGetPageLink -- Conditionally Update Name List and Shortcut -- local function doUpdateNameList(strLastname,strForename,strFullname,strAsterisk) local strEntry = strPagelink..strLastname..strForename..strLifedate if not tblShortcut[strEntry] then tblShortcut[strEntry] = {} end -- New shortcut to Name entry needed if #tblShortcut[strEntry] == 0 or strRecordId == '' then -- New shortcut to new Name, or replica Name in name index page table.insert(tblNameList,{ Pagelink=strPagelink; Lastname=strLastname; Sortname=encoder.StrUTF8_ASCII(strLastname); Forename=strForename; Fullname=strFullname; Lifedate=strLifedate; Asterisk=strAsterisk; RecordId=strRecordId; Identity=strIdentity; TitleTxt=strTitleTxt; }) -- V3.1 table.insert(tblShortcut[strEntry],#tblNameList) -- Save shortcut to Name entry, which may have replicas end end -- local function doUpdateNameList -- Comparison for table sort that returns true when 1st is less than 2nd -- local function isLessThan(tbl1st,tbl2nd) local str1stPref = '' -- Adjust sort so non-alpabetic initial chars come first but ordered according to any subsequent letters local str2ndPref = '' local str1stName = tbl1st["Sortname"] local str2ndName = tbl2nd["Sortname"] if not str1stName:match('^%u') then str1stPref = '!'..(str1stName:match('(%u+)') or '!') end if not str2ndName:match('^%u') then str2ndPref = '!'..(str2ndName:match('(%u+)') or '!') end str1stName = str1stPref..str1stName..','..tbl1st["Forename"]..tbl1st["Lifedate"]..tbl1st["Pagelink"] -- V2.5 added ',' needed to separate names str2ndName = str2ndPref..str2ndName..','..tbl2nd["Forename"]..tbl2nd["Lifedate"]..tbl2nd["Pagelink"] -- V2.5 added ',' needed to separate names return str1stName < str2ndName end -- local function isLessThan -- Read _nameindex.html file of text into tblNameIndex, down to

    fhOverridePreference("SURNAMES_UPPERCASE",true,true) local strStyleCSS = ' ' if strStyle:match("Italics") then strStyleCSS = strStyleCSS..'font-style:italic; ' end if strStyle:match("Underline") then strStyleCSS = strStyleCSS..'text-decoration:underline; ' end local strDivIdTop = nil -- V2.4 -- First div id may be "header" or "menubar" local intAlphabet = 0 -- Alphabet Anchor Hyperlinks line number local tblNameIndex = {} -- Table of _nameindex.html leading text lines local strNameIndex = '' -- Text of _nameindex.html trailing text lines local strLastname = '' local strForename = '' local strFullname = '' local intFamilyId = 0 local strFileMode = 'head' for strLine in io.lines(strFileHtml) do -- Read _nameindex.html leading text line by line (Unicode UTF-8 not needed) if strFileMode == 'head' then if not -- V2.4 -- Exclude search filter scripts & paragraphs and XHTML CSS married/alternate/index/ident/FhInd styles ( strLine:match('^$') or strLine:match('^$') or strLine:match('^') -- V2.4 -- V2.5 ) then if strLine == '' then -- V2.5 -- End of Header so insert XHTML CSS married/alternate/name/FhIndexList/FhInd styles (FhInd needed to remove white borders around Alphabet Index when custom body background) -- # table.insert(tblNameIndex,'') -- # -- V2.5 the {float:left;width:xx%;} style made Search Filter run very slowly so changed to {display:inline-block;width:50%} and original .FhIndexList ul {margin-top:-1.25em;} style table.insert(tblNameIndex,'') end strDivIdTop = strDivIdTop or strLine:match('^
    ') -- V2.4 -- Save first div id name for Return to Top links if strLine:match('
    ') then -- V3.1 -- class= FH V7 FhIndexPage FH V6 FhInd if isFilter then -- V2.5 -- Insert Search Filter box and Alpahbetic Lookup advice above
    local arrTitle = { } -- Translate advice -- V3.2 table.insert( arrTitle, StrTranslate("Examples of what Search Filter text will find:") ) table.insert( arrTitle, "' Bond, James (' "..StrTranslate("full name").." BOND, James" ) table.insert( arrTitle, "' Anderson, ' "..StrTranslate("any").." "..StrTranslate("matching").." "..StrTranslate("Surname") ) table.insert( arrTitle, "' Elizabeth ' "..StrTranslate("any").." "..StrTranslate("matching").." "..StrTranslate("Forename") ) table.insert( arrTitle, "', Jonathan ' "..StrTranslate("matching").." "..StrTranslate("first").." "..StrTranslate("Forename") ) table.insert( arrTitle, "' Jonathan (' "..StrTranslate("matching").." "..StrTranslate("final").." "..StrTranslate("Forename") ) table.insert( arrTitle, "'arth' Arthur, Martha, Garth, etc" ) table.insert( arrTitle, "'(1789-1876)' "..StrTranslate("matching").." ("..StrTranslate("Life-Dates")..")" ) -- # if not strIdent:match('None') then table.insert( arrTitle, "'RiD:12 ' "..StrTranslate("matching").." "..StrTranslate("Identities") ) -- # end table.insert( arrTitle, "( "..StrTranslate("Note the space character before and after names").." )" ) local strTitle = " "..table.concat(arrTitle,"\n ") -- V2.5 -- Add tooltip help text to Search Filter (leading   makes it match uniquely above) local strLine = ' ' table.insert(tblNameIndex,strLine) -- V2.5 -- Insert the Search Filter advice and id="search" for filter form local strLine = '

    '..StrTranslate("Surnames Alphabetic Lookup")..'
    '..StrTranslate("Click any letter below to jump to surnames starting with that letter")..'

    ' table.insert(tblNameIndex,strLine) -- V2.5 -- Insert the Alphabetic Lookup advice and Search Filter javascript table.insert(tblNameIndex,'') table.insert(tblNameIndex,'') Str_filter_js = Str_filter_js:gsub("Clear Search",StrTranslate("Clear Search")) Str_filter_js = Str_filter_js:gsub("Start Search",StrTranslate("Start Search")) Str_filter_js = Str_filter_js:gsub("Please Wait" ,StrTranslate("Please Wait")) general.SaveStringToFile(Str_filter_js,strFileJava) -- V2.5 -- Save _nameindex.js file general.SaveStringToFile(Str_jquery_js,strFileJmin) -- V2.5 -- Save jquery.min.js file else general.DeleteFile(strFileJava) -- V2.5 -- Remove _nameindex.js file general.DeleteFile(strFileJmin) -- V2.5 -- Remove jquery.min.js file end end table.insert(tblNameIndex,strLine) -- Save leading text lines if strLine:match('^.*
    %u.*

    $') then -- V2.4 -- V3.2 intAlphabet = #tblNameIndex -- Save index to Alphabet Anchor Hyperlinks line end if strLine == '
    ' then -- Pause at
    line strFileMode = 'list' end end elseif strFileMode == 'list' then -- Read _nameindex.html web page link & full name & life dates & asterisk into a tblNameList and tblShortcut dictionary local strStar1, strStar2 strPagelink, strFullname, strLifedate, strStar1, strStar2 = strLine:match('^.- *(.+ )(%(.-%))(.-)(.-)$') -- V2.4 -- V3.1 if strPagelink and strFullname and strLifedate then -- List the entry details if not ( strFullname:match(' title="%u%l+ Name">') -- Exclude alternate/married names with tooltip title="Alternate/Married Name" or strFullname:match('') -- V2.4 -- or with legacy italics or strFullname:match('') ) then -- or with name strLastname = strFullname:match('^([^a-z]+), ') or '?' -- Allow for any surname including accented capitals strForename = strFullname:match(strLastname:plain()..', (.+) ') or '?' strAsterisk = strStar1:match('(%*)') or strStar2:match('(%*)') or '' -- FH V7 superscipt asterisk -- V3.1 doUpdateNameList(strLastname,strForename,strLastname..', '..strForename..' ',' '..strAsterisk) -- V3.1 if strPagelink:match('fam') then intFamilyId = -1 end end end if strLine == '
    ' or strLine:match('

     ?

    ') then -- FH V7

     

    FH V6
    -- V3.1 strNameIndex = strLine..'\n' -- Save _nameindex.html trailing lines -- V3.1 strFileMode = 'tail' end elseif strFileMode == 'tail' then strNameIndex = strNameIndex..strLine..'\n' end end -- Search all GEDCOM Names to add Alternate Names &/or Married Womens Surnames to tables with correct web page link and Identity local ptrName = fhNewItemPtr() local ptrFams = fhNewItemPtr() local ptrFamr = fhNewItemPtr() local ptrSpou = fhNewItemPtr() local strStat = '' local strMarriedName = ' title="'..StrTranslate("Married Name") ..'"' -- V3.2 local strAlternateName = ' title="'..StrTranslate("Alternate Name")..'"' -- V3.2 local ptrIndi = fhNewItemPtr() ptrIndi:MoveToFirstRecord('INDI') while ptrIndi:IsNotNull() do -- Loop through every Individual to ensure RecordId & Identity added to Primary Names strRecordId = tostring(fhGetRecordId(ptrIndi)) -- Get current RecordId text strLifedate = '('..fhCallBuiltInFunction("LifeDates2",ptrIndi)..')' -- Get current Lifedate text in name index format strIdentity = strGetIdentity(ptrIndi) -- Get current Identity text required by GUI option strPagelink = strGetPageLink(ptrIndi,intFamilyId) -- Get current Pagelink text from matching Name list entry if strPagelink then ptrName = fhGetItemPtr(ptrIndi,'~.NAME') collectgarbage("step",0) -- Improves run time in loops! -- V3.2 if strNames:match( "Married" ) then -- Add Married Womens Surnames strTitleTxt = strMarriedName -- V3.2 if fhGetItemText(ptrIndi,'~.SEX') == "Female" then -- For Females use her Forename with the Husband's Lastname strLastname, strForename = strGetNames(ptrName) -- Get her Lastname & Forename ptrFams:MoveTo(ptrIndi,'~.FAMS') while ptrFams:IsNotNull() do ptrFamr = fhGetValueAsLink(ptrFams) ptrSpou = fhGetItemPtr(ptrFamr,'~.HUSB>') -- Check husband is Male if fhGetItemText(ptrSpou,'~.SEX') == "Male" then strStat = fhGetItemText(ptrFamr,'~._STAT') if strStat ~= "Never Married" and strStat ~= "Unmarried Couple" then if intFamilyId < 0 then -- Use the Husband's family Pagelink strPagelink = strGetPageLink(ptrSpou,fhGetRecordId(ptrFamr)) end if strPagelink then -- Get the Husband's Surname strLastname = strGetNames(fhGetItemPtr(ptrSpou,'~.NAME')) if strLastname ~= '?' then -- Add the Married Name in italics &/or underline with tooltip title doUpdateNameList(strLastname,strForename,''..strLastname..', '..strForename..' ') end end end end ptrFams:MoveNext('SAME_TAG') end end end -- Add Married Womens Surnames if strNames:match( "Alternate" ) then -- Add Alternate Names strTitleTxt = strAlternateName -- V3.2 ptrName:MoveNext('SAME_TAG') -- Check each Alternate Name entry while ptrName:IsNotNull() do strLastname, strForename = strGetNames(ptrName) -- Get Alternate Surname & Forename if strLastname..strForename ~= '??' then -- Add the Alternate Name in italics &/or underline with tooltip title doUpdateNameList(strLastname,strForename,''..strLastname..', '..strForename..' ') end ptrName:MoveNext('SAME_TAG') end end -- Add Alternate Names end ptrIndi:MoveNext() end table.sort(tblNameList,isLessThan) -- Sort the NameList alphabetically by Name and Lifedates and Pagelink local strHeadName = nil local strAlphabet = '

    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 

    ' local strNextInit = '' local strPrevInit = '' local strPreamble = '' local strReturnToTop = '' -- V3.2 table.insert(tblNameIndex,'
      ') -- V2.4 -- Start of Search Filter unordered list for i, tblItem in ipairs(tblNameList) do -- Add each NameList item to _nameindex.html text lines strPagelink = tblItem["Pagelink"] strLastname = tblItem["Lastname"] strSortname = tblItem["Sortname"] strForename = tblItem["Forename"] strFullname = tblItem["Fullname"] strLifedate = tblItem["Lifedate"] strAsterisk = tblItem["Asterisk"] or '' -- V3.1 strIdentity = tblItem["Identity"] strTitleTxt = tblItem["TitleTxt"] if strHeadName ~= strSortname then if strHeadName then table.insert(tblNameIndex,'
    \n') end -- V2.5 -- End of previous Names unordered sub-list strHeadName = strSortname -- Each new Lastname needs a Header strNextInit = strHeadName:sub(1,1) if strPrevInit ~= strNextInit -- Each new Initial Letter needs an Alphabet Hyperlink
    I and Anchor and strNextInit:match('%u') then -- ?????? strAlphabet = strAlphabet:gsub(strNextInit..' ',''..strNextInit..' ') strPrevInit = '' else strPrevInit = '' end strLastname = strLastname:gsub('^?$','(no surname)') -- V2.4 -- Change ? to (no surname) strLastname = strLastname:sub(1,IntLength) -- V3.0 -- Truncate to avoid overwriting Names list strPreamble = '
  • \n

    '..strPrevInit..strReturnToTop..strLastname..'

    \n
  • ' -- V2.5 -- V3.2 table.insert(tblNameIndex,strPreamble) -- V2.5 table.insert(tblNameIndex,'
  • \n
      ') -- V2.5 -- Start of next Names unordered sub-list strPrevInit = strNextInit end --# strIdentity = 'Rid:{HREF} ' -- V2.5 Temporary fudge for testing strIdentity = strIdentity:gsub("{HREF}",strPagelink) -- V2.4 -- Identity hyperlink local strClassName = strIdentity:match('class="%l+" ') or '' -- V2.5 table.insert(tblNameIndex,'
    • '..strFullname..strLifedate..strAsterisk..''..strIdentity..'
    • ') -- V2.5 -- Insert Page link, full Name, Life dates & Idents -- V3.1 Asterisk end strAlphabet = strAlphabet:gsub(' 

      $','

      ') -- Remove trailing   from Alphabet Anchor Hyperlinks tblNameIndex[intAlphabet] = strAlphabet table.insert(tblNameIndex,'
    \n
  • ') -- V2.5 -- End of final Names unordered sub-list table.insert(tblNameIndex,'\n') -- V2.4 -- End of Search Filter unordered list strNameIndex = table.concat(tblNameIndex,'\n')..strNameIndex strLoadIndex = general.StrLoadFromFile(strFileHtml) if strLoadIndex == strNameIndex then return 0 end -- V3.2 general.SaveStringToFile(strNameIndex,strFileHtml) -- Save _nameindex.html file return #tblNameList -- Number of names in list end -- function IntImproveIndex -- Create a 'gendex.txt' file for the http://gendexnetwork.org/ GenDex web site -- function IntCreateGenDex(strFolder,strGenDex) -- strFolder ~ Website HTML folder -- Compose webpage Filename matching Individual Record -- local function strFilename(ptrIndi) local strFile = "ind"..fhGetRecordId(ptrIndi)..".html" -- Does "ind{RecId}.html" Individual Summary webpage file exist? if general.FlgFileExists(strFolder.."\\"..strFile) then return strFile end local ptrFams = fhNewItemPtr() ptrFams:MoveTo(ptrIndi,"~.FAMS>") strFile = "fam"..fhGetRecordId(ptrFams)..".html" -- Does "fam{RecId}.html" Family Group Sheet webpage file exist? if general.FlgFileExists(strFolder.."\\"..strFile) then return strFile end return nil end -- local function strFilename local strGedcomDate = NewGedcomDate() -- Define local function from prototype below fhOverridePreference("SURNAMES_UPPERCASE",true,true) local tblGenDex = {} local ptrIndi = fhNewItemPtr() ptrIndi:MoveToFirstRecord("INDI") while ptrIndi:IsNotNull() do local strFile = strFilename(ptrIndi) if strFile and not ( strGenDex:match("Living") and fhGetItemText(ptrIndi,"~._FLGS.__LIVING") == "Y" ) and not ( strGenDex:match("Private") and fhGetItemText(ptrIndi,"~._FLGS.__PRIVATE") == "Y" ) then collectgarbage("step",0) -- Improves run time in loops! -- V3.2 local tblData = {} table.insert(tblData,strFile) -- Reference is featured webpage filename table.insert(tblData,fhGetItemText(ptrIndi,"~.NAME:SURNAME")) -- Surname in uppercase table.insert(tblData,fhGetItemText(ptrIndi,"~.NAME:STORED")) -- Stored full name for intList, tblList in ipairs ( { { "~.BIRT"; "~.BAPM"; "~.CHR"; }; { "~.DEAT"; "~.BURI"; "~.CREM"; }; } ) do local strDate = "" local strPlac = "" for intFact, strFact in ipairs ( tblList ) do local ptrFact = fhGetItemPtr(ptrIndi,strFact) -- Get Birth/Baptism/Christening or Death/Burial/Cremation Date & Place if ptrFact:IsNotNull() then if strDate == "" then strDate = fhGetItemText(ptrFact,"~.DATE") end if strPlac == "" then strPlac = fhGetItemText(ptrFact,"~.PLAC") end end end table.insert(tblData,strGedcomDate(strDate)) -- Convert date to GEDCOM format table.insert(tblData,strPlac) end -- Should characters be converted from CP1252 to UTF-8 ??? table.insert(tblData,"") table.insert(tblGenDex,table.concat(tblData,"|")) -- Create "|" separated Individual data entry end ptrIndi:MoveNext() end local strGendex = strFolder.."\\gendex.txt" local strLoad = "" if general.FlgFileExists(strGendex) then strLoad = general.StrLoadFromFile(strGendex) -- V3.2 end local strSave = table.concat(tblGenDex,"\n") if strLoad == strSave then return 0 end -- V3.2 general.SaveStringToFile(strSave,strGendex) -- Create the GenDex file in website folder return #tblGenDex end -- function IntCreateGenDex -- Apply current Output Language to all HTML page menus and headings -- -- V3.2 function IntApplyLanguage(strFolder) -- strFolder ~ Website HTML folder local intFile = 0 -- Count HTML file pages found local intPage = 0 -- Count HTML file pages changed local strMenu = nil -- New translated menu bar local strBar = '\n' -- Menu bar pattern match local arrFind = { -- 'See also' section key words { Text='other family'; Suffix=': ' ; }; { Text='other families'; Suffix=': ' ; }; { Text='with'; Suffix=' \n(.-)\n
      \n(.-)\n
    \n
    ') if strSee and strRef then local strMap = strRef:match('(
  • \n)<[!li>][^<]') or "" -- Map of Locations entries from Map Life Facts plugin local strEng = strRef:match('') or strRef:match('
  • [^<].-$') local strPre = strRef:match('\n(') or "" local strSuf = strRef:match('
  • (\n %-->)\n') or "" local strNew = strPre..strEng..strSuf strSee = strSee:gsub( '

    .-

    ', '

    '..StrTranslate("See also")..'

    ', 1 ) for _, dicFind in ipairs (arrFind) do -- Use lookup table above local strText = dicFind.Text local strSuff = dicFind.Suffix strNew = strNew:gsub( '([ (])'..strText..'%'..strSuff, '%1'..StrTranslate(strText)..strSuff ) end strMap = strMap:gsub( '(
  • ).-(: .-
  • )', '%1'..StrTranslate("Map of Locations for")..'%2' ) strPage = strPage:gsub( '(
    \n).-(\n\n
    )', '%1'..strSee:inert()..'\n
    )', '%1'..StrTranslate("Has own or shared page")..'%2', 1 ) return strPage end -- local function strNewNameIndex local function strNewTableOf(strPage) -- Translate Table of Pictures, Reports, Downloads pages strPage = strPage:gsub( '()[^\n]-( Family Historian 7

    )', '%1'..StrTranslate("Created with")..'%2', 1 ) return strPage end -- local function strNewTableOf local function strNewMapOfLoc(strPage) -- Translate Map Life Facts Map of Locations pages return strPage end -- local function strNewMapOfLoc local function strNewContents(strPage) -- Translate Table of Contents pages return strPage end -- local function strNewContents local function strNewMyLoc(strPage) -- Translate My Locations page return strPage end -- local function strNewMyLoc local dicPage = { -- Invoke translation function per page file [StrHomeHTML] = strNewIndexHome ; ["_contact.html"] = strNewContact ; ["_statistics.html"] = strNewStatistics ; ["_nameindex.html"] = strNewNameIndex ; ["_topindex.html"] = strNewTableOf ; ["_torindex.html"] = strNewTableOf ; ["_todindex.html"] = strNewTableOf ; ["ind9.html"] = strNewFeature ; ["fam9.html"] = strNewFeature ; ["map9.html"] = strNewMapOfLoc ; ["toc9.html"] = strNewContents ; ["myloc.html"] = strNewMyLoc ; } -- Search each HTML file and convert its menu bar and headings of unique pages -- progbar.Setup(iup_gui.DialogueAttributes("Bars")) for strFile in lfs.dir(strFolder) do if strFile:match("^.+%.html$") then intFile = intFile + 1 end -- Count all HTML file pages end if intFile > 100 then progbar.Start("Translating HTML Pages",intFile) -- If more than 100 files, start progress bar end intFile = 0 for strFile in lfs.dir(strFolder) do if strFile:match("^.+%.html$") then -- HTML file page found intFile = intFile + 1 if intFile % 23 == 0 then progbar.Message("Translating HTML page "..strFile) -- Reduce rate of messages progbar.Step(23) collectgarbage("step",0) -- Improves run time in loops! end local strPath = strFolder.."\\"..strFile -- Load HTML file to translate local strPage = general.StrLoadFromFile(strPath) local strOrig = strPage if not strMenu then -- Translate the menu bar once strMenu = strPage:match( strBar ) if strMenu then strMenu = strMenu:gsub( '()[^\n]-' , '%1'..StrTranslate("Home")..'' , 1 ) strMenu = strMenu:gsub( '()[^\n]-' , '%1'..StrTranslate("Contact")..'' , 1 ) strMenu = strMenu:gsub( '()[^\n]-', '%1'..StrTranslate("Statistics")..'', 1 ) strMenu = strMenu:gsub( '()[^\n]-' , '%1'..StrTranslate("Index")..'' , 1 ) end end if strMenu then strPage = strPage:gsub( strBar, strMenu, 1 ) -- Substitute the menu bar in every page end strPage = dicPage[ strFile:gsub('%d+','9') ](strPage) -- Translate each type of HTML file page if strOrig ~= strPage then general.SaveStringToFile(strPage,strPath) -- Save changed HTML file page intPage = intPage + 1 end if progbar.Stop() then break end end end progbar.Close() -- Close the Progress Bar return intPage end -- function IntApplyLanguage -- Convert any Date to Gedcom Format Function Prototype -- function NewGedcomDate() -- Convert 'approx.', 'calculated', 'estimated' dates -- local tblQual = { app="ABT "; cal="CAL "; est="EST "; } local function strQualifiedDate(strDate,strQual) if strQual then return tblQual[strQual:sub(1,3)]..strDate else return nil end end -- local function strQualifiedDate -- Convert a Quarter Date to Gedcom format -- local tblBeg = { "JAN "; "APR "; "JUL "; "OCT "; } local tblEnd = { "MAR "; "JUN "; "SEP "; "DEC "; } local function strQuarterDate(strNum,strYear) if strNum then local intNum = tonumber(strNum) return "BET "..tblBeg[intNum]..strYear.." AND "..tblEnd[intNum]..strYear else return nil end end -- local function strQuarterDate -- Convert any Date to Gedcom format -- local tblGedcomDate = { January="JAN"; February="FEB"; March="MAR"; April="APR"; May="MAY"; June="JUN"; July="JUL"; August="AUG"; September="SEP"; October="OCT"; November="NOV"; December="DEC"; from="FROM"; ["to "]="TO "; before="BEF"; after="AFT"; between="BET"; ["and"]="AND"; } return function(strDate) local strText = "" if strDate:match("\".*\"") then -- Convert 'Date Phrase' must be first to avoid converting months, etc in Phrase text local strGsub = "(.-)%(?\"(.*)\"%)?$" strText = strDate:gsub(strGsub,"(%2)") -- Separate Phrase (Text) from interpreted Date strDate = strDate:gsub(strGsub,"%1") if strDate ~= "" then strDate = "INT "..strDate end -- Convert '...interpreted as' end strDate = strDate:gsub("(.*) %((.*)%)$",strQualifiedDate) -- Convert (qualified) date for strOld, strNew in pairs ( tblGedcomDate ) do strDate = strDate:gsub(strOld,strNew) -- Convert months, and 'Period' and 'Range' dates end strDate = strDate:gsub("^Q([1-4]) (%d%d%d%d)",strQuarterDate) -- Convert 'Quarter Date' return strDate..strText end -- anonymous function end -- function NewGedcomDate -- Graphical User Interface -- function GUI_MainDialogue() local strInitialText = " No improvement executed in this session " local strFolderTitle = "Browse to the Project '\\Public\\FH Website' or '\\Public\\FH CD-DVD\\data' or equivalent HTML folder" -- V3.2 if fhGetAppVersion() > 6 then strFolderTitle = "Browse to the Project '... .fh_data\\Packages\\website\\data files' or equivalent HTML folder" -- V3.2 end -- Create GUI controls local btnFolder = iup.button { Title=strFolderTitle; } -- V3.2 local txtFolder = iup.text { Value=strInitialText; ReadOnly="YES"; Border="NO"; } local txtStatus = iup.text { Value=strInitialText; ReadOnly="YES"; Border="NO"; } local vbxFolder = iup.vbox { btnFolder; txtFolder; txtStatus; } -- Hyperlinks / Text tab local lblPages = iup.label { Title=" Open URL pages:"; } local lstPages = iup.list { DropDown="YES"; Visible_Items="9"; Value=IntPages; " In same Page frame"; " In new Tab / Window"; } local lblChars = iup.label { Title=" Abbreviate URL:"; } local lstChars = iup.list { DropDown="YES"; Visible_Items="9"; Value=IntChars; " Never"; " > 30 char"; " > 50 char"; " > 70 char"; " > 90 char"; } local lblTabSep = iup.label { Title=" Tab separator:"; } local lstTabSep = iup.list { DropDown="YES"; Visible_Items="9"; Value=IntTabSep; " None"; " Comma ,"; " Bar |"; " {TAB}"; " ,{TAB}"; " {TAB}|"; " {TAB}…"; } local hbxHyper = iup.hbox { lblPages; lstPages; lblChars; lstChars; lblTabSep; lstTabSep; } local btnHyper = iup.button { Title="Convert all URL (www. http: https: ftp: mailto:) to active Hyperlinks and alter special Hypertext"; } local txtHyper = iup.text { Value=strInitialText; ReadOnly="YES"; Border="NO"; } local vbxHyper = iup.vbox { hbxHyper; iup.hbox{ btnHyper; Margin="99x0"; }; txtHyper; } -- V3.0 -- Media Popups tab local btnMedia = iup.button { Title="Turn non-image Media into icons"; } -- V2.8 local lblPopup = iup.label { Title=" Version of image window Popup:"; } local lstPopup = iup.list { DropDown="YES"; Visible_Items="9"; Value=IntPopup; " Default Lytebox v4.0 supplied with FH"; " Updated Lytebox v5.5 from lytebox.com"; " Customised version with Zoom & Scroll"; " Customised version Large Screen popup"; " Customised version Dojo Zoomer popup"; " None - popups completely inhibited"; } -- V2.4 Dojo and None added local hbxPopup = iup.hbox { btnMedia; lblPopup; lstPopup; } local btnPopup = iup.button { Title="Convert all media file Popups to one of the alternative versions chosen above"; } local txtPopup = iup.text { Value=strInitialText; ReadOnly="YES"; Border="NO"; } local vbxPopup = iup.vbox { hbxPopup; iup.hbox{ btnPopup; Margin="99x0"; }; txtPopup; } -- V3.0 -- Tree Diagrams tab local tglAlso = iup.toggle { Title=" Retain 'See also' links at the top: "; Value=StrAlso; RightButton="YES"; } -- V3.2 local tglMini = iup.toggle { Title=" Align to left instead of in centre: "; Value=StrMini; RightButton="YES"; } -- V3.2 local hbxTrees = iup.hbox { tglAlso; tglMini; Homogeneous="YES"; } -- V3.2 local btnAddMin = iup.button { Title="Insert GEDmill minitrees into featured pages"; } -- V3.2 local btnRemMin = iup.button { Title="Remove GEDmill minitrees from featured pages"; } -- V3.2 local txtTrees = iup.text { Value=strInitialText; ReadOnly="YES"; Border="NO"; } -- V3.2 local vbxTrees = iup.vbox { hbxTrees; iup.hbox{ btnAddMin; btnRemMin; Margin="99x0"; }; txtTrees; } -- V3.2 -- Content Tables tab local tglToPs = iup.toggle { Title=" Table of Pictures: "; Value=StrToPs; RightButton="YES"; } local tglToRs = iup.toggle { Title=" Table of Reports: "; Value=StrToRs; RightButton="YES"; } local tglToDs = iup.toggle { Title="Table of Downloads: "; Value=StrToDs; RightButton="YES"; } local tglMenu = iup.toggle { Title=" Links in Menu: "; Value=StrMenu; RightButton="YES"; } local hbxTable = iup.hbox { tglToPs; tglToRs; tglToDs; tglMenu; Homogeneous="YES"; } local btnTable = iup.button { Title="Adjust custom Tables for each type of Contents item chosen above"; } local txtTable = iup.text { Value=strInitialText; ReadOnly="YES"; Border="NO"; } local vbxTable = iup.vbox { hbxTable; iup.hbox{ btnTable; Margin="99x0"; }; txtTable; } -- V3.0 -- Index of Names tab local tglFilter = iup.toggle { Title="Filter:"; Value=StrFilter; RightButton="YES"; } -- V2.5 local lblLength = iup.label { Title=" Surname:"; } -- V3.0 -- V3.2 local maxLength = iup.text { Spin="YES"; Size=28; SpinValue=30; SpinMin=1; SpinMax=99; } -- V3.0 local lblNames = iup.label { Title=" Names:"; } local lstNames = iup.list { DropDown="YES"; Visible_Items="9"; VisibleColumns="12"; Value=IntNames; "Default Primary Names"; "+Alternate Names only"; "+Married Surnames only"; "+Alternate/Married Names"; } local lblStyle = iup.label { Title=" Style:"; } local lstStyle = iup.list { DropDown="YES"; Visible_Items="9"; VisibleColumns="8"; Value=IntStyle; "Italics only"; "Underline only"; "Italics+Underline"; } local tglIdRiD = iup.toggle { Title=" RiD:"; Value=StrIdRiD; RightButton="YES"; } -- V3.2 local tglIdPRN = iup.toggle { Title=" PRN:"; Value=StrIdPRN; RightButton="YES"; } -- V3.2 local tglIdCiD = iup.toggle { Title=" CiD:"; Value=StrIdCiD; RightButton="YES"; } -- V3.2 local tglIdARI = iup.toggle { Title=" ARI:"; Value=StrIdARI; RightButton="YES"; } -- V3.2 local tglIdUID = iup.toggle { Title=" UID:"; Value=StrIdUID; RightButton="YES"; } -- V3.2 local tglIdFSI = iup.toggle { Title=" FSI:"; Value=StrIdFSI; RightButton="YES"; } -- V3.2 local hbxIndex = iup.hbox { tglFilter; lblLength; maxLength; lblNames; lstNames; lblStyle; lstStyle; tglIdRiD; tglIdPRN; tglIdCiD; tglIdARI; } -- V3.2 if fhGetAppVersion() > 6 then hbxIndex:append( tglIdUID ) hbxIndex:append( tglIdFSI ) end local btnIndex = iup.button { Title="Adjust Search Filter, Surname max length, Alternate Names / Married Women Surnames, and Idents"; } local txtIndex = iup.text { Value=strInitialText; ReadOnly="YES"; Border="NO"; } local vbxIndex = iup.vbox { hbxIndex; iup.hbox{ btnIndex; Margin="99x0"; }; txtIndex; } -- V3.0 -- Create GenDex tab local lblGenDex = iup.label { Title=" Exclusion Flags:"; } local lstGenDex = iup.list { DropDown="YES"; Visible_Items="9"; Value=IntGenDex; " None"; " Private only"; " Living only"; " Private & Living"; } local hbxGenDex = iup.hbox { lblGenDex; lstGenDex; } local btnGenDex = iup.button { Title="Create a 'gendex.txt' file excluding Individuals with chosen flags"; } local txtGenDex = iup.text { Value=strInitialText; ReadOnly="YES"; Border="NO"; } local vbxGenDex = iup.vbox { hbxGenDex; iup.hbox{ btnGenDex; Margin="99x0"; }; txtGenDex; } -- V3.0 -- Apply Language tab local lblDoLang = iup.label { Title=" Apply translations"..g_trans_misc.Output_Language; } -- V3.2 local hbxDoLang = iup.hbox { lblDoLang; } local btnDoLang = iup.button { Title="Apply the chosen Output Language to all page menus and headings"; } -- V3.2 local txtDoLang = iup.text { Value=strInitialText; ReadOnly="YES"; Border="NO"; } -- V3.2 local vbxDoLang = iup.vbox { hbxDoLang; iup.hbox{ btnDoLang; Margin="99x0"; }; txtDoLang; } -- V3.2 -- Shared buttons local btnDefault= iup.button { Title="Restore Defaults"; } local btnSetFont= iup.button { Title="Set Window Fonts"; } local btnGetHelp= iup.button { Title=" Help && Advice"; } local btnDestroy= iup.button { Title="Close Plugin"; } -- Create the Tab controls layout vbxHyper. TabTitle=" Hyperlinks / Text " vbxPopup. TabTitle=" Media Popups " vbxTrees. TabTitle=" Tree Diagrams " -- V3.2 vbxTable. TabTitle=" Content Tables " vbxIndex. TabTitle=" Index of Names " vbxGenDex.TabTitle=" Create GenDex " vbxDoLang.TabTitle=" Apply Language " -- V3.2 local tabCont = iup.tabs { vbxHyper; vbxPopup; vbxTrees; vbxTable; vbxIndex; vbxGenDex; } if fhGetAppVersion() > 6 then tabCont:append( vbxDoLang ) end -- Combine all the above controls local allCont = iup.vbox { iup.hbox { vbxFolder; }; iup.hbox { tabCont; }; iup.hbox { btnDefault; btnSetFont; btnGetHelp; btnDestroy; Homogeneous="YES"; } } -- Create dialogue and turn off resize, maximize, minimize, and menubox except Close button local dialogMain = iup.dialog { Title=iup_gui.Plugin..iup_gui.Version..StrTranslate("Output_Language"); -- V3.2 allCont; } local function setStyleList(strMode) -- Disable Style option when only Primary Names included if lstNames[IntNames]:match(" Primary ") then strMode = "NO" end lstStyle.Active = strMode end -- local function setStyleList local function setActiveGUI(strMode) -- Disable GUI options when invalid folder and during button operations dialogMain.Active= strMode vbxHyper.Active = strMode vbxPopup.Active = strMode vbxTrees.Active = strMode -- V3.2 vbxTable.Active = strMode vbxIndex.Active = strMode vbxGenDex.Active = strMode btnMedia.Active = strMode -- V2.9 setStyleList(strMode) end -- local function setActiveGUI local function setNumberOfText(itemName,intNumber,strOld,strNew)-- Insert number into text status message for each tab if intNumber >= 0 then local strText = itemName.Tip:gsub("Number of",tostring(intNumber)) if strOld and strNew then strText = strText:gsub(strOld,strNew) -- Adjust wording of tip end if intNumber == 1 then strText = strText:gsub("s "," ",1) -- Remove plural "s" if number is 1 end itemName.Value = strText end end -- local function setNumberOfText local function isFolderOK() -- Check if folder contains FH Website or CD-DVD HTML files and set global paths local strNameIndex = StrFolder.."\\_nameindex.html" local isOK = general.FlgFileExists(strNameIndex) and general.FlgFileExists(StrFolder.."\\fhstyle.css") if isOK then local intLine = 0 for strLine in io.lines(strNameIndex) do -- Search the _nameindex.html file (Unicode UTF-8 not needed) -- V3.0 intLine = intLine + 1 if intLine == 1 then if not strLine:match(" XHTML ") -- First line for FH V5 & V6 contains XHTML, earlier versions do not and fhGetAppVersion() < 7 then -- First line for FH V7 also does not contain XHTML -- V3.1 isOK = false break end elseif intLine < 30 then -- Find custom style CSS subfolder path and Home page HTML filename -- V3.0 local strStyleCSS = strLine:match('^$') -- FH V7 CSS line not on new line -- V3.1 -- FH V7.0.8 is on new line -- V3.2 if strStyleCSS then StrStyleCSS = strStyleCSS end local strHomeHTML = strLine:match('^
  • .+
  • $') -- Allow for translated Home menu entry -- V3.2 if strHomeHTML then StrHomeHTML = strHomeHTML end else break end end end if isOK then setActiveGUI("YES") txtStatus.Value = "The HTML folder is valid, so please use any of the improvement option tabs below" txtStatus.FgColor = iup_gui.Safe txtFolder.FgColor = iup_gui.Info else setActiveGUI("NO") txtStatus.Value = "Folder does NOT contain FH V5 or later Website or CD-DVD HTML files, so use Browse button" txtStatus.FgColor = iup_gui.Warn txtFolder.FgColor = iup_gui.Warn end txtFolder.Value = StrFolder dialogMain.Active = "YES" btnMedia.Active = "YES" -- "Turn non-image Media into icons" button must always be active -- V2.9 return isOK end -- local function isFolderOK local function setControls() -- Reset GUI control values lstPages.Value = IntPages lstChars.Value = IntChars lstTabSep.Value = IntTabSep txtHyper.Value = strInitialText lstPopup.Value = IntPopup txtPopup.Value = strInitialText tglAlso.Value = StrAlso -- V3.2 tglMini.Value = StrMini -- V3.2 txtTrees.Value = strInitialText -- V3.2 tglToPs.Value = StrToPs tglToRs.Value = StrToRs tglToDs.Value = StrToDs tglMenu.Value = StrMenu txtTable.Value = strInitialText tglFilter.Value = StrFilter -- V2.5 maxLength.SpinValue = IntLength -- V3.0 lstNames.Value = IntNames lstStyle.Value = IntStyle tglIdRiD.Value = StrIdRiD -- V3.2 tglIdPRN.Value = StrIdPRN -- V3.2 tglIdCiD.Value = StrIdCiD -- V3.2 tglIdARI.Value = StrIdARI -- V3.2 tglIdUID.Value = StrIdUID -- V3.2 tglIdFSI.Value = StrIdFSI -- V3.2 txtIndex.Value = strInitialText lstGenDex.Value = IntGenDex txtGenDex.Value = strInitialText tabCont.ValuePos = math.max(0,IntTabPosn - 1) -- Adjust tab selection min value = 0 -- 31 July 2013 isFolderOK() -- Adjust buttons according to current folder, etc end -- local function setControls -- Set other GUI control attributes local tblControls={{"Font";"FgColor";"Alignment";"Expand";"Padding";"Gap";"Margin";"Tip";{"TipBalloon";"Balloon"};{"help_cb";function() iup_gui.HelpDialogue(0) end};setControls}; [btnFolder] = { "FontHead"; "Safe"; false ; "YES"; "4x8"; false; false; " Click here to choose HTML folder "; }; [txtFolder] = { "FontBody"; "Body"; "ACENTER"; "YES"; "0x8"; false; false; " Chosen HTML folder full path "; }; [txtStatus] = { "FontBody"; "Body"; "ACENTER"; "YES"; "4x8"; false; false; " Advisory HTML folder message "; }; [vbxFolder] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "10" ; false; false; }; -- Hyperlinks / Text tab [lblPages] = { "FontBody"; "Body"; "ARIGHT" ; "NO" ; "0x8"; false; false; " Choose URL target attribute "; }; [lstPages] = { "FontBody"; "Safe"; false ; "NO" ; "0x8"; false; false; " Choose URL target attribute "; }; [lblChars] = { "FontBody"; "Body"; "ARIGHT" ; "NO" ; "0x8"; false; false; " Choose URL text abbreviation length "; }; [lstChars] = { "FontBody"; "Safe"; false ; "NO" ; "0x8"; false; false; " Choose URL text abbreviation length "; }; [lblTabSep] = { "FontBody"; "Body"; "ARIGHT" ; "NO" ; "0x8"; false; false; " Choose column tab separator characters "; }; [lstTabSep] = { "FontBody"; "Safe"; false ; "NO" ; "0x8"; false; false; " Choose column tab separator characters "; }; [hbxHyper] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "4" ; "0x0"; false; }; [btnHyper] = { "FontHead"; "Safe"; false ; "YES"; "0x8"; false; false; " Click here to convert URL Hyperlinks and Hypertext "; }; [txtHyper] = { "FontBody"; "Body"; "ACENTER"; "YES"; "0x8"; false; false; " Number of converted lines of Hyperlinks / Text in the HTML folder pages "; }; [vbxHyper] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "10" ; false; false; }; -- Media Popups tab [btnMedia] = { "FontHead"; "Safe"; false ; "NO" ; "8x4"; false; false; " Temporarily turn each non-image Media into a JPG icon \n See Help & Advice for full details before using this button "; }; -- V2.8 -- V3.0 [lblPopup] = { "FontBody"; "Body"; "ARIGHT" ; "NO" ; "0x8"; false; false; " Choose image popup version "; }; [lstPopup] = { "FontBody"; "Safe"; false ; "NO" ; "0x8"; false; false; " Choose image popup version "; }; [hbxPopup] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "4" ; "0x0"; false; }; [btnPopup] = { "FontHead"; "Safe"; false ; "YES"; "0x8"; false; false; " Click here to convert media file Popups "; }; [txtPopup] = { "FontBody"; "Body"; "ACENTER"; "YES"; "0x8"; false; false; " Number of changed non-image Media records or HTML folder pages with media Popups "; }; [vbxPopup] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "10" ; false; false; }; -- Tree Diagrams tab [tglAlso] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Retain 'See also' links section at top ? "; }; -- V3.2 [tglMini] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Align minitrees to left instead of centre ? "; }; [hbxTrees] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "60" ; "0x8"; false; }; -- V3.2 [btnAddMin] = { "FontHead"; "Safe"; false ; "YES"; "0x8"; false; false; " Click here to insert GEDmill minitrees "; }; -- V3.2 [btnRemMin] = { "FontHead"; "Safe"; false ; "YES"; "0x8"; false; false; " Click here to remove GEDmill minitrees "; }; -- V3.2 [txtTrees] = { "FontBody"; "Body"; "ACENTER"; "YES"; "0x8"; false; false; " Number of HTML folder featured pages with GEDmill minitrees updated "; }; [vbxTrees] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "10" ; false; false; }; -- V3.2 -- Content Tables tab [tglToPs] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Create a Table of Picture Images ? "; }; [tglToRs] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Create a Table of all Reports ? "; }; [tglToDs] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Create a Table of File Downloads ? "; }; [tglMenu] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Include links in the Menu Bar ? "; }; [hbxTable] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "4" ; "0x8"; false; }; [btnTable] = { "FontHead"; "Safe"; false ; "YES"; "0x8"; false; false; " Click here to create custom Tables of Contents "; }; [txtTable] = { "FontBody"; "Body"; "ACENTER"; "YES"; "0x8"; false; false; " Number of HTML folder pages changed due to the Contents item Tables chosen "; }; [vbxTable] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "10" ; false; false; }; -- Index of Names tab [tglFilter] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Create the Names Search Filter form ? "; }; -- V2.5 [lblLength] = { "FontBody"; "Body"; "ARIGHT" ; "NO" ; "0x0"; false; false; " Set maximum length of Surnames in 1st column "; }; -- V3.0 [maxLength] = { "FontBody"; "Safe"; "ARIGHT" ; "NO" ; "0x0"; false; false; " Set maximum length of Surnames in 1st column "; }; -- V3.0 [lblNames] = { "FontBody"; "Body"; "ARIGHT" ; "NO" ; "0x8"; false; false; " Choose Names to include in Index "; }; [lstNames] = { "FontBody"; "Safe"; false ; "NO" ; "0x8"; false; false; " Choose Names to include in Index "; }; [lblStyle] = { "FontBody"; "Body"; "ARIGHT" ; "NO" ; "0x8"; false; false; " Choose font style of additional Names "; }; [lstStyle] = { "FontBody"; "Safe"; false ; "NO" ; "0x8"; false; false; " Choose font style of additional Names "; }; [tglIdRiD] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Include the Record Identity ? " ; }; -- V3.2 [tglIdPRN] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Include the Permanent Record No ? "; }; -- V3.2 [tglIdCiD] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Include the Custom Identity ? " ; }; -- V3.2 [tglIdARI] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Include the Automatic Record Id ? "; }; -- V3.2 [tglIdUID] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Include the Unique Identity ? " ; }; -- V3.2 [tglIdFSI] = { "FontBody"; "Safe"; false ; "NO" ; false; false; false; " Include the Family Search Id ? " ; }; -- V3.2 [hbxIndex] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "4" ; "0x0"; false; }; [btnIndex] = { "FontHead"; "Safe"; false ; "YES"; "0x8"; false; false; " Click here to convert Index of Names "; }; [txtIndex] = { "FontBody"; "Body"; "ACENTER"; "YES"; "0x8"; false; false; " Number of Names processed in the 'Index of Names' page "; }; [vbxIndex] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "10" ; false; false; }; -- Create GenDex tab [lblGenDex] = { "FontBody"; "Body"; "ARIGHT" ; "NO" ; "0x8"; false; false; " Choose flags to exclude Individuals "; }; [lstGenDex] = { "FontBody"; "Safe"; false ; "NO" ; "0x8"; false; false; " Choose flags to exclude Individuals "; }; [hbxGenDex] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "4" ; "0x0"; false; }; [btnGenDex] = { "FontHead"; "Safe"; false ; "YES"; "0x8"; false; false; " Click here to create 'gendex.txt' file "; }; [txtGenDex] = { "FontBody"; "Body"; "ACENTER"; "YES"; "0x8"; false; false; " Number of Individuals included in a new 'gendex.txt' file "; }; [vbxGenDex] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "10" ; false; false; }; -- Apply Language tab [lblDoLang] = { "FontBody"; "Body"; "ARIGHT" ; "NO" ; "0x8"; false; false; " Apply translations"..g_trans_misc.Output_Language; }; -- V3.2 [hbxDoLang] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "4" ; "0x0"; false; }; [btnDoLang] = { "FontHead"; "Safe"; false ; "YES"; "0x8"; false; false; " Click here to apply current Output Language to page headings "; }; -- V3.2 [txtDoLang] = { "FontBody"; "Body"; "ACENTER"; "YES"; "0x8"; false; false; " Number of HTML folder pages translated to Output Language "; }; -- V3.2 [vbxDoLang] = { "FontBody"; "Body"; "ACENTER"; "YES"; false; "10" ; false; false; }; -- Shared buttons [btnDefault]= { "FontBody"; "Safe"; false ; "YES"; "4x6"; false; false; " Restore all tab option settings, and window positions and sizes "; }; [btnSetFont]= { "FontBody"; "Safe"; false ; "YES"; "4x6"; false; false; " Choose user interface window font styles "; }; [btnGetHelp]= { "FontBody"; "Safe"; false ; "YES"; "4x6"; false; false; " Obtain online Help and Advice pages "; }; [btnDestroy]= { "FontBody"; "Risk"; false ; "YES"; "4x6"; false; false; " Close this Plugin "; }; [tabCont] = { "FontHead"; "Safe"; false ; false; false; false; false; " Select required improvement option tab "; }; -- V3.1 [allCont] = { "FontBody"; "Body"; false ; false; false; "6" ; "3x8"; false; }; } iup_gui.AssignAttributes(tblControls) -- Assign GUI control attributes if fhGetAppVersion() > 6 then -- FH V7 IUP 3.28 tabCont.TabPadding = "4x4" -- V3.2 -- was 26x4 else -- FH V6 IUP 3.11 tabCont.Padding = "4x4" -- V3.2 -- was 26x4 end local function setToolTip(iupItem) -- Refresh control tooltip otherwise it vanishes in XP! iupItem.Tip = iupItem.Tip end -- local function setToolTip function btnFolder:action() -- Action for Folder button repeat local strDirectory = StrFolder if not general.FlgFolderExists(StrFolder) then strDirectory = StrPublic end local dialogFolder = iup.filedlg { dialogtype="DIR"; Title="Please select 'FH Website' or 'FH CD-DVD\\data' or equivalent HTML folder"; directory=strDirectory; } dialogFolder:popup(iup.CENTER, iup.CENTER) local strStatus = dialogFolder.Status if strStatus == "0" and StrFolder ~= dialogFolder.Value then StrFolder = dialogFolder.Value SaveSettings() -- Save sticky data settings txtHyper.Value = strInitialText txtPopup.Value = strInitialText txtTrees.Value = strInitialText -- V3.2 txtIndex.Value = strInitialText txtGenDex.Value = strInitialText end until isFolderOK() or strStatus == "-1" end -- function btnFolder:action function lstPages:action(strText,intItem,intState) -- Action for Pages dropdown if intState == 1 then IntPages = intItem SaveSettings() -- Save sticky data settings setToolTip(lstPages) end end -- function lstPages:action function lstChars:action(strText,intItem,intState) -- Action for Chars dropdown if intState == 1 then IntChars = intItem SaveSettings() -- Save sticky data settings setToolTip(lstChars) end end -- function lstChars:action function lstTabSep:action(strText,intItem,intState) -- Action for TabSep dropdown if intState == 1 then IntTabSep = intItem SaveSettings() -- Save sticky data settings setToolTip(lstTabSep) end end -- function lstTabSep:action function btnHyper:action() -- Action for Hyper button if isFolderOK() then setActiveGUI("NO") local intLines = IntImproveHTML(StrFolder,tonumber(IntPages),tonumber(IntChars),lstTabSep[IntTabSep]) setNumberOfText(txtHyper,intLines) setActiveGUI("YES") dialogMain.BringFront = "YES" end end -- function btnHyper:action function btnMedia:action() -- Action for Media button -- V2.8 Unconditional -- V3.0 setActiveGUI("NO") local strAns = 1 if not isFolderOK() then -- V3.2 strAns = iup_gui.WarnDialogue("Confirm Usage"," This is intended only for use with FH Website/CD/DVD HTML pages \n The next step would be Publish > Create a Website/CD/DVD wizard \n If that is not your purpose then please Cancel now \n","OK","Cancel") end if strAns == 1 then local intLines = IntMediaIcons() if intLines >= 0 then setNumberOfText(txtPopup,intLines) iup_gui.MemoDialogue(" Non-image Media icons prepared, so now Close Plugin and use \n\n Publish > Create a Website/CD/DVD wizard \n or \n Edit > Undo Plugin Updates if not wanted \n") end setActiveGUI("YES") dialogMain.BringFront = "YES" end end -- function btnMedia:action function lstPopup:action(strText,intItem,intState) -- Action for Popup dropdown if intState == 1 then IntPopup = intItem SaveSettings() -- Save sticky data settings setToolTip(lstPopup) end end -- function lstPopup:action function btnPopup:action() -- Action for Popup button if isFolderOK() then setActiveGUI("NO") local intLines = IntImprovePopup(StrFolder,lstPopup[IntPopup]) setNumberOfText(txtPopup,intLines) setActiveGUI("YES") dialogMain.BringFront = "YES" end end -- function btnPopup:action function tglAlso:action(iState) -- Action for See Also links setting if iState == 0 then StrAlso = "OFF" elseif iState == 1 then StrAlso = "ON" else iup_gui.MemoDialogue("tglAlso invalid iState") Strlso = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglAlso) end -- function tglAlso:action function tglMini:action(iState) -- Action for Minitree alignment setting if iState == 0 then StrMini = "OFF" elseif iState == 1 then StrMini = "ON" else iup_gui.MemoDialogue("tglMini invalid iState") Strlso = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglMini) end -- function tglMini:action function btnAddMin:action() -- Action for Update GEDmill Minitrees button -- V3.2 if isFolderOK() then setActiveGUI("NO") local intTrees = IntAddMiniTrees(StrFolder) setNumberOfText(txtTrees,intTrees) setActiveGUI("YES") dialogMain.BringFront = "YES" end end -- function btnAddMin:action function btnRemMin:action() -- Action for Remove GEDmill Minitrees button -- V3.2 if isFolderOK() then setActiveGUI("NO") local intTrees = IntRemMiniTrees(StrFolder) setNumberOfText(txtTrees,intTrees,"updated","removed") setActiveGUI("YES") dialogMain.BringFront = "YES" end end -- function btnRemMin:action function tglToPs:action(iState) -- Action for Table of Pictures if iState == 0 then StrToPs = "OFF" elseif iState == 1 then StrToPs = "ON" else iup_gui.MemoDialogue("tglToPs invalid iState") StrToPs = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglToPs) end -- function tglToPs:action function tglToRs:action(iState) -- Action for Table of Reports if iState == 0 then StrToRs = "OFF" elseif iState == 1 then StrToRs = "ON" else iup_gui.MemoDialogue("tglToRs invalid iState") StrToRs = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglToRs) end -- function tglToRs:action function tglToDs:action(iState) -- Action for Table of Downloads if iState == 0 then StrToDs = "OFF" elseif iState == 1 then StrToDs = "ON" else iup_gui.MemoDialogue("tglToDs invalid iState") StrToDs = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglToDs) end -- function tglToDs:action function tglMenu:action(iState) -- Action for Menu Bar setting if iState == 0 then StrMenu = "OFF" elseif iState == 1 then StrMenu = "ON" else iup_gui.MemoDialogue("tglMenu invalid iState") StrMenu = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglMenu) end -- function tglMenu:action function btnTable:action() -- Action for Table button if isFolderOK() then setActiveGUI("NO") local intTable = IntImproveTable(StrFolder,StrToPs=="ON",StrToRs=="ON",StrToDs=="ON",StrMenu=="ON") setNumberOfText(txtTable,intTable) setActiveGUI("YES") dialogMain.BringFront = "YES" end end -- function btnTable:action function tglFilter:action(iState) -- V2.5 -- Action for Search Filter setting if iState == 0 then StrFilter = "OFF" elseif iState == 1 then StrFilter = "ON" else iup_gui.MemoDialogue("tglFilter invalid iState") StrFilter = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglFilter) end -- function tglFilter:action function maxLength:valuechanged_cb() -- Action for Length spin value maxLength.SpinValue = math.min(tonumber(maxLength.SpinValue),tonumber(maxLength.SpinMax)) IntLength = tonumber(maxLength.SpinValue) SaveSettings() -- Save sticky data settings setToolTip(maxLength) end -- function maxLength:valuechanged_cb function lstNames:action(strText,intItem,intState) -- Action for Names dropdown if intState == 1 then IntNames = intItem SaveSettings() -- Save sticky data settings setStyleList("YES") setToolTip(lstNames) end end -- function lstNames:action function lstStyle:action(strText,intItem,intState) -- Action for Style dropdown if intState == 1 then IntStyle = intItem SaveSettings() -- Save sticky data settings setToolTip(lstStyle) end end -- function lstStyle:action function tglIdRiD:action(iState) -- V3.2 -- Action for RiD setting if iState == 0 then StrIdRiD = "OFF" elseif iState == 1 then StrIdRiD = "ON" else iup_gui.MemoDialogue("tglIdRiD invalid iState") StrFilter = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglIdRiD) end -- function tglIdRiD:action function tglIdPRN:action(iState) -- V3.2 -- Action for PRN setting if iState == 0 then StrIdPRN = "OFF" elseif iState == 1 then StrIdPRN = "ON" else iup_gui.MemoDialogue("tglIdPRN invalid iState") StrFilter = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglIdPRN) end -- function tglIdPRN:action function tglIdCiD:action(iState) -- V3.2 -- Action for CiD setting if iState == 0 then StrIdCiD = "OFF" elseif iState == 1 then StrIdCiD = "ON" else iup_gui.MemoDialogue("tglIdCiD invalid iState") StrFilter = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglIdCiD) end -- function tglIdCiD:action function tglIdARI:action(iState) -- V3.2 -- Action for ARI setting if iState == 0 then StrIdARI = "OFF" elseif iState == 1 then StrIdARI = "ON" else iup_gui.MemoDialogue("tglIdARI invalid iState") StrFilter = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglIdARI) end -- function tglIdARI:action function tglIdUID:action(iState) -- V3.2 -- Action for UID setting if iState == 0 then StrIdUID = "OFF" elseif iState == 1 then StrIdUID = "ON" else iup_gui.MemoDialogue("tglIdUID invalid iState") StrFilter = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglIdUID) end -- function tglIdUID:action function tglIdFSI:action(iState) -- V3.2 -- Action for FSI setting if iState == 0 then StrIdFSI = "OFF" elseif iState == 1 then StrIdFSI = "ON" else iup_gui.MemoDialogue("tglIdFSI invalid iState") StrFilter = "ERROR" end SaveSettings() -- Save sticky data settings setToolTip(tglIdFSI) end -- function tglIdFSI:action function btnIndex:action() -- Action for Index button if isFolderOK() then setActiveGUI("NO") local intNames = IntImproveIndex(StrFolder,StrFilter=="ON",lstNames[IntNames],lstStyle[IntStyle]) -- V2.5 StrFilter -- V3.2 remove lstIdent setNumberOfText(txtIndex,intNames) setActiveGUI("YES") dialogMain.BringFront = "YES" end end -- function btnIndex:action function lstGenDex:action(strText,intItem,intState) -- Action for GenDex dropdown if intState == 1 then IntGenDex = intItem SaveSettings() -- Save sticky data settings setToolTip(lstGenDex) end end -- function lstGenDex:action function btnGenDex:action() -- Action for GenDex button if isFolderOK() then setActiveGUI("NO") local intNames = IntCreateGenDex(StrFolder,lstGenDex[IntGenDex]) setNumberOfText(txtGenDex,intNames) setActiveGUI("YES") dialogMain.BringFront = "YES" end end -- function btnGenDex:action function btnDoLang:action() -- Action for Apply Language button -- V3.2 if isFolderOK() then setActiveGUI("NO") local intPages = IntApplyLanguage(StrFolder) setNumberOfText(txtDoLang,intPages) setActiveGUI("YES") dialogMain.BringFront = "YES" end end -- function btnDoLang:action function btnDefault:action() -- Action for Restore Defaults button ResetDefaultSettings() setControls() -- Reset controls & redisplay Main dialogue iup_gui.ShowDialogue("Main") SaveSettings() -- Save sticky data settings end -- function btnDefault:action function btnSetFont:action() -- Action for Set Interface Fonts button setActiveGUI("NO") iup_gui.FontDialogue(tblControls) SaveSettings() -- Save sticky data settings isFolderOK() -- Only enable GUI if HTML folder exists -- V3.1 end -- function btnSetFont:action function btnSetFont:button_cb(intButton,intPress) -- Action for mouse right-click on Set Window Fonts button if intButton == iup.BUTTON3 and intPress == 0 then iup_gui.BalloonToggle() -- Toggle tooltips Balloon mode end end -- function btnSetFont:button_cb local function doExecute(strExecutable, strParameter) -- Invoke FH Shell Execute API -- V3.1 local function ReportError(strMessage) iup_gui.WarnDialogue( "Shell Execute Error", "ERROR: "..strMessage.." :\n"..strExecutable.."\n"..strParameter.."\n\n", "OK" ) end -- local function ReportError return general.DoExecute(strExecutable, strParameter, ReportError) end -- local function doExecute local strHelp = "https://pluginstore.family-historian.co.uk/page/help/improve-website-or-cd-dvd-html" local arrHelp = { "-hyperlinks-text-tab"; "-media-popups-tab"; "-tree-diagrams-tab"; "-content-tables-tab"; "-index-of-names-tab"; "-create-gendex-tab"; "-apply-language-tab"; } -- V3.2 function btnGetHelp:action() -- Action for Help & Advice button according to current tab -- V3.1 local strPage = arrHelp[IntTabPosn] or "" doExecute( strHelp..strPage ) fhSleep(3000,500) dialogMain.BringFront="YES" end -- function btnGetHelp:action function btnDestroy:action() -- Action for Close Plugin button return iup.CLOSE end -- function btnDestroy:action function tabCont:tabchangepos_cb(intNew,intOld) -- Call back when Main tab position is changed IntTabPosn = intNew + 1 -- 31 July 2013 SaveSettings() -- Save sticky data settings end -- function tabCont:tabchangepos_cb iup_gui.ShowDialogue("Main",dialogMain,btnDestroy,"map") -- Needed to honour setting tabCont.ValuePos below tabCont.ValuePos = math.max(0,IntTabPosn - 1) -- Adjust tab selection, min value = "0" -- 31 July 2013 isFolderOK() -- Adjust buttons according to current folder, etc iup_gui.ShowDialogue("Main") -- Display Main GUI Dialogue and optionally Version History Help end -- function GUI_MainDialogue doc_icon = { Name="doc_icon"; File="improve_doc_icon.jpg"; Code="FFD8FFE000104A46494600010101004800480000FFDB00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFDB00430101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFC00011080063006503012200021101031101FFC4001D000001050101010100000000000000000006000508090A07040B03FFC40055100000040404030403080C0A0905000000010203040506071100082131124151091361711422810A1516243291A1B1172325273642454666C1D1F0333544536263647286F1263437435256656773748294B2E1FFC4001B01000202030100000000000000000000000506040702030801FFC4004511000202010204030505010B0D010000000102030411052100061231134151071422326115236271819134424352535493A1B1E1F01726354445556373A2A3C1C3D1D2FFDA000C03010002110311003F00DFC61628464CCF267BAA343862123C0E984C00994E75DB42E98CE91774D532B855B154748C2A65782826A2A89C1339CC0535840078804A0E6FB35BDA7C8717714B65152DCFEC195514BE9FD08C85EE3B7D77C5AF67D8E736539E4AB6EDF2CD6B111C4904FCC5A7C52A1D8E192475607041191B8DC64119518F9D34896359628754923719574D3A76561EA194107BF91DBCF1C4FAAFB06613DE62689D399A05D3F9362D029922CEE080F5CB76868AB16B16236892644542774FD140CA3607698956F455976FC4292AA10C7A7C9AE5FD40F5A517637E91F8C86FD2CF39E2986335F7B4822551E55AA117A230D894C1253088C360ADD9D18AB2CA1666F134DCA4E85F3723B70E1CAB6722644C9BB6E54CC428090F730098BDCFA76A3A1C5E8F9639795000D0428A56E3FF00F589069F3DF95F12BFC96F362455A3ABAC72CC7D31B09163E6AD3A31E2B4D2B02712A86628D1E5B7C0C027E1C082DCDBA1992679E8EA9275BA9467D2667211628D7032B90030638FA927BEF6C47C9465DCF7E293DE8DF7FF00486361D7A3DF1C794F91BCB7A9A1A4C7A3FE238E87D20FAFF4E29E5EF68776AEA1C5DCE54A04A805EC21426BB1C77D3E4C542F7F65BC4431FBE5ABB55738D3E671A8F65AEB5D36A5D26359EA6389C1A6587A126CFB2DCEB0749A4A51498DA88349966638B059CFA3B1549E9B0A581564B0A842002A92A18D8F65FCF9052BF7C6A5A4D9834CA36751B6B4B99AA5B992A558CCB34821826776C28C0C0DD885CE48CE29CE1CAF258AB58D3B91496EC4556133E95243199A660B1A97750A3277F32002718EF6E87C8765A14BF1494FC6FD2679803EA7FE5F375BE3C87C80E57D4F95244446FF00A533107D5100F30E83E63799F858AB3ED0BDFCF2D7F4F2FF00FAFA0E1D7DCEA7F3683FA24FFE71098FD9E995753E5C8B121FF164C81F54471C92B8E43B2CF2651DAA9394064A88348ECAD4F66F8FC1DD2934CC2E136F1384405F3E64B1DBAF103A0B953708266324B10E928002450A6298C036698E139A1370E5BABD9B5F56905441D37D2548A0DFD9FAB1B60D42F19E106E5A20CD1820CF2104175C820B60823623D38D53D2A821988AD002229082224C8210E08DB6230083E447140F28CB7563218DE53CDFE5A908CCFB4AE7E9625D8FE6AB2FAE1F2EE862E570C527511A912409CAB1D9C6A12470EDC98C421CF0C4C0E9AC4792C387AD211A0EA115DE98664E984B7576914C8DE6493E646FC49A85E04A250689A444C6232ECC50E0514560F30C1D6501BC4E1AB984C91C535DBA8E58B968EDC565D1F8F8168E53344C6E22848B2DA6628D8C5307BD2D8A25128E8216BDC074B683A6F0AA266A8D902AAF18CCB659E14B4C746A6A7447998BCB8B458C8C35F3222873389EA436E522A8C1A350B2AAABA0066DB861E50590511712CBA76CA14F7AD72D9BD07BDD5502EA202CA3005940A30ADB002651B2B9C750011CFCA555B4CD5C516586524D57233DC9AEC71975DC9F089DDD3F7A72C9DD81D34E1638FD08AEF4C33254C65CAB948A646F32C9F3237E24952F0A312844451293DF097E618689CEBC1E6084AA7042230D703C44114DC3751CB172D1DB858AD19591991C147462ACAC086560705581C1041D882320F0ECACAEA194865601959482181190411B1046E08E29FF00B2DE7E80C952955C9C66978A3197A589459C5A2EF526AE1E28D98A331C5FBD54AD59A4BBA5F842C3C082475042E205D313E17ED1FCA2B6BF7D51A225B7E84CE66DBC4B031FDFC715B5D9230081547976B649333B53C425E98A4767088BB32385DA1DC43DFCC11C45C24472D5549CA07310C20559055351337AC4314C0038AB5AFCBC0E4BACF3AC8F0E2AAC20C95698D537979313ACF018A6B4E8FA5E8295DACAA8ABA5506C9910072E0E65DC99321943116586C6EB9D7391F9639D3DA673EC3AE5BD5A94FA5C5A7EA1E2D392A4754516A1555DA43341625332C993D2AAABE19EFD580696A7CC5AB685CB1CBF25086A4F1DC6B35C24C92B4A6C2CCD8081248D4215200C924B1DF6DF8D484273F19598C48B3F54A6D51974A4BA6212F1A768D3B94A6F6E483966A7EBC3608616830317CF08E5E3659250EC9B382B50282AE8C8A420710BA3BDA71933AF0854D774D2A7C4632CE90492F6A24FAEDD4873DC25284C9F0F5CADDD45D02C4A5D6CBC5008A1CA25670D45D3F50B714DA9AC2019A7CDECCD22D24834F791CA373BB8A8EF6608A4A4FF0035D539B822DA57651591E22EE2703A55203640CAAA2EE1F155D6566D89BD7AF0C0E122437BA6EBB588326A1BD9FF0057A89D108B67EA63ACC88AD4DD3CAB4164E19521AF95864526E7930C7594321726C0176EB22E9289C71750AD9270D942AAD12170FCE72A6DD55012EE7B25D0D797B52E67D3DB9866D33ED6A35B435984097752D3EC3518259D6ABD485D5E4B0F73DD3C410F8917BBB491A62424B41CF1A8FDA75748B034C4B869D896FB2973054B3189E448DA5599D4AAC620F182F5F4B78A031F871A645FB5B3206DAE0B5715496FFB6B554DB0DB4E1924D7EBA7D78A1E97EB34835C7B75290548A631DF847234D351965A0519343A2B0917A9B0A16B42DE1861F1B650E89B6EEA20C1D3702BA66898FDD02A901D23A67354FD1395275CCE66368F50694A0AD20D30D5E9BDDA91087C37D3E20CA43919207B158A3F70BBF70EDF2C94B5024155D672EDC28ABB343564C4C65DC2607B0681520A6F403B74292D10950EFA1D4F64DA81E86D0D188BACE9EA69446839E38F1675145448A1945E291072A80FAA448AA9504CA548852859D5F90791B9223E6FA3A3EA3CC167986F7B37D7ECD8A57BDC674A5A738A2AE654AD0577171A79238E18D64746F0AC8660550B244FCD3CC7CC52E833DFABA645A557E6BD3A186CD7162333DB1E395E86964914D7112B3BB94565EB8CE0E48E36B770EA1F3861710750F9C3F6E383AB06A346BF7932B60FF13A85B07977FA63C4103A1E0B26609A1AF7A55082997E15AB71500C1C0005F48F5844C01EAEB7E98E40F708B072DA88207FBB1B19DB6CFBD76F538FD38BF3DEE41818A9E43F768FA67BC59F33EBFD9990A262977100F3100FAF003536546551A9F4F14F9EBF5A1CCA7694E60951D441A15255CB2426085BA85A8ED0496FB52AAB723A15534D4B10E62810C21C5A718AFB0E611E9CA8F4122867030A8844E6749F24D9D2CD14513230872A4005903954298144CA20251DAE17B08DC02354A69BB3138A0DE2E050E67982267F66ABEBD07E8E57294343AB24146CCF7ECC32DB49278E3874F4B0B188AD4B5C7548D760258BC05F1E1602B0CE77E075AD52C78D6E086A412C75D9627792E342CE5E08A63D2AB5A4017A65E907AF2C41D87108DEE442BD4B83EF04AD5A671712E41C89C3E0AA90F2AC3C8786B64C88B5023259EACAB629532814891D5398A0500318DA0E061E648F3327298A35827330180404A2F6511010100D06EE7E4DB4101B87872C74DCC3250D92E9EC622B2B2D1165134DEC15A20E0F1278B8A493E8BB366E4C42AAA9838C5BACA814C203C0610317D6280859691D08B56E3C57116E8DC447511EEC9CC77D46E3E3BDF9BBDB9EF69B5E8CB1D94B91D933C799EACB5A456AEB07C4C12ECAAC1FC7EE0A81D27037E17AAC55EECF6617AED5DA0589FEEEC09D584A640002F04654A988E73D44861E638C98D423549C944ED1694E8ED569EA4A19D912CC9392329C61DCA8D1FC798C663D070517632FBC4A1AB9D006EB824E1241210496EE0A028228F0AC7A3B459D885754400DF91A29CFF4E26EF10B5F70E7AEB858DD7285436652F56BB3B156666851D896546DD9D4B1C6C0124EC06FC438AD5844558EC4888B90A8B2380B83D800C07700EC077EDBF1637D87CF81D44EAD2770114E56808EFBF14CB1CB885834E603B78E29B73A4C4933574AEB0C18A3E83AACEBBD4488B67F0C501278D9E4367C8EA8D956EA880191513580AA11520828450853266010B8DB0F60D3E1751DACE4BDC529425C1B72F5A668F5BDBFB7C3150D99D874F938E6A6B7C994D2528A4F53DCC5980AA10396256847742F22915773FC7D26E90197551488826202B3A58CA148820451654E9A24515274568829C7ED93DA736A0D1A534E5DD224B2663D09E0AD6A8D21739184E852CDB8DBB9C6FC573AAC939E49E5315FA8CEFAA5F8E208096F13C52A8147AF5150363DFF006C5C64843A5A60683C39651C2C02A3F883970A82F117CE1C1CE2BC49F29615143AEA14C02A1BD5F504A51B1471C3A6784C1D38FBB9BDEBF7BC09A68BC76C545C810B32D0A6E72347CB200528AAA324CCB288F7873948A2873A7C37B0E94A3BD9AB0DC94F6676672A855374CA70CCFD4393E493CE11E4BBB7707A7B0A3D4393D44E40925410314A83432E64A3D1C40C43471D1012438216D902AB9BCA334BE61CE5E62696E5629CB97068B5469C90864DF1541BAC64E5292A102688CE71F722A10891890882347CEC0806E074B3749814C655E24533CF2DF3FF0029F3072FF306B31C3041A2F2B6A1255AB25874F0ED350A70DC16A187019634F1B080F5165432EC1800A9ACF2DEB9A66ABA669EF24926A1ACD58E6992256EA856DCEF0185DF70598A60E3A402420DF7E34ABEE77B294E1B4A950B3DD3F424E9C7AACAAF241A2E93E444AB42A99C0622294C71E6A0A07A9F0AE62608C31154A521C1A4B2E5648E76B1911343CAAF1887B2F74912CFBECD4CFE1C854E878BA6446A0F8EBA6396B6B620343009551050C43708E960E310F56F8D7FD34A7729D23A7B24D2F9121484124DA7F2BC125096A14DCA054D9C1A010F421AC53308000AAB0A2DCAA39707BAAE5C1D570B18CAA8730E352A8C6620C3DD38CB4685C38632FDBD54858B5858394D98BB39B2C2DB893072B01924AC4139F88E0203C16E618E63E47E61B5CDBCD1ED675EB45D9AEFB3CE64786369843E0D616F4C8EB422663D10958F1D6FF2090BC846E4F16E7346950E83A3F2269B080BE0F3569426754F13AE6305A695FA06F202C0855DD8AAAA8DF8D6F2B3AD28287AF21BD36823F810DC6FE43C3F56FCAF86C3CF548C554CA4901D9543284021FE03360E13898A05309C09EAF09AC226E561372C11399FAA705ED49161E5F85B08B73FEA76F1D83C477625A7FA9C650841A48B1086394A75066D8408264130018FC3DD05C0A5D44A1611B58314FC5559864C6C36DFFCE0AC40181B90372307E5EFE59E2C1798647DE2F71FEC997277507723E99F2C6D9DBB0AD777C0DA70A48E2E0008446673E9C80CC1816C03AF21DBE9BEFCA2679B38BBCBA9600B8807106BBE81B5C435DAD60B6FADC8B338DEA039F80F13A7D233C9E9F425F464CF18B589C321456E8BA418108A2CEA26BA601DE702A098228B81E24CC07294040D8873156F9988871F165FE2885C07F3DA593DAFFF00BCBC560E5D39EA177AD029549F4DD29A4B54A36822B11324D6EBC52296BD6655CC7248AE01590153D3839CF9F0AFA9D9B115DD402C169C4B2C5223475E67465F75AF1FCC8857E6520EFB1046380BCC54C3EF848B116C2A7101E27031B716E29C6191C34F30BDBC75BDED8B5F2B8F8B371BFF00274BADBF830E7B7EBF984714E738529CC74DCC4D0D5E8E44A1E92AED9AC75466696DC8101B3B4571B93D390E2BF776D0DEADC2D71D316DFDF8A6DD221C6C244532182F7F58A4294434D34B721B0EE0236C11E6786A341A4C35ECD7B1E1B5B67104D1CC17C41540EAF0CB019E83D393BE091EA23685258F1B509268A6883AD65532C6F196E9329214328CFCE077DB6DF7E3293DA36F2D5DD1D7F234576B07E7D4DC3CFCFD985816ED1F79C35E100B85BDE48A885C47FE7A9BF90EDFBEF6C2C6DD4E1297A75E93B18FD7F928CFA7E7FE3188B5E4262423041C90727B751C7F57F8DCF065D98F9FDA539297B5023352A0F334C69CE702854261ED252525D33C66BC2E37157CB28F891D8DC188449649E2608770758E630184C429784C3688AF6FE64A214F8D14468AD4E4A262A2CA9A22D6154A927C65570382EA8BC2CEA4702A2E07501638A9C4A01CE0713711806D04FD9CD90F506E7CA4505388F33539978DF5B41C7914ECD7C812BAA993ECBF29FDFA6B2E1BEB6638F398FDA07B2DE6AD62D6B9AC726F33C9A85C4812C357E608AB44EB5E08EBA011246540F0A350DEA7248DCE26699CABCE7A4528B4FA3CC1A3AD681E492259B4C699D1A590CAE7C473D44F593D276C0DB8AAF8EFBA33C973D64E21B1AA31596310E74052B960F9952C7CC9C913508A94176AEA7D51058A4548450A072180AA10A60003142DCDE1FEE8A3B3DA50881E312FE592A740E2C292A81E290194E8AC2E227457128AC88BD653B3672649631082B1054E0504A51301B842D27AB1E44725F2EE79729F23C1B2BD43E1D28CE12DD4C7332CB4853B97020D1D710A82445687AF13879D899B3B519A852A8DCCA90C291CA5314404A18997397660767CC699B740F955CBE4B829B91545D43695C88D5671F6A509E8EA9D78398A64804DDE70005C0C403696C428B58F62EBEEE8FC99CDC90DB42EFD1CCAC422896487E28D23FBD27C3EE00D8E31B71949A77B45669DD398F42692BBAA296D1D0331E88A4F85DB3D23E3F33DC12363C5513BF752592C697E2A2798B5042F704DB52B1DB7DEA4869AFEAC51C517CD7C0739FEE8268EE6128FB0894AB0AA9352D4732E42A7A4E1A78AC3060797E7F2D3BF7E9BC02231964505DC411E2CD81A3E720641640CA702BDEA69EB21E764AF6792823C740E89DEE3BD38A73E1FF43D7A5BE6D75C7A649ECCFC9252A9CA0751299D30A6520CF72C395DE4BB384A5264890199208EDC3472C56710A8BC3E1483C62B2AC9DBA6AA2882A4319BAEB2261121CC5169D2F9B7D9572FD0E615E57E5CE60A5A96BDCBF7F416B1A8DC92FD68E1BA61932D59D4061E3411162324C619403D5C02BFCBDCF7AADAD21F5AD6F4BB34F4AD56B6A821AD5E2AB33490064004D19C8FBB9640A0E075F4B13B67898EE9B5711BF0CC92200F3118744ADA0E9FC9F9F4BF8F90E396F5DB8443E12C821CC3EE744AC3E3A37B8F4E5BE97DF0DEE64311B87D99277001B88142636BEDE56D4443907B2F81E732088DC06B3CF01A6C13233000E61F8BFF00ED8446FCC6AEAF142403D5448EE07D95267B8ED94FD370001BF96CF72BCA73F0DAF5FF004847B0D8F7EAEDB7D4819EFB70E2E5AD7A1BDA67A7E1E70D8A07D1E8E2170F2EB6B0ECC2E5A57DD6D33D3CEBAC3229D370F8B5F71D76FA0470DAE29F00DFEFD53D875B4CACF9EFF8BCC06DA800EA1E42C0E29D8D86D5BA7D0E7609999F98F2F1EBCC750E4660861DB2D4B60300696E3BE00CFC193E9DBB76F3C0F95E5F35B607639D4137C6361F17FE3B79F0E8E1A66000C5134D14EC4A060E22FBD714011280DCC50F8B68221A05EDBEE1B8F5270E4DF8C21C4001FDD01E7A751D7CB11E97A7425394DF6709F4DC2629B8466766251B0EA510E1B881B61D75F1C76170EAC16E21D8038AFA8887CF7F11FAC6F79AF5D18C3D2616C12488AAB56033D1F3752AF5E7D4671BE7B8C478E56C3E44806063C4B027CFE58242F993DBAB6C1DB0326BDA4AF002BDA17307F1245797E9DCE1FAAC3D35B06D85814ED29776AF6DC2FA7BC5151DB8BF3F2710DEF6E5CB0B07B5D4E8D5AE212C0ABC40804FF2317A03EBEBE99DCEC0E83755381BD558FF00D6DC6F730B0B0B1CE9C5C9C566E6156EEBB45324A17B714A75807E697A23CBF7D2F89AB529490D586B209F946E48703D11642E157C917D37B857428B112A826EE3BDD0F725AE36E2B5E0B6655604BB45F238023F2A53AC9CEC3EACBB10FDB6FF002C4D39FA63630460D577F2BC566A4D677DD26CA13094230B363822AA80E5541C1C854931294520542E6E3394A21630E182A42D21D3197C4EA10B85F064114BB5AB04F448C0856EFB90401F53C079E409EFE1BA31E321FBC43247B415FE645DDBB7D30719F3E38CB90CB4DC789C42F71DDE4C57BEDC94E5B696BF3E6381C745CB27AC267109D6FF00CBA641B7CCAF4D37E7BEE3821735225D0011FB0DCE83BEA123C3442DD2DE91A88F3D76D435D4471CD4996C2FF7979DC475DA45868873FED36DBEB1B8F3C3957AF640049D5FB8C2FDA707D3F06327077FECF35F92684EE3ECFF002CE294C33DB7EE36F5EDE9D86381E744CAF888F1388405BFB74CA1F52BED11F6F800FB9265642F77308E77F8F4CE1D6FFEF7A7874EB821715325A0B8FD84E791D443F00A183B787A4DB7E5E41E02C0E6A7CB001FEC3E7A11BF2902183CAFCDD5B6D442DCB71E4660AF676C9D54F618FB460D81C0FE2E3EBEBFA0E07CB343E4286DE94E6F51DFE2381B1CEE7BE3F31B764CA985EEE60D70E5E9F347807F3B71E7CBF56071D2794FBFACEA0F700DFD3E68E7D00161D3900DC7C475BE095DD519602F7A1B3E18397DEFA14223ECF49B0EC01A69E781C7554A560BDE84CFDCADF7BC856B7D2FFEB5D3CF60BE0CD7AF3EDBEA80FC3BFBFC07D3F0E3D71FAF036596238C7B8EE7B7BA4D8EC36D8EDE7B79FE6361C592CA60AA5E07305EF38CBC001109A447BC030705AEB5AFC56DC440447A6D2756721C3701D2C005B0E96D2DFBEE3CFA0C675AA94AC2A1402844FE51130077834F2140051110B18441D5C00A3A88EE161B5C6D8EF2B3901001DAE1B0F2BFE2DAFA086C21D7C37933412168430B240271EF33A4C7F7990BD03E1FA83B9DBD0E718645F8FA3C003E1CF830B458C671D418907B6D8D80FCC1E3243DA5CF386BF370BDED018A06F6FCFD9CB95C3F7F0B61604FB4CDE70E601B05C47EE045479DFF000FA73DF98790F3BE1624734C413983525F49A31FF621FEFF00DBF4E20E90DD5A6D56F5573BFF00CD7DFF005F4F2EDC7D0C70B0B0B1CD5C5D5C55266996EEBB46B22BFD294EB473B7C9971F6BECBEBB62684E9179B18B36EA4A307611B7A77024748441F961E9A2DBBA50DDE91431880A1C5504C9C17B814C2610F5748359B75853ED1DC8685F434A55B3E8971DFCFA7F96255BFABF4EDBAAAA0BCD70E4D54543A6AA66075C49A899848A14441B8FAC530080D8476D3A83A6935E496B51923AED64A472031F448E841B3301D62321B0493D98648EFB70B9765449AE2B4C21CC91E1BA915B22080FC3D60AF7FA1183DBB0E07DCCD35B400D69065A10D779991E77FEB42F6F0EBB6B61607335572D7869ECB03B8DBE13A5CBAFDB87F68F40D2CFCE6B4D34D4067086DB5E4EFAFF00E9BC3C75F9C581D56DA6417019C6180017D001E6BA8DEDF16E9A5FC3D80DB5E9DA38E9D114938F96ADDF2C76FBDEFF004DCEE7D4E01CB3C233D5A93FEB3561DB1B1FBBC773DBFB3BF0C0E66CAEBC236A792B8EDBCCE8875D3F861B8EA3B6DE781E7536D7800F569CCAA36B6F34A41E76FB778FECBE9620735BE9805EF39433A5B85E7969F15EB7FD7D0479CD71A5E17119D215A5EE22577A72B00FA2FD3E0360C1EADA75E38C686C73BE454BBF4FC7FDE720E4ED81535AAE324EA87FA7ADDB603BA6C77FD3E98DC79CCDD5F6E36A6F2A0EC16F8548F87F5DCFCB96C16C30399BB301ADA9ACA7AF59B12DFC7EDC1FE436F322715D295F39D615A69F25EF21F16BAEDE3D3A5985CD78A5217BCEF09D3A83C0D7FF008BED1E9D7A9DAFA4EA6D8C72FCD8DB18A57BF0EFF31EFE67EBBF99E06C976A79EACA0FA9B3541EE3F0FE9DBB7E5C0EAD37E60B88A034D2520209CBC4609B111102898388403BFD4402E21D4435E58EC4A2C63EFBF3B0F3E76EBE7E6201ADF1C916AF5496FEBCF3092F5F55EF8746836F9FC395B1E33E6028DA7F2E7E8317CCAFBE9F89E9EDC141A1EB0FD063D07508F1DFC1D3EF10D9E9EE195F3823B8F539E079D534F4EA0DAA5670718F12D5618DBCB05719CEE0FF0056F9CA8769D3B12E60DB005EE1008B00E81CAA04E8017B87D1FAAD85810ED378AB77B5F21CF99AE0B337B2CC49DB45C9C5C0BB6713F4E8AA0A96FC26E0512314C5031404004004006E18581FCEA862E68D62320AB2588D5958619585683A9581DC30390411907008C8DFDD0640DA4526520831B10474E0832BE083839C8C60F9E41FCFE90F8585858E5FE2F5E32B5EE8727B9C242ABB9558A49B304425B89FC03AA462C4614A15B3F48C58DCA68713778520B96C2641D3844E2DD54FBC4963914E228DB19CF7998EAEE71398D55A7531846E263465C08888986E222237111B8DC475111111D70B0B160E832489A757E8775F9FE5665FE1DBD08F53FB4FAF09DAC2A9B9312AA4E10E4804E4431E0E71E5C3039CC557310111AA939DEE1F965C7878F86075DE61AB8001FEFA539E83A7DD9703CBCF0B0B06D6DDA0C3166C0F89BB4D20EC063B370316188F48314646DB1453E9F4E181CE612B7D8DF7D19CF7307F1CB9DAD7FF008BAE185CE606B61AF7AA1398E83F969D740DFD6D77E785858D8B7AE8E9C5CB43E1CED6251BFC3BFCFC7AD5AB91BD784EC3BC519FE27E1FA9FDA7D7860735F6B50F15EA74E5A00887DDA77D47FA781E795EAB38EA353672BEBF96DDF4FEFE1616331A96A21B6BF746338C5A9C6364FF0089C696A9531FB9ABF75FE063F51F8781D755E2B308D86A6CE7CBF2E3CE771FE730C4BD75AC6611BD4B9C86E201FC78F7A7FE4C2C2C6C1AAEA83B6A57C7E572C0FF00D9C4734A9E3F7255EE7FD5E2F2C63F79E5C03C72A54F91F708B98ECD3168D386E87A320BC597088AC8B6050EB02092AF08B1D3441659657BA21809DE2AA1F8788E6115858588AF3CF23B3C934B23B1CB3BC8ECEC4F72CCCC4927D49278DC228900548A35501405545500606C00000FD38FFFD9"; } etc_icon = { Name="etc_icon"; File="improve_etc_icon.jpg"; Code="FFD8FFE000104A46494600010101004800480000FFDB00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFDB00430101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFC00011080064006403012200021101031101FFC4001E00000202020301010000000000000000000009080A0607010305040BFFC40032100001040104010303020602030100000003010204050600070811120913211422311541162332517181246142529153FFC4001D010100020203010100000000000000000000070806090103040205FFC400281100020202020104020203010000000000020301040005060711081213142131154122235171FFDA000C03010002110311003F00BFC68D1AC077337370ADA1C2AFF703702FE1E398BE390D65D8D8CC572A76E72323C48801A38F3AC671DCC8B5F5F15859536511808E3791DD698CCCA74F895D164CD9B2634487082F953654A9028D1E1C4131C434A927339820470898F214A57306C1B1EF7391AD554591B9BEA538F4CC8656DDF14F6E6EF9299E81CB1E65AD194D59B6D8F908EF6D926CB287C62258461991154F112151C96A2A0B256391C9A8D567337A3D466C9B7F974AC8B66F88B166A2E3981C222D7E61BBA28E76BE3DB64728442083507F61E4F077D4D5C620C61A78F63304FC9853BB02DBCC276C31D878AEDFE33578AD0C26B50502AC082F7888D46BA54E3B9C4913E795AD6A489D30F225C8E910C72235BD319116561BEA0BBD2AE95B9FC8FA7D88A497FCC6E15B2B4EF35A450AABD162C8C99A6AF9B1A428DEEFE70728BD1213C0AD6BDE88D5F209E9E1B7D7A8E36E2EF1F20F71E717EF912724DC571DA632AA2B9EA86AD952DC8AEF9443CE90444E93DD546A6B70726F989B19C4CC78771BAD932B2E6C40F363B82D10C76999E45E0F5138B0EA90E16C2AF61BF924B7B6340AA69DAF8C931D29BEC2A1EDD3F5CDDEDB99F285B43B6380E0F46A55485232E5B4CD72278511A8D2C87C6998ED345797A57BE1A56587B0E7FB4D9E7417BC593386750F3DE769FB9A3D310EB64A4476BB168D1A0C919F05086362596A04BC8915553840A244C866263222E7DDE7D6BD6EFF00A3C8F7C25B5F6C1CE9F5693D96C96243EE19B0A44FC54FDE3304B1B8EAE4C12120821989C6F4DF4CDE39C6542D54FDD0A4949F7366D5E6DECCBF3FFF004521AA8EC577688A8A836FE13FDFA21E1F6EDE008D93B1FCCADF6C45D1FF0099169731B6FE3AC755E9D2B40FAA24EA5AE101FF000C711D553D18D6B7DC8E66A2352BDA6F587E6F94ED3A65F848511FE4F8C2C06952315BFF00A111FE6756F5D276876BFAEFEFD6FADB1F5C5DF2A597181BAFB7B8666B59DB5B22762AE9F895C858E73114CD1CB9577552DE2679BD62B62D630DF0C6CA1F5E4B9D5EF4C5DA34EB93D35F49B1318F3F568ED63EC17E3CF808BB5E9A4A7FAF10D8999FD44E473ACF57FD3BB0B6159F6B7FAA032818B7B1D3CFD51F74C444B26959BAE11FCF99294F818F3E66223CCBB781CA2E7371DDEC76FBED3D1720F6FA13BA999EECF7941CBEBE18D8F2127D8E3628F1E2CB6C68ED4428598F52C069115EFBE5F047BD8471FF00953B2BC98A47DC6D5E5D12CE5C418C9738BD8F55996D0A13A6A7EA946772C8687DD7342CB086E9754727DB1A799551350278D9CDDD97E4D510AD30ABB58F60370C16D8F5A34516F69E53D88F68AC60B4A5F119115DECCB8C493064A0CAE8B28BE0468FA77A3887419A5D8B76765EF0FB29BF54CF7D9546778AB9F5B02EA6A317FE26575B15AD04C1587928A5D9880F9266482FEA916F22B1D5A781B63ADD869EF59D6ED29D8D7EC2A3255669DB5126C21911E7DA6B38898F31304251E44C660C24866266C9EAB6DACDE6BAA6DB4F7AAECB5B7942FA9769B81F5DEA2FD12D8B9219F1312243E6080E080E0486621C322A2A768A8A9FDD3E534697171339A7719BE572B8EDC8EA48DB73C8BC6A3BDA21ABC40C7773ABE38DCF4BCC55E8F50B6692308930F5D14858732334B3E988A20D856D331C4547223917B4544545FEE8A9DA2FFF0035E2CFD0CE7468D1A633E39F3A25743953A7490C2870E2C8972E6C920C1162468C279A44A9120CE600008E263CC62988C1086C73C8E6B115748CA5D8D8FA8C6F4C9CBF20FAC8FC46D9BBF3D6E0B8DBD878E1DDFCBE0B9C39792DAC52363B8B52113FBF03B98E0551E3D28800976B9191249FA966E6E43371DDBBE2AEDDCBFA6CF3921784A6B79427B9C5A2DB5AC2C7264D6520624438E358A3D61C85446FD45242C940C557A3575B736FB02C776BF09C6B6FF1488C87418A5582A6BC68D634A56815EE34D96E6222167CF92434D9F23A4FA99720C7444691134C665808E18C10C78E11C78F184C0468E06A0C11A38DA8C100036235830898D6B4636318C6A27DAC6FE13BB46B18CDB278D84E1997E693432A4C3C4717BFC9A4C5840749972C5455726D1F123019D90B2248E2902018BB290CF63048A473535D8951BDAA4286498E6029631FB23614080C7EBF32531111FDF9F19D4F72EBA5B61C500A42CDAC39F3E056B193329F1E67C40C4CCFFE6537FD5277247B93CD8DDA911CE32C0C28D4FB7701E37FB8C126295418B72147A77E3ED64D22FD1EC6AAB1AF5778A22AAF6B6E35FC29F29D0EAC53ED4EC556BD2BA04A9434545545443B468022A74BDA088456FF00E488A8A89B4ED23E49BB9956477362C2C9BFCDF27B0B1B930D08BEEDBE493E459583DC88BE43529244872F4BF28E6AAA76E4EAC71C16F4E1C0EAF06ACBBCA29E31A5CB8C12F66022AA2BD8F472A0CAEF25473BB5FC7CFC2AF7D6AEDF3FEE6BDD4CAE3FD77C46A6AED5CD0687528DB5FBA0CB285588A8AF6D54210FAE32D35C0D9B0D361C47D81582E0848A35EDD67D05ADEEDB5CA3B4F9D5EDC55A1C9792EEAC68F59AE3553736B7DD6C1DBB362CA2D1C256DF754AE85A9724555866C90911CACCBEBEC44DF3935B6311A8D472ACB85223A222FE3EE20D19DF5D2FC397E35F2FC7ECA8A9FB2A7CA2A7F745FDD17F6D5DA32DE056CC5FD3C880CC76BD84781CC63DF1869DB9115C8D5EDDE3E0E54E9CDFFC93E1517B5D5467951B4ACD8DE406E7ED805EE7C4C6B2144AD5739AE7FE976D021DDC01BD58BD79C78B6220AAAF6E7A35AE7395CABACF3A3BBB2FF655BDA6937BAFA54F6DAFA83B1458D743975AE54F9975DE275DED7921C86B912322F307030BFC172BFF006467EA2BD3D6BBA9A96A79171BD9DFBDA3D95E2D659ABB49432E50BB28659AE4AB35D55C6C567A9162260EB83106A1F2C6C37FD782ED4EE8E57B419B5466F87D91EB6D2BCEC61FDA7B9819F5C42B4932BE68C6E67BF1A47837B6915DEC91072048850B3571FE13F26EB77E76FA9ACC5258B3DD122A90687F3384BEDA2B91EA8AD723D8FE98E454456B98BF0BF95A4C7F9EBAFDFB4ED3FDA2FC2A7FD69DD7A3D6756A1CC2F684477AC404BF6DAC791EA9F231B9CEF155E91EAE57B95551557BEFBD609EAD78FEBA35DC6B942D4B56D2760CD358680C09DBA6755D6D1F2CC78F79546573154CF9280B071E7DB111123FA22E53B62DAF2CE1CD7B1DA78D62B7F552C2220A575572BD1B1F044F985C5D55A593863C449D45944449194BFDE4CF1CEB37E71A8522B26FF0BEEB61466DB6D867B00A48165477B18CC971624AB1888B356964CC8F1D4C825F7ABA4236D2231E7028CBB8B835CA4B6DF6C42F709DCF89FA06FE6D0CC162FB9B8F98428A5B0201C48D0B3083186A83FA4B8503C76038ADFA6876837B808CAEB0AB79F351AFD8D72768AA88EFCFCF6EFB97E7FCAE97CF2752771B379F6BB9A587C69095F5D655FB7FBF1595C35F2C8304BA33228AE24B07F2F975A14FA2094AF77B9651B116A289910884A3F9B13C781A35E7565AC1B8AE836D5B24132BACA2469F026462A1812E14C0B24C494123115AF1488E51986E4F8731ED72768A8AA698C4D985495DE7F502E48EE8CB7ACCA5D8DAAA6D95C31A4F270234F2B658F243C37ABDE3F7A2D8D7E47EFF004EF3516448AAD6ABFEE9D9A5F9E9DCF4BBDBADDDDC12BBDD99B8BBFF00B87924990FED4C5FA85AC27447AA76E449322615A9F28D798AADE9AE444607A630D751C4870182BD7451B87DAFE511E9D2AA27EFFE3FEFE75DBA34C62F9CDB8438C665B8E1CDE7C78E590396C92BD013A5E9EDED15EFE9CD5E9BE48A9D7CA7EDD6A73E358FC4C6AA6255C31A0C3182D1B18D5EDAC44555E9BF9F84EFAFF1FB6BDFD7289DAA227E555113F6F955E93E57E3F3AE666667CCCCCCCFF73E667FE7EFF79C4408C78188188FEA222223FEFE23F198EE5D9563F82E299266B95D9C7A6C6314A4B3C82FAD653BC4102AEAA21A6CC90EF8EC8F60004F680D5429CBE210A38AF631D437E44EEE4BDF8DF0DCFDDD980244FE3ACB2CADE0C232B1D22BE91A44878FD69DE2441BCB5B471ABE0BDEC4F17900F276E57AB95B6FAB773D9BB91773F8BFB4D6CA4C0B16B46AEE7645064790730CAAB4CC56E2B0CA3722131BC66507CEC5DDBC76F9089A834644A701EC10F585844AB8CF9B34C3047639ACEDEABE647BD551A28ED5EDC632F4BD0DBE4F5E97E17A5D6C03D3AF5E9707E3D7F9C72625EBAEEEE901242D98578D5E81731665D6CDA422A3BA50165A2723F1574D7F77861B561AC5F551DA71D87C9F59D75C461BB4A3A0D81ADE5440AC96E3923BCD48453058913C35E12CAAA3544C3ACD8B3EC935028C8B29E0AA8322C24BBC4519BE5E288BE6522AA34401FEDEE19EA8C677F08ABE4EFB5174FC7D18B69AF061919858445096C4E59AE57B5C8AC59252189DBFA445F157391AEF84733A77EFA50BC5EE30E7BC9ACF29A59A964C2C4E04D61E24638C9F237A2B526CE5F1512C820FC7C468E56476B9CD6BBC95EE5BA5F19762E9F65F06AEA8871442923008647B5115EEFE5FCAAAA227DBD78F49DF7F72F7D7CA6ABFF7EF6AD6EC2DE55D6E90C8F8E680AC0D5B33041FCA5E77B02C5D8028121AC02B84D3F7C41904B9D3E21E201677D32F4BDCEAEE3D776DC88003967270AA572A8C89FF0011AEAFEF656D71306484AD1B1A4FBF2044B160A1032535C98C93488D44446FE111113AFC7C7C2F5FEF5AEB77B6FA16EAED8679B773D07ED65D8BDA53808545F08D6260FB95539553F0B5D662893D9DF7D92333AF944D6C65FF00089FF49F09A355FB2CF66AEF4CFDCF939FF11B028979255B7DB6F36EB6B6D99288E6C812E2520694C12B7DD739AF06313E8C0AD72AAA28FE15CDF1729A4F345C8AB2E3667FC86DBBA8B4355C03F217713250441906C63076E94E2178A11AE778B410442674AAD6B06D627F4E8D31933FD33DAB1F8E12AAC9F12A9773737AB9AD55EDDF5212569DCE7F7F3E4AC90345ED13FA7AEBE34C23501B8722FE02DD4E63EC7C967D33F0BDF5B6CAE9A293ED7BB1CCD89292A0B1D8A88AF0A5754D619EE6A235BF56C2748846A6A7CE98C3468D1A630D2B8F555E5D59719F6362E2F854F757EE76F212DB1DC76CE391E2998D6395B1E22E5B92C42318F51D88076959535054700809B71FA9008A5AAF6C8D1F557DF5DA8F6ECDF1D999E571D68CDB51630EB9AE6F9454B48397D8C9BAF172FF004996258D07B8889D2B104BF2AABA96FA3B8EEB793F6671DD76D801D45476F62CAACF1216CF5D51D6D15CC67F06B272D64D09F226B031289199C843D45728DB710EA3E51B5D21B51B07052D5AEDA64A19495B5BA8A562D018F825B010D6029A3224A6B16C198218C43D73701AD679950D2A6CB72B21420764973A43D57A462391EAD45FEA2909F68BC917CDEAAD574E0E1EF0273DE4364B5B9266908D1A9D1ED7C7828C7361C38CE7F9A782BDCA8773D88FF221111CEF25FBBC1E88CF3B827C6816FCEF1BA7DD31C7AFA798C0C6195837044D8EF547AB7CD5BD2B888AEEBF75F9722F5DA5CD369768718DAEC760D452C004574608D9F6086DF246B11A8AA8D6F5E1D7C35A8AA89F288BD6B25EEFEDDDCF32DDECB8E536375FC5B51B07D25D15112CB66EA2E24CDCBF11E24C65AB23AD54A214818032097C49C625E9E3A3B8FF0002E3FAAE59792ADA733DEEB6B6C1DB1708B43508D8202C450D5C4C4C28A14D10B772265D60E5800635A6173AC78F1C5FC2764F1D8102B2AE23648422614A308D8F73863637B7AFB6C55FBD1CBF1D7DABF0A9DF5A95DF09F0888888889D222227489D27C27FD268D1AAFF0096730D1A35D1264821C734B9451C78B18269126415C8C1800013CE6291CBF08C18C6F7397F64455D319588E4C6256B97F23B7D6D2A0320F101B9B90D6B9F1DEF4624884B1D8762F80D515ED2397B555F256F8F7D7E10D37FF4FED8BA2DF6DAADC8DE5CB2191CFDC9DFCDCBCA28CA76A31C5A395FA1C762A309DB9AC15AC5B68C89DF48A054444EB4698CC9793B05FC77E756D5EF9B98B1300E45E3C9B3F9DCEE9A38B5F9855BE2371C9F62423D05199363C7C786032A2392255E445239AD4723A6D2A2A7E53AFF003ACAF957C7EA4E4C6CAE59B5B6CE1449B6117F53C56ECC253371FCBAAD1C7A3B47319FCD703DE57C1B160BB21AA6658471A7B866AEA07F1137AEF335A0BBDA0DD409A9B7E3648EB8A67D5164F4FD4ADE2D6BDB16BB2C1BBED49AC9C1F68567300851489883B41BC512E2BD8563262E8D72A8AD556B915AE455456B915151517A54545E951517FBEB8D31869767A8AF11EBF94DB634F15B249579261B6AFBAA1B98D5C3B09611185EC5CD520DE58EBF4F6B11A1EDBEF28DB2E1C396F1BD62B06561E6306388B22414608E01BCC7395EC188211355E5291E47306C18D8D73DEF7BD8C6B5155CE6B51552A95CC3F571DEADC4CDEF719E3E644FDB4DA9A89F22BEAEE6B21432E5F9A86194A04BEB1B2B3826352C3B073524D65555060C98F0942969224C823871655EA2E1BCDB957255DBE13695AABBA1955C6EEEC9C855D793BE55244A053649ECB110E01AD0860B00592C885894E431DDFCFBAF785F126D3EC2A8EDCEBF914368AB8F540865CDA0A654D798493EB0D6555224195B2B0A253493F0CCBA4222617A77F16B33D95CDEC9F690CAD8CB2D112411BE3EF315CF629555A88D4ED3E59D749E28D4735ABF08FD468A83677FB35A9FED11354EAE3BFAA672336AB2A864DC3C9A56E9E132250FF5981791A0BB22851553A2CDA4BA8D1812C9241F69560D83A64796D1AC71AC2217EB45698D88DF9C577BB18819063B3852872C013A35AF6F9B8660A15AFF001F856AB915557B46FCFC748A9D6BAFB47AE397F03DB8BF94B91B12DE36D5C5EEA9B58EAF7AD1321B721B2D4D76AADC1B61AC59A844A1BEE5118C17B3EFA6FB5B8376568CEBF0D45AD50F1D4D4A2DD05F5291675F4C55F0D124C25F652EA72B4CA54C5B88809520E059107BF7FE8D1A351764C986A1DF39373A5EDFEC45DD063E87939DEEDCA06D5E11570BA7594FB2CB3AAEB0FA2622389EE8EA6449004AC6F439F3AB9AAF1B8C276A5CD8CE87555F36D6CA645AEADAE89267D8584D28C10E0C28417C9972E498A410831C00110863148C1886D791EE6B5AAE4839C62C6EC7995C9595CA2BB83303B17B1D22762BB115D3C4510728CC3DCEED33B6C433D46E1D795053108F089DF5ECC7A3B1E9331FB11358C663C72DA316C66C76D96D4C650A9F0FC56BE0DB9C2D728A6E4525AEB0C927897A5738532F264F3895EAAE413D8D55546A68D6F5D1A6338544722B57F0A8A8BF94F854E97E53E53FD7CE96F734F89795E657549C93E399E363BC8BDB988F44037DB041DD0C6C034426297C8E7082794D8ED7C78049856827437BA9E6940D6D5D854322D70A88A9D2A22A2FE5153B4FFE2E98C563C74E4C62DBF755360161970ADCFC5949033EDB3B861E35DE3D6904AD8D6070479418D26554A4D550219B1D0D5E55642B41C594412164BFF8D609CA5E0CE29BE76D1F74F02C8256CFF202818C2E3FB958D05E04B22C61F8468398C28840BADA2B86D6C35B21B99671E12A462AD9D68FF4934326728F7938E5631B0DE69ED65C54C3FA9640ABDF2C06BCB7B81E40E572B5B26D23C303475F2C8A8F2907008CB027B882FE1280C0B5EF63264EEBD24EC9F6BF71B18AC23416592E0B9763F5F21CE5620275CE3F635D10CAE454F1688F206F57768AD44EFBF8D7E7FF00715763476D674B710CF5D6B513E5D659574A12864D7CF8077C5990A40D7E5878D20441158BF70DED51BBE58BABFDE0DBA9B5FBB15AE99B7F9D63198457815E60D4588A44F8A3722B5EE9D56E515957F877F2D9D1E213BF8446BBAD262E7DFA70C2DCEC92C773703AF6D665366E79AD89086A30DB95AE63592AC41E2F1966289A8C74B17D3C8322794821BA44D587E82ED9D3F5C5CDCD1E429B11A9DDFD36FDEA8A97BA95AA70F01F96B8F836D66ADE5052AF7B56C0091518994855AF531D23BCED8A3A1D8F18B157F9BE3B17D31AEBCEFAC8D853BE554CE1364A0948B486D6190877B14D5B0E09A12B183AC877D76AAF462235CAE7ABBC11AD445572ABBB446A237B555554EBAED3E7AD3F8F464CEAFADA3CDAF52CA2D38E415B05CF713DA646691EC0B86D7B7A6B1C354F16A2F488BE3E28DEBA5BF13D38B7F729BF65058C27C0A87C86065BA188C35940717C5584391847A31EC4735E30A89AF6AF8B9153CBBB29706389757C73C282FB16478670461926C890A08A10B1A2FE616414A441046D55EDCAF56B5BE3F3D74889ECEFDEE0D2762C69F4FC6D164B57AAB0EBCED8DC4CD665BB6D5421615EB94CB575D2A967B8DD026D61C78500AC499E1F4CDD13C87AAA77BBDE5762A06E37756BEB93ACA163ED2E95243FECB196AC00C25B61ED854002658B4AD653F319BA454C9B5F24FB1AEA98532CEDA743ADADAE8C59961613E48A1C2851003298F2A54A90F18011C02090A73148D6046D5791CD67CEA23EE8738B6276F64A5051DD9F7673B92658359836D68FF8AAD67D9AFDAC83F5D5EA4AB117DD56B0A004C97683457B9958570D59AC4F1BE32725B9996706F393D24DB17B16093F595BB158C4E3FF0018E521699878BFC6D668D09600D18C1B8893C4C968FF0073E971AC7A5B053F55B32D9E61B367E73EA279C4ADA6DA73D9E2FC56C5AD061DDADDB131F164E7B2E2BC72930FC47EA07E04091A8123555A5F6D0C1B9B88EC8A2ABAEB775D83E118B6DC62741846174D128318C66AE35453D5416B9A08B0E2B7A6A2B9EE794E62BD487932A43CB2654931E4C8294E6291C60F83E27B718AD3615845055E338C50446C3AAA6A88CC8B0E2851CE7BDC8C6F6F2C890473CF2E59DE59532510B2651CD20A42BF2BD31868D1A34C61A3468D3186BCEB6ADAEB8AF95576D061D9D6D804912757D84504D8332319AA8504A8924658F202444E9E230DE37B555AE6AA2AA68D1A632016E7FA66711F3A9326F60E0B61B6B7C342CB15B6D6DD9F13F664A299CD347A650D8633188D555F1747A312A7E7FABB554DBC86AADC0E3D5ADA5060BC81DFE995B5A528230728DC06DCB5A260D0AD1A899510E3B59E6AABE2200DBD2F48889D68D1A6322B617BEDBEB985C7E9967BCBB83185EF144E3D55B46AF96E6B1C36B5CA7140554274F5EDED6B5DDA22F7DA76AE2761B807B59BF3450729DE5DC6DFADC773950A4A3C9B72965D11144D4731A8215202C80D4EFAFF008B691DC88888D737AD1A34C634DDA3E36EC6EC54640ED4EDAE33881D44D8F22DA140649C8668555AE70A764961F577D304E72F9284F62F0F689D0D111135BCF468D31868D1A34C61A3468D319FFFD9"; } pdf_icon = { Name="pdf_icon"; File="improve_pdf_icon.jpg"; Code="FFD8FFE000104A46494600010101004800480000FFDB00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFDB00430101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFC00011080064006403012200021101031101FFC4001F0000010402030101000000000000000000000108090A060702030B0405FFC40052100000050301040505090B070D000000000102030405060711000812213109134151710A326181B114182239767791B4B515161A23589296A1D3E1F02636374262D1D2293335475354576773A2B2D7F1FFC4001D010000060301000000000000000000000000050607080901020403FFC40046110002010302030501080E0B01000000000102030405110021061231071314415108152261719192D1D21623353742535474768193B1B3F0243252556263737594A1C1D3FFDA000C03010002110311003F00BEB3DAB6988C585AC84FC4317040298CDDDC9336EB14A70012985359621C00C51C9444388710C806BE51AEE8BC0FF2AA9EEEFF004C47E33CBFDE3BFBB3AA63F4B8BB8253A41EEA34956E9AC64A93B7AA1007E0EE18298880CF0C818048200190C8018703DF1F854A8A3A7C59A619FED0E04440338F83CF97F5BD23CF3A6AEEBDA57B9970ACA036932F8594C5DE789E4E7C11EFB1DCBE0107A64E31E79C6A7C767BEC43371E70670EF16C7C78B402FB6CA5B89A2F708D4785F111A3F74661738849CA495E6E44C9FC11E5715E945DB6EA6D8FF65F92BC76514B7F54D6EDAB1A469F4E2EA53399B8C18F9B923357EB0B2839B8A7E2B2490099254AECA9A66C0AA5317050ADB0F9481B7300FF00473B3E679E4697B83CF3CFFA47E3DF90F47AB0FD9AF63869B4F30B8533175B5236FA0EDAB26CFEA097AB5CBF6EC1166E0A738BA33868D9C9124D02A422A1D61214A02039EDD25D7E8F0ACA889EB48831AB289AD68ABDB5035A768CB8F4ACABA90A68649D2805EA9F188D0AE92FC59BAC219341522C5DE29044C43801157F14713DCC4770B7C357474532C6A91452C72800C8232E4B46A4273601380A09193BE9C8E12EC3FB03E07A9B870371B5F2D1C47C5343533CF3D6565BAE16E1143E112A52883C53CB4F2CC90C6F2AA24CD2B292020C1272F379489B7286376DDECF7BB80E3F7B170FBBE7207B71DD9F4700D71FC247DB93FE1E6CF3FA33707D3FF31FC3F5F7F05ACFA20AB34A42ACA4A80BD5682E1DD0A3A354949CB57073CF9AD5E82056E570549265231EDD305D44D428A29ACAA40AEF100A7DF390A2DD767EE8D7A8AFEDA7B8977E56EA5076769BB675D38B7F53AF72D6988E08E994528700F74ACDA3DC3641351E4E308F2A6B2A4399DEF25BBF0CA26F115FC671CCB0B5455832233021E0EEFED614C9CD2E4A294C8C8660467A634743823D96A4B54F798296C72D1D34D490480C5775AB0D5CE63A42B44C7C5BA543A30865588C6E46CDB674E1FF00091F6E3CE06DEECF3C7BE99B85FF00B23B718EEEFE7A3F09236E101E36F76772F8D377000007B7FD6307AFC03D3A888DA1ECCC5598B89334147DC0A42E7378B6710E53AC6867CB3DA71F0CA342BB3B668BAE8A0B28BB1153DCEE4BD5801562894078674D7DFB2477840C6C801B9F3CF11E7C739E02203910E39C0647058FC597D86478A4BA54ABC6FC8E0089D798150C39D01523208C8247CBA5BD37B3D76475D6FA4B9D0F04DAA7A3AFA74AAA591E4B840ED0C8A0A978A6912446DF74750CBB8201C8D58547CA48DB80C5E16F767330F689E98B84A0077018A172B039E5C871CF5606E8ACE93D75B70DB0752974E9DA7A89B8B03542F4D4A169955EA7493F72748AEA34ED529755EBF8C70F5A184C9B570FDEEF9C87023B31C4120F3C5835406466114FCC40E893380C9B780C222221C87001D9C7D1CF569EF27C69535556F36996AB947DCA8563491DA9F7478BC18350C629140101228050298A62894E4100326621CA0605570A7105DAB2EE90D555C9531344582B950BB0C9380A37DBA677EA3006A3EFB41F63DD9E70E7671557EE1FE1FA5B3DC692BA0884D4F2543F32BC851A32249594AE467217DE9F2E845CD5350AA140C021C447967B047BC0A39E1CB1AECD356B59736461DE36A1AB958C7700256D013ABE001EEE177538E9354F902BC200E1BB90102BC294431D714E02E953501428183800E701E038CF670E5D81A776375917994E7D467707D08C9C6ABC48E5383FAB19C63CBC86DAECD1A346B7D635414E9BCAA8F07D265779982BD589E87B68712E718EB2938AEEEF02008678088F6806A2D53B8EA60041C07C3CF0DFE421D9CBD391CF7766A4A3A7A6D1DFDA87A4BEE854942590BB55C5372541DB46CDA7A93B7B55D410CB394295669B9452938B8976CD459BAC514D74D258C648E6129CA5306350E2164F6AB2E33B2FED080390C62D157A381CF1001FB83D9C871C339CF0D477E26B1D655DF6E32A52CAE1E7660CB1310412BB83D0E33E59E87E3D5C97619DB070D583B2CE0AB556DEADF4F554964A3865865A9895D4A45180ACA5810463073BE73AB1F7448D4F454BD8BDB8C6E2C8C932A2DBD0B1A352BF886E2F2519C51D1740ED760D8077977042009924C07063E0398EB1D436DDB235BDC0D8C365AD9C236AC25ACB797760655CD4F5A81529EA9265D3D290820CB740ED104CE63A86038104C2702148621778615685BEBB69EC896C2EDD3C8596B93435BDBC516DE94AF64EBAB4751336CBB557AC49AB5612F3518D518E74B99412A7D4A82B2A7C010861000D348B797FEAEB5F5A52F5F52911208D4B484E33A8A156790CBBD6C9C8472A0BB732ED5544135D203972648E600300E0478EBA56B2A68A96D76F5492031298EB0CA9CAE51AA44C162CE0856007310373E78DF491A8B258B8B78AB8FB8C27AE5B90B94EB3F0D454557DEDB56BE3B2FB9DE22B121461E22179184799480AC5D9738D5B8A0D857329D3B730E69B6529F7069C917F255AC8248394E39BD346A0166874A41D10856FD4AF2CE2241349450DD6AA894C053754224D98CABBD9D9EEC8BD28956DCF8B9EAAAC39B6DD9F348B2A09FB76325289A9535A72C33A867AA9156C540679760F161126EACC0AE013C0980C35DDBA1D391B6FDCD829D85018EA28D54313C75473F405B88DA66AB9668A24641C24E6A46A80CA754B2073A625EB44C4218DD51C823BC0D06036F7BE94DECDB72F6538B8B745B4B75EAC655A55AC9C52A0EA75D4F47BDA6641BB96D3AA07BB1AA7D7D230BD6A24012AA52BA031B0E4F832F1D4D04932C0649BBC92AEAB3328004D508A8202BCC7316773923E15D2025E0CBFDDA9AD335CFC35B67B6D1F0AD8C416CA998CCF4163AF7AAA8B9355470284AC65702040AC50FE1675B37683AAECD4BDD1A9DED818AA9606D2B818D0A6622B17894854ACD40838F4A57DDEED04D345505678922E1B090A1BAD1544821BC5D3767324513644FE800CF01E39E21DF8E021C73C075A457AE6544E05FBDC9F111ED08E7050011C8088FE28718E61910EDC806BF3D6AAA60E60C40CF1447886F3073BD9EC00C25CB7407FAC03DBE9D23A4B7B4D23CAE238DD9CB18C32AA6E41C22824003A6001E9E5A9430F1841416EA3B742F70AB8E8A9E3A649EA9679EA6511A85579E664E79256EACF8CB31C9249D6D6A39E03B9CAAB77E1091C36011010FF00663E0201C7F770D597BA2F7A447647D82766D9E8DAFE52ABA8EEBDC2ACDDD47354C51F4F3891247C6C7A058E856EF6514EAD93776AB6EB0E62194F800251C871C55E2D29644CFEA878F59BD66578BB43A40F1BAA818E5021CA6C02852E7025C0E320190CE33ADDC2003CC3F8FEEF472D77D1DC64B3D6B4F4EB134AB0F202D9708582925704027D09DB7C1D25EFFC0F6EED438420B3DEE6ADA4A096BDEB278E9C88A59C46EDC91B1910B2A12431C0072300F5CDB6E73CA13D95EA231DA2F642F2B76FBC2509331A9E39C4A5C09152364DE75E532460298A2512A843001882070010943E8FFE968D9C36B8A991B3F4B54B38D2B86914B4A44455610CE61A5A463DB1934576E2E16CB59372D8CE5BFE39B1F2B9778FD58180C50F3E200E400018E1C3906007D01C318CE7525BD0FE73B7E908B06A22751313CE3A45414CE6209D233413992309440770C7297219C08800E38696162E31B9D5DD2928A5580C552FC9272A729DBA13CAC371BE361F1606A3876ABECC7C07C31C0B7EE22B4C9734ADB3D0354C225A81246EE1E31F6C5283DE95246DEA48F3D7A4114720022181ED0EE10E03FAF46B8A79DC0CF3E23E19111C7AB968D3BFAAF0D21D24CE2027214C21C8440071CBBFBF01F4766BACCDD100137565C871C01439F7F2FA75F4690DC87C07D9AC607A0F906B60CC3A330F8891A81FF2878A42747ACC894852E6E95B4CEE877CD0F6F3C7B3971D50C357D1F2887E2F499F9D1B65F6DEA85DA633B4338BF281B0F0911DB6C93BE76F8F1AB48F63801BB2DAAE6018FD91DC3761CC7021A4C0C9C9C68D2808E438887A788FD001C47C349A03D9C47C039E909CCC3CCFCA752D3BB4FEC27CD1F47C03E4D3CFD93366682BFD4EED2B5AD613D310947ECE962EA4BBB248D3C9312CBD4922C135C21A9F45DC9B478C9820F9568ECAE5D1DB2EB140899112018E272B7EBB346C250F56123A979B3D434A4BC0D3D5652D28E3A82C9FDC7A862D07611B3246A42374E6A11D9DE42490A49A48B9731E678822820E52449253D1488AF5B33DBA2C4314BAF9ABC5B185C9634AB44CC615646A6A7D462316C9BA6429D6556304E397054D14943891B287028F55908C9B9F413EB655B4BD0D26E0EE25205953C694EB51336519C9CBD370B39251228A8A2A728C23E93711063985332E76267028202B75099D544016D34532C40F79DEF79367ABAC830A77C0214AE474233F08D35B65BAD754F68BC5968ADB8B08A856DAF6EB5777198DE8A7A1476A8521438C5487058B6E7DE9C6B5F0FF001DF8F1E1EBF0E5A3468D12649EA73A749400A0018181D36F4FDFE7A50E7EA37B075251D10FF182580F942E3EA63A8D70E7EA37B075251D10FF00182D80F942E3EA63A3EE17FBBF6DFF00587FE69A5EDD7EF51C6DFECEFF00C45D7A4293CD0F5FB47468279A1EBF68E8D492D52DEB9690DC87C07D9A5D21B90F80FB343435043E510FC5E933F3A36CBEDBD50BB57D1F2887E2F499F9D1B65F6DEA85DA62BB44FBBE3F348BF72EAD2FD8DFEF5B55FA4771FE0D268D2944004047180CE73CB181D26830080080871C1B8731E5DD9E5CB3C43C748500938032752D09001248006E4938007A927A6A537A1A51AADBEDF56B2A381768C5C051D015ED457326E45C999C2C3DB86D46CD339C7932B0289A6922477211A8B332D940B28E182AB9934535152EB0E93EB51565A3DB8AFDC7558B0BF4EB1AD24AE4D2B300502B69BA4AB974BCD42ACC8C4DE4D4462CAA2D00A1883B865E156390013310477B43B526C71D1BF2F523837DCFBF5B7EBC5A9CA7DB9CC2949D37B31D26AF5B3D2A99000AE1046E24A2A0C0E06DD4E421DF423D6C703B254499ADBFA9A93E92DD9F68AD9CAB9A821A96DB1AC2C3AB1562AB3A8DDA2C23EF1D029362016DA4BCAAE7DD24F4526D9BA714BB938A8A11B3555139854993AAB15A589AD305999B96E0CBEE8A27364664002D3FBEFEABBA61D7A64E17A9D4687BE56D271F5C7B4C4A6593839241C1B70A88E20D3A4342C0B5F032E59E8A0AF32534A1464460CD928350AE218E780E38C8F211EE01EF1E401CF3C349ADBB5C586BC76E2B498B7F585B4AD216AB827066D290CE60249470DD4011022891D141549CB75800146CEDB9D46EE50315544E62180758DBFB6371E2DB15EC950558B168721942B9754DCC22898851C18FBE76601BA03C04470003CF49734352AAD9A798721E520A36032E32338C6DBED9DF6FD6FDC5C49619D21922BCDA9E39D51E12B5D4F9915B948283BDC90738000F80FA6B070E7EA37B075251D10FF182D80F942E3EA63A8D8129883BA72894D81C90C02060E60390100C080860C1CC3B43524FD10FF182D80F942E3EA63A35E19056FF006D0460F7C320F51D3FEFF9EBA6FF00B73656EC9B8D9948606CCC54839041742083E60839046BD2149E687AFDA3A3413CD0F5FB4746A48EA9735CB486E43E03ECD2E90DC87C07D9A1A1A821F2887E2F499F9D1B65F6DEA85DABE8F9443F17A4CFCE8DB2FB6F542EC67877F0D315DA27DDF1F9A45FB975697EC6FF007ADAAFD23B8FF06934A020510C8F211C8638F2CE31CC72003CBC74EDF62AD9ADCED377EE98A225943C3DB9846EEABEBC5552A7168CE94B534B94B2152CA2EFCF849A1DF362FDC98F5C4E0047CF925B8A682C24D9BB306CD54DBFB70F7697BC7152551D1E4AA4B6FECF5A6841582A1BE774CDF0518343DCE53394695885B74D51BA6E00B2A54CEC887477CCA69D9ED737049B27D8590D9C6257A79B6D2FB41B68A9CDA59CD1E83461196C6DD32440D47D84831625004106CD47AC9F445432EB94CB95E9D6193DD489A82D8228D6E35A7969A9F966EE88C199B6EEA3DF1CC246EA3A84E663D34E6F15F1F4B73AD9F827852369EF35F335AE4B821FB55B79957C7D560062C28A9DCE64CAA0A968E00C598E350ED837DB657BD77DE66E0D493F5FD7D43D35170F6F2D15ADB6D1CDA92A6290B73493723380855EA3A803AF4D47063BB937CB40C3BE6BEE87AAA082C76CDDA88E9D83DB5E98B60ED8BDB0DB2DD92A0E5634E99D9D515A3698BAB5422E1B9C0EDA411793AEA3E2DA3F4CE54D52AED6203AB549BC4E038060E220380DDDDDD10C07673E19E3D9C43B3808F0CE8D724B77AAEFE4A889A38E591D9FBC48D3BC4071855720955030001B0C0C6E33A50DBBB33B0535BA92D95CF5D74A2A5A78E214D555938A390E14BBBD246C90B33B82EECEAC599989241399D3B35D39F7D635198A7768BA7595D1A7EA0228D56AA28F334A1EE3536D15307180966E82CD1716A1F0DA20E88902661309D55844403EC96DAA6DA552E96AAED6748C6D0F6D1C3931DCB9B797BA8D5AB88D6E81877CF1CA4B3064FE366486013262896292218A3B990CEF6A08346BA53886BDA1EEEA1D6A4024833121F27046590A13BE3A923CB44557D897070AD359678A7B13C8A8B2C342B04B46EAB803FA256453C11B74CBC4A8C76C9CEE767DE6AADAD69746B6A9635CC73B61313CF9E22F626012A5A3644AA1800D20D69E48EA250E57A7299C8B04C4A548541C10823B80F6BA21FE305B01F285C7D4C751AC1C3C3BBB3239E3E3C75253D10DC7A412C00872FBE171FAD9987F8CFF00F36E1C72FC436E6C0F7D3EF8DC03B9C67F9CF5F4D6BDB3D2250F639C5B491B33253D88C29CC003CB198946C361D37036C9D7A4293CD0F5FB47468279A1EBF68E8D48ED532EB9690DC87C07D9A5D20F101F01D0D0D410F94401FE4F499F9D1B65F6DF7EA85DC43887010E43DC3ABEEF94231D252BD1F33C8C5C649CA2ADEE45BA7AE138C8F792076CCDA4C0A8E5E392B3416141A37206FAEE55EAD1489F09450A5C8EA8443C380E40738C081B3D9DC03DFA6538FA9669AF8B24714922F85886511987403A807D3E0EBAB38F641BCDAE8BB32A986AEE3434B2B710D7B84A9AA8A1628D15280C15DD4E090403D3208F2D3B5B39B6D5FF00B114B31A42DFD430E8C642AF3EF69273334D434ECA50EFEA83247A81FD1F2326D5C38A7DF49A8822B2CE580A6A82C911521CA6CE5B2541514E55933295154F2D213D3D38F969294979472A3B7EEDEB9505570BB870A9CEAACA2CA18C6398C61C00E0300001AFC4C87A7F34DF472FDDE9D190F4FE69BFBB49178AE5246914895524683088C92155000036C609036C9DF739F2D493A27E06B75755DCE867E1CA5AFAF3CF57571545224F50DB64C92070CC09F7C7A8E639EBBE97468C877FFDA6FA3CDFDDE9D1E191E1D8530FABCDE7FC675CDE02A7F27A8FD9B7D5D1CFD9258BAFBB569C1E87C753E3CBFCCF87468D2E3C7F34FF00E1D18F40FE69FF00C3A1E02A7F279FF66DF57F9C8F51A1F64961FEFBB47FCEA7FF00E9F08F9749A928E885F8C0F67FF940BFD4C751B1CB888184039E08711C76F002E7806A4A7A21086374825800290E2213EB9840086C94BEE5DCC9F20024003180326000111EEE3A3AE1CA4A88AF96E66866551302C59180503CC9C018E9BE9A8EDC2FD65A9ECB38CA286ED6D9A67B4CDC91435903BB9E78F654572C7AE761AF484279A1EBF68E8D04F343F8EDD1A911AA72D72D1CF468D0D0D61D5552D4DD6910EA99AB20A32A1A7E55B8B6908896689BD60E9138FC222CDD60326701FED14443B34DE07614D8EC44447670B4C223C47F9231A1C7C01200FA03468D6A638CF2B14424A2E495524FC6719D7AF889E20AB14D2C6A40256391D1492CB9242B0049F5D27BC4F63AFC9C2D37E89477ECF47BC4F63BFC9C2D37E89477ECF468D63BB8FF00169F357E8D6A6B6B7A78BAAC606DE225F41FE3D1EF14D8EF39F7B85A6EFF00E69477ECF40EC27B1D08E4DB37DA430F78D211823E1C52E5A34687771FE2D3E62FD1AC78DADE9E2EAB1E9E225FAFA4F7896C71F936DA3FD0F8BFD968F7896C71F936DA3FD0F8BFD968D1A1DDC7F8B4F9ABF46B1E36B3F2BA9FDBCBF5F486D84363810308ECDB68F8067F99F17CC07FE8FA7B3591D15B246CCF6FAA06556511642DC52F52C4A87523A6A16998E6320CD4380018E83845229D3360B8C9443809B39DE1D1A35ABC681090880864C10A01193BE0819DFCF5915756CCB1B54D432347273234D2143B8EAA5883D0751E43D34E512FF365F5FF00E43A3468D7AEB5D7FFD9"; } txt_icon = { Name="txt_icon"; File="improve_txt_icon.jpg"; Code="FFD8FFE000104A46494600010101004800480000FFDB00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFDB00430101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101FFC0001108006B005D03012200021101031101FFC4001E0000020301000301010000000000000000090A0007080603050B0204FFC40050100000060102020207130906070000000001020304050607000809111213141619213175D60A151A3437385455565758949596B4B6B7D1D417184151617793D5D725597678979836718183B2B3F0FFC40017010101010100000000000000000000000000010302FFC4003011000102040305080203010000000000000100110231617121415191A1B1B2C1031233527281D1F062E14282C2F1FFDA000C03010002110311003F00700DEAEF4ABBB4FAB8CFCBA0E5D88AA8B74DB312A6ABA55570A9124FAB4943A603D13A80750C638002451100300F2D0C7478D0D85CA08BA6986F24B968E9145D35748D6D63A2E5AB9488B36708A85112A88AE89D355250A631144CE5390C6218A23CFF001D73182B95E281840A6956FCCA023C8797439730F00F2FD1CFC1A06EDDA57DE3A7E336A32EBD3ABE3516C67D350B1C2926D7165445322294CB17043A6A9DD2C0B1535D3EB4C44BA042AADCAA6A3E2DF723D5B6D1704B921CE04012FC0E957F6AA3BB29C6C2561199A4263126418B60451344EF5FC09DA3422AB098124CEE17391221D5129813298E02710102808872D733DDE385F71961FE033FC568125ADB47B285C80DE2946E2D4F59A7A8A91A49C34921D921726C5398CA41A2DD991512149D24D54BB28A5128A8224327CA9FA1DB9C506E956BB348780B03AAA4EC6CF3784B5470CBD6E5568D749BA4D84E4582CDBCF08B7274C1378CFAF481C22264C4E50373D1AA777C2007CD1069E20BE00E3870AEA531DF779217DC6587F80CFF0015A9DDE485F71B610FFB0CFF0015FF005D60ECFB9A2C347DACECB2E31757C5EE2C399B13EE11ADE645E62FA311C3D9443294857EBB686CB474247AED2C54F629B7ED6DD1553B46E76C912418C93632EDD6B0F7FB331D42BD67189C7733445E65817018258923309D5DB1F1F575D519A5EECF92A2E710AF24531DAD9EBF586328E923B8866EC6F2FE3675A9CF28C4C9B1D785297D38318E64E5983A7E230D5EB915ABBBBC70BEE32C3FC067FAB9FB2B53BBC90BEE32C3FC067F8AD666CF72CAC56DC61376349C65468DCB36BA8E1DC73B86A176A548775BC34D142BAB2576F6DA88D21C14A92DB8260DA1552BE702D17AFC64DC8C5B53A32D648F751B5E5DB3B4E32D90E23CEC5A261C5ADD7ADCC677AD5898298A2989C0B9A939A5B25232A8920D22DACA3686AC2D32BB9AE8232E47EC9CB666B2EFDD9923F5B7DF87C7D7B3007FE47498A53419B3FF00675B73BBC70DEE32C3FC067F8AD4EEF1C2FB8CB0FF00019FE2B4B31A9A8D53BBE33FF8AF77F28B75294C7DF54D1D4AE39951B05A61ABF250EFE14661E11AB43C8837448E161E8F34D3145454C61200F4CE1C80797E9E420027DF18DE9A642A845D9DA880A320DD25CA201DEE4A90140F0730F0087804443C03DF0D7CCC6C8B2A9652C18292AA2626B93A03740E62F483B1DA8F237210E61CFBFDFF00D3AFA2E6CBCC26C054A11EF8F9D8CFBE3E1F4B27E1FDBA07738B80C06B2C78AE21889ED2381C910884E33C40437389F600B26E8724E2CC1F519985AFD86EB2F2448E95B1767043B63C2C0C84FAC0F4D1AD5EBD29566F16B22919068B882EA2407295313A84CA897050DD21504D256F3B7651518B868A78BA360CDF1DE78A3070AC6BECD578D631168C4CE0D1B1EDD270649B269AA7EB0E09940FD102B997BD7CFB60F1EDD3ECDAD9AD1F9EB789B6FDB1C9D761F39E4D8EA0C8DAD8BE92806AF622C924691631AE116AF574CF050D289A4541C38453315C1D13985401214C50110099BF40BB138FD5FE214BF927C0DB732F61E5E1D9DE76E912139D8257EF9B4B6617EF4C8B07C9C8A48A6336C24DBA4551DA491D6395BF5C7297A207001373AF7D0FC6E6FDF87077C66F5E4868E4F758F87D7C23207E6AE41F24B53BAC7C3EBE11B03F35720F925AABA4262CDC1D77E771ACE39A759B70D8065AAF890C038EE156AC1D26759219D03F70DD0EC5C5882AFD9483E0ECD978F9752418CD3C328EE59BBD70A28A9FBF9AE18DC4B2C72735353BB9EDBD4A4D59235AC358261E63C81565A6E1D91E3546D112B25F91607AFE2931878B4C635CAEAB255060D9B2C828D9304B44C98F156D81C93D671ECB71104BBC7EE9BB2688055EFC5159CBA5488209018F54290A2A2AA10806398A4011E6630000886C5CB393AB986B17DEF2DDB4920AD5B1E5565EE13A4896C477267898566A3E760C1AAAB364D77464123752928BA2539F914CA139F30225F047854F11B41DE557C9EE7F05764E6F4D14B2C0AB564974AEE937288204966ABE2655A90899C45D015822CC05F8F9E47034800390E364F833EFAE6315C261392DC2E08718BEB72CBCF41D54211F209C74D3A0295DCB212EDB19A3601917A991341FBC5659470F9BA493678A2EDD24D32EE2EEEFEC9FDAECCDF3222BCA9D4EEEFEC9FDAECCDF3222BCA9D110C6F43F1B9BF7E1C1DF19BD7921A9E87E3737EFC383BE337AF24344E7BBBFB27F6BB337CC88AF2A7570603E2E3B59DC6E5BA7E18C7ECB27A56FBB39916B0EA4F5523E3E24AA4643C8CE39178F11B03C55128B28C7054C4AD95E92C29904000C262912A16FFF0086F653D8DDA76A767C8B77A1DA90BEE549AAFC537A6A93EA2AD168B836D26E967E3350D1442A674CC9268763997318FD675804002899D2B65BEA034AF1633FA325A093E690BD2BC3EFF7FF0079FA951FA36DB2DF501A578B19FD152D4138AFD02C20F1FB6B41CA151F97BD7CFB60F1EDD3ECDAD9A1A1C6CE8EAE49DD7EC5E8256AABA4AEEE8D4E5CA991512F5767C954E8450145120E6913AB7A731D4E917A04298FD20E8F3025F97BD7CFB60F1EDD3ECDAD9A236BC7B074BA0E5CB166E1CB51033670BB64565DB98A603819055421944840E50380A662881800C1DF001D04E2BF40B58671FA87242831710CD8D6D1B17ECA73E5DB1FE00C7755B7D5E9CC1D40D922A20C94BC63A1B241333396EECEB9CE0B9DB2EB24A287E918E5554E90889847418382BE13C4F9E373F90AA798A855DC895B8DC0D63B13086B332ECE62D671AE42C651ADE511484E4E8BB4984B4935229CFBC8BD5CBCB91FBCC99C527D603B9AFF0003B0FADB5CD004F33FBEBC0C9FFE5B2D7F6A18875574AC5E2FDB4DC4580F27ED4AD184B1B41E3E86B5CCCAC1D91855592EDA3DDC9414FD5A4231EB86C075914DD0B4967E88AC404CCB22DC8557A6082625649CFE862C7381B2937CE0B0B7C3EBE3E9F4F252E5525113254D345AA13A72AB0A06972095875C2068D28BC0E5CD0013F2D5BAEE3D83F04C1F3266F411309D1076D9172091C79009D3EB88704CC20000262F211E41DFEF6A8BDD630C6D29B6BCE31D98A765AAF8ADEE32B6B6C81628168E1FCD42D4D58970499918B64D23661CBA7AD590AAAB74108A9055450A5291A2C23D012201FDA9799F8F74CF3E59CF5F86D4ED4BCCFC7BA679FB3FB6B3D7E1B594BF243C0EBE16DB8BFF004F2E1FD0FD4FC90F03AF85B6E2FF00D3CB87F43F452791CB4A57EB1A3EADED4BCCFC7BA679F2D67AFC37FCB5A4368F5DE0E0C770D8E5D6D9679CBBCE093C991A237524F2F38228E8D5B982CA80A3644090A7E8C08CA1801E980A02501487AE04F430BF243C0EBE16DB8BFF004F2E1FD0FD6B8D89E37E14507BACC4B29B79DC666ABB6626B21603532B16AA65962E0655CA9519F4A4937EFDF629AEB46E56F0A7927688AD30CC0EE10493299539CA82A57DB87CAFE7F3485E95E1F7FBFF00BCFD4A8FD1B6D96FA80D2BC58CFE8A96824F9A42F4AF0FBFDFFDE7EA547E8DB6CB7D40695E2C67F454B504E2BF40B083C7EDAD0728547E5EF5F3ED83C7B74FB36B66893E86C65EF5F3ED83C7B74FB36B66893E82715FA05AC338FD43921580F8A4FAC07735FE0761F5B6B9A009E67F7D78193FFCB65AFED4310E8FD714A100D80EE6B988073A4478073100E6236DAE000073FD223DE00F088E802799FE3006F07270088009B6DB6B02808F7CC3F94FC443C803F48800088F2F000088F780755749BE354DEE1EB78E2E182F2CD5B3058BB52C5B60A1D8E26FF670956506303547B1CBA13329E7C48B676C233B0D99D55BB35E3570DD0E8F58AA47280946E4D50FBA2A854EFF00B74CD749BDDCDB63AA65AB1B5AE0ECF7B7856C76B5184918A70DE427DC11E3868D4C8C6B739DC9C1C39412129040EA903998088027E639C117E1A6AFFB81C5BE446A7E639C117E1A6AFF00B81C5BE446A86EE6E70E9FEF3CA3FC4E93E596A77373874FF79E51FE2749F2CB45CB8AE59DAB6DFAE37CFE639C117E1A6AFF00B81C5BE446B486D1B6A1C2AB1E6E1F1C5C76FBBA252FF98215E4D294DA89B31D02CC130E5CD6A6594927E71C3D563E49FF006342B89279D16CF11325D8FD79C4C924A10C3DBB9B9C3A7FBCF28FF13A4F965AD59B22D8FECA714EE8B155FF0016EFCEA997AF95E7F3EBD7F1D4736AAA6F6C8B3BA9CEC73B4113B0B33F7602CA3DE3B933F52D551E83230180A989CE52A1BE97C854D3E9C792F3485E95E1F7FBFF00BCFD4A8FD1B6D96FA80D2BC58CFE8A96824F9A42F4AF0FBFDFFDE7EA547E8DB6CB7D40695E2C67F454B504E2BF40B183C7EDAD0728547E5EF5F3ED83C7B74FB36B6688FBA76D58B670F5F396ECD9B445472E9DBA5936ED9B374482A2CBB85D63112451493298EA2AA1CA44C851318C05011D0E0CBDEBE7DB078F6E9F66D6CD6CFCF38F5FE5AC2397B16C53F69152791B1A5DE8F1D28FC8B28C639EDA6B92308D5F3C23629DC1DB3559E91758A814CA9932181301388682715FA05AC338FD439214303797B25ACEEFEFD376290DFBC951687331B04C4D88E3E7E26669082F0ADD2219FF009D8ADFA3631670F5DA447EA89E280C4725229D33A842A819129FC1B31A63C9A4EC942E226FA956145155B2539532D76BD2E9B65C482BB72C8C4E4A68EC1058534C554415EAD412104E53744BCA90F43DF9E7DFE7127C9371FE5FA9E87BF3CFBFCE24F926E3FCBF557499471BDAA8346C7B46A54BE6DAB5D656A350AE56A4AE337718334CDA9FC1C4348D77629532D34F56348CD2ED94917A655E3A505CB8505472B1B9A86E5F3E278A73AE15CA586DDE60A4D75B64DA458296BCEB7B1D6E457884A7E3D660790498A930D5376A36058552A077281541281455200F305D6F43DF9E7DFE7127C9371FE5FA9E87BF3CFBFCE24F926E3FCBF44563770D3007C39A23E41A57F50753B869803E1CD11F20D2BFA83AAE7D0F7E79F7F9C49F24DC7F97EA7A1EFCF3EFF3893E49B8FF002FD1158DDC34C01F0E688F90695FD41D692DA1F09EC3BB77DC4E37CC75ADD8C6E449BA5BD9A72C29A844D59AAB34792AD4CC2AA9956617193764EC5424957C3D4B15C44AD4C5301082654989FD0F7E79F7F9C49F24DC7F97EB51ECBB83465DDB1EE6318672B265EC756385A1BD9C74F6160E3ACA8CA3E2CAD5A6E01223555FB445A10525E5525D4154E0028A4A14A027128688AB0F3485E95E1F7FBFFBCFD4A8FD1B6D96FA80D2BC58CFE8A96824F9A42F4AF0FBFDFF00DE7EA547E8DB6CB7D40695E2C67F454B504E2BF40B083C7EDAD072858A77BF9D68FB6BDC7609CCF918B347A7D3E6EC26962D79825272E6097A6CEC234068C9776C52587B3645BF5BD37490268F58A733090086F5BDDDDD907B0B35FCC288F2C359278EDFFC3F59F1CB7FFC34B19A09C57E816B0CE3F50E485394F7777641EC2CD7F30A1FCB0D4EEEEEC83D859AFE6143F961A5F7DB563CC673DB21DE9E55B3E39AB59AFF0086A4F0B1E836299F3F0CB462778B3BD8A9F6AE59B09A611D22D9668C532362BC68A2AD1455751154A6394496E64CD8751EED8EF0C669C6AF9B62B6368DA3DD772998A90B2CFACC15E6F8F5CD7620EF31FC64A4A9661C31BE4BD89AA7111B376014235245DBB34B1D1441A1AAAEDBF72359DDDDD907B0B35FCC287F2C353BBBBB20F6166BF9850FE58697BAD1C3DA62874F92CB175C9F1F19865AD2F6F17B67718CAA3C999F5A13716F4AD201492A7253481A24D564D0957365FEDC7C2A958B66F0859273288910E8AA3C39995DDDE1125773BB09189CEB45DC15DEAD3C963B92459A0DF01CAA91EE9259ABEB33390334B6A08AAFA2DE2AD9A3B8E4CCDD07F146554545023DF67CF09D11EFEEEEEC83D859AFE6143F961A9DDDDD907B0B35FCC287F2C34016B3C3C98D8E9F49B1866D2C7C95EB6A971DD54643AF8DDC2ED5957682B18B67AD48CB37B91942C91514965619E358A7294898809BB6D17D2298DE397E1CD28C23E52E6C72B473EC6519B71C65B8D52C6F6B6C602C230B94AC0F2B5095B5ABB2F7542119C8B37D1CFDCC948B8BA1639262810C899678E11667238FB5923FDDDDDD907B0B35FCC287F2C353BBBBB20F6166BF9850FE58680FD3786F40DC56C709476E4EB928D32A676BEE05A7CF54A8EA5A6B3253D50AC92D91D634E5D3BA4774AB73918B20938395B1A421E40CAB4065249A2773AA77336DA6878AB6998672C127EC12393721655CCD4E9D6A78E689D6D9B6C5D3C95514611AA96585C112078D1DCA2526B3170E24FB38AD146B12932299D155A338C6710BC05BD198D94D630F217C464A9598EDB609935BEBACA11B1983DAD46C6220CD56B372A65D7EC93809D33A6894A9809814110E8E9A7B65DEA034AF1633FA327AF9CDDAFD56B01FF8B65BFF004C3EBE8C9B2DF501A578B19FD192D4138AFD02C20F1FB6B41CA10FBE2D9B5FC839E6A2CBB466847922C1EB7749A2AF5C54CC52097AC2F4D24D537332606028F57D013894A262FE95CC370F6DD294C62FE4FD730018400C0A98004007973EFA5CF90F87C003FB34FCCE1A36745E8B941358A03CC0142818007F673FFEF0FEB1D7A3ED6ABFED3C7FC593FBB4B618ED5AB1724167C64282760924699B6EDFD63DA55A71CD421978AA45E1D473CB8D6CD135F928DB3B887581C449A71396837A691245B8015E39272751164B19455B11251550C7FD7E6EBC42C6DAF6F2BAB6D7D6591AAB9A2483C92921936AFA88ED8F9D8BD1DDC44836710CBD30D1E04684AAA8C0D028A29225463D31452123B676B35FF69E3FE2C9FDDA9DACD7FDA78FF8B27F769EFC29FBDB6463E635C0632C6543B6892D63F1071268D90B3C8A0F2C4BF6E55984A6D9A264D38999AC4B552B0D936958AF2F5196887B564E26B2D922235C64DE211420C806F3B08D44E7137B2AD635E26D508A8486AE4AD96359564B6D46BA62A50EE5FC1B4BE282B5C63E2655DC338948F8BB0AE3D7C9C535768C72EB948B7629554C862B9C76B35FF69E3FE2C9FDDA9DACD7FDA78FF8B27F768C75E147DBD6C8C7CC760A52FB6892F19E26E24F1EDA199325659B35AF63B9AC4B0A8271B5DEAA3B1AD8C5319EA5B628C09BA3032E29945FB33748AE44CA8A823D72BD3F3B1C61C4C235EC63D66FA7883138E91C46D58A8CE05C42AD8C5B19451AD164E01D41AD09335B66AACB2CCE32623DFA0D175D659B95355554E673CED66BFED3C7FC593FBB53B59AFF00B4F1FF00164FEED18EBC29FBDA8C7CDB85294FAC92F21713F129AE25584611CCDC7A74BB84A5FEA60832811357AE7331EB44C958A2CEA4198ED641C45AC68D0390DD041826D5A374D2419B4221C958B6DDBFFB651898D6C4CA5E528C95A27AE6857174E38ACDA58ECF25E7C581EB13A3189396284B4B72937B18CDC2114ABF226EFB0817492391DC3B59AFFB4F1FF164FEED4ED66BFED3C7FC593FBB463AF0A7EF6D93BA461DE3B21A3E59E3B698A1ED6F85EEE26DD91B1E4A4F549CC535A9CCAD22550A273F59D942D08A14C20528140A56C5F0F3E626E7C800074ECDB6FA64851713D66B9200093960C1AA2729804079910214798180043C1E0F0F7FBFC879805C4857E0D150A74A298A670E7C8C46E428F83F5806BDE10854CA042140A528720294390007EC00D06AE7100EED32AA8200228A2773133C8480192FFFD9"; } Str_lytebox_js = [==[ //**************************************************************************************************/ // Lytebox v5.5 // // Author: Markus F. Hay // Website: http://lytebox.com (http://dolem.com/lytebox) // Date: January 26, 2012 // License: Creative Commons Attribution 3.0 License (http://creativecommons.org/licenses/by/3.0/) //**************************************************************************************************/ function Lytebox(bInitialize, aHttp) { /*** Language Configuration ***/ // English - configure for your language or customize as needed. // Note that these values will be seen by users when mousing over buttons. this.label = new Object(); this.label['close'] = 'Close (Esc)'; this.label['prev'] = 'Previous (\u2190)'; // Previous (left arrow) this.label['next'] = 'Next (\u2192)'; // Next (right arrow) this.label['play'] = 'Play (spacebar)'; this.label['pause'] = 'Pause (spacebar)'; this.label['print'] = 'Print'; this.label['image'] = 'Image %1 of %2'; // %1 = active image, %2 = total images this.label['page'] = 'Page %1 of %2'; // %1 = active page, %2 = total pages /*** Configure Lytebox ***/ this.theme = (typeof lyteboxTheme !== 'undefined') && /^(black|grey|red|green|blue|gold|orange)$/i.test(lyteboxTheme) ? lyteboxTheme : 'grey'; // themes: black (default), grey, red, green, blue, gold, orange this.roundedBorder = true; // controls whether or not the viewer uses rounded corners (false = square corners) this.innerBorder = true; // controls whether to show the inner border around image/html content this.outerBorder = true; // controls whether to show the outer grey (or theme) border this.resizeSpeed = 8; // controls the speed of the image resizing (1=slowest and 10=fastest) this.maxOpacity = 20; // higher opacity = darker overlay, lower opacity = lighter overlay this.borderSize = 12; // if you adjust the padding in the CSS, you will need to update this variable -- otherwise, leave this alone... this.appendQS = false; // if true, will append request_from=lytebox to the QS. Use this with caution as it may cause pages to not render this.fixedPosition = this.isMobile() ? false : true; // if true, viewer will remain in a fixed position, otherwise page scrolling will be allowed this.inherit = true; // controls whether or not data-lyte-options are inherited from the first link in a grouped set this.__hideObjects = true; // controls whether or not objects (such as Flash, Java, etc.) should be hidden when the viewer opens this.__autoResize = true; // controls whether or not images should be resized if larger than the browser window dimensions this.__doAnimations = true; // controls ALL animation effects (i.e. overlay fade in/out, image resize transition, etc.) this.__animateOverlay = false; // controls ONLY the overlay (background darkening) effects, and whether or not to fade in/out this.__forceCloseClick = false; // if true, users are forced to click on the "Close" button when viewing content this.__refreshPage = false; // force page refresh after closing Lytebox this.__showPrint = false; // true to show print button, false to hide this.__navType = 3; // 1 = "Prev/Next" buttons on top left and left // 2 = "Prev/Next" buttons in navigation bar // 3 = navType_1 + navType_2 (show both) // These two options control the position of the title/counter and navigation buttons. Note that for mobile devices, // the title is displayed on top and the navigation on the bottom. This is due to the view area being limited. // You can customize this for non-mobile devices by changing the 2nd condition (: false) to true (: true) this.__navTop = this.isMobile() ? false : false; // true to show the buttons on the top right, false to show them on bottom right (default) this.__titleTop = this.isMobile() ? true : false; // true to show the title on the top left, false to show it on the bottom left (default) /*** Configure HTML Content / Media Viewer Options ***/ this.__width = '80%'; // default width of content viewer this.__height = '80%'; // default height of content viewer this.__scrolling = 'auto'; // controls whether or not scrolling is allowed in the content viewer -- options are auto|yes|no this.__loopPlayback = false; // controls whether or not embedded media is looped (swf, avi, mov, etc.) this.__autoPlay = true; // controls whether or not to autoplay embedded media this.__autoEmbed = true; // controls whether or not to automatically embed media in an object tag /*** Configure Slideshow Options ***/ this.__slideInterval = 4000; // change value (milliseconds) to increase/decrease the time between "slides" this.__showNavigation = false; // true to display Next/Prev buttons/text during slideshow, false to hide this.__showClose = true; // true to display the Close button, false to hide this.__showDetails = true; // true to display image details (caption, count), false to hide this.__showPlayPause = true; // true to display pause/play buttons next to close button, false to hide this.__autoEnd = true; // true to automatically close Lytebox after the last image is reached, false to keep open this.__pauseOnNextClick = false; // true to pause the slideshow when the "Next" button is clicked this.__pauseOnPrevClick = true; // true to pause the slideshow when the "Prev" button is clicked this.__loopSlideshow = false; // true to continuously loop through slides, false otherwise /*** Configure Event Callbacks ***/ this.__beforeStart = ''; // function to call before the viewer starts this.__afterStart = ''; // function to call after the viewer starts this.__beforeEnd = ''; // function to call before the viewer ends (after close click) this.__afterEnd = ''; // function to call after the viewer ends /*** Configure Lytetip (tooltips) Options ***/ this.__changeTipCursor = true; // true to change the cursor to 'help', false to leave default (inhereted) this.__tipDecoration = 'dotted'; // controls the text-decoration (underline) of the tip link (dotted|solid|none) this.__tipStyle = 'classic'; // sets the default tip style if none is specified via data-lyte-options. Possible values are classic, info, help, warning, error this.__tipRelative = true; // if true, tips will be positioned relative to the element. if false, tips will be absolutely positioned on the page. // if you are having issues with tooltips not being properly positioned, then set this to false this.navTypeHash = new Object(); this.navTypeHash['Hover_by_type_1'] = true; this.navTypeHash['Display_by_type_1'] = false; this.navTypeHash['Hover_by_type_2'] = false; this.navTypeHash['Display_by_type_2'] = true; this.navTypeHash['Hover_by_type_3'] = true; this.navTypeHash['Display_by_type_3'] = true; this.resizeWTimerArray = new Array(); this.resizeWTimerCount = 0; this.resizeHTimerArray = new Array(); this.resizeHTimerCount = 0; this.changeContentTimerArray= new Array(); this.changeContentTimerCount= 0; this.overlayTimerArray = new Array(); this.overlayTimerCount = 0; this.imageTimerArray = new Array(); this.imageTimerCount = 0; this.timerIDArray = new Array(); this.timerIDCount = 0; this.slideshowIDArray = new Array(); this.slideshowIDCount = 0; this.imageArray = new Array(); this.slideArray = new Array(); this.frameArray = new Array(); this.contentNum = null; this.aPageSize = new Array(); this.overlayLoaded = false; this.checkFrame(); this.isSlideshow = false; this.isLyteframe = false; this.tipSet = false; this.ieVersion = this.ffVersion = this.chromeVersion = this.operaVersion = this.safariVersion = -1; this.ie = this.ff = this.chrome = this.opera = this.safari = false; this.setBrowserInfo(); this.classAttribute = (((this.ie && this.doc.compatMode == 'BackCompat') || (this.ie && this.ieVersion <= 7)) ? 'className' : 'class'); this.classAttribute = (this.ie && (document.documentMode == 8 || document.documentMode == 9)) ? 'class' : this.classAttribute; this.isReady = false; if (bInitialize) { this.http = aHttp; this.bodyOnscroll = document.body.onscroll; if(this.resizeSpeed > 10) { this.resizeSpeed = 10; } if(this.resizeSpeed < 1) { this.resizeSpeed = 1; } var ie8Duration = 2; var isWinXP = (navigator.userAgent.match(/windows nt 5.1/i) || navigator.userAgent.match(/windows nt 5.2/i) ? true : false); this.resizeDuration = (11 - this.resizeSpeed) * (this.ie ? (this.ieVersion >= 9 ? 6 : (this.ieVersion == 8 ? (this.doc.compatMode == 'BackCompat' ? ie8Duration : ie8Duration - 1) : 3)) : 7); this.resizeDuration = this.ff ? (11 - this.resizeSpeed) * (this.ffVersion < 6 ? 3 : (isWinXP ? 6 : 12)) : this.resizeDuration; this.resizeDuration = this.chrome ? (11 - this.resizeSpeed) * 5 : this.resizeDuration; this.resizeDuration = this.safari ? (11 - this.resizeSpeed) * 20 : this.resizeDuration; this.resizeDuration = this.isMobile() ? (11 - this.resizeSpeed) * 2 : this.resizeDuration; if (window.name != 'lbIframe') { this.initialize(); } } else { this.http = new Array(); if (typeof $ == 'undefined') { $ = function (id) { if ($.cache[id] === undefined) { $.cache[id] = document.getElementById(id) || false; } return $.cache[id]; }; $.cache = {}; } } } Lytebox.prototype.setBrowserInfo = function() { var ua = navigator.userAgent.toLowerCase(); this.chrome = ua.indexOf('chrome') > -1; this.ff = ua.indexOf('firefox') > -1; this.safari = !this.chrome && ua.indexOf('safari') > -1; this.opera = ua.indexOf('opera') > -1; this.ie = /*@cc_on!@*/false; if (this.chrome) { var re = new RegExp("chrome/([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) { this.chromeVersion = parseInt( RegExp.$1 ); } } if (this.ff) { var re = new RegExp("firefox/([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) { this.ffVersion = parseInt( RegExp.$1 ); } } if (this.ie) { var re = new RegExp("msie ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) { this.ieVersion = parseInt( RegExp.$1 ); } } if (this.opera) { var re = new RegExp("opera/([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) { this.operaVersion = parseInt( RegExp.$1 ); } } if (this.safari) { var re = new RegExp("version/([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) { this.safariVersion = parseInt( RegExp.$1 ); } } }; Lytebox.prototype.initialize = function() { this.updateLyteboxItems(); var oBody = this.doc.getElementsByTagName('body').item(0); if (this.doc.$('lbOverlay')) { oBody.removeChild(this.doc.$('lbOverlay')); } if (this.doc.$('lbMain')) { oBody.removeChild(this.doc.$('lbMain')); } if (this.doc.$('lbLauncher')) { oBody.removeChild(this.doc.$('lbLauncher')); } var oLauncher = this.doc.createElement('a'); oLauncher.setAttribute('id','lbLauncher'); oLauncher.setAttribute(this.classAttribute, 'lytebox'); oLauncher.style.display = 'none'; oBody.appendChild(oLauncher); var oOverlay = this.doc.createElement('div'); oOverlay.setAttribute('id','lbOverlay'); oOverlay.setAttribute(this.classAttribute, this.theme); if (this.ie && (this.ieVersion <= 6 || (this.ieVersion <= 9 && this.doc.compatMode == 'BackCompat'))) { oOverlay.style.position = 'absolute'; } oOverlay.style.display = 'none'; oBody.appendChild(oOverlay); var oLytebox = this.doc.createElement('div'); oLytebox.setAttribute('id','lbMain'); oLytebox.style.display = 'none'; oBody.appendChild(oLytebox); var oOuterContainer = this.doc.createElement('div'); oOuterContainer.setAttribute('id','lbOuterContainer'); oOuterContainer.setAttribute(this.classAttribute, this.theme); if (this.roundedBorder) { oOuterContainer.style.MozBorderRadius = '8px'; oOuterContainer.style.borderRadius = '8px'; } oLytebox.appendChild(oOuterContainer); var oTopContainer = this.doc.createElement('div'); oTopContainer.setAttribute('id','lbTopContainer'); oTopContainer.setAttribute(this.classAttribute, this.theme); if (this.roundedBorder) { oTopContainer.style.MozBorderRadius = '8px'; oTopContainer.style.borderRadius = '8px'; } oOuterContainer.appendChild(oTopContainer); var oTopData = this.doc.createElement('div'); oTopData.setAttribute('id','lbTopData'); oTopData.setAttribute(this.classAttribute, this.theme); oTopContainer.appendChild(oTopData); var oTitleTop = this.doc.createElement('span'); oTitleTop.setAttribute('id','lbTitleTop'); oTopData.appendChild(oTitleTop); var oNumTop = this.doc.createElement('span'); oNumTop.setAttribute('id','lbNumTop'); oTopData.appendChild(oNumTop); var oTopNav = this.doc.createElement('div'); oTopNav.setAttribute('id','lbTopNav'); oTopContainer.appendChild(oTopNav); var oCloseTop = this.doc.createElement('a'); oCloseTop.setAttribute('id','lbCloseTop'); oCloseTop.setAttribute('title', this.label['close']); oCloseTop.setAttribute(this.classAttribute, this.theme); oCloseTop.setAttribute('href','javascript:void(0)'); oTopNav.appendChild(oCloseTop); var oPrintTop = this.doc.createElement('a'); oPrintTop.setAttribute('id','lbPrintTop') oPrintTop.setAttribute('title', this.label['print']); oPrintTop.setAttribute(this.classAttribute, this.theme); oPrintTop.setAttribute('href','javascript:void(0)'); oTopNav.appendChild(oPrintTop); var oNextTop = this.doc.createElement('a'); oNextTop.setAttribute('id','lbNextTop'); oNextTop.setAttribute('title', this.label['next']); oNextTop.setAttribute(this.classAttribute, this.theme); oNextTop.setAttribute('href','javascript:void(0)'); oTopNav.appendChild(oNextTop); var oPauseTop = this.doc.createElement('a'); oPauseTop.setAttribute('id','lbPauseTop'); oPauseTop.setAttribute('title', this.label['pause']); oPauseTop.setAttribute(this.classAttribute, this.theme); oPauseTop.setAttribute('href','javascript:void(0)'); oPauseTop.style.display = 'none'; oTopNav.appendChild(oPauseTop); var oPlayTop = this.doc.createElement('a'); oPlayTop.setAttribute('id','lbPlayTop'); oPlayTop.setAttribute('title', this.label['play']); oPlayTop.setAttribute(this.classAttribute, this.theme); oPlayTop.setAttribute('href','javascript:void(0)'); oPlayTop.style.display = 'none'; oTopNav.appendChild(oPlayTop); var oPrevTop = this.doc.createElement('a'); oPrevTop.setAttribute('id','lbPrevTop'); oPrevTop.setAttribute('title', this.label['prev']); oPrevTop.setAttribute(this.classAttribute, this.theme); oPrevTop.setAttribute('href','javascript:void(0)'); oTopNav.appendChild(oPrevTop); var oIframeContainer = this.doc.createElement('div'); oIframeContainer.setAttribute('id','lbIframeContainer'); oIframeContainer.style.display = 'none'; oOuterContainer.appendChild(oIframeContainer); var oIframe = this.doc.createElement('iframe'); oIframe.setAttribute('id','lbIframe'); oIframe.setAttribute('name','lbIframe') oIframe.setAttribute('frameBorder','0'); if (this.innerBorder) { oIframe.setAttribute(this.classAttribute, this.theme); } oIframe.style.display = 'none'; oIframeContainer.appendChild(oIframe); var oImageContainer = this.doc.createElement('div'); oImageContainer.setAttribute('id','lbImageContainer'); oOuterContainer.appendChild(oImageContainer); var oLyteboxImage = this.doc.createElement('img'); oLyteboxImage.setAttribute('id','lbImage'); if (this.innerBorder) { oLyteboxImage.setAttribute(this.classAttribute, this.theme); } oImageContainer.appendChild(oLyteboxImage); var oLoading = this.doc.createElement('div'); oLoading.setAttribute('id','lbLoading'); oLoading.setAttribute(this.classAttribute, this.theme); oOuterContainer.appendChild(oLoading); var oBottomContainer = this.doc.createElement('div'); oBottomContainer.setAttribute('id','lbBottomContainer'); oBottomContainer.setAttribute(this.classAttribute, this.theme); if (this.roundedBorder) { oBottomContainer.style.MozBorderRadius = '8px'; oBottomContainer.style.borderRadius = '8px'; } oOuterContainer.appendChild(oBottomContainer); var oDetailsBottom = this.doc.createElement('div'); oDetailsBottom.setAttribute('id','lbBottomData'); oDetailsBottom.setAttribute(this.classAttribute, this.theme); oBottomContainer.appendChild(oDetailsBottom); var oTitleBottom = this.doc.createElement('span'); oTitleBottom.setAttribute('id','lbTitleBottom'); oDetailsBottom.appendChild(oTitleBottom); var oNumBottom = this.doc.createElement('span'); oNumBottom.setAttribute('id','lbNumBottom'); oDetailsBottom.appendChild(oNumBottom); var oDescBottom = this.doc.createElement('span'); oDescBottom.setAttribute('id','lbDescBottom'); oDetailsBottom.appendChild(oDescBottom); var oHoverNav = this.doc.createElement('div'); oHoverNav.setAttribute('id','lbHoverNav'); oImageContainer.appendChild(oHoverNav); var oBottomNav = this.doc.createElement('div'); oBottomNav.setAttribute('id','lbBottomNav'); oBottomContainer.appendChild(oBottomNav); var oPrevHov = this.doc.createElement('a'); oPrevHov.setAttribute('id','lbPrevHov'); oPrevHov.setAttribute('title', this.label['prev']); oPrevHov.setAttribute(this.classAttribute, this.theme); oPrevHov.setAttribute('href','javascript:void(0)'); oHoverNav.appendChild(oPrevHov); var oNextHov = this.doc.createElement('a'); oNextHov.setAttribute('id','lbNextHov'); oNextHov.setAttribute('title', this.label['next']); oNextHov.setAttribute(this.classAttribute, this.theme); oNextHov.setAttribute('href','javascript:void(0)'); oHoverNav.appendChild(oNextHov); var oClose = this.doc.createElement('a'); oClose.setAttribute('id','lbClose'); oClose.setAttribute('title', this.label['close']); oClose.setAttribute(this.classAttribute, this.theme); oClose.setAttribute('href','javascript:void(0)'); oBottomNav.appendChild(oClose); var oPrint = this.doc.createElement('a'); oPrint.setAttribute('id','lbPrint'); oPrint.setAttribute('title', this.label['print']); oPrint.setAttribute(this.classAttribute, this.theme); oPrint.setAttribute('href','javascript:void(0)'); oPrint.style.display = 'none'; oBottomNav.appendChild(oPrint); var oNext = this.doc.createElement('a'); oNext.setAttribute('id','lbNext'); oNext.setAttribute('title', this.label['next']); oNext.setAttribute(this.classAttribute, this.theme); oNext.setAttribute('href','javascript:void(0)'); oBottomNav.appendChild(oNext); var oPause = this.doc.createElement('a'); oPause.setAttribute('id','lbPause'); oPause.setAttribute('title', this.label['pause']); oPause.setAttribute(this.classAttribute, this.theme); oPause.setAttribute('href','javascript:void(0)'); oPause.style.display = 'none'; oBottomNav.appendChild(oPause); var oPlay = this.doc.createElement('a'); oPlay.setAttribute('id','lbPlay'); oPlay.setAttribute('title', this.label['play']); oPlay.setAttribute(this.classAttribute, this.theme); oPlay.setAttribute('href','javascript:void(0)'); oPlay.style.display = 'none'; oBottomNav.appendChild(oPlay); var oPrev = this.doc.createElement('a'); oPrev.setAttribute('id','lbPrev'); oPrev.setAttribute('title', this.label['prev']); oPrev.setAttribute(this.classAttribute, this.theme); oPrev.setAttribute('href','javascript:void(0)'); oBottomNav.appendChild(oPrev); var iframes = (this.isFrame && window.parent.frames[window.name].document) ? window.parent.frames[window.name].document.getElementsByTagName('iframe') : document.getElementsByTagName('iframe'); for (var i = 0; i < iframes.length; i++) { if (/youtube/i.test(iframes[i].src)) { iframes[i].src += ((/\?/.test(iframes[i].src)) ? '&' : '?') + 'wmode=transparent'; } } this.isReady = true; }; Lytebox.prototype.updateLyteboxItems = function() { var anchors = (this.isFrame && window.parent.frames[window.name].document) ? window.parent.frames[window.name].document.getElementsByTagName('a') : document.getElementsByTagName('a'); anchors = (this.isFrame) ? anchors : document.getElementsByTagName('a'); var areas = (this.isFrame && window.parent.frames[window.name].document) ? window.parent.frames[window.name].document.getElementsByTagName('area') : document.getElementsByTagName('area'); var lyteLinks = this.combine(anchors, areas); var myLink = relAttribute = revAttribute = classAttribute = dataOptions = dataTip = tipDecoration = tipStyle = tipImage = tipHtml = aSetting = sName = sValue = sExt = aUrl = null; var bImage = bRelative = false; for (var i = 0; i < lyteLinks.length; i++) { myLink = lyteLinks[i]; relAttribute = String(myLink.getAttribute('rel')); classAttribute = String(myLink.getAttribute(this.classAttribute)); if (myLink.getAttribute('href')) { sType = classAttribute.match(/lytebox|lyteshow|lyteframe/i); sType = this.isEmpty(sType) ? relAttribute.match(/lytebox|lyteshow|lyteframe/i) : sType; dataOptions = String(myLink.getAttribute('data-lyte-options')); dataOptions = this.isEmpty(dataOptions) ? String(myLink.getAttribute('rev')) : dataOptions; aUrl = myLink.getAttribute('href').split('?'); sExt = aUrl[0].split('.').pop().toLowerCase(); bImage = (sExt == 'png' || sExt == 'jpg' || sExt == 'jpeg' || sExt == 'gif' || sExt == 'bmp'); if (sType && sType.length >= 1) { if (this.isMobile() && /youtube/i.test(myLink.getAttribute('href'))) { myLink.target = '_blank'; } else if (bImage && (dataOptions.match(/slide:true/i) || sType[0].toLowerCase() == 'lyteshow')) { myLink.onclick = function () { $lb.start(this, true, false); return false; } } else if (bImage) { myLink.onclick = function () { $lb.start(this, false, false); return false; } } else { myLink.onclick = function () { $lb.start(this, false, true); return false; } } } dataTip = String(myLink.getAttribute('data-tip')); dataTip = this.isEmpty(dataTip) ? myLink.getAttribute('title') : dataTip; if (classAttribute.toLowerCase().match('lytetip') && !this.isEmpty(dataTip) && !this.tipsSet) { if (this.__changeTipCursor) { myLink.style.cursor = 'help'; } tipDecoration = this.__tipDecoration; tipStyle = this.__tipStyle; bRelative = this.__tipRelative; if (!this.isEmpty(dataOptions)) { aOptions = dataOptions.split(' '); for (var j = 0; j < aOptions.length; j++) { aSetting = aOptions[j].split(':'); sName = (aSetting.length > 1 ? this.trim(aSetting[0]).toLowerCase() : ''); sValue = (aSetting.length > 1 ? this.trim(aSetting[1]) : ''); switch(sName) { case 'tipstyle': tipStyle = (/classic|info|help|warning|error/.test(sValue) ? sValue : tipStyle); break; case 'changetipcursor': myLink.style.cursor = (/true|false/.test(sValue) ? (sValue == 'true' ? 'help' : '') : myLink.style.cursor); break; case 'tiprelative': bRelative = (/true|false/.test(sValue) ? (sValue == 'true') : bRelative); break; case 'tipdecoration': tipDecoration = (/dotted|solid|none/.test(sValue) ? sValue : tipDecoration); break; } } } if (tipDecoration != 'dotted') { myLink.style.borderBottom = (tipDecoration == 'solid' ? '1px solid' : 'none'); } switch(tipStyle) { case 'info': tipStyle = 'lbCustom lbInfo'; tipImage = 'lbTipImg lbInfoImg'; break; case 'help': tipStyle = 'lbCustom lbHelp'; tipImage = 'lbTipImg lbHelpImg'; break; case 'warning': tipStyle = 'lbCustom lbWarning'; tipImage = 'lbTipImg lbWarningImg'; break; case 'error': tipStyle = 'lbCustom lbError'; tipImage = 'lbTipImg lbErrorImg'; break; case 'classic': tipStyle = 'lbClassic'; tipImage = ''; break; default: tipStyle = 'lbClassic'; tipImage = ''; } if ((this.ie && this.ieVersion <= 7) || (this.ieVersion == 8 && this.doc.compatMode == 'BackCompat')) { tipImage = ''; if (tipStyle != 'lbClassic' && !this.isEmpty(tipStyle)) { tipStyle += ' lbIEFix'; } } var aLinkPos = this.findPos(myLink); if ((this.ie && (this.ieVersion <= 6 || this.doc.compatMode == 'BackCompat')) || bRelative) { myLink.style.position = 'relative'; } tipHtml = myLink.innerHTML; myLink.innerHTML = ''; if ((this.ie && this.ieVersion <= 6 && this.doc.compatMode != 'BackCompat') || bRelative) { myLink.innerHTML = tipHtml + '' + (tipImage ? '
    ' : '') + dataTip + '
    '; } else { myLink.innerHTML = tipHtml + '' + (tipImage ? '
    ' : '') + dataTip + '
    '; } if (classAttribute.match(/lytebox|lyteshow|lyteframe/i) == null) { myLink.setAttribute('title',''); } } } } this.tipsSet = true; }; Lytebox.prototype.launch = function(args) { var sUrl = this.isEmpty(args.url) ? '' : String(args.url); var sOptions = this.isEmpty(args.options) ? '' : String(args.options); var sTitle = this.isEmpty(args.title) ? '' : args.title; var sDesc = this.isEmpty(args.description) ? '' : args.description; var bSlideshow = /slide:true/i.test(sOptions); if (this.isEmpty(sUrl)) { return false; } if (!this.isReady) { this.timerIDArray[this.timerIDCount++] = setTimeout("$lb.launch({ url: '" + sUrl + "', options: '" + sOptions + "', title: '" + sTitle + "', description: '" + sDesc + "' })", 100); return; } else { for (var i = 0; i < this.timerIDCount; i++) { window.clearTimeout(this.timerIDArray[i]); } } var aUrl = sUrl.split('?'); var sExt = aUrl[0].split('.').pop().toLowerCase(); var bImage = (sExt == 'png' || sExt == 'jpg' || sExt == 'jpeg' || sExt == 'gif' || sExt == 'bmp'); var oLauncher = this.doc.$('lbLauncher'); oLauncher.setAttribute('href', sUrl); oLauncher.setAttribute('data-lyte-options', sOptions); oLauncher.setAttribute('data-title', sTitle); oLauncher.setAttribute('data-description', sDesc); this.updateLyteboxItems(); this.start(oLauncher, bSlideshow, (bImage ? false : true)); }; Lytebox.prototype.start = function(oLink, bSlideshow, bFrame) { var dataOptions = String(oLink.getAttribute('data-lyte-options')); dataOptions = this.isEmpty(dataOptions) ? String(oLink.getAttribute('rev')) : dataOptions; this.setOptions(dataOptions); this.isSlideshow = (bSlideshow ? true : false); this.isLyteframe = (bFrame ? true : false); if (!this.isEmpty(this.beforeStart)) { var callback = window[this.beforeStart]; if (typeof callback === 'function') { if (!callback(this.args)) { return; } } } if (this.ie && this.ieVersion <= 6) { this.toggleSelects('hide'); } if (this.hideObjects) { this.toggleObjects('hide'); } if (this.isFrame && window.parent.frames[window.name].document) { window.parent.$lb.printId = (this.isLyteframe ? 'lbIframe' : 'lbImage'); } else { this.printId = (this.isLyteframe ? 'lbIframe' : 'lbImage'); } this.aPageSize = this.getPageSize(); var objOverlay = this.doc.$('lbOverlay'); var objBody = this.doc.getElementsByTagName("body").item(0); objOverlay.style.height = this.aPageSize[1] + "px"; objOverlay.style.display = ''; this.fadeIn({ id: 'lbOverlay', opacity: (this.doAnimations && this.animateOverlay && (!this.ie || this.ieVersion >= 9) ? 0 : this.maxOpacity) }); var anchors = (this.isFrame && window.parent.frames[window.name].document) ? window.parent.frames[window.name].document.getElementsByTagName('a') : document.getElementsByTagName('a'); anchors = (this.isFrame) ? anchors : document.getElementsByTagName('a'); var areas = (this.isFrame && window.parent.frames[window.name].document) ? window.parent.frames[window.name].document.getElementsByTagName('area') : document.getElementsByTagName('area'); var lyteLinks = this.combine(anchors, areas); var sType = sExt = aUrl = null; this.frameArray = []; this.frameNum = 0; this.imageArray = []; this.imageNum = 0; this.slideArray = []; this.slideNum = 0; if (this.isEmpty(this.group)) { dataOptions = String(oLink.getAttribute('data-lyte-options')); dataOptions = this.isEmpty(dataOptions) ? String(oLink.getAttribute('rev')) : dataOptions; if (this.isLyteframe) { this.frameArray.push(new Array(oLink.getAttribute('href'), (!this.isEmpty(oLink.getAttribute('data-title')) ? oLink.getAttribute('data-title') : oLink.getAttribute('title')), oLink.getAttribute('data-description'), dataOptions)); } else { this.imageArray.push(new Array(oLink.getAttribute('href'), (!this.isEmpty(oLink.getAttribute('data-title')) ? oLink.getAttribute('data-title') : oLink.getAttribute('title')), oLink.getAttribute('data-description'), dataOptions)); } } else { for (var i = 0; i < lyteLinks.length; i++) { var myLink = lyteLinks[i]; dataOptions = String(myLink.getAttribute('data-lyte-options')); dataOptions = this.isEmpty(dataOptions) ? String(myLink.getAttribute('rev')) : dataOptions; if (myLink.getAttribute('href') && dataOptions.toLowerCase().match('group:' + this.group)) { sType = String(myLink.getAttribute(this.classAttribute)).match(/lytebox|lyteshow|lyteframe/i); sType = this.isEmpty(sType) ? myLink.getAttribute('rel').match(/lytebox|lyteshow|lyteframe/i) : sType; aUrl = myLink.getAttribute('href').split('?'); sExt = aUrl[0].split('.').pop().toLowerCase(); bImage = (sExt == 'png' || sExt == 'jpg' || sExt == 'jpeg' || sExt == 'gif' || sExt == 'bmp'); if (sType && sType.length >= 1) { if (bImage && (dataOptions.match(/slide:true/i) || sType[0].toLowerCase() == 'lyteshow')) { this.slideArray.push(new Array(myLink.getAttribute('href'), (!this.isEmpty(myLink.getAttribute('data-title')) ? myLink.getAttribute('data-title') : myLink.getAttribute('title')), myLink.getAttribute('data-description'), dataOptions)); } else if (bImage) { this.imageArray.push(new Array(myLink.getAttribute('href'), (!this.isEmpty(myLink.getAttribute('data-title')) ? myLink.getAttribute('data-title') : myLink.getAttribute('title')), myLink.getAttribute('data-description'), dataOptions)); } else { this.frameArray.push(new Array(myLink.getAttribute('href'), (!this.isEmpty(myLink.getAttribute('data-title')) ? myLink.getAttribute('data-title') : myLink.getAttribute('title')), myLink.getAttribute('data-description'), dataOptions)); } } } } if (this.isLyteframe) { this.frameArray = this.removeDuplicates(this.frameArray); while(this.frameArray[this.frameNum][0] != oLink.getAttribute('href')) { this.frameNum++; } } else if (bSlideshow) { this.slideArray = this.removeDuplicates(this.slideArray); try { while(this.slideArray[this.slideNum][0] != oLink.getAttribute('href')) { this.slideNum++; } } catch(e) { } } else { this.imageArray = this.removeDuplicates(this.imageArray); while(this.imageArray[this.imageNum][0] != oLink.getAttribute('href')) { this.imageNum++; } } } this.changeContent(this.isLyteframe ? this.frameNum : (this.isSlideshow ? this.slideNum : this.imageNum)); }; Lytebox.prototype.changeContent = function(iContentNum) { this.contentNum = iContentNum; if (!this.overlayLoaded) { this.changeContentTimerArray[this.changeContentTimerCount++] = setTimeout("$lb.changeContent(" + this.contentNum + ")", 250); return; } else { for (var i = 0; i < this.changeContentTimerCount; i++) { window.clearTimeout(this.changeContentTimerArray[i]); } } var sDataLyteOptions = (this.isLyteframe) ? this.frameArray[this.contentNum][3] : (this.isSlideshow ? this.slideArray[this.contentNum][3] : this.imageArray[this.contentNum][3]); if (!this.inherit || /inherit:false/i.test(sDataLyteOptions)) { this.setOptions(String(sDataLyteOptions)); } else { var sDataLyteOptions1 = String((this.isLyteframe) ? this.frameArray[0][3] : (this.isSlideshow ? this.slideArray[0][3] : this.imageArray[0][3])); if (this.isLyteframe) { var sWidth = sHeight = null; try { sWidth = sDataLyteOptions.match(/width:\d+(%|px|)/i)[0]; } catch(e) { } try { sHeight = sDataLyteOptions.match(/height:\d+(%|px|)/i)[0]; } catch(e) { } if (!this.isEmpty(sWidth)) { sDataLyteOptions1 = sDataLyteOptions1.replace(/width:\d+(%|px|)/i, sWidth); } if (!this.isEmpty(sHeight)) { sDataLyteOptions1 = sDataLyteOptions1.replace(/height:\d+(%|px|)/i, sHeight); } } this.setOptions(sDataLyteOptions1); } var object = this.doc.$('lbMain'); object.style.display = ''; var iDivisor = 40; if (this.autoResize && this.fixedPosition) { if (this.ie && (this.ieVersion <= 7 || this.doc.compatMode == 'BackCompat')) { object.style.top = (this.getPageScroll() + (this.aPageSize[3] / iDivisor)) + "px"; var ps = (this.aPageSize[3] / iDivisor); this.scrollHandler = function(){ $lb.doc.$('lbMain').style.top = ($lb.getPageScroll() + ps) + 'px'; } this.bodyOnscroll = document.body.onscroll; if (window.addEventListener) { window.addEventListener('scroll', this.scrollHandler); } else if (window.attachEvent) { window.attachEvent('onscroll', this.scrollHandler); } object.style.position = "absolute"; } else { object.style.top = ((this.aPageSize[3] / iDivisor)) + "px"; object.style.position = "fixed"; } } else { object.style.position = "absolute"; object.style.top = (this.getPageScroll() + (this.aPageSize[3] / iDivisor)) + "px"; } this.doc.$('lbOuterContainer').style.paddingBottom = '0'; if (!this.outerBorder) { this.doc.$('lbOuterContainer').style.border = 'none'; } else { this.doc.$('lbOuterContainer').setAttribute(this.classAttribute, this.theme); } if (this.forceCloseClick) { this.doc.$('lbOverlay').onclick = ''; } else { this.doc.$('lbOverlay').onclick = function() { $lb.end(); return false; } } this.doc.$('lbMain').onclick = function(e) { var e = e; if (!e) { if (window.parent.frames[window.name] && (parent.document.getElementsByTagName('frameset').length <= 0)) { e = window.parent.window.event; } else { e = window.event; } } var id = (e.target ? e.target.id : e.srcElement.id); if ((id == 'lbMain') && (!$lb.forceCloseClick)) { $lb.end(); return false; } } this.doc.$('lbPrintTop').onclick = this.doc.$('lbPrint').onclick = function() { $lb.printWindow(); return false; } this.doc.$('lbCloseTop').onclick = this.doc.$('lbClose').onclick = function() { $lb.end(); return false; } this.doc.$('lbPauseTop').onclick = function() { $lb.togglePlayPause("lbPauseTop", "lbPlayTop"); return false; } this.doc.$('lbPause').onclick = function() { $lb.togglePlayPause("lbPause", "lbPlay"); return false; } this.doc.$('lbPlayTop').onclick = function() { $lb.togglePlayPause("lbPlayTop", "lbPauseTop"); return false; } this.doc.$('lbPlay').onclick = function() { $lb.togglePlayPause("lbPlay", "lbPause"); return false; } if (this.isSlideshow && this.showPlayPause && this.isPaused) { this.doc.$('lbPlay').style.display = ''; this.doc.$('lbPause').style.display = 'none'; } if (this.isSlideshow) { for (var i = 0; i < this.slideshowIDCount; i++) { window.clearTimeout(this.slideshowIDArray[i]); } } if (!this.outerBorder) { this.doc.$('lbOuterContainer').style.border = 'none'; } else { this.doc.$('lbOuterContainer').setAttribute(this.classAttribute, this.theme); } var iDecreaseMargin = 10; if (this.titleTop || this.navTop) { this.doc.$('lbTopContainer').style.visibility = 'hidden'; iDecreaseMargin += this.doc.$('lbTopContainer').offsetHeight; } else { this.doc.$('lbTopContainer').style.display = 'none'; } this.doc.$('lbBottomContainer').style.display = 'none'; this.doc.$('lbImage').style.display = 'none'; this.doc.$('lbIframe').style.display = 'none'; this.doc.$('lbPrevHov').style.display = 'none'; this.doc.$('lbNextHov').style.display = 'none'; this.doc.$('lbIframeContainer').style.display = 'none'; this.doc.$('lbLoading').style.marginTop = '-' + iDecreaseMargin + 'px'; this.doc.$('lbLoading').style.display = ''; if (this.isLyteframe) { var iframe = $lb.doc.$('lbIframe'); iframe.src = 'about:blank'; var w = this.trim(this.width); var h = this.trim(this.height); if (/\%/.test(w)) { var percent = parseInt(w); w = parseInt((this.aPageSize[2]-50)*percent/100); w = w+'px'; } if (/\%/.test(h)) { var percent = parseInt(h); h = parseInt((this.aPageSize[3]-150)*percent/100); h = h+'px'; } if (this.autoResize) { var x = this.aPageSize[2] - 50; var y = this.aPageSize[3] - 150; w = (parseInt(w) > x ? x : w) + 'px'; h = (parseInt(h) > y ? y : h) + 'px'; } iframe.height = this.height = h; iframe.width = this.width = w; iframe.scrolling = this.scrolling; var oDoc = iframe.contentWindow || iframe.contentDocument; try { if (oDoc.document) { oDoc = oDoc.document; } oDoc.body.style.margin = 0; oDoc.body.style.padding = 0; if (this.ie && this.ieVersion <= 8) { oDoc.body.scroll = this.scrolling; oDoc.body.overflow = this.scrolling = 'no' ? 'hidden' : 'auto'; } } catch(e) { } this.resizeContainer(parseInt(this.width), parseInt(this.height)); } else { this.imgPreloader = new Image(); this.imgPreloader.onload = function() { var imageWidth = $lb.imgPreloader.width; var imageHeight = $lb.imgPreloader.height; if ($lb.autoResize) { var x = $lb.aPageSize[2] - 50; var y = $lb.aPageSize[3] - 150; if (imageWidth > x) { imageHeight = Math.round(imageHeight * (x / imageWidth)); imageWidth = x; if (imageHeight > y) { imageWidth = Math.round(imageWidth * (y / imageHeight)); imageHeight = y; } } else if (imageHeight > y) { imageWidth = Math.round(imageWidth * (y / imageHeight)); imageHeight = y; if (imageWidth > x) { imageHeight = Math.round(imageHeight * (x / imageWidth)); imageWidth = x; } } } var lbImage = $lb.doc.$('lbImage'); lbImage.src = $lb.imgPreloader.src; lbImage.width = imageWidth; lbImage.height = imageHeight; $lb.resizeContainer(imageWidth, imageHeight); $lb.imgPreloader.onload = function() {}; } this.imgPreloader.src = (this.isSlideshow ? this.slideArray[this.contentNum][0] : this.imageArray[this.contentNum][0]); } }; Lytebox.prototype.resizeContainer = function(iWidth, iHeight) { this.resizeWidth = iWidth; this.resizeHeight = iHeight; this.wCur = this.doc.$('lbOuterContainer').offsetWidth; this.hCur = this.doc.$('lbOuterContainer').offsetHeight; this.xScale = ((this.resizeWidth + (this.borderSize * 2)) / this.wCur) * 100; this.yScale = ((this.resizeHeight + (this.borderSize * 2)) / this.hCur) * 100; var wDiff = (this.wCur - this.borderSize * 2) - this.resizeWidth; var hDiff = (this.hCur - this.borderSize * 2) - this.resizeHeight; this.wDone = (wDiff == 0); if (!(hDiff == 0)) { this.hDone = false; this.resizeH('lbOuterContainer', this.hCur, this.resizeHeight + this.borderSize * 2, this.getPixelRate(this.hCur, this.resizeHeight)); } else { this.hDone = true; if (!this.wDone) { this.resizeW('lbOuterContainer', this.wCur, this.resizeWidth + this.borderSize * 2, this.getPixelRate(this.wCur, this.resizeWidth)); } } if ((hDiff == 0) && (wDiff == 0)) { if (this.ie){ this.pause(250); } else { this.pause(100); } } this.doc.$('lbPrevHov').style.height = this.resizeHeight + "px"; this.doc.$('lbNextHov').style.height = this.resizeHeight + "px"; if (this.hDone && this.wDone) { if (this.isLyteframe) { this.loadContent(); } else { this.showContent(); } } }; Lytebox.prototype.loadContent = function() { try { var iframe = this.doc.$('lbIframe'); var uri = this.frameArray[this.contentNum][0]; if (!this.inline && this.appendQS) { uri += ((/\?/.test(uri)) ? '&' : '?') + 'request_from=lytebox'; } if (this.autoPlay && /youtube/i.test(uri)) { uri += ((/\?/.test(uri)) ? '&' : '?') + 'autoplay=1'; } if (!this.autoEmbed || (this.ff && (uri.match(/.pdf|.mov|.wmv/i)))) { this.frameSource = uri; this.showContent(); return; } if (this.ie) { iframe.onreadystatechange = function() { if ($lb.doc.$('lbIframe').readyState == "complete") { $lb.showContent(); $lb.doc.$('lbIframe').onreadystatechange = null; } }; } else { iframe.onload = function() { $lb.showContent(); $lb.doc.$('lbIframe').onload = null; }; } if (this.inline || (uri.match(/.mov|.avi|.wmv|.mpg|.mpeg|.swf/i))) { iframe.src = 'about:blank'; this.frameSource = ''; var sHtml = (this.inline) ? this.doc.$(uri.substr(uri.indexOf('#') + 1, uri.length)).innerHTML : this.buildObject(parseInt(this.width), parseInt(this.height), uri); var oDoc = iframe.contentWindow || iframe.contentDocument; if (oDoc.document) { oDoc = oDoc.document; } oDoc.open(); oDoc.write(sHtml); oDoc.close(); oDoc.body.style.margin = 0; oDoc.body.style.padding = 0; if (!this.inline) { oDoc.body.style.backgroundColor = '#fff'; oDoc.body.style.fontFamily = 'Verdana, Helvetica, sans-serif'; oDoc.body.style.fontSize = '0.9em'; } this.frameSource = ''; } else { this.frameSource = uri; iframe.src = uri; } } catch(e) { } }; Lytebox.prototype.showContent = function() { if (this.isSlideshow) { if(this.contentNum == (this.slideArray.length - 1)) { if (this.loopSlideshow) { this.slideshowIDArray[this.slideshowIDCount++] = setTimeout("$lb.changeContent(0)", this.slideInterval); } else if (this.autoEnd) { this.slideshowIDArray[this.slideshowIDCount++] = setTimeout("$lb.end('slideshow')", this.slideInterval); } } else { if (!this.isPaused) { this.slideshowIDArray[this.slideshowIDCount++] = setTimeout("$lb.changeContent("+(this.contentNum+1)+")", this.slideInterval); } } this.doc.$('lbHoverNav').style.display = (this.ieVersion != 6 && this.showNavigation && this.navTypeHash['Hover_by_type_' + this.navType] ? '' : 'none'); this.doc.$('lbCloseTop').style.display = (this.showClose && this.navTop ? '' : 'none'); this.doc.$('lbClose').style.display = (this.showClose && !this.navTop ? '' : 'none'); this.doc.$('lbBottomData').style.display = (this.showDetails ? '' : 'none'); this.doc.$('lbPauseTop').style.display = (this.showPlayPause && this.navTop ? (!this.isPaused ? '' : 'none') : 'none'); this.doc.$('lbPause').style.display = (this.showPlayPause && !this.navTop ? (!this.isPaused ? '' : 'none') : 'none'); this.doc.$('lbPlayTop').style.display = (this.showPlayPause && this.navTop ? (!this.isPaused ? 'none' : '') : 'none'); this.doc.$('lbPlay').style.display = (this.showPlayPause && !this.navTop ? (!this.isPaused ? 'none' : '') : 'none'); this.doc.$('lbPrevTop').style.display = (this.navTop && this.showNavigation && this.navTypeHash['Display_by_type_' + this.navType] ? '' : 'none'); this.doc.$('lbPrev').style.display = (!this.navTop && this.showNavigation && this.navTypeHash['Display_by_type_' + this.navType] ? '' : 'none'); this.doc.$('lbNextTop').style.display = (this.navTop && this.showNavigation && this.navTypeHash['Display_by_type_' + this.navType] ? '' : 'none'); this.doc.$('lbNext').style.display = (!this.navTop && this.showNavigation && this.navTypeHash['Display_by_type_' + this.navType] ? '' : 'none'); } else { this.doc.$('lbHoverNav').style.display = (this.ieVersion != 6 && this.navTypeHash['Hover_by_type_' + this.navType] && !this.isLyteframe ? '' : 'none'); if ((this.navTypeHash['Display_by_type_' + this.navType] && !this.isLyteframe && this.imageArray.length > 1) || (this.frameArray.length > 1 && this.isLyteframe)) { this.doc.$('lbPrevTop').style.display = (this.navTop ? '' : 'none'); this.doc.$('lbPrev').style.display = (!this.navTop ? '' : 'none'); this.doc.$('lbNextTop').style.display = (this.navTop ? '' : 'none'); this.doc.$('lbNext').style.display = (!this.navTop ? '' : 'none'); } else { this.doc.$('lbPrevTop').style.display = 'none'; this.doc.$('lbPrev').style.display = 'none'; this.doc.$('lbNextTop').style.display = 'none'; this.doc.$('lbNext').style.display = 'none'; } this.doc.$('lbCloseTop').style.display = (this.navTop ? '' : 'none'); this.doc.$('lbClose').style.display = (!this.navTop ? '' : 'none'); this.doc.$('lbBottomData').style.display = ''; this.doc.$('lbPauseTop').style.display = 'none'; this.doc.$('lbPause').style.display = 'none'; this.doc.$('lbPlayTop').style.display = 'none'; this.doc.$('lbPlay').style.display = 'none'; } this.doc.$('lbPrintTop').style.display = (this.showPrint && this.navTop ? '' : 'none'); this.doc.$('lbPrint').style.display = (this.showPrint && !this.navTop ? '' : 'none'); this.updateDetails(); this.doc.$('lbLoading').style.display = 'none'; this.doc.$('lbImageContainer').style.display = (this.isLyteframe ? 'none' : ''); this.doc.$('lbIframeContainer').style.display = (this.isLyteframe ? '' : 'none'); if (this.isLyteframe) { if (!this.isEmpty(this.frameSource)) { this.doc.$('lbIframe').src = this.frameSource; } this.doc.$('lbIframe').style.display = ''; this.fadeIn({ id: 'lbIframe', opacity: (this.doAnimations && (!this.ie || this.ieVersion >= 9) ? 0 : 100) }); } else { this.doc.$('lbImage').style.display = ''; this.fadeIn({ id: 'lbImage', opacity: (this.doAnimations && (!this.ie || this.ieVersion >= 9) ? 0 : 100) }); this.preloadNeighborImages(); } if (!this.isEmpty(this.afterStart)) { var callback = window[this.afterStart]; if (typeof callback === 'function') { callback(this.args); } } }; Lytebox.prototype.updateDetails = function() { var sTitle = (this.isSlideshow ? this.slideArray[this.contentNum][1] : (this.isLyteframe ? this.frameArray[this.contentNum][1] : this.imageArray[this.contentNum][1])); var sDesc = (this.isSlideshow ? this.slideArray[this.contentNum][2] : (this.isLyteframe ? this.frameArray[this.contentNum][2] : this.imageArray[this.contentNum][2])); if (this.ie && this.ieVersion <= 7 || (this.ieVersion >= 8 && this.doc.compatMode == 'BackCompat')) { this.doc.$(this.titleTop ? 'lbTitleBottom' : 'lbTitleTop').style.display = 'none'; this.doc.$(this.titleTop ? 'lbTitleTop' : 'lbTitleBottom').style.display = (this.isEmpty(sTitle) ? 'none' : 'block'); } this.doc.$('lbDescBottom').style.display = (this.isEmpty(sDesc) ? 'none' : ''); this.doc.$(this.titleTop ? 'lbTitleTop' : 'lbTitleBottom').innerHTML = (this.isEmpty(sTitle) ? '' : sTitle); this.doc.$(this.titleTop ? 'lbTitleBottom' : 'lbTitleTop').innerHTML = ''; this.doc.$(this.titleTop ? 'lbNumBottom' : 'lbNumTop').innerHTML = ''; this.updateNav(); if (this.titleTop || this.navTop) { this.doc.$('lbTopContainer').style.display = 'block'; this.doc.$('lbTopContainer').style.visibility = 'visible'; } else { this.doc.$('lbTopContainer').style.display = 'none'; } var object = (this.titleTop ? this.doc.$('lbNumTop') : this.doc.$('lbNumBottom')); if (this.isSlideshow && this.slideArray.length > 1) { object.innerHTML = this.label['image'].replace('%1', this.contentNum + 1).replace('%2', this.slideArray.length); } else if (this.imageArray.length > 1 && !this.isLyteframe) { object.innerHTML = this.label['image'].replace('%1', this.contentNum + 1).replace('%2', this.imageArray.length); } else if (this.frameArray.length > 1 && this.isLyteframe) { object.innerHTML = this.label['page'].replace('%1', this.contentNum + 1).replace('%2', this.frameArray.length); } else { object.innerHTML = ''; } var bAddSpacer = !(this.titleTop || (this.isEmpty(sTitle) && this.isEmpty(object.innerHTML))); this.doc.$('lbDescBottom').innerHTML = (this.isEmpty(sDesc) ? '' : (bAddSpacer ? '
    ' : '') + sDesc); var iNavWidth = 0; if (this.ie && this.ieVersion <= 7 || (this.ieVersion >= 8 && this.doc.compatMode == 'BackCompat')) { iNavWidth = 39 + (this.showPrint ? 39 : 0) + (this.isSlideshow && this.showPlayPause ? 39 : 0); if ((this.isSlideshow && this.slideArray.length > 1 && this.showNavigation && this.navType != 1) || (this.frameArray.length > 1 && this.isLyteframe) || (this.imageArray.length > 1 && !this.isLyteframe && this.navType != 1)) { iNavWidth += 39*2; } } this.doc.$('lbBottomContainer').style.display = (!(this.titleTop && this.navTop) || !this.isEmpty(sDesc) ? 'block' : 'none'); if (this.titleTop && this.navTop) { if (iNavWidth > 0) { this.doc.$('lbTopNav').style.width = iNavWidth + 'px'; } this.doc.$('lbTopData').style.width = (this.doc.$('lbTopContainer').offsetWidth - this.doc.$('lbTopNav').offsetWidth - 15) + 'px'; if (!this.isEmpty(sDesc)) { this.doc.$('lbDescBottom').style.width = (this.doc.$('lbBottomContainer').offsetWidth - 15) + 'px'; } } else if ((!this.titleTop || !this.isEmpty(sDesc)) && !this.navTop) { if (iNavWidth > 0) { this.doc.$('lbBottomNav').style.width = iNavWidth + 'px'; } this.doc.$('lbBottomData').style.width = (this.doc.$('lbBottomContainer').offsetWidth - this.doc.$('lbBottomNav').offsetWidth - 15) + 'px'; this.doc.$('lbDescBottom').style.width = this.doc.$('lbBottomData').style.width; } this.fixBottomPadding(); this.aPageSize = this.getPageSize(); var iMainTop = parseInt(this.doc.$('lbMain').style.top); if ((this.ie && this.ieVersion <= 7) || (this.ieVersion >= 8 && this.doc.compatMode == 'BackCompat')) { iMainTop = (this.ie ? parseInt(this.doc.$('lbMain').style.top) - this.getPageScroll() : parseInt(this.doc.$('lbMain').style.top)); } var iOverlap = (this.doc.$('lbOuterContainer').offsetHeight + iMainTop) - this.aPageSize[3]; var iDivisor = 40; if (iOverlap > 0 && this.autoResize && this.fixedPosition) { if (this.ie && (this.ieVersion <= 7 || this.doc.compatMode == 'BackCompat')) { document.body.onscroll = this.bodyOnscroll; if (window.removeEventListener) { window.removeEventListener('scroll', this.scrollHandler); } else if (window.detachEvent) { window.detachEvent('onscroll', this.scrollHandler); } } this.doc.$('lbMain').style.position = "absolute"; this.doc.$('lbMain').style.top = (this.getPageScroll() + (this.aPageSize[3] / iDivisor)) + "px"; } }; Lytebox.prototype.updateNav = function() { if (this.isSlideshow) { if (this.contentNum != 0) { if (this.navTypeHash['Display_by_type_' + this.navType] && this.showNavigation) { this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').setAttribute(this.classAttribute, this.theme); this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').style.display = ''; this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').onclick = function() { if ($lb.pauseOnPrevClick) { $lb.togglePlayPause($lb.navTop ? 'lbPauseTop' : 'lbPause', $lb.navTop ? 'lbPlayTop' : 'lbPlay'); } $lb.changeContent($lb.contentNum - 1); return false; } } if (this.navTypeHash['Hover_by_type_' + this.navType]) { var object = this.doc.$('lbPrevHov'); object.style.display = ''; object.onclick = function() { if ($lb.pauseOnPrevClick) { $lb.togglePlayPause($lb.navTop ? 'lbPauseTop' : 'lbPause', $lb.navTop ? 'lbPlayTop' : 'lbPlay'); } $lb.changeContent($lb.contentNum - 1); return false; } } } else { if (this.navTypeHash['Display_by_type_' + this.navType]) { this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').setAttribute(this.classAttribute, this.theme + 'Off'); this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').onclick = function() { return false; } } } if (this.contentNum != (this.slideArray.length - 1) && this.showNavigation) { if (this.navTypeHash['Display_by_type_' + this.navType]) { this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').setAttribute(this.classAttribute, this.theme); this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').style.display = ''; this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').onclick = function() { if ($lb.pauseOnNextClick) { $lb.togglePlayPause($lb.navTop ? 'lbPauseTop' : 'lbPause', $lb.navTop ? 'lbPlayTop' : 'lbPlay'); } $lb.changeContent($lb.contentNum + 1); return false; } } if (this.navTypeHash['Hover_by_type_' + this.navType]) { var object = this.doc.$('lbNextHov'); object.style.display = ''; object.onclick = function() { if ($lb.pauseOnNextClick) { $lb.togglePlayPause($lb.navTop ? 'lbPauseTop' : 'lbPause', $lb.navTop ? 'lbPlayTop' : 'lbPlay'); } $lb.changeContent($lb.contentNum + 1); return false; } } } else { if (this.navTypeHash['Display_by_type_' + this.navType]) { this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').setAttribute(this.classAttribute, this.theme + 'Off'); this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').onclick = function() { return false; } } } } else if (this.isLyteframe) { if(this.contentNum != 0) { this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').setAttribute(this.classAttribute, this.theme); this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').style.display = ''; this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').onclick = function() { $lb.changeContent($lb.contentNum - 1); return false; } } else { this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').setAttribute(this.classAttribute, this.theme + 'Off'); this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').onclick = function() { return false; } } if(this.contentNum != (this.frameArray.length - 1)) { this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').setAttribute(this.classAttribute, this.theme); this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').style.display = ''; this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').onclick = function() { $lb.changeContent($lb.contentNum + 1); return false; } } else { this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').setAttribute(this.classAttribute, this.theme + 'Off'); this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').onclick = function() { return false; } } } else { if(this.contentNum != 0) { if (this.navTypeHash['Display_by_type_' + this.navType]) { this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').setAttribute(this.classAttribute, this.theme); this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').style.display = ''; this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').onclick = function() { $lb.changeContent($lb.contentNum - 1); return false; } } if (this.navTypeHash['Hover_by_type_' + this.navType]) { var object2 = this.doc.$('lbPrevHov'); object2.style.display = ''; object2.onclick = function() { $lb.changeContent($lb.contentNum - 1); return false; } } } else { if (this.navTypeHash['Display_by_type_' + this.navType]) { this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').setAttribute(this.classAttribute, this.theme + 'Off'); this.doc.$(this.navTop ? 'lbPrevTop' : 'lbPrev').onclick = function() { return false; } } } if(this.contentNum != (this.imageArray.length - 1)) { if (this.navTypeHash['Display_by_type_' + this.navType]) { this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').setAttribute(this.classAttribute, this.theme); this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').style.display = ''; this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').onclick = function() { $lb.changeContent($lb.contentNum + 1); return false; } } if (this.navTypeHash['Hover_by_type_' + this.navType]) { var object2 = this.doc.$('lbNextHov'); object2.style.display = ''; object2.onclick = function() { $lb.changeContent($lb.contentNum + 1); return false; } } } else { if (this.navTypeHash['Display_by_type_' + this.navType]) { this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').setAttribute(this.classAttribute, this.theme + 'Off'); this.doc.$(this.navTop ? 'lbNextTop' : 'lbNext').onclick = function() { return false; } } } } this.enableKeyboardNav(); }; Lytebox.prototype.fixBottomPadding = function() { if (!((this.ieVersion == 7 || this.ieVersion == 8 || this.ieVersion == 9) && this.doc.compatMode == 'BackCompat') && this.ieVersion != 6) { var titleHeight = this.doc.$('lbTopContainer').offsetHeight + 5; var offsetHeight = (titleHeight == 5 ? 0 : titleHeight) + this.doc.$('lbBottomContainer').offsetHeight; this.doc.$('lbOuterContainer').style.paddingBottom = (offsetHeight + 5) + 'px'; } }; Lytebox.prototype.enableKeyboardNav = function() { document.onkeydown = this.keyboardAction; }; Lytebox.prototype.disableKeyboardNav = function() { document.onkeydown = ''; }; Lytebox.prototype.keyboardAction = function(e) { var keycode = key = escape = null; keycode = (e == null) ? event.keyCode : e.which; key = String.fromCharCode(keycode).toLowerCase(); escape = (e == null) ? 27 : e.DOM_VK_ESCAPE; if ((key == 'x') || (key == 'c') || (keycode == escape || keycode == 27)) { parent.$lb.end(); } else if (keycode == 32 && $lb.isSlideshow && $lb.showPlayPause) { if ($lb.isPaused) { $lb.togglePlayPause($lb.navTop ? 'lbPlayTop' : 'lbPlay', $lb.navTop ? 'lbPauseTop' : 'lbPause'); } else { $lb.togglePlayPause($lb.navTop ? 'lbPauseTop' : 'lbPause', $lb.navTop ? 'lbPlayTop' : 'lbPlay'); } return false; } else if (key == 'p' || keycode == 37) { if ($lb.isSlideshow) { if($lb.contentNum != 0) { $lb.disableKeyboardNav(); $lb.changeContent($lb.contentNum - 1); } } else if ($lb.isLyteframe) { if($lb.contentNum != 0) { $lb.disableKeyboardNav(); $lb.changeContent($lb.contentNum - 1); } } else { if($lb.contentNum != 0) { $lb.disableKeyboardNav(); $lb.changeContent($lb.contentNum - 1); } } } else if (key == 'n' || keycode == 39) { if ($lb.isSlideshow) { if($lb.contentNum != ($lb.slideArray.length - 1)) { $lb.disableKeyboardNav(); $lb.changeContent($lb.contentNum + 1); } } else if ($lb.isLyteframe) { if($lb.contentNum != ($lb.frameArray.length - 1)) { $lb.disableKeyboardNav(); $lb.changeContent($lb.contentNum + 1); } } else { if($lb.contentNum != ($lb.imageArray.length - 1)) { $lb.disableKeyboardNav(); $lb.changeContent($lb.contentNum + 1); } } } }; Lytebox.prototype.preloadNeighborImages = function() { if (this.isSlideshow) { if ((this.slideArray.length - 1) > this.contentNum) { var preloadNextImage = new Image(); preloadNextImage.src = this.slideArray[this.contentNum + 1][0]; } if (this.contentNum > 0) { var preloadPrevImage = new Image(); preloadPrevImage.src = this.slideArray[this.contentNum - 1][0]; } } else { if ((this.imageArray.length - 1) > this.contentNum) { var preloadNextImage = new Image(); preloadNextImage.src = this.imageArray[this.contentNum + 1][0]; } if (this.contentNum > 0) { var preloadPrevImage = new Image(); preloadPrevImage.src = this.imageArray[this.contentNum - 1][0]; } } }; Lytebox.prototype.togglePlayPause = function(sHideId, sShowId) { if (this.isSlideshow && (sHideId == 'lbPauseTop' || sHideId == 'lbPause')) { for (var i = 0; i < this.slideshowIDCount; i++) { window.clearTimeout(this.slideshowIDArray[i]); } } this.doc.$(sHideId).style.display = 'none'; this.doc.$(sShowId).style.display = ''; if (sHideId == 'lbPlayTop' || sHideId == 'lbPlay') { this.isPaused = false; if (this.contentNum == (this.slideArray.length - 1)) { if (this.loopSlideshow) { this.changeContent(0); } else if (this.autoEnd) { this.end(); } } else { this.changeContent(this.contentNum + 1); } } else { this.isPaused = true; } }; Lytebox.prototype.end = function(sCaller) { var closeClick = (sCaller == 'slideshow' ? false : true); if (this.isSlideshow && this.isPaused && !closeClick) { return; } if (!this.isEmpty(this.beforeEnd)) { var callback = window[this.beforeEnd]; if (typeof callback === 'function') { if (!callback(this.args)) { return; } } } this.disableKeyboardNav(); document.body.onscroll = this.bodyOnscroll; if (this.refreshPage) { var uri_href = top.location.href; var reg=/\#.*$/g; uri_href=uri_href.replace(reg, ""); top.location.href = uri_href; return; } this.doc.$('lbMain').style.display = 'none'; this.fadeOut({ id: 'lbOverlay', opacity: (this.doAnimations && this.animateOverlay && (!this.ie || this.ieVersion >= 9) ? this.maxOpacity : 0), speed: 5, display: 'none' }); this.toggleSelects('visible'); if (this.hideObjects) { this.toggleObjects('visible'); } this.doc.$('lbOuterContainer').style.width = '200px'; this.doc.$('lbOuterContainer').style.height = '200px'; if (this.inline && this.safari) { var iframe = this.doc.$('lbIframe'); var oDoc = iframe.contentWindow || iframe.contentDocument; if (oDoc.document) { oDoc = oDoc.document; } oDoc.open(); oDoc.write(''); oDoc.close(); } if (this.isSlideshow) { for (var i = 0; i < this.slideshowIDCount; i++) { window.clearTimeout(this.slideshowIDArray[i]); } this.isPaused = false; } if (!this.isEmpty(this.afterEnd)) { var callback = window[this.afterEnd]; if (typeof callback === 'function') { callback(this.args); } } }; Lytebox.prototype.checkFrame = function() { if (window.parent.frames[window.name] && (parent.document.getElementsByTagName('frameset').length <= 0) && window.name != 'lbIframe') { this.isFrame = true; this.doc = parent.document; } else { this.isFrame = false; this.doc = document; } this.doc.$ = this.doc.getElementById; }; Lytebox.prototype.getPixelRate = function(iCurrent, iDim) { var diff = (iDim > iCurrent) ? iDim - iCurrent : iCurrent - iDim; if (diff >= 0 && diff <= 100) { return (100 / this.resizeDuration); } if (diff > 100 && diff <= 200) { return (150 / this.resizeDuration); } if (diff > 200 && diff <= 300) { return (200 / this.resizeDuration); } if (diff > 300 && diff <= 400) { return (250 / this.resizeDuration); } if (diff > 400 && diff <= 500) { return (300 / this.resizeDuration); } if (diff > 500 && diff <= 600) { return (350 / this.resizeDuration); } if (diff > 600 && diff <= 700) { return (400 / this.resizeDuration); } if (diff > 700) { return (450 / this.resizeDuration); } }; Lytebox.prototype.fadeIn = function(args) { var sId = this.isEmpty(args.id) ? '' : args.id; var iSpeed = (this.isEmpty(args.speed) ? 5 : (parseInt(args.speed) > 5 ? 5 : parseInt(args.speed))); iSpeed = isNaN(iSpeed) ? 5 : iSpeed; var iOpacity = this.isEmpty(args.opacity) ? 0 : parseInt(args.opacity); iOpacity = isNaN(iOpacity) ? 0 : iOpacity; var sDisplay = this.isEmpty(args.display) ? '' : args.display; var sVisibility = this.isEmpty(args.visibility) ? '' : args.visibility; var oElement = this.doc.$(sId); var iIncrement = iSpeed; if (/lbImage|lbIframe|lbOverlay|lbBottomContainer|lbTopContainer/.test(sId)) { iIncrement = this.ff ? (this.ffVersion >= 6 ? 2 : 5) : (this.safari ? 3 : (this.ieVersion <= 8 ? 10 : 5)); iIncrement = this.isMobile() ? 20 : iIncrement; iIncrement = (sId == 'lbOverlay' ? iIncrement * 2 : iIncrement); iIncrement = (sId == 'lbIframe' ? 100 : iIncrement); } else if (this.ieVersion == 7 || this.ieVersion == 8) { iIncrement = 10; } oElement.style.opacity = (iOpacity / 100); oElement.style.filter = "alpha(opacity=" + (iOpacity) + ")"; if (iOpacity >= 100 && (sId == 'lbImage' || sId == 'lbIframe')) { try { oElement.style.removeAttribute("filter"); } catch(e) {} this.fixBottomPadding(); } else if (iOpacity >= this.maxOpacity && sId == 'lbOverlay') { for (var i = 0; i < this.overlayTimerCount; i++) { window.clearTimeout(this.overlayTimerArray[i]); } this.overlayLoaded = true; return; } else if (iOpacity >= 100 && (sId == 'lbBottomContainer' || sId == 'lbTopContainer')) { try { oElement.style.removeAttribute("filter"); } catch(e) {} for (var i = 0; i < this.imageTimerCount; i++) { window.clearTimeout(this.imageTimerArray[i]); } this.doc.$('lbOverlay').style.height = this.aPageSize[1] + "px"; } else if (iOpacity >= 100) { for (var i = 0; i < this.imageTimerCount; i++) { window.clearTimeout(this.imageTimerArray[i]); } } else { if (sId == 'lbOverlay') { this.overlayTimerArray[this.overlayTimerCount++] = setTimeout("$lb.fadeIn({ id: '" + sId + "', opacity: " + (iOpacity + iIncrement) + ", speed: " + iSpeed + " })", 1); } else { this.imageTimerArray[this.imageTimerCount++] = setTimeout("$lb.fadeIn({ id: '" + sId + "', opacity: " + (iOpacity + iIncrement) + ", speed: " + iSpeed + " })", 1); } } }; Lytebox.prototype.fadeOut = function(args) { var sId = this.isEmpty(args.id) ? '' : args.id; var iSpeed = (this.isEmpty(args.speed) ? 5 : (parseInt(args.speed) > 5 ? 5 : parseInt(args.speed))); iSpeed = isNaN(iSpeed) ? 5 : iSpeed; var iOpacity = this.isEmpty(args.opacity) ? 100 : parseInt(args.opacity); iOpacity = isNaN(iOpacity) ? 100 : iOpacity; var sDisplay = this.isEmpty(args.display) ? '' : args.display; var sVisibility = this.isEmpty(args.visibility) ? '' : args.visibility; var oElement = this.doc.$(sId); if (this.ieVersion == 7 || this.ieVersion == 8) { iSpeed *= 2; } oElement.style.opacity = (iOpacity / 100); oElement.style.filter = "alpha(opacity=" + iOpacity + ")"; if (iOpacity <= 0) { try { if (!this.isEmpty(sDisplay)) { oElement.style.display = sDisplay; } if (!this.isEmpty(sVisibility)) { oElement.style.visibility = sVisibility; } } catch(err) { } if (sId == 'lbOverlay') { this.overlayLoaded = false; if (this.isLyteframe) { this.doc.$('lbIframe').src = 'about:blank'; this.initialize(); } } else { for (var i = 0; i < this.timerIDCount; i++) { window.clearTimeout(this.timerIDArray[i]); } } } else if (sId == 'lbOverlay') { this.overlayTimerArray[this.overlayTimerCount++] = setTimeout("$lb.fadeOut({ id: '" + sId + "', opacity: " + (iOpacity - (iSpeed * 2)) + ", speed: " + iSpeed + ", display: '" + sDisplay + "', visibility: '" + sVisibility + "' })", 1); } else { this.timerIDArray[this.timerIDCount++] = setTimeout("$lb.fadeOut({ id: '" + sId + "', opacity: " + (iOpacity - iSpeed) + ", speed: " + iSpeed + ", display: '" + sDisplay + "', visibility: '" + sVisibility + "' })", 1); } }; Lytebox.prototype.resizeW = function(sId, iCurrentW, iMaxW, iPixelRate, iSpeed) { var object = this.doc.$(sId); var newW = (this.doAnimations ? iCurrentW : iMaxW); object.style.width = (newW) + "px"; if (newW < iMaxW) { newW += (newW + iPixelRate >= iMaxW) ? (iMaxW - newW) : iPixelRate; } else if (newW > iMaxW) { newW -= (newW - iPixelRate <= iMaxW) ? (newW - iMaxW) : iPixelRate; } this.resizeWTimerArray[this.resizeWTimerCount++] = setTimeout("$lb.resizeW('" + sId + "', " + newW + ", " + iMaxW + ", " + iPixelRate + ", " + (iSpeed) + ")", iSpeed); if (parseInt(object.style.width) == iMaxW) { this.wDone = true; for (var i = 0; i < this.resizeWTimerCount; i++) { window.clearTimeout(this.resizeWTimerArray[i]); } if (this.isLyteframe) { this.loadContent(); } else { this.showContent(); } } }; Lytebox.prototype.resizeH = function(sId, iCurrentH, iMaxH, iPixelRate, iSpeed) { var object = this.doc.$(sId); var newH = (this.doAnimations ? iCurrentH : iMaxH); object.style.height = (newH) + "px"; if (newH < iMaxH) { newH += (newH + iPixelRate >= iMaxH) ? (iMaxH - newH) : iPixelRate; } else if (newH > iMaxH) { newH -= (newH - iPixelRate <= iMaxH) ? (newH - iMaxH) : iPixelRate; } this.resizeHTimerArray[this.resizeHTimerCount++] = setTimeout("$lb.resizeH('" + sId + "', " + newH + ", " + iMaxH + ", " + iPixelRate + ", " + (iSpeed+.02) + ")", iSpeed+.02); if (parseInt(object.style.height) == iMaxH) { this.hDone = true; for (var i = 0; i < this.resizeHTimerCount; i++) { window.clearTimeout(this.resizeHTimerArray[i]); } this.resizeW('lbOuterContainer', this.wCur, this.resizeWidth + this.borderSize * 2, this.getPixelRate(this.wCur, this.resizeWidth)); } }; Lytebox.prototype.getPageScroll = function() { if (self.pageYOffset) { return this.isFrame ? parent.pageYOffset : self.pageYOffset; } else if (this.doc.documentElement && this.doc.documentElement.scrollTop){ return this.doc.documentElement.scrollTop; } else if (document.body) { return this.doc.body.scrollTop; } }; Lytebox.prototype.getPageSize = function() { var xScroll, yScroll, windowWidth, windowHeight; if (window.innerHeight && window.scrollMaxY) { xScroll = this.doc.scrollWidth; yScroll = (this.isFrame ? parent.innerHeight : self.innerHeight) + (this.isFrame ? parent.scrollMaxY : self.scrollMaxY); } else if (this.doc.body.scrollHeight > this.doc.body.offsetHeight){ xScroll = this.doc.body.scrollWidth; yScroll = this.doc.body.scrollHeight; } else { xScroll = this.doc.getElementsByTagName("html").item(0).offsetWidth; yScroll = this.doc.getElementsByTagName("html").item(0).offsetHeight; xScroll = (xScroll < this.doc.body.offsetWidth) ? this.doc.body.offsetWidth : xScroll; yScroll = (yScroll < this.doc.body.offsetHeight) ? this.doc.body.offsetHeight : yScroll; } if (self.innerHeight) { windowWidth = (this.isFrame) ? parent.innerWidth : self.innerWidth; windowHeight = (this.isFrame) ? parent.innerHeight : self.innerHeight; } else if (document.documentElement && document.documentElement.clientHeight) { windowWidth = this.doc.documentElement.clientWidth; windowHeight = this.doc.documentElement.clientHeight; windowWidth = (windowWidth == 0) ? this.doc.body.clientWidth : windowWidth; windowHeight = (windowHeight == 0) ? this.doc.body.clientHeight : windowHeight; } else if (document.body) { windowWidth = this.doc.getElementsByTagName("html").item(0).clientWidth; windowHeight = this.doc.getElementsByTagName("html").item(0).clientHeight; windowWidth = (windowWidth == 0) ? this.doc.body.clientWidth : windowWidth; windowHeight = (windowHeight == 0) ? this.doc.body.clientHeight : windowHeight; } var pageHeight = (yScroll < windowHeight) ? windowHeight : yScroll; var pageWidth = (xScroll < windowWidth) ? windowWidth : xScroll; return new Array(pageWidth, pageHeight, windowWidth, windowHeight); }; Lytebox.prototype.toggleObjects = function(sState) { var objects = this.doc.getElementsByTagName("object"); for (var i = 0; i < objects.length; i++) { objects[i].style.visibility = (sState == "hide") ? 'hidden' : 'visible'; } var embeds = this.doc.getElementsByTagName("embed"); for (var i = 0; i < embeds.length; i++) { embeds[i].style.visibility = (sState == "hide") ? 'hidden' : 'visible'; } if (this.isFrame) { for (var i = 0; i < parent.frames.length; i++) { try { objects = parent.frames[i].window.document.getElementsByTagName("object"); for (var j = 0; j < objects.length; j++) { objects[j].style.visibility = (sState == "hide") ? 'hidden' : 'visible'; } } catch(e) {} try { embeds = parent.frames[i].window.document.getElementsByTagName("embed"); for (var j = 0; j < embeds.length; j++) { embeds[j].style.visibility = (sState == "hide") ? 'hidden' : 'visible'; } } catch(e) {} } } }; Lytebox.prototype.toggleSelects = function(sState) { var selects = this.doc.getElementsByTagName("select"); for (var i = 0; i < selects.length; i++ ) { selects[i].style.visibility = (sState == "hide") ? 'hidden' : 'visible'; } if (this.isFrame) { for (var i = 0; i < parent.frames.length; i++) { try { selects = parent.frames[i].window.document.getElementsByTagName("select"); for (var j = 0; j < selects.length; j++) { selects[j].style.visibility = (sState == "hide") ? 'hidden' : 'visible'; } } catch(e) {} } } }; Lytebox.prototype.pause = function(iMillis) { var now = new Date(); var exitTime = now.getTime() + iMillis; while (true) { now = new Date(); if (now.getTime() > exitTime) { return; } } }; Lytebox.prototype.combine = function(aAnchors, aAreas) { var lyteLinks = []; for (var i = 0; i < aAnchors.length; i++) { lyteLinks.push(aAnchors[i]); } for (var i = 0; i < aAreas.length; i++) { lyteLinks.push(aAreas[i]); } return lyteLinks; }; Lytebox.prototype.removeDuplicates = function (aArray) { var aNew = new Array(); o:for(var i = 0, n = aArray.length; i < n; i++) { for(var x = 0, y = aNew.length; x < y; x++) { if (aNew[x][0].toLowerCase() == aArray[i][0].toLowerCase()) { continue o; } } aNew[aNew.length] = aArray[i]; } return aNew; }; Lytebox.prototype.printWindow = function () { var w = this.isLyteframe ? 800 : this.imgPreloader.width + 20; var h = this.isLyteframe ? 600 : this.imgPreloader.height + 20; var left = parseInt((screen.availWidth/2) - (w/2)); var top = parseInt((screen.availHeight/2) - (h/2)); var wOpts = "width=" + w + ",height=" + h + ",left=" + left + ",top=" + top + "screenX=" + left + ",screenY=" + top + "directories=0,location=0,menubar=0,resizable=0,scrollbars=0,status=0,titlebar=0,toolbar=0"; var d = new Date(); var wName = 'Print' + d.getTime(); var wUrl = document.getElementById(this.printId).src; this.wContent = window.open(wUrl, wName, wOpts); this.wContent.focus(); var t = setTimeout("$lb.printContent()",1000); }; Lytebox.prototype.printContent = function() { try { if (this.wContent.document.readyState == 'complete') { this.wContent.print(); this.wContent.close(); this.wContent = null; } else { var t = setTimeout("$lb.printContent()",1000); } } catch(e) { } }; Lytebox.prototype.setOptions = function(sOptions) { this.args = ''; this.group = ''; this.inline = false; this.hideObjects = this.__hideObjects; this.autoResize = this.__autoResize; this.doAnimations = this.__doAnimations; this.animateOverlay = this.__animateOverlay; this.forceCloseClick = this.__forceCloseClick; this.refreshPage = this.__refreshPage; this.showPrint = this.__showPrint; this.navType = this.__navType; this.titleTop = this.__titleTop; this.navTop = this.__navTop; this.beforeStart = this.__beforeStart; this.afterStart = this.__afterStart this.beforeEnd = this.__beforeEnd; this.afterEnd = this.__afterEnd; this.scrolling = this.__scrolling; this.width = this.__width; this.height = this.__height; this.loopPlayback = this.__loopPlayback; this.autoPlay = this.__autoPlay; this.autoEmbed = this.__autoEmbed; this.slideInterval = this.__slideInterval; this.showNavigation = this.__showNavigation; this.showClose = this.__showClose; this.showDetails = this.__showDetails; this.showPlayPause = this.__showPlayPause; this.autoEnd = this.__autoEnd; this.pauseOnNextClick = this.__pauseOnNextClick; this.pauseOnPrevClick = this.__pauseOnPrevClick; this.loopSlideshow = this.__loopSlideshow; var sName = sValue = ''; var aSetting = null; var aOptions = sOptions.split(' '); for (var i = 0; i < aOptions.length; i++) { aSetting = aOptions[i].split(':'); sName = (aSetting.length > 1 ? this.trim(aSetting[0]).toLowerCase() : ''); sValue = (aSetting.length > 1 ? this.trim(aSetting[1]): ''); switch(sName) { case 'group': this.group = (sName == 'group' ? (!this.isEmpty(sValue) ? sValue.toLowerCase() : '') : ''); break; case 'hideobjects': this.hideObjects = (/true|false/.test(sValue) ? (sValue == 'true') : this.__hideObjects); break; case 'autoresize': this.autoResize = (/true|false/.test(sValue) ? (sValue == 'true') : this.__autoResize); break; case 'doanimations': this.doAnimations = (/true|false/.test(sValue) ? (sValue == 'true') : this.__doAnimations); break; case 'animateoverlay': this.animateOverlay = (/true|false/.test(sValue) ? (sValue == 'true') : this.__animateOverlay); break; case 'forcecloseclick': this.forceCloseClick = (/true|false/.test(sValue) ? (sValue == 'true') : this.__forceCloseClick); break; case 'refreshpage': this.refreshPage = (/true|false/.test(sValue) ? (sValue == 'true') : this.__refreshPage); break; case 'showprint': this.showPrint = (/true|false/.test(sValue) ? (sValue == 'true') : this.__showPrint); break; case 'navtype': this.navType = (/[1-3]{1}/.test(sValue) ? parseInt(sValue) : this.__navType); break; case 'titletop': this.titleTop = (/true|false/.test(sValue) ? (sValue == 'true') : this.__titleTop); break; case 'navtop': this.navTop = (/true|false/.test(sValue) ? (sValue == 'true') : this.__navTop); break; case 'beforestart': this.beforeStart = (!this.isEmpty(sValue) ? sValue : this.__beforeStart); break; case 'afterstart': this.afterStart = (!this.isEmpty(sValue) ? sValue : this.__afterStart); break; case 'beforeend': this.beforeEnd = (!this.isEmpty(sValue) ? sValue : this.__beforeEnd); break; case 'afterend': this.afterEnd = (!this.isEmpty(sValue) ? sValue : this.__afterEnd); break; case 'args': this.args = (!this.isEmpty(sValue) ? sValue : ''); break; case 'scrollbars': this.scrolling = (/auto|yes|no/.test(sValue) ? sValue : this.__scrolling); break; case 'scrolling': this.scrolling = (/auto|yes|no/.test(sValue) ? sValue : this.__scrolling); break; case 'width': this.width = (/\d(%|px|)/.test(sValue) ? sValue : this.__width); break; case 'height': this.height = (/\d(%|px|)/.test(sValue) ? sValue : this.__height); break; case 'loopplayback': this.loopPlayback = (/true|false/.test(sValue) ? (sValue == 'true') : this.__loopPlayback); break; case 'autoplay': this.autoPlay = (/true|false/.test(sValue) ? (sValue == 'true') : this.__autoPlay); break; case 'autoembed': this.autoEmbed = (/true|false/.test(sValue) ? (sValue == 'true') : this.__autoEmbed); break; case 'inline': this.inline = (/true|false/.test(sValue) ? (sValue == 'true') : false); case 'slideinterval': this.slideInterval = (/\d/.test(sValue) ? parseInt(sValue) : this.__slideInterval); break; case 'shownavigation': this.showNavigation = (/true|false/.test(sValue) ? (sValue == 'true') : this.__showNavigation); break; case 'showclose': this.showClose = (/true|false/.test(sValue) ? (sValue == 'true') : this.__showClose); break; case 'showdetails': this.showDetails = (/true|false/.test(sValue) ? (sValue == 'true') : this.__showDetails); break; case 'showplaypause': this.showPlayPause = (/true|false/.test(sValue) ? (sValue == 'true') : this.__showPlayPause); break; case 'autoend': this.autoEnd = (/true|false/.test(sValue) ? (sValue == 'true') : this.__autoEnd); break; case 'pauseonnextclick': this.pauseOnNextClick = (/true|false/.test(sValue) ? (sValue == 'true') : this.__pauseOnNextClick); break; case 'pauseonprevclick': this.pauseOnPrevClick = (/true|false/.test(sValue) ? (sValue == 'true') : this.__pauseOnPrevClick); break; case 'loopslideshow': this.loopSlideshow = (/true|false/.test(sValue) ? (sValue == 'true') : this.__loopSlideshow); break; } } }; Lytebox.prototype.buildObject = function(w, h, url) { var object = ''; var classId = ''; var codebase = ''; var pluginsPage = ''; var auto = this.autoPlay ? 'true' : 'false'; var loop = this.loopPlayback ? 'true' : 'false'; var type = url.match(/.mov|.avi|.wmv|.mpg|.mpeg|.swf/i); switch(type[0]) { case '.mov': codebase = 'http://www.apple.com/qtactivex/qtplugin.cab'; pluginsPage = 'http://www.apple.com/quicktime/'; classId = 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B'; object = '' + '' + '' + '' + '' + '' + ''; if (this.getQuicktimeVersion() <= 0) { object = '
    ' + '

    QUICKTIME PLAYER

    ' + '

    Content on this page requires a newer version of QuickTime. Please click the image link below to download and install the latest version.

    ' + '

    Get QuickTime

    ' + '
    '; } break; case '.avi': case '.mpg': case '.mpeg': case '.wmv': classId = 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B'; object = '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + ''; break; case '.swf': classId = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'; object = '' + '' + '' + '' + '' + '' + '' + '' + '' + '
    ' + '

    FLASH PLAYER

    ' + '

    Content on this page requires a newer version of Adobe Flash Player. Please click the image link below to download and install the latest version.

    ' + '

    Get Adobe Flash player

    ' + '
    ' + '' + '
    ' + '' + '
    '; break; } return object; }; Lytebox.prototype.getQuicktimeVersion = function() { var agent = navigator.userAgent.toLowerCase(); var version = -1; if (navigator.plugins != null && navigator.plugins.length > 0) { for (i=0; i < navigator.plugins.length; i++ ) { var plugin = navigator.plugins[i]; if (plugin.name.indexOf('QuickTime') > -1) { version = parseFloat(plugin.name.substring(18)); } } } else if (this.autoEmbed && agent.indexOf('msie') != -1 && parseInt(navigator.appVersion) >= 4 && agent.indexOf('win') != -1 && agent.indexOf('16bit') == -1) { var control = null; try { control = new ActiveXObject('QuickTime.QuickTime'); } catch (e) { } if (control) { isInstalled = true; } try { control = new ActiveXObject('QuickTimeCheckObject.QuickTimeCheck'); } catch (e) { return; } if (control) { isInstalled = true; version = control.QuickTimeVersion.toString(16); version = version.substring(0, 1) + '.' + version.substring(1, 3); version = parseInt(version); } } return version; }; Lytebox.prototype.findPos = function(el) { if (this.ie && this.doc.compatMode == 'BackCompat') { return [0, 16, 12]; } var left = 0; var top = 0; var height = 0; height = el.offsetHeight + 6; if (el.offsetParent) { do { left += el.offsetLeft; top += el.offsetTop; } while (el = el.offsetParent); } return [left, top, height]; }; Lytebox.prototype.isMobile = function() { var ua = navigator.userAgent; return (ua.match(/ipad/i) != null) || (ua.match(/ipod/i) != null) || (ua.match(/iphone/i) != null) || (ua.match(/android/i) != null) || (ua.match(/opera mini/i) != null) || (ua.match(/blackberry/i) != null) || (ua.match(/(pre\/|palm os|palm|hiptop|avantgo|plucker|xiino|blazer|elaine)/i) != null) || (ua.match(/(iris|3g_t|windows ce|opera mobi|windows ce; smartphone;|windows ce; iemobile)/i) != null) || (ua.match(/(mini 9.5|vx1000|lge |m800|e860|u940|ux840|compal|wireless| mobi|ahong|lg380|lgku|lgu900|lg210|lg47|lg920|lg840|lg370|sam-r|mg50|s55|g83|t66|vx400|mk99|d615|d763|el370|sl900|mp500|samu3|samu4|vx10|xda_|samu5|samu6|samu7|samu9|a615|b832|m881|s920|n210|s700|c-810|_h797|mob-x|sk16d|848b|mowser|s580|r800|471x|v120|rim8|c500foma:|160x|x160|480x|x640|t503|w839|i250|sprint|w398samr810|m5252|c7100|mt126|x225|s5330|s820|htil-g1|fly v71|s302|-x113|novarra|k610i|-three|8325rc|8352rc|sanyo|vx54|c888|nx250|n120|mtk |c5588|s710|t880|c5005|i;458x|p404i|s210|c5100|teleca|s940|c500|s590|foma|samsu|vx8|vx9|a1000|_mms|myx|a700|gu1100|bc831|e300|ems100|me701|me702m-three|sd588|s800|8325rc|ac831|mw200|brew |d88|htc\/|htc_touch|355x|m50|km100|d736|p-9521|telco|sl74|ktouch|m4u\/|me702|8325rc|kddi|phone|lg |sonyericsson|samsung|240x|x320|vx10|nokia|sony cmd|motorola|up.browser|up.link|mmp|symbian|smartphone|midp|wap|vodafone|o2|pocket|kindle|mobile|psp|treo)/i) != null); }; Lytebox.prototype.validate = function(args) { var reTest = sName = ''; var bValid = false; var oElement = this.isEmpty(args.id) ? (this.isEmpty(args.element) ? null : args.element) : document.getElementById(args.id); var sInput = this.isEmpty(args.value) ? '' : String(args.value); var sType = this.isEmpty(args.type) ? '' : String(args.type).toLowerCase(); var sRegex = this.isEmpty(args.regex) ? '' : args.regex; var sCCType = (/visa|mc|amex|diners|discover|jcb/.test(args.ccType) ? args.ccType : ''); var sImageType = this.isEmpty(args.imageType) ? '' : String(args.imageType.toLowerCase()); var iMin = (/^\d+$/.test(args.min) ? parseInt(args.min) : 0); var iMax = (/^\d+$/.test(args.max) ? parseInt(args.max) : 0); var bInclusive = args.inclusive ? true : (/true|false/.test(args.inclusive) ? (args.inclusive == 'true') : true); var bAllowComma = args.allowComma ? true : (/true|false/.test(args.allowComma) ? (args.allowComma == 'true') : true); var bAllowWhitespace = args.allowWhiteSpace ? true : (/true|false/.test(args.allowWhiteSpace) ? (args.allowWhiteSpace == 'true') : true); if ((this.isEmpty(sInput) && this.isEmpty(oElement)) || (this.isEmpty(sType) && this.isEmpty(sRegex))) { return false; } var sInput = this.isEmpty(sInput) ? oElement.value : sInput; if (!this.isEmpty(sRegex)) { bValid = sRegex.test(sInput); } else { switch(sType) { case 'alnum': bValid = (bAllowWhitespace ? /^[a-z0-9\s]+$/i.test(sInput) : /^[a-z0-9]+$/i.test(sInput)); break; case 'alpha': bValid = (bAllowWhitespace ? /^[a-z\s]+$/i.test(sInput) : /^[a-z]+$/i.test(sInput)); break; case 'between': var iInput = bAllowComma ? parseInt(sInput.replace(/\,/g,'')) : parseInt(sInput); bValid = (bInclusive ? (iInput >= iMin && iInput <= iMax) : (iInput > iMin && iInput < iMax)); break; case 'ccnum': if (this.isEmpty(sCCType)) { bValid = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/.test(sInput); break; } else { switch(sCCType) { case 'visa': bValid = /^4[0-9]{12}(?:[0-9]{3})?$/.test(sInput); break; case 'mc': bValid = /^5[1-5][0-9]{14}$/.test(sInput); break; case 'amex': bValid = /^3[47][0-9]{13}$/.test(sInput); break; case 'diners': bValid = /^3(?:0[0-5]|[68][0-9])[0-9]{11}$/.test(sInput); break; case 'discover': bValid = /^6(?:011|5[0-9]{2})[0-9]{12}$/.test(sInput); break; case 'jcb': bValid = /^(?:2131|1800|35\d{3})\d{11}$/.test(sInput); break; default: bValid = false; } } case 'date': var date = new Date(sInput); bValid = !(date.toString() == 'NaN' || date.toString() == 'Invalid Date'); break; case 'digits': bValid = /^\d+$/.test(sInput); break; case 'email': bValid = /^([a-z0-9_\.\-])+\@(([a-z0-9\-])+\.)+([a-z0-9]{2,4})+$/i.test(sInput); break; case 'float': bValid = /^[-+]?[0-9]*\.?[0-9]+$/.test(bAllowComma ? sInput.replace(/\,/g,'') : sInput); break; case 'image': if (this.isEmpty(sImageType)) { bValid = /^(png|jpg|jpeg|gif)$/i.test(sInput.split('.').pop()); break; } else { bValid = (sInput.split('.').pop().toLowerCase().match(sImageType) ? true : false); break; } case 'int': case 'integer': bValid = /^[-+]?\d+$/.test(sInput.replace(/\,/g,'')); break; case 'len': case 'length': bValid = (iMin == iMax) ? (sInput.length == iMin) : (sInput.length >= iMin && sInput.length <= iMax); break; case 'phone': bValid = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/.test(sInput); break; case 'notempty': bValid = !this.isEmpty(sInput); break; case 'ssn': bValid = /^[0-9]{3}\-?[0-9]{2}\-?[0-9]{4}$/.test(sInput); break; case 'url': bValid = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»""'']))/i.test(sInput); break; case 'zip': bValid = /^\d{5}$|^\d{5}-\d{4}$/.test(sInput); break; } } return bValid; }; Lytebox.prototype.ajax = function(args) { var iIndex = this.http.length; var oRequest = this.getRequestObject(); this.http[iIndex] = oRequest; var oHttpArgs = args; oHttpArgs.index = iIndex; oHttpArgs.method = !(/get|post/i.test(oHttpArgs.method)) ? 'get' : oHttpArgs.method; oHttpArgs.cache = !(/true|false/.test(oHttpArgs.cache)) ? true : (oHttpArgs.cache == 'true' || oHttpArgs.cache); if (!this.isEmpty(oHttpArgs.timeout) && (/^\d+$/.test(oHttpArgs.timeout))) { oHttpArgs.timerId = setTimeout("$lb.http["+iIndex+"].abort()", oHttpArgs.timeout); } oRequest.onreadystatechange = function() { return function() { if (oRequest.readyState == 4 && oRequest.status == 200) { if (document.getElementById(oHttpArgs.updateId)) { try { document.getElementById(oHttpArgs.updateId).innerHTML = oRequest.responseText; } catch(e) { alert(e.description ); }; } if (typeof oHttpArgs.success === 'function') { oHttpArgs.success(oRequest); } window.clearTimeout(oHttpArgs.timerId); $lb.http[oHttpArgs.index] = null; } else if (oRequest.readyState == 4 && oRequest.status != 200) { if (typeof oHttpArgs.fail === 'function') { oHttpArgs.fail(oRequest); } window.clearTimeout(oHttpArgs.timerId); $lb.http[oHttpArgs.index] = null; } } (oRequest, oHttpArgs); } if (oHttpArgs.method.toLowerCase() == 'post') { var oForm = document.getElementById(oHttpArgs.form); var bStripTags = !(/true|false/.test(args.stripTags)) ? false : (args.stripTags == 'true' || args.stripTags); var sParams = (oForm == null ? this.serialize({ name: oHttpArgs.form, stripTags: bStripTags }) : this.serialize({ element: oForm, stripTags: bStripTags })); var sTimestamp = (!oHttpArgs.cache ? ((/\&/.test(sParams)) ? '&' : '') + new Date().getTime() : ''); oRequest.open('post', oHttpArgs.url, true); oRequest.setRequestHeader('Content-type','application/x-www-form-urlencoded'); oRequest.send(sParams + sTimestamp); } else { var sTimestamp = (!oHttpArgs.cache ? ((/\?/.test(oHttpArgs.url)) ? '&' : '?') + new Date().getTime() : ''); oRequest.open('get', oHttpArgs.url + sTimestamp, true); oRequest.send(); } }; Lytebox.prototype.serialize = function(args) { var sParams = sValue = ''; var bStripTags = !(/true|false/.test(args.stripTags)) ? false : (args.stripTags == 'true' || args.stripTags); var oElements = this.isEmpty(args.id) ? (this.isEmpty(args.element) ? null : args.element) : document.getElementById(args.id); if (oElements == null) { for (var i = 0; i < document.forms.length; i++) { if (document.forms[i].name == args.name) { oElements = document.forms[i].elements; } } } for (var i = 0; i < oElements.length; i++) { if ((oElements[i].type == 'checkbox' && !oElements[i].checked) || (oElements[i].type == 'radio' && !oElements[i].checked) || (oElements[i].disabled) || (oElements[i].name == '') || (oElements[i].type == 'reset')) { continue; } if (oElements[i].type == 'select-multiple') { for (var j = 0; j < oElements[i].options.length; j++) { if (oElements[i].options[j].selected == true) { sParams += (sParams == '' ? '' : '&') + oElements[i].name + '=' + encodeURIComponent(oElements[i].options[j].value); } } } else { sValue = bStripTags ? this.stripTags({ value: oElements[i].value }) : oElements[i].value; sParams += (sParams == '' ? '' : '&') + oElements[i].name + '=' + encodeURIComponent(sValue); } } return sParams; }; Lytebox.prototype.getRequestObject = function () { var oReq = null; if (window.XMLHttpRequest) { try { oReq = new XMLHttpRequest(); } catch (e) { } } else if (typeof ActiveXObject != 'undefined') { try { oReq = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { oReq = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { } } } return oReq; }; Lytebox.prototype.isEmpty = function(args) { var sValue = ''; try { sValue = this.isEmpty(args.value) ? args : args.value; } catch(e) { sValue = args; } return (this.trim(sValue) == '' || sValue == 'null' || sValue == null || typeof(sValue) == 'undefined'); }; Lytebox.prototype.stripTags = function(args) { var oElement = this.isEmpty(args.id) ? (this.isEmpty(args.element) ? null : args.element) : document.getElementById(args.id); if (!this.isEmpty(oElement)) { oElement.value = String(oElement.value).replace(/(<([^>]+)>)/ig, ''); } else { var sValue = ''; try { sValue = this.isEmpty(args.value) ? args : args.value; } catch(e) { sValue = args; } return (this.trim(sValue) == '[object Object]') ? '' : String(sValue).replace(/(<([^>]+)>)/ig, ''); } }; Lytebox.prototype.trim = function(args) { var sValue = ''; try { sValue = this.isEmpty(args.value) ? args : args.value; } catch(e) { sValue = args; } return String(sValue).replace(/^\s+|\s+$/g, ''); }; Lytebox.prototype.capitalize = function (args) { return String(args.value ? args.value : args).replace( /(^|\s)([a-z])/g , function(m,p1,p2){return p1+p2.toUpperCase();}); }; Lytebox.prototype.hasClass = function (args) { var sClass = this.isEmpty(args.name) ? '' : args.name; var oElement = this.isEmpty(args.id) ? (this.isEmpty(args.element) ? null : args.element) : document.getElementById(args.id); return new RegExp('(\\s|^)' + sClass + '(\\s|$)').test(oElement.className); }; Lytebox.prototype.addClass = function (args) { var sClass = this.isEmpty(args.name) ? '' : args.name; var oElement = this.isEmpty(args.id) ? (this.isEmpty(args.element) ? null : args.element) : document.getElementById(args.id); var aClasses = sClass.split(' '); for (var i = 0; i < aClasses.length; i++) { if (!this.hasClass({ element: oElement, name: aClasses[i] })) { oElement.className += ' ' + aClasses[i]; } } }; Lytebox.prototype.removeClass = function (args) { var sClass = this.isEmpty(args.name) ? '' : args.name; var oElement = this.isEmpty(args.id) ? (this.isEmpty(args.element) ? null : args.element) : document.getElementById(args.id); var aClasses = sClass.split(' '); for (var i = 0; i < aClasses.length; i++) { if (this.hasClass({ element: oElement, name: aClasses[i] })) { oElement.className = oElement.className.replace(new RegExp('(\\s|^)' + aClasses[i] + '(\\s|$)'), ' ').replace(/\s+/g, ' ').replace(/^\s|\s$/, ''); } } }; if (window.addEventListener) { window.addEventListener("load", initLytebox, false); } else if (window.attachEvent) { window.attachEvent("onload", initLytebox); } else { window.onload = function() {initLytebox();} } function initLytebox() { myLytebox = $lb = new Lytebox(true, $lb.http); } myLytebox = $lb = new Lytebox(false); ]==] Str_lytebox_css = [==[ #lbOverlay { position: fixed; top: 0; left: 0; z-index: 99997; width: 100%; height: 100%; } #lbOverlay.black { background-color: #000000; } #lbOverlay.grey { background-color: #000000; } #lbOverlay.red { background-color: #330000; } #lbOverlay.green { background-color: #003300; } #lbOverlay.blue { background-color: #011D50; } #lbOverlay.gold { background-color: #666600; } #lbOverlay.orange { background-color: #FFBB48; } #lbMain { position: absolute; left: 0; width: 100%; z-index: 99998; text-align: center; line-height: 0; display:-moz-inline-stack; } #lbMain a img { border: 1px solid #ffffff; } #lbOuterContainer { position: relative; background-color: #fff; width: 200px; height: 200px; margin: 0 auto; } #lbOuterContainer.black { border: 2px solid #CCCCCC; background-color: #000000; } #lbOuterContainer.grey { border: 2px solid #888888; } #lbOuterContainer.red { border: 2px solid #DD0000; } #lbOuterContainer.green { border: 2px solid #00B000; } #lbOuterContainer.blue { border: 2px solid #5F89D8; } #lbOuterContainer.gold { border: 2px solid #B0B000; } #lbOuterContainer.orange { border: 2px solid #D15211; } #lbTopContainer, #lbBottomContainer { font: 0.85em Verdana, Helvetica, sans-serif; background-color: #fff; width: 100%; line-height: 1.4em; font-size: 0.9em; overflow: hidden; margin: 0 auto; padding: 0; position: relative; z-index: 14; display: none; } #lbTopContainer { overflow: hidden; margin-top: 5px; } #lbTopContainer.black, #lbBottomContainer.black { background-color: #000000; } #lbTopContainer.grey, #lbTopContainer.red, #lbTopContainer.green, #lbTopContainer.blue, #lbTopContainer.gold, #lbTopContainer.orange, #lbBottomContainer.grey, #lbBottomContainer.red, #lbBottomContainer.green, #lbBottomContainer.blue, #lbBottomContainer.gold, #lbBottomContainer.orange { background-color: #ffffff; } #lbImage, #lbIframe { border: none; } #lbImage.black, #lbIframe.black { border: 1px solid #CCCCCC; } #lbImage.grey, #lbIframe.grey { border: 1px solid #888888; } #lbImage.red, #lbIframe.red { border: 1px solid #DD0000; } #lbImage.green, #lbIframe.green { border: 1px solid #00B000; } #lbImage.blue, #lbIframe.blue { border: 1px solid #5F89D8; } #lbImage.gold, #lbIframe.gold { border: 1px solid #B0B000; } #lbImage.orange, #lbIframe.orange { border: 1px solid #D15211; } #lbImageContainer, #lbIframeContainer { padding: 10px; z-index: 12; } #lbLoading { height: 100%; width: 100%; margin-top: -10px; background: url('loading_white.gif') center no-repeat; } #lbLoading.black { background: url('loading_black.gif') center no-repeat; } #lbHoverNav { position: absolute; top: 0; left: 0; height: 100%; width: 100%; z-index: 10; } #lbImageContainer>#lbHoverNav { left: 0; } #lbHoverNav a { outline: none; } #lbPrevHov { width: 48%; height: 100%; background: transparent url('blank.gif') no-repeat; display: block; left: 0; float: left; margin-left: 3px; border: none !important; } #lbPrevHov.black:hover, #lbPrevHov.black:visited { background: url('prev_black_t.png') left 30% no-repeat; } #lbPrevHov.grey:hover, #lbPrevHov.grey:visited { background: url('prev_grey_t.png') left 30% no-repeat; } #lbPrevHov.red:hover, #lbPrevHov.red:visited { background: url('prev_red_t.png') left 30% no-repeat; } #lbPrevHov.green:hover, #lbPrevHov.green:visited { background: url('prev_green_t.png') left 30% no-repeat; } #lbPrevHov.blue:hover, #lbPrevHov.blue:visited { background: url('prev_blue_t.png') left 30% no-repeat; } #lbPrevHov.gold:hover, #lbPrevHov.gold:visited { background: url('prev_gold_t.png') left 30% no-repeat; } #lbPrevHov.orange:hover, #lbPrevHov.orange:visited { background: url('prev_orange_t.png') left 30% no-repeat; } #lbNextHov { width: 48%; height: 100%; background: transparent url('blank.gif') no-repeat; display: block; right: 0; float: right; margin-right: 3px; border: none !important; } #lbNextHov.black:hover, #lbNextHov.black:visited { background: url('next_black_t.png') right 30% no-repeat; } #lbNextHov.grey:hover, #lbNextHov.grey:visited { background: url('next_grey_t.png') right 30% no-repeat; } #lbNextHov.red:hover, #lbNextHov.red:visited { background: url('next_red_t.png') right 30% no-repeat; } #lbNextHov.green:hover, #lbNextHov.green:visited { background: url('next_green_t.png') right 30% no-repeat; } #lbNextHov.blue:hover, #lbNextHov.blue:visited { background: url('next_blue_t.png') right 30% no-repeat; } #lbNextHov.gold:hover, #lbNextHov.gold:visited { background: url('next_gold_t.png') right 30% no-repeat; } #lbNextHov.orange:hover, #lbNextHov.orange:visited { background: url('next_orange_t.png') right 30% no-repeat; } #lbPrev, #lbPrevTop { width: 26px; height: 28px; float: right; margin: 0 0 1px 8px; border: none !important; } #lbPrev.black, #lbPrevTop.black { background: url('prev_black.png') no-repeat; } #lbPrev.blackOff, #lbPrevTop.blackOff { background: url('prev_black_off.png') no-repeat; cursor: default; } #lbPrev.grey, #lbPrevTop.grey { background: url('prev_grey.png') no-repeat; } #lbPrev.greyOff, #lbPrevTop.greyOff { background: url('prev_grey_off.png') no-repeat; cursor: default; } #lbPrev.red, #lbPrevTop.red { background: url('prev_red.png') no-repeat; } #lbPrev.redOff, #lbPrevTop.redOff { background: url('prev_red_off.png') no-repeat; cursor: default; } #lbPrev.green, #lbPrevTop.green { background: url('prev_green.png') no-repeat; } #lbPrev.greenOff, #lbPrevTop.greenOff { background: url('prev_green_off.png') no-repeat; cursor: default; } #lbPrev.blue, #lbPrevTop.blue { background: url('prev_blue.png') no-repeat; } #lbPrev.blueOff, #lbPrevTop.blueOff { background: url('prev_blue_off.png') no-repeat; cursor: default; } #lbPrev.gold, #lbPrevTop.gold { background: url('prev_gold.png') no-repeat; } #lbPrev.goldOff, #lbPrevTop.goldOff { background: url('prev_gold_off.png') no-repeat; cursor: default; } #lbPrev.orange, #lbPrevTop.orange { background: url('prev_orange.png') no-repeat; } #lbPrev.orangeOff, #lbPrevTop.orangeOff { background: url('prev_orange_off.png') no-repeat; cursor: default; } #lbNext, #lbNextTop { width: 26px; height: 28px; float: right; margin: 0 0 1px 8px; border: none !important; } #lbNext.black, #lbNextTop.black { background: url('next_black.png') no-repeat; } #lbNext.blackOff, #lbNextTop.blackOff { background: url('next_black_off.png') no-repeat; cursor: default; } #lbNext.grey, #lbNextTop.grey { background: url('next_grey.png') no-repeat; } #lbNext.greyOff, #lbNextTop.greyOff { background: url('next_grey_off.png') no-repeat; cursor: default; } #lbNext.red, #lbNextTop.red { background: url('next_red.png') no-repeat; } #lbNext.redOff, #lbNextTop.redOff { background: url('next_red_off.png') no-repeat; cursor: default; } #lbNext.green, #lbNextTop.green { background: url('next_green.png') no-repeat; } #lbNext.greenOff, #lbNextTop.greenOff { background: url('next_green_off.png') no-repeat; cursor: default; } #lbNext.blue, #lbNextTop.blue { background: url('next_blue.png') no-repeat; } #lbNext.blueOff, #lbNextTop.blueOff { background: url('next_blue_off.png') no-repeat; cursor: default; } #lbNext.gold, #lbNextTop.gold { background: url('next_gold.png') no-repeat; } #lbNext.goldOff, #lbNextTop.goldOff { background: url('next_gold_off.png') no-repeat; cursor: default; } #lbNext.orange, #lbNextTop.orange { background: url('next_orange.png') no-repeat; } #lbNext.orangeOff, #lbNextTop.orangeOff { background: url('next_orange_off.png') no-repeat; cursor: default; } #lbTopData, #lbBottomData { float: left; text-align: left; padding-left: 10px; } #lbBottomData { padding-bottom: 0.5em; } #lbBottomData.black, #lbTopData.black { color: #ffffff; } #lbBottomData.grey, #lbTopData.grey { color: #333333; } #lbBottomData.red, #lbTopData.red { color: #620000; } #lbBottomData.green, #lbTopData.green { color: #003300; } #lbBottomData.blue, #lbTopData.blue { color: #01379E; } #lbBottomData.gold, #lbTopData.gold { color: #666600; } #lbBottomData.orange, #lbTopData.orange { color: #D15211; } #lbTopNav, #lbBottomNav { float: right; text-align: right; padding-right: 10px; } #lbNumTop, #lbNumBottom { font-style: italic; } #lbDescBottom { display: block; } #lbTitleTop, #lbTopNav { margin-top: 0.3em; } #lbTitleTop, #lbTitleBottom { display: block; font-weight: bold; } #lbClose, #lbCloseTop { width: 64px; height: 28px; float: right; margin: 0 0 1px 8px; border: none !important; } #lbClose.black, #lbCloseTop.black { background: url('close_black.png') no-repeat; } #lbClose.grey, #lbCloseTop.grey { background: url('close_grey.png') no-repeat; } #lbClose.red, #lbCloseTop.red { background: url('close_red.png') no-repeat; } #lbClose.green, #lbCloseTop.green { background: url('close_green.png') no-repeat; } #lbClose.blue, #lbCloseTop.blue { background: url('close_blue.png') no-repeat; } #lbClose.gold, #lbCloseTop.gold { background: url('close_gold.png') no-repeat; } #lbClose.orange, #lbCloseTop.orange { background: url('close_orange.png') no-repeat; } #lbPrint, #lbPrintTop { width: 26px; height: 28px; float: right; margin: 0 0 1px 8px; border: none !important; } #lbPrint.black, #lbPrintTop.black { background: url('print_black.png') no-repeat; } #lbPrint.grey, #lbPrintTop.grey { background: url('print_grey.png') no-repeat; } #lbPrint.red, #lbPrintTop.red { background: url('print_red.png') no-repeat; } #lbPrint.green, #lbPrintTop.green { background: url('print_green.png') no-repeat; } #lbPrint.blue, #lbPrintTop.blue { background: url('print_blue.png') no-repeat; } #lbPrint.gold, #lbPrintTop.gold { background: url('print_gold.png') no-repeat; } #lbPrint.orange, #lbPrintTop.orange { background: url('print_orange.png') no-repeat; } #lbPlay, #lbPlayTop { width: 26px; height: 28px; float: right; margin: 0 0 1px 8px; border: none !important; } #lbPlay.black, #lbPlayTop.black { background: url('play_black.png') no-repeat; } #lbPlay.grey, #lbPlayTop.grey { background: url('play_grey.png') no-repeat; } #lbPlay.red, #lbPlayTop.red { background: url('play_red.png') no-repeat; } #lbPlay.green, #lbPlayTop.green { background: url('play_green.png') no-repeat; } #lbPlay.blue, #lbPlayTop.blue { background: url('play_blue.png') no-repeat; } #lbPlay.gold, #lbPlayTop.gold { background: url('play_gold.png') no-repeat; } #lbPlay.orange, #lbPlayTop.orange { background: url('play_orange.png') no-repeat; } #lbPause, #lbPauseTop { width: 26px; height: 28px; float: right; margin: 0 0 1px 8px; border: none !important; } #lbPause.black, #lbPauseTop.black { background: url('pause_black.png') no-repeat; } #lbPause.grey, #lbPauseTop.grey { background: url('pause_grey.png') no-repeat; } #lbPause.red, #lbPauseTop.red { background: url('pause_red.png') no-repeat; } #lbPause.green, #lbPauseTop.green { background: url('pause_green.png') no-repeat; } #lbPause.blue, #lbPauseTop.blue { background: url('pause_blue.png') no-repeat; } #lbPause.gold, #lbPauseTop.gold { background: url('pause_gold.png') no-repeat; } #lbPause.orange, #lbPauseTop.orange { background: url('pause_orange.png') no-repeat; } /* Some extra padding on the bottom buttons so it's not too close to the border. */ #lbClose, #lbPrint, #lbPlay, #lbPause { margin: 0 0 6px 8px; } /* Lytetip */ * html a:hover { background: transparent; } .lytetip { outline: none; border-bottom: 1px dotted; z-index:24; text-decoration:none; } .lytetip span { color: #000000; position: absolute; top: 2em; left:0; padding: 0.5em 0.8em; font: 10pt "Trebuchet MS", Arial, Helvetica, sans-serif !important; background: #F4F5FB; border: 1px solid #888888; border-radius: 5px 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; -webkit-box-shadow: 1px 2px 3px 0px #949494;-moz-box-shadow: 1px 2px 3px 0px #949494;box-shadow: 1px 2px 3px 0px #949494; width: 240px; filter: alpha(opacity:95); KHTMLOpacity: 0.95; MozOpacity: 0.95; opacity: 0.95; text-align: left; display: none; } .lytetip:hover { z-index:25; color: #aaaaff; background:; text-decoration: none; } .lytetip:hover span { display: block; } .lytetip:hover em { font-size: 1.2em; font-weight: bold; display: block; padding: 0 0 0.6em 0; } .lytetip:hover .lbTipImg { border: 0; margin: -20px 0 0 -36px; float: left; position: absolute; height: 32px; width: 32px; } .lbErrorImg { background: url('error.png'); } .lbInfoImg { background: url('info.png'); } .lbHelpImg { background: url('help.png'); } .lbWarningImg { background: url('warning.png'); } span.lbCustom { padding: 0.5em 0.8em 0.5em 1.5em !important; } span.lbIEFix { padding: 0.5em 0.8em !important; } .lytetip .lbError { background: #FFE7D7; border: 1px solid #FF3334; } .lytetip .lbInfo, .lytetip .lbHelp { background: #D2EEF7; border: 1px solid #2BB0D7; } .lytetip .lbWarning { background: #FFFFAA; border: 1px solid #FFAD33; } ]==] Str_dojopop_js = [==[ /* Copyright (c) 2004-2009, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ /* This is a compiled version of Dojo, built for deployment and not for development. To get an editable version, please visit: Download by http://www.codefans.net http://dojotoolkit.org for documentation and information on getting the source. */ (function() { var _1 = null; if ((_1 || (typeof djConfig != "undefined" && djConfig.scopeMap)) && ( typeof window != "undefined")) { var _2 = "", _3 = "", _4 = "", _5 = {}, _6 = {}; _1 = _1 || djConfig.scopeMap; for (var i = 0; i < _1.length; i++) { var _8 = _1[i]; _2 += "var " + _8[0] + " = {}; " + _8[1] + " = " + _8[0] + ";" + _8[1] + "._scopeName = '" + _8[1] + "';"; _3 += (i == 0 ? "" : ",") + _8[0]; _4 += (i == 0 ? "" : ",") + _8[1]; _5[_8[0]] = _8[1]; _6[_8[1]] = _8[0]; } eval(_2 + "dojo._scopeArgs = [" + _4 + "];"); dojo._scopePrefixArgs = _3; dojo._scopePrefix = "(function(" + _3 + "){"; dojo._scopeSuffix = "})(" + _4 + ")"; dojo._scopeMap = _5; dojo._scopeMapRev = _6; }(function() { if (typeof this["loadFirebugConsole"] == "function") { this["loadFirebugConsole"](); } else { this.console = this.console || {}; var cn = ["assert", "count", "debug", "dir", "dirxml", "error", "group", "groupEnd", "info", "profile", "profileEnd", "time", "timeEnd", "trace", "warn", "log" ]; var i = 0, tn; while ((tn = cn[i++])) { if (!console[tn]) { (function() { var _c = tn + ""; console[_c] = ("log" in console) ? function() { var a = Array.apply({}, arguments); a.unshift(_c + ":"); console["log"](a.join(" ")); } : function() {}; })(); } } } if (typeof dojo == "undefined") { this.dojo = { _scopeName: "dojo", _scopePrefix: "", _scopePrefixArgs: "", _scopeSuffix: "", _scopeMap: {}, _scopeMapRev: {} }; } var d = dojo; if (typeof dijit == "undefined") { this.dijit = { _scopeName: "dijit" }; } if (typeof dojox == "undefined") { this.dojox = { _scopeName: "dojox" }; } if (!d._scopeArgs) { d._scopeArgs = [dojo, dijit, dojox]; } d.global = this; d.config = { isDebug: false, debugAtAllCosts: false }; if (typeof djConfig != "undefined") { for (var _f in djConfig) { d.config[_f] = djConfig[_f]; } } dojo.locale = d.config.locale; var rev = "$Rev: 16807 $".match(/\d+/); dojo.version = { major: 1, minor: 3, patch: 1, flag: "", revision: rev ? +rev[0] : NaN, toString: function() { with(d.version) { return major + "." + minor + "." + patch + flag + " (" + revision + ")"; } } }; if (typeof OpenAjax != "undefined") { OpenAjax.hub.registerLibrary(dojo._scopeName, "http://dojotoolkit.org", d.version.toString()); } var _11 = {}; dojo._mixin = function(obj, _13) { for (var x in _13) { if (_11[x] === undefined || _11[x] != _13[x]) { obj[x] = _13[x]; } } if (d.isIE && _13) { var p = _13.toString; if (typeof p == "function" && p != obj.toString && p != _11.toString && p != "\nfunction toString() {\n [native code]\n}\n" ) { obj.toString = _13.toString; } } return obj; }; dojo.mixin = function(obj, _17) { if (!obj) { obj = {}; } for (var i = 1, l = arguments.length; i < l; i++) { d._mixin(obj, arguments[i]); } return obj; }; dojo._getProp = function(_1a, _1b, _1c) { var obj = _1c || d.global; for (var i = 0, p; obj && (p = _1a[i]); i++) { if (i == 0 && this._scopeMap[p]) { p = this._scopeMap[p]; } obj = (p in obj ? obj[p] : (_1b ? obj[p] = {} : undefined)); } return obj; }; dojo.setObject = function(_20, _21, _22) { var _23 = _20.split("."), p = _23.pop(), obj = d._getProp(_23, true, _22); return obj && p ? (obj[p] = _21) : undefined; }; dojo.getObject = function(_26, _27, _28) { return d._getProp(_26.split("."), _27, _28); }; dojo.exists = function(_29, obj) { return !!d.getObject(_29, false, obj); }; dojo["eval"] = function(_2b) { return d.global.eval ? d.global.eval(_2b) : eval(_2b); }; d.deprecated = d.experimental = function() {}; })(); (function() { var d = dojo; d.mixin(d, { _loadedModules: {}, _inFlightCount: 0, _hasResource: {}, _modulePrefixes: { dojo: { name: "dojo", value: "." }, doh: { name: "doh", value: "../util/doh" }, tests: { name: "tests", value: "tests" } }, _moduleHasPrefix: function(_2d) { var mp = this._modulePrefixes; return !!(mp[_2d] && mp[_2d].value); }, _getModulePrefix: function(_2f) { var mp = this._modulePrefixes; if (this._moduleHasPrefix(_2f)) { return mp[_2f].value; } return _2f; }, _loadedUrls: [], _postLoad: false, _loaders: [], _unloaders: [], _loadNotifying: false }); dojo._loadPath = function(_31, _32, cb) { var uri = ((_31.charAt(0) == "/" || _31.match(/^\w+:/)) ? "" : this.baseUrl) + _31; try { return !_32 ? this._loadUri(uri, cb) : this._loadUriAndCheck( uri, _32, cb); } catch (e) { console.error(e); return false; } }; dojo._loadUri = function(uri, cb) { if (this._loadedUrls[uri]) { return true; } var _37 = this._getText(uri, true); if (!_37) { return false; } this._loadedUrls[uri] = true; this._loadedUrls.push(uri); if (cb) { _37 = "(" + _37 + ")"; } else { _37 = this._scopePrefix + _37 + this._scopeSuffix; } if (d.isMoz) { _37 += "\r\n//@ sourceURL=" + uri; } var _38 = d["eval"](_37); if (cb) { cb(_38); } return true; }; dojo._loadUriAndCheck = function(uri, _3a, cb) { var ok = false; try { ok = this._loadUri(uri, cb); } catch (e) { console.error("failed loading " + uri + " with error: " + e); } return !!(ok && this._loadedModules[_3a]); }; dojo.loaded = function() { this._loadNotifying = true; this._postLoad = true; var mll = d._loaders; this._loaders = []; for (var x = 0; x < mll.length; x++) { mll[x](); } this._loadNotifying = false; if (d._postLoad && d._inFlightCount == 0 && mll.length) { d._callLoaded(); } }; dojo.unloaded = function() { var mll = d._unloaders; while (mll.length) { (mll.pop())(); } }; d._onto = function(arr, obj, fn) { if (!fn) { arr.push(obj); } else { if (fn) { var _43 = (typeof fn == "string") ? obj[fn] : fn; arr.push(function() { _43.call(obj); }); } } }; dojo.addOnLoad = function(obj, _45) { d._onto(d._loaders, obj, _45); if (d._postLoad && d._inFlightCount == 0 && !d._loadNotifying) { d._callLoaded(); } }; var dca = d.config.addOnLoad; if (dca) { d.addOnLoad[(dca instanceof Array ? "apply" : "call")](d, dca); } dojo._modulesLoaded = function() { if (d._postLoad) { return; } if (d._inFlightCount > 0) { console.warn("files still in flight!"); return; } d._callLoaded(); }; dojo._callLoaded = function() { if (typeof setTimeout == "object" || (dojo.config.useXDomain && d.isOpera)) { if (dojo.isAIR) { setTimeout(function() { dojo.loaded(); }, 0); } else { setTimeout(dojo._scopeName + ".loaded();", 0); } } else { d.loaded(); } }; dojo._getModuleSymbols = function(_47) { var _48 = _47.split("."); for (var i = _48.length; i > 0; i--) { var _4a = _48.slice(0, i).join("."); if ((i == 1) && !this._moduleHasPrefix(_4a)) { _48[0] = "../" + _48[0]; } else { var _4b = this._getModulePrefix(_4a); if (_4b != _4a) { _48.splice(0, i, _4b); break; } } } return _48; }; dojo._global_omit_module_check = false; dojo.loadInit = function(_4c) { _4c(); }; dojo._loadModule = dojo.require = function(_4d, _4e) { _4e = this._global_omit_module_check || _4e; var _4f = this._loadedModules[_4d]; if (_4f) { return _4f; } var _50 = this._getModuleSymbols(_4d).join("/") + ".js"; var _51 = (!_4e) ? _4d : null; var ok = this._loadPath(_50, _51); if (!ok && !_4e) { throw new Error("Could not load '" + _4d + "'; last tried '" + _50 + "'"); } if (!_4e && !this._isXDomain) { _4f = this._loadedModules[_4d]; if (!_4f) { throw new Error("symbol '" + _4d + "' is not defined after loading '" + _50 + "'"); } } return _4f; }; dojo.provide = function(_53) { _53 = _53 + ""; return (d._loadedModules[_53] = d.getObject(_53, true)); }; dojo.platformRequire = function(_54) { var _55 = _54.common || []; var _56 = _55.concat(_54[d._name] || _54["default"] || []); for (var x = 0; x < _56.length; x++) { var _58 = _56[x]; if (_58.constructor == Array) { d._loadModule.apply(d, _58); } else { d._loadModule(_58); } } }; dojo.requireIf = function(_59, _5a) { if (_59 === true) { var _5b = []; for (var i = 1; i < arguments.length; i++) { _5b.push(arguments[i]); } d.require.apply(d, _5b); } }; dojo.requireAfterIf = d.requireIf; dojo.registerModulePath = function(_5d, _5e) { d._modulePrefixes[_5d] = { name: _5d, value: _5e }; }; dojo.requireLocalization = function(_5f, _60, _61, _62) { d.require("dojo.i18n"); d.i18n._requireLocalization.apply(d.hostenv, arguments); }; var ore = new RegExp( "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$" ); var ire = new RegExp( "^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$" ); dojo._Url = function() { var n = null; var _a = arguments; var uri = [_a[0]]; for (var i = 1; i < _a.length; i++) { if (!_a[i]) { continue; } var _69 = new d._Url(_a[i] + ""); var _6a = new d._Url(uri[0] + ""); if (_69.path == "" && !_69.scheme && !_69.authority && !_69.query) { if (_69.fragment != n) { _6a.fragment = _69.fragment; } _69 = _6a; } else { if (!_69.scheme) { _69.scheme = _6a.scheme; if (!_69.authority) { _69.authority = _6a.authority; if (_69.path.charAt(0) != "/") { var _6b = _6a.path.substring(0, _6a .path.lastIndexOf("/") + 1) + _69.path; var _6c = _6b.split("/"); for (var j = 0; j < _6c.length; j++) { if (_6c[j] == ".") { if (j == _6c.length - 1) { _6c[j] = ""; } else { _6c.splice(j, 1); j--; } } else { if (j > 0 && !(j == 1 && _6c[0] == "") && _6c[j] == ".." && _6c[j - 1] != "..") { if (j == (_6c.length - 1)) { _6c.splice(j, 1); _6c[j - 1] = ""; } else { _6c.splice(j - 1, 2); j -= 2; } } } } _69.path = _6c.join("/"); } } } } uri = []; if (_69.scheme) { uri.push(_69.scheme, ":"); } if (_69.authority) { uri.push("//", _69.authority); } uri.push(_69.path); if (_69.query) { uri.push("?", _69.query); } if (_69.fragment) { uri.push("#", _69.fragment); } } this.uri = uri.join(""); var r = this.uri.match(ore); this.scheme = r[2] || (r[1] ? "" : n); this.authority = r[4] || (r[3] ? "" : n); this.path = r[5]; this.query = r[7] || (r[6] ? "" : n); this.fragment = r[9] || (r[8] ? "" : n); if (this.authority != n) { r = this.authority.match(ire); this.user = r[3] || n; this.password = r[4] || n; this.host = r[6] || r[7]; this.port = r[9] || n; } }; dojo._Url.prototype.toString = function() { return this.uri; }; dojo.moduleUrl = function(_6f, url) { var loc = d._getModuleSymbols(_6f).join("/"); if (!loc) { return null; } if (loc.lastIndexOf("/") != loc.length - 1) { loc += "/"; } var _72 = loc.indexOf(":"); if (loc.charAt(0) != "/" && (_72 == -1 || _72 > loc.indexOf( "/"))) { loc = d.baseUrl + loc; } return new d._Url(loc, url); }; })(); if (typeof window != "undefined") { dojo.isBrowser = true; dojo._name = "browser"; (function() { var d = dojo; if (document && document.getElementsByTagName) { var _74 = document.getElementsByTagName("script"); var _75 = /dojo(\.xd)?\.js(\W|$)/i; for (var i = 0; i < _74.length; i++) { var src = _74[i].getAttribute("src"); if (!src) { continue; } var m = src.match(_75); if (m) { if (!d.config.baseUrl) { d.config.baseUrl = src.substring(0, m.index); } var cfg = _74[i].getAttribute("djConfig"); if (cfg) { var _7a = eval("({ " + cfg + " })"); for (var x in _7a) { dojo.config[x] = _7a[x]; } } break; } } } d.baseUrl = d.config.baseUrl; var n = navigator; var dua = n.userAgent, dav = n.appVersion, tv = parseFloat(dav); if (dua.indexOf("Opera") >= 0) { d.isOpera = tv; } if (dua.indexOf("AdobeAIR") >= 0) { d.isAIR = 1; } d.isKhtml = (dav.indexOf("Konqueror") >= 0) ? tv : 0; d.isWebKit = parseFloat(dua.split("WebKit/")[1]) || undefined; d.isChrome = parseFloat(dua.split("Chrome/")[1]) || undefined; var _80 = Math.max(dav.indexOf("WebKit"), dav.indexOf( "Safari"), 0); if (_80 && !dojo.isChrome) { d.isSafari = parseFloat(dav.split("Version/")[1]); if (!d.isSafari || parseFloat(dav.substr(_80 + 7)) <= 419.3) { d.isSafari = 2; } } if (dua.indexOf("Gecko") >= 0 && !d.isKhtml && !d.isWebKit) { d.isMozilla = d.isMoz = tv; } if (d.isMoz) { d.isFF = parseFloat(dua.split("Firefox/")[1] || dua.split( "Minefield/")[1] || dua.split("Shiretoko/")[ 1]) || undefined; } if (document.all && !d.isOpera) { d.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined; if (d.isIE >= 8 && document.documentMode != 5) { d.isIE = document.documentMode; } } if (dojo.isIE && window.location.protocol === "file:") { dojo.config.ieForceActiveXXhr = true; } var cm = document.compatMode; d.isQuirks = cm == "BackCompat" || cm == "QuirksMode" || d.isIE < 6; d.locale = dojo.config.locale || (d.isIE ? n.userLanguage : n.language).toLowerCase(); d._XMLHTTP_PROGIDS = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP", "Msxml2.XMLHTTP.4.0" ]; d._xhrObj = function() { var _82, _83; if (!dojo.isIE || !dojo.config.ieForceActiveXXhr) { try { _82 = new XMLHttpRequest(); } catch (e) {} } if (!_82) { for (var i = 0; i < 3; ++i) { var _85 = d._XMLHTTP_PROGIDS[i]; try { _82 = new ActiveXObject(_85); } catch (e) { _83 = e; } if (_82) { d._XMLHTTP_PROGIDS = [_85]; break; } } } if (!_82) { throw new Error("XMLHTTP not available: " + _83); } return _82; }; d._isDocumentOk = function(_86) { var _87 = _86.status || 0; return (_87 >= 200 && _87 < 300) || _87 == 304 || _87 == 1223 || (!_87 && (location.protocol == "file:" || location.protocol == "chrome:")); }; var _88 = window.location + ""; var _89 = document.getElementsByTagName("base"); var _8a = (_89 && _89.length > 0); d._getText = function(uri, _8c) { var _8d = this._xhrObj(); if (!_8a && dojo._Url) { uri = (new dojo._Url(_88, uri)).toString(); } if (d.config.cacheBust) { uri += ""; uri += (uri.indexOf("?") == -1 ? "?" : "&") + String(d.config.cacheBust).replace(/\W+/g, ""); } _8d.open("GET", uri, false); try { _8d.send(null); if (!d._isDocumentOk(_8d)) { var err = Error("Unable to load " + uri + " status:" + _8d.status); err.status = _8d.status; err.responseText = _8d.responseText; throw err; } } catch (e) { if (_8c) { return null; } throw e; } return _8d.responseText; }; var _w = window; var _90 = function(_91, fp) { var _93 = _w[_91] || function() {}; _w[_91] = function() { fp.apply(_w, arguments); _93.apply(_w, arguments); }; }; d._windowUnloaders = []; d.windowUnloaded = function() { var mll = d._windowUnloaders; while (mll.length) { (mll.pop())(); } }; var _95 = 0; d.addOnWindowUnload = function(obj, _97) { d._onto(d._windowUnloaders, obj, _97); if (!_95) { _95 = 1; _90("onunload", d.windowUnloaded); } }; var _98 = 0; d.addOnUnload = function(obj, _9a) { d._onto(d._unloaders, obj, _9a); if (!_98) { _98 = 1; _90("onbeforeunload", dojo.unloaded); } }; })(); dojo._initFired = false; dojo._loadInit = function(e) { dojo._initFired = true; var _9c = e && e.type ? e.type.toLowerCase() : "load"; if (arguments.callee.initialized || (_9c != "domcontentloaded" && _9c != "load")) { return; } arguments.callee.initialized = true; if ("_khtmlTimer" in dojo) { clearInterval(dojo._khtmlTimer); delete dojo._khtmlTimer; } if (dojo._inFlightCount == 0) { dojo._modulesLoaded(); } }; if (!dojo.config.afterOnLoad) { if (document.addEventListener) { if (dojo.isWebKit > 525 || dojo.isOpera || dojo.isFF >= 3 || (dojo.isMoz && dojo.config.enableMozDomContentLoaded === true)) { document.addEventListener("DOMContentLoaded", dojo._loadInit, null); } window.addEventListener("load", dojo._loadInit, null); } if (dojo.isAIR) { window.addEventListener("load", dojo._loadInit, null); } else { if ((dojo.isWebKit < 525) || dojo.isKhtml) { dojo._khtmlTimer = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { dojo._loadInit(); } }, 10); } } } if (dojo.isIE) { if (!dojo.config.afterOnLoad) { document.write("" + ""); } try { document.namespaces.add("v", "urn:schemas-microsoft-com:vml"); document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML); display:inline-block" ); } catch (e) {} } }(function() { var mp = dojo.config["modulePaths"]; if (mp) { for (var _9e in mp) { dojo.registerModulePath(_9e, mp[_9e]); } } })(); if (dojo.config.isDebug) { dojo.require("dojo._firebug.firebug"); } if (dojo.config.debugAtAllCosts) { dojo.config.useXDomain = true; dojo.require("dojo._base._loader.loader_xd"); dojo.require("dojo._base._loader.loader_debug"); dojo.require("dojo.i18n"); } if (!dojo._hasResource["dojo._base.lang"]) { dojo._hasResource["dojo._base.lang"] = true; dojo.provide("dojo._base.lang"); dojo.isString = function(it) { return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); }; dojo.isArray = function(it) { return it && (it instanceof Array || typeof it == "array"); }; dojo.isFunction = (function() { var _a1 = function(it) { var t = typeof it; return it && (t == "function" || it instanceof Function); }; return dojo.isSafari ? function(it) { if (typeof it == "function" && it == "[object NodeList]") { return false; } return _a1(it); } : _a1; })(); dojo.isObject = function(it) { return it !== undefined && (it === null || typeof it == "object" || dojo.isArray(it) || dojo.isFunction(it) ); }; dojo.isArrayLike = function(it) { var d = dojo; return it && it !== undefined && !d.isString(it) && !d.isFunction( it) && !(it.tagName && it.tagName.toLowerCase() == "form") && (d.isArray(it) || isFinite(it.length)); }; dojo.isAlien = function(it) { return it && !dojo.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); }; dojo.extend = function(_a9, _aa) { for (var i = 1, l = arguments.length; i < l; i++) { dojo._mixin(_a9.prototype, arguments[i]); } return _a9; }; dojo._hitchArgs = function(_ad, _ae) { var pre = dojo._toArray(arguments, 2); var _b0 = dojo.isString(_ae); return function() { var _b1 = dojo._toArray(arguments); var f = _b0 ? (_ad || dojo.global)[_ae] : _ae; return f && f.apply(_ad || this, pre.concat(_b1)); }; }; dojo.hitch = function(_b3, _b4) { if (arguments.length > 2) { return dojo._hitchArgs.apply(dojo, arguments); } if (!_b4) { _b4 = _b3; _b3 = null; } if (dojo.isString(_b4)) { _b3 = _b3 || dojo.global; if (!_b3[_b4]) { throw (["dojo.hitch: scope[\"", _b4, "\"] is null (scope=\"", _b3, "\")" ].join("")); } return function() { return _b3[_b4].apply(_b3, arguments || []); }; } return !_b3 ? _b4 : function() { return _b4.apply(_b3, arguments || []); }; }; dojo.delegate = dojo._delegate = (function() { function TMP() {}; return function(obj, _b7) { TMP.prototype = obj; var tmp = new TMP(); if (_b7) { dojo._mixin(tmp, _b7); } return tmp; }; })(); (function() { var _b9 = function(obj, _bb, _bc) { return (_bc || []).concat(Array.prototype.slice.call( obj, _bb || 0)); }; var _bd = function(obj, _bf, _c0) { var arr = _c0 || []; for (var x = _bf || 0; x < obj.length; x++) { arr.push(obj[x]); } return arr; }; dojo._toArray = dojo.isIE ? function(obj) { return ((obj.item) ? _bd : _b9).apply(this, arguments); } : _b9; })(); dojo.partial = function(_c4) { var arr = [null]; return dojo.hitch.apply(dojo, arr.concat(dojo._toArray( arguments))); }; dojo.clone = function(o) { if (!o) { return o; } if (dojo.isArray(o)) { var r = []; for (var i = 0; i < o.length; ++i) { r.push(dojo.clone(o[i])); } return r; } if (!dojo.isObject(o)) { return o; } if (o.nodeType && o.cloneNode) { return o.cloneNode(true); } if (o instanceof Date) { return new Date(o.getTime()); } r = new o.constructor(); for (i in o) { if (!(i in r) || r[i] != o[i]) { r[i] = dojo.clone(o[i]); } } return r; }; dojo.trim = String.prototype.trim ? function(str) { return str.trim(); } : function(str) { return str.replace(/^\s\s*/, "").replace(/\s\s*$/, ""); }; } if (!dojo._hasResource["dojo._base.declare"]) { dojo._hasResource["dojo._base.declare"] = true; dojo.provide("dojo._base.declare"); dojo.declare = function(_cb, _cc, _cd) { var dd = arguments.callee, _cf; if (dojo.isArray(_cc)) { _cf = _cc; _cc = _cf.shift(); } if (_cf) { dojo.forEach(_cf, function(m, i) { if (!m) { throw (_cb + ": mixin #" + i + " is null"); } _cc = dd._delegate(_cc, m); }); } var _d2 = dd._delegate(_cc); _cd = _cd || {}; _d2.extend(_cd); dojo.extend(_d2, { declaredClass: _cb, _constructor: _cd.constructor }); _d2.prototype.constructor = _d2; return dojo.setObject(_cb, _d2); }; dojo.mixin(dojo.declare, { _delegate: function(_d3, _d4) { var bp = (_d3 || 0).prototype, mp = (_d4 || 0).prototype, dd = dojo.declare; var _d8 = dd._makeCtor(); dojo.mixin(_d8, { superclass: bp, mixin: mp, extend: dd._extend }); if (_d3) { _d8.prototype = dojo._delegate(bp); } dojo.extend(_d8, dd._core, mp || 0, { _constructor: null, preamble: null }); _d8.prototype.constructor = _d8; _d8.prototype.declaredClass = (bp || 0).declaredClass + "_" + (mp || 0).declaredClass; return _d8; }, _extend: function(_d9) { var i, fn; for (i in _d9) { if (dojo.isFunction(fn = _d9[i]) && !0[i]) { fn.nom = i; fn.ctor = this; } } dojo.extend(this, _d9); }, _makeCtor: function() { return function() { this._construct(arguments); }; }, _core: { _construct: function(_dc) { var c = _dc.callee, s = c.superclass, ct = s && s.constructor, m = c.mixin, mct = m && m.constructor, a = _dc, ii, fn; if (a[0]) { if (((fn = a[0].preamble))) { a = fn.apply(this, a) || a; } } if ((fn = c.prototype.preamble)) { a = fn.apply(this, a) || a; } if (ct && ct.apply) { ct.apply(this, a); } if (mct && mct.apply) { mct.apply(this, a); } if ((ii = c.prototype._constructor)) { ii.apply(this, _dc); } if (this.constructor.prototype == c.prototype && (ct = this.postscript)) { ct.apply(this, _dc); } }, _findMixin: function(_e5) { var c = this.constructor, p, m; while (c) { p = c.superclass; m = c.mixin; if (m == _e5 || (m instanceof _e5.constructor)) { return p; } if (m && m._findMixin && (m = m._findMixin( _e5))) { return m; } c = p && p.constructor; } }, _findMethod: function(_e9, _ea, _eb, has) { var p = _eb, c, m, f; do { c = p.constructor; m = c.mixin; if (m && (m = this._findMethod(_e9, _ea, m, has))) { return m; } if ((f = p[_e9]) && (has == (f == _ea))) { return p; } p = c.superclass; } while (p); return !has && (p = this._findMixin(_eb)) && this._findMethod(_e9, _ea, p, has); }, inherited: function(_f1, _f2, _f3) { var a = arguments; if (!dojo.isString(a[0])) { _f3 = _f2; _f2 = _f1; _f1 = _f2.callee.nom; } a = _f3 || _f2; var c = _f2.callee, p = this.constructor.prototype, fn, mp; if (this[_f1] != c || p[_f1] == c) { mp = (c.ctor || 0).superclass || this._findMethod( _f1, c, p, true); if (!mp) { throw (this.declaredClass + ": inherited method \"" + _f1 + "\" mismatch"); } p = this._findMethod(_f1, c, mp, false); } fn = p && p[_f1]; if (!fn) { throw (mp.declaredClass + ": inherited method \"" + _f1 + "\" not found"); } return fn.apply(this, a); } } }); } if (!dojo._hasResource["dojo._base.connect"]) { dojo._hasResource["dojo._base.connect"] = true; dojo.provide("dojo._base.connect"); dojo._listener = { getDispatcher: function() { return function() { var ap = Array.prototype, c = arguments.callee, ls = c._listeners, t = c.target; var r = t && t.apply(this, arguments); var lls; lls = [].concat(ls); for (var i in lls) { if (!(i in ap)) { lls[i].apply(this, arguments); } } return r; }; }, add: function(_100, _101, _102) { _100 = _100 || dojo.global; var f = _100[_101]; if (!f || !f._listeners) { var d = dojo._listener.getDispatcher(); d.target = f; d._listeners = []; f = _100[_101] = d; } return f._listeners.push(_102); }, remove: function(_105, _106, _107) { var f = (_105 || dojo.global)[_106]; if (f && f._listeners && _107--) { delete f._listeners[_107]; } } }; dojo.connect = function(obj, _10a, _10b, _10c, _10d) { var a = arguments, args = [], i = 0; args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]); var a1 = a[i + 1]; args.push(dojo.isString(a1) || dojo.isFunction(a1) ? a[i++] : null, a[i++]); for (var l = a.length; i < l; i++) { args.push(a[i]); } return dojo._connect.apply(this, args); }; dojo._connect = function(obj, _113, _114, _115) { var l = dojo._listener, h = l.add(obj, _113, dojo.hitch(_114, _115)); return [obj, _113, h, l]; }; dojo.disconnect = function(_118) { if (_118 && _118[0] !== undefined) { dojo._disconnect.apply(this, _118); delete _118[0]; } }; dojo._disconnect = function(obj, _11a, _11b, _11c) { _11c.remove(obj, _11a, _11b); }; dojo._topics = {}; dojo.subscribe = function(_11d, _11e, _11f) { return [_11d, dojo._listener.add(dojo._topics, _11d, dojo.hitch( _11e, _11f))]; }; dojo.unsubscribe = function(_120) { if (_120) { dojo._listener.remove(dojo._topics, _120[0], _120[1]); } }; dojo.publish = function(_121, args) { var f = dojo._topics[_121]; if (f) { f.apply(this, args || []); } }; dojo.connectPublisher = function(_124, obj, _126) { var pf = function() { dojo.publish(_124, arguments); }; return (_126) ? dojo.connect(obj, _126, pf) : dojo.connect( obj, pf); }; } if (!dojo._hasResource["dojo._base.Deferred"]) { dojo._hasResource["dojo._base.Deferred"] = true; dojo.provide("dojo._base.Deferred"); dojo.Deferred = function(_128) { this.chain = []; this.id = this._nextId(); this.fired = -1; this.paused = 0; this.results = [null, null]; this.canceller = _128; this.silentlyCancelled = false; }; dojo.extend(dojo.Deferred, { _nextId: (function() { var n = 1; return function() { return n++; }; })(), cancel: function() { var err; if (this.fired == -1) { if (this.canceller) { err = this.canceller(this); } else { this.silentlyCancelled = true; } if (this.fired == -1) { if (!(err instanceof Error)) { var res = err; var msg = "Deferred Cancelled"; if (err && err.toString) { msg += ": " + err.toString(); } err = new Error(msg); err.dojoType = "cancel"; err.cancelResult = res; } this.errback(err); } } else { if ((this.fired == 0) && (this.results[0] instanceof dojo .Deferred)) { this.results[0].cancel(); } } }, _resback: function(res) { this.fired = ((res instanceof Error) ? 1 : 0); this.results[this.fired] = res; this._fire(); }, _check: function() { if (this.fired != -1) { if (!this.silentlyCancelled) { throw new Error("already called!"); } this.silentlyCancelled = false; return; } }, callback: function(res) { this._check(); this._resback(res); }, errback: function(res) { this._check(); if (!(res instanceof Error)) { res = new Error(res); } this._resback(res); }, addBoth: function(cb, cbfn) { var _132 = dojo.hitch.apply(dojo, arguments); return this.addCallbacks(_132, _132); }, addCallback: function(cb, cbfn) { return this.addCallbacks(dojo.hitch.apply(dojo, arguments)); }, addErrback: function(cb, cbfn) { return this.addCallbacks(null, dojo.hitch.apply( dojo, arguments)); }, addCallbacks: function(cb, eb) { this.chain.push([cb, eb]); if (this.fired >= 0) { this._fire(); } return this; }, _fire: function() { var _139 = this.chain; var _13a = this.fired; var res = this.results[_13a]; var self = this; var cb = null; while ((_139.length > 0) && (this.paused == 0)) { var f = _139.shift()[_13a]; if (!f) { continue; } var func = function() { var ret = f(res); if (typeof ret != "undefined") { res = ret; } _13a = ((res instanceof Error) ? 1 : 0); if (res instanceof dojo.Deferred) { cb = function(res) { self._resback(res); self.paused--; if ((self.paused == 0) && (self.fired >= 0)) { self._fire(); } }; this.paused++; } }; if (dojo.config.debugAtAllCosts) { func.call(this); } else { try { func.call(this); } catch (err) { _13a = 1; res = err; } } } this.fired = _13a; this.results[_13a] = res; if ((cb) && (this.paused)) { res.addBoth(cb); } } }); } if (!dojo._hasResource["dojo._base.json"]) { dojo._hasResource["dojo._base.json"] = true; dojo.provide("dojo._base.json"); dojo.fromJson = function(json) { return eval("(" + json + ")"); }; dojo._escapeString = function(str) { return ("\"" + str.replace(/(["\\])/g, "\\$1") + "\"").replace( /[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace( /[\n]/g, "\\n").replace(/[\t]/g, "\\t").replace( /[\r]/g, "\\r"); }; dojo.toJsonIndentStr = "\t"; dojo.toJson = function(it, _145, _146) { if (it === undefined) { return "undefined"; } var _147 = typeof it; if (_147 == "number" || _147 == "boolean") { return it + ""; } if (it === null) { return "null"; } if (dojo.isString(it)) { return dojo._escapeString(it); } var _148 = arguments.callee; var _149; _146 = _146 || ""; var _14a = _145 ? _146 + dojo.toJsonIndentStr : ""; var tf = it.__json__ || it.json; if (dojo.isFunction(tf)) { _149 = tf.call(it); if (it !== _149) { return _148(_149, _145, _14a); } } if (it.nodeType && it.cloneNode) { throw new Error("Can't serialize DOM nodes"); } var sep = _145 ? " " : ""; var _14d = _145 ? "\n" : ""; if (dojo.isArray(it)) { var res = dojo.map(it, function(obj) { var val = _148(obj, _145, _14a); if (typeof val != "string") { val = "undefined"; } return _14d + _14a + val; }); return "[" + res.join("," + sep) + _14d + _146 + "]"; } if (_147 == "function") { return null; } var _151 = [], key; for (key in it) { var _153, val; if (typeof key == "number") { _153 = "\"" + key + "\""; } else { if (typeof key == "string") { _153 = dojo._escapeString(key); } else { continue; } } val = _148(it[key], _145, _14a); if (typeof val != "string") { continue; } _151.push(_14d + _14a + _153 + ":" + sep + val); } return "{" + _151.join("," + sep) + _14d + _146 + "}"; }; } if (!dojo._hasResource["dojo._base.array"]) { dojo._hasResource["dojo._base.array"] = true; dojo.provide("dojo._base.array"); (function() { var _155 = function(arr, obj, cb) { return [dojo.isString(arr) ? arr.split("") : arr, obj || dojo.global, dojo.isString(cb) ? new Function( "item", "index", "array", cb) : cb ]; }; dojo.mixin(dojo, { indexOf: function(_159, _15a, _15b, _15c) { var step = 1, end = _159.length || 0, i = 0; if (_15c) { i = end - 1; step = end = -1; } if (_15b != undefined) { i = _15b; } if ((_15c && i > end) || i < end) { for (; i != end; i += step) { if (_159[i] == _15a) { return i; } } } return -1; }, lastIndexOf: function(_15f, _160, _161) { return dojo.indexOf(_15f, _160, _161, true); }, forEach: function(arr, _163, _164) { if (!arr || !arr.length) { return; } var _p = _155(arr, _164, _163); arr = _p[0]; for (var i = 0, l = arr.length; i < l; ++ i) { _p[2].call(_p[1], arr[i], i, arr); } }, _everyOrSome: function(_168, arr, _16a, _16b) { var _p = _155(arr, _16b, _16a); arr = _p[0]; for (var i = 0, l = arr.length; i < l; ++ i) { var _16f = !!_p[2].call(_p[1], arr[ i], i, arr); if (_168 ^ _16f) { return _16f; } } return _168; }, every: function(arr, _171, _172) { return this._everyOrSome(true, arr, _171, _172); }, some: function(arr, _174, _175) { return this._everyOrSome(false, arr, _174, _175); }, map: function(arr, _177, _178) { var _p = _155(arr, _178, _177); arr = _p[0]; var _17a = (arguments[3] ? (new arguments[ 3]()) : []); for (var i = 0, l = arr.length; i < l; ++ i) { _17a.push(_p[2].call(_p[1], arr[i], i, arr)); } return _17a; }, filter: function(arr, _17e, _17f) { var _p = _155(arr, _17f, _17e); arr = _p[0]; var _181 = []; for (var i = 0, l = arr.length; i < l; ++ i) { if (_p[2].call(_p[1], arr[i], i, arr)) { _181.push(arr[i]); } } return _181; } }); })(); } if (!dojo._hasResource["dojo._base.Color"]) { dojo._hasResource["dojo._base.Color"] = true; dojo.provide("dojo._base.Color"); (function() { var d = dojo; dojo.Color = function(_185) { if (_185) { this.setColor(_185); } }; dojo.Color.named = { black: [0, 0, 0], silver: [192, 192, 192], gray: [128, 128, 128], white: [255, 255, 255], maroon: [128, 0, 0], red: [255, 0, 0], purple: [128, 0, 128], fuchsia: [255, 0, 255], green: [0, 128, 0], lime: [0, 255, 0], olive: [128, 128, 0], yellow: [255, 255, 0], navy: [0, 0, 128], blue: [0, 0, 255], teal: [0, 128, 128], aqua: [0, 255, 255] }; dojo.extend(dojo.Color, { r: 255, g: 255, b: 255, a: 1, _set: function(r, g, b, a) { var t = this; t.r = r; t.g = g; t.b = b; t.a = a; }, setColor: function(_18b) { if (d.isString(_18b)) { d.colorFromString(_18b, this); } else { if (d.isArray(_18b)) { d.colorFromArray(_18b, this); } else { this._set(_18b.r, _18b.g, _18b.b, _18b.a); if (!(_18b instanceof d.Color)) { this.sanitize(); } } } return this; }, sanitize: function() { return this; }, toRgb: function() { var t = this; return [t.r, t.g, t.b]; }, toRgba: function() { var t = this; return [t.r, t.g, t.b, t.a]; }, toHex: function() { var arr = d.map(["r", "g", "b"], function(x) { var s = this[x].toString(16); return s.length < 2 ? "0" + s : s; }, this); return "#" + arr.join(""); }, toCss: function(_191) { var t = this, rgb = t.r + ", " + t.g + ", " + t.b; return (_191 ? "rgba(" + rgb + ", " + t .a : "rgb(" + rgb) + ")"; }, toString: function() { return this.toCss(true); } }); dojo.blendColors = function(_194, end, _196, obj) { var t = obj || new d.Color(); d.forEach(["r", "g", "b", "a"], function(x) { t[x] = _194[x] + (end[x] - _194[x]) * _196; if (x != "a") { t[x] = Math.round(t[x]); } }); return t.sanitize(); }; dojo.colorFromRgb = function(_19a, obj) { var m = _19a.toLowerCase().match( /^rgba?\(([\s\.,0-9]+)\)/); return m && dojo.colorFromArray(m[1].split( /\s*,\s*/), obj); }; dojo.colorFromHex = function(_19d, obj) { var t = obj || new d.Color(), bits = (_19d.length == 4) ? 4 : 8, mask = (1 << bits) - 1; _19d = Number("0x" + _19d.substr(1)); if (isNaN(_19d)) { return null; } d.forEach(["b", "g", "r"], function(x) { var c = _19d & mask; _19d >>= bits; t[x] = bits == 4 ? 17 * c : c; }); t.a = 1; return t; }; dojo.colorFromArray = function(a, obj) { var t = obj || new d.Color(); t._set(Number(a[0]), Number(a[1]), Number(a[2]), Number(a[3])); if (isNaN(t.a)) { t.a = 1; } return t.sanitize(); }; dojo.colorFromString = function(str, obj) { var a = d.Color.named[str]; return a && d.colorFromArray(a, obj) || d.colorFromRgb( str, obj) || d.colorFromHex(str, obj); }; })(); } if (!dojo._hasResource["dojo._base"]) { dojo._hasResource["dojo._base"] = true; dojo.provide("dojo._base"); } if (!dojo._hasResource["dojo._base.window"]) { dojo._hasResource["dojo._base.window"] = true; dojo.provide("dojo._base.window"); dojo.doc = window["document"] || null; dojo.body = function() { return dojo.doc.body || dojo.doc.getElementsByTagName( "body")[0]; }; dojo.setContext = function(_1aa, _1ab) { dojo.global = _1aa; dojo.doc = _1ab; }; dojo.withGlobal = function(_1ac, _1ad, _1ae, _1af) { var _1b0 = dojo.global; try { dojo.global = _1ac; return dojo.withDoc.call(null, _1ac.document, _1ad, _1ae, _1af); } finally { dojo.global = _1b0; } }; dojo.withDoc = function(_1b1, _1b2, _1b3, _1b4) { var _1b5 = dojo.doc, _1b6 = dojo._bodyLtr; try { dojo.doc = _1b1; delete dojo._bodyLtr; if (_1b3 && dojo.isString(_1b2)) { _1b2 = _1b3[_1b2]; } return _1b2.apply(_1b3, _1b4 || []); } finally { dojo.doc = _1b5; if (_1b6 !== undefined) { dojo._bodyLtr = _1b6; } } }; } if (!dojo._hasResource["dojo._base.event"]) { dojo._hasResource["dojo._base.event"] = true; dojo.provide("dojo._base.event"); (function() { var del = (dojo._event_listener = { add: function(node, name, fp) { if (!node) { return; } name = del._normalizeEventName(name); fp = del._fixCallback(name, fp); var _1bb = name; if (!dojo.isIE && (name == "mouseenter" || name == "mouseleave")) { var ofp = fp; name = (name == "mouseenter") ? "mouseover" : "mouseout"; fp = function(e) { if (dojo.isFF <= 2) { try { e.relatedTarget.tagName; } catch (e2) { return; } } if (!dojo.isDescendant(e.relatedTarget, node)) { return ofp.call(this, e); } }; } node.addEventListener(name, fp, false); return fp; }, remove: function(node, _1bf, _1c0) { if (node) { _1bf = del._normalizeEventName(_1bf); if (!dojo.isIE && (_1bf == "mouseenter" || _1bf == "mouseleave")) { _1bf = (_1bf == "mouseenter") ? "mouseover" : "mouseout"; } node.removeEventListener(_1bf, _1c0, false); } }, _normalizeEventName: function(name) { return name.slice(0, 2) == "on" ? name.slice( 2) : name; }, _fixCallback: function(name, fp) { return name != "keypress" ? fp : function(e) { return fp.call(this, del._fixEvent( e, this)); }; }, _fixEvent: function(evt, _1c6) { switch (evt.type) { case "keypress": del._setKeyChar(evt); break; } return evt; }, _setKeyChar: function(evt) { evt.keyChar = evt.charCode ? String.fromCharCode( evt.charCode) : ""; evt.charOrCode = evt.keyChar || evt.keyCode; }, _punctMap: { 106: 42, 111: 47, 186: 59, 187: 43, 188: 44, 189: 45, 190: 46, 191: 47, 192: 96, 219: 91, 220: 92, 221: 93, 222: 39 } }); dojo.fixEvent = function(evt, _1c9) { return del._fixEvent(evt, _1c9); }; dojo.stopEvent = function(evt) { evt.preventDefault(); evt.stopPropagation(); }; var _1cb = dojo._listener; dojo._connect = function(obj, _1cd, _1ce, _1cf, _1d0) { var _1d1 = obj && (obj.nodeType || obj.attachEvent || obj.addEventListener); var lid = _1d1 ? (_1d0 ? 2 : 1) : 0, l = [dojo._listener, del, _1cb][lid]; var h = l.add(obj, _1cd, dojo.hitch(_1ce, _1cf)); return [obj, _1cd, h, lid]; }; dojo._disconnect = function(obj, _1d6, _1d7, _1d8) { ([dojo._listener, del, _1cb][_1d8]).remove(obj, _1d6, _1d7); }; dojo.keys = { BACKSPACE: 8, TAB: 9, CLEAR: 12, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, PAUSE: 19, CAPS_LOCK: 20, ESCAPE: 27, SPACE: 32, PAGE_UP: 33, PAGE_DOWN: 34, END: 35, HOME: 36, LEFT_ARROW: 37, UP_ARROW: 38, RIGHT_ARROW: 39, DOWN_ARROW: 40, INSERT: 45, DELETE: 46, HELP: 47, LEFT_WINDOW: 91, RIGHT_WINDOW: 92, SELECT: 93, NUMPAD_0: 96, NUMPAD_1: 97, NUMPAD_2: 98, NUMPAD_3: 99, NUMPAD_4: 100, NUMPAD_5: 101, NUMPAD_6: 102, NUMPAD_7: 103, NUMPAD_8: 104, NUMPAD_9: 105, NUMPAD_MULTIPLY: 106, NUMPAD_PLUS: 107, NUMPAD_ENTER: 108, NUMPAD_MINUS: 109, NUMPAD_PERIOD: 110, NUMPAD_DIVIDE: 111, F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, F7: 118, F8: 119, F9: 120, F10: 121, F11: 122, F12: 123, F13: 124, F14: 125, F15: 126, NUM_LOCK: 144, SCROLL_LOCK: 145 }; if (dojo.isIE) { var _1d9 = function(e, code) { try { return (e.keyCode = code); } catch (e) { return 0; } }; var iel = dojo._listener; var _1dd = (dojo._ieListenersName = "_" + dojo._scopeName + "_listeners"); if (!dojo.config._allow_leaks) { _1cb = iel = dojo._ie_listener = { handlers: [], add: function(_1de, _1df, _1e0) { _1de = _1de || dojo.global; var f = _1de[_1df]; if (!f || !f[_1dd]) { var d = dojo._getIeDispatcher(); d.target = f && (ieh.push(f) - 1); d[_1dd] = []; f = _1de[_1df] = d; } return f[_1dd].push(ieh.push(_1e0) - 1); }, remove: function(_1e4, _1e5, _1e6) { var f = (_1e4 || dojo.global)[_1e5], l = f && f[_1dd]; if (f && l && _1e6--) { delete ieh[l[_1e6]]; delete l[_1e6]; } } }; var ieh = iel.handlers; } dojo.mixin(del, { add: function(node, _1ea, fp) { if (!node) { return; } _1ea = del._normalizeEventName(_1ea); if (_1ea == "onkeypress") { var kd = node.onkeydown; if (!kd || !kd[_1dd] || !kd._stealthKeydownHandle) { var h = del.add(node, "onkeydown", del._stealthKeyDown ); kd = node.onkeydown; kd._stealthKeydownHandle = h; kd._stealthKeydownRefs = 1; } else { kd._stealthKeydownRefs++; } } return iel.add(node, _1ea, del._fixCallback( fp)); }, remove: function(node, _1ef, _1f0) { _1ef = del._normalizeEventName(_1ef); iel.remove(node, _1ef, _1f0); if (_1ef == "onkeypress") { var kd = node.onkeydown; if (--kd._stealthKeydownRefs <= 0) { iel.remove(node, "onkeydown", kd._stealthKeydownHandle ); delete kd._stealthKeydownHandle; } } }, _normalizeEventName: function(_1f2) { return _1f2.slice(0, 2) != "on" ? "on" + _1f2 : _1f2; }, _nop: function() {}, _fixEvent: function(evt, _1f4) { if (!evt) { var w = _1f4 && (_1f4.ownerDocument || _1f4.document || _1f4).parentWindow || window; evt = w.event; } if (!evt) { return (evt); } evt.target = evt.srcElement; evt.currentTarget = (_1f4 || evt.srcElement); evt.layerX = evt.offsetX; evt.layerY = evt.offsetY; var se = evt.srcElement, doc = (se && se.ownerDocument) || document; var _1f8 = ((dojo.isIE < 6) || (doc[ "compatMode"] == "BackCompat")) ? doc.body : doc.documentElement; var _1f9 = dojo._getIeDocumentElementOffset(); evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft( _1f8.scrollLeft || 0) - _1f9.x; evt.pageY = evt.clientY + (_1f8.scrollTop || 0) - _1f9.y; if (evt.type == "mouseover") { evt.relatedTarget = evt.fromElement; } if (evt.type == "mouseout") { evt.relatedTarget = evt.toElement; } evt.stopPropagation = del._stopPropagation; evt.preventDefault = del._preventDefault; return del._fixKeys(evt); }, _fixKeys: function(evt) { switch (evt.type) { case "keypress": var c = ("charCode" in evt ? evt.charCode : evt.keyCode ); if (c == 10) { c = 0; evt.keyCode = 13; } else { if (c == 13 || c == 27) { c = 0; } else { if (c == 3) { c = 99; } } } evt.charCode = c; del._setKeyChar(evt); break; } return evt; }, _stealthKeyDown: function(evt) { var kp = evt.currentTarget.onkeypress; if (!kp || !kp[_1dd]) { return; } var k = evt.keyCode; var _1ff = k != 13 && k != 32 && k != 27 && (k < 48 || k > 90) && (k < 96 || k > 111) && (k < 186 || k > 192) && (k < 219 || k > 222); if (_1ff || evt.ctrlKey) { var c = _1ff ? 0 : k; if (evt.ctrlKey) { if (k == 3 || k == 13) { return; } else { if (c > 95 && c < 106) { c -= 48; } else { if ((!evt.shiftKey) && (c >= 65 && c <= 90)) { c += 32; } else { c = del._punctMap[ c] || c; } } } } var faux = del._synthesizeEvent( evt, { type: "keypress", faux: true, charCode: c }); kp.call(evt.currentTarget, faux); evt.cancelBubble = faux.cancelBubble; evt.returnValue = faux.returnValue; _1d9(evt, faux.keyCode); } }, _stopPropagation: function() { this.cancelBubble = true; }, _preventDefault: function() { this.bubbledKeyCode = this.keyCode; if (this.ctrlKey) { _1d9(this, 0); } this.returnValue = false; } }); dojo.stopEvent = function(evt) { evt = evt || window.event; del._stopPropagation.call(evt); del._preventDefault.call(evt); }; } del._synthesizeEvent = function(evt, _204) { var faux = dojo.mixin({}, evt, _204); del._setKeyChar(faux); faux.preventDefault = function() { evt.preventDefault(); }; faux.stopPropagation = function() { evt.stopPropagation(); }; return faux; }; if (dojo.isOpera) { dojo.mixin(del, { _fixEvent: function(evt, _207) { switch (evt.type) { case "keypress": var c = evt.which; if (c == 3) { c = 99; } c = c < 41 && !evt.shiftKey ? 0 : c; if (evt.ctrlKey && !evt.shiftKey && c >= 65 && c <= 90) { c += 32; } return del._synthesizeEvent( evt, { charCode: c }); } return evt; } }); } if (dojo.isWebKit) { del._add = del.add; del._remove = del.remove; dojo.mixin(del, { add: function(node, _20a, fp) { if (!node) { return; } var _20c = del._add(node, _20a, fp); if (del._normalizeEventName(_20a) == "keypress") { _20c._stealthKeyDownHandle = del._add(node, "keydown", function(evt) { var k = evt.keyCode; var _20f = k != 13 && k != 32 && k != 27 && (k < 48 || k > 90) && (k < 96 || k > 111) && (k < 186 || k > 192) && (k < 219 || k > 222); if (_20f || evt.ctrlKey) { var c = _20f ? 0 : k; if (evt.ctrlKey) { if (k == 3 || k == 13 ) { return; } else { if (c > 95 && c < 106 ) { c -= 48; } else { if (! evt .shiftKey && c >= 65 && c <= 90 ) { c += 32; } else { c = del ._punctMap[ c ] || c; } } } } var faux = del._synthesizeEvent( evt, { type: "keypress", faux: true, charCode: c }); fp.call(evt.currentTarget, faux); } }); } return _20c; }, remove: function(node, _213, _214) { if (node) { if (_214._stealthKeyDownHandle) { del._remove(node, "keydown", _214._stealthKeyDownHandle ); } del._remove(node, _213, _214); } }, _fixEvent: function(evt, _216) { switch (evt.type) { case "keypress": if (evt.faux) { return evt; } var c = evt.charCode; c = c >= 32 ? c : 0; return del._synthesizeEvent( evt, { charCode: c, faux: true }); } return evt; } }); } })(); if (dojo.isIE) { dojo._ieDispatcher = function(args, _219) { var ap = Array.prototype, h = dojo._ie_listener.handlers, c = args.callee, ls = c[dojo._ieListenersName], t = h[c.target]; var r = t && t.apply(_219, args); var lls = [].concat(ls); for (var i in lls) { var f = h[lls[i]]; if (!(i in ap) && f) { f.apply(_219, args); } } return r; }; dojo._getIeDispatcher = function() { return new Function(dojo._scopeName + "._ieDispatcher(arguments, this)"); }; dojo._event_listener._fixCallback = function(fp) { var f = dojo._event_listener._fixEvent; return function(e) { return fp.call(this, f(e, this)); }; }; } } if (!dojo._hasResource["dojo._base.html"]) { dojo._hasResource["dojo._base.html"] = true; dojo.provide("dojo._base.html"); try { document.execCommand("BackgroundImageCache", false, true); } catch (e) {} if (dojo.isIE || dojo.isOpera) { dojo.byId = function(id, doc) { if (dojo.isString(id)) { var _d = doc || dojo.doc; var te = _d.getElementById(id); if (te && (te.attributes.id.value == id || te.id == id)) { return te; } else { var eles = _d.all[id]; if (!eles || eles.nodeName) { eles = [eles]; } var i = 0; while ((te = eles[i++])) { if ((te.attributes && te.attributes.id && te.attributes.id.value == id) || te .id == id) { return te; } } } } else { return id; } }; } else { dojo.byId = function(id, doc) { return dojo.isString(id) ? (doc || dojo.doc).getElementById( id) : id; }; }(function() { var d = dojo; var _22f = null; d.addOnWindowUnload(function() { _22f = null; }); dojo._destroyElement = dojo.destroy = function(node) { node = d.byId(node); try { if (!_22f || _22f.ownerDocument != node.ownerDocument) { _22f = node.ownerDocument.createElement( "div"); } _22f.appendChild(node.parentNode ? node.parentNode .removeChild(node) : node); _22f.innerHTML = ""; } catch (e) {} }; dojo.isDescendant = function(node, _232) { try { node = d.byId(node); _232 = d.byId(_232); while (node) { if (node === _232) { return true; } node = node.parentNode; } } catch (e) {} return false; }; dojo.setSelectable = function(node, _234) { node = d.byId(node); if (d.isMozilla) { node.style.MozUserSelect = _234 ? "" : "none"; } else { if (d.isKhtml || d.isWebKit) { node.style.KhtmlUserSelect = _234 ? "auto" : "none"; } else { if (d.isIE) { var v = (node.unselectable = _234 ? "" : "on"); d.query("*", node).forEach( "item.unselectable = '" + v + "'"); } } } }; var _236 = function(node, ref) { var _239 = ref.parentNode; if (_239) { _239.insertBefore(node, ref); } }; var _23a = function(node, ref) { var _23d = ref.parentNode; if (_23d) { if (_23d.lastChild == ref) { _23d.appendChild(node); } else { _23d.insertBefore(node, ref.nextSibling); } } }; dojo.place = function(node, _23f, _240) { _23f = d.byId(_23f); if (d.isString(node)) { node = node.charAt(0) == "<" ? d._toDom(node, _23f.ownerDocument) : d.byId(node); } if (typeof _240 == "number") { var cn = _23f.childNodes; if (!cn.length || cn.length <= _240) { _23f.appendChild(node); } else { _236(node, cn[_240 < 0 ? 0 : _240]); } } else { switch (_240) { case "before": _236(node, _23f); break; case "after": _23a(node, _23f); break; case "replace": _23f.parentNode.replaceChild(node, _23f); break; case "only": d.empty(_23f); _23f.appendChild(node); break; case "first": if (_23f.firstChild) { _236(node, _23f.firstChild); break; } default: _23f.appendChild(node); } } return node; }; dojo.boxModel = "content-box"; if (d.isIE) { var _dcm = document.compatMode; d.boxModel = _dcm == "BackCompat" || _dcm == "QuirksMode" || d.isIE < 6 ? "border-box" : "content-box"; } var gcs; if (d.isWebKit) { gcs = function(node) { var s; if (node instanceof HTMLElement) { var dv = node.ownerDocument.defaultView; s = dv.getComputedStyle(node, null); if (!s && node.style) { node.style.display = ""; s = dv.getComputedStyle(node, null); } } return s || {}; }; } else { if (d.isIE) { gcs = function(node) { return node.nodeType == 1 ? node.currentStyle : {}; }; } else { gcs = function(node) { return node instanceof HTMLElement ? node.ownerDocument .defaultView.getComputedStyle(node, null) : {}; }; } } dojo.getComputedStyle = gcs; if (!d.isIE) { d._toPixelValue = function(_249, _24a) { return parseFloat(_24a) || 0; }; } else { d._toPixelValue = function(_24b, _24c) { if (!_24c) { return 0; } if (_24c == "medium") { return 4; } if (_24c.slice && _24c.slice(-2) == "px") { return parseFloat(_24c); } with(_24b) { var _24d = style.left; var _24e = runtimeStyle.left; runtimeStyle.left = currentStyle.left; try { style.left = _24c; _24c = style.pixelLeft; } catch (e) { _24c = 0; } style.left = _24d; runtimeStyle.left = _24e; } return _24c; }; } var px = d._toPixelValue; var astr = "DXImageTransform.Microsoft.Alpha"; var af = function(n, f) { try { return n.filters.item(astr); } catch (e) { return f ? {} : null; } }; dojo._getOpacity = d.isIE ? function(node) { try { return af(node).Opacity / 100; } catch (e) { return 1; } } : function(node) { return gcs(node).opacity; }; dojo._setOpacity = d.isIE ? function(node, _257) { var ov = _257 * 100; node.style.zoom = 1; af(node, 1).Enabled = !(_257 == 1); if (!af(node)) { node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")"; } else { af(node, 1).Opacity = ov; } if (node.nodeName.toLowerCase() == "tr") { d.query("> td", node).forEach(function(i) { d._setOpacity(i, _257); }); } return _257; } : function(node, _25b) { return node.style.opacity = _25b; }; var _25c = { left: true, top: true }; var _25d = /margin|padding|width|height|max|min|offset/; var _25e = function(node, type, _261) { type = type.toLowerCase(); if (d.isIE) { if (_261 == "auto") { if (type == "height") { return node.offsetHeight; } if (type == "width") { return node.offsetWidth; } } if (type == "fontweight") { switch (_261) { case 700: return "bold"; case 400: default: return "normal"; } } } if (!(type in _25c)) { _25c[type] = _25d.test(type); } return _25c[type] ? px(node, _261) : _261; }; var _262 = d.isIE ? "styleFloat" : "cssFloat", _263 = { "cssFloat": _262, "styleFloat": _262, "float": _262 }; dojo.style = function(node, _265, _266) { var n = d.byId(node), args = arguments.length, op = (_265 == "opacity"); _265 = _263[_265] || _265; if (args == 3) { return op ? d._setOpacity(n, _266) : n.style[ _265] = _266; } if (args == 2 && op) { return d._getOpacity(n); } var s = gcs(n); if (args == 2 && !d.isString(_265)) { for (var x in _265) { d.style(node, x, _265[x]); } return s; } return (args == 1) ? s : _25e(n, _265, s[_265] || n .style[_265]); }; dojo._getPadExtents = function(n, _26d) { var s = _26d || gcs(n), l = px(n, s.paddingLeft), t = px(n, s.paddingTop); return { l: l, t: t, w: l + px(n, s.paddingRight), h: t + px(n, s.paddingBottom) }; }; dojo._getBorderExtents = function(n, _272) { var ne = "none", s = _272 || gcs(n), bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0), bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0); return { l: bl, t: bt, w: bl + (s.borderRightStyle != ne ? px(n, s.borderRightWidth) : 0), h: bt + (s.borderBottomStyle != ne ? px(n, s.borderBottomWidth) : 0) }; }; dojo._getPadBorderExtents = function(n, _278) { var s = _278 || gcs(n), p = d._getPadExtents(n, s), b = d._getBorderExtents(n, s); return { l: p.l + b.l, t: p.t + b.t, w: p.w + b.w, h: p.h + b.h }; }; dojo._getMarginExtents = function(n, _27d) { var s = _27d || gcs(n), l = px(n, s.marginLeft), t = px(n, s.marginTop), r = px(n, s.marginRight), b = px(n, s.marginBottom); if (d.isWebKit && (s.position != "absolute")) { r = l; } return { l: l, t: t, w: l + r, h: t + b }; }; dojo._getMarginBox = function(node, _284) { var s = _284 || gcs(node), me = d._getMarginExtents(node, s); var l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode; if (d.isMoz) { var sl = parseFloat(s.left), st = parseFloat(s.top); if (!isNaN(sl) && !isNaN(st)) { l = sl, t = st; } else { if (p && p.style) { var pcs = gcs(p); if (pcs.overflow != "visible") { var be = d._getBorderExtents(p, pcs); l += be.l, t += be.t; } } } } else { if (d.isOpera || (d.isIE > 7 && !d.isQuirks)) { if (p) { be = d._getBorderExtents(p); l -= be.l; t -= be.t; } } } return { l: l, t: t, w: node.offsetWidth + me.w, h: node.offsetHeight + me.h }; }; dojo._getContentBox = function(node, _28f) { var s = _28f || gcs(node), pe = d._getPadExtents(node, s), be = d._getBorderExtents(node, s), w = node.clientWidth, h; if (!w) { w = node.offsetWidth, h = node.offsetHeight; } else { h = node.clientHeight, be.w = be.h = 0; } if (d.isOpera) { pe.l += be.l; pe.t += be.t; } return { l: pe.l, t: pe.t, w: w - pe.w - be.w, h: h - pe.h - be.h }; }; dojo._getBorderBox = function(node, _296) { var s = _296 || gcs(node), pe = d._getPadExtents(node, s), cb = d._getContentBox(node, s); return { l: cb.l - pe.l, t: cb.t - pe.t, w: cb.w + pe.w, h: cb.h + pe.h }; }; dojo._setBox = function(node, l, t, w, h, u) { u = u || "px"; var s = node.style; if (!isNaN(l)) { s.left = l + u; } if (!isNaN(t)) { s.top = t + u; } if (w >= 0) { s.width = w + u; } if (h >= 0) { s.height = h + u; } }; dojo._isButtonTag = function(node) { return node.tagName == "BUTTON" || node.tagName == "INPUT" && node.getAttribute("type").toUpperCase() == "BUTTON"; }; dojo._usesBorderBox = function(node) { var n = node.tagName; return d.boxModel == "border-box" || n == "TABLE" || d._isButtonTag(node); }; dojo._setContentSize = function(node, _2a5, _2a6, _2a7) { if (d._usesBorderBox(node)) { var pb = d._getPadBorderExtents(node, _2a7); if (_2a5 >= 0) { _2a5 += pb.w; } if (_2a6 >= 0) { _2a6 += pb.h; } } d._setBox(node, NaN, NaN, _2a5, _2a6); }; dojo._setMarginBox = function(node, _2aa, _2ab, _2ac, _2ad, _2ae) { var s = _2ae || gcs(node), bb = d._usesBorderBox(node), pb = bb ? _2b2 : d._getPadBorderExtents(node, s); if (d.isWebKit) { if (d._isButtonTag(node)) { var ns = node.style; if (_2ac >= 0 && !ns.width) { ns.width = "4px"; } if (_2ad >= 0 && !ns.height) { ns.height = "4px"; } } } var mb = d._getMarginExtents(node, s); if (_2ac >= 0) { _2ac = Math.max(_2ac - pb.w - mb.w, 0); } if (_2ad >= 0) { _2ad = Math.max(_2ad - pb.h - mb.h, 0); } d._setBox(node, _2aa, _2ab, _2ac, _2ad); }; var _2b2 = { l: 0, t: 0, w: 0, h: 0 }; dojo.marginBox = function(node, box) { var n = d.byId(node), s = gcs(n), b = box; return !b ? d._getMarginBox(n, s) : d._setMarginBox( n, b.l, b.t, b.w, b.h, s); }; dojo.contentBox = function(node, box) { var n = d.byId(node), s = gcs(n), b = box; return !b ? d._getContentBox(n, s) : d._setContentSize( n, b.w, b.h, s); }; var _2bf = function(node, prop) { if (!(node = (node || 0).parentNode)) { return 0; } var val, _2c3 = 0, _b = d.body(); while (node && node.style) { if (gcs(node).position == "fixed") { return 0; } val = node[prop]; if (val) { _2c3 += val - 0; if (node == _b) { break; } } node = node.parentNode; } return _2c3; }; dojo._docScroll = function() { var _b = d.body(), _w = d.global, de = d.doc.documentElement; return { y: (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0), x: (_w.pageXOffset || d._fixIeBiDiScrollLeft(de .scrollLeft) || _b.scrollLeft || 0) }; }; dojo._isBodyLtr = function() { return ("_bodyLtr" in d) ? d._bodyLtr : d._bodyLtr = gcs(d.body()).direction == "ltr"; }; dojo._getIeDocumentElementOffset = function() { var de = d.doc.documentElement; if (d.isIE < 7) { return { x: d._isBodyLtr() || window.parent == window ? de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft, y: de.clientTop }; } else { if (d.isIE < 8) { return { x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top }; } else { return { x: 0, y: 0 }; } } }; dojo._fixIeBiDiScrollLeft = function(_2c9) { var dd = d.doc; if (d.isIE < 8 && !d._isBodyLtr()) { var de = dd.compatMode == "BackCompat" ? dd.body : dd.documentElement; return _2c9 + de.clientWidth - de.scrollWidth; } return _2c9; }; dojo._abs = function(node, _2cd) { var db = d.body(), dh = d.body().parentNode, ret; if (node["getBoundingClientRect"]) { var _2d1 = node.getBoundingClientRect(); ret = { x: _2d1.left, y: _2d1.top }; if (d.isFF >= 3) { var cs = gcs(dh); ret.x -= px(dh, cs.marginLeft) + px(dh, cs.borderLeftWidth); ret.y -= px(dh, cs.marginTop) + px(dh, cs.borderTopWidth); } if (d.isIE) { var _2d3 = d._getIeDocumentElementOffset(); ret.x -= _2d3.x + (d.isQuirks ? db.clientLeft : 0); ret.y -= _2d3.y + (d.isQuirks ? db.clientTop : 0); } } else { ret = { x: 0, y: 0 }; if (node["offsetParent"]) { ret.x -= _2bf(node, "scrollLeft"); ret.y -= _2bf(node, "scrollTop"); var _2d4 = node; do { var n = _2d4.offsetLeft, t = _2d4.offsetTop; ret.x += isNaN(n) ? 0 : n; ret.y += isNaN(t) ? 0 : t; cs = gcs(_2d4); if (_2d4 != node) { if (d.isFF) { ret.x += 2 * px(_2d4, cs.borderLeftWidth); ret.y += 2 * px(_2d4, cs.borderTopWidth); } else { ret.x += px(_2d4, cs.borderLeftWidth); ret.y += px(_2d4, cs.borderTopWidth); } } if (d.isFF && cs.position == "static") { var _2d7 = _2d4.parentNode; while (_2d7 != _2d4.offsetParent) { var pcs = gcs(_2d7); if (pcs.position == "static") { ret.x += px(_2d4, pcs.borderLeftWidth); ret.y += px(_2d4, pcs.borderTopWidth); } _2d7 = _2d7.parentNode; } } _2d4 = _2d4.offsetParent; } while ((_2d4 != dh) && _2d4); } else { if (node.x && node.y) { ret.x += isNaN(node.x) ? 0 : node.x; ret.y += isNaN(node.y) ? 0 : node.y; } } } if (_2cd) { var _2d9 = d._docScroll(); ret.x += _2d9.x; ret.y += _2d9.y; } return ret; }; dojo.coords = function(node, _2db) { var n = d.byId(node), s = gcs(n), mb = d._getMarginBox(n, s); var abs = d._abs(n, _2db); mb.x = abs.x; mb.y = abs.y; return mb; }; var _2e0 = d.isIE < 8; var _2e1 = function(name) { switch (name.toLowerCase()) { case "tabindex": return _2e0 ? "tabIndex" : "tabindex"; case "readonly": return "readOnly"; case "class": return "className"; case "for": case "htmlfor": return _2e0 ? "htmlFor" : "for"; default: return name; } }; var _2e3 = { colspan: "colSpan", enctype: "enctype", frameborder: "frameborder", method: "method", rowspan: "rowSpan", scrolling: "scrolling", shape: "shape", span: "span", type: "type", valuetype: "valueType", classname: "className", innerhtml: "innerHTML" }; dojo.hasAttr = function(node, name) { node = d.byId(node); var _2e6 = _2e1(name); _2e6 = _2e6 == "htmlFor" ? "for" : _2e6; var attr = node.getAttributeNode && node.getAttributeNode( _2e6); return attr ? attr.specified : false; }; var _2e8 = {}, _ctr = 0, _2ea = dojo._scopeName + "attrid", _2eb = { col: 1, colgroup: 1, table: 1, tbody: 1, tfoot: 1, thead: 1, tr: 1, title: 1 }; dojo.attr = function(node, name, _2ee) { node = d.byId(node); var args = arguments.length; if (args == 2 && !d.isString(name)) { for (var x in name) { d.attr(node, x, name[x]); } return; } name = _2e1(name); if (args == 3) { if (d.isFunction(_2ee)) { var _2f1 = d.attr(node, _2ea); if (!_2f1) { _2f1 = _ctr++; d.attr(node, _2ea, _2f1); } if (!_2e8[_2f1]) { _2e8[_2f1] = {}; } var h = _2e8[_2f1][name]; if (h) { d.disconnect(h); } else { try { delete node[name]; } catch (e) {} } _2e8[_2f1][name] = d.connect(node, name, _2ee); } else { if (typeof _2ee == "boolean") { node[name] = _2ee; } else { if (name === "style" && !d.isString( _2ee)) { d.style(node, _2ee); } else { if (name == "className") { node.className = _2ee; } else { if (name === "innerHTML") { if (d.isIE && node.tagName.toLowerCase() in _2eb) { d.empty(node); node.appendChild(d._toDom( _2ee, node.ownerDocument )); } else { node[name] = _2ee; } } else { node.setAttribute(name, _2ee); } } } } } } else { var prop = _2e3[name.toLowerCase()]; if (prop) { return node[prop]; } var _2f4 = node[name]; return (typeof _2f4 == "boolean" || typeof _2f4 == "function") ? _2f4 : (d.hasAttr(node, name) ? node.getAttribute(name) : null); } }; dojo.removeAttr = function(node, name) { d.byId(node).removeAttribute(_2e1(name)); }; dojo.create = function(tag, _2f8, _2f9, pos) { var doc = d.doc; if (_2f9) { _2f9 = d.byId(_2f9); doc = _2f9.ownerDocument; } if (d.isString(tag)) { tag = doc.createElement(tag); } if (_2f8) { d.attr(tag, _2f8); } if (_2f9) { d.place(tag, _2f9, pos); } return tag; }; d.empty = d.isIE ? function(node) { node = d.byId(node); for (var c; c = node.lastChild;) { d.destroy(c); } } : function(node) { d.byId(node).innerHTML = ""; }; var _2ff = { option: ["select"], tbody: ["table"], thead: ["table"], tfoot: ["table"], tr: ["table", "tbody"], td: ["table", "tbody", "tr"], th: ["table", "thead", "tr"], legend: ["fieldset"], caption: ["table"], colgroup: ["table"], col: ["table", "colgroup"], li: ["ul"] }, _300 = /<\s*([\w\:]+)/, _301 = {}, _302 = 0, _303 = "__" + d._scopeName + "ToDomId"; for (var _304 in _2ff) { var tw = _2ff[_304]; tw.pre = _304 == "option" ? "", a.querySelectorAll( "[msallowclip^='']").length && q.push("[*^$]=" + M + "*(?:''|\"\")"), a.querySelectorAll( "[selected]").length || q.push( "\\[" + M + "*(?:value|" + L + ")"), a.querySelectorAll( ":checked").length || q.push( ":checked") }), ib(function(a) { var b = e.createElement("input"); b.setAttribute("type", "hidden"), a .appendChild(b).setAttribute( "name", "D"), a.querySelectorAll( "[name=d]").length && q.push( "name" + M + "*[*^$|!~]?="), a.querySelectorAll(":enabled").length || q.push(":enabled", ":disabled"), a.querySelectorAll("*,:x"), q.push( ",.*:") })), (c.matchesSelector = $.test(s = o.matches || o.webkitMatchesSelector || o.mozMatchesSelector || o.oMatchesSelector || o.msMatchesSelector )) && ib(function(a) { c.disconnectedMatch = s.call(a, "div"), s.call(a, "[s!='']:x"), r.push("!=", Q) }), q = q.length && new RegExp(q.join("|")), r = r.length && new RegExp(r.join("|")), b = $.test( o.compareDocumentPosition), t = b || $.test( o.contains) ? function(a, b) { var c = 9 === a.nodeType ? a.documentElement : a, d = b && b.parentNode; return a === d || !(!d || 1 !== d.nodeType || !(c.contains ? c.contains(d) : a.compareDocumentPosition && 16 & a.compareDocumentPosition( d))) } : function(a, b) { if (b) while (b = b.parentNode) if (b === a) return !0; return !1 }, B = b ? function(a, b) { if (a === b) return l = !0, 0; var d = !a.compareDocumentPosition - !b.compareDocumentPosition; return d ? d : (d = (a.ownerDocument || a) === (b.ownerDocument || b) ? a.compareDocumentPosition( b) : 1, 1 & d || !c.sortDetached && b.compareDocumentPosition(a) === d ? a === e || a.ownerDocument === v && t(v, a) ? -1 : b === e || b.ownerDocument === v && t(v, b) ? 1 : k ? K.call(k, a) - K.call(k, b) : 0 : 4 & d ? -1 : 1) } : function(a, b) { if (a === b) return l = !0, 0; var c, d = 0, f = a.parentNode, g = b.parentNode, h = [a], i = [b]; if (!f || !g) return a === e ? -1 : b === e ? 1 : f ? -1 : g ? 1 : k ? K.call(k, a) - K.call(k, b) : 0; if (f === g) return kb(a, b); c = a; while (c = c.parentNode) h.unshift(c); c = b; while (c = c.parentNode) i.unshift(c); while (h[d] === i[d]) d++; return d ? kb(h[d], i[d]) : h[d] === v ? -1 : i[d] === v ? 1 : 0 }, e) : n }, fb.matches = function(a, b) { return fb(a, null, null, b) }, fb.matchesSelector = function(a, b) { if ((a.ownerDocument || a) !== n && m(a), b = b.replace( U, "='$1']"), !(!c.matchesSelector || !p || r && r.test(b) || q && q.test(b))) try { var d = s.call(a, b); if (d || c.disconnectedMatch || a.document && 11 !== a.document.nodeType) return d } catch (e) {} return fb(b, n, null, [a]).length > 0 }, fb.contains = function(a, b) { return (a.ownerDocument || a) !== n && m(a), t(a, b) }, fb.attr = function(a, b) { (a.ownerDocument || a) !== n && m(a); var e = d.attrHandle[b.toLowerCase()], f = e && E.call(d.attrHandle, b.toLowerCase()) ? e( a, b, !p) : void 0; return void 0 !== f ? f : c.attributes || !p ? a.getAttribute( b) : (f = a.getAttributeNode(b)) && f.specified ? f.value : null }, fb.error = function(a) { throw new Error( "Syntax error, unrecognized expression: " + a) }, fb.uniqueSort = function(a) { var b, d = [], e = 0, f = 0; if (l = !c.detectDuplicates, k = !c.sortStable && a.slice( 0), a.sort(B), l) { while (b = a[f++]) b === a[f] && (e = d.push(f)); while (e--) a.splice(d[e], 1) } return k = null, a }, e = fb.getText = function(a) { var b, c = "", d = 0, f = a.nodeType; if (f) { if (1 === f || 9 === f || 11 === f) { if ("string" == typeof a.textContent) return a.textContent; for (a = a.firstChild; a; a = a.nextSibling) c += e(a) } else if (3 === f || 4 === f) return a.nodeValue } else while (b = a[d++]) c += e(b); return c }, d = fb.selectors = { cacheLength: 50, createPseudo: hb, match: X, attrHandle: {}, find: {}, relative: { ">": { dir: "parentNode", first: !0 }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: !0 }, "~": { dir: "previousSibling" } }, preFilter: { ATTR: function(a) { return a[1] = a[1].replace(cb, db), a[3] = (a[3] || a[4] || a[5] || "").replace(cb, db), "~=" === a[2] && (a[3] = " " + a[3] + " "), a.slice(0, 4) }, CHILD: function(a) { return a[1] = a[1].toLowerCase(), "nth" === a[1].slice(0, 3) ? (a[3] || fb.error(a[ 0]), a[4] = +(a[4] ? a[5] + (a[ 6] || 1) : 2 * ("even" === a[3] || "odd" === a[3])), a[5] = + (a[7] + a[8] || "odd" === a[3])) : a[3] && fb.error(a[0]), a }, PSEUDO: function(a) { var b, c = !a[6] && a[2]; return X.CHILD.test(a[0]) ? null : (a[3] ? a[2] = a[4] || a[5] || "" : c && V.test( c) && (b = g(c, !0)) && (b = c.indexOf( ")", c.length - b) - c.length) && (a[0] = a[0].slice(0, b), a[2] = c.slice( 0, b)), a.slice(0, 3)) } }, filter: { TAG: function(a) { var b = a.replace(cb, db).toLowerCase(); return "*" === a ? function() { return !0 } : function(a) { return a.nodeName && a.nodeName.toLowerCase() === b } }, CLASS: function(a) { var b = y[a + " "]; return b || (b = new RegExp("(^|" + M + ")" + a + "(" + M + "|$)")) && y(a, function(a) { return b.test("string" == typeof a.className && a .className || typeof a.getAttribute !== C && a.getAttribute( "class") || "") }) }, ATTR: function(a, b, c) { return function(d) { var e = fb.attr(d, a); return null == e ? "!=" === b : b ? (e += "", "=" === b ? e === c : "!=" === b ? e !== c : "^=" === b ? c && 0 === e.indexOf(c) : "*=" === b ? c && e.indexOf( c) > -1 : "$=" === b ? c && e.slice(-c.length) === c : "~=" === b ? (" " + e + " ").indexOf(c) > -1 : "|=" === b ? e === c || e.slice( 0, c.length + 1) === c + "-" : !1) : !0 } }, CHILD: function(a, b, c, d, e) { var f = "nth" !== a.slice(0, 3), g = "last" !== a.slice(-4), h = "of-type" === b; return 1 === d && 0 === e ? function(a) { return !!a.parentNode } : function(b, c, i) { var j, k, l, m, n, o, p = f !== g ? "nextSibling" : "previousSibling", q = b.parentNode, r = h && b.nodeName.toLowerCase(), s = !i && !h; if (q) { if (f) { while (p) { l = b; while (l = l[p]) if (h ? l.nodeName.toLowerCase() === r : 1 === l.nodeType ) return !1; o = p = "only" === a && !o && "nextSibling" } return !0 } if (o = [g ? q.firstChild : q.lastChild], g && s) { k = q[u] || (q[u] = {}), j = k[a] || [], n = j[0] === w && j[1], m = j[0] === w && j[2], l = n && q.childNodes[ n]; while (l = ++n && l && l[p] || (m = n = 0) || o.pop()) if (1 === l.nodeType && ++m && l === b) { k[a] = [w, n, m]; break } } else if (s && (j = (b[u] || ( b[u] = {}))[a]) && j[0] === w) m = j[1]; else while (l = ++n && l && l[p] || (m = n = 0) || o.pop()) if ((h ? l.nodeName.toLowerCase() === r : 1 === l.nodeType ) && ++m && (s && ( (l[u] || (l[ u ] = {}))[a] = [ w, m ]), l === b)) break; return m -= e, m === d || m % d === 0 && m / d >= 0 } } }, PSEUDO: function(a, b) { var c, e = d.pseudos[a] || d.setFilters[a.toLowerCase()] || fb.error("unsupported pseudo: " + a); return e[u] ? e(b) : e.length > 1 ? (c = [a, a, "", b ], d.setFilters.hasOwnProperty(a.toLowerCase()) ? hb(function(a, c) { var d, f = e(a, b), g = f.length; while (g--) d = K.call(a, f[ g]), a[d] = !(c[d] = f[g]) }) : function(a) { return e(a, 0, c) }) : e } }, pseudos: { not: hb(function(a) { var b = [], c = [], d = h(a.replace(R, "$1")); return d[u] ? hb(function(a, b, c, e) { var f, g = d(a, null, e, []), h = a.length; while (h--)(f = g[h]) && (a[ h] = !(b[h] = f)) }) : function(a, e, f) { return b[0] = a, d(b, null, f, c), !c.pop() } }), has: hb(function(a) { return function(b) { return fb(a, b).length > 0 } }), contains: hb(function(a) { return function(b) { return (b.textContent || b.innerText || e(b)).indexOf(a) > -1 } }), lang: hb(function(a) { return W.test(a || "") || fb.error( "unsupported lang: " + a), a = a.replace(cb, db).toLowerCase(), function(b) { var c; do if (c = p ? b.lang : b.getAttribute( "xml:lang") || b.getAttribute( "lang")) return c = c.toLowerCase(), c === a || 0 === c.indexOf( a + "-"); while ((b = b.parentNode) && 1 === b.nodeType); return !1 } }), target: function(b) { var c = a.location && a.location.hash; return c && c.slice(1) === b.id }, root: function(a) { return a === o }, focus: function(a) { return a === n.activeElement && (!n.hasFocus || n.hasFocus()) && !!(a.type || a.href || ~a.tabIndex) }, enabled: function(a) { return a.disabled === !1 }, disabled: function(a) { return a.disabled === !0 }, checked: function(a) { var b = a.nodeName.toLowerCase(); return "input" === b && !!a.checked || "option" === b && !!a.selected }, selected: function(a) { return a.parentNode && a.parentNode.selectedIndex, a.selected === !0 }, empty: function(a) { for (a = a.firstChild; a; a = a.nextSibling) if (a.nodeType < 6) return !1; return !0 }, parent: function(a) { return !d.pseudos.empty(a) }, header: function(a) { return Z.test(a.nodeName) }, input: function(a) { return Y.test(a.nodeName) }, button: function(a) { var b = a.nodeName.toLowerCase(); return "input" === b && "button" === a.type || "button" === b }, text: function(a) { var b; return "input" === a.nodeName.toLowerCase() && "text" === a.type && (null == (b = a.getAttribute( "type")) || "text" === b.toLowerCase()) }, first: nb(function() { return [0] }), last: nb(function(a, b) { return [b - 1] }), eq: nb(function(a, b, c) { return [0 > c ? c + b : c] }), even: nb(function(a, b) { for (var c = 0; b > c; c += 2) a.push(c); return a }), odd: nb(function(a, b) { for (var c = 1; b > c; c += 2) a.push(c); return a }), lt: nb(function(a, b, c) { for (var d = 0 > c ? c + b : c; --d >= 0;) a.push(d); return a }), gt: nb(function(a, b, c) { for (var d = 0 > c ? c + b : c; ++d < b;) a.push(d); return a }) } }, d.pseudos.nth = d.pseudos.eq; for (b in { radio: !0, checkbox: !0, file: !0, password: !0, image: !0 }) d.pseudos[b] = lb(b); for (b in { submit: !0, reset: !0 }) d.pseudos[b] = mb(b); function pb() {} pb.prototype = d.filters = d.pseudos, d.setFilters = new pb, g = fb.tokenize = function(a, b) { var c, e, f, g, h, i, j, k = z[a + " "]; if (k) return b ? 0 : k.slice(0); h = a, i = [], j = d.preFilter; while (h) { (!c || (e = S.exec(h))) && (e && (h = h.slice(e[0].length) || h), i.push(f = [])), c = !1, (e = T.exec(h)) && (c = e.shift(), f.push({ value: c, type: e[0].replace(R, " ") }), h = h.slice(c.length)); for (g in d.filter)!(e = X[g].exec(h)) || j[g] && ! (e = j[g](e)) || (c = e.shift(), f.push({ value: c, type: g, matches: e }), h = h.slice(c.length)); if (!c) break } return b ? h.length : h ? fb.error(a) : z(a, i).slice(0) }; function qb(a) { for (var b = 0, c = a.length, d = ""; c > b; b++) d += a[b].value; return d } function rb(a, b, c) { var d = b.dir, e = c && "parentNode" === d, f = x++; return b.first ? function(b, c, f) { while (b = b[d]) if (1 === b.nodeType || e) return a(b, c, f) } : function(b, c, g) { var h, i, j = [w, f]; if (g) { while (b = b[d]) if ((1 === b.nodeType || e) && a(b, c, g)) return !0 } else while (b = b[d]) if (1 === b.nodeType || e) { if (i = b[u] || (b[u] = {}), (h = i[ d]) && h[0] === w && h[1] === f) return j[2] = h[2]; if (i[d] = j, j[2] = a(b, c, g)) return !0 } } } function sb(a) { return a.length > 1 ? function(b, c, d) { var e = a.length; while (e--) if (!a[e](b, c, d)) return !1; return !0 } : a[0] } function tb(a, b, c) { for (var d = 0, e = b.length; e > d; d++) fb(a, b[d], c); return c } function ub(a, b, c, d, e) { for (var f, g = [], h = 0, i = a.length, j = null != b; i > h; h++)(f = a[h]) && (!c || c(f, d, e)) && (g.push( f), j && b.push(h)); return g } function vb(a, b, c, d, e, f) { return d && !d[u] && (d = vb(d)), e && !e[u] && (e = vb( e, f)), hb(function(f, g, h, i) { var j, k, l, m = [], n = [], o = g.length, p = f || tb(b || "*", h.nodeType ? [h] : h, []), q = !a || !f && b ? p : ub(p, m, a, h, i), r = c ? e || (f ? a : o || d) ? [] : g : q; if (c && c(q, r, h, i), d) { j = ub(r, n), d(j, [], h, i), k = j.length; while (k--)(l = j[k]) && (r[n[k]] = !(q[ n[k]] = l)) } if (f) { if (e || a) { if (e) { j = [], k = r.length; while (k--)(l = r[k]) && j.push( q[k] = l); e(null, r = [], j, i) } k = r.length; while (k--)(l = r[k]) && (j = e ? K .call(f, l) : m[k]) > -1 && (f[j] = !(g[j] = l)) } } else r = ub(r === g ? r.splice(o, r.length) : r), e ? e(null, g, r, i) : I.apply( g, r) }) } function wb(a) { for (var b, c, e, f = a.length, g = d.relative[a[0].type], h = g || d.relative[" "], i = g ? 1 : 0, k = rb( function(a) { return a === b }, h, !0), l = rb(function(a) { return K.call(b, a) > -1 }, h, !0), m = [ function(a, c, d) { return !g && (d || c !== j) || ((b = c) .nodeType ? k(a, c, d) : l(a, c, d)) } ]; f > i; i++) if (c = d.relative[a[i].type]) m = [rb(sb(m), c)]; else { if (c = d.filter[a[i].type].apply(null, a[i].matches), c[u]) { for (e = ++i; f > e; e++) if (d.relative[a[e].type]) break; return vb(i > 1 && sb(m), i > 1 && qb(a.slice( 0, i - 1).concat({ value: " " === a[i - 2] .type ? "*" : "" })).replace(R, "$1"), c, e > i && wb(a.slice(i, e)), f > e && wb(a = a.slice(e)), f > e && qb(a)) } m.push(c) } return sb(m) } function xb(a, b) { var c = b.length > 0, e = a.length > 0, f = function(f, g, h, i, k) { var l, m, o, p = 0, q = "0", r = f && [], s = [], t = j, u = f || e && d.find.TAG("*", k), v = w += null == t ? 1 : Math.random() || .1, x = u.length; for (k && (j = g !== n && g); q !== x && null != (l = u[q]); q++) { if (e && l) { m = 0; while (o = a[m++]) if (o(l, g, h)) { i.push(l); break } k && (w = v) } c && ((l = !o && l) && p--, f && r.push(l)) } if (p += q, c && q !== p) { m = 0; while (o = b[m++]) o(r, s, g, h); if (f) { if (p > 0) while (q--) r[q] || s[q] || (s[q] = G.call(i)); s = ub(s) } I.apply(i, s), k && !f && s.length > 0 && p + b.length > 1 && fb.uniqueSort(i) } return k && (w = v, j = t), r }; return c ? hb(f) : f } return h = fb.compile = function(a, b) { var c, d = [], e = [], f = A[a + " "]; if (!f) { b || (b = g(a)), c = b.length; while (c--) f = wb(b[c]), f[u] ? d.push(f) : e.push( f); f = A(a, xb(e, d)), f.selector = a } return f }, i = fb.select = function(a, b, e, f) { var i, j, k, l, m, n = "function" == typeof a && a, o = !f && g(a = n.selector || a); if (e = e || [], 1 === o.length) { if (j = o[0] = o[0].slice(0), j.length > 2 && "ID" === (k = j[0]).type && c.getById && 9 === b.nodeType && p && d.relative[j[1].type]) { if (b = (d.find.ID(k.matches[0].replace(cb, db), b) || [])[0], !b) return e; n && (b = b.parentNode), a = a.slice(j.shift().value .length) } i = X.needsContext.test(a) ? 0 : j.length; while (i--) { if (k = j[i], d.relative[l = k.type]) break; if ((m = d.find[l]) && (f = m(k.matches[0].replace( cb, db), ab.test(j[0].type) && ob(b.parentNode) || b))) { if (j.splice(i, 1), a = f.length && qb(j), ! a) return I.apply(e, f), e; break } } } return (n || h(a, o))(f, b, !p, e, ab.test(a) && ob(b.parentNode) || b), e }, c.sortStable = u.split("").sort(B).join("") === u, c.detectDuplicates = ! !l, m(), c.sortDetached = ib(function(a) { return 1 & a.compareDocumentPosition(n.createElement( "div")) }), ib(function(a) { return a.innerHTML = "", "#" === a.firstChild .getAttribute("href") }) || jb("type|href|height|width", function(a, b, c) { return c ? void 0 : a.getAttribute(b, "type" === b.toLowerCase() ? 1 : 2) }), c.attributes && ib(function(a) { return a.innerHTML = "", a.firstChild.setAttribute( "value", ""), "" === a.firstChild.getAttribute( "value") }) || jb("value", function(a, b, c) { return c || "input" !== a.nodeName.toLowerCase() ? void 0 : a.defaultValue }), ib(function(a) { return null == a.getAttribute("disabled") }) || jb(L, function(a, b, c) { var d; return c ? void 0 : a[b] === !0 ? b.toLowerCase() : (d = a.getAttributeNode(b)) && d.specified ? d.value : null }), fb }(a); m.find = s, m.expr = s.selectors, m.expr[":"] = m.expr.pseudos, m.unique = s.uniqueSort, m.text = s.getText, m.isXMLDoc = s.isXML, m.contains = s.contains; var t = m.expr.match.needsContext, u = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, v = /^.[^:#\[\.,]*$/; function w(a, b, c) { if (m.isFunction(b)) return m.grep(a, function(a, d) { return !!b.call(a, d, a) !== c }); if (b.nodeType) return m.grep(a, function(a) { return a === b !== c }); if ("string" == typeof b) { if (v.test(b)) return m.filter(b, a, c); b = m.filter(b, a) } return m.grep(a, function(a) { return m.inArray(a, b) >= 0 !== c }) } m.filter = function(a, b, c) { var d = b[0]; return c && (a = ":not(" + a + ")"), 1 === b.length && 1 === d.nodeType ? m.find.matchesSelector(d, a) ? [d] : [] : m.find.matches(a, m.grep(b, function(a) { return 1 === a.nodeType })) }, m.fn.extend({ find: function(a) { var b, c = [], d = this, e = d.length; if ("string" != typeof a) return this.pushStack(m(a) .filter(function() { for (b = 0; e > b; b++) if (m.contains(d[b], this)) return !0 })); for (b = 0; e > b; b++) m.find(a, d[b], c); return c = this.pushStack(e > 1 ? m.unique(c) : c), c.selector = this.selector ? this.selector + " " + a : a, c }, filter: function(a) { return this.pushStack(w(this, a || [], !1)) }, not: function(a) { return this.pushStack(w(this, a || [], !0)) }, is: function(a) { return !!w(this, "string" == typeof a && t.test(a) ? m(a) : a || [], !1).length } }); var x, y = a.document, z = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, A = m.fn.init = function(a, b) { var c, d; if (!a) return this; if ("string" == typeof a) { if (c = "<" === a.charAt(0) && ">" === a.charAt(a.length - 1) && a.length >= 3 ? [null, a, null] : z.exec(a), ! c || !c[1] && b) return !b || b.jquery ? (b || x).find( a) : this.constructor(b).find(a); if (c[1]) { if (b = b instanceof m ? b[0] : b, m.merge(this, m.parseHTML( c[1], b && b.nodeType ? b.ownerDocument || b : y, !0)), u.test(c[1]) && m.isPlainObject(b)) for (c in b) m.isFunction(this[c]) ? this[c](b[c]) : this.attr(c, b[c]); return this } if (d = y.getElementById(c[2]), d && d.parentNode) { if (d.id !== c[2]) return x.find(a); this.length = 1, this[0] = d } return this.context = y, this.selector = a, this } return a.nodeType ? (this.context = this[0] = a, this.length = 1, this) : m.isFunction(a) ? "undefined" != typeof x.ready ? x.ready(a) : a(m) : (void 0 !== a.selector && (this.selector = a.selector, this.context = a.context), m.makeArray( a, this)) }; A.prototype = m.fn, x = m(y); var B = /^(?:parents|prev(?:Until|All))/, C = { children: !0, contents: !0, next: !0, prev: !0 }; m.extend({ dir: function(a, b, c) { var d = [], e = a[b]; while (e && 9 !== e.nodeType && (void 0 === c || 1 !== e.nodeType || !m(e).is(c))) 1 === e.nodeType && d.push(e), e = e[b]; return d }, sibling: function(a, b) { for (var c = []; a; a = a.nextSibling) 1 === a.nodeType && a !== b && c.push(a); return c } }), m.fn.extend({ has: function(a) { var b, c = m(a, this), d = c.length; return this.filter(function() { for (b = 0; d > b; b++) if (m.contains(this, c[b])) return ! 0 }) }, closest: function(a, b) { for (var c, d = 0, e = this.length, f = [], g = t.test( a) || "string" != typeof a ? m(a, b || this.context) : 0; e > d; d++) for (c = this[d]; c && c !== b; c = c.parentNode) if (c.nodeType < 11 && (g ? g.index(c) > -1 : 1 === c.nodeType && m.find.matchesSelector( c, a))) { f.push(c); break } return this.pushStack(f.length > 1 ? m.unique(f) : f) }, index: function(a) { return a ? "string" == typeof a ? m.inArray(this[0], m(a)) : m.inArray(a.jquery ? a[0] : a, this) : this[0] && this[0].parentNode ? this.first().prevAll() .length : -1 }, add: function(a, b) { return this.pushStack(m.unique(m.merge(this.get(), m(a, b)))) }, addBack: function(a) { return this.add(null == a ? this.prevObject : this.prevObject .filter(a)) } }); function D(a, b) { do a = a[b]; while (a && 1 !== a.nodeType); return a } m.each({ parent: function(a) { var b = a.parentNode; return b && 11 !== b.nodeType ? b : null }, parents: function(a) { return m.dir(a, "parentNode") }, parentsUntil: function(a, b, c) { return m.dir(a, "parentNode", c) }, next: function(a) { return D(a, "nextSibling") }, prev: function(a) { return D(a, "previousSibling") }, nextAll: function(a) { return m.dir(a, "nextSibling") }, prevAll: function(a) { return m.dir(a, "previousSibling") }, nextUntil: function(a, b, c) { return m.dir(a, "nextSibling", c) }, prevUntil: function(a, b, c) { return m.dir(a, "previousSibling", c) }, siblings: function(a) { return m.sibling((a.parentNode || {}).firstChild, a) }, children: function(a) { return m.sibling(a.firstChild) }, contents: function(a) { return m.nodeName(a, "iframe") ? a.contentDocument || a.contentWindow.document : m.merge([], a.childNodes) } }, function(a, b) { m.fn[a] = function(c, d) { var e = m.map(this, b, c); return "Until" !== a.slice(-5) && (d = c), d && "string" == typeof d && (e = m.filter(d, e)), this.length > 1 && (C[a] || (e = m.unique(e)), B.test(a) && (e = e.reverse())), this.pushStack( e) } }); var E = /\S+/g, F = {}; function G(a) { var b = F[a] = {}; return m.each(a.match(E) || [], function(a, c) { b[c] = !0 }), b } m.Callbacks = function(a) { a = "string" == typeof a ? F[a] || G(a) : m.extend({}, a); var b, c, d, e, f, g, h = [], i = !a.once && [], j = function(l) { for (c = a.memory && l, d = !0, f = g || 0, g = 0, e = h.length, b = !0; h && e > f; f++) if (h[f].apply(l[0], l[1]) === !1 && a.stopOnFalse) { c = !1; break } b = !1, h && (i ? i.length && j(i.shift()) : c ? h = [] : k.disable()) }, k = { add: function() { if (h) { var d = h.length; ! function f(b) { m.each(b, function(b, c) { var d = m.type(c); "function" === d ? a.unique && k.has(c) || h.push( c) : c && c.length && "string" !== d && f( c) }) }(arguments), b ? e = h.length : c && ( g = d, j(c)) } return this }, remove: function() { return h && m.each(arguments, function(a, c) { var d; while ((d = m.inArray(c, h, d)) > - 1) h.splice(d, 1), b && (e >= d && e--, f >= d && f--) }), this }, has: function(a) { return a ? m.inArray(a, h) > -1 : !(!h || !h.length) }, empty: function() { return h = [], e = 0, this }, disable: function() { return h = i = c = void 0, this }, disabled: function() { return !h }, lock: function() { return i = void 0, c || k.disable(), this }, locked: function() { return !i }, fireWith: function(a, c) { return !h || d && !i || (c = c || [], c = [a, c .slice ? c.slice() : c ], b ? i.push(c) : j(c)), this }, fire: function() { return k.fireWith(this, arguments), this }, fired: function() { return !!d } }; return k }, m.extend({ Deferred: function(a) { var b = [ ["resolve", "done", m.Callbacks( "once memory"), "resolved"], ["reject", "fail", m.Callbacks( "once memory"), "rejected"], ["notify", "progress", m.Callbacks("memory")] ], c = "pending", d = { state: function() { return c }, always: function() { return e.done(arguments).fail( arguments), this }, then: function() { var a = arguments; return m.Deferred(function(c) { m.each(b, function(b, f) { var g = m.isFunction( a[b] ) && a[ b]; e[f[1]]( function() { var a = g && g .apply( this, arguments ); a && m .isFunction( a .promise ) ? a .promise() .done( c .resolve ) .fail( c .reject ) .progress( c .notify ) : c[ f[ 0 ] + "With" ] ( this === d ? c .promise() : this, g ? [ a ] : arguments ) }) }), a = null }).promise() }, promise: function(a) { return null != a ? m.extend(a, d) : d } }, e = {}; return d.pipe = d.then, m.each(b, function(a, f) { var g = f[2], h = f[3]; d[f[1]] = g.add, h && g.add(function() { c = h }, b[1 ^ a][2].disable, b[2][2] .lock), e[f[0]] = function() { return e[f[0] + "With"](this === e ? d : this, arguments ), this }, e[f[0] + "With"] = g.fireWith }), d.promise(e), a && a.call(e, e), e }, when: function(a) { var b = 0, c = d.call(arguments), e = c.length, f = 1 !== e || a && m.isFunction(a.promise) ? e : 0, g = 1 === f ? a : m.Deferred(), h = function(a, b, c) { return function(e) { b[a] = this, c[a] = arguments.length > 1 ? d.call(arguments) : e, c === i ? g.notifyWith(b, c) : --f || g.resolveWith(b, c) } }, i, j, k; if (e > 1) for (i = new Array(e), j = new Array(e), k = new Array(e); e > b; b++) c[b] && m.isFunction( c[b].promise) ? c[b].promise().done(h(b, k, c)).fail(g.reject).progress(h(b, j, i)) : --f; return f || g.resolveWith(k, c), g.promise() } }); var H; m.fn.ready = function(a) { return m.ready.promise().done(a), this }, m.extend({ isReady: !1, readyWait: 1, holdReady: function(a) { a ? m.readyWait++ : m.ready(!0) }, ready: function(a) { if (a === !0 ? !--m.readyWait : !m.isReady) { if (!y.body) return setTimeout(m.ready); m.isReady = !0, a !== !0 && --m.readyWait > 0 || (H.resolveWith(y, [m]), m.fn.triggerHandler && (m(y).triggerHandler("ready"), m(y).off( "ready"))) } } }); function I() { y.addEventListener ? (y.removeEventListener("DOMContentLoaded", J, !1), a.removeEventListener("load", J, !1)) : (y.detachEvent( "onreadystatechange", J), a.detachEvent("onload", J)) } function J() { (y.addEventListener || "load" === event.type || "complete" === y.readyState) && (I(), m.ready()) } m.ready.promise = function(b) { if (!H) if (H = m.Deferred(), "complete" === y.readyState) setTimeout(m.ready); else if (y.addEventListener) y.addEventListener( "DOMContentLoaded", J, !1), a.addEventListener("load", J, !1); else { y.attachEvent("onreadystatechange", J), a.attachEvent( "onload", J); var c = !1; try { c = null == a.frameElement && y.documentElement } catch (d) {} c && c.doScroll && ! function e() { if (!m.isReady) { try { c.doScroll("left") } catch (a) { return setTimeout(e, 50) } I(), m.ready() } }() } return H.promise(b) }; var K = "undefined", L; for (L in m(k)) break; k.ownLast = "0" !== L, k.inlineBlockNeedsLayout = !1, m(function() { var a, b, c, d; c = y.getElementsByTagName("body")[0], c && c.style && (b = y.createElement("div"), d = y.createElement("div"), d.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px", c.appendChild(d).appendChild(b), typeof b.style.zoom !== K && (b.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1", k.inlineBlockNeedsLayout = a = 3 === b.offsetWidth, a && (c.style.zoom = 1)), c.removeChild(d)) }), function() { var a = y.createElement("div"); if (null == k.deleteExpando) { k.deleteExpando = !0; try { delete a.test } catch (b) { k.deleteExpando = !1 } } a = null }(), m.acceptData = function(a) { var b = m.noData[(a.nodeName + " ").toLowerCase()], c = +a.nodeType || 1; return 1 !== c && 9 !== c ? !1 : !b || b !== !0 && a.getAttribute( "classid") === b }; var M = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, N = /([A-Z])/g; function O(a, b, c) { if (void 0 === c && 1 === a.nodeType) { var d = "data-" + b.replace(N, "-$1").toLowerCase(); if (c = a.getAttribute(d), "string" == typeof c) { try { c = "true" === c ? !0 : "false" === c ? !1 : "null" === c ? null : +c + "" === c ? +c : M.test(c) ? m.parseJSON( c) : c } catch (e) {} m.data(a, b, c) } else c = void 0 } return c } function P(a) { var b; for (b in a) if (("data" !== b || !m.isEmptyObject(a[b])) && "toJSON" !== b) return !1; return !0 } function Q(a, b, d, e) { if (m.acceptData(a)) { var f, g, h = m.expando, i = a.nodeType, j = i ? m.cache : a, k = i ? a[h] : a[h] && h; if (k && j[k] && (e || j[k].data) || void 0 !== d || "string" != typeof b) return k || (k = i ? a[h] = c.pop() || m.guid++ : h), j[k] || (j[k] = i ? {} : { toJSON: m.noop }), ("object" == typeof b || "function" == typeof b) && (e ? j[k] = m.extend(j[k], b) : j[k].data = m.extend( j[k].data, b)), g = j[k], e || (g.data || (g.data = {}), g = g.data), void 0 !== d && (g[m.camelCase(b)] = d), "string" == typeof b ? (f = g[b], null == f && (f = g[m.camelCase(b)])) : f = g, f } } function R(a, b, c) { if (m.acceptData(a)) { var d, e, f = a.nodeType, g = f ? m.cache : a, h = f ? a[m.expando] : m.expando; if (g[h]) { if (b && (d = c ? g[h] : g[h].data)) { m.isArray(b) ? b = b.concat(m.map(b, m.camelCase)) : b in d ? b = [b] : (b = m.camelCase(b), b = b in d ? [b] : b.split(" ")), e = b.length; while (e--) delete d[b[e]]; if (c ? !P(d) : !m.isEmptyObject(d)) return }(c || (delete g[h].data, P(g[h]))) && (f ? m.cleanData( [a], !0) : k.deleteExpando || g != g.window ? delete g[h] : g[h] = null) } } } m.extend({ cache: {}, noData: { "applet ": !0, "embed ": !0, "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" }, hasData: function(a) { return a = a.nodeType ? m.cache[a[m.expando]] : a[m .expando], !!a && !P(a) }, data: function(a, b, c) { return Q(a, b, c) }, removeData: function(a, b) { return R(a, b) }, _data: function(a, b, c) { return Q(a, b, c, !0) }, _removeData: function(a, b) { return R(a, b, !0) } }), m.fn.extend({ data: function(a, b) { var c, d, e, f = this[0], g = f && f.attributes; if (void 0 === a) { if (this.length && (e = m.data(f), 1 === f.nodeType && !m._data(f, "parsedAttrs"))) { c = g.length; while (c--) g[c] && (d = g[c].name, 0 === d .indexOf("data-") && (d = m.camelCase( d.slice(5)), O(f, d, e[d]))); m._data(f, "parsedAttrs", !0) } return e } return "object" == typeof a ? this.each(function() { m.data(this, a) }) : arguments.length > 1 ? this.each(function() { m.data(this, a, b) }) : f ? O(f, a, m.data(f, a)) : void 0 }, removeData: function(a) { return this.each(function() { m.removeData(this, a) }) } }), m.extend({ queue: function(a, b, c) { var d; return a ? (b = (b || "fx") + "queue", d = m._data( a, b), c && (!d || m.isArray(c) ? d = m ._data(a, b, m.makeArray(c)) : d.push(c) ), d || []) : void 0 }, dequeue: function(a, b) { b = b || "fx"; var c = m.queue(a, b), d = c.length, e = c.shift(), f = m._queueHooks(a, b), g = function() { m.dequeue(a, b) }; "inprogress" === e && (e = c.shift(), d--), e && ( "fx" === b && c.unshift("inprogress"), delete f.stop, e.call(a, g, f)), !d && f && f.empty.fire() }, _queueHooks: function(a, b) { var c = b + "queueHooks"; return m._data(a, c) || m._data(a, c, { empty: m.Callbacks("once memory").add( function() { m._removeData(a, b + "queue"), m._removeData( a, c) }) }) } }), m.fn.extend({ queue: function(a, b) { var c = 2; return "string" != typeof a && (b = a, a = "fx", c--), arguments.length < c ? m.queue(this[0], a) : void 0 === b ? this : this.each(function() { var c = m.queue(this, a, b); m._queueHooks(this, a), "fx" === a && "inprogress" !== c[0] && m.dequeue( this, a) }) }, dequeue: function(a) { return this.each(function() { m.dequeue(this, a) }) }, clearQueue: function(a) { return this.queue(a || "fx", []) }, promise: function(a, b) { var c, d = 1, e = m.Deferred(), f = this, g = this.length, h = function() { --d || e.resolveWith(f, [f]) }; "string" != typeof a && (b = a, a = void 0), a = a || "fx"; while (g--) c = m._data(f[g], a + "queueHooks"), c && c.empty && (d++, c.empty.add(h)); return h(), e.promise(b) } }); var S = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, T = ["Top", "Right", "Bottom", "Left"], U = function(a, b) { return a = b || a, "none" === m.css(a, "display") || !m.contains( a.ownerDocument, a) }, V = m.access = function(a, b, c, d, e, f, g) { var h = 0, i = a.length, j = null == c; if ("object" === m.type(c)) { e = !0; for (h in c) m.access(a, b, h, c[h], !0, f, g) } else if (void 0 !== d && (e = !0, m.isFunction(d) || (g = !0), j && (g ? (b.call(a, d), b = null) : (j = b, b = function(a, b, c) { return j.call(m(a), c) })), b)) for (; i > h; h++) b(a[h], c, g ? d : d.call(a[h], h, b(a[h], c))); return e ? a : j ? b.call(a) : i ? b(a[0], c) : f }, W = /^(?:checkbox|radio)$/i; ! function() { var a = y.createElement("input"), b = y.createElement("div"), c = y.createDocumentFragment(); if (b.innerHTML = "
    a", k.leadingWhitespace = 3 === b.firstChild.nodeType, k.tbody = !b .getElementsByTagName("tbody").length, k.htmlSerialize = !!b.getElementsByTagName( "link").length, k.html5Clone = "<:nav>" !== y.createElement( "nav").cloneNode(!0).outerHTML, a.type = "checkbox", a.checked = ! 0, c.appendChild(a), k.appendChecked = a.checked, b.innerHTML = "", k.noCloneChecked = !!b.cloneNode(!0).lastChild .defaultValue, c.appendChild(b), b.innerHTML = "", k.checkClone = b.cloneNode(!0).cloneNode(!0).lastChild.checked, k.noCloneEvent = ! 0, b.attachEvent && (b.attachEvent("onclick", function() { k.noCloneEvent = !1 }), b.cloneNode(!0).click()), null == k.deleteExpando) { k.deleteExpando = !0; try { delete b.test } catch (d) { k.deleteExpando = !1 } } }(), function() { var b, c, d = y.createElement("div"); for (b in { submit: !0, change: !0, focusin: !0 }) c = "on" + b, (k[b + "Bubbles"] = c in a) || (d.setAttribute(c, "t"), k[b + "Bubbles"] = d.attributes[c].expando === !1); d = null }(); var X = /^(?:input|select|textarea)$/i, Y = /^key/, Z = /^(?:mouse|pointer|contextmenu)|click/, $ = /^(?:focusinfocus|focusoutblur)$/, _ = /^([^.]*)(?:\.(.+)|)$/; function ab() { return !0 } function bb() { return !1 } function cb() { try { return y.activeElement } catch (a) {} } m.event = { global: {}, add: function(a, b, c, d, e) { var f, g, h, i, j, k, l, n, o, p, q, r = m._data(a); if (r) { c.handler && (i = c, c = i.handler, e = i.selector), c.guid || (c.guid = m.guid++), (g = r.events) || (g = r.events = {}), (k = r.handle) || (k = r.handle = function(a) { return typeof m === K || a && m.event.triggered === a.type ? void 0 : m.event.dispatch.apply( k.elem, arguments) }, k.elem = a), b = (b || "").match(E) || [ "" ], h = b.length; while (h--) f = _.exec(b[h]) || [], o = q = f[1], p = (f[2] || "").split(".").sort(), o && (j = m.event .special[o] || {}, o = (e ? j.delegateType : j.bindType) || o, j = m.event.special[o] || {}, l = m.extend({ type: o, origType: q, data: d, handler: c, guid: c.guid, selector: e, needsContext: e && m.expr.match.needsContext .test(e), namespace: p.join(".") }, i), (n = g[o]) || (n = g[o] = [], n.delegateCount = 0, j.setup && j.setup.call(a, d, p, k) !== !1 || (a.addEventListener ? a.addEventListener( o, k, !1) : a.attachEvent && a.attachEvent( "on" + o, k))), j.add && (j.add.call( a, l), l.handler.guid || (l.handler .guid = c.guid)), e ? n.splice(n.delegateCount++, 0, l) : n.push(l), m.event.global[o] = ! 0); a = null } }, remove: function(a, b, c, d, e) { var f, g, h, i, j, k, l, n, o, p, q, r = m.hasData(a) && m._data(a); if (r && (k = r.events)) { b = (b || "").match(E) || [""], j = b.length; while (j--) if (h = _.exec(b[j]) || [], o = q = h[1], p = ( h[2] || "").split(".").sort(), o) { l = m.event.special[o] || {}, o = (d ? l.delegateType : l.bindType) || o, n = k[o] || [], h = h[2] && new RegExp("(^|\\.)" + p.join( "\\.(?:.*\\.|)") + "(\\.|$)"), i = f = n.length; while (f--) g = n[f], !e && q !== g.origType || c && c.guid !== g.guid || h && !h.test( g.namespace) || d && d !== g.selector && ("**" !== d || !g.selector) || (n.splice( f, 1), g.selector && n.delegateCount--, l.remove && l.remove.call(a, g)); i && !n.length && (l.teardown && l.teardown .call(a, p, r.handle) !== !1 || m.removeEvent( a, o, r.handle), delete k[o]) } else for (o in k) m.event.remove(a, o + b[j], c, d, !0); m.isEmptyObject(k) && (delete r.handle, m._removeData( a, "events")) } }, trigger: function(b, c, d, e) { var f, g, h, i, k, l, n, o = [d || y], p = j.call(b, "type") ? b.type : b, q = j.call(b, "namespace") ? b.namespace.split(".") : []; if (h = l = d = d || y, 3 !== d.nodeType && 8 !== d.nodeType && !$.test(p + m.event.triggered) && (p.indexOf(".") >= 0 && (q = p.split("."), p = q.shift(), q.sort()), g = p.indexOf(":") < 0 && "on" + p, b = b[m.expando] ? b : new m.Event(p, "object" == typeof b && b), b.isTrigger = e ? 2 : 3, b.namespace = q.join( "."), b.namespace_re = b.namespace ? new RegExp( "(^|\\.)" + q.join("\\.(?:.*\\.|)") + "(\\.|$)") : null, b.result = void 0, b.target || (b.target = d), c = null == c ? [b] : m.makeArray( c, [b]), k = m.event.special[p] || {}, e || !k.trigger || k.trigger.apply(d, c) !== !1)) { if (!e && !k.noBubble && !m.isWindow(d)) { for (i = k.delegateType || p, $.test(i + p) || (h = h.parentNode); h; h = h.parentNode) o.push( h), l = h; l === (d.ownerDocument || y) && o.push(l.defaultView || l.parentWindow || a) } n = 0; while ((h = o[n++]) && !b.isPropagationStopped()) b .type = n > 1 ? i : k.bindType || p, f = (m._data( h, "events") || {})[b.type] && m._data(h, "handle"), f && f.apply(h, c), f = g && h[g], f && f.apply && m.acceptData(h) && (b.result = f.apply(h, c), b.result === !1 && b.preventDefault() ); if (b.type = p, !e && !b.isDefaultPrevented() && (! k._default || k._default.apply(o.pop(), c) === !1) && m.acceptData(d) && g && d[p] && !m.isWindow( d)) { l = d[g], l && (d[g] = null), m.event.triggered = p; try { d[p]() } catch (r) {} m.event.triggered = void 0, l && (d[g] = l) } return b.result } }, dispatch: function(a) { a = m.event.fix(a); var b, c, e, f, g, h = [], i = d.call(arguments), j = (m._data(this, "events") || {})[a.type] || [], k = m.event.special[a.type] || {}; if (i[0] = a, a.delegateTarget = this, !k.preDispatch || k.preDispatch.call(this, a) !== !1) { h = m.event.handlers.call(this, a, j), b = 0; while ((f = h[b++]) && !a.isPropagationStopped()) { a.currentTarget = f.elem, g = 0; while ((e = f.handlers[g++]) && !a.isImmediatePropagationStopped()) (!a.namespace_re || a.namespace_re.test(e.namespace)) && (a.handleObj = e, a.data = e.data, c = ((m.event .special[e.origType] || {}).handle || e.handler).apply(f.elem, i), void 0 !== c && (a.result = c) === !1 && (a.preventDefault(), a.stopPropagation())) } return k.postDispatch && k.postDispatch.call(this, a), a.result } }, handlers: function(a, b) { var c, d, e, f, g = [], h = b.delegateCount, i = a.target; if (h && i.nodeType && (!a.button || "click" !== a.type)) for (; i != this; i = i.parentNode || this) if (1 === i.nodeType && (i.disabled !== !0 || "click" !== a.type)) { for (e = [], f = 0; h > f; f++) d = b[f], c = d.selector + " ", void 0 === e[c] && (e[ c] = d.needsContext ? m(c, this) .index(i) >= 0 : m.find(c, this, null, [i]).length), e[c] && e.push( d); e.length && g.push({ elem: i, handlers: e }) } return h < b.length && g.push({ elem: this, handlers: b.slice(h) }), g }, fix: function(a) { if (a[m.expando]) return a; var b, c, d, e = a.type, f = a, g = this.fixHooks[e]; g || (this.fixHooks[e] = g = Z.test(e) ? this.mouseHooks : Y.test(e) ? this.keyHooks : {}), d = g.props ? this.props.concat(g.props) : this.props, a = new m.Event( f), b = d.length; while (b--) c = d[b], a[c] = f[c]; return a.target || (a.target = f.srcElement || y), 3 === a.target.nodeType && (a.target = a.target.parentNode), a.metaKey = !!a.metaKey, g.filter ? g.filter(a, f) : a }, props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which" .split(" "), fixHooks: {}, keyHooks: { props: "char charCode key keyCode".split(" "), filter: function(a, b) { return null == a.which && (a.which = null != b.charCode ? b.charCode : b.keyCode), a } }, mouseHooks: { props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement" .split(" "), filter: function(a, b) { var c, d, e, f = b.button, g = b.fromElement; return null == a.pageX && null != b.clientX && (d = a.target.ownerDocument || y, e = d.documentElement, c = d.body, a.pageX = b.clientX + (e && e.scrollLeft || c && c.scrollLeft || 0) - (e && e.clientLeft || c && c.clientLeft || 0), a.pageY = b.clientY + (e && e.scrollTop || c && c.scrollTop || 0) - (e && e.clientTop || c && c.clientTop || 0) ), !a.relatedTarget && g && (a.relatedTarget = g === a.target ? b.toElement : g), a.which || void 0 === f || (a.which = 1 & f ? 1 : 2 & f ? 3 : 4 & f ? 2 : 0), a } }, special: { load: { noBubble: !0 }, focus: { trigger: function() { if (this !== cb() && this.focus) try { return this.focus(), !1 } catch (a) {} }, delegateType: "focusin" }, blur: { trigger: function() { return this === cb() && this.blur ? (this.blur(), ! 1) : void 0 }, delegateType: "focusout" }, click: { trigger: function() { return m.nodeName(this, "input") && "checkbox" === this.type && this.click ? (this.click(), !1) : void 0 }, _default: function(a) { return m.nodeName(a.target, "a") } }, beforeunload: { postDispatch: function(a) { void 0 !== a.result && a.originalEvent && (a.originalEvent .returnValue = a.result) } } }, simulate: function(a, b, c, d) { var e = m.extend(new m.Event, c, { type: a, isSimulated: !0, originalEvent: {} }); d ? m.event.trigger(e, null, b) : m.event.dispatch.call( b, e), e.isDefaultPrevented() && c.preventDefault() } }, m.removeEvent = y.removeEventListener ? function(a, b, c) { a.removeEventListener && a.removeEventListener(b, c, !1) } : function(a, b, c) { var d = "on" + b; a.detachEvent && (typeof a[d] === K && (a[d] = null), a.detachEvent( d, c)) }, m.Event = function(a, b) { return this instanceof m.Event ? (a && a.type ? (this.originalEvent = a, this.type = a.type, this.isDefaultPrevented = a.defaultPrevented || void 0 === a.defaultPrevented && a.returnValue === !1 ? ab : bb) : this.type = a, b && m.extend(this, b), this.timeStamp = a && a.timeStamp || m.now(), void(this[m.expando] = !0)) : new m.Event(a, b) }, m.Event.prototype = { isDefaultPrevented: bb, isPropagationStopped: bb, isImmediatePropagationStopped: bb, preventDefault: function() { var a = this.originalEvent; this.isDefaultPrevented = ab, a && (a.preventDefault ? a.preventDefault() : a.returnValue = !1) }, stopPropagation: function() { var a = this.originalEvent; this.isPropagationStopped = ab, a && (a.stopPropagation && a.stopPropagation(), a.cancelBubble = !0) }, stopImmediatePropagation: function() { var a = this.originalEvent; this.isImmediatePropagationStopped = ab, a && a.stopImmediatePropagation && a.stopImmediatePropagation(), this.stopPropagation() } }, m.each({ mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", pointerleave: "pointerout" }, function(a, b) { m.event.special[a] = { delegateType: b, bindType: b, handle: function(a) { var c, d = this, e = a.relatedTarget, f = a.handleObj; return (!e || e !== d && !m.contains(d, e)) && (a.type = f.origType, c = f.handler.apply( this, arguments), a.type = b), c } } }), k.submitBubbles || (m.event.special.submit = { setup: function() { return m.nodeName(this, "form") ? !1 : void m.event .add(this, "click._submit keypress._submit", function(a) { var b = a.target, c = m.nodeName(b, "input") || m.nodeName( b, "button") ? b.form : void 0; c && !m._data(c, "submitBubbles") && (m .event.add(c, "submit._submit", function(a) { a._submit_bubble = !0 }), m._data(c, "submitBubbles", !0)) }) }, postDispatch: function(a) { a._submit_bubble && (delete a._submit_bubble, this.parentNode && !a.isTrigger && m.event.simulate("submit", this.parentNode, a, !0)) }, teardown: function() { return m.nodeName(this, "form") ? !1 : void m.event .remove(this, "._submit") } }), k.changeBubbles || (m.event.special.change = { setup: function() { return X.test(this.nodeName) ? (("checkbox" === this.type || "radio" === this.type) && (m.event.add(this, "propertychange._change", function(a) { "checked" === a.originalEvent.propertyName && (this._just_changed = !0) }), m.event.add(this, "click._change", function(a) { this._just_changed && !a.isTrigger && (this._just_changed = !1), m.event.simulate("change", this, a, !0) })), !1) : void m.event.add(this, "beforeactivate._change", function(a) { var b = a.target; X.test(b.nodeName) && !m._data(b, "changeBubbles") && (m.event.add( b, "change._change", function(a) { !this.parentNode || a.isSimulated || a.isTrigger || m.event .simulate("change", this.parentNode, a, !0) }), m._data(b, "changeBubbles", !0)) }) }, handle: function(a) { var b = a.target; return this !== b || a.isSimulated || a.isTrigger || "radio" !== b.type && "checkbox" !== b.type ? a .handleObj.handler.apply(this, arguments) : void 0 }, teardown: function() { return m.event.remove(this, "._change"), !X.test( this.nodeName) } }), k.focusinBubbles || m.each({ focus: "focusin", blur: "focusout" }, function(a, b) { var c = function(a) { m.event.simulate(b, a.target, m.event.fix(a), !0) }; m.event.special[b] = { setup: function() { var d = this.ownerDocument || this, e = m._data(d, b); e || d.addEventListener(a, c, !0), m._data( d, b, (e || 0) + 1) }, teardown: function() { var d = this.ownerDocument || this, e = m._data(d, b) - 1; e ? m._data(d, b, e) : (d.removeEventListener( a, c, !0), m._removeData(d, b)) } } }), m.fn.extend({ on: function(a, b, c, d, e) { var f, g; if ("object" == typeof a) { "string" != typeof b && (c = c || b, b = void 0); for (f in a) this.on(f, b, c, a[f], e); return this } if (null == c && null == d ? (d = b, c = b = void 0) : null == d && ("string" == typeof b ? (d = c, c = void 0) : (d = c, c = b, b = void 0)), d === !1) d = bb; else if (!d) return this; return 1 === e && (g = d, d = function(a) { return m().off(a), g.apply(this, arguments) }, d.guid = g.guid || (g.guid = m.guid++)), this.each(function() { m.event.add(this, a, d, c, b) }) }, one: function(a, b, c, d) { return this.on(a, b, c, d, 1) }, off: function(a, b, c) { var d, e; if (a && a.preventDefault && a.handleObj) return d = a.handleObj, m(a.delegateTarget).off(d.namespace ? d.origType + "." + d.namespace : d.origType, d.selector, d.handler), this; if ("object" == typeof a) { for (e in a) this.off(e, b, a[e]); return this } return (b === !1 || "function" == typeof b) && (c = b, b = void 0), c === !1 && (c = bb), this.each( function() { m.event.remove(this, a, c, b) }) }, trigger: function(a, b) { return this.each(function() { m.event.trigger(a, b, this) }) }, triggerHandler: function(a, b) { var c = this[0]; return c ? m.event.trigger(a, b, c, !0) : void 0 } }); function db(a) { var b = eb.split("|"), c = a.createDocumentFragment(); if (c.createElement) while (b.length) c.createElement(b.pop()); return c } var eb = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", fb = / jQuery\d+="(?:null|\d+)"/g, gb = new RegExp("<(?:" + eb + ")[\\s/>]", "i"), hb = /^\s+/, ib = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, jb = /<([\w:]+)/, kb = /\s*$/g, rb = { option: [1, ""], legend: [1, "
    ", "
    "], area: [1, "", ""], param: [1, "", ""], thead: [1, "", "
    "], tr: [2, "", "
    "], col: [2, "", "
    " ], td: [3, "", "
    "], _default: k.htmlSerialize ? [0, "", ""] : [1, "X
    ", "
    " ] }, sb = db(y), tb = sb.appendChild(y.createElement("div")); rb.optgroup = rb.option, rb.tbody = rb.tfoot = rb.colgroup = rb.caption = rb.thead, rb.th = rb.td; function ub(a, b) { var c, d, e = 0, f = typeof a.getElementsByTagName !== K ? a.getElementsByTagName( b || "*") : typeof a.querySelectorAll !== K ? a.querySelectorAll( b || "*") : void 0; if (!f) for (f = [], c = a.childNodes || a; null != (d = c[e]); e++) !b || m.nodeName(d, b) ? f.push(d) : m.merge(f, ub(d, b)); return void 0 === b || b && m.nodeName(a, b) ? m.merge([a], f) : f } function vb(a) { W.test(a.type) && (a.defaultChecked = a.checked) } function wb(a, b) { return m.nodeName(a, "table") && m.nodeName(11 !== b.nodeType ? b : b.firstChild, "tr") ? a.getElementsByTagName( "tbody")[0] || a.appendChild(a.ownerDocument.createElement( "tbody")) : a } function xb(a) { return a.type = (null !== m.find.attr(a, "type")) + "/" + a.type, a } function yb(a) { var b = pb.exec(a.type); return b ? a.type = b[1] : a.removeAttribute("type"), a } function zb(a, b) { for (var c, d = 0; null != (c = a[d]); d++) m._data(c, "globalEval", !b || m._data(b[d], "globalEval")) } function Ab(a, b) { if (1 === b.nodeType && m.hasData(a)) { var c, d, e, f = m._data(a), g = m._data(b, f), h = f.events; if (h) { delete g.handle, g.events = {}; for (c in h) for (d = 0, e = h[c].length; e > d; d++) m.event.add( b, c, h[c][d]) } g.data && (g.data = m.extend({}, g.data)) } } function Bb(a, b) { var c, d, e; if (1 === b.nodeType) { if (c = b.nodeName.toLowerCase(), !k.noCloneEvent && b[m.expando]) { e = m._data(b); for (d in e.events) m.removeEvent(b, d, e.handle); b.removeAttribute(m.expando) } "script" === c && b.text !== a.text ? (xb(b).text = a.text, yb(b)) : "object" === c ? (b.parentNode && (b.outerHTML = a.outerHTML), k.html5Clone && a.innerHTML && !m .trim(b.innerHTML) && (b.innerHTML = a.innerHTML)) : "input" === c && W.test(a.type) ? (b.defaultChecked = b .checked = a.checked, b.value !== a.value && (b.value = a.value)) : "option" === c ? b.defaultSelected = b.selected = a.defaultSelected : ("input" === c || "textarea" === c) && (b.defaultValue = a.defaultValue) } } m.extend({ clone: function(a, b, c) { var d, e, f, g, h, i = m.contains(a.ownerDocument, a); if (k.html5Clone || m.isXMLDoc(a) || !gb.test("<" + a.nodeName + ">") ? f = a.cloneNode(!0) : ( tb.innerHTML = a.outerHTML, tb.removeChild( f = tb.firstChild)), !(k.noCloneEvent && k.noCloneChecked || 1 !== a.nodeType && 11 !== a.nodeType || m.isXMLDoc(a))) for (d = ub(f), h = ub(a), g = 0; null != (e = h[g]); ++g) d[g] && Bb(e, d[g]); if (b) if (c) for (h = h || ub(a), d = d || ub(f), g = 0; null != (e = h[g]); g++) Ab(e, d[g]); else Ab(a, f); return d = ub(f, "script"), d.length > 0 && zb(d, ! i && ub(a, "script")), d = h = e = null, f }, buildFragment: function(a, b, c, d) { for (var e, f, g, h, i, j, l, n = a.length, o = db( b), p = [], q = 0; n > q; q++) if (f = a[q], f || 0 === f) if ("object" === m.type(f)) m.merge(p, f.nodeType ? [ f ] : f); else if (lb.test(f)) { h = h || o.appendChild(b.createElement("div")), i = (jb.exec(f) || ["", ""])[1].toLowerCase(), l = rb[i] || rb._default, h.innerHTML = l[1] + f.replace(ib, "<$1>") + l[2], e = l[0]; while (e--) h = h.lastChild; if (!k.leadingWhitespace && hb.test(f) && p.push( b.createTextNode(hb.exec(f)[0])), !k.tbody) { f = "table" !== i || kb.test(f) ? "" !== l[1] || kb.test(f) ? 0 : h : h.firstChild, e = f && f.childNodes.length; while (e--) m.nodeName(j = f.childNodes[e], "tbody") && !j.childNodes.length && f.removeChild(j) } m.merge(p, h.childNodes), h.textContent = ""; while (h.firstChild) h.removeChild(h.firstChild); h = o.lastChild } else p.push(b.createTextNode(f)); h && o.removeChild(h), k.appendChecked || m.grep(ub( p, "input"), vb), q = 0; while (f = p[q++]) if ((!d || -1 === m.inArray(f, d)) && (g = m.contains( f.ownerDocument, f), h = ub(o.appendChild( f), "script"), g && zb(h), c)) { e = 0; while (f = h[e++]) ob.test(f.type || "") && c.push(f) } return h = null, o }, cleanData: function(a, b) { for (var d, e, f, g, h = 0, i = m.expando, j = m.cache, l = k.deleteExpando, n = m.event.special; null != (d = a[h]); h++) if ((b || m.acceptData(d)) && (f = d[i], g = f && j[f])) { if (g.events) for (e in g.events) n[e] ? m.event.remove( d, e) : m.removeEvent(d, e, g.handle); j[f] && (delete j[f], l ? delete d[i] : typeof d.removeAttribute !== K ? d.removeAttribute( i) : d[i] = null, c.push(f)) } } }), m.fn.extend({ text: function(a) { return V(this, function(a) { return void 0 === a ? m.text(this) : this.empty().append((this[0] && this[0].ownerDocument || y) .createTextNode(a)) }, null, a, arguments.length) }, append: function() { return this.domManip(arguments, function(a) { if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) { var b = wb(this, a); b.appendChild(a) } }) }, prepend: function() { return this.domManip(arguments, function(a) { if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) { var b = wb(this, a); b.insertBefore(a, b.firstChild) } }) }, before: function() { return this.domManip(arguments, function(a) { this.parentNode && this.parentNode.insertBefore( a, this) }) }, after: function() { return this.domManip(arguments, function(a) { this.parentNode && this.parentNode.insertBefore( a, this.nextSibling) }) }, remove: function(a, b) { for (var c, d = a ? m.filter(a, this) : this, e = 0; null != (c = d[e]); e++) b || 1 !== c.nodeType || m.cleanData( ub(c)), c.parentNode && (b && m.contains(c.ownerDocument, c) && zb(ub(c, "script")), c.parentNode .removeChild(c)); return this }, empty: function() { for (var a, b = 0; null != (a = this[b]); b++) { 1 === a.nodeType && m.cleanData(ub(a, !1)); while (a.firstChild) a.removeChild(a.firstChild); a.options && m.nodeName(a, "select") && (a.options .length = 0) } return this }, clone: function(a, b) { return a = null == a ? !1 : a, b = null == b ? a : b, this.map(function() { return m.clone(this, a, b) }) }, html: function(a) { return V(this, function(a) { var b = this[0] || {}, c = 0, d = this.length; if (void 0 === a) return 1 === b.nodeType ? b.innerHTML.replace(fb, "") : void 0; if (!("string" != typeof a || mb.test(a) || !k.htmlSerialize && gb.test(a) || !k.leadingWhitespace && hb.test( a) || rb[(jb.exec(a) || ["", "" ])[1].toLowerCase()])) { a = a.replace(ib, "<$1>"); try { for (; d > c; c++) b = this[c] || {}, 1 === b.nodeType && (m.cleanData( ub(b, !1)), b.innerHTML = a); b = 0 } catch (e) {} } b && this.empty().append(a) }, null, a, arguments.length) }, replaceWith: function() { var a = arguments[0]; return this.domManip(arguments, function(b) { a = this.parentNode, m.cleanData(ub( this)), a && a.replaceChild(b, this) }), a && (a.length || a.nodeType) ? this : this .remove() }, detach: function(a) { return this.remove(a, !0) }, domManip: function(a, b) { a = e.apply([], a); var c, d, f, g, h, i, j = 0, l = this.length, n = this, o = l - 1, p = a[0], q = m.isFunction(p); if (q || l > 1 && "string" == typeof p && !k.checkClone && nb.test(p)) return this.each(function(c) { var d = n.eq(c); q && (a[0] = p.call(this, c, d.html())), d.domManip(a, b) }); if (l && (i = m.buildFragment(a, this[0].ownerDocument, ! 1, this), c = i.firstChild, 1 === i.childNodes .length && (i = c), c)) { for (g = m.map(ub(i, "script"), xb), f = g.length; l > j; j++) d = i, j !== o && (d = m.clone(d, ! 0, !0), f && m.merge(g, ub(d, "script"))), b.call(this[j], d, j); if (f) for (h = g[g.length - 1].ownerDocument, m.map( g, yb), j = 0; f > j; j++) d = g[j], ob .test(d.type || "") && !m._data(d, "globalEval") && m.contains(h, d) && (d.src ? m._evalUrl && m._evalUrl(d.src) : m.globalEval((d.text || d.textContent || d.innerHTML || "").replace( qb, ""))); i = c = null } return this } }), m.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function(a, b) { m.fn[a] = function(a) { for (var c, d = 0, e = [], g = m(a), h = g.length - 1; h >= d; d++) c = d === h ? this : this.clone(! 0), m(g[d])[b](c), f.apply(e, c.get()); return this.pushStack(e) } }); var Cb, Db = {}; function Eb(b, c) { var d, e = m(c.createElement(b)).appendTo(c.body), f = a.getDefaultComputedStyle && (d = a.getDefaultComputedStyle( e[0])) ? d.display : m.css(e[0], "display"); return e.detach(), f } function Fb(a) { var b = y, c = Db[a]; return c || (c = Eb(a, b), "none" !== c && c || (Cb = (Cb || m( "