1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 * Namespace for utility functions.
13 * Returns a function that console.log's its arguments, prefixed by |msg|.
15 * @param {string} msg The message prefix to use in the log.
16 * @param {function(...string)=} opt_callback A function to invoke after
18 * @return {function(...string)} Function that logs.
20 util.flog = function(msg, opt_callback) {
22 var ary = Array.apply(null, arguments);
23 console.log(msg + ': ' + ary.join(', '));
25 opt_callback.apply(null, arguments);
30 * Returns a function that throws an exception that includes its arguments
33 * @param {string} msg The message prefix to use in the exception.
34 * @return {function(...string)} Function that throws.
36 util.ferr = function(msg) {
38 var ary = Array.apply(null, arguments);
39 throw new Error(msg + ': ' + ary.join(', '));
44 * Install a sensible toString() on the FileError object.
46 * FileError.prototype.code is a numeric code describing the cause of the
47 * error. The FileError constructor has a named property for each possible
48 * error code, but provides no way to map the code to the named property.
49 * This toString() implementation fixes that.
51 util.installFileErrorToString = function() {
52 FileError.prototype.toString = function() {
53 return '[object FileError: ' + util.getFileErrorMnemonic(this.code) + ']';
58 * @param {number} code The file error code.
59 * @return {string} The file error mnemonic.
61 util.getFileErrorMnemonic = function(code) {
62 for (var key in FileError) {
63 if (key.search(/_ERR$/) != -1 && FileError[key] == code)
71 * @param {number} code File error code (from FileError object).
72 * @return {string} Translated file error string.
74 util.getFileErrorString = function(code) {
75 for (var key in FileError) {
76 var match = /(.*)_ERR$/.exec(key);
77 if (match && FileError[key] == code) {
78 // This would convert 1 to 'NOT_FOUND'.
83 console.warn('File error: ' + code);
84 return loadTimeData.getString('FILE_ERROR_' + code) ||
85 loadTimeData.getString('FILE_ERROR_GENERIC');
89 * @param {string} str String to escape.
90 * @return {string} Escaped string.
92 util.htmlEscape = function(str) {
93 return str.replace(/[<>&]/g, function(entity) {
95 case '<': return '<';
96 case '>': return '>';
97 case '&': return '&';
103 * @param {string} str String to unescape.
104 * @return {string} Unescaped string.
106 util.htmlUnescape = function(str) {
107 return str.replace(/&(lt|gt|amp);/g, function(entity) {
109 case '<': return '<';
110 case '>': return '>';
111 case '&': return '&';
117 * Iterates the entries contained by dirEntry, and invokes callback once for
118 * each entry. On completion, successCallback will be invoked.
120 * @param {DirectoryEntry} dirEntry The entry of the directory.
121 * @param {function(Entry, function())} callback Invoked for each entry.
122 * @param {function()} successCallback Invoked on completion.
123 * @param {function(FileError)} errorCallback Invoked if an error is found on
124 * directory entry reading.
126 util.forEachDirEntry = function(
127 dirEntry, callback, successCallback, errorCallback) {
128 var reader = dirEntry.createReader();
129 var iterate = function() {
130 reader.readEntries(function(entries) {
131 if (entries.length == 0) {
138 function(forEachCallback, entry) {
139 // Do not pass index nor entries.
140 callback(entry, forEachCallback);
149 * Reads contents of directory.
150 * @param {DirectoryEntry} root Root entry.
151 * @param {string} path Directory path.
152 * @param {function(Array.<Entry>)} callback List of entries passed to callback.
154 util.readDirectory = function(root, path, callback) {
155 var onError = function(e) {
158 root.getDirectory(path, {create: false}, function(entry) {
159 var reader = entry.createReader();
161 var readNext = function() {
162 reader.readEntries(function(results) {
163 if (results.length == 0) {
167 r.push.apply(r, results);
176 * Utility function to resolve multiple directories with a single call.
178 * The successCallback will be invoked once for each directory object
179 * found. The errorCallback will be invoked once for each
180 * path that could not be resolved.
182 * The successCallback is invoked with a null entry when all paths have
185 * @param {DirEntry} dirEntry The base directory.
186 * @param {Object} params The parameters to pass to the underlying
187 * getDirectory calls.
188 * @param {Array.<string>} paths The list of directories to resolve.
189 * @param {function(!DirEntry)} successCallback The function to invoke for
190 * each DirEntry found. Also invoked once with null at the end of the
192 * @param {function(FileError)} errorCallback The function to invoke
193 * for each path that cannot be resolved.
195 util.getDirectories = function(dirEntry, params, paths, successCallback,
198 // Copy the params array, since we're going to destroy it.
199 params = [].slice.call(params);
201 var onComplete = function() {
202 successCallback(null);
205 var getNextDirectory = function() {
206 var path = paths.shift();
210 dirEntry.getDirectory(
213 successCallback(entry);
226 * Utility function to resolve multiple files with a single call.
228 * The successCallback will be invoked once for each directory object
229 * found. The errorCallback will be invoked once for each
230 * path that could not be resolved.
232 * The successCallback is invoked with a null entry when all paths have
235 * @param {DirEntry} dirEntry The base directory.
236 * @param {Object} params The parameters to pass to the underlying
238 * @param {Array.<string>} paths The list of files to resolve.
239 * @param {function(!FileEntry)} successCallback The function to invoke for
240 * each FileEntry found. Also invoked once with null at the end of the
242 * @param {function(FileError)} errorCallback The function to invoke
243 * for each path that cannot be resolved.
245 util.getFiles = function(dirEntry, params, paths, successCallback,
247 // Copy the params array, since we're going to destroy it.
248 params = [].slice.call(params);
250 var onComplete = function() {
251 successCallback(null);
254 var getNextFile = function() {
255 var path = paths.shift();
262 successCallback(entry);
275 * Resolve a path to either a DirectoryEntry or a FileEntry, regardless of
276 * whether the path is a directory or file.
278 * @param {DirectoryEntry} root The root of the filesystem to search.
279 * @param {string} path The path to be resolved.
280 * @param {function(Entry)} resultCallback Called back when a path is
281 * successfully resolved. Entry will be either a DirectoryEntry or
283 * @param {function(FileError)} errorCallback Called back if an unexpected
284 * error occurs while resolving the path.
286 util.resolvePath = function(root, path, resultCallback, errorCallback) {
287 if (path == '' || path == '/') {
288 resultCallback(root);
293 path, {create: false},
296 if (err.code == FileError.TYPE_MISMATCH_ERR) {
297 // Bah. It's a directory, ask again.
299 path, {create: false},
309 * Locate the file referred to by path, creating directories or the file
310 * itself if necessary.
311 * @param {DirEntry} root The root entry.
312 * @param {string} path The file path.
313 * @param {function(FileEntry)} successCallback The callback.
314 * @param {function(FileError)} errorCallback The callback.
316 util.getOrCreateFile = function(root, path, successCallback, errorCallback) {
320 var onDirFound = function(dirEntry) {
321 dirEntry.getFile(basename, { create: true },
322 successCallback, errorCallback);
325 var i = path.lastIndexOf('/');
327 dirname = path.substr(0, i);
328 basename = path.substr(i + 1);
338 util.getOrCreateDirectory(root, dirname, onDirFound, errorCallback);
342 * Locate the directory referred to by path, creating directories along the
344 * @param {DirEntry} root The root entry.
345 * @param {string} path The directory path.
346 * @param {function(FileEntry)} successCallback The callback.
347 * @param {function(FileError)} errorCallback The callback.
349 util.getOrCreateDirectory = function(root, path, successCallback,
351 var names = path.split('/');
353 var getOrCreateNextName = function(dir) {
355 return successCallback(dir);
359 name = names.shift();
360 } while (!name || name == '.');
362 dir.getDirectory(name, { create: true }, getOrCreateNextName,
366 getOrCreateNextName(root);
370 * Renames the entry to newName.
371 * @param {Entry} entry The entry to be renamed.
372 * @param {string} newName The new name.
373 * @param {function(Entry)} successCallback Callback invoked when the rename
374 * is successfully done.
375 * @param {function(FileError)} errorCallback Callback invoked when an error
378 util.rename = function(entry, newName, successCallback, errorCallback) {
379 entry.getParent(function(parent) {
380 // Before moving, we need to check if there is an existing entry at
381 // parent/newName, since moveTo will overwrite it.
382 // Note that this way has some timing issue. After existing check,
383 // a new entry may be create on background. However, there is no way not to
384 // overwrite the existing file, unfortunately. The risk should be low,
385 // assuming the unsafe period is very short.
386 (entry.isFile ? parent.getFile : parent.getDirectory).call(
387 parent, newName, {create: false},
389 // The entry with the name already exists.
390 errorCallback(util.createFileError(FileError.PATH_EXISTS_ERR));
393 if (error.code != FileError.NOT_FOUND_ERR) {
394 // Unexpected error is found.
395 errorCallback(error);
399 // No existing entry is found.
400 entry.moveTo(parent, newName, successCallback, errorCallback);
406 * Remove a file or a directory.
407 * @param {Entry} entry The entry to remove.
408 * @param {function()} onSuccess The success callback.
409 * @param {function(FileError)} onError The error callback.
411 util.removeFileOrDirectory = function(entry, onSuccess, onError) {
412 if (entry.isDirectory)
413 entry.removeRecursively(onSuccess, onError);
415 entry.remove(onSuccess, onError);
419 * Checks if an entry exists at |relativePath| in |dirEntry|.
420 * If exists, tries to deduplicate the path by inserting parenthesized number,
421 * such as " (1)", before the extension. If it still exists, tries the
422 * deduplication again by increasing the number up to 10 times.
423 * For example, suppose "file.txt" is given, "file.txt", "file (1).txt",
424 * "file (2).txt", ..., "file (9).txt" will be tried.
426 * @param {DirectoryEntry} dirEntry The target directory entry.
427 * @param {string} relativePath The path to be deduplicated.
428 * @param {function(string)} onSuccess Called with the deduplicated path on
430 * @param {function(FileError)} onError Called on error.
432 util.deduplicatePath = function(dirEntry, relativePath, onSuccess, onError) {
433 // The trial is up to 10.
436 // Crack the path into three part. The parenthesized number (if exists) will
437 // be replaced by incremented number for retry. For example, suppose
438 // |relativePath| is "file (10).txt", the second check path will be
440 var match = /^(.*?)(?: \((\d+)\))?(\.[^.]*?)?$/.exec(relativePath);
441 var prefix = match[1];
442 var copyNumber = match[2] ? parseInt(match[2], 10) : 0;
443 var ext = match[3] ? match[3] : '';
445 // The path currently checking the existence.
446 var trialPath = relativePath;
448 var onNotResolved = function(err) {
449 // We expect to be unable to resolve the target file, since we're going
450 // to create it during the copy. However, if the resolve fails with
451 // anything other than NOT_FOUND, that's trouble.
452 if (err.code != FileError.NOT_FOUND_ERR) {
457 // Found a path that doesn't exist.
458 onSuccess(trialPath);
461 var numRetry = MAX_RETRY;
462 var onResolved = function(entry) {
463 if (--numRetry == 0) {
464 // Hit the limit of the number of retrial.
465 // Note that we cannot create FileError object directly, so here we use
466 // Object.create instead.
467 onError(util.createFileError(FileError.PATH_EXISTS_ERR));
472 trialPath = prefix + ' (' + copyNumber + ')' + ext;
473 util.resolvePath(dirEntry, trialPath, onResolved, onNotResolved);
476 // Check to see if the target exists.
477 util.resolvePath(dirEntry, trialPath, onResolved, onNotResolved);
481 * Convert a number of bytes into a human friendly format, using the correct
484 * @param {number} bytes The number of bytes.
485 * @return {string} Localized string.
487 util.bytesToString = function(bytes) {
488 // Translation identifiers for size units.
489 var UNITS = ['SIZE_BYTES',
496 // Minimum values for the units above.
504 var str = function(n, u) {
505 // TODO(rginda): Switch to v8Locale's number formatter when it's
507 return strf(u, n.toLocaleString());
510 var fmt = function(s, u) {
511 var rounded = Math.round(bytes / s * 10) / 10;
512 return str(rounded, u);
515 // Less than 1KB is displayed like '80 bytes'.
516 if (bytes < STEPS[1]) {
517 return str(bytes, UNITS[0]);
520 // Up to 1MB is displayed as rounded up number of KBs.
521 if (bytes < STEPS[2]) {
522 var rounded = Math.ceil(bytes / STEPS[1]);
523 return str(rounded, UNITS[1]);
526 // This loop index is used outside the loop if it turns out |bytes|
527 // requires the largest unit.
530 for (i = 2 /* MB */; i < UNITS.length - 1; i++) {
531 if (bytes < STEPS[i + 1])
532 return fmt(STEPS[i], UNITS[i]);
535 return fmt(STEPS[i], UNITS[i]);
539 * Utility function to read specified range of bytes from file
540 * @param {File} file The file to read.
541 * @param {number} begin Starting byte(included).
542 * @param {number} end Last byte(excluded).
543 * @param {function(File, Uint8Array)} callback Callback to invoke.
544 * @param {function(FileError)} onError Error handler.
546 util.readFileBytes = function(file, begin, end, callback, onError) {
547 var fileReader = new FileReader();
548 fileReader.onerror = onError;
549 fileReader.onloadend = function() {
550 callback(file, new ByteReader(fileReader.result));
552 fileReader.readAsArrayBuffer(file.slice(begin, end));
556 * Write a blob to a file.
557 * Truncates the file first, so the previous content is fully overwritten.
558 * @param {FileEntry} entry File entry.
559 * @param {Blob} blob The blob to write.
560 * @param {function(Event)} onSuccess Completion callback. The first argument is
561 * a 'writeend' event.
562 * @param {function(FileError)} onError Error handler.
564 util.writeBlobToFile = function(entry, blob, onSuccess, onError) {
565 var truncate = function(writer) {
566 writer.onerror = onError;
567 writer.onwriteend = write.bind(null, writer);
571 var write = function(writer) {
572 writer.onwriteend = onSuccess;
576 entry.createWriter(truncate, onError);
580 * Returns a string '[Ctrl-][Alt-][Shift-][Meta-]' depending on the event
581 * modifiers. Convenient for writing out conditions in keyboard handlers.
583 * @param {Event} event The keyboard event.
584 * @return {string} Modifiers.
586 util.getKeyModifiers = function(event) {
587 return (event.ctrlKey ? 'Ctrl-' : '') +
588 (event.altKey ? 'Alt-' : '') +
589 (event.shiftKey ? 'Shift-' : '') +
590 (event.metaKey ? 'Meta-' : '');
594 * @param {HTMLElement} element Element to transform.
595 * @param {Object} transform Transform object,
596 * contains scaleX, scaleY and rotate90 properties.
598 util.applyTransform = function(element, transform) {
599 element.style.webkitTransform =
600 transform ? 'scaleX(' + transform.scaleX + ') ' +
601 'scaleY(' + transform.scaleY + ') ' +
602 'rotate(' + transform.rotate90 * 90 + 'deg)' :
607 * Makes filesystem: URL from the path.
608 * @param {string} path File or directory path.
609 * @return {string} URL.
611 util.makeFilesystemUrl = function(path) {
612 path = path.split('/').map(encodeURIComponent).join('/');
613 var prefix = 'external';
614 return 'filesystem:' + chrome.runtime.getURL(prefix + path);
618 * Extracts path from filesystem: URL.
619 * @param {string} url Filesystem URL.
620 * @return {string} The path.
622 util.extractFilePath = function(url) {
624 /^filesystem:[\w-]*:\/\/[\w]*\/(external|persistent|temporary)(\/.*)$/.
626 var path = match && match[2];
627 if (!path) return null;
628 return decodeURIComponent(path);
632 * Traverses a directory tree whose root is the given entry, and invokes
633 * callback for each entry. Upon completion, successCallback will be called.
634 * On error, errorCallback will be called.
636 * @param {Entry} entry The root entry.
637 * @param {function(Entry):boolean} callback Callback invoked for each entry.
638 * If this returns false, entries under it won't be traversed. Note that
639 * its siblings (and their children) will be still traversed.
640 * @param {function()} successCallback Called upon successful completion.
641 * @param {function(error)} errorCallback Called upon error.
643 util.traverseTree = function(entry, callback, successCallback, errorCallback) {
644 if (!callback(entry)) {
649 util.forEachDirEntry(
651 function(child, iterationCallback) {
652 util.traverseTree(child, callback, iterationCallback, errorCallback);
659 * A shortcut function to create a child element with given tag and class.
661 * @param {HTMLElement} parent Parent element.
662 * @param {string=} opt_className Class name.
663 * @param {string=} opt_tag Element tag, DIV is omitted.
664 * @return {Element} Newly created element.
666 util.createChild = function(parent, opt_className, opt_tag) {
667 var child = parent.ownerDocument.createElement(opt_tag || 'div');
669 child.className = opt_className;
670 parent.appendChild(child);
675 * Update the app state.
677 * @param {string} path Path to be put in the address bar after the hash.
678 * If null the hash is left unchanged.
679 * @param {string|Object=} opt_param Search parameter. Used directly if string,
680 * stringified if object. If omitted the search query is left unchanged.
682 util.updateAppState = function(path, opt_param) {
683 window.appState = window.appState || {};
684 if (typeof opt_param == 'string')
685 window.appState.params = {};
686 else if (typeof opt_param == 'object')
687 window.appState.params = opt_param;
689 window.appState.defaultPath = path;
695 * Return a translated string.
697 * Wrapper function to make dealing with translated strings more concise.
698 * Equivalent to loadTimeData.getString(id).
700 * @param {string} id The id of the string to return.
701 * @return {string} The translated string.
704 return loadTimeData.getString(id);
708 * Return a translated string with arguments replaced.
710 * Wrapper function to make dealing with translated strings more concise.
711 * Equivalent to loadTimeData.getStringF(id, ...).
713 * @param {string} id The id of the string to return.
714 * @param {...string} var_args The values to replace into the string.
715 * @return {string} The translated string with replaced values.
717 function strf(id, var_args) {
718 return loadTimeData.getStringF.apply(loadTimeData, arguments);
722 * Adapter object that abstracts away the the difference between Chrome app APIs
723 * v1 and v2. Is only necessary while the migration to v2 APIs is in progress.
724 * TODO(mtomasz): Clean up this. crbug.com/240606.
728 * @return {boolean} True if Files.app is running as an open files or a select
729 * folder dialog. False otherwise.
731 runningInBrowser: function() {
732 return !window.appID;
736 * @param {function(Object)} callback Function accepting a preference map.
738 getPreferences: function(callback) {
739 chrome.storage.local.get(callback);
743 * @param {string} key Preference name.
744 * @param {function(string)} callback Function accepting the preference value.
746 getPreference: function(key, callback) {
747 chrome.storage.local.get(key, function(items) {
748 callback(items[key]);
753 * @param {string} key Preference name.
754 * @param {string|Object} value Preference value.
755 * @param {function()=} opt_callback Completion callback.
757 setPreference: function(key, value, opt_callback) {
758 if (typeof value != 'string')
759 value = JSON.stringify(value);
763 chrome.storage.local.set(items, opt_callback);
768 * Attach page load handler.
769 * @param {function()} handler Application-specific load handler.
771 util.addPageLoadHandler = function(handler) {
772 document.addEventListener('DOMContentLoaded', function() {
778 * Save app launch data to the local storage.
780 util.saveAppState = function() {
782 util.platform.setPreference(window.appID, window.appState);
786 * AppCache is a persistent timestamped key-value storage backed by
787 * HTML5 local storage.
789 * It is not designed for frequent access. In order to avoid costly
790 * localStorage iteration all data is kept in a single localStorage item.
791 * There is no in-memory caching, so concurrent access is _almost_ safe.
793 * TODO(kaznacheev) Reimplement this based on Indexed DB.
795 util.AppCache = function() {};
800 util.AppCache.KEY = 'AppCache';
803 * Max number of items.
805 util.AppCache.CAPACITY = 100;
810 util.AppCache.LIFETIME = 30 * 24 * 60 * 60 * 1000; // 30 days.
813 * @param {string} key Key.
814 * @param {function(number)} callback Callback accepting a value.
816 util.AppCache.getValue = function(key, callback) {
817 util.AppCache.read_(function(map) {
818 var entry = map[key];
819 callback(entry && entry.value);
826 * @param {string} key Key.
827 * @param {string} value Value. Remove the key if value is null.
828 * @param {number=} opt_lifetime Maximum time to keep an item (in milliseconds).
830 util.AppCache.update = function(key, value, opt_lifetime) {
831 util.AppCache.read_(function(map) {
835 expire: Date.now() + (opt_lifetime || util.AppCache.LIFETIME)
837 } else if (key in map) {
840 return; // Nothing to do.
842 util.AppCache.cleanup_(map);
843 util.AppCache.write_(map);
848 * @param {function(Object)} callback Callback accepting a map of timestamped
852 util.AppCache.read_ = function(callback) {
853 util.platform.getPreference(util.AppCache.KEY, function(json) {
856 callback(JSON.parse(json));
858 // The local storage item somehow got messed up, start fresh.
866 * @param {Object} map A map of timestamped key-value pairs.
869 util.AppCache.write_ = function(map) {
870 util.platform.setPreference(util.AppCache.KEY, JSON.stringify(map));
874 * Remove over-capacity and obsolete items.
876 * @param {Object} map A map of timestamped key-value pairs.
879 util.AppCache.cleanup_ = function(map) {
880 // Sort keys by ascending timestamps.
882 for (var key in map) {
883 if (map.hasOwnProperty(key))
886 keys.sort(function(a, b) { return map[a].expire > map[b].expire });
888 var cutoff = Date.now();
891 while (obsolete < keys.length &&
892 map[keys[obsolete]].expire < cutoff) {
896 var overCapacity = Math.max(0, keys.length - util.AppCache.CAPACITY);
898 var itemsToDelete = Math.max(obsolete, overCapacity);
899 for (var i = 0; i != itemsToDelete; i++) {
907 * @param {Image} image Image element.
908 * @param {string} url Source url.
909 * @param {Object=} opt_options Hash array of options, eg. width, height,
910 * maxWidth, maxHeight, scale, cache.
911 * @param {function()=} opt_isValid Function returning false iff the task
912 * is not valid and should be aborted.
913 * @return {?number} Task identifier or null if fetched immediately from
916 util.loadImage = function(image, url, opt_options, opt_isValid) {
917 return ImageLoaderClient.loadToImage(url,
921 function() { image.onerror(); },
926 * Cancels loading an image.
927 * @param {number} taskId Task identifier returned by util.loadImage().
929 util.cancelLoadImage = function(taskId) {
930 ImageLoaderClient.getInstance().cancel(taskId);
934 * Finds proerty descriptor in the object prototype chain.
935 * @param {Object} object The object.
936 * @param {string} propertyName The property name.
937 * @return {Object} Property descriptor.
939 util.findPropertyDescriptor = function(object, propertyName) {
940 for (var p = object; p; p = Object.getPrototypeOf(p)) {
941 var d = Object.getOwnPropertyDescriptor(p, propertyName);
949 * Calls inherited property setter (useful when property is
951 * @param {Object} object The object.
952 * @param {string} propertyName The property name.
953 * @param {*} value Value to set.
955 util.callInheritedSetter = function(object, propertyName, value) {
956 var d = util.findPropertyDescriptor(Object.getPrototypeOf(object),
958 d.set.call(object, value);
962 * Returns true if the board of the device matches the given prefix.
963 * @param {string} boardPrefix The board prefix to match against.
964 * (ex. "x86-mario". Prefix is used as the actual board name comes with
965 * suffix like "x86-mario-something".
966 * @return {boolean} True if the board of the device matches the given prefix.
968 util.boardIs = function(boardPrefix) {
969 // The board name should be lower-cased, but making it case-insensitive for
970 // backward compatibility just in case.
971 var board = str('CHROMEOS_RELEASE_BOARD');
972 var pattern = new RegExp('^' + boardPrefix, 'i');
973 return board.match(pattern) != null;
977 * Adds an isFocused method to the current window object.
979 util.addIsFocusedMethod = function() {
982 window.addEventListener('focus', function() {
986 window.addEventListener('blur', function() {
991 * @return {boolean} True if focused.
993 window.isFocused = function() {
999 * Makes a redirect to the specified Files.app's window from another window.
1000 * @param {number} id Window id.
1001 * @param {string} url Target url.
1002 * @return {boolean} True if the window has been found. False otherwise.
1004 util.redirectMainWindow = function(id, url) {
1005 // TODO(mtomasz): Implement this for Apps V2, once the photo importer is
1011 * Checks, if the Files.app's window is in a full screen mode.
1013 * @param {AppWindow} appWindow App window to be maximized.
1014 * @return {boolean} True if the full screen mode is enabled.
1016 util.isFullScreen = function(appWindow) {
1018 return appWindow.isFullscreen();
1020 console.error('App window not passed. Unable to check status of ' +
1021 'the full screen mode.');
1027 * Toggles the full screen mode.
1029 * @param {AppWindow} appWindow App window to be maximized.
1030 * @param {boolean} enabled True for enabling, false for disabling.
1032 util.toggleFullScreen = function(appWindow, enabled) {
1035 appWindow.fullscreen();
1037 appWindow.restore();
1042 'App window not passed. Unable to toggle the full screen mode.');
1046 * The type of a file operation.
1049 util.FileOperationType = {
1056 * The type of a file operation error.
1059 util.FileOperationErrorType = {
1060 UNEXPECTED_SOURCE_FILE: 0,
1062 FILESYSTEM_ERROR: 2,
1066 * The kind of an entry changed event.
1069 util.EntryChangedKind = {
1075 * @param {DirectoryEntry|Object} entry DirectoryEntry to be checked.
1076 * @return {boolean} True if the given entry is fake.
1078 util.isFakeDirectoryEntry = function(entry) {
1079 // Currently, fake entry doesn't support createReader.
1080 return !('createReader' in entry);
1084 * Creates a FileError instance with given code.
1085 * Note that we cannot create FileError instance by "new FileError(code)",
1086 * unfortunately, so here we use Object.create.
1087 * @param {number} code Error code for the FileError.
1088 * @return {FileError} FileError instance
1090 util.createFileError = function(code) {
1091 return Object.create(FileError.prototype, {
1092 code: { get: function() { return code; } }
1097 * @param {Entry|Object} entry1 The entry to be compared. Can be a fake.
1098 * @param {Entry|Object} entry2 The entry to be compared. Can be a fake.
1099 * @return {boolean} True if the both entry represents a same file or directory.
1101 util.isSameEntry = function(entry1, entry2) {
1102 // Currently, we can assume there is only one root.
1103 // When we support multi-file system, we need to look at filesystem, too.
1104 return entry1 === null ? entry2 === null : entry1.fullPath == entry2.fullPath;
1108 * @param {Entry|Object} parent The parent entry. Can be a fake.
1109 * @param {Entry|Object} child The child entry. Can be a fake.
1110 * @return {boolean} True if parent entry is actualy the parent of the child
1113 util.isParentEntry = function(parent, child) {
1114 // Currently, we can assume there is only one root.
1115 // When we support multi-file system, we need to look at filesystem, too.
1116 return PathUtil.isParentPath(parent.fullPath, child.fullPath);
1120 * Views files in the browser.
1122 * @param {Array.<string>} urls URLs of files to view.
1123 * @param {function(bool)} callback Callback notifying success or not.
1125 util.viewFilesInBrowser = function(urls, callback) {
1126 var taskId = chrome.runtime.id + '|file|view-in-browser';
1127 chrome.fileBrowserPrivate.executeTask(taskId, urls, callback);
1133 * If the browser is opening, the url is opened in a new tag, otherwise the url
1134 * is opened in a new window.
1136 * @param {string} url URL to visit.
1138 util.visitURL = function(url) {
1139 var params = {url: url};
1140 chrome.tabs.create(params, function() {
1141 if (chrome.runtime.lastError)
1142 chrome.windows.create(params);
1147 * Returns normalized current locale, or default locale - 'en'.
1148 * @return {string} Current locale
1150 util.getCurrentLocaleOrDefault = function() {
1151 // chrome.i18n.getMessage('@@ui_locale') can't be used in packed app.
1152 // Instead, we pass it from C++-side with strings.
1153 return str('UI_LOCALE') || 'en';
1157 * Error type of VolumeManager.
1160 util.VolumeError = Object.freeze({
1161 /* Internal errors */
1162 NOT_MOUNTED: 'not_mounted',
1166 UNKNOWN: 'error_unknown',
1167 INTERNAL: 'error_internal',
1168 UNKNOWN_FILESYSTEM: 'error_unknown_filesystem',
1169 UNSUPPORTED_FILESYSTEM: 'error_unsupported_filesystem',
1170 INVALID_ARCHIVE: 'error_invalid_archive',
1171 AUTHENTICATION: 'error_authentication',
1172 PATH_UNMOUNTED: 'error_path_unmounted'
1176 * List of connection types of drive.
1178 * Keep this in sync with the kDriveConnectionType* constants in
1179 * file_browser_private_api.cc.
1183 util.DriveConnectionType = Object.freeze({
1184 OFFLINE: 'offline', // Connection is offline or drive is unavailable.
1185 METERED: 'metered', // Connection is metered. Should limit traffic.
1186 ONLINE: 'online' // Connection is online.
1190 * List of reasons of DriveConnectionType.
1192 * Keep this in sync with the kDriveConnectionReason constants in
1193 * file_browser_private_api.cc.
1197 util.DriveConnectionReason = Object.freeze({
1198 NOT_READY: 'not_ready', // Drive is not ready or authentication is failed.
1199 NO_NETWORK: 'no_network', // Network connection is unavailable.
1200 NO_SERVICE: 'no_service' // Drive service is unavailable.
1204 * The type of each volume.
1207 util.VolumeType = Object.freeze({
1209 DOWNLOADS: 'downloads',
1210 REMOVABLE: 'removable',