1/**
2 * Copyright (c) 2006-2017, JGraph Ltd
3 * Copyright (c) 2006-2017, Gaudenz Alder
4 */
5OneDriveFile = function(ui, data, meta)
6{
7	DrawioFile.call(this, ui, data);
8
9	this.meta = meta;
10};
11
12//Extends mxEventSource
13mxUtils.extend(OneDriveFile, DrawioFile);
14
15/**
16 * Shorter autosave delay for optimistic sync.
17 */
18OneDriveFile.prototype.autosaveDelay = 300;
19
20/**
21 * Translates this point by the given vector.
22 *
23 * @param {number} dx X-coordinate of the translation.
24 * @param {number} dy Y-coordinate of the translation.
25 */
26OneDriveFile.prototype.share = function()
27{
28	var url = this.meta.webUrl;
29	url = url.substring(0, url.lastIndexOf('/'));
30
31	if (this.meta.parentReference != null)
32	{
33		try
34		{
35			// Best effort guessing of the web interface URL for the file
36			if (this.meta.parentReference.driveType == 'personal')
37			{
38				url = 'https://onedrive.live.com/?cid=' + encodeURIComponent(this.meta.parentReference.driveId) +
39					'&id=' + encodeURIComponent(this.meta.id);
40			}
41			else if (this.meta.parentReference.driveType == 'documentLibrary')
42			{
43				var path = this.meta.parentReference.path;
44				path = path.substring(path.indexOf('/root:') + 6);
45
46				var id = this.meta.webUrl;
47				var url = id.substring(0, id.length - path.length - this.meta.name.length - ((path.length > 0) ? 1 : 0));
48				id = id.substring(id.indexOf('/', 8));
49
50				url = url + '/Forms/AllItems.aspx?id=' + id + '&parent=' + id.substring(0, id.lastIndexOf('/'));
51			}
52			else if (this.meta.parentReference.driveType == 'business')
53			{
54				var url = this.meta['@microsoft.graph.downloadUrl'];
55				var idx = url.indexOf('/_layouts/15/download.aspx?');
56
57				// Strips protocol
58				var id = this.meta.webUrl;
59				var parent = id;
60
61				id = id.substring(8);
62
63				// Gets path and parent path
64				id = id.substring(id.indexOf('/'));
65				parent = parent.substring(0, parent.lastIndexOf('/'));
66				parent = parent.substring(parent.indexOf('/', 8))
67
68				url = url.substring(0, idx) + '/_layouts/15/onedrive.aspx?id=' + id + '&parent=' + parent;
69			}
70		}
71		catch (e)
72		{
73			// ignore
74		}
75	}
76
77	this.ui.editor.graph.openLink(url);
78};
79
80/**
81 * Translates this point by the given vector.
82 *
83 * @param {number} dx X-coordinate of the translation.
84 * @param {number} dy Y-coordinate of the translation.
85 */
86OneDriveFile.prototype.getId = function()
87{
88	return this.getIdOf(this.meta);
89};
90
91/**
92 * Translates this point by the given vector.
93 *
94 * @param {number} dx X-coordinate of the translation.
95 * @param {number} dy Y-coordinate of the translation.
96 */
97OneDriveFile.prototype.getParentId = function()
98{
99	return this.getIdOf(this.meta, true);
100};
101
102/**
103 * Translates this point by the given vector.
104 *
105 * @param {number} dx X-coordinate of the translation.
106 * @param {number} dy Y-coordinate of the translation.
107 */
108OneDriveFile.prototype.getIdOf = function(itemObj, parent)
109{
110	//TODO driveId is most probably always there. No need to check if it exists. Also, after some time, the code that check the old id format won't be needed
111	return ((itemObj.parentReference != null && itemObj.parentReference.driveId != null) ? itemObj.parentReference.driveId + '/' : '') +
112		((parent != null) ? itemObj.parentReference.id : itemObj.id);
113};
114
115/**
116 * Gets the channel ID for sync messages.
117 */
118OneDriveFile.prototype.getChannelId = function()
119{
120	return 'W-' + DrawioFile.prototype.getChannelId.apply(this, arguments);
121};
122
123/**
124 * Translates this point by the given vector.
125 *
126 * @param {number} dx X-coordinate of the translation.
127 * @param {number} dy Y-coordinate of the translation.
128 */
129OneDriveFile.prototype.getHash = function()
130{
131	return 'W' + encodeURIComponent(this.getId());
132};
133
134/**
135 * Translates this point by the given vector.
136 *
137 * @param {number} dx X-coordinate of the translation.
138 * @param {number} dy Y-coordinate of the translation.
139 */
140OneDriveFile.prototype.getMode = function()
141{
142	return App.MODE_ONEDRIVE;
143};
144
145/**
146 * Overridden to enable the autosave option in the document properties dialog.
147 */
148OneDriveFile.prototype.isAutosaveOptional = function()
149{
150	return true;
151};
152
153/**
154 * Translates this point by the given vector.
155 *
156 * @param {number} dx X-coordinate of the translation.
157 * @param {number} dy Y-coordinate of the translation.
158 */
159OneDriveFile.prototype.getTitle = function()
160{
161	return this.meta.name;
162};
163
164/**
165 * Translates this point by the given vector.
166 *
167 * @param {number} dx X-coordinate of the translation.
168 * @param {number} dy Y-coordinate of the translation.
169 */
170OneDriveFile.prototype.isRenamable = function()
171{
172	return true;
173};
174
175/**
176 * Returns true if the notification to update should be sent
177 * together with the save request.
178 */
179OneDriveFile.prototype.isOptimisticSync = function()
180{
181	return true;
182};
183
184/**
185 * Hook for subclassers.
186 */
187OneDriveFile.prototype.isSyncSupported = function()
188{
189	return true;
190};
191
192/**
193 * Specifies if notify events should be ignored.
194 */
195OneDriveFile.prototype.getSize = function()
196{
197	return this.meta.size;
198};
199
200/**
201 * Adds the listener for automatically saving the diagram for local changes.
202 */
203OneDriveFile.prototype.isConflict = function(req)
204{
205	return req != null && (req.getStatus() == 412 || req.getStatus() == 409);
206};
207
208/**
209 * Returns the current etag.
210 */
211OneDriveFile.prototype.getCurrentUser = function()
212{
213	return (this.ui.oneDrive != null) ? this.ui.oneDrive.user : null;
214};
215
216/**
217 * Adds the listener for automatically saving the diagram for local changes.
218 */
219OneDriveFile.prototype.loadDescriptor = function(success, error)
220{
221	this.ui.oneDrive.executeRequest(this.ui.oneDrive.getItemURL(this.getId()), mxUtils.bind(this, function(req)
222	{
223		if (req.getStatus() >= 200 && req.getStatus() <= 299)
224		{
225			success(JSON.parse(req.getText()));
226		}
227		else if (error != null)
228		{
229			error();
230		}
231	}), error);
232};
233
234/**
235 * Adds the listener for automatically saving the diagram for local changes.
236 */
237OneDriveFile.prototype.getLatestVersion = function(success, error)
238{
239	this.ui.oneDrive.getFile(this.getId(), success, error);
240};
241
242/**
243 * Hook for subclassers to update the descriptor from given file
244 */
245OneDriveFile.prototype.getDescriptor = function()
246{
247	return this.meta;
248};
249
250/**
251 * Hook for subclassers to update the descriptor from given file
252 */
253OneDriveFile.prototype.setDescriptor = function(desc)
254{
255	this.meta = desc;
256};
257
258/**
259 * Adds all listeners.
260 */
261OneDriveFile.prototype.getDescriptorEtag = function(desc)
262{
263	return desc.eTag;
264};
265
266/**
267 * Adds the listener for automatically saving the diagram for local changes.
268 */
269OneDriveFile.prototype.setDescriptorEtag = function(desc, etag)
270{
271	desc.eTag = etag;
272};
273
274/**
275 * Adds the listener for automatically saving the diagram for local changes.
276 */
277OneDriveFile.prototype.loadPatchDescriptor = function(success, error)
278{
279	var url = this.ui.oneDrive.getItemURL(this.getId());
280
281	this.ui.oneDrive.executeRequest(url + '?select=etag,file' , mxUtils.bind(this, function(req)
282	{
283		if (req.getStatus() >= 200 && req.getStatus() <= 299)
284		{
285			success(JSON.parse(req.getText()));
286		}
287		else
288		{
289			error(this.ui.oneDrive.parseRequestText(req));
290		}
291	}), error)
292};
293
294/**
295 * Using MD5 of create timestamp and user ID as crypto key.
296 */
297OneDriveFile.prototype.getChannelKey = function()
298{
299	if (typeof CryptoJS !== 'undefined')
300	{
301		return CryptoJS.MD5(this.meta.createdDateTime +
302			((this.meta.createdBy != null &&
303			this.meta.createdBy.user != null) ?
304			this.meta.createdBy.user.id : '')).toString();
305	}
306
307	return null;
308};
309
310/**
311 * Adds all listeners.
312 */
313OneDriveFile.prototype.getLastModifiedDate = function()
314{
315	return new Date(this.meta.lastModifiedDateTime);
316};
317
318/**
319 * Translates this point by the given vector.
320 *
321 * @param {number} dx X-coordinate of the translation.
322 * @param {number} dy Y-coordinate of the translation.
323 */
324OneDriveFile.prototype.save = function(revision, success, error, unloading, overwrite)
325{
326	this.doSave(this.getTitle(), revision, success, error, unloading, overwrite);
327};
328
329/**
330 * Translates this point by the given vector.
331 *
332 * @param {number} dx X-coordinate of the translation.
333 * @param {number} dy Y-coordinate of the translation.
334 */
335OneDriveFile.prototype.saveAs = function(title, success, error)
336{
337	this.doSave(title, false, success, error);
338};
339
340/**
341 * Translates this point by the given vector.
342 *
343 * @param {number} dx X-coordinate of the translation.
344 * @param {number} dy Y-coordinate of the translation.
345 */
346OneDriveFile.prototype.doSave = function(title, revision, success, error, unloading, overwrite)
347{
348	// Forces update of data for new extensions
349	var prev = this.meta.name;
350	this.meta.name = title;
351
352	DrawioFile.prototype.save.apply(this, [null, mxUtils.bind(this, function()
353	{
354		this.meta.name = prev;
355		this.saveFile(title, revision, success, error, unloading, overwrite);
356	}), error, unloading, overwrite]);
357};
358
359/**
360 * Translates this point by the given vector.
361 *
362 * @param {number} dx X-coordinate of the translation.
363 * @param {number} dy Y-coordinate of the translation.
364 */
365OneDriveFile.prototype.saveFile = function(title, revision, success, error, unloading, overwrite)
366{
367	if (!this.isEditable())
368	{
369		if (success != null)
370		{
371			success();
372		}
373	}
374	else if (!this.savingFile)
375	{
376		if (this.getTitle() == title)
377		{
378			var doSave = mxUtils.bind(this, function()
379			{
380				try
381				{
382					// Sets shadow modified state during save
383					this.savingFileTime = new Date();
384					this.setShadowModified(false);
385					this.savingFile = true;
386
387					var etag = (!overwrite && this.constructor == OneDriveFile &&
388						(DrawioFile.SYNC == 'manual' || DrawioFile.SYNC == 'auto')) ?
389						this.getCurrentEtag() : null;
390					var lastDesc = this.meta;
391					this.fileSaving();
392
393					this.ui.oneDrive.saveFile(this, mxUtils.bind(this, function(meta, savedData)
394					{
395						// Checks for changes during save
396						this.setModified(this.getShadowModified());
397						this.savingFile = false;
398						this.meta = meta;
399
400						this.fileSaved(savedData, lastDesc, mxUtils.bind(this, function()
401						{
402							this.contentChanged();
403
404							if (success != null)
405							{
406								success();
407							}
408						}), error);
409					}), mxUtils.bind(this, function(err, req)
410					{
411						try
412						{
413							this.savingFile = false;
414
415							if (this.isConflict(req))
416					    	{
417								this.inConflictState = true;
418
419								if (this.sync != null)
420								{
421									this.savingFile = true;
422
423									this.sync.fileConflict(null, mxUtils.bind(this, function()
424									{
425										// Adds random cool-off
426										window.setTimeout(mxUtils.bind(this, function()
427										{
428											this.updateFileData();
429											doSave();
430										}), 100 + Math.random() * 500);
431									}), mxUtils.bind(this, function()
432									{
433										this.savingFile = false;
434
435										if (error != null)
436										{
437											error();
438										}
439									}));
440								}
441								else if (error != null)
442								{
443									error();
444								}
445							}
446							else if (error != null)
447							{
448								error(err);
449							}
450						}
451						catch (e)
452						{
453							this.savingFile = false;
454
455							if (error != null)
456							{
457								error(e);
458							}
459							else
460							{
461								throw e;
462							}
463						}
464					}), etag);
465				}
466				catch (e)
467				{
468					this.savingFile = false;
469
470					if (error != null)
471					{
472						error(e);
473					}
474					else
475					{
476						throw e;
477					}
478				}
479			});
480
481			doSave();
482		}
483		else
484		{
485			// Sets shadow modified state during save
486			this.savingFileTime = new Date();
487			this.setShadowModified(false);
488			this.savingFile = true;
489
490			this.ui.oneDrive.insertFile(title, this.getData(), mxUtils.bind(this, function(file)
491			{
492				// Checks for changes during save
493				this.setModified(this.getShadowModified());
494				this.savingFile = false;
495
496				if (success != null)
497				{
498					success();
499				}
500
501				this.ui.fileLoaded(file);
502			}), mxUtils.bind(this, function()
503			{
504				this.savingFile = false;
505
506				if (error != null)
507				{
508					error();
509				}
510			}));
511		}
512	}
513};
514
515/**
516 * Translates this point by the given vector.
517 *
518 * @param {number} dx X-coordinate of the translation.
519 * @param {number} dy Y-coordinate of the translation.
520 */
521OneDriveFile.prototype.rename = function(title, success, error)
522{
523	var etag = this.getCurrentEtag();
524
525	this.ui.oneDrive.renameFile(this, title, mxUtils.bind(this, function(meta)
526	{
527		if (!this.hasSameExtension(title, this.getTitle()))
528		{
529			this.meta = meta;
530
531			if (this.sync != null)
532			{
533				this.sync.descriptorChanged(etag);
534			}
535
536			this.save(true, success, error);
537		}
538		else
539		{
540			this.meta = meta;
541			this.descriptorChanged();
542
543			if (this.sync != null)
544			{
545				this.sync.descriptorChanged(etag);
546			}
547
548			if (success != null)
549			{
550				success(meta);
551			}
552		}
553	}), error);
554};
555
556/**
557 * Translates this point by the given vector.
558 *
559 * @param {number} dx X-coordinate of the translation.
560 * @param {number} dy Y-coordinate of the translation.
561 */
562OneDriveFile.prototype.move = function(folderId, success, error)
563{
564	this.ui.oneDrive.moveFile(this.getId(), folderId, mxUtils.bind(this, function(meta)
565	{
566		this.meta = meta;
567		this.descriptorChanged();
568
569		if (success != null)
570		{
571			success(meta);
572		}
573	}), error);
574};
575