node-gtk-cc
Version:
GNOME Gtk+ camelCase bindings for NodeJS
255 lines (218 loc) • 8.04 kB
JavaScript
var gi;
try {
gi = require('../build/Release/node-gtk');
} catch(e) {
gi = require('../build/Debug/node-gtk');
}
// The bootstrap from C here contains functions and methods for each object,
// namespaced with underscores. See gi.cc for more information.
var GIRepository = gi.Bootstrap();
// The GIRepository API is fairly poor, and contains methods on classes,
// methods on objects, and what should be methods interpreted as functions,
// because the scanner does not interpret methods on typedefs correctly.
// We extend this bootstrap'd repo to define all flags / enums, which
// are all we need to start declaring objects.
(function() {
var repo = GIRepository.Repository_get_default();
var ns = "GIRepository";
// First, grab InfoType so we can find enums / flags.
var InfoType = makeEnum(GIRepository.Repository_find_by_name.call(repo, ns, "InfoType"));
// Now, define all enums / flags.
var nInfos = GIRepository.Repository_get_n_infos.call(repo, ns);
for (var i = 0; i < nInfos; i++) {
var info = GIRepository.Repository_get_info.call(repo, ns, i);
var name = GIRepository.BaseInfo_get_name.call(info);
var type = GIRepository.BaseInfo_get_type.call(info);
if (type === InfoType.ENUM || type === InfoType.FLAGS)
GIRepository[name] = makeEnum(info);
}
})();
function declareFunction(obj, info) {
var name = GIRepository.BaseInfo_get_name.call(info);
var flags = GIRepository.function_info_get_flags(info);
var func = gi.MakeFunction(info);
var target = flags & GIRepository.FunctionInfoFlags.IS_METHOD ? obj.prototype : obj;
Object.defineProperty(target, toCamelCase(name), {
configurable: true,
writable: true,
value: func
});
}
function makeEnum(info) {
var obj = {};
var nValues = GIRepository.enum_info_get_n_values(info);
for (var i = 0; i < nValues; i++) {
var valueInfo = GIRepository.enum_info_get_value(info, i);
var valueName = GIRepository.BaseInfo_get_name.call(valueInfo);
var valueValue = GIRepository.value_info_get_value(valueInfo);
obj[valueName.toUpperCase()] = valueValue;
}
return obj;
}
function makeConstant(info) {
return gi.GetConstantValue(info);
}
function makeFunction(info) {
return gi.MakeFunction(info);
}
function makeObject(info) {
function propertyGetter(propertyName) {
return function() {
return gi.ObjectPropertyGetter(this, propertyName);
};
}
function propertySetter(propertyName) {
return function(value) {
return gi.ObjectPropertySetter(this, propertyName, value);
};
}
var constructor = gi.MakeClass(info);
var nMethods = GIRepository.object_info_get_n_methods(info);
for (var i = 0; i < nMethods; i++) {
var methodInfo = GIRepository.object_info_get_method(info, i);
declareFunction(constructor, methodInfo);
}
var nProperties = GIRepository.object_info_get_n_properties(info);
for (var i = 0; i < nProperties; i++) {
var propertyInfo = GIRepository.object_info_get_property(info, i);
var propertyName = GIRepository.BaseInfo_get_name.call(propertyInfo);
Object.defineProperty(constructor.prototype, toCamelCase(propertyName), {
configurable: true,
get: propertyGetter(propertyName),
set: propertySetter(propertyName)
}
);
}
return constructor;
}
function makeInfo(info) {
var type = GIRepository.BaseInfo_get_type.call(info);
if (type === GIRepository.InfoType.ENUM)
return makeEnum(info);
if (type === GIRepository.InfoType.CONSTANT)
return makeConstant(info);
if (type === GIRepository.InfoType.FUNCTION)
return makeFunction(info);
if (type === GIRepository.InfoType.OBJECT)
return makeObject(info);
}
function importNS(ns, version) {
var module = {};
var repo = GIRepository.Repository_get_default();
GIRepository.Repository_require.call(repo, ns, version || null, 0);
var nInfos = GIRepository.Repository_get_n_infos.call(repo, ns);
for (var i = 0; i < nInfos; i++) {
var info = GIRepository.Repository_get_info.call(repo, ns, i);
var name = GIRepository.BaseInfo_get_name.call(info);
// beside constants, everything should be camel Case
if (!/^[A-Z_]+$/.test(name)) name = toCamelCase(name);
module[name] = makeInfo(info);
// in case the export is a constructor
if (typeof module[name] === 'function' && /^[A-Z]/.test(name)) {
// export its camelCase aware counterpart
module[name] = wrapConstructor(module[name]);
}
}
var override;
try {
override = require('./overrides/' + ns);
} catch (e) {
// No override
}
if (override)
override.apply(module);
return module;
}
function toCamelCase(key) {
return key.replace(/[_-]([a-z])/g, function ($0, $1) {
return $1.toUpperCase();
});
}
function deCamelCase(key) {
return key.replace(/([a-z])([A-Z]+)/g, function ($0, $1, $2) {
return $1 + '_' + $2.toLowerCase();
});
}
// It makes it possible to create instances directly
// witohut using the `new` keyword.
// e.g. factory[3].apply(Constructor, [1, 2, 3]);
// Returns the result of `new Constructor(1, 2, 3)`
var factory = createFactory(10);
function createFactory(maxAmountOfArguments) {
for (var
length = maxAmountOfArguments + 1,
factory = [],
args = function (length) {
var out = [];
for (var i = 0; i < length; i++) {
out[i] = 'arguments[' + i + ']';
}
return out.join(',');
},
i = 0; i < length; i++
) {
factory[i] = Function('return new this(' + args(i) + ')');
}
return factory;
}
// Returns true if an instanceof Object is
// actually just an Object
var isObject = (function (toString) {
var reference = toString.call({});
return function isObject(object) {
return toString.call(object) === reference;
};
}(Object.prototype.toString));
function withCamelCaseProperties(obj) {
var out = {};
for (var key in obj)
out[deCamelCase(key)] = obj[key];
return out;
}
function wrapConstructor(Constructor) {
function NodeGtkClass() {
for (var
length = arguments.length,
args = [],
i = 0; i < length; i++
) {
args[i] = withCamelCaseProperties(arguments[i]);
}
return factory[length].apply(Constructor, args);
}
NodeGtkClass.prototype = Constructor.prototype;
Object.defineProperties(
NodeGtkClass,
Object.getOwnPropertyNames(Constructor).reduce(function (d, key) {
d[key] = Object.getOwnPropertyDescriptor(Constructor, key);
return d;
}, {})
);
return NodeGtkClass;
}
// Bootstrap step.
(function() {
var repo = GIRepository.Repository_get_default();
var ns = "GIRepository";
// First, grab InfoType.
var InfoType = makeEnum(GIRepository.Repository_find_by_name.call(repo, ns, "InfoType"));
// Now, define all enums / flags.
var nInfos = GIRepository.Repository_get_n_infos.call(repo, ns);
for (var i = 0; i < nInfos; i++) {
var info = GIRepository.Repository_get_info.call(repo, ns, i);
var name = GIRepository.BaseInfo_get_name.call(info);
var type = GIRepository.BaseInfo_get_type.call(info);
if (type === InfoType.ENUM || type === InfoType.FLAGS)
GIRepository[name] = makeEnum(info);
}
})();
// Used to avoid exporting same module every time it's required
var cache = Object.create(null);
exports.importNS = function(ns, version) {
var module = cache[ns] || (cache[ns] = {});
var ver = version || '*';
return module[ver] || (module[ver] = importNS(ns, version));
};
exports.startLoop = function() {
gi.StartLoop();
};