Home | History | Annotate | Download | only in js
      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.
      4 
      5 'use strict';
      6 
      7 /**
      8  * Type of a root directory.
      9  * @enum
     10  */
     11 var RootType = {
     12   DOWNLOADS: 'downloads',
     13   ARCHIVE: 'archive',
     14   REMOVABLE: 'removable',
     15   DRIVE: 'drive',
     16   DRIVE_OFFLINE: 'drive_offline',  // A fake root. Not the actual filesystem.
     17   DRIVE_SHARED_WITH_ME: 'drive_shared_with_me',  // A fake root.
     18   DRIVE_RECENT: 'drive_recent'  // A fake root.
     19 };
     20 
     21 /**
     22  * Top directory for each root type.
     23  * @type {Object.<RootType,string>}
     24  */
     25 var RootDirectory = {
     26   DOWNLOADS: '/Downloads',
     27   ARCHIVE: '/archive',
     28   REMOVABLE: '/removable',
     29   DRIVE: '/drive',
     30   DRIVE_OFFLINE: '/drive_offline',  // A fake root. Not the actual filesystem.
     31   DRIVE_SHARED_WITH_ME: '/drive_shared_with_me',  // A fake root.
     32   DRIVE_RECENT: '/drive_recent'  // A fake root.
     33 };
     34 
     35 /**
     36  * Sub root directory for Drive. "root" and "other". This is not used now.
     37  * TODO(haruki): Add namespaces support. http://crbug.com/174233.
     38  * @enum
     39  */
     40 var DriveSubRootDirectory = {
     41   ROOT: 'root',
     42   OTHER: 'other',
     43 };
     44 
     45 var PathUtil = {};
     46 
     47 /**
     48  * The path to the default directory.
     49  * @type {string}
     50  * @const
     51  */
     52 PathUtil.DEFAULT_DIRECTORY = RootDirectory.DOWNLOADS;
     53 
     54 /**
     55  * Checks if the given path represents a special search. Fake entries in
     56  * RootDirectory correspond to special searches.
     57  * @param {string} path Path to check.
     58  * @return {boolean} True if the given path represents a special search.
     59  */
     60 PathUtil.isSpecialSearchRoot = function(path) {
     61   var type = PathUtil.getRootType(path);
     62   return type == RootType.DRIVE_OFFLINE ||
     63       type == RootType.DRIVE_SHARED_WITH_ME ||
     64       type == RootType.DRIVE_RECENT;
     65 };
     66 
     67 /**
     68  * Checks |path| and return true if it is under Google Drive or a sepecial
     69  * search root which represents a special search from Google Drive.
     70  * @param {string} path Path to check.
     71  * @return {boolean} True if the given path represents a Drive based path.
     72  */
     73 PathUtil.isDriveBasedPath = function(path) {
     74   var rootType = PathUtil.getRootType(path);
     75   return rootType === RootType.DRIVE ||
     76       rootType === RootType.DRIVE_SHARED_WITH_ME ||
     77       rootType === RootType.DRIVE_RECENT ||
     78       rootType === RootType.DRIVE_OFFLINE;
     79 };
     80 
     81 /**
     82  * @param {string} path Path starting with '/'.
     83  * @return {string} Top directory (starting with '/').
     84  */
     85 PathUtil.getTopDirectory = function(path) {
     86   var i = path.indexOf('/', 1);
     87   return i === -1 ? path : path.substring(0, i);
     88 };
     89 
     90 /**
     91  * Obtains the parent path of the specified path.
     92  * @param {string} path Path string.
     93  * @return {string} Parent path.
     94  */
     95 PathUtil.getParentDirectory = function(path) {
     96   if (path[path.length - 1] == '/')
     97     return PathUtil.getParentDirectory(path.substring(0, path.length - 1));
     98   var index = path.lastIndexOf('/');
     99   if (index == 0)
    100     return '/';
    101   else if (index == -1)
    102     return '.';
    103   return path.substring(0, index);
    104 };
    105 
    106 /**
    107  * @param {string} path Any unix-style path (may start or not start from root).
    108  * @return {Array.<string>} Path components.
    109  */
    110 PathUtil.split = function(path) {
    111   var fromRoot = false;
    112   if (path[0] === '/') {
    113     fromRoot = true;
    114     path = path.substring(1);
    115   }
    116 
    117   var components = path.split('/');
    118   if (fromRoot)
    119     components[0] = '/' + components[0];
    120   return components;
    121 };
    122 
    123 /**
    124  * Returns a directory part of the given |path|. In other words, the path
    125  * without its base name.
    126  *
    127  * Examples:
    128  * PathUtil.dirname('abc') -> ''
    129  * PathUtil.dirname('a/b') -> 'a'
    130  * PathUtil.dirname('a/b/') -> 'a/b'
    131  * PathUtil.dirname('a/b/c') -> 'a/b'
    132  * PathUtil.dirname('/') -> '/'
    133  * PathUtil.dirname('/abc') -> '/'
    134  * PathUtil.dirname('/abc/def') -> '/abc'
    135  * PathUtil.dirname('') -> ''
    136  *
    137  * @param {string} path The path to be parsed.
    138  * @return {string} The directory path.
    139  */
    140 PathUtil.dirname = function(path) {
    141   var index = path.lastIndexOf('/');
    142   if (index < 0)
    143     return '';
    144   if (index == 0)
    145     return '/';
    146   return path.substring(0, index);
    147 };
    148 
    149 /**
    150  * Returns the base name (the last component) of the given |path|. If the
    151  * |path| ends with '/', returns an empty component.
    152  *
    153  * Examples:
    154  * PathUtil.basename('abc') -> 'abc'
    155  * PathUtil.basename('a/b') -> 'b'
    156  * PathUtil.basename('a/b/') -> ''
    157  * PathUtil.basename('a/b/c') -> 'c'
    158  * PathUtil.basename('/') -> ''
    159  * PathUtil.basename('/abc') -> 'abc'
    160  * PathUtil.basename('/abc/def') -> 'def'
    161  * PathUtil.basename('') -> ''
    162  *
    163  * @param {string} path The path to be parsed.
    164  * @return {string} The base name.
    165  */
    166 PathUtil.basename = function(path) {
    167   var index = path.lastIndexOf('/');
    168   return index >= 0 ? path.substring(index + 1) : path;
    169 };
    170 
    171 /**
    172  * Join path components into a single path. Can be called either with a list of
    173  * components as arguments, or with an array of components as the only argument.
    174  *
    175  * Examples:
    176  * Path.join('abc', 'def') -> 'abc/def'
    177  * Path.join('/', 'abc', 'def/ghi') -> '/abc/def/ghi'
    178  * Path.join(['/abc/def', 'ghi']) -> '/abc/def/ghi'
    179  *
    180  * @return {string} Resulting path.
    181  */
    182 PathUtil.join = function() {
    183   var components;
    184 
    185   if (arguments.length === 1 && typeof(arguments[0]) === 'object') {
    186     components = arguments[0];
    187   } else {
    188     components = arguments;
    189   }
    190 
    191   var path = '';
    192   for (var i = 0; i < components.length; i++) {
    193     if (components[i][0] === '/') {
    194       path = components[i];
    195       continue;
    196     }
    197     if (path.length === 0 || path[path.length - 1] !== '/')
    198       path += '/';
    199     path += components[i];
    200   }
    201   return path;
    202 };
    203 
    204 /**
    205  * @param {string} path Path starting with '/'.
    206  * @return {RootType} RootType.DOWNLOADS, RootType.DRIVE etc.
    207  */
    208 PathUtil.getRootType = function(path) {
    209   var rootDir = PathUtil.getTopDirectory(path);
    210   for (var type in RootDirectory) {
    211     if (rootDir === RootDirectory[type])
    212       return RootType[type];
    213   }
    214 };
    215 
    216 /**
    217  * @param {string} path Any path.
    218  * @return {string} The root path.
    219  */
    220 PathUtil.getRootPath = function(path) {
    221   var type = PathUtil.getRootType(path);
    222 
    223   if (type == RootType.DOWNLOADS || type == RootType.DRIVE_OFFLINE ||
    224       type == RootType.DRIVE_SHARED_WITH_ME || type == RootType.DRIVE_RECENT)
    225     return PathUtil.getTopDirectory(path);
    226 
    227   if (type == RootType.DRIVE || type == RootType.ARCHIVE ||
    228       type == RootType.REMOVABLE) {
    229     var components = PathUtil.split(path);
    230     if (components.length > 1) {
    231       return PathUtil.join(components[0], components[1]);
    232     } else {
    233       return components[0];
    234     }
    235   }
    236 
    237   return '/';
    238 };
    239 
    240 /**
    241  * @param {string} path A path.
    242  * @return {boolean} True if it is a path to the root.
    243  */
    244 PathUtil.isRootPath = function(path) {
    245   return PathUtil.getRootPath(path) === path;
    246 };
    247 
    248 /**
    249  * @param {string} path A root path.
    250  * @return {boolean} True if the given path is root and user can unmount it.
    251  */
    252 PathUtil.isUnmountableByUser = function(path) {
    253   if (!PathUtil.isRootPath(path))
    254     return false;
    255 
    256   var type = PathUtil.getRootType(path);
    257   return (type == RootType.ARCHIVE || type == RootType.REMOVABLE);
    258 };
    259 
    260 /**
    261  * @param {string} parent_path The parent path.
    262  * @param {string} child_path The child path.
    263  * @return {boolean} True if |parent_path| is parent file path of |child_path|.
    264  */
    265 PathUtil.isParentPath = function(parent_path, child_path) {
    266   if (!parent_path || parent_path.length == 0 ||
    267       !child_path || child_path.length == 0)
    268     return false;
    269 
    270   if (parent_path[parent_path.length - 1] != '/')
    271     parent_path += '/';
    272 
    273   if (child_path[child_path.length - 1] != '/')
    274     child_path += '/';
    275 
    276   return child_path.indexOf(parent_path) == 0;
    277 };
    278 
    279 /**
    280  * Return the localized name for the root.
    281  * @param {string} path The full path of the root (starting with slash).
    282  * @return {string} The localized name.
    283  */
    284 PathUtil.getRootLabel = function(path) {
    285   var str = function(id) {
    286     return loadTimeData.getString(id);
    287   };
    288 
    289   if (path === RootDirectory.DOWNLOADS)
    290     return str('DOWNLOADS_DIRECTORY_LABEL');
    291 
    292   if (path === RootDirectory.ARCHIVE)
    293     return str('ARCHIVE_DIRECTORY_LABEL');
    294   if (PathUtil.isParentPath(RootDirectory.ARCHIVE, path))
    295     return path.substring(RootDirectory.ARCHIVE.length + 1);
    296 
    297   if (path === RootDirectory.REMOVABLE)
    298     return str('REMOVABLE_DIRECTORY_LABEL');
    299   if (PathUtil.isParentPath(RootDirectory.REMOVABLE, path))
    300     return path.substring(RootDirectory.REMOVABLE.length + 1);
    301 
    302   // TODO(haruki): Add support for "drive/root" and "drive/other".
    303   if (path === RootDirectory.DRIVE + '/' + DriveSubRootDirectory.ROOT)
    304     return str('DRIVE_DIRECTORY_LABEL');
    305 
    306   if (path === RootDirectory.DRIVE_OFFLINE)
    307     return str('DRIVE_OFFLINE_COLLECTION_LABEL');
    308 
    309   if (path === RootDirectory.DRIVE_SHARED_WITH_ME)
    310     return str('DRIVE_SHARED_WITH_ME_COLLECTION_LABEL');
    311 
    312   if (path === RootDirectory.DRIVE_RECENT)
    313     return str('DRIVE_RECENT_COLLECTION_LABEL');
    314 
    315   return path;
    316 };
    317 
    318 /**
    319  * Return the label of the folder to be shown. Eg.
    320  *  - '/foo/bar/baz' -> 'baz'
    321  *  - '/hoge/fuga/ -> 'fuga'
    322  * If the directory is root, returns the root label, which is same as
    323  * PathUtil.getRootLabel().
    324  *
    325  * @param {string} directoryPath The full path of the folder.
    326  * @return {string} The label to be shown.
    327  */
    328 PathUtil.getFolderLabel = function(directoryPath) {
    329   var label = '';
    330   if (PathUtil.isRootPath(directoryPath))
    331     label = PathUtil.getRootLabel(directoryPath);
    332 
    333   if (label && label != directoryPath)
    334     return label;
    335 
    336   var matches = directoryPath.match(/([^\/]*)[\/]?$/);
    337   if (matches[1])
    338     return matches[1];
    339 
    340   return directoryPath;
    341 };
    342 
    343 /**
    344  * Returns if the given path can be a target path of folder shortcut.
    345  *
    346  * @param {string} directoryPath Diretcoty path to be checked.
    347  * @return {boolean} True if the path can be a target path of the shortcut.
    348  */
    349 PathUtil.isEligibleForFolderShortcut = function(directoryPath) {
    350   return !PathUtil.isSpecialSearchRoot(directoryPath) &&
    351          !PathUtil.isRootPath(directoryPath) &&
    352          PathUtil.isDriveBasedPath(directoryPath);
    353 };
    354