1// $Id = LocalFile.js,v 1.12 2010-01-02 09 =45 =14 gaudenz Exp $
2// Copyright (c) 2006-2014, JGraph Ltd
3/**
4 * Constructs a new point for the optional x and y coordinates. If no
5 * coordinates are given, then the default values for <x> and <y> are used.
6 * @constructor
7 * @class Implements a basic 2D point. Known subclassers = {@link mxRectangle}.
8 * @param {number} x X-coordinate of the point.
9 * @param {number} y Y-coordinate of the point.
10 */
11LocalFile = function(ui, data, title, temp, fileHandle, desc)
12{
13	DrawioFile.call(this, ui, data);
14
15	this.title = title;
16	this.mode = (temp) ? null : App.MODE_DEVICE;
17	this.fileHandle = fileHandle;
18	this.desc = desc;
19};
20
21//Extends mxEventSource
22mxUtils.extend(LocalFile, DrawioFile);
23
24/**
25 * Translates this point by the given vector.
26 *
27 * @param {number} dx X-coordinate of the translation.
28 * @param {number} dy Y-coordinate of the translation.
29 */
30LocalFile.prototype.isAutosave = function()
31{
32	return this.fileHandle != null && !this.invalidFileHandle && DrawioFile.prototype.isAutosave.apply(this, arguments);
33};
34
35/**
36 * Specifies if the autosave checkbox should be shown in the document
37 * properties dialog. Default is false.
38 */
39LocalFile.prototype.isAutosaveOptional = function()
40{
41	return this.fileHandle != null;
42};
43
44/**
45 * Translates this point by the given vector.
46 *
47 * @param {number} dx X-coordinate of the translation.
48 * @param {number} dy Y-coordinate of the translation.
49 */
50LocalFile.prototype.getMode = function()
51{
52	return this.mode;
53};
54
55/**
56 * Translates this point by the given vector.
57 *
58 * @param {number} dx X-coordinate of the translation.
59 * @param {number} dy Y-coordinate of the translation.
60 */
61LocalFile.prototype.getTitle = function()
62{
63	return this.title;
64};
65
66/**
67 * Translates this point by the given vector.
68 *
69 * @param {number} dx X-coordinate of the translation.
70 * @param {number} dy Y-coordinate of the translation.
71 */
72LocalFile.prototype.isRenamable = function()
73{
74	return true;
75};
76
77/**
78 * Translates this point by the given vector.
79 *
80 * @param {number} dx X-coordinate of the translation.
81 * @param {number} dy Y-coordinate of the translation.
82 */
83LocalFile.prototype.save = function(revision, success, error)
84{
85	this.saveAs(this.title, success, error);
86};
87
88/**
89 * Translates this point by the given vector.
90 *
91 * @param {number} dx X-coordinate of the translation.
92 * @param {number} dy Y-coordinate of the translation.
93 */
94LocalFile.prototype.saveAs = function(title, success, error)
95{
96	this.saveFile(title, false, success, error);
97};
98
99/**
100 * Translates this point by the given vector.
101 *
102 * @param {number} dx X-coordinate of the translation.
103 * @param {number} dy Y-coordinate of the translation.
104 */
105LocalFile.prototype.saveAs = function(title, success, error)
106{
107	this.saveFile(title, false, success, error);
108};
109
110/**
111 * Adds all listeners.
112 */
113LocalFile.prototype.getDescriptor = function()
114{
115	return this.desc;
116};
117
118/**
119* Updates the descriptor of this file with the one from the given file.
120*/
121LocalFile.prototype.setDescriptor = function(desc)
122{
123	this.desc = desc;
124};
125
126/**
127 * Translates this point by the given vector.
128 *
129 * @param {number} dx X-coordinate of the translation.
130 * @param {number} dy Y-coordinate of the translation.
131 */
132LocalFile.prototype.getLatestVersion = function(success, error)
133{
134	if (this.fileHandle == null)
135	{
136		success(null);
137	}
138	else
139	{
140		this.ui.loadFileSystemEntry(this.fileHandle, success, error);
141	}
142};
143
144/**
145 * Translates this point by the given vector.
146 *
147 * @param {number} dx X-coordinate of the translation.
148 * @param {number} dy Y-coordinate of the translation.
149 */
150LocalFile.prototype.saveFile = function(title, revision, success, error, useCurrentData)
151{
152	if (title != this.title)
153	{
154		this.fileHandle = null;
155		this.desc = null;
156	}
157
158	this.title = title;
159
160	// Updates data after changing file name
161	if (!useCurrentData)
162	{
163		this.updateFileData();
164	}
165
166	var binary = this.ui.useCanvasForExport && /(\.png)$/i.test(this.getTitle());
167	this.setShadowModified(false);
168	var savedData = this.getData();
169
170	var done = mxUtils.bind(this, function()
171	{
172		this.setModified(this.getShadowModified());
173		this.contentChanged();
174
175		if (success != null)
176		{
177			success();
178		}
179	});
180
181	var doSave = mxUtils.bind(this, function(data)
182	{
183		if (this.fileHandle != null)
184		{
185			// Sets shadow modified state during save
186			if (!this.savingFile)
187			{
188				this.savingFileTime = new Date();
189				this.savingFile = true;
190
191				var errorWrapper = mxUtils.bind(this, function(e)
192				{
193					this.savingFile = false;
194
195					if (error != null)
196					{
197						// Wraps error object to offer save status option
198						error({error: e});
199					}
200				});
201
202				// Saves a copy as a draft while saving
203				this.saveDraft();
204
205				this.fileHandle.createWritable().then(mxUtils.bind(this, function(writable)
206				{
207					this.fileHandle.getFile().then(mxUtils.bind(this, function(newDesc)
208					{
209						this.invalidFileHandle = null;
210
211						if (this.desc.lastModified == newDesc.lastModified)
212						{
213							writable.write((binary) ? this.ui.base64ToBlob(data, 'image/png') : data).then(mxUtils.bind(this, function()
214							{
215								writable.close().then(mxUtils.bind(this, function()
216								{
217									this.fileHandle.getFile().then(mxUtils.bind(this, function(desc)
218									{
219										try
220										{
221											var lastDesc = this.desc;
222											this.savingFile = false;
223											this.desc = desc;
224											this.fileSaved(savedData, lastDesc, done, errorWrapper);
225
226											// Deletes draft after saving
227											this.removeDraft();
228										}
229										catch (e)
230										{
231											errorWrapper(e);
232										}
233									}), errorWrapper);
234								}), errorWrapper);
235							}), errorWrapper);
236						}
237						else
238						{
239							this.inConflictState = true;
240							errorWrapper();
241						}
242					}), mxUtils.bind(this, function(e)
243					{
244						this.invalidFileHandle = true;
245						errorWrapper(e);
246					}));
247				}), errorWrapper);
248			}
249		}
250		else
251		{
252			if (this.ui.isOfflineApp() || this.ui.isLocalFileSave())
253			{
254				this.ui.doSaveLocalFile(data, title, (binary) ?
255					'image/png' : 'text/xml', binary);
256			}
257			else
258			{
259				if (data.length < MAX_REQUEST_SIZE)
260				{
261					var dot = title.lastIndexOf('.');
262					var format = (dot > 0) ? title.substring(dot + 1) : 'xml';
263
264					// Do not update modified flag
265					new mxXmlRequest(SAVE_URL, 'format=' + format +
266						'&xml=' + encodeURIComponent(data) +
267						'&filename=' + encodeURIComponent(title) +
268						((binary) ? '&binary=1' : '')).
269						simulate(document, '_blank');
270				}
271				else
272				{
273					this.ui.handleError({message: mxResources.get('drawingTooLarge')}, mxResources.get('error'), mxUtils.bind(this, function()
274					{
275						mxUtils.popup(data);
276					}));
277				}
278			}
279
280			done();
281		}
282	});
283
284	if (binary)
285	{
286		var p = this.ui.getPngFileProperties(this.ui.fileNode);
287
288		this.ui.getEmbeddedPng(mxUtils.bind(this, function(imageData)
289		{
290			doSave(imageData);
291		}), error, (this.ui.getCurrentFile() != this) ?
292			savedData : null, p.scale, p.border);
293	}
294	else
295	{
296		doSave(savedData);
297	}
298};
299
300/**
301 * Translates this point by the given vector.
302 *
303 * @param {number} dx X-coordinate of the translation.
304 * @param {number} dy Y-coordinate of the translation.
305 */
306LocalFile.prototype.rename = function(title, success, error)
307{
308	this.title = title;
309	this.descriptorChanged();
310
311	if (success != null)
312	{
313		success();
314	}
315};
316
317/**
318 * Returns the location as a new object.
319 * @type mx.Point
320 */
321LocalFile.prototype.open = function()
322{
323	this.ui.setFileData(this.getData());
324	this.installListeners();
325};
326