Commit 07a9540a by Matthew Ryo

Support for custom functions

parent 8c641f4c
......@@ -17,9 +17,9 @@ matrix:
before_install:
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update
- sudo apt-get install gcc-4.8 g++-4.8
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 20
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 20
- sudo apt-get install gcc-4.7 g++-4.7
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.7 20
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.7 20
- g++ --version
- sudo apt-get update -qq
- git submodule update --init --recursive
......
......@@ -89,6 +89,68 @@ When returning or calling `done()` with `{ contents: "String" }`, the string val
`this` refers to a contextual scope for the immediate run of `sass.render` or `sass.renderSync`
### functions
`functions` is an `Object` that holds a collection of custom functions that may be invoked by the sass files being compiled. They may take zero or more input parameters and must return a value either synchronously (`return ...;`) or asynchronously (`done();`). Those parameters will be instances of one of the constructors contained in the `require('node-sass').types` hash. The return value must be of one of these types as well. See the list of available types below:
#### types.Number(value [, unit = ""])
* `getValue()`/ `setValue(value)` : gets / sets the numerical portion of the number
* `getUnit()` / `setUnit(unit)` : gets / sets the unit portion of the number
#### types.String(value)
* `getValue()` / `setValue(value)` : gets / sets the enclosed string
#### types.Color(r, g, b [, a = 1.0]) or types.Color(argb)
* `getR()` / `setR(value)` : red component (integer from `0` to `255`)
* `getG()` / `setG(value)` : green component (integer from `0` to `255`)
* `getB()` / `setB(value)` : blue component (integer from `0` to `255`)
* `getA()` / `setA(value)` : alpha component (number from `0` to `1.0`)
Example:
```javascript
var Color = require('node-sass').types.Color,
c1 = new Color(255, 0, 0),
c2 = new Color(0xff0088cc);
```
#### types.Boolean(value)
* `getValue()` : gets the enclosed boolean
* `types.Boolean.TRUE` : Singleton instance of `types.Boolean` that holds "true"
* `types.Boolean.FALSE` : Singleton instance of `types.Boolean` that holds "false"
#### types.List(length [, commaSeparator = true])
* `getValue(index)` / `setValue(index, value)` : `value` must itself be an instance of one of the constructors in `sass.types`.
* `getSeparator()` / `setSeparator(isComma)` : whether to use commas as a separator
* `getLength()`
#### types.Map(length)
* `getKey(index)` / `setKey(index, value)`
* `getValue(index)` / `setValue(index, value)`
* `getLength()`
#### types.Null()
* `types.Null.NULL` : Singleton instance of `types.Null`.
#### Example
```javascript
sass.renderSync({
data: '#{headings(2,5)} { color: #08c; }',
functions: {
'headings($from: 0, $to: 6)': function(from, to) {
var i, f = from.getValue(), t = to.getValue(),
list = new sass.types.List(t - f + 1);
for (i = f; i <= t; i++) {
list.setValue(i - f, new sass.types.String('h' + i));
}
return list;
}
}
});
```
### includePaths
Type: `Array<String>`
Default: `[]`
......
......@@ -4,7 +4,19 @@
'target_name': 'binding',
'sources': [
'src/binding.cpp',
'src/sass_context_wrapper.cpp'
'src/create_string.cpp',
'src/custom_function_bridge.cpp',
'src/custom_importer_bridge.cpp',
'src/sass_context_wrapper.cpp',
'src/sass_types/boolean.cpp',
'src/sass_types/color.cpp',
'src/sass_types/error.cpp',
'src/sass_types/factory.cpp',
'src/sass_types/list.cpp',
'src/sass_types/map.cpp',
'src/sass_types/null.cpp',
'src/sass_types/number.cpp',
'src/sass_types/string.cpp'
],
'include_dirs': [
'<!(node -e "require(\'nan\')")',
......@@ -47,9 +59,7 @@
'-std=c++11',
'-stdlib=libc++'
],
'OTHER_LDFLAGS': [
'-stdlib=libc++'
],
'OTHER_LDFLAGS': [],
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'GCC_ENABLE_CPP_RTTI': 'YES',
'MACOSX_DEPLOYMENT_TARGET': '10.7'
......@@ -73,6 +83,7 @@
}],
['OS!="win"', {
'cflags_cc+': [
'-fexceptions',
'-std=c++0x'
]
}]
......
......@@ -137,6 +137,67 @@ function getOptions(options, cb) {
}
/**
* Executes a callback and transforms any exception raised into a sass error
*
* @param {Function} callback
* @param {Array} arguments
* @api private
*/
function tryCallback(callback, args) {
try {
return callback.apply(this, args);
} catch (e) {
if (typeof e === 'string') {
return new binding.types.Error(e);
} else if (e instanceof Error) {
return new binding.types.Error(e.message);
} else {
return new binding.types.Error('An unexpected error occurred');
}
}
}
/**
* Normalizes the signature of custom functions to make it possible to just supply the
* function name and have the signature default to `fn(...)`. The callback is adjusted
* to transform the input sass list into discrete arguments.
*
* @param {String} signature
* @param {Function} callback
* @return {Object}
* @api private
*/
function normalizeFunctionSignature(signature, callback) {
if (!/^\*|@warn|@error|@debug|\w+\(.*\)$/.test(signature)) {
if (!/\w+/.test(signature)) {
throw new Error('Invalid function signature format "' + signature + '"');
}
return {
signature: signature + '(...)',
callback: function() {
var args = Array.prototype.slice.call(arguments),
list = args.shift(),
i;
for (i = list.getLength() - 1; i >= 0; i--) {
args.unshift(list.getValue(i));
}
return callback.apply(this, args);
}
};
}
return {
signature: signature,
callback: callback
};
}
/**
* Render
*
* @param {Object} options
......@@ -172,13 +233,9 @@ module.exports.render = function(options, cb) {
var importer = options.importer;
if (importer) {
options.importer = function(file, prev, key) {
options.importer = function(file, prev, bridge) {
function done(data) {
console.log(data); // ugly hack
binding.importedCallback({
index: key,
objectLiteral: data
});
bridge.success(data);
}
var result = importer.call(options.context, file, prev, done);
......@@ -189,6 +246,31 @@ module.exports.render = function(options, cb) {
};
}
var functions = options.functions;
if (functions) {
options.functions = {};
Object.keys(functions).forEach(function(signature) {
var cb = normalizeFunctionSignature(signature, functions[signature]);
options.functions[cb.signature] = function() {
var args = Array.prototype.slice.call(arguments),
bridge = args.pop();
function done(data) {
bridge.success(data);
}
var result = tryCallback(cb.callback, args.concat(done));
if (result) {
done(result);
}
};
});
}
options.data ? binding.render(options) : binding.renderFile(options);
};
......@@ -206,10 +288,24 @@ module.exports.renderSync = function(options) {
if (importer) {
options.importer = function(file, prev) {
return { objectLiteral: importer.call(options.context, file, prev) };
return importer.call(options.context, file, prev);
};
}
var functions = options.functions;
if (options.functions) {
options.functions = {};
Object.keys(functions).forEach(function(signature) {
var cb = normalizeFunctionSignature(signature, functions[signature]);
options.functions[cb.signature] = function() {
return tryCallback(cb.callback, arguments);
};
});
}
var status = options.data ? binding.renderSync(options) : binding.renderFileSync(options);
var result = options.result;
......@@ -228,3 +324,12 @@ module.exports.renderSync = function(options) {
*/
module.exports.info = process.sass.versionInfo;
/**
* Expose sass types
*/
module.exports.types = binding.types;
module.exports.TRUE = binding.types.Boolean.TRUE;
module.exports.FALSE = binding.types.Boolean.FALSE;
module.exports.NULL = binding.types.Null.NULL;
\ No newline at end of file
......@@ -61,9 +61,7 @@
'-std=c++11',
'-stdlib=libc++'
],
'OTHER_LDFLAGS': [
'-stdlib=libc++'
],
'OTHER_LDFLAGS': [],
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'GCC_ENABLE_CPP_RTTI': 'YES',
'MACOSX_DEPLOYMENT_TARGET': '10.7'
......
......@@ -26,7 +26,7 @@
"coverage": "node scripts/coverage.js",
"install": "node scripts/install.js",
"postinstall": "node scripts/build.js",
"pretest": "node_modules/.bin/jshint bin lib test",
"pretest": "node_modules/.bin/jshint bin lib scripts test",
"test": "node_modules/.bin/mocha test"
},
"files": [
......
......@@ -53,7 +53,7 @@ function afterBuild(options) {
*/
function build(options) {
var arguments = [
var args = [
path.join('node_modules', 'pangyp', 'bin', 'node-gyp'),
'rebuild',
].concat(
......@@ -63,9 +63,9 @@ function build(options) {
})
).concat(options.args);
console.log(['Building:', process.sass.runtime.execPath].concat(arguments).join(' '));
console.log(['Building:', process.sass.runtime.execPath].concat(args).join(' '));
var proc = spawn(process.sass.runtime.execPath, arguments, {
var proc = spawn(process.sass.runtime.execPath, args, {
stdio: [0, 1, 2]
});
......
#ifndef CALLBACK_BRIDGE_H
#define CALLBACK_BRIDGE_H
#include <vector>
#include <nan.h>
#include <condition_variable>
#include <algorithm>
#define COMMA ,
using namespace v8;
template <typename T, typename L = void*>
class CallbackBridge {
public:
CallbackBridge(NanCallback*, bool);
virtual ~CallbackBridge();
// Executes the callback
T operator()(std::vector<void*>);
protected:
// We will expose a bridge object to the JS callback that wraps this instance so we don't loose context.
// This is the V8 constructor for such objects.
static Handle<Function> get_wrapper_constructor();
static NAN_METHOD(New);
static NAN_METHOD(ReturnCallback);
static Persistent<Function> wrapper_constructor;
Persistent<Object> wrapper;
// The callback that will get called in the main thread after the worker thread used for the sass
// compilation step makes a call to uv_async_send()
static void dispatched_async_uv_callback(uv_async_t*);
// The V8 values sent to our ReturnCallback must be read on the main thread not the sass worker thread.
// This gives a chance to specialized subclasses to transform those values into whatever makes sense to
// sass before we resume the worker thread.
virtual T post_process_return_value(Handle<Value>) const =0;
virtual std::vector<Handle<Value>> pre_process_args(std::vector<L>) const =0;
NanCallback* callback;
bool is_sync;
std::mutex cv_mutex;
std::condition_variable condition_variable;
uv_async_t async;
std::vector<L> argv;
bool has_returned;
T return_value;
};
template <typename T, typename L>
Persistent<Function> CallbackBridge<T, L>::wrapper_constructor;
template <typename T, typename L>
CallbackBridge<T, L>::CallbackBridge(NanCallback* callback, bool is_sync) : callback(callback), is_sync(is_sync) {
// This assumes the main thread will be the one instantiating the bridge
if (!is_sync) {
uv_async_init(uv_default_loop(), &this->async, (uv_async_cb) dispatched_async_uv_callback);
this->async.data = (void*) this;
}
NanAssignPersistent(wrapper, NanNew(CallbackBridge<T, L>::get_wrapper_constructor())->NewInstance());
NanSetInternalFieldPointer(NanNew(wrapper), 0, this);
}
template <typename T, typename L>
CallbackBridge<T, L>::~CallbackBridge() {
delete this->callback;
NanDisposePersistent(this->wrapper);
if (!is_sync) {
uv_close((uv_handle_t*)&this->async, NULL);
}
}
template <typename T, typename L>
T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
// argv.push_back(wrapper);
if (this->is_sync) {
std::vector<Handle<Value>> argv_v8 = pre_process_args(argv);
argv_v8.push_back(NanNew(wrapper));
return this->post_process_return_value(
NanNew<Value>(this->callback->Call(argv_v8.size(), &argv_v8[0]))
);
}
this->argv = argv;
std::unique_lock<std::mutex> lock(this->cv_mutex);
this->has_returned = false;
uv_async_send(&this->async);
this->condition_variable.wait(lock, [this] { return this->has_returned; });
return this->return_value;
}
template <typename T, typename L>
void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) {
CallbackBridge* bridge = static_cast<CallbackBridge*>(req->data);
NanScope();
TryCatch try_catch;
std::vector<Handle<Value>> argv_v8 = bridge->pre_process_args(bridge->argv);
argv_v8.push_back(NanNew(bridge->wrapper));
NanNew<Value>(bridge->callback->Call(argv_v8.size(), &argv_v8[0]));
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
}
template <typename T, typename L>
NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
NanScope();
CallbackBridge<T, L>* bridge = static_cast<CallbackBridge<T, L>*>(NanGetInternalFieldPointer(args.This(), 0));
TryCatch try_catch;
bridge->return_value = bridge->post_process_return_value(args[0]);
{
std::lock_guard<std::mutex> lock(bridge->cv_mutex);
bridge->has_returned = true;
}
bridge->condition_variable.notify_all();
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
NanReturnUndefined();
}
template <typename T, typename L>
Handle<Function> CallbackBridge<T, L>::get_wrapper_constructor() {
if (wrapper_constructor.IsEmpty()) {
Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
tpl->SetClassName(NanNew("CallbackBridge"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->PrototypeTemplate()->Set(
NanNew("success"),
NanNew<FunctionTemplate>(ReturnCallback)->GetFunction()
);
NanAssignPersistent(wrapper_constructor, tpl->GetFunction());
}
return NanNew(wrapper_constructor);
}
template <typename T, typename L>
NAN_METHOD(CallbackBridge<T COMMA L>::New) {
NanScope();
NanReturnValue(args.This());
}
#endif
#include <nan.h>
#include <string.h>
#include "create_string.h"
char* create_string(Local<Value> value) {
if (value->IsNull() || !value->IsString()) {
return 0;
}
String::Utf8Value string(value);
char *str = (char *)malloc(string.length() + 1);
strcpy(str, *string);
return str;
}
#ifndef CREATE_STRING_H
#define CREATE_STRING_H
#include <nan.h>
using namespace v8;
char* create_string(Local<Value>);
#endif
\ No newline at end of file
#include <nan.h>
#include "custom_function_bridge.h"
#include <sass_context.h>
#include "sass_types/factory.h"
Sass_Value* CustomFunctionBridge::post_process_return_value(Handle<Value> val) const {
try {
return SassTypes::Factory::unwrap(val)->get_sass_value();
} catch (const std::invalid_argument& e) {
return sass_make_error(e.what());
}
}
std::vector<Handle<Value>> CustomFunctionBridge::pre_process_args(std::vector<void*> in) const {
std::vector<Handle<Value>> argv = std::vector<Handle<Value>>();
for (void* value : in) {
argv.push_back(
SassTypes::Factory::create(static_cast<Sass_Value*>(value))->get_js_object()
);
}
return argv;
}
#ifndef CUSTOM_FUNCTION_BRIDGE_H
#define CUSTOM_FUNCTION_BRIDGE_H
#include <nan.h>
#include "callback_bridge.h"
#include <sass_context.h>
using namespace v8;
class CustomFunctionBridge : public CallbackBridge<Sass_Value*> {
public:
CustomFunctionBridge(NanCallback* cb, bool is_sync) : CallbackBridge<Sass_Value*>(cb, is_sync) {}
private:
Sass_Value* post_process_return_value(Handle<Value>) const;
std::vector<Handle<Value>> pre_process_args(std::vector<void*>) const;
};
#endif
#include <nan.h>
#include <sass_context.h>
#include "custom_importer_bridge.h"
#include "create_string.h"
SassImportList CustomImporterBridge::post_process_return_value(Handle<Value> val) const {
SassImportList imports;
NanScope();
Local<Value> returned_value = NanNew(val);
if (returned_value->IsArray()) {
Handle<Array> array = Handle<Array>::Cast(returned_value);
imports = sass_make_import_list(array->Length());
for (size_t i = 0; i < array->Length(); ++i) {
Local<Value> value = array->Get(static_cast<uint32_t>(i));
if (!value->IsObject())
continue;
Local<Object> object = Local<Object>::Cast(value);
char* path = create_string(object->Get(NanNew<String>("file")));
char* contents = create_string(object->Get(NanNew<String>("contents")));
imports[i] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0);
}
}
else if (returned_value->IsObject()) {
imports = sass_make_import_list(1);
Local<Object> object = Local<Object>::Cast(returned_value);
char* path = create_string(object->Get(NanNew<String>("file")));
char* contents = create_string(object->Get(NanNew<String>("contents")));
imports[0] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0);
}
else {
imports = sass_make_import_list(1);
imports[0] = sass_make_import_entry((char const*) this->argv[0], 0, 0);
}
return imports;
}
std::vector<Handle<Value>> CustomImporterBridge::pre_process_args(std::vector<void*> in) const {
std::vector<Handle<Value>> out;
for (void* ptr : in) {
out.push_back(NanNew<String>((char const*) ptr));
}
return out;
}
#ifndef CUSTOM_IMPORTER_BRIDGE_H
#define CUSTOM_IMPORTER_BRIDGE_H
#include <nan.h>
#include <sass_context.h>
#include "callback_bridge.h"
using namespace v8;
typedef Sass_Import** SassImportList;
class CustomImporterBridge : public CallbackBridge<SassImportList> {
public:
CustomImporterBridge(NanCallback* cb, bool is_sync) : CallbackBridge<SassImportList>(cb, is_sync) {}
private:
SassImportList post_process_return_value(Handle<Value>) const;
std::vector<Handle<Value>> pre_process_args(std::vector<void*>) const;
};
#endif
......@@ -23,12 +23,7 @@ extern "C" {
}
sass_context_wrapper* sass_make_context_wrapper() {
sass_context_wrapper* ctx_w = (sass_context_wrapper*)calloc(1, sizeof(sass_context_wrapper));
ctx_w->importer_mutex = new std::mutex();
ctx_w->importer_condition_variable = new std::condition_variable();
return ctx_w;
return (sass_context_wrapper*)calloc(1, sizeof(sass_context_wrapper));
}
void sass_wrapper_dispose(struct sass_context_wrapper* ctx_w, char* string = 0) {
......@@ -39,20 +34,24 @@ extern "C" {
sass_delete_file_context(ctx_w->fctx);
}
delete ctx_w->file;
delete ctx_w->prev;
delete ctx_w->error_callback;
delete ctx_w->success_callback;
delete ctx_w->importer_callback;
delete ctx_w->importer_mutex;
delete ctx_w->importer_condition_variable;
NanDisposePersistent(ctx_w->result);
if(string) {
free(string);
}
if (!ctx_w->function_bridges.empty()) {
for (CustomFunctionBridge* bridge : ctx_w->function_bridges) {
delete bridge;
}
}
if (ctx_w->importer_bridge) {
delete ctx_w->importer_bridge;
}
}
void sass_free_context_wrapper(sass_context_wrapper* ctx_w) {
......
#include <stdlib.h>
#ifndef SASS_CONTEXT_WRAPPER
#define SASS_CONTEXT_WRAPPER
#include <vector>
#include <nan.h>
#include <stdlib.h>
#include <condition_variable>
#include <sass_context.h>
#include "custom_function_bridge.h"
#include "custom_importer_bridge.h"
#ifdef __cplusplus
extern "C" {
......@@ -17,13 +23,8 @@ extern "C" {
// binding related
bool is_sync;
void* cookie;
const char* prev;
const char* file;
std::mutex* importer_mutex;
std::condition_variable* importer_condition_variable;
// libsass related
Sass_Import** imports;
Sass_Data_Context* dctx;
Sass_File_Context* fctx;
......@@ -35,7 +36,9 @@ extern "C" {
Persistent<Object> result;
NanCallback* error_callback;
NanCallback* success_callback;
NanCallback* importer_callback;
std::vector<CustomFunctionBridge*> function_bridges;
CustomImporterBridge* importer_bridge;
};
struct sass_context_wrapper* sass_make_context_wrapper(void);
......@@ -45,3 +48,5 @@ extern "C" {
#ifdef __cplusplus
}
#endif
#endif
#include <nan.h>
#include <sass_values.h>
#include "boolean.h"
#include "sass_value_wrapper.h"
using namespace v8;
namespace SassTypes
{
Persistent<Function> Boolean::constructor;
bool Boolean::constructor_locked = false;
Boolean::Boolean(bool v) : value(v) {}
Boolean& Boolean::get_singleton(bool v) {
static Boolean instance_false(false), instance_true(true);
return v ? instance_true : instance_false;
}
Handle<Function> Boolean::get_constructor() {
if (constructor.IsEmpty()) {
Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
tpl->SetClassName(NanNew("SassBoolean"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->PrototypeTemplate()->Set(NanNew("getValue"), NanNew<FunctionTemplate>(GetValue)->GetFunction());
NanAssignPersistent(constructor, tpl->GetFunction());
NanAssignPersistent(get_singleton(false).js_object, NanNew(constructor)->NewInstance());
NanSetInternalFieldPointer(NanNew(get_singleton(false).js_object), 0, &get_singleton(false));
NanNew(constructor)->Set(NanNew("FALSE"), NanNew(get_singleton(false).js_object));
NanAssignPersistent(get_singleton(true).js_object, NanNew(constructor)->NewInstance());
NanSetInternalFieldPointer(NanNew(get_singleton(true).js_object), 0, &get_singleton(true));
NanNew(constructor)->Set(NanNew("TRUE"), NanNew(get_singleton(true).js_object));
constructor_locked = true;
}
return NanNew(constructor);
}
Sass_Value* Boolean::get_sass_value() {
return sass_make_boolean(value);
}
Local<Object> Boolean::get_js_object() {
return NanNew(this->js_object);
}
NAN_METHOD(Boolean::New) {
NanScope();
if (args.IsConstructCall()) {
if (constructor_locked) {
return NanThrowError(NanNew("Cannot instantiate SassBoolean"));
}
} else {
if (args.Length() != 1 || !args[0]->IsBoolean()) {
return NanThrowError(NanNew("Expected one boolean argument"));
}
NanReturnValue(NanNew(get_singleton(args[0]->ToBoolean()->Value()).get_js_object()));
}
NanReturnUndefined();
}
NAN_METHOD(Boolean::GetValue) {
NanScope();
NanReturnValue(NanNew(static_cast<Boolean*>(Factory::unwrap(args.This()))->value));
}
}
#ifndef SASS_TYPES_BOOLEAN_H
#define SASS_TYPES_BOOLEAN_H
#include <nan.h>
#include <sass_values.h>
#include "value.h"
namespace SassTypes
{
using namespace v8;
class Boolean : public Value {
public:
static Boolean& get_singleton(bool);
static Handle<Function> get_constructor();
Sass_Value* get_sass_value();
Local<Object> get_js_object();
static NAN_METHOD(New);
static NAN_METHOD(GetValue);
private:
Boolean(bool);
bool value;
Persistent<Object> js_object;
static Persistent<Function> constructor;
static bool constructor_locked;
};
}
#endif
#include <nan.h>
#include <sass_values.h>
#include "color.h"
#include "sass_value_wrapper.h"
using namespace v8;
namespace SassTypes
{
Color::Color(Sass_Value* v) : SassValueWrapper(v) {}
Sass_Value* Color::construct(const std::vector<Local<v8::Value>> raw_val) {
double a = 1.0, r = 0, g = 0, b = 0;
unsigned argb;
switch (raw_val.size()) {
case 1:
if (!raw_val[0]->IsNumber()) {
throw std::invalid_argument("Only argument should be an integer.");
}
argb = raw_val[0]->ToInt32()->Value();
a = (double) ((argb >> 030) & 0xff) / 0xff;
r = (double) ((argb >> 020) & 0xff);
g = (double) ((argb >> 010) & 0xff);
b = (double) (argb & 0xff);
break;
case 4:
if (!raw_val[3]->IsNumber()) {
throw std::invalid_argument("Constructor arguments should be numbers exclusively.");
}
a = raw_val[3]->ToNumber()->Value();
// fall through vvv
case 3:
if (!raw_val[0]->IsNumber() || !raw_val[1]->IsNumber() || !raw_val[2]->IsNumber()) {
throw std::invalid_argument("Constructor arguments should be numbers exclusively.");
}
r = raw_val[0]->ToNumber()->Value();
g = raw_val[1]->ToNumber()->Value();
b = raw_val[2]->ToNumber()->Value();
break;
case 0:
break;
default:
throw std::invalid_argument("Constructor should be invoked with either 0, 1, 3 or 4 arguments.");
}
return sass_make_color(r, g, b, a);
}
void Color::initPrototype(Handle<ObjectTemplate> proto) {
proto->Set(NanNew("getR"), NanNew<FunctionTemplate>(GetR)->GetFunction());
proto->Set(NanNew("getG"), NanNew<FunctionTemplate>(GetG)->GetFunction());
proto->Set(NanNew("getB"), NanNew<FunctionTemplate>(GetB)->GetFunction());
proto->Set(NanNew("getA"), NanNew<FunctionTemplate>(GetA)->GetFunction());
proto->Set(NanNew("setR"), NanNew<FunctionTemplate>(SetR)->GetFunction());
proto->Set(NanNew("setG"), NanNew<FunctionTemplate>(SetG)->GetFunction());
proto->Set(NanNew("setB"), NanNew<FunctionTemplate>(SetB)->GetFunction());
proto->Set(NanNew("setA"), NanNew<FunctionTemplate>(SetA)->GetFunction());
}
NAN_METHOD(Color::GetR) {
NanScope();
NanReturnValue(NanNew(sass_color_get_r(unwrap(args.This())->value)));
}
NAN_METHOD(Color::GetG) {
NanScope();
NanReturnValue(NanNew(sass_color_get_g(unwrap(args.This())->value)));
}
NAN_METHOD(Color::GetB) {
NanScope();
NanReturnValue(NanNew(sass_color_get_b(unwrap(args.This())->value)));
}
NAN_METHOD(Color::GetA) {
NanScope();
NanReturnValue(NanNew(sass_color_get_a(unwrap(args.This())->value)));
}
NAN_METHOD(Color::SetR) {
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied value should be a number"));
}
sass_color_set_r(unwrap(args.This())->value, args[0]->ToNumber()->Value());
NanReturnUndefined();
}
NAN_METHOD(Color::SetG) {
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied value should be a number"));
}
sass_color_set_g(unwrap(args.This())->value, args[0]->ToNumber()->Value());
NanReturnUndefined();
}
NAN_METHOD(Color::SetB) {
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied value should be a number"));
}
sass_color_set_b(unwrap(args.This())->value, args[0]->ToNumber()->Value());
NanReturnUndefined();
}
NAN_METHOD(Color::SetA) {
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied value should be a number"));
}
sass_color_set_a(unwrap(args.This())->value, args[0]->ToNumber()->Value());
NanReturnUndefined();
}
}
#ifndef SASS_TYPES_COLOR_H
#define SASS_TYPES_COLOR_H
#include <nan.h>
#include <sass_values.h>
#include "sass_value_wrapper.h"
namespace SassTypes
{
using namespace v8;
class Color : public SassValueWrapper<Color> {
public:
Color(Sass_Value*);
static char const* get_constructor_name() { return "SassColor"; }
static Sass_Value* construct(const std::vector<Local<v8::Value>>);
static void initPrototype(Handle<ObjectTemplate>);
static NAN_METHOD(GetR);
static NAN_METHOD(GetG);
static NAN_METHOD(GetB);
static NAN_METHOD(GetA);
static NAN_METHOD(SetR);
static NAN_METHOD(SetG);
static NAN_METHOD(SetB);
static NAN_METHOD(SetA);
};
}
#endif
#include <nan.h>
#include <sass_values.h>
#include "error.h"
#include "../create_string.h"
#include "sass_value_wrapper.h"
using namespace v8;
namespace SassTypes
{
Error::Error(Sass_Value* v) : SassValueWrapper(v) {}
Sass_Value* Error::construct(const std::vector<Local<v8::Value>> raw_val) {
char const* value = "";
if (raw_val.size() >= 1) {
if (!raw_val[0]->IsString()) {
throw std::invalid_argument("Argument should be a string.");
}
value = create_string(raw_val[0]);
}
return sass_make_error(value);
}
void Error::initPrototype(Handle<ObjectTemplate>) {}
}
#ifndef SASS_TYPES_ERROR_H
#define SASS_TYPES_ERROR_H
#include <nan.h>
#include <sass_values.h>
#include "sass_value_wrapper.h"
namespace SassTypes
{
using namespace v8;
class Error : public SassValueWrapper<Error> {
public:
Error(Sass_Value*);
static char const* get_constructor_name() { return "SassError"; }
static Sass_Value* construct(const std::vector<Local<v8::Value>>);
static void initPrototype(Handle<ObjectTemplate>);
};
}
#endif
#include <nan.h>
#include "factory.h"
#include <sass_values.h>
#include "value.h"
#include "number.h"
#include "string.h"
#include "color.h"
#include "boolean.h"
#include "list.h"
#include "map.h"
#include "null.h"
#include "error.h"
using namespace v8;
namespace SassTypes
{
Value* Factory::create(Sass_Value* v) {
switch (sass_value_get_tag(v)) {
case SASS_NUMBER:
return new Number(v);
case SASS_STRING:
return new String(v);
case SASS_COLOR:
return new Color(v);
case SASS_BOOLEAN:
return &Boolean::get_singleton(sass_boolean_get_value(v));
case SASS_LIST:
return new List(v);
case SASS_MAP:
return new Map(v);
case SASS_NULL:
return &Null::get_singleton();
case SASS_ERROR:
return new Error(v);
default:
throw std::invalid_argument("Unknown type encountered.");
}
}
void Factory::initExports(Handle<Object> exports) {
Local<Object> types = NanNew<Object>();
exports->Set(NanNew("types"), types);
types->Set(NanNew("Number"), Number::get_constructor());
types->Set(NanNew("String"), String::get_constructor());
types->Set(NanNew("Color"), Color::get_constructor());
types->Set(NanNew("Boolean"), Boolean::get_constructor());
types->Set(NanNew("List"), List::get_constructor());
types->Set(NanNew("Map"), Map::get_constructor());
types->Set(NanNew("Null"), Null::get_constructor());
types->Set(NanNew("Error"), Error::get_constructor());
}
Value* Factory::unwrap(Handle<v8::Value> obj) {
// Todo: non-SassValue objects could easily fall under that condition, need to be more specific.
if (!obj->IsObject() || obj->ToObject()->InternalFieldCount() != 1) {
throw std::invalid_argument("A SassValue object was expected.");
}
return static_cast<Value*>(NanGetInternalFieldPointer(obj->ToObject(), 0));
}
}
#ifndef SASS_TYPES_FACTORY_H
#define SASS_TYPES_FACTORY_H
#include <nan.h>
#include <sass_values.h>
#include "value.h"
namespace SassTypes
{
using namespace v8;
// This is the guru that knows everything about instantiating the right subclass of SassTypes::Value
// to wrap a given Sass_Value object.
class Factory {
public:
static void initExports(Handle<Object>);
static Value* create(Sass_Value*);
static Value* unwrap(Handle<v8::Value>);
};
}
#endif
#include <nan.h>
#include <sass_values.h>
#include "list.h"
#include "sass_value_wrapper.h"
using namespace v8;
namespace SassTypes
{
List::List(Sass_Value* v) : SassValueWrapper(v) {}
Sass_Value* List::construct(const std::vector<Local<v8::Value>> raw_val) {
size_t length = 0;
bool comma = true;
if (raw_val.size() >= 1) {
if (!raw_val[0]->IsNumber()) {
throw std::invalid_argument("First argument should be an integer.");
}
length = raw_val[0]->ToInt32()->Value();
if (raw_val.size() >= 2) {
if (!raw_val[1]->IsBoolean()) {
throw std::invalid_argument("Second argument should be a boolean.");
}
comma = raw_val[1]->ToBoolean()->Value();
}
}
return sass_make_list(length, comma ? SASS_COMMA : SASS_SPACE);
}
void List::initPrototype(Handle<ObjectTemplate> proto) {
proto->Set(NanNew("getLength"), NanNew<FunctionTemplate>(GetLength)->GetFunction());
proto->Set(NanNew("getSeparator"), NanNew<FunctionTemplate>(GetSeparator)->GetFunction());
proto->Set(NanNew("setSeparator"), NanNew<FunctionTemplate>(SetSeparator)->GetFunction());
proto->Set(NanNew("getValue"), NanNew<FunctionTemplate>(GetValue)->GetFunction());
proto->Set(NanNew("setValue"), NanNew<FunctionTemplate>(SetValue)->GetFunction());
}
NAN_METHOD(List::GetValue) {
NanScope();
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied index should be an integer"));
}
Sass_Value* list = unwrap(args.This())->value;
size_t index = args[0]->ToInt32()->Value();
if (index >= sass_list_get_length(list)) {
return NanThrowError(NanNew("Out of bound index"));
}
NanReturnValue(Factory::create(sass_list_get_value(list, args[0]->ToInt32()->Value()))->get_js_object());
}
NAN_METHOD(List::SetValue) {
if (args.Length() != 2) {
return NanThrowError(NanNew("Expected two arguments"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied index should be an integer"));
}
if (!args[1]->IsObject()) {
return NanThrowError(NanNew("Supplied value should be a SassValue object"));
}
Value* sass_value = Factory::unwrap(args[1]);
sass_list_set_value(unwrap(args.This())->value, args[0]->ToInt32()->Value(), sass_value->get_sass_value());
NanReturnUndefined();
}
NAN_METHOD(List::GetSeparator) {
NanScope();
NanReturnValue(NanNew(sass_list_get_separator(unwrap(args.This())->value) == SASS_COMMA));
}
NAN_METHOD(List::SetSeparator) {
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsBoolean()) {
return NanThrowError(NanNew("Supplied value should be a boolean"));
}
sass_list_set_separator(unwrap(args.This())->value, args[0]->ToBoolean()->Value() ? SASS_COMMA : SASS_SPACE);
NanReturnUndefined();
}
NAN_METHOD(List::GetLength) {
NanScope();
NanReturnValue(NanNew<v8::Number>(sass_list_get_length(unwrap(args.This())->value)));
}
}
#ifndef SASS_TYPES_LIST_H
#define SASS_TYPES_LIST_H
#include <nan.h>
#include <sass_values.h>
#include "sass_value_wrapper.h"
namespace SassTypes
{
using namespace v8;
class List : public SassValueWrapper<List> {
public:
List(Sass_Value*);
static char const* get_constructor_name() { return "SassList"; }
static Sass_Value* construct(const std::vector<Local<v8::Value>>);
static void initPrototype(Handle<ObjectTemplate>);
static NAN_METHOD(GetValue);
static NAN_METHOD(SetValue);
static NAN_METHOD(GetSeparator);
static NAN_METHOD(SetSeparator);
static NAN_METHOD(GetLength);
};
}
#endif
#include <nan.h>
#include <sass_values.h>
#include "map.h"
#include "sass_value_wrapper.h"
using namespace v8;
namespace SassTypes
{
Map::Map(Sass_Value* v) : SassValueWrapper(v) {}
Sass_Value* Map::construct(const std::vector<Local<v8::Value>> raw_val) {
size_t length = 0;
if (raw_val.size() >= 1) {
if (!raw_val[0]->IsNumber()) {
throw std::invalid_argument("First argument should be an integer.");
}
length = raw_val[0]->ToInt32()->Value();
}
return sass_make_map(length);
}
void Map::initPrototype(Handle<ObjectTemplate> proto) {
proto->Set(NanNew("getLength"), NanNew<FunctionTemplate>(GetLength)->GetFunction());
proto->Set(NanNew("getKey"), NanNew<FunctionTemplate>(GetKey)->GetFunction());
proto->Set(NanNew("setKey"), NanNew<FunctionTemplate>(SetKey)->GetFunction());
proto->Set(NanNew("getValue"), NanNew<FunctionTemplate>(GetValue)->GetFunction());
proto->Set(NanNew("setValue"), NanNew<FunctionTemplate>(SetValue)->GetFunction());
}
NAN_METHOD(Map::GetValue) {
NanScope();
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied index should be an integer"));
}
Sass_Value* map = unwrap(args.This())->value;
size_t index = args[0]->ToInt32()->Value();
if (index >= sass_map_get_length(map)) {
return NanThrowError(NanNew("Out of bound index"));
}
NanReturnValue(NanNew(Factory::create(sass_map_get_value(map, args[0]->ToInt32()->Value()))->get_js_object()));
}
NAN_METHOD(Map::SetValue) {
if (args.Length() != 2) {
return NanThrowError(NanNew("Expected two arguments"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied index should be an integer"));
}
if (!args[1]->IsObject()) {
return NanThrowError(NanNew("Supplied value should be a SassValue object"));
}
Value* sass_value = Factory::unwrap(args[1]);
sass_map_set_value(unwrap(args.This())->value, args[0]->ToInt32()->Value(), sass_value->get_sass_value());
NanReturnUndefined();
}
NAN_METHOD(Map::GetKey) {
NanScope();
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied index should be an integer"));
}
Sass_Value* map = unwrap(args.This())->value;
size_t index = args[0]->ToInt32()->Value();
if (index >= sass_map_get_length(map)) {
return NanThrowError(NanNew("Out of bound index"));
}
NanReturnValue(Factory::create(sass_map_get_key(map, args[0]->ToInt32()->Value()))->get_js_object());
}
NAN_METHOD(Map::SetKey) {
if (args.Length() != 2) {
return NanThrowError(NanNew("Expected two arguments"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied index should be an integer"));
}
if (!args[1]->IsObject()) {
return NanThrowError(NanNew("Supplied value should be a SassValue object"));
}
Value* sass_value = Factory::unwrap(args[1]);
sass_map_set_key(unwrap(args.This())->value, args[0]->ToInt32()->Value(), sass_value->get_sass_value());
NanReturnUndefined();
}
NAN_METHOD(Map::GetLength) {
NanScope();
NanReturnValue(NanNew<v8::Number>(sass_map_get_length(unwrap(args.This())->value)));
}
}
#ifndef SASS_TYPES_MAP_H
#define SASS_TYPES_MAP_H
#include <nan.h>
#include <sass_values.h>
#include "sass_value_wrapper.h"
namespace SassTypes
{
using namespace v8;
class Map : public SassValueWrapper<Map> {
public:
Map(Sass_Value*);
static char const* get_constructor_name() { return "SassMap"; }
static Sass_Value* construct(const std::vector<Local<v8::Value>>);
static void initPrototype(Handle<ObjectTemplate>);
static NAN_METHOD(GetValue);
static NAN_METHOD(SetValue);
static NAN_METHOD(GetKey);
static NAN_METHOD(SetKey);
static NAN_METHOD(GetLength);
};
}
#endif
#include <nan.h>
#include <sass_values.h>
#include "null.h"
#include "sass_value_wrapper.h"
using namespace v8;
namespace SassTypes
{
Persistent<Function> Null::constructor;
bool Null::constructor_locked = false;
Null::Null() {}
Null& Null::get_singleton() {
static Null singleton_instance;
return singleton_instance;
}
Handle<Function> Null::get_constructor() {
if (constructor.IsEmpty()) {
Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
tpl->SetClassName(NanNew("SassNull"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
NanAssignPersistent(constructor, tpl->GetFunction());
NanAssignPersistent(get_singleton().js_object, NanNew(constructor)->NewInstance());
NanSetInternalFieldPointer(NanNew(get_singleton().js_object), 0, &get_singleton());
NanNew(constructor)->Set(NanNew("NULL"), NanNew(get_singleton().js_object));
constructor_locked = true;
}
return NanNew(constructor);
}
Sass_Value* Null::get_sass_value() {
return sass_make_null();
}
Local<Object> Null::get_js_object() {
return NanNew(this->js_object);
}
NAN_METHOD(Null::New) {
NanScope();
if (args.IsConstructCall()) {
if (constructor_locked) {
return NanThrowError(NanNew("Cannot instantiate SassNull"));
}
} else {
NanReturnValue(NanNew(get_singleton().get_js_object()));
}
NanReturnUndefined();
}
}
#ifndef SASS_TYPES_NULL_H
#define SASS_TYPES_NULL_H
#include <nan.h>
#include <sass_values.h>
#include "value.h"
namespace SassTypes
{
using namespace v8;
class Null : public Value {
public:
static Null& get_singleton();
static Handle<Function> get_constructor();
Sass_Value* get_sass_value();
Local<Object> get_js_object();
static NAN_METHOD(New);
private:
Null();
Persistent<Object> js_object;
static Persistent<Function> constructor;
static bool constructor_locked;
};
}
#endif
#include <nan.h>
#include <sass_values.h>
#include "number.h"
#include "../create_string.h"
#include "sass_value_wrapper.h"
using namespace v8;
namespace SassTypes
{
Number::Number(Sass_Value* v) : SassValueWrapper(v) {}
Sass_Value* Number::construct(const std::vector<Local<v8::Value>> raw_val) {
double value = 0;
char const* unit = "";
if (raw_val.size() >= 1) {
if (!raw_val[0]->IsNumber()) {
throw std::invalid_argument("First argument should be a number.");
}
value = raw_val[0]->ToNumber()->Value();
if (raw_val.size() >= 2) {
if (!raw_val[1]->IsString()) {
throw std::invalid_argument("Second argument should be a string.");
}
unit = create_string(raw_val[1]);
}
}
return sass_make_number(value, unit);
}
void Number::initPrototype(Handle<ObjectTemplate> proto) {
proto->Set(NanNew("getValue"), NanNew<FunctionTemplate>(GetValue)->GetFunction());
proto->Set(NanNew("getUnit"), NanNew<FunctionTemplate>(GetUnit)->GetFunction());
proto->Set(NanNew("setValue"), NanNew<FunctionTemplate>(SetValue)->GetFunction());
proto->Set(NanNew("setUnit"), NanNew<FunctionTemplate>(SetUnit)->GetFunction());
}
NAN_METHOD(Number::GetValue) {
NanScope();
NanReturnValue(NanNew(sass_number_get_value(unwrap(args.This())->value)));
}
NAN_METHOD(Number::GetUnit) {
NanScope();
NanReturnValue(NanNew(sass_number_get_unit(unwrap(args.This())->value)));
}
NAN_METHOD(Number::SetValue) {
NanScope();
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsNumber()) {
return NanThrowError(NanNew("Supplied value should be a number"));
}
sass_number_set_value(unwrap(args.This())->value, args[0]->ToNumber()->Value());
NanReturnUndefined();
}
NAN_METHOD(Number::SetUnit) {
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsString()) {
return NanThrowError(NanNew("Supplied value should be a string"));
}
sass_number_set_unit(unwrap(args.This())->value, create_string(args[0]));
NanReturnUndefined();
}
}
#ifndef SASS_TYPES_NUMBER_H
#define SASS_TYPES_NUMBER_H
#include <nan.h>
#include <sass_values.h>
#include "sass_value_wrapper.h"
namespace SassTypes
{
using namespace v8;
class Number : public SassValueWrapper<Number> {
public:
Number(Sass_Value*);
static char const* get_constructor_name() { return "SassNumber"; }
static Sass_Value* construct(const std::vector<Local<v8::Value>>);
static void initPrototype(Handle<ObjectTemplate>);
static NAN_METHOD(GetValue);
static NAN_METHOD(GetUnit);
static NAN_METHOD(SetValue);
static NAN_METHOD(SetUnit);
};
}
#endif
#ifndef SASS_TYPES_SASS_VALUE_WRAPPER_H
#define SASS_TYPES_SASS_VALUE_WRAPPER_H
#include <stdexcept>
#include <vector>
#include <nan.h>
#include <sass_values.h>
#include "value.h"
#include "factory.h"
namespace SassTypes
{
using namespace v8;
// Include this in any SassTypes::Value subclasses to handle all the heavy lifting of constructing JS
// objects and wrapping sass values inside them
template <class T>
class SassValueWrapper : public Value {
public:
static char const* get_constructor_name() { return "SassValue"; }
SassValueWrapper(Sass_Value*);
virtual ~SassValueWrapper();
Sass_Value* get_sass_value();
Local<Object> get_js_object();
static Handle<Function> get_constructor();
static Local<FunctionTemplate> get_constructor_template();
static NAN_METHOD(New);
protected:
Sass_Value* value;
static T* unwrap(Local<Object>);
private:
static Persistent<Function> constructor;
Persistent<Object> js_object;
};
template <class T>
Persistent<Function> SassValueWrapper<T>::constructor;
template <class T>
SassValueWrapper<T>::SassValueWrapper(Sass_Value* v) {
this->value = sass_clone_value(v);
}
template <class T>
SassValueWrapper<T>::~SassValueWrapper() {
NanDisposePersistent(this->js_object);
sass_delete_value(this->value);
}
template <class T>
Sass_Value* SassValueWrapper<T>::get_sass_value() {
return sass_clone_value(this->value);
}
template <class T>
Local<Object> SassValueWrapper<T>::get_js_object() {
if (this->js_object.IsEmpty()) {
Local<Object> wrapper = NanNew(T::get_constructor())->NewInstance();
delete static_cast<T*>(NanGetInternalFieldPointer(wrapper, 0));
NanSetInternalFieldPointer(wrapper, 0, this);
NanAssignPersistent(this->js_object, wrapper);
}
return NanNew(this->js_object);
}
template <class T>
Local<FunctionTemplate> SassValueWrapper<T>::get_constructor_template() {
Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
tpl->SetClassName(NanNew(NanNew(T::get_constructor_name())));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
T::initPrototype(tpl->PrototypeTemplate());
return tpl;
}
template <class T>
Handle<Function> SassValueWrapper<T>::get_constructor() {
if (constructor.IsEmpty()) {
NanAssignPersistent(constructor, T::get_constructor_template()->GetFunction());
}
return NanNew(constructor);
}
template <class T>
NAN_METHOD(SassValueWrapper<T>::New) {
NanScope();
if (!args.IsConstructCall()) {
unsigned argc = args.Length();
std::vector<Handle<v8::Value>> argv;
argv.reserve(argc);
for (unsigned i = 0; i < argc; i++) {
argv.push_back(args[i]);
}
NanReturnValue(NanNew(T::get_constructor())->NewInstance(argc, &argv[0]));
}
std::vector<Local<v8::Value>> localArgs(args.Length());
for (auto i = 0; i < args.Length(); ++i) {
localArgs[i] = args[i];
}
try {
Sass_Value* value = T::construct(localArgs);
T* obj = new T(value);
sass_delete_value(value);
NanSetInternalFieldPointer(args.This(), 0, obj);
NanAssignPersistent(obj->js_object, args.This());
} catch (const std::exception& e) {
return NanThrowError(NanNew(e.what()));
}
NanReturnUndefined();
}
template <class T>
T* SassValueWrapper<T>::unwrap(Local<Object> obj) {
return static_cast<T*>(Factory::unwrap(obj));
}
}
#endif
#include <nan.h>
#include <sass_values.h>
#include "string.h"
#include "../create_string.h"
#include "sass_value_wrapper.h"
using namespace v8;
namespace SassTypes
{
String::String(Sass_Value* v) : SassValueWrapper(v) {}
Sass_Value* String::construct(const std::vector<Local<v8::Value>> raw_val) {
char const* value = "";
if (raw_val.size() >= 1) {
if (!raw_val[0]->IsString()) {
throw std::invalid_argument("Argument should be a string.");
}
value = create_string(raw_val[0]);
}
return sass_make_string(value);
}
void String::initPrototype(Handle<ObjectTemplate> proto) {
proto->Set(NanNew("getValue"), NanNew<FunctionTemplate>(GetValue)->GetFunction());
proto->Set(NanNew("setValue"), NanNew<FunctionTemplate>(SetValue)->GetFunction());
}
NAN_METHOD(String::GetValue) {
NanScope();
NanReturnValue(NanNew(sass_string_get_value(unwrap(args.This())->value)));
}
NAN_METHOD(String::SetValue) {
if (args.Length() != 1) {
return NanThrowError(NanNew("Expected just one argument"));
}
if (!args[0]->IsString()) {
return NanThrowError(NanNew("Supplied value should be a string"));
}
sass_string_set_value(unwrap(args.This())->value, create_string(args[0]));
NanReturnUndefined();
}
}
#ifndef SASS_TYPES_STRING_H
#define SASS_TYPES_STRING_H
#include <nan.h>
#include <sass_values.h>
#include "sass_value_wrapper.h"
namespace SassTypes
{
using namespace v8;
class String : public SassValueWrapper<String> {
public:
String(Sass_Value*);
static char const* get_constructor_name() { return "SassString"; }
static Sass_Value* construct(const std::vector<Local<v8::Value>>);
static void initPrototype(Handle<ObjectTemplate>);
static NAN_METHOD(GetValue);
static NAN_METHOD(SetValue);
};
}
#endif
#ifndef SASS_TYPES_VALUE_H
#define SASS_TYPES_VALUE_H
#include <nan.h>
#include <sass_values.h>
namespace SassTypes
{
using namespace v8;
// This is the interface that all sass values must comply with
class Value {
public:
virtual Sass_Value* get_sass_value() =0;
virtual Local<Object> get_js_object() =0;
};
}
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment