1 // extensions: xls;xlsx;xlsm;xlsb;xlam
\r
3 // TortoiseSVN Diff script for Excel files
\r
5 // Copyright (C) 2004-2008, 2012-2015 the TortoiseSVN team
\r
6 // This file is distributed under the same license as TortoiseSVN
\r
9 // $Author: steveking $
\r
10 // $Date: 2015-10-26 20:01:19 +0100 (Mo, 26 Okt 2015) $
\r
14 // Hiroki Najima <h.najima at gmail.com>, 2013
\r
15 // Michael Joras <michael@joras.net>, 2008
\r
16 // Suraj Barkale, 2006
\r
19 // ----- configuration -----
\r
21 // Fast mode does not copy Worksheets but require opened base document at the same time.
\r
22 var bFastMode = false;
\r
24 // ----- constants -----
\r
25 var vbCritical = 0x10;
\r
26 var vbExclamation = 0x30;
\r
27 //var vbInformation = 0x40;
\r
30 var xlMaximized = -4137;
\r
31 var xlArrangeStyleHorizontal = -4128;
\r
32 var xlCellValue = 1;
\r
33 //var xlExpression = 2;
\r
37 //var vOffice95 = 7;
\r
38 //var vOffice97 = 8;
\r
39 //var vOffice2000 = 9;
\r
40 //var vOffice2002 = 10;
\r
41 var vOffice2003 = 11;
\r
42 //var vOffice2007 = 12;
\r
43 //var vOffice2010 = 14;
\r
44 //var vOffice2013 = 15;
\r
48 var aWarningMessages = [];
\r
50 var objArgs = WScript.Arguments;
\r
51 if (objArgs.length < 2)
\r
53 Abort("Usage: [CScript | WScript] diff-xls.js base.xls new.xls", "Invalid arguments");
\r
56 var sBaseDoc = objArgs(0);
\r
57 var sNewDoc = objArgs(1);
\r
59 var objScript = new ActiveXObject("Scripting.FileSystemObject");
\r
61 if (objScript.GetBaseName(sBaseDoc) === objScript.GetBaseName(sNewDoc))
\r
63 Abort("File '" + sBaseDoc + "' and '" + sNewDoc + "' is same file name.\nCannot compare the documents.", "Same file name");
\r
66 if (!objScript.FileExists(sBaseDoc))
\r
68 Abort("File '" + sBaseDoc + "' does not exist.\nCannot compare the documents.", "File not found");
\r
71 if (!objScript.FileExists(sNewDoc))
\r
73 Abort("File '" + sNewDoc + "' does not exist.\nCannot compare the documents.", "File not found");
\r
76 sBaseDoc = objScript.GetAbsolutePathName(sBaseDoc);
\r
77 sNewDoc = objScript.GetAbsolutePathName(sNewDoc);
\r
83 objExcelApp = WScript.CreateObject("Excel.Application");
\r
87 Abort("You must have Excel installed to perform this operation.", "Excel Instantiation Failed");
\r
89 var fExcelVersion = parseInt(objExcelApp.Version, 10);
\r
91 // Open base Excel book
\r
92 var objBaseWorkbook;
\r
95 objBaseWorkbook = objExcelApp.Workbooks.Open(sBaseDoc, null, true);
\r
99 Abort("Failed to open '" + sBaseDoc + "'\nIt might not be a valid Excel file.", "File open error");
\r
102 // Open new Excel book
\r
103 var objNewWorkbook;
\r
106 objNewWorkbook = objExcelApp.Workbooks.Open(sNewDoc, null, true);
\r
110 Abort("Failed to open '" + sNewDoc + "'\nIt might not be a valid Excel file.", "File open error");
\r
113 // Show Excel window
\r
114 objExcelApp.Visible = true;
\r
117 if (objBaseWorkbook.ProtectWindows || objNewWorkbook.ProtectWindows)
\r
119 StoreWarning("Unable to arrange windows because one or both Workbooks are protected.");
\r
123 // Make windows a compare side by side view
\r
124 if (fExcelVersion >= vOffice2003)
\r
126 objExcelApp.Windows.CompareSideBySideWith(objExcelApp.Windows(2).Caption);
\r
128 objExcelApp.Application.WindowState = xlMaximized;
\r
129 objExcelApp.Windows.Arrange(xlArrangeStyleHorizontal);
\r
132 if (!bFastMode && objNewWorkbook.ProtectWindows)
\r
134 StoreWarning("Fall back to fast mode because " + objNewWorkbook.Name + " is protected.");
\r
138 // Create a special workbook for formula convertion.
\r
139 var objSpecialWorkbook = objExcelApp.Workbooks.Add;
\r
141 // Mark differences in sNewDoc red
\r
142 var length = objNewWorkbook.Worksheets.Count;
\r
143 for (var i = 1; i <= length; i++)
\r
145 var objBaseWorksheet = null;
\r
146 if (i <= objBaseWorkbook.Worksheets.Count)
\r
147 objBaseWorksheet = objBaseWorkbook.Worksheets(i);
\r
148 var objNewWorksheet = objNewWorkbook.Worksheets(i);
\r
150 if (objBaseWorksheet != null)
\r
151 UnhideWorksheet(objBaseWorksheet);
\r
152 UnhideWorksheet(objNewWorksheet);
\r
154 if (!bFastMode && (objBaseWorksheet != null))
\r
156 objBaseWorkbook.Sheets(i).Copy(null, objNewWorkbook.Sheets(objNewWorkbook.Sheets.Count));
\r
157 var objDummyWorksheet = objNewWorkbook.Sheets(objNewWorkbook.Sheets.Count);
\r
158 objDummyWorksheet.Name = "Dummy_for_Comparison" + i;
\r
159 objDummyWorksheet.Visible = true;
\r
160 if (fExcelVersion >= vOffice2003)
\r
162 objDummyWorksheet.Tab.ColorIndex = 16; // 16:Dark gray RGB(128,128,128)
\r
166 if (objNewWorksheet.ProtectContents)
\r
168 StoreWarning("Unable to mark differences to " +
\r
169 ToAbsoluteReference(objNewWorksheet) +
\r
170 " because the Worksheet is protected.");
\r
174 objNewWorksheet.Cells.FormatConditions.Delete();
\r
176 if (bFastMode && (objBaseWorksheet != null))
\r
178 sFormula = "=INDIRECT(\"" + ToAbsoluteReference(objBaseWorksheet) + "!\"&ADDRESS(ROW(),COLUMN()))";
\r
182 sFormula = "=INDIRECT(\"Dummy_for_Comparison" + i + "!\"&ADDRESS(ROW(),COLUMN()))";
\r
184 sFormula = convertFormula(sFormula);
\r
185 objNewWorksheet.Cells.FormatConditions.Add(xlCellValue, xlNotEqual, sFormula);
\r
186 objNewWorksheet.Cells.FormatConditions(1).Interior.ColorIndex = 3; // 3:Red RGB(128,0,0)
\r
190 // Close the special workbook quietly
\r
191 objSpecialWorkbook.Saved = true;
\r
192 objSpecialWorkbook.Close();
\r
194 // Activate first Worksheet
\r
195 objBaseWorkbook.Sheets(1).Activate();
\r
196 objNewWorkbook.Sheets(1).Activate();
\r
198 // Suppress save dialog if nothing changed
\r
199 objBaseWorkbook.Saved = true;
\r
200 objNewWorkbook.Saved = true;
\r
202 // Show warnings if exist
\r
208 // ----- functions -----
\r
210 // Show Message Dialog
\r
211 // VBcript's MsgBox emulation
\r
212 function MsgBox(sMessage, iButtons, sTitle)
\r
214 var objShell = new ActiveXObject("WScript.Shell");
\r
215 objShell.popup(sMessage, 0, sTitle, iButtons);
\r
218 // Show an error message and quit script with cleanup Excel Application Object.
\r
219 function Abort(sMessage, sTitle)
\r
221 MsgBox(sMessage, vbCritical, sTitle);
\r
224 objExcelApp.Quit();
\r
229 // Unhide the Worksheet if it is hidden.
\r
230 // This also sets color to the tab, if Office2003 or later.
\r
231 // - 46(Orange) : Hidden Worksheet
\r
232 // - xlNone(default) : Not hidden Worksheet
\r
233 function UnhideWorksheet(objWorksheet)
\r
235 if (objWorksheet.Visible)
\r
237 if (fExcelVersion >= vOffice2003)
\r
239 if (objWorksheet.Tab.ColorIndex !== xlNone)
\r
241 if (objWorksheet.Parent.ProtectStructure)
\r
243 StoreWarning("Unable to set tab color to " +
\r
244 ToAbsoluteReference(objWorksheet) +
\r
245 " because the Workbook's structure is protected.");
\r
249 objWorksheet.Tab.ColorIndex = xlNone;
\r
254 else if (objWorksheet.Parent.ProtectStructure)
\r
256 StoreWarning("Unable to unhide " +
\r
257 ToAbsoluteReference(objWorksheet) +
\r
258 " because the Workbook's structure is protected.");
\r
262 objWorksheet.Visible = true;
\r
263 if (fExcelVersion >= vOffice2003)
\r
265 objWorksheet.Tab.ColorIndex = 10; // 10:Green RGB(0,128,0)
\r
271 // Generate Absolute Reference Formula of Worksheet.
\r
272 function ToAbsoluteReference(objWorksheet)
\r
274 return "[" + objWorksheet.Parent.Name + "]" + objWorksheet.Name;
\r
277 // Convert a formula for workaround in some situation.
\r
278 // Actually I don't know what will be changed between sFormula and FormulaLocal.
\r
279 function convertFormula(sFormula)
\r
281 var worksheet = objSpecialWorkbook.Sheets(1);
\r
282 var originalContent = worksheet.Cells(1, 1).Formula;
\r
283 worksheet.Cells(1, 1).Formula = sFormula;
\r
284 sFormula = worksheet.Cells(1, 1).FormulaLocal;
\r
285 worksheet.Cells(1, 1).Formula = originalContent;
\r
289 // Accumulate a warning message.
\r
290 function StoreWarning(sMessage)
\r
292 aWarningMessages[aWarningMessages.length] = sMessage;
\r
295 // Show accumulated warning messages if exist.
\r
296 // To avoid make huge message dialog, this limits message count to show.
\r
297 function ShowWarning()
\r
299 if (aWarningMessages.length === 0)
\r
303 var sMessage = "The following warnings occurred while processing.\n";
\r
304 for (var j = 0; j < aWarningMessages.length; j++)
\r
308 sMessage += "... And more " + (aWarningMessages.length - j) + " messages";
\r
311 sMessage += "[" + (j + 1) + "] " + aWarningMessages[j] + "\n";
\r
313 MsgBox(sMessage, vbExclamation, "Warning");
\r