Skip to content
Browse files

added syncFileSystem sample by kinuko@

  • Loading branch information...
1 parent d14dc15 commit bdffa50ceba981429f5385f96b87128edbd3beb2 @agektmr agektmr committed Feb 19, 2013
View
3 syncfs-editor/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "third_party/CodeMirror"]
+ path = third_party/CodeMirror
+ url = https://github.com/marijnh/CodeMirror.git
View
22 syncfs-editor/README.md
@@ -0,0 +1,22 @@
+syncfs-editor
+=============
+
+Cloud-backed text editor using the new chrome.syncFileSystem API.
+(You need to use Chrome M27+ Canary for try this out)
+
+LICENSE:
+-
+
+Copyright 2012, 2013 {kinuko,tzik,nhiroki,calvinlo}@chromium.org
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
View
244 syncfs-editor/css/editor.css
@@ -0,0 +1,244 @@
+body {
+ font-family: Verdana, Arial, Helvetica, "Liberation Sans", sans-serif;
+ color: #222;
+ background: #fff;
+}
+
+.hide {
+ display: none;
+}
+
+.info {
+ color: #930;
+ font-size: 12px;
+ clear: both;
+}
+
+.error {
+ color: #f00;
+ font-weight: bold;
+ font-size: 12px;
+ clear: both;
+}
+
+#log {
+ color: #666;
+ font-size: 12px;
+ clear: both;
+ border-top: solid 1px #ccc;
+ padding: 3px;
+}
+
+.button, .fs-button {
+ -webkit-border-radius:3px;
+ padding: 1px 4px;
+ margin: 2px 4px;
+ border: solid 1px #ccc;
+}
+
+#fs-selector {
+ margin-top: -1px;
+ padding-bottom: 3px;
+ margin-bottom: 3px;
+ border-bottom: solid 1px #ccc;
+}
+
+.fs-button {
+ font-size: 13px;
+ background: #acf;
+}
+
+.fs-button.selected {
+ color: white;
+ background: #009;
+}
+
+.fs-button:hover {
+ color: white;
+ background: #009;
+}
+
+.button {
+ background: #eee;
+}
+
+.button:hover {
+ background: #fb9;
+}
+
+/* Filer ------------------------------------------------------*/
+
+.filer {
+ height: 400px;
+ width: 250px;
+ float: left;
+ padding: 2px;
+ border-right: solid 1px #ccc;
+}
+
+.filer-tools {
+ margin: 2px 4px;
+ padding: 2px;
+ font-size: 12px;
+ color: #666;
+}
+
+#filer-empty-label {
+ margin-top: 20px;
+ text-align: center;
+ font-size: 12px;
+ color: #ccc;
+}
+
+#filer ul {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font-style: normal;
+ font-weight: normal;
+ list-style: none;
+ text-decoration: none;
+ background: transparent;
+ vertical-align: baseline;
+}
+#filer ul * {
+ font-size: 12px;
+}
+#filer ul ul {
+ margin-top: 1px;
+}
+#filer ul li {
+ margin: 0;
+ padding: 2px 0 1px;
+ position: relative;
+}
+#filer ul li li {
+ padding-left: 20px;
+}
+#filer .dir ul {
+ height: 0;
+ opacity: 0;
+ -webkit-transition: .25s;
+ transition: .15s;
+}
+#filer .dir.open > ul {
+ height: auto;
+ opacity: 1;
+ -webkit-transition: .25s;
+ transition: .15s;
+}
+#filer ul li .entry {
+ padding: 2px 3px;
+ line-height: 18px;
+ border-bottom: solid 1px #ddd;
+}
+#filer ul li .entry *:first-child {
+ text-decoration: none;
+ color: #222222;
+ display: block;
+ width: 70%;
+}
+#filer ul li .entry *:first-child:before {
+ display: inline-block;
+ content: url(../img/filler.png);
+ width: 16px;
+ height: 16px;
+}
+#filer ul li .entry.synced *:first-child:before {
+ display: inline-block;
+ content: url(../img/icon-synced.png);
+ width: 16px;
+ height: 16px;
+}
+#filer ul li .entry.pending *:first-child:before {
+ display: inline-block;
+ content: url(../img/icon-pending.png);
+ width: 16px;
+ height: 16px;
+}
+#filer ul li .entry.conflicting *:first-child:before {
+ display: inline-block;
+ content: url(../img/icon-conflicting.png);
+ width: 16px;
+ height: 16px;
+}
+#filer ul li .entry .size {
+ color: #03a;
+ text-align: right;
+ position: absolute;
+ display: block;
+ top: 3px;
+ font-size: 11px;
+ right: 38px;
+ width: 38px;
+}
+#filer ul li .entry button:last-child {
+ position: absolute;
+ top: 3px;
+ right: 5px;
+ font-size: 11px;
+ background: white;
+ padding: 1px 4px;
+ -webkit-border-radius:3px;
+}
+#filer ul li .entry * {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+#filer ul li .entry .button:hover {
+ background: #fb9;
+}
+#filer .file { background: url(../img/icon_file.png) no-repeat 0; }
+#filer .dir { background: url(../img/icon_folder.png) no-repeat 0; }
+#filer .dir.open { background: url(../img/icon_folder_open.png) no-repeat 0; }
+
+/* Editor -----------------------------------------------------*/
+
+.editor {
+ float: left;
+ margin-left: 10px;
+ height: 400px;
+ width: 480px;
+}
+
+#editor-path {
+ color: #666;
+ font-weight: bold;
+ font-size: 13px;
+}
+
+.editor-tools {
+ margin: 2px 4px;
+ padding: 2px;
+}
+
+.editor-dialog {
+ position: absolute;
+ top: 30%;
+ left: 25%;
+ padding: 30px;
+ background: #eee;
+ z-index: 100;
+ -webkit-border-radius:3px;
+}
+
+.editor-dialog-buttons {
+ text-align: center;
+ margin-top: 5px;
+}
+
+#editor-content {
+ border: solid 1px #ccc;
+ margin: 2px 4px;
+ padding: 2px;
+ font-size: 12px;
+ height: 350px;
+ width: 450px;
+ resize: none;
+}
+
+.CodeMirror-scroll {
+ height: 100%;
+}
View
BIN syncfs-editor/img/128.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/16.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/24.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/filler.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/icon-128.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/icon-16.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/icon-24.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/icon-conflicting.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/icon-pending.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/icon-synced.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/icon_file.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/icon_folder.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/icon_folder_open.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/loading.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/ng-icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/ok-icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN syncfs-editor/img/syncing-icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
5 syncfs-editor/js/background.js
@@ -0,0 +1,5 @@
+chrome.app.runtime.onLaunched.addListener(function (arg) {
+ chrome.app.window.create(
+ 'main.html',
+ { width:780, height:490, type:"shell" });
+});
View
221 syncfs-editor/js/editor.js
@@ -0,0 +1,221 @@
+Editor = function(filesystem, container, filer) {
+ this.filesystem = filesystem;
+ this.filer = filer;
+
+ this.container = document.getElementById(container);
+ this.container.innerHTML = '';
+
+ var tools = createElement('div', {class:'editor-tools'});
+ tools.appendChild(createElement('span', {id:'editor-path'}));
+ tools.appendChild(createElement(
+ 'button', {id:'editor-save', class:'button',
+ disabled:true, innerText:'Save'}));
+ tools.appendChild(createElement(
+ 'button', {id:'editor-saveas', class:'button',
+ innerText:'Save As'}));
+ tools.appendChild(createElement(
+ 'button', {id:'editor-new', class:'button',
+ innerText:'New'}));
+ this.container.appendChild(tools);
+
+ // Document object for CodeMirror.
+ this.cm_doc;
+
+ if (window.CodeMirror) {
+ this.cm_doc =
+ CodeMirror(appendEditor.bind(this), {lineNumbers: true, theme: 'neat'});
+ }
+ else
+ appendEditor.call(this, createElement('textarea'));
+
+ function appendEditor(editor) {
+ this.container.appendChild(editor);
+ editor.setAttribute('id', 'editor-content');
+
+ var scratchPath = '* scratch *';
+ this.setCurrentPath(scratchPath);
+ this.isScratch = function() {
+ return ($('#editor-path').innerText == scratchPath);
+ };
+
+ $('#editor-new').addEventListener('click', function() {
+ this.setContent('');
+ this.setCurrentPath(scratchPath);
+ }.bind(this));
+ $('#editor-save').addEventListener('click', this.save.bind(this));
+ $('#editor-saveas').addEventListener('click', this.saveAs.bind(this));
+ $('#editor-content').addEventListener('keydown', function() {
+ if (!this.isScratch())
+ $('#editor-save').disabled = false;
+ }.bind(this));
+ $('body').addEventListener('keydown', function(e) {
+ if (e.keyCode == 27) {
+ var dialog = $('.editor-dialog');
+ if (dialog) dialog.parentNode.removeChild(dialog);
+ }
+ });
+ };
+}
+
+Editor.prototype.open = function(path) {
+ this.filesystem.root.getFile(
+ path, {},
+ this.load.bind(this),
+ error.bind(null, "getFile " + path));
+};
+
+Editor.prototype.load = function(entry) {
+ log('Opening: ' + entry.fullPath);
+ this.fileHasNewData = false;
+ this.setCurrentPath(entry.fullPath);
+ entry.file(function(file) {
+ var reader = new FileReader();
+ reader.readAsText(file, "utf-8");
+ reader.onload = function(ev) {
+ this.setContent(ev.target.result);
+ $('#editor-save').disabled = true;
+ }.bind(this);
+ }.bind(this), error);
+};
+
+Editor.prototype.save = function() {
+ if (this.isScratch()) {
+ return;
+ }
+ if (this.fileHasNewData) {
+ this.showDialog({
+ dialogLabel: 'The file is updated. Do you want to overwrite it?',
+ submitLabel: 'Overwrite',
+ cancelLabel: 'Cancel',
+ submitCallback: function() {
+ this.fileHasNewData = false;
+ this.save();
+ }.bind(this)
+ });
+ return;
+ }
+ var path = this.getCurrentPath();
+ log('Saving to:' + path);
+ this.filesystem.root.getFile(
+ path, {create: true},
+ function(entry) {
+ entry.createWriter(function (writer) {
+ writer.truncate(0);
+ writer.onerror = error.bind(null, 'writer.truncate');
+ writer.onwriteend = function() {
+ var content = this.getContent();
+ var blob = new Blob([content]);
+ var size = content.length;
+ writer.write(blob);
+ writer.onerror = error;
+ writer.onwriteend = this.onSave.bind(this, entry, size);
+ }.bind(this);
+ }.bind(this));
+ }.bind(this));
+};
+
+Editor.prototype.saveAs = function() {
+ this.showDialog({
+ inputLabel: 'Save as: ',
+ submitLabel: 'Save',
+ cancelLabel: 'Cancel',
+ submitCallback: this.onSaveAs.bind(this),
+ });
+};
+
+Editor.prototype.onSave = function(entry, size) {
+ $('#editor-save').disabled = true;
+ this.fileHasNewData = false;
+ log('File saved: ' + size + ' bytes');
+ this.filer.reload();
+};
+
+Editor.prototype.onSaveAs = function(path) {
+ if (!validFileName(path))
+ return;
+ this.filesystem.root.getFile(
+ path, {create: true, exclusive: true},
+ function(entry) {
+ this.setCurrentPath(entry.fullPath);
+ this.save();
+ }.bind(this),
+ function(e) {
+ error('The path already exists.');
+ this.saveAs();
+ }.bind(this));
+};
+
+// dialogOptions {
+// dialogLabel: a string value to be shown at the top of the dialog.
+// inputLabel: a string value to be shown next to the input box.
+// submitLabel: a string value to be shown on the submit button.
+// cancelLabel: a string value to be shown on the cancel button.
+// submitCallback: a callback to be dispatched upon submit.
+// cancelCallback: callback to be dispatched upon cancel.
+// }
+Editor.prototype.showDialog = function(dialogOptions) {
+ dialogOptions.submitCallback = dialogOptions.submitCallback || function() {};
+ dialogOptions.cancelCallback = dialogOptions.cancelCallback || function() {};
+ var dialog = createElement('div', {class:'editor-dialog'});
+ if (dialogOptions.dialogLabel) {
+ dialog.appendChild(createElement(
+ 'div', {innerText: dialogOptions.dialogLabel}));
+ }
+ var getInputValue = function() { return null; };
+ if (dialogOptions.inputLabel) {
+ dialog.appendChild(createElement(
+ 'span', {innerText: dialogOptions.inputLabel}));
+ dialog.appendChild(createElement(
+ 'input', {id:'editor-dialog-input', type:'text', size:40}));
+ getInputValue = function() { return $('#editor-dialog-input').value; };
+ }
+ var buttons = createElement('div', {class:'editor-dialog-buttons'});
+ buttons.appendChild(createElement(
+ 'input', {id:'editor-dialog-submit',
+ value:dialogOptions.submitLabel,
+ type:'button'}));
+ buttons.appendChild(createElement(
+ 'input', {id:'editor-dialog-cancel',
+ value:dialogOptions.cancelLabel,
+ type:'button'}));
+ dialog.appendChild(buttons);
+ this.container.appendChild(dialog);
+ if (dialogOptions.inputLabel) {
+ $('#editor-dialog-input').focus();
+ $('#editor-dialog-input').addEventListener('keydown', function(e) {
+ if (e.keyCode == 13) {
+ dialogOptions.submitCallback(getInputValue());
+ dialog.parentNode.removeChild(dialog);
+ }
+ }.bind(this));
+ }
+ $('#editor-dialog-submit').addEventListener('click', function() {
+ dialogOptions.submitCallback(getInputValue());
+ dialog.parentNode.removeChild(dialog);
+ }.bind(this));
+ $('#editor-dialog-cancel').addEventListener('click', function() {
+ dialogOptions.cancelCallback();
+ dialog.parentNode.removeChild(dialog);
+ }.bind(this));
+};
+
+Editor.prototype.getCurrentPath = function() {
+ return $('#editor-path').innerText;
+};
+
+Editor.prototype.setCurrentPath = function(path) {
+ $('#editor-path').innerText = path;
+};
+
+Editor.prototype.getContent = function() {
+ if (window.CodeMirror)
+ return this.cm_doc.getValue();
+ return $('#editor-content').value;
+}
+
+Editor.prototype.setContent = function(content) {
+ if (window.CodeMirror)
+ this.cm_doc.setValue(content);
+ else
+ $('#editor-content').value = content;
+}
View
253 syncfs-editor/js/filer.js
@@ -0,0 +1,253 @@
+Filer = function(filesystem, container, editor) {
+ this.filesystem = filesystem;
+ this.editor = editor;
+ this.isSyncable = (filesystem.name.indexOf('Syncable') != -1);
+
+ // Directory path => ul node mapping.
+ var nodes = {};
+ this.getListNode = function(path) { return nodes[path]; };
+ this.setListNode = function(path, node) { nodes[path] = node; };
+
+ var container = document.getElementById(container);
+ container.innerHTML = '';
+
+ var tools = createElement('div', {class: 'filer-tools'});
+ tools.appendChild(createElement('span', {id:'filer-usage'}));
+ tools.appendChild(createElement(
+ 'button', {id:'filer-reload', class:'button', innerText:'Reload'}));
+ container.appendChild(tools);
+ container.appendChild(createElement(
+ 'div', {id:'filer-empty-label', innerText:'-- empty --'}));
+
+ // Accept dropping file(s).
+ this.setupDragAndDrop(container);
+
+ // Set up the root node.
+ var rootNode = createElement('ul');
+ this.setListNode('/', rootNode);
+ container.appendChild(rootNode);
+
+ this.reload = function() {
+ rootNode.innerHTML = '';
+ this.showUsage();
+ this.list(filesystem.root);
+ };
+ $('#filer-reload').addEventListener('click', this.reload.bind(this));
+ this.reload();
+
+ if (this.isSyncable) {
+ if (chrome.syncFileSystem.onFileStatusChanged) {
+ chrome.syncFileSystem.onFileStatusChanged.addListener(
+ function(detail) {
+ if (detail.direction == 'remote_to_local') {
+ info('File ' + detail.fileEntry.fullPath +
+ ' is ' + detail.action + ' by background sync.');
+ if (this.editor.getCurrentPath() == detail.fileEntry.fullPath &&
+ detail.action == 'updated') {
+ this.editor.fileHasNewData = true;
+ }
+ }
+ this.reload();
+ }.bind(this));
+ }
+ if (chrome.syncFileSystem.onServiceStatusChanged) {
+ chrome.syncFileSystem.onServiceStatusChanged.addListener(
+ function(detail) {
+ log('Service state updated: ' + detail.state + ': '
+ + detail.description);
+ }.bind(this));
+ }
+ }
+};
+
+Filer.prototype.list = function(dir) {
+ // TODO(kinuko): This should be queued up.
+ var node = this.getListNode(dir.fullPath);
+ if (node.fetching)
+ return;
+ node.fetching = true;
+ var reader = dir.createReader();
+ reader.readEntries(this.didReadEntries.bind(this, dir, reader), error);
+};
+
+Filer.prototype.didReadEntries = function(dir, reader, entries) {
+ var node = this.getListNode(dir.fullPath);
+ if (!entries.length) {
+ node.fetching = false;
+ return;
+ }
+
+ hide('#filer-empty-label');
+
+ for (var i = 0; i < entries.length; ++i) {
+ if (entries[i].isFile) {
+ // Get File object so that we can show the file size.
+ entries[i].file(this.addEntry.bind(this, node, entries[i]),
+ error.bind(null, "Entry.file:", entries[i]));
+ } else {
+ this.addEntry(node, entries[i]);
+ }
+ }
+
+ // Continue reading.
+ reader.readEntries(this.didReadEntries.bind(this, dir, reader), error);
+};
+
+Filer.prototype.rename = function(oldName, newName) {
+ this.filesystem.root.getFile(
+ oldName, {create:false},
+ function(entry) {
+ entry.moveTo(this.filesystem.root, newName,
+ log.bind(null, 'Renamed: ' + oldName + ' -> ' + newName),
+ error);
+ }.bind(this), error.bind(null, 'getFile:' + oldName));
+};
+
+Filer.prototype.addEntry = function(parentNode, entry, file) {
+ var li = createElement('li', {title: entry.name});
+ var node = createElement('div');
+ node.classList.add(entry.isFile ? 'file' : 'dir');
+ node.classList.add('entry');
+ var a = createElement('a', {href: '#'});
+ var nameNode = document.createTextNode(entry.name);
+ a.appendChild(nameNode);
+ node.appendChild(a);
+ li.appendChild(node);
+
+ if (this.isSyncable && chrome.syncFileSystem.getFileStatus) {
+ chrome.syncFileSystem.getFileStatus(entry, function(status) {
+ node.classList.add(status);
+ });
+ }
+
+ if (!entry.isFile) {
+ console.log('Skipping directory:' + entry.fullPath);
+ return;
+ }
+
+ // Show size in a separate div '<div>[size] KB</div>'
+ var sizeDiv = createElement('div', {class:'size'});
+ sizeDiv.appendChild(document.createTextNode(this.formatSize(file.size)));
+ node.appendChild(sizeDiv);
+
+ // Set up an input field and double-click handler for rename oepration.
+ var inputNode = createElement(
+ 'input', {type:'text', value:entry.name, style:'display:inline-block'});
+ inputNode.addEventListener('keydown', function(ev) {
+ if (ev.keyCode == 13) {
+ this.resetRenameFocus = null;
+ var oldName = nameNode.textContent;
+ var newName = inputNode.value;
+ if (!validFileName(newName)) {
+ inputNode.value = oldName;
+ return;
+ }
+ nameNode.textContent = newName;
+ a.replaceChild(nameNode, inputNode);
+ if (oldName != newName)
+ this.rename(oldName, newName);
+ }
+ }.bind(this));
+
+ a.addEventListener('dblclick', function(ev) {
+ if (this.resetRenameFocus)
+ this.resetRenameFocus();
+ this.resetRenameFocus = function() { a.replaceChild(nameNode, inputNode); };
+ a.replaceChild(inputNode, nameNode);
+ inputNode.focus();
+ }.bind(this));
+
+ // Set up click handler to open the file in the editor.
+ a.addEventListener('click', function(ev) {
+ if (this.resetRenameFocus) {
+ this.resetRenameFocus();
+ this.resetRenameFocus = null;
+ }
+ this.editor.open(nameNode.textContent);
+ }.bind(this));
+
+ // Show delete button.
+ var deleteButton = createElement('button',
+ {class:'button delete-button', innerText:'x'});
+ node.appendChild(deleteButton);
+ deleteButton.addEventListener('click', function(ev) {
+ ev.stopPropagation();
+ this.filesystem.root.getFile(
+ nameNode.textContent, {create:false},
+ function(entry) {
+ entry.remove(function() {
+ parentNode.removeChild.bind(parentNode, li),
+ this.reload();
+ }.bind(this), error.bind(this, "remove:", entry));
+ }.bind(this));
+ }.bind(this));
+
+ parentNode.appendChild(li);
+};
+
+Filer.prototype.showUsage = function() {
+ if (this.isSyncable && chrome && chrome.syncFileSystem) {
+ chrome.syncFileSystem.getUsageAndQuota(
+ this.filesystem,
+ function(info) {
+ if (chrome.runtime.lastError) {
+ error('getUsageAndQuota: ' + chrome.runtime.lastError.message);
+ return;
+ }
+ $('#filer-usage').innerText =
+ 'Usage:' + this.formatSize(info.usageBytes);
+ }.bind(this));
+ return;
+ }
+ webkitStorageInfo.queryUsageAndQuota(
+ this.filesystem,
+ function(usage, quota) {
+ $('#filer-usage').innerText =
+ 'Usage:' + this.formatSize(usage);
+ }.bind(this));
+};
+
+Filer.prototype.formatSize = function(size) {
+ var unit = 0;
+ while (size > 1024 && unit < 5) {
+ size /= 1024;
+ unit++;
+ }
+ size = Math.floor(size);
+ return size + ' ' + ['', 'K', 'M', 'G', 'T'][unit] + 'B';
+};
+
+Filer.prototype.setupDragAndDrop = function(elem) {
+ elem.addEventListener('dragover', function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ }, false);
+ elem.addEventListener('drop', function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ var items = e.dataTransfer.items;
+ for (var i = 0; i < items.length; ++i) {
+ if (items[i].kind != 'file') {
+ log('Skipping non-file entry:' + items[i].kind);
+ continue;
+ }
+ if (!items[i].webkitGetAsEntry) {
+ error('Entries in drag-and-drop not supported in your browser.');
+ break;
+ }
+ var entry = items[i].webkitGetAsEntry();
+ if (!entry || !entry.isFile) {
+ log('Skipping non-file entries:' + items[i].getAsFile().name);
+ continue;
+ }
+ log('Copying file:' + entry.name);
+ entry.copyTo(this.filesystem.root, entry.name, function(copied) {
+ copied.file(function(file) {
+ log('Copied file:' + copied.name);
+ var node = this.getListNode('/');
+ this.addEntry(node, copied, file);
+ }.bind(this), error);
+ }.bind(this), error);
+ }
+ }.bind(this), false);
+}
View
46 syncfs-editor/js/main.js
@@ -0,0 +1,46 @@
+var supportsSyncFileSystem = chrome && chrome.syncFileSystem;
+
+document.addEventListener(
+ 'DOMContentLoaded',
+ function() {
+ $('#fs-syncable').addEventListener('click', openSyncableFileSystem);
+ $('#fs-temporary').addEventListener('click', openTemporaryFileSystem);
+
+ if (supportsSyncFileSystem)
+ openSyncableFileSystem();
+ else
+ openTemporaryFileSystem();
+ }
+);
+
+function onFileSystemOpened(fs) {
+ console.log('Got FileSystem:' + fs.name);
+ var editor = new Editor(fs, 'editor');
+ var filer = new Filer(fs, 'filer', editor);
+ editor.filer = filer;
+}
+
+function openTemporaryFileSystem() {
+ $('#fs-temporary').classList.add('selected');
+ $('#fs-syncable').classList.remove('selected');
+ webkitRequestFileSystem(TEMPORARY, 1024,
+ onFileSystemOpened,
+ error.bind(null, 'requestFileSystem'));
+}
+
+function openSyncableFileSystem() {
+ if (!chrome || !chrome.syncFileSystem ||
+ !chrome.syncFileSystem.requestFileSystem) {
+ error('Syncable FileSystem is not supported in your environment.');
+ return;
+ }
+ $('#fs-syncable').classList.add('selected');
+ $('#fs-temporary').classList.remove('selected');
+ chrome.syncFileSystem.requestFileSystem(function (fs) {
+ if (chrome.runtime.lastError) {
+ error('requestFileSystem: ' + chrome.runtime.lastError.message);
+ return;
+ }
+ onFileSystemOpened(fs);
+ });
+}
View
89 syncfs-editor/js/utils.js
@@ -0,0 +1,89 @@
+function $(q) {
+ return document.querySelector(q);
+}
+
+function show(q) {
+ $(q).classList.remove('hide');
+}
+
+function hide(q) {
+ $(q).classList.add('hide');
+}
+
+function validFileName(path) {
+ if (!path.length) {
+ error('Empty name was given.');
+ return false;
+ }
+ if (path.indexOf('/') >= 0) {
+ error('File name should not contain any slash (/): "' + path + '"');
+ return false;
+ }
+ return true;
+}
+
+function log(msg) {
+ document.getElementById('log').innerHTML = msg;
+ console.log(msg, arguments);
+}
+
+function createElement(name, attributes) {
+ var elem = document.createElement(name);
+ for (var key in attributes) {
+ if (key == 'id')
+ elem.id = attributes[key];
+ else if (key == 'innerText')
+ elem.innerText = attributes[key];
+ else
+ elem.setAttribute(key, attributes[key]);
+ }
+ return elem;
+}
+
+function info(msg) {
+ console.log('INFO: ', arguments);
+ var e = document.getElementById('info');
+ e.innerText = msg;
+ e.classList.remove('hide');
+ window.setTimeout(function() { e.innerHTML = ''; }, 5000);
+}
+
+function error(msg) {
+ console.log('ERROR: ', arguments);
+ var message = '';
+ for (var i = 0; i < arguments.length; i++) {
+ var description = '';
+ if (arguments[i] instanceof FileError) {
+ switch (arguments[i].code) {
+ case FileError.QUOTA_EXCEEDED_ERR:
+ description = 'QUOTA_EXCEEDED_ERR';
+ break;
+ case FileError.NOT_FOUND_ERR:
+ description = 'NOT_FOUND_ERR';
+ break;
+ case FileError.SECURITY_ERR:
+ description = 'SECURITY_ERR';
+ break;
+ case FileError.INVALID_MODIFICATION_ERR:
+ description = 'INVALID_MODIFICATION_ERR';
+ break;
+ case FileError.INVALID_STATE_ERR:
+ description = 'INVALID_STATE_ERR';
+ break;
+ default:
+ description = 'Unknown Error';
+ break;
+ }
+ message += ': ' + description;
+ } else if (arguments[i].fullPath) {
+ message += arguments[i].fullPath + ' ';
+ } else {
+ message += arguments[i] + ' ';
+ }
+ }
+ var e = document.getElementById('error');
+ e.innerText = 'ERROR:' + message;
+ e.classList.remove('hide');
+ window.setTimeout(function() { e.innerHTML = ''; }, 5000);
+}
+
View
26 syncfs-editor/main.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<head>
+ <link rel="stylesheet" href="third_party/CodeMirror/lib/codemirror.css">
+ <link rel="stylesheet" href="third_party/CodeMirror/theme/neat.css">
+ <link rel="stylesheet" href="css/editor.css" type="text/css" />
+ <script type="text/javascript" src="third_party/CodeMirror/lib/codemirror.js"></script>
+ <script type="text/javascript" src="js/utils.js"></script>
+ <script type="text/javascript" src="js/filer.js"></script>
+ <script type="text/javascript" src="js/editor.js"></script>
+ <title>Cloud editor (SyncFileSystem sample)</title>
+</head>
+<body>
+ <div id="fs-selector">
+ <button id="fs-syncable" class="fs-button">Syncable</button>
+ <button id="fs-temporary" class="fs-button">Temporary</button>
+ </div>
+
+ <div id="error" class="error" class="hide"></div>
+
+ <div class="filer" id="filer"></div>
+ <div class="editor" id="editor"></div>
+
+ <div id="log"></div>
+ <div id="info" class="info" class="hide"></div>
+ <script src="js/main.js"></script>
+</body>
View
17 syncfs-editor/manifest.json
@@ -0,0 +1,17 @@
+{
+ "name": "SyncFS editor",
+ "version": "0.286",
+ "manifest_version": 2,
+ "description": "SyncFS editor (syncFileSystem sample)",
+ "icons": {
+ "16": "img/icon-16.png",
+ "24": "img/icon-24.png",
+ "128": "img/icon-128.png"
+ },
+ "app": {
+ "background": {
+ "scripts": ["js/background.js"]
+ }
+ },
+ "permissions": ["syncFileSystem"]
+}

0 comments on commit bdffa50

Please sign in to comment.
Something went wrong with that request. Please try again.