Ajout police Sawaasdee, rename js to src
This commit is contained in:
29
src/util/cache.js
Normal file
29
src/util/cache.js
Normal file
@@ -0,0 +1,29 @@
|
||||
var crypto = require('crypto');
|
||||
|
||||
function Cache() {
|
||||
this._store = {};
|
||||
}
|
||||
|
||||
Cache.prototype.get = function(key) {
|
||||
key = this._serialize(key);
|
||||
return key in this._store ? this._store[key] : undefined;
|
||||
};
|
||||
|
||||
Cache.prototype.set = function(key, value) {
|
||||
key = this._serialize(key);
|
||||
this._store[key] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
Cache.prototype._serialize = function(mixedKey) {
|
||||
var json = JSON.stringify(mixedKey);
|
||||
return this._hash(json);
|
||||
};
|
||||
|
||||
Cache.prototype._hash = function(str) {
|
||||
var shasum = crypto.createHash('md5');
|
||||
shasum.update(str);
|
||||
return shasum.digest('hex');
|
||||
};
|
||||
|
||||
module.exports = Cache;
|
2
src/util/const.js
Normal file
2
src/util/const.js
Normal file
@@ -0,0 +1,2 @@
|
||||
exports.EDIT_MODE = 'edit';
|
||||
exports.LAUNCHER_MODE = 'launcher';
|
7
src/util/debug.js
Normal file
7
src/util/debug.js
Normal file
@@ -0,0 +1,7 @@
|
||||
var debug = require('debug');
|
||||
var util = require('util');
|
||||
|
||||
module.exports = function createLogger(namespace) {
|
||||
var logger = debug('pitaya:'+namespace);
|
||||
return logger;
|
||||
};
|
297
src/util/desktop-apps.js
Normal file
297
src/util/desktop-apps.js
Normal file
@@ -0,0 +1,297 @@
|
||||
var path = require('path');
|
||||
var System = require('./system');
|
||||
var debug = require('./debug')('desktop-apps');
|
||||
var Cache = require('./cache');
|
||||
var promises = require('./promises');
|
||||
|
||||
// Constants
|
||||
var ICON_REALPATH_REGEX = /\..+$/;
|
||||
var ICON_THEMES_ROOTDIR = '/usr/share/icons';
|
||||
var PIXMAPS_ICONS_ROOTDIR = '/usr/share/pixmaps';
|
||||
|
||||
/**
|
||||
* Find and load all the desktop files in the subdirectories of given dirs
|
||||
*
|
||||
* @param Array[String] rootDirs
|
||||
* @return Promise
|
||||
*/
|
||||
exports.loadAllDesktopFiles = function(rootDirs) {
|
||||
|
||||
return exports.findAllDesktopFiles(rootDirs)
|
||||
.then(function(filePaths) {
|
||||
|
||||
return promises.seq(filePaths, function(path) {
|
||||
return exports.loadDesktopFile(path);
|
||||
})
|
||||
.then(function(contents) {
|
||||
return contents.map(function(content, i) {
|
||||
return { content: content, path: filePaths[i] };
|
||||
});
|
||||
})
|
||||
;
|
||||
|
||||
})
|
||||
;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Find all the desktop files in the subdirectories of given dirs
|
||||
*
|
||||
* @param Array[String] baseDirs
|
||||
* @return Promise
|
||||
*/
|
||||
exports.findAllDesktopFiles = function(baseDirs) {
|
||||
|
||||
if(!Array.isArray(baseDirs)) {
|
||||
baseDirs = [baseDirs];
|
||||
}
|
||||
|
||||
return promises.seq(baseDirs, function(baseDir) {
|
||||
return System.findFiles('**/*.desktop', {cwd: baseDir, realpath: true});
|
||||
})
|
||||
.then(function(apps) {
|
||||
return uniq(flatten(apps));
|
||||
})
|
||||
;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a .desktop file ans return its parsed content
|
||||
*
|
||||
* @param string filePath
|
||||
* @return Promise
|
||||
*/
|
||||
exports.loadDesktopFile = function(filePath) {
|
||||
return System.loadINIFile(filePath);
|
||||
};
|
||||
|
||||
var iconCache = new Cache();
|
||||
|
||||
/**
|
||||
* Find the absolute path of a desktop icon
|
||||
*
|
||||
* @param string iconPath
|
||||
* @return Promise
|
||||
*/
|
||||
exports.findIcon = function(iconName, themeName, size, themeIgnore) {
|
||||
|
||||
var cachedIcon = iconCache.get([iconName, themeName, size]);
|
||||
|
||||
if(cachedIcon) {
|
||||
debug('Icon %s:%s:%s found in cache !', iconName, themeName, size);
|
||||
return Promise.resolve(cachedIcon);
|
||||
}
|
||||
|
||||
themeIgnore = themeIgnore || [];
|
||||
if(themeIgnore.indexOf(themeIgnore) !== -1) {
|
||||
debug('Theme %s already processed, ignoring...', themeName);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
themeIgnore.push(themeName);
|
||||
|
||||
debug('Finding icon %s:%s:%s...', iconName, themeName, size);
|
||||
|
||||
if( ICON_REALPATH_REGEX.test(iconName) ) {
|
||||
return Promise.resolve(iconName);
|
||||
}
|
||||
|
||||
if(!themeName) {
|
||||
return exports.findIconThemes()
|
||||
.then(function(themes) {
|
||||
themeIgnore = themeIgnore || [];
|
||||
return promises.seq(themes, function(theme) {
|
||||
return exports.findIcon(iconName, theme, size, themeIgnore);
|
||||
})
|
||||
.then(exports._selectBestIcon)
|
||||
;
|
||||
})
|
||||
.then(_cacheIcon)
|
||||
;
|
||||
}
|
||||
|
||||
return exports.findClosestSizeIcon(iconName, themeName, size)
|
||||
.then(function(foundIcon) {
|
||||
|
||||
if(foundIcon) return foundIcon;
|
||||
|
||||
debug('No icon found. Search in parents...');
|
||||
|
||||
return exports.findParentsThemeIcon(iconName, themeName, size, themeIgnore)
|
||||
.then(function(iconPath) {
|
||||
|
||||
if(iconPath) return iconPath;
|
||||
|
||||
return exports.findPixmapsIcon(iconName);
|
||||
|
||||
})
|
||||
;
|
||||
|
||||
})
|
||||
.then(_cacheIcon)
|
||||
;
|
||||
|
||||
|
||||
function _cacheIcon(iconPath) {
|
||||
iconCache.set([iconName, themeName, size], iconPath);
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports.findParentsThemeIcon = function(iconName, themeName, size, themeIgnore) {
|
||||
|
||||
return exports.themeIndexExists(themeName)
|
||||
.then(function(exists) {
|
||||
|
||||
if(!exists) return null;
|
||||
|
||||
return exports.loadThemeIndex(themeName)
|
||||
.then(function(themeIndex) {
|
||||
|
||||
if(!themeIndex || !themeIndex['Icon Theme'].Inherits) return;
|
||||
|
||||
var parents = themeIndex['Icon Theme'].Inherits.split(',');
|
||||
|
||||
debug('Found parents %j', parents);
|
||||
|
||||
return promises.seq(parents, function(themeName) {
|
||||
return exports.findIcon(iconName, themeName, size, themeIgnore);
|
||||
})
|
||||
.then(exports._selectBestIcon)
|
||||
;
|
||||
|
||||
})
|
||||
;
|
||||
|
||||
})
|
||||
;
|
||||
|
||||
};
|
||||
|
||||
exports.findClosestSizeIcon = function(iconName, themeName, size) {
|
||||
|
||||
var themePath = path.join(ICON_THEMES_ROOTDIR, themeName);
|
||||
|
||||
var extPattern = '{svg,png}';
|
||||
var filePattern = themeName+'/*/*/'+iconName+'.'+extPattern;
|
||||
|
||||
debug('File pattern %s', filePattern);
|
||||
|
||||
return System.findFiles(filePattern, {cwd: ICON_THEMES_ROOTDIR})
|
||||
.then(function(iconFiles) {
|
||||
|
||||
debug('Found files %j', iconFiles);
|
||||
|
||||
var scalableIcon = iconFiles.reduce(function(scalableIcon, iconPath) {
|
||||
if(iconPath.indexOf('scalable') !== -1) {
|
||||
debug('Found scalable icon %s', iconPath);
|
||||
scalableIcon = iconPath;
|
||||
}
|
||||
return scalableIcon;
|
||||
}, null);
|
||||
|
||||
if(scalableIcon) return scalableIcon;
|
||||
|
||||
if(!size) {
|
||||
size = Math.max.apply(Math, clean(iconFiles.map(sizeFromPath)));
|
||||
}
|
||||
|
||||
var closestIcon = iconFiles.reduce(function(foundIcon, iconPath) {
|
||||
var foundSize = sizeFromPath(iconPath);
|
||||
if( foundSize && Math.abs(foundSize - size) < Math.abs(foundIcon.size - size) ) {
|
||||
foundIcon.path = iconPath;
|
||||
foundIcon.size = foundSize;
|
||||
}
|
||||
return foundIcon;
|
||||
}, {path: null, size: null});
|
||||
|
||||
return closestIcon.path;
|
||||
|
||||
})
|
||||
.then(function(iconPath) {
|
||||
debug('Closest icon %j', iconPath);
|
||||
return iconPath ? path.join(ICON_THEMES_ROOTDIR, iconPath) : null;
|
||||
})
|
||||
;
|
||||
|
||||
function sizeFromPath(iconPath) {
|
||||
var simpleSizeRegex = /\/(\d+)\//;
|
||||
var matches = simpleSizeRegex.exec(iconPath);
|
||||
if(matches && matches[1]) return +matches[1];
|
||||
var doubleSizeRegex = /\/(\d+)x\d+\//;
|
||||
matches = doubleSizeRegex.exec(iconPath);
|
||||
if(matches && matches[1]) return +matches[1];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports.findPixmapsIcon = function(iconName) {
|
||||
var filePattern = iconName+'.{svg,png}';
|
||||
debug('Looking for pixmap icon %s', filePattern);
|
||||
return System.findFiles(filePattern, {cwd: PIXMAPS_ICONS_ROOTDIR})
|
||||
.then(function(iconPaths) {
|
||||
iconPaths = iconPaths.map(function(iconPath) {
|
||||
return path.join(PIXMAPS_ICONS_ROOTDIR, iconPath);
|
||||
});
|
||||
return exports._selectBestIcon(iconPaths);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.findIconThemes = function() {
|
||||
return System.findFiles('*/', {cwd: ICON_THEMES_ROOTDIR, realpath: true})
|
||||
.then(function(files) {
|
||||
return files.map(function(f) {
|
||||
return path.basename(f);
|
||||
});
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.loadThemeIndex = function(themeName) {
|
||||
var themeIndexPath = path.join(ICON_THEMES_ROOTDIR, themeName, 'index.theme');
|
||||
return System.loadINIFile(themeIndexPath);
|
||||
};
|
||||
|
||||
exports.themeIndexExists = function(themeName) {
|
||||
var themeIndexPath = path.join(ICON_THEMES_ROOTDIR, themeName, 'index.theme');
|
||||
return System.exists(themeIndexPath);
|
||||
};
|
||||
|
||||
exports._selectBestIcon = function(iconPaths) {
|
||||
var iconSelection = iconPaths.reduce(function(iconSelection, iconPath) {
|
||||
if(iconPath) {
|
||||
var key = iconPath.indexOf('scalable') !== -1 ? 'scalable' : 'bitmap';
|
||||
iconSelection[key] = iconPath;
|
||||
}
|
||||
return iconSelection;
|
||||
}, {scalable: null, bitmap: null});
|
||||
debug('Icon selection %j', iconSelection);
|
||||
return iconSelection.scalable || iconSelection.bitmap;
|
||||
};
|
||||
|
||||
// Array helpers
|
||||
|
||||
function clean(arr) {
|
||||
return arr.filter(function(item) {
|
||||
return item !== null && item !== undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function flatten(arr) {
|
||||
return arr.reduce(function(result, item) {
|
||||
result = result.concat.apply(result, Array.isArray(item) ? flatten(item) : [item]);
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function uniq(arr) {
|
||||
return arr.reduce(function(result, item) {
|
||||
if(result.indexOf(item) === -1) {
|
||||
result.push(item);
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
}
|
7
src/util/index.js
Normal file
7
src/util/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
exports.System = require('./system');
|
||||
exports.DesktopApps = require('./desktop-apps');
|
||||
exports.Cache = require('./cache');
|
||||
exports.Debug = require('./debug');
|
||||
exports.Tree = require('./tree');
|
||||
exports.Const = require('./const');
|
||||
exports.Promises = require('./promises');
|
31
src/util/promises.js
Normal file
31
src/util/promises.js
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
exports.seq = function(items, generator) {
|
||||
|
||||
var results = [];
|
||||
var p = Promise.resolve();
|
||||
|
||||
for(var i = 0, len = items.length; i < len; ++i) {
|
||||
p = p.then(generateNextHandler(items[i], i === 0))
|
||||
}
|
||||
|
||||
return p.then(function(lastResult) {
|
||||
results.push(lastResult);
|
||||
return results;
|
||||
});
|
||||
|
||||
// Internal helper
|
||||
function generateNextHandler(item, ignoreResult) {
|
||||
return function(prevResult) {
|
||||
if(!ignoreResult) results.push(prevResult);
|
||||
return generator(item, prevResult);
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports.delay = function(delay) {
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(resolve, delay);
|
||||
});
|
||||
};
|
103
src/util/system.js
Normal file
103
src/util/system.js
Normal file
@@ -0,0 +1,103 @@
|
||||
var fs = require('fs');
|
||||
var cp = require('child_process');
|
||||
var glob = require('glob');
|
||||
var ini = require('ini');
|
||||
var Cache = require('./cache');
|
||||
|
||||
/**
|
||||
* Load a JSON file
|
||||
*
|
||||
* @param filePath The path of the json file
|
||||
* @return Promise
|
||||
*/
|
||||
exports.loadJSON = function(filePath) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fs.readFile(filePath, 'utf8', function(err, fileContent) {
|
||||
if(err) return reject(err);
|
||||
try {
|
||||
var json = JSON.parse(fileContent);
|
||||
return resolve(json);
|
||||
} catch(err) {
|
||||
return reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.saveJSON = function(filePath, obj) {
|
||||
|
||||
var jsonStr = JSON.stringify(obj, null, 2);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
fs.writeFile(filePath, jsonStr, function(err) {
|
||||
if(err) return reject(err);
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a INI file
|
||||
*
|
||||
* @param filePath The path of the json file
|
||||
* @return Promise
|
||||
*/
|
||||
exports.loadINIFile = function(filePath) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fs.readFile(filePath, 'utf8', function(err, content) {
|
||||
if(err) return reject(err);
|
||||
try {
|
||||
var decoded = ini.decode(content);
|
||||
return resolve(decoded);
|
||||
} catch(err) {
|
||||
return reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.clearFreeDesktopFlags = function(exec) {
|
||||
return exec.replace(/%[uUdDfFnNickvm]/g, '');
|
||||
};
|
||||
|
||||
exports.runApp = function(execPath, opts) {
|
||||
|
||||
opts = opts || {};
|
||||
|
||||
if(opts.clearFreeDesktopFlags) {
|
||||
execPath = exports.clearFreeDesktopFlags(execPath);
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
cp.exec(execPath, function(err) {
|
||||
if(err) return reject(err);
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var _searchCache = new Cache();
|
||||
|
||||
exports.findFiles = function(pattern, opts) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
var cachedResult = _searchCache.get([pattern, opts]);
|
||||
if( cachedResult !== undefined) {
|
||||
return resolve(cachedResult);
|
||||
}
|
||||
|
||||
glob(pattern, opts, function(err, files) {
|
||||
if(err) return reject(err);
|
||||
_searchCache.set([pattern, opts], files);
|
||||
return resolve(files);
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
exports.exists = function(filePath) {
|
||||
return new Promise(function(resolve) {
|
||||
fs.exists(filePath, resolve);
|
||||
});
|
||||
};
|
53
src/util/tree.js
Normal file
53
src/util/tree.js
Normal file
@@ -0,0 +1,53 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
// Tree manipulation helpers
|
||||
|
||||
exports.walk = function(branch, func, parent) {
|
||||
|
||||
if(!branch) return;
|
||||
|
||||
var breakHere = func(branch, parent);
|
||||
|
||||
if(breakHere) return breakHere;
|
||||
|
||||
var items = branch.items;
|
||||
|
||||
if(!items) return;
|
||||
|
||||
for( var i = 0, item = items[i]; (item = items[i]); i++ ) {
|
||||
breakHere = exports.walk(item, func, branch);
|
||||
if(breakHere) return breakHere;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports.find = function(tree, obj) {
|
||||
|
||||
var result;
|
||||
|
||||
exports.walk(tree, function(item, parent) {
|
||||
if( _.isEqual(item, obj) ) {
|
||||
result = {item: item, parent: parent};
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
};
|
||||
|
||||
exports.matches = function(tree, obj) {
|
||||
|
||||
var results = [];
|
||||
|
||||
var matches = _.matches(obj);
|
||||
|
||||
exports.walk(tree, function(item) {
|
||||
if( matches(item) ) {
|
||||
results.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
|
||||
};
|
Reference in New Issue
Block a user