|  | /* | 
|  | * Copyright (C) 2013 Google Inc. All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions are | 
|  | * met: | 
|  | * | 
|  | *     * Redistributions of source code must retain the above copyright | 
|  | * notice, this list of conditions and the following disclaimer. | 
|  | *     * Redistributions in binary form must reproduce the above | 
|  | * copyright notice, this list of conditions and the following disclaimer | 
|  | * in the documentation and/or other materials provided with the | 
|  | * distribution. | 
|  | *     * Neither the name of Google Inc. nor the names of its | 
|  | * contributors may be used to endorse or promote products derived from | 
|  | * this software without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * @unrestricted | 
|  | */ | 
|  | export default class FileSystemWorkspaceBinding { | 
|  | /** | 
|  | * @param {!Persistence.IsolatedFileSystemManager} isolatedFileSystemManager | 
|  | * @param {!Workspace.Workspace} workspace | 
|  | */ | 
|  | constructor(isolatedFileSystemManager, workspace) { | 
|  | this._isolatedFileSystemManager = isolatedFileSystemManager; | 
|  | this._workspace = workspace; | 
|  | this._eventListeners = [ | 
|  | this._isolatedFileSystemManager.addEventListener( | 
|  | Persistence.IsolatedFileSystemManager.Events.FileSystemAdded, this._onFileSystemAdded, this), | 
|  | this._isolatedFileSystemManager.addEventListener( | 
|  | Persistence.IsolatedFileSystemManager.Events.FileSystemRemoved, this._onFileSystemRemoved, this), | 
|  | this._isolatedFileSystemManager.addEventListener( | 
|  | Persistence.IsolatedFileSystemManager.Events.FileSystemFilesChanged, this._fileSystemFilesChanged, this) | 
|  | ]; | 
|  | /** @type {!Map.<string, !FileSystem>} */ | 
|  | this._boundFileSystems = new Map(); | 
|  | this._isolatedFileSystemManager.waitForFileSystems().then(this._onFileSystemsLoaded.bind(this)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {string} fileSystemPath | 
|  | * @return {string} | 
|  | */ | 
|  | static projectId(fileSystemPath) { | 
|  | return fileSystemPath; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @return {!Array<string>} | 
|  | */ | 
|  | static relativePath(uiSourceCode) { | 
|  | const baseURL = | 
|  | /** @type {!FileSystem}*/ (uiSourceCode.project())._fileSystemBaseURL; | 
|  | return uiSourceCode.url().substring(baseURL.length).split('/'); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @return {string} | 
|  | */ | 
|  | static tooltipForUISourceCode(uiSourceCode) { | 
|  | const fileSystem = | 
|  | /** @type {!FileSystem}*/ (uiSourceCode.project())._fileSystem; | 
|  | return fileSystem.tooltipForURL(uiSourceCode.url()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Workspace.Project} project | 
|  | * @return {string} | 
|  | */ | 
|  | static fileSystemType(project) { | 
|  | const fileSystem = | 
|  | /** @type {!FileSystem}*/ (project)._fileSystem; | 
|  | return fileSystem.type(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Workspace.Project} project | 
|  | * @return {boolean} | 
|  | */ | 
|  | static fileSystemSupportsAutomapping(project) { | 
|  | const fileSystem = | 
|  | /** @type {!FileSystem}*/ (project)._fileSystem; | 
|  | return fileSystem.supportsAutomapping(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Workspace.Project} project | 
|  | * @param {string} relativePath | 
|  | * @return {string} | 
|  | */ | 
|  | static completeURL(project, relativePath) { | 
|  | const fsProject = /** @type {!FileSystem}*/ (project); | 
|  | return fsProject._fileSystemBaseURL + relativePath; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {string} projectId | 
|  | * @return {string} | 
|  | */ | 
|  | static fileSystemPath(projectId) { | 
|  | return projectId; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return {!Persistence.IsolatedFileSystemManager} | 
|  | */ | 
|  | fileSystemManager() { | 
|  | return this._isolatedFileSystemManager; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Array<!Persistence.IsolatedFileSystem>} fileSystems | 
|  | */ | 
|  | _onFileSystemsLoaded(fileSystems) { | 
|  | for (const fileSystem of fileSystems) { | 
|  | this._addFileSystem(fileSystem); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Common.Event} event | 
|  | */ | 
|  | _onFileSystemAdded(event) { | 
|  | const fileSystem = /** @type {!Persistence.PlatformFileSystem} */ (event.data); | 
|  | this._addFileSystem(fileSystem); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Persistence.PlatformFileSystem} fileSystem | 
|  | */ | 
|  | _addFileSystem(fileSystem) { | 
|  | const boundFileSystem = new FileSystem(this, fileSystem, this._workspace); | 
|  | this._boundFileSystems.set(fileSystem.path(), boundFileSystem); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Common.Event} event | 
|  | */ | 
|  | _onFileSystemRemoved(event) { | 
|  | const fileSystem = /** @type {!Persistence.PlatformFileSystem} */ (event.data); | 
|  | const boundFileSystem = this._boundFileSystems.get(fileSystem.path()); | 
|  | boundFileSystem.dispose(); | 
|  | this._boundFileSystems.remove(fileSystem.path()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Common.Event} event | 
|  | */ | 
|  | _fileSystemFilesChanged(event) { | 
|  | const paths = /** @type {!Persistence.IsolatedFileSystemManager.FilesChangedData} */ (event.data); | 
|  | for (const fileSystemPath of paths.changed.keysArray()) { | 
|  | const fileSystem = this._boundFileSystems.get(fileSystemPath); | 
|  | if (!fileSystem) { | 
|  | continue; | 
|  | } | 
|  | paths.changed.get(fileSystemPath).forEach(path => fileSystem._fileChanged(path)); | 
|  | } | 
|  |  | 
|  | for (const fileSystemPath of paths.added.keysArray()) { | 
|  | const fileSystem = this._boundFileSystems.get(fileSystemPath); | 
|  | if (!fileSystem) { | 
|  | continue; | 
|  | } | 
|  | paths.added.get(fileSystemPath).forEach(path => fileSystem._fileChanged(path)); | 
|  | } | 
|  |  | 
|  | for (const fileSystemPath of paths.removed.keysArray()) { | 
|  | const fileSystem = this._boundFileSystems.get(fileSystemPath); | 
|  | if (!fileSystem) { | 
|  | continue; | 
|  | } | 
|  | paths.removed.get(fileSystemPath).forEach(path => fileSystem.removeUISourceCode(path)); | 
|  | } | 
|  | } | 
|  |  | 
|  | dispose() { | 
|  | Common.EventTarget.removeEventListeners(this._eventListeners); | 
|  | for (const fileSystem of this._boundFileSystems.values()) { | 
|  | fileSystem.dispose(); | 
|  | this._boundFileSystems.remove(fileSystem._fileSystem.path()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @implements {Workspace.Project} | 
|  | * @unrestricted | 
|  | */ | 
|  | export class FileSystem extends Workspace.ProjectStore { | 
|  | /** | 
|  | * @param {!FileSystemWorkspaceBinding} fileSystemWorkspaceBinding | 
|  | * @param {!Persistence.PlatformFileSystem} isolatedFileSystem | 
|  | * @param {!Workspace.Workspace} workspace | 
|  | */ | 
|  | constructor(fileSystemWorkspaceBinding, isolatedFileSystem, workspace) { | 
|  | const fileSystemPath = isolatedFileSystem.path(); | 
|  | const id = FileSystemWorkspaceBinding.projectId(fileSystemPath); | 
|  | console.assert(!workspace.project(id)); | 
|  | const displayName = fileSystemPath.substr(fileSystemPath.lastIndexOf('/') + 1); | 
|  |  | 
|  | super(workspace, id, Workspace.projectTypes.FileSystem, displayName); | 
|  |  | 
|  | this._fileSystem = isolatedFileSystem; | 
|  | this._fileSystemBaseURL = this._fileSystem.path() + '/'; | 
|  | this._fileSystemParentURL = this._fileSystemBaseURL.substr(0, fileSystemPath.lastIndexOf('/') + 1); | 
|  | this._fileSystemWorkspaceBinding = fileSystemWorkspaceBinding; | 
|  | this._fileSystemPath = fileSystemPath; | 
|  | /** @type {!Set<string>} */ | 
|  | this._creatingFilesGuard = new Set(); | 
|  |  | 
|  | workspace.addProject(this); | 
|  | this.populate(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return {string} | 
|  | */ | 
|  | fileSystemPath() { | 
|  | return this._fileSystemPath; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @return {string} | 
|  | */ | 
|  | mimeType(uiSourceCode) { | 
|  | return this._fileSystem.mimeFromPath(uiSourceCode.url()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return {!Array<string>} | 
|  | */ | 
|  | initialGitFolders() { | 
|  | return this._fileSystem.initialGitFolders().map(folder => this._fileSystemPath + '/' + folder); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @return {string} | 
|  | */ | 
|  | _filePathForUISourceCode(uiSourceCode) { | 
|  | return uiSourceCode.url().substring(this._fileSystemPath.length); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @return {boolean} | 
|  | */ | 
|  | isServiceProject() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @return {!Promise<?Workspace.UISourceCodeMetadata>} | 
|  | */ | 
|  | requestMetadata(uiSourceCode) { | 
|  | if (uiSourceCode[_metadata]) { | 
|  | return uiSourceCode[_metadata]; | 
|  | } | 
|  | const relativePath = this._filePathForUISourceCode(uiSourceCode); | 
|  | const promise = this._fileSystem.getMetadata(relativePath).then(onMetadata); | 
|  | uiSourceCode[_metadata] = promise; | 
|  | return promise; | 
|  |  | 
|  | /** | 
|  | * @param {?{modificationTime: !Date, size: number}} metadata | 
|  | * @return {?Workspace.UISourceCodeMetadata} | 
|  | */ | 
|  | function onMetadata(metadata) { | 
|  | if (!metadata) { | 
|  | return null; | 
|  | } | 
|  | return new Workspace.UISourceCodeMetadata(metadata.modificationTime, metadata.size); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @return {!Promise<?Blob>} | 
|  | */ | 
|  | requestFileBlob(uiSourceCode) { | 
|  | return this._fileSystem.requestFileBlob(this._filePathForUISourceCode(uiSourceCode)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @returns {!Promise<!Common.DeferredContent>} | 
|  | */ | 
|  | requestFileContent(uiSourceCode) { | 
|  | const filePath = this._filePathForUISourceCode(uiSourceCode); | 
|  | return this._fileSystem.requestFileContent(filePath); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @return {boolean} | 
|  | */ | 
|  | canSetFileContent() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @param {string} newContent | 
|  | * @param {boolean} isBase64 | 
|  | * @return {!Promise} | 
|  | */ | 
|  | async setFileContent(uiSourceCode, newContent, isBase64) { | 
|  | const filePath = this._filePathForUISourceCode(uiSourceCode); | 
|  | await this._fileSystem.setFileContent(filePath, newContent, isBase64); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @return {string} | 
|  | */ | 
|  | fullDisplayName(uiSourceCode) { | 
|  | const baseURL = | 
|  | /** @type {!FileSystem}*/ (uiSourceCode.project())._fileSystemParentURL; | 
|  | return uiSourceCode.url().substring(baseURL.length); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @return {boolean} | 
|  | */ | 
|  | canRename() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @param {string} newName | 
|  | * @param {function(boolean, string=, string=, !Common.ResourceType=)} callback | 
|  | */ | 
|  | rename(uiSourceCode, newName, callback) { | 
|  | if (newName === uiSourceCode.name()) { | 
|  | callback(true, uiSourceCode.name(), uiSourceCode.url(), uiSourceCode.contentType()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | let filePath = this._filePathForUISourceCode(uiSourceCode); | 
|  | this._fileSystem.renameFile(filePath, newName, innerCallback.bind(this)); | 
|  |  | 
|  | /** | 
|  | * @param {boolean} success | 
|  | * @param {string=} newName | 
|  | * @this {FileSystem} | 
|  | */ | 
|  | function innerCallback(success, newName) { | 
|  | if (!success || !newName) { | 
|  | callback(false, newName); | 
|  | return; | 
|  | } | 
|  | console.assert(newName); | 
|  | const slash = filePath.lastIndexOf('/'); | 
|  | const parentPath = filePath.substring(0, slash); | 
|  | filePath = parentPath + '/' + newName; | 
|  | filePath = filePath.substr(1); | 
|  | const newURL = this._fileSystemBaseURL + filePath; | 
|  | const newContentType = this._fileSystem.contentType(newName); | 
|  | this.renameUISourceCode(uiSourceCode, newName); | 
|  | callback(true, newName, newURL, newContentType); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | * @param {string} query | 
|  | * @param {boolean} caseSensitive | 
|  | * @param {boolean} isRegex | 
|  | * @return {!Promise<!Array<!Common.ContentProvider.SearchMatch>>} | 
|  | */ | 
|  | async searchInFileContent(uiSourceCode, query, caseSensitive, isRegex) { | 
|  | const filePath = this._filePathForUISourceCode(uiSourceCode); | 
|  | const {content} = await this._fileSystem.requestFileContent(filePath); | 
|  | if (content) { | 
|  | return Common.ContentProvider.performSearchInContent(content, query, caseSensitive, isRegex); | 
|  | } | 
|  | return []; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Workspace.ProjectSearchConfig} searchConfig | 
|  | * @param {!Array.<string>} filesMathingFileQuery | 
|  | * @param {!Common.Progress} progress | 
|  | * @return {!Promise<!Array<string>>} | 
|  | */ | 
|  | async findFilesMatchingSearchRequest(searchConfig, filesMathingFileQuery, progress) { | 
|  | let result = filesMathingFileQuery; | 
|  | const queriesToRun = searchConfig.queries().slice(); | 
|  | if (!queriesToRun.length) { | 
|  | queriesToRun.push(''); | 
|  | } | 
|  | progress.setTotalWork(queriesToRun.length); | 
|  |  | 
|  | for (const query of queriesToRun) { | 
|  | const files = await this._fileSystem.searchInPath(searchConfig.isRegex() ? '' : query, progress); | 
|  | result = result.intersectOrdered(files.sort(), String.naturalOrderComparator); | 
|  | progress.worked(1); | 
|  | } | 
|  |  | 
|  | progress.done(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Common.Progress} progress | 
|  | */ | 
|  | indexContent(progress) { | 
|  | this._fileSystem.indexContent(progress); | 
|  | } | 
|  |  | 
|  | populate() { | 
|  | const chunkSize = 1000; | 
|  | const filePaths = this._fileSystem.initialFilePaths(); | 
|  | reportFileChunk.call(this, 0); | 
|  |  | 
|  | /** | 
|  | * @param {number} from | 
|  | * @this {FileSystem} | 
|  | */ | 
|  | function reportFileChunk(from) { | 
|  | const to = Math.min(from + chunkSize, filePaths.length); | 
|  | for (let i = from; i < to; ++i) { | 
|  | this._addFile(filePaths[i]); | 
|  | } | 
|  | if (to < filePaths.length) { | 
|  | setTimeout(reportFileChunk.bind(this, to), 100); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {string} url | 
|  | */ | 
|  | excludeFolder(url) { | 
|  | let relativeFolder = url.substring(this._fileSystemBaseURL.length); | 
|  | if (!relativeFolder.startsWith('/')) { | 
|  | relativeFolder = '/' + relativeFolder; | 
|  | } | 
|  | if (!relativeFolder.endsWith('/')) { | 
|  | relativeFolder += '/'; | 
|  | } | 
|  | this._fileSystem.addExcludedFolder(relativeFolder); | 
|  |  | 
|  | const uiSourceCodes = this.uiSourceCodes().slice(); | 
|  | for (let i = 0; i < uiSourceCodes.length; ++i) { | 
|  | const uiSourceCode = uiSourceCodes[i]; | 
|  | if (uiSourceCode.url().startsWith(url)) { | 
|  | this.removeUISourceCode(uiSourceCode.url()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {string} path | 
|  | * @return {boolean} | 
|  | */ | 
|  | canExcludeFolder(path) { | 
|  | return this._fileSystem.canExcludeFolder(path); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @return {boolean} | 
|  | */ | 
|  | canCreateFile() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {string} path | 
|  | * @param {?string} name | 
|  | * @param {string} content | 
|  | * @param {boolean=} isBase64 | 
|  | * @return {!Promise<?Workspace.UISourceCode>} | 
|  | */ | 
|  | async createFile(path, name, content, isBase64) { | 
|  | const guardFileName = this._fileSystemPath + path + (!path.endsWith('/') ? '/' : '') + name; | 
|  | this._creatingFilesGuard.add(guardFileName); | 
|  | const filePath = await this._fileSystem.createFile(path, name); | 
|  | if (!filePath) { | 
|  | return null; | 
|  | } | 
|  | const uiSourceCode = this._addFile(filePath); | 
|  | uiSourceCode.setContent(content, !!isBase64); | 
|  | this._creatingFilesGuard.delete(guardFileName); | 
|  | return uiSourceCode; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | * @param {!Workspace.UISourceCode} uiSourceCode | 
|  | */ | 
|  | deleteFile(uiSourceCode) { | 
|  | const relativePath = this._filePathForUISourceCode(uiSourceCode); | 
|  | this._fileSystem.deleteFile(relativePath).then(success => { | 
|  | if (success) { | 
|  | this.removeUISourceCode(uiSourceCode.url()); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @override | 
|  | */ | 
|  | remove() { | 
|  | this._fileSystemWorkspaceBinding._isolatedFileSystemManager.removeFileSystem(this._fileSystem); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {string} filePath | 
|  | * @return {!Workspace.UISourceCode} | 
|  | */ | 
|  | _addFile(filePath) { | 
|  | const contentType = this._fileSystem.contentType(filePath); | 
|  | const uiSourceCode = this.createUISourceCode(this._fileSystemBaseURL + filePath, contentType); | 
|  | this.addUISourceCode(uiSourceCode); | 
|  | return uiSourceCode; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {string} path | 
|  | */ | 
|  | _fileChanged(path) { | 
|  | // Ignore files that are being created but do not have content yet. | 
|  | if (this._creatingFilesGuard.has(path)) { | 
|  | return; | 
|  | } | 
|  | const uiSourceCode = this.uiSourceCodeForURL(path); | 
|  | if (!uiSourceCode) { | 
|  | const contentType = this._fileSystem.contentType(path); | 
|  | this.addUISourceCode(this.createUISourceCode(path, contentType)); | 
|  | return; | 
|  | } | 
|  | uiSourceCode[_metadata] = null; | 
|  | uiSourceCode.checkContentUpdated(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param {string} url | 
|  | * @return {string} | 
|  | */ | 
|  | tooltipForURL(url) { | 
|  | return this._fileSystem.tooltipForURL(url); | 
|  | } | 
|  |  | 
|  | dispose() { | 
|  | this.removeProject(); | 
|  | } | 
|  | } | 
|  |  | 
|  | const _metadata = Symbol('FileSystemWorkspaceBinding.Metadata'); | 
|  |  | 
|  | /* Legacy exported object */ | 
|  | self.Persistence = self.Persistence || {}; | 
|  |  | 
|  | /* Legacy exported object */ | 
|  | Persistence = Persistence || {}; | 
|  |  | 
|  | /** @constructor */ | 
|  | Persistence.FileSystemWorkspaceBinding = FileSystemWorkspaceBinding; | 
|  |  | 
|  | /** @constructor */ | 
|  | Persistence.FileSystemWorkspaceBinding.FileSystem = FileSystem; |