<commit_before>// application/javascript;version=1.8
// Copyright 2011 Giovanni Campagna
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

var GLib = imports.gi.GLib;
var GObject = imports.gi.GObject;
var GjsPrivate = imports.gi.GjsPrivate;
var Lang = imports.lang;
var Signals = imports.signals;
var Gio;

function _signatureLength(sig) {
    var counter = 0;
    // make it an array
    var signature = Array.prototype.slice.call(sig);
    while (signature.length) {
        GLib._read_single_type(sig);
        counter++;
    }
    return counter;
}

function _proxyInvoker(methodName, sync, inSignature, arg_array) {
    var replyFunc;
    var flags = 0;
    var cancellable = null;

    /* Convert arg_array to a *real* array */
    arg_array = Array.prototype.slice.call(arg_array);

    /* The default replyFunc only logs the responses */
    replyFunc = _logReply;

    var signatureLength = inSignature.length;
    var minNumberArgs = signatureLength;
    var maxNumberArgs = signatureLength + 3;

    if (arg_array.length < minNumberArgs) {
        throw new Error("Not enough arguments passed for method: " + methodName +
                       ". Expected " + minNumberArgs + ", got " + arg_array.length);
    } else if (arg_array.length > maxNumberArgs) {
        throw new Error("Too many arguments passed for method: " + methodName +
                       ". Maximum is " + maxNumberArgs +
                        " + one callback and/or flags");
    }

    while (arg_array.length > signatureLength) {
        var argNum = arg_array.length - 1;
        var arg = arg_array.pop();
        if (typeof(arg) == "function" && !sync) {
            replyFunc = arg;
        } else if (typeof(arg) == "number") {
            flags = arg;
        } else if (arg instanceof Gio.Cancellable) {
            cancellable = arg;
        } else {
            throw new Error("Argument " + argNum + " of method " + methodName +
                            " is " + typeof(arg) + ". It should be a callback, flags or a Gio.Cancellable");
        }
    }

    var inVariant = new GLib.Variant('(' + inSignature.join('') + ')', arg_array);

    var asyncCallback = function (proxy, result) {
        try {
            var outVariant = proxy.call_finish(result);
            replyFunc(outVariant.deep_unpack(), null);
        } catch (e) {
            replyFunc(null, e);
        }
    };

    if (sync) {
        return this.call_sync(methodName,
                              inVariant,
                              flags,
                              -1,
                              cancellable).deep_unpack();
    } else {
        return this.call(methodName,
                         inVariant,
                         flags,
                         -1,
                         cancellable,
                         asyncCallback);
    }
}

function _logReply(result, exc) {
    if (exc != null) {
        log("Ignored exception from dbus method: " + exc.toString());
    }
}

function _makeProxyMethod(method, sync) {
    var i;
    var name = method.name;
    var inArgs = method.in_args;
    var inSignature = [ ];
    for (i = 0; i < inArgs.length; i++)
        inSignature.push(inArgs[i].signature);

    return function() {
        return _proxyInvoker.call(this, name, sync, inSignature, arguments);
    };
}

function _convertToNativeSignal(proxy, sender_name, signal_name, parameters) {
    Signals._emit.call(proxy, signal_name, sender_name, parameters.deep_unpack());
}

function _propertyGetter(name) {
    let value = this.get_cached_property(name);
    return value ? value.deep_unpack() : null;
}

function _propertySetter(value, name, signature) {
    let variant = new GLib.Variant(signature, value);
    this.set_cached_property(name, variant);

    this.call('org.freedesktop.DBus.Properties.Set',
              new GLib.Variant('(ssv)',
                               [this.g_interface_name,
                                name, variant]),
              Gio.DBusCallFlags.NONE, -1, null,
              Lang.bind(this, function(proxy, result) {
                  try {
                      this.call_finish(result);
                  } catch(e) {
                      log('Could not set property ' + name + ' on remote object ' +
                          this.g_object_path + ': ' + e.message);
                  }
              }));
}

function _addDBusConvenience() {
    let info = this.g_interface_info;
    if (!info)
        return;

    if (info.signals.length > 0)
        this.connect('g-signal', _convertToNativeSignal);

    let i, methods = info.methods;
    for (i = 0; i < methods.length; i++) {
        var method = methods[i];
        this[method.name + 'Remote'] = _makeProxyMethod(methods[i], false);
        this[method.name + 'Sync'] = _makeProxyMethod(methods[i], true);
    }

    let properties = info.properties;
    for (i = 0; i < properties.length; i++) {
        let name = properties[i].name;
        let signature = properties[i].signature;
        Lang.defineAccessorProperty(this, name,
                                    Lang.bind(this, _propertyGetter, name),
                                    Lang.bind(this, _propertySetter, name, signature));
    }
}

function _makeProxyWrapper(interfaceXml) {
    var info = _newInterfaceInfo(interfaceXml);
    var iname = info.name;
    return function(bus, name, object, asyncCallback, cancellable) {
        var obj = new Gio.DBusProxy({ g_connection: bus,
                                      g_interface_name: iname,
                                      g_interface_info: info,
                                      g_name: name,
                                      g_object_path: object });
        if (!cancellable)
            cancellable = null;
        if (asyncCallback)
            obj.init_async(GLib.PRIORITY_DEFAULT, cancellable, function(initable, result) {
                let caughtErrorWhenInitting = null;
                try {
                    initable.init_finish(result);
                } catch(e) {
                    caughtErrorWhenInitting = e;
                }

                if (caughtErrorWhenInitting === null) {
                    asyncCallback(initable, null);
                } else {
                    asyncCallback(null, caughtErrorWhenInitting);
                }
            });
        else
            obj.init(cancellable);
        return obj;
    };
}

function _newNodeInfo(constructor, value) {
    if (typeof value == 'string')
        return constructor(value);
    else if (value instanceof XML)
        return constructor(value.toXMLString());
    else
        throw TypeError('Invalid type ' + Object.prototype.toString.call(value));
}

function _newInterfaceInfo(value) {
    var xml;
    if (typeof value == 'string')
        xml = new XML(value);
    else if (value instanceof XML)
        xml = value;
    else
        throw TypeError('Invalid type ' + Object.prototype.toString.call(value));

    var node;
    if (xml.name() == 'interface') {
        // wrap inside a node
        node = <node/>;
        node.node += xml;
    } else
        node = xml;

    var nodeInfo = Gio.DBusNodeInfo.new_for_xml(node);
    return nodeInfo.interfaces[0];
}

function _injectToMethod(klass, method, addition) {
    var previous = klass[method];

    klass[method] = function() {
        addition.apply(this, arguments);
        return previous.apply(this, arguments);
    }
}

function _wrapFunction(klass, method, addition) {
    var previous = klass[method];

    klass[method] = function() {
        var args = Array.prototype.slice.call(arguments);
        args.unshift(previous);
        return addition.apply(this, args);
    }
}

function _makeOutSignature(args) {
    var ret = '(';
    for (var i = 0; i < args.length; i++)
        ret += args[i].signature;

    return ret + ')';
}

function _handleMethodCall(info, impl, method_name, parameters, invocation) {
    // prefer a sync version if available
    if (this[method_name]) {
        let retval;
        try {
            retval = this[method_name].apply(this, parameters.deep_unpack());
        } catch (e) {
            if (e instanceof GLib.Error) {
                invocation.return_gerror(e);
            } else {
                let name = e.name;
                if (name.indexOf('.') == -1) {
                    // likely to be a normal JS error
                    name = 'org.gnome.gjs.JSError.' + name;
                }
                invocation.return_dbus_error(name, e.message);
            }
            return;
        }
        if (retval === undefined) {
            // undefined (no return value) is the empty tuple
            retval = new GLib.Variant('()', []);
        }
        try {
            if (!(retval instanceof GLib.Variant)) {
                // attempt packing according to out signature
                let methodInfo = info.lookup_method(method_name);
                let outArgs = methodInfo.out_args;
                let outSignature = _makeOutSignature(outArgs);
                if (outArgs.length == 1) {
                    // if one arg, we don't require the handler wrapping it
                    // into an Array
                    retval = [retval];
                }
                retval = new GLib.Variant(outSignature, retval);
            }
            invocation.return_value(retval);
        } catch(e) {
            // if we don't do this, the other side will never see a reply
            invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError',
                                         "Service implementation returned an incorrect value type");
        }
    } else if (this[method_name + 'Async']) {
        this[method_name + 'Async'](parameters.deep_unpack(), invocation);
    } else {
        log('Missing handler for DBus method ' + method_name);
        invocation.return_gerror(new Gio.DBusError({ code: Gio.DBusError.UNKNOWN_METHOD,
                                                     message: 'Method ' + method_name + ' is not implemented' }));
    }
}

function _handlePropertyGet(info, impl, property_name) {
    let propInfo = info.lookup_property(property_name);
    let jsval = this[property_name];
    if (jsval != undefined)
        return new GLib.Variant(propInfo.signature, jsval);
    else
        return null;
}

function _handlePropertySet(info, impl, property_name, new_value) {
    this[property_name] = new_value.deep_unpack();
}

function _wrapJSObject(interfaceInfo, jsObj) {
    var info;
    if (interfaceInfo instanceof Gio.DBusInterfaceInfo)
        info = interfaceInfo;
    else
        info = Gio.DBusInterfaceInfo.new_for_xml(interfaceInfo);
    info.cache_build();

    var impl = new GjsPrivate.DBusImplementation({ g_interface_info: info });
    impl.connect('handle-method-call', function(impl, method_name, parameters, invocation) {
        return _handleMethodCall.call(jsObj, info, impl, method_name, parameters, invocation)
    });
    impl.connect('handle-property-get', function(impl, property_name) {
        return _handlePropertyGet.call(jsObj, info, impl, property_name);
    });
    impl.connect('handle-property-set', function(impl, property_name, value) {
        return _handlePropertySet.call(jsObj, info, impl, property_name, value);
    });

    return impl;
}

function _init() {
    Gio = this;

    Gio.DBus = {
        get session() {
            return Gio.bus_get_sync(Gio.BusType.SESSION, null);
        },
        get system() {
            return Gio.bus_get_sync(Gio.BusType.SYSTEM, null);
        },

        // Namespace some functions
        get:        Gio.bus_get,
        get_finish: Gio.bus_get_finish,
        get_sync:   Gio.bus_get_sync,

        own_name:               Gio.bus_own_name,
        own_name_on_connection: Gio.bus_own_name_on_connection,
        unown_name:             Gio.bus_unown_name,

        watch_name:               Gio.bus_watch_name,
        watch_name_on_connection: Gio.bus_watch_name_on_connection,
        unwatch_name:             Gio.bus_unwatch_name,
    };

    Gio.DBusConnection.prototype.watch_name = function(name, flags, appeared, vanished) {
        return Gio.bus_watch_name_on_connection(this, name, flags, appeared, vanished);
    };
    Gio.DBusConnection.prototype.unwatch_name = function(id) {
        return Gio.bus_unwatch_name(id);
    };
    Gio.DBusConnection.prototype.own_name = function(name, flags, acquired, lost) {
        return Gio.bus_own_name_on_connection(this, name, flags, acquired, lost);
    };
    Gio.DBusConnection.prototype.unown_name = function(id) {
        return Gio.bus_unown_name(id);
    };

    _injectToMethod(Gio.DBusProxy.prototype, 'init', _addDBusConvenience);
    _injectToMethod(Gio.DBusProxy.prototype, 'init_async', _addDBusConvenience);
    Gio.DBusProxy.prototype.connectSignal = Signals._connect;
    Gio.DBusProxy.prototype.disconnectSignal = Signals._disconnect;

    Gio.DBusProxy.makeProxyWrapper = _makeProxyWrapper;

    // Some helpers
    _wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo);
    Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo;

    Gio.DBusExportedObject = GjsPrivate.DBusImplementation;
    Gio.DBusExportedObject.wrapJSObject = _wrapJSObject;
}
<commit_msg>Gio: Don't eat errors when calling reply functions<commit_after>// application/javascript;version=1.8
// Copyright 2011 Giovanni Campagna
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

var GLib = imports.gi.GLib;
var GObject = imports.gi.GObject;
var GjsPrivate = imports.gi.GjsPrivate;
var Lang = imports.lang;
var Signals = imports.signals;
var Gio;

function _signatureLength(sig) {
    var counter = 0;
    // make it an array
    var signature = Array.prototype.slice.call(sig);
    while (signature.length) {
        GLib._read_single_type(sig);
        counter++;
    }
    return counter;
}

function _proxyInvoker(methodName, sync, inSignature, arg_array) {
    var replyFunc;
    var flags = 0;
    var cancellable = null;

    /* Convert arg_array to a *real* array */
    arg_array = Array.prototype.slice.call(arg_array);

    /* The default replyFunc only logs the responses */
    replyFunc = _logReply;

    var signatureLength = inSignature.length;
    var minNumberArgs = signatureLength;
    var maxNumberArgs = signatureLength + 3;

    if (arg_array.length < minNumberArgs) {
        throw new Error("Not enough arguments passed for method: " + methodName +
                       ". Expected " + minNumberArgs + ", got " + arg_array.length);
    } else if (arg_array.length > maxNumberArgs) {
        throw new Error("Too many arguments passed for method: " + methodName +
                       ". Maximum is " + maxNumberArgs +
                        " + one callback and/or flags");
    }

    while (arg_array.length > signatureLength) {
        var argNum = arg_array.length - 1;
        var arg = arg_array.pop();
        if (typeof(arg) == "function" && !sync) {
            replyFunc = arg;
        } else if (typeof(arg) == "number") {
            flags = arg;
        } else if (arg instanceof Gio.Cancellable) {
            cancellable = arg;
        } else {
            throw new Error("Argument " + argNum + " of method " + methodName +
                            " is " + typeof(arg) + ". It should be a callback, flags or a Gio.Cancellable");
        }
    }

    var inVariant = new GLib.Variant('(' + inSignature.join('') + ')', arg_array);

    var asyncCallback = function (proxy, result) {
        var outVariant = null, succeeded = false;
        try {
            outVariant = proxy.call_finish(result);
            succeeded = true;
        } catch (e) {
            replyFunc(null, e);
        }

        if (succeeded)
            replyFunc(outVariant.deep_unpack(), null);
    };

    if (sync) {
        return this.call_sync(methodName,
                              inVariant,
                              flags,
                              -1,
                              cancellable).deep_unpack();
    } else {
        return this.call(methodName,
                         inVariant,
                         flags,
                         -1,
                         cancellable,
                         asyncCallback);
    }
}

function _logReply(result, exc) {
    if (exc != null) {
        log("Ignored exception from dbus method: " + exc.toString());
    }
}

function _makeProxyMethod(method, sync) {
    var i;
    var name = method.name;
    var inArgs = method.in_args;
    var inSignature = [ ];
    for (i = 0; i < inArgs.length; i++)
        inSignature.push(inArgs[i].signature);

    return function() {
        return _proxyInvoker.call(this, name, sync, inSignature, arguments);
    };
}

function _convertToNativeSignal(proxy, sender_name, signal_name, parameters) {
    Signals._emit.call(proxy, signal_name, sender_name, parameters.deep_unpack());
}

function _propertyGetter(name) {
    let value = this.get_cached_property(name);
    return value ? value.deep_unpack() : null;
}

function _propertySetter(value, name, signature) {
    let variant = new GLib.Variant(signature, value);
    this.set_cached_property(name, variant);

    this.call('org.freedesktop.DBus.Properties.Set',
              new GLib.Variant('(ssv)',
                               [this.g_interface_name,
                                name, variant]),
              Gio.DBusCallFlags.NONE, -1, null,
              Lang.bind(this, function(proxy, result) {
                  try {
                      this.call_finish(result);
                  } catch(e) {
                      log('Could not set property ' + name + ' on remote object ' +
                          this.g_object_path + ': ' + e.message);
                  }
              }));
}

function _addDBusConvenience() {
    let info = this.g_interface_info;
    if (!info)
        return;

    if (info.signals.length > 0)
        this.connect('g-signal', _convertToNativeSignal);

    let i, methods = info.methods;
    for (i = 0; i < methods.length; i++) {
        var method = methods[i];
        this[method.name + 'Remote'] = _makeProxyMethod(methods[i], false);
        this[method.name + 'Sync'] = _makeProxyMethod(methods[i], true);
    }

    let properties = info.properties;
    for (i = 0; i < properties.length; i++) {
        let name = properties[i].name;
        let signature = properties[i].signature;
        Lang.defineAccessorProperty(this, name,
                                    Lang.bind(this, _propertyGetter, name),
                                    Lang.bind(this, _propertySetter, name, signature));
    }
}

function _makeProxyWrapper(interfaceXml) {
    var info = _newInterfaceInfo(interfaceXml);
    var iname = info.name;
    return function(bus, name, object, asyncCallback, cancellable) {
        var obj = new Gio.DBusProxy({ g_connection: bus,
                                      g_interface_name: iname,
                                      g_interface_info: info,
                                      g_name: name,
                                      g_object_path: object });
        if (!cancellable)
            cancellable = null;
        if (asyncCallback)
            obj.init_async(GLib.PRIORITY_DEFAULT, cancellable, function(initable, result) {
                let caughtErrorWhenInitting = null;
                try {
                    initable.init_finish(result);
                } catch(e) {
                    caughtErrorWhenInitting = e;
                }

                if (caughtErrorWhenInitting === null) {
                    asyncCallback(initable, null);
                } else {
                    asyncCallback(null, caughtErrorWhenInitting);
                }
            });
        else
            obj.init(cancellable);
        return obj;
    };
}

function _newNodeInfo(constructor, value) {
    if (typeof value == 'string')
        return constructor(value);
    else if (value instanceof XML)
        return constructor(value.toXMLString());
    else
        throw TypeError('Invalid type ' + Object.prototype.toString.call(value));
}

function _newInterfaceInfo(value) {
    var xml;
    if (typeof value == 'string')
        xml = new XML(value);
    else if (value instanceof XML)
        xml = value;
    else
        throw TypeError('Invalid type ' + Object.prototype.toString.call(value));

    var node;
    if (xml.name() == 'interface') {
        // wrap inside a node
        node = <node/>;
        node.node += xml;
    } else
        node = xml;

    var nodeInfo = Gio.DBusNodeInfo.new_for_xml(node);
    return nodeInfo.interfaces[0];
}

function _injectToMethod(klass, method, addition) {
    var previous = klass[method];

    klass[method] = function() {
        addition.apply(this, arguments);
        return previous.apply(this, arguments);
    }
}

function _wrapFunction(klass, method, addition) {
    var previous = klass[method];

    klass[method] = function() {
        var args = Array.prototype.slice.call(arguments);
        args.unshift(previous);
        return addition.apply(this, args);
    }
}

function _makeOutSignature(args) {
    var ret = '(';
    for (var i = 0; i < args.length; i++)
        ret += args[i].signature;

    return ret + ')';
}

function _handleMethodCall(info, impl, method_name, parameters, invocation) {
    // prefer a sync version if available
    if (this[method_name]) {
        let retval;
        try {
            retval = this[method_name].apply(this, parameters.deep_unpack());
        } catch (e) {
            if (e instanceof GLib.Error) {
                invocation.return_gerror(e);
            } else {
                let name = e.name;
                if (name.indexOf('.') == -1) {
                    // likely to be a normal JS error
                    name = 'org.gnome.gjs.JSError.' + name;
                }
                invocation.return_dbus_error(name, e.message);
            }
            return;
        }
        if (retval === undefined) {
            // undefined (no return value) is the empty tuple
            retval = new GLib.Variant('()', []);
        }
        try {
            if (!(retval instanceof GLib.Variant)) {
                // attempt packing according to out signature
                let methodInfo = info.lookup_method(method_name);
                let outArgs = methodInfo.out_args;
                let outSignature = _makeOutSignature(outArgs);
                if (outArgs.length == 1) {
                    // if one arg, we don't require the handler wrapping it
                    // into an Array
                    retval = [retval];
                }
                retval = new GLib.Variant(outSignature, retval);
            }
            invocation.return_value(retval);
        } catch(e) {
            // if we don't do this, the other side will never see a reply
            invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError',
                                         "Service implementation returned an incorrect value type");
        }
    } else if (this[method_name + 'Async']) {
        this[method_name + 'Async'](parameters.deep_unpack(), invocation);
    } else {
        log('Missing handler for DBus method ' + method_name);
        invocation.return_gerror(new Gio.DBusError({ code: Gio.DBusError.UNKNOWN_METHOD,
                                                     message: 'Method ' + method_name + ' is not implemented' }));
    }
}

function _handlePropertyGet(info, impl, property_name) {
    let propInfo = info.lookup_property(property_name);
    let jsval = this[property_name];
    if (jsval != undefined)
        return new GLib.Variant(propInfo.signature, jsval);
    else
        return null;
}

function _handlePropertySet(info, impl, property_name, new_value) {
    this[property_name] = new_value.deep_unpack();
}

function _wrapJSObject(interfaceInfo, jsObj) {
    var info;
    if (interfaceInfo instanceof Gio.DBusInterfaceInfo)
        info = interfaceInfo;
    else
        info = Gio.DBusInterfaceInfo.new_for_xml(interfaceInfo);
    info.cache_build();

    var impl = new GjsPrivate.DBusImplementation({ g_interface_info: info });
    impl.connect('handle-method-call', function(impl, method_name, parameters, invocation) {
        return _handleMethodCall.call(jsObj, info, impl, method_name, parameters, invocation)
    });
    impl.connect('handle-property-get', function(impl, property_name) {
        return _handlePropertyGet.call(jsObj, info, impl, property_name);
    });
    impl.connect('handle-property-set', function(impl, property_name, value) {
        return _handlePropertySet.call(jsObj, info, impl, property_name, value);
    });

    return impl;
}

function _init() {
    Gio = this;

    Gio.DBus = {
        get session() {
            return Gio.bus_get_sync(Gio.BusType.SESSION, null);
        },
        get system() {
            return Gio.bus_get_sync(Gio.BusType.SYSTEM, null);
        },

        // Namespace some functions
        get:        Gio.bus_get,
        get_finish: Gio.bus_get_finish,
        get_sync:   Gio.bus_get_sync,

        own_name:               Gio.bus_own_name,
        own_name_on_connection: Gio.bus_own_name_on_connection,
        unown_name:             Gio.bus_unown_name,

        watch_name:               Gio.bus_watch_name,
        watch_name_on_connection: Gio.bus_watch_name_on_connection,
        unwatch_name:             Gio.bus_unwatch_name,
    };

    Gio.DBusConnection.prototype.watch_name = function(name, flags, appeared, vanished) {
        return Gio.bus_watch_name_on_connection(this, name, flags, appeared, vanished);
    };
    Gio.DBusConnection.prototype.unwatch_name = function(id) {
        return Gio.bus_unwatch_name(id);
    };
    Gio.DBusConnection.prototype.own_name = function(name, flags, acquired, lost) {
        return Gio.bus_own_name_on_connection(this, name, flags, acquired, lost);
    };
    Gio.DBusConnection.prototype.unown_name = function(id) {
        return Gio.bus_unown_name(id);
    };

    _injectToMethod(Gio.DBusProxy.prototype, 'init', _addDBusConvenience);
    _injectToMethod(Gio.DBusProxy.prototype, 'init_async', _addDBusConvenience);
    Gio.DBusProxy.prototype.connectSignal = Signals._connect;
    Gio.DBusProxy.prototype.disconnectSignal = Signals._disconnect;

    Gio.DBusProxy.makeProxyWrapper = _makeProxyWrapper;

    // Some helpers
    _wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo);
    Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo;

    Gio.DBusExportedObject = GjsPrivate.DBusImplementation;
    Gio.DBusExportedObject.wrapJSObject = _wrapJSObject;
}
<|endoftext|>


<commit_before>angular.module('grisu-noe').service('util', function($ionicPopup, $ionicLoading, $cordovaToast) {
    this.showErrorDialog = function(message) {
        $ionicPopup.alert({
            title: message,
            buttons: [{
                text: 'OK',
                type: 'button-assertive'
            }]
        });
    };

    this.showLoadingErrorDialog = function(httpCode) {
        var message = 'Daten konnten nicht geladen werden. ';

        switch (httpCode) {
            case 0:
                message += 'Möglicherweise besteht keine Internetverbindung.';
                break;
            default:
                message += 'Fehlercode ' + httpCode;
                break;
        }

        $cordovaToast.showShortBottom(message);
    };

    this.showLoadingDelayed = function() {
        $ionicLoading.show({
            template: ' Lade Daten...',
            delay: 1000
        });
    };

    this.hideLoading = function() {
        $ionicLoading.hide();
    };

    this.hideLoadingInScope = function(scope) {
        scope.isRefreshing = false;
        scope.$broadcast('scroll.refreshComplete');
        this.hideLoading();
    };

    this.genericRefresh = function(scope, dataPromise, callback, options) {
        var self = this;
        scope.isRefreshing = true;
        this.showLoadingDelayed();

        var hideLoading = true;
        if ((typeof options === 'object') &&
            options.hasOwnProperty('hideRefreshers') &&
            options.hideRefreshers === false) {
            hideLoading = false;
        }

        var showErrorDialog = true;
        if ((typeof options === 'object') &&
            options.hasOwnProperty('showErrorDialog') &&
            options.showErrorDialog === false) {
            showErrorDialog = false;
        }

        dataPromise.then(function(data) {
            callback(data);
        }, function(errCode) {
            if (showErrorDialog) {
                self.showLoadingErrorDialog(errCode);
            }

            if (!hideLoading) {
                self.hideLoadingInScope(scope);
            }
        }).finally(function() {
            if (hideLoading) {
                self.hideLoadingInScope(scope);
            }
        });
    };

    this.calculateBygoneTime = function(dateTimeStr, pattern) {
        var begin = moment(dateTimeStr, pattern).utc();

        if (!begin.isValid()) {
            console.error('Parsing of bygone time failed!');
            return null;
        }

        var millis = moment().utc().diff(begin);

        if (millis < 1000) {
            console.debug('Value of milliseconds is too small', millis);
            return null;
        }

        var duration = moment.duration(millis);

        return {
            hours: Math.floor(duration.asHours()),
            minutes: moment.utc(millis).format('m')
        };
    };

    this.formatWastlDate = function(dateStr) {
        if (angular.isUndefinedOrNull(dateStr)) {
            return null;
        }
        return moment(dateStr.substring(0, 19).replace('T', ' '), 'YYYY-MM-DD HH:mm:ss').format('DD.MM.YYYY HH:mm:ss');
    };
});<commit_msg>#50: Corrected loading position<commit_after>angular.module('grisu-noe').service('util', function($ionicPopup, $ionicLoading, $cordovaToast) {
    this.showErrorDialog = function(message) {
        $ionicPopup.alert({
            title: message,
            buttons: [{
                text: 'OK',
                type: 'button-assertive'
            }]
        });
    };

    this.showLoadingErrorDialog = function(httpCode) {
        var message = 'Daten konnten nicht geladen werden. ';

        switch (httpCode) {
            case 0:
                message += 'Möglicherweise besteht keine Internetverbindung.';
                break;
            default:
                message += 'Fehlercode ' + httpCode;
                break;
        }

        $cordovaToast.showShortCenter(message);
    };

    this.showLoadingDelayed = function() {
        $ionicLoading.show({
            template: ' Lade Daten...',
            delay: 1000
        });
    };

    this.hideLoading = function() {
        $ionicLoading.hide();
    };

    this.hideLoadingInScope = function(scope) {
        scope.isRefreshing = false;
        scope.$broadcast('scroll.refreshComplete');
        this.hideLoading();
    };

    this.genericRefresh = function(scope, dataPromise, callback, options) {
        var self = this;
        scope.isRefreshing = true;
        this.showLoadingDelayed();

        var hideLoading = true;
        if ((typeof options === 'object') &&
            options.hasOwnProperty('hideRefreshers') &&
            options.hideRefreshers === false) {
            hideLoading = false;
        }

        var showErrorDialog = true;
        if ((typeof options === 'object') &&
            options.hasOwnProperty('showErrorDialog') &&
            options.showErrorDialog === false) {
            showErrorDialog = false;
        }

        dataPromise.then(function(data) {
            callback(data);
        }, function(errCode) {
            if (showErrorDialog) {
                self.showLoadingErrorDialog(errCode);
            }

            if (!hideLoading) {
                self.hideLoadingInScope(scope);
            }
        }).finally(function() {
            if (hideLoading) {
                self.hideLoadingInScope(scope);
            }
        });
    };

    this.calculateBygoneTime = function(dateTimeStr, pattern) {
        var begin = moment(dateTimeStr, pattern).utc();

        if (!begin.isValid()) {
            console.error('Parsing of bygone time failed!');
            return null;
        }

        var millis = moment().utc().diff(begin);

        if (millis < 1000) {
            console.debug('Value of milliseconds is too small', millis);
            return null;
        }

        var duration = moment.duration(millis);

        return {
            hours: Math.floor(duration.asHours()),
            minutes: moment.utc(millis).format('m')
        };
    };

    this.formatWastlDate = function(dateStr) {
        if (angular.isUndefinedOrNull(dateStr)) {
            return null;
        }
        return moment(dateStr.substring(0, 19).replace('T', ' '), 'YYYY-MM-DD HH:mm:ss').format('DD.MM.YYYY HH:mm:ss');
    };
});<|endoftext|>


<commit_before>function TLWebView(controller, messageHandler) {
    this.controller = controller
    this.messageHandler = messageHandler
    controller.adapter = this
}

TLWebView.prototype = {
    visitLocationWithAction: function(location, action) {
        this.controller.startVisitToLocationWithAction(location, action)
    },

    // Current visit

    issueRequest: function() {
        this.currentVisit.issueRequest()
    },

    changeHistory: function() {
        this.currentVisit.changeHistory()
    },

    restoreSnapshot: function() {
        this.currentVisit.restoreSnapshot()
    },

    loadResponse: function() {
        this.currentVisit.loadResponse()
    },

    cancelVisit: function() {
        this.currentVisit.cancel()
    },

    // Adapter interface

    visitProposedToLocationWithAction: function(location, action) {
        this.postMessage("visitProposed", { location: location.absoluteURL })
    },

    visitStarted: function(visit) {
        this.currentVisit = visit
        var location = visit.location.absoluteURL
        var hasSnapshot = visit.hasSnapshot()
        this.postMessage("visitStarted", { location: location, hasSnapshot: hasSnapshot })
    },

    visitRequestStarted: function(visit) {
        this.postMessage("visitRequestStarted")
    },

    visitRequestCompleted: function(visit) {
        this.postMessage("visitRequestCompleted")
    },

    visitRequestFailedWithStatusCode: function(visit, statusCode) {
        this.postMessage("visitRequestFailed", { statusCode: statusCode })
    },

    visitRequestFinished: function(visit) {
        this.postMessage("visitRequestFinished")
    },

    visitSnapshotRestored: function(visit) {
        this.postMessageAfterNextRepaint("visitSnapshotRestored")
    },

    visitResponseLoaded: function(visit) {
        this.postMessageAfterNextRepaint("visitResponseLoaded")
    },

    pageInvalidated: function() {
        this.postMessage("pageInvalidated")
    },

    // Private

    postMessage: function(name, data) {
        this.messageHandler.postMessage({ name: name, data: data || {} })
    },

    postMessageAfterNextRepaint: function(name, data) {
        var postMessage = this.postMessage.bind(this, name, data)
        requestAnimationFrame(function() {
            requestAnimationFrame(postMessage)
        })
    }
}

window.webView = new TLWebView(Turbolinks.controller, webkit.messageHandlers.turbolinks)

addEventListener("error", function(event) {
    var message = event.message + " (" + event.filename + ":" + event.lineno + ":" + event.colno + ")"
    webView.postMessage("error", { message: message })
}, false)
<commit_msg>Implement visitCompleted() on the adapter<commit_after>function TLWebView(controller, messageHandler) {
    this.controller = controller
    this.messageHandler = messageHandler
    controller.adapter = this
}

TLWebView.prototype = {
    visitLocationWithAction: function(location, action) {
        this.controller.startVisitToLocationWithAction(location, action)
    },

    // Current visit

    issueRequest: function() {
        this.currentVisit.issueRequest()
    },

    changeHistory: function() {
        this.currentVisit.changeHistory()
    },

    restoreSnapshot: function() {
        this.currentVisit.restoreSnapshot()
    },

    loadResponse: function() {
        this.currentVisit.loadResponse()
    },

    cancelVisit: function() {
        this.currentVisit.cancel()
    },

    // Adapter interface

    visitProposedToLocationWithAction: function(location, action) {
        this.postMessage("visitProposed", { location: location.absoluteURL })
    },

    visitStarted: function(visit) {
        this.currentVisit = visit
        var location = visit.location.absoluteURL
        var hasSnapshot = visit.hasSnapshot()
        this.postMessage("visitStarted", { location: location, hasSnapshot: hasSnapshot })
    },

    visitRequestStarted: function(visit) {
        this.postMessage("visitRequestStarted")
    },

    visitRequestCompleted: function(visit) {
        this.postMessage("visitRequestCompleted")
    },

    visitRequestFailedWithStatusCode: function(visit, statusCode) {
        this.postMessage("visitRequestFailed", { statusCode: statusCode })
    },

    visitRequestFinished: function(visit) {
        this.postMessage("visitRequestFinished")
    },

    visitSnapshotRestored: function(visit) {
        this.postMessageAfterNextRepaint("visitSnapshotRestored")
    },

    visitResponseLoaded: function(visit) {
        this.postMessageAfterNextRepaint("visitResponseLoaded")
    },

    visitCompleted: function(visit) {
        this.postMessageAfterNextRepaint("visitCompleted")
    },

    pageInvalidated: function() {
        this.postMessage("pageInvalidated")
    },

    // Private

    postMessage: function(name, data) {
        this.messageHandler.postMessage({ name: name, data: data || {} })
    },

    postMessageAfterNextRepaint: function(name, data) {
        var postMessage = this.postMessage.bind(this, name, data)
        requestAnimationFrame(function() {
            requestAnimationFrame(postMessage)
        })
    }
}

window.webView = new TLWebView(Turbolinks.controller, webkit.messageHandlers.turbolinks)

addEventListener("error", function(event) {
    var message = event.message + " (" + event.filename + ":" + event.lineno + ":" + event.colno + ")"
    webView.postMessage("error", { message: message })
}, false)
<|endoftext|>


<commit_before>var xpath = require("xpath");
var dom = require('xmldom').DOMParser;
var fs = require("fs");
var path = require('path');
var argv = require('yarg').argv;

var fight_mapping = {
    patchwerk_ba_2t: 0.025,
    patchwerk_ba_st: 0.07,
    patchwerk_sa_2t: 0.02,
    patchwerk_sa_st: 0.07,
    patchwerk_na_2t: 0,
    patchwerk_na_st: 0.07,
    lowmovement_ba_2t: 0.155,
    lowmovement_ba_st: 0.195,
    lowmovement_sa_2t: 0.05,
    lowmovement_sa_st: 0.12,
    lowmovement_na_2t: 0.03,
    lowmovement_na_st: 0.065,
    highmovement_ba_2t: 0,
    highmovement_ba_st: 0.08,
    highmovement_sa_2t: 0.015,
    highmovement_sa_st: 0.035,
    highmovement_na_2t: 0,
    highmovement_na_st: 0
}

var time_mapping = {
    250: .15,
    400: .85
}

var sum = 0;
Object.keys(time_mapping).forEach(function(time) {
    sum += time_mapping[time];
})
console.log("Time calibration: " + sum);
sum = 0;
Object.keys(fight_mapping).forEach(function(fight) {
    sum += fight_mapping[fight];
})
console.log("Fight calibration: " + sum);

sum = 0;

var damage = 0;
var dps = 0;
var int = 0;
var mastery = 0;
var haste = 0;
var crit = 0;
var sp = 0;
var simname;
var vers = 0;
fs.readdirSync(path.join(".","/results")).forEach(function(result){
    var xml = fs.readFileSync(path.join('.','results', result), "utf-8");
    var doc = new dom().parseFromString(xml);
    var res = result.match("^(.*)_([0-9]*)_(.*).simc.xml");

    var time = res[2];
    var fight = res[3];
    simname = name;
    var weight = fight_mapping[fight]*time_mapping[time];

	damage += xpath.select1("//simulationcraft/summary/dmg/@total", doc).value*weight;
	dps += xpath.select1("//simulationcraft/summary/dmg/@dps", doc).value*weight;
    var name = res[1];
	if(name != "sim_test") {
		name = xpath.select1("//simulationcraft/summary/player_by_dps/player/@name").value;
	}
    if(!argv.noweights) {
        int += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Int']/@value", doc).value*weight;
        mastery += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Mastery']/@value", doc).value*weight;
        haste += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Haste']/@value", doc).value*weight;
        crit +=xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Crit']/@value", doc).value*weight;
        sp += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='SP']/@value", doc).value*weight;
        vers += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Vers']/@value", doc).value*weight;
    }
    sum += weight;
});

if(process.argv[2] == "sim_test") {
    simname = "simtest";
}
console.log("Total calibration: " + sum);
console.log("Damage (DPS): " + damage + " (" + dps + ")");
if(!argv.noweights) {
    var sint = int/int;
    var smastery = mastery/int;
    var shaste = haste/int;
    var scrit = crit/int;
    var svers = vers/int;
    console.log("( Pawn: v1: \"" + simname + "_selfsim\": Intellect=" + sint + ", Versatility="+ svers.toFixed(4) + ", HasteRating=" + shaste.toFixed(4) + ", MasteryRating=" + smastery.toFixed(4) + ", CritRating=" + scrit.toFixed(4) + " )");
    if(process.argv[2] == "sim_test") {
        console.log("Expected Values: ( Pawn: v1: \"SL 4-piece\": Intellect=1, MasteryRating=1.36, HasteRating=1.2, CritRating=1.02, Versatility=0.9 )")
        console.log("Differences: int=" + (1-sint) + ", mastery=" + (1.36-smastery) + ", haste=" + (1.2-shaste) + ", crit=" + (1.02-scrit) + ", vers=" + (0.9-svers));
    }
}

console.log("Note: If your calibration values vary greatly from 1, please verify weights/that all sims were run.");
<commit_msg>Fixing a bug in analyze where it would fail to run.<commit_after>var xpath = require("xpath");
var dom = require('xmldom').DOMParser;
var fs = require("fs");
var path = require('path');
var argv = require('yargs').argv;

var fight_mapping = {
    patchwerk_ba_2t: 0.025,
    patchwerk_ba_st: 0.07,
    patchwerk_sa_2t: 0.02,
    patchwerk_sa_st: 0.07,
    patchwerk_na_2t: 0,
    patchwerk_na_st: 0.07,
    lowmovement_ba_2t: 0.155,
    lowmovement_ba_st: 0.195,
    lowmovement_sa_2t: 0.05,
    lowmovement_sa_st: 0.12,
    lowmovement_na_2t: 0.03,
    lowmovement_na_st: 0.065,
    highmovement_ba_2t: 0,
    highmovement_ba_st: 0.08,
    highmovement_sa_2t: 0.015,
    highmovement_sa_st: 0.035,
    highmovement_na_2t: 0,
    highmovement_na_st: 0
}

var time_mapping = {
    250: .15,
    400: .85
}

var sum = 0;
Object.keys(time_mapping).forEach(function(time) {
    sum += time_mapping[time];
})
console.log("Time calibration: " + sum);
sum = 0;
Object.keys(fight_mapping).forEach(function(fight) {
    sum += fight_mapping[fight];
})
console.log("Fight calibration: " + sum);

sum = 0;

var damage = 0;
var dps = 0;
var int = 0;
var mastery = 0;
var haste = 0;
var crit = 0;
var sp = 0;
var simname;
var vers = 0;
fs.readdirSync(path.join(".","/results")).forEach(function(result){
    var xml = fs.readFileSync(path.join('.','results', result), "utf-8");
    var doc = new dom().parseFromString(xml);
    var res = result.match("^(.*)_([0-9]*)_(.*).simc.xml");

    var time = res[2];
    var fight = res[3];
    simname = name;
    var weight = fight_mapping[fight]*time_mapping[time];

	damage += xpath.select1("//simulationcraft/summary/dmg/@total", doc).value*weight;
	dps += xpath.select1("//simulationcraft/summary/dmg/@dps", doc).value*weight;
    var name = res[1];
	if(name != "sim_test") {
		name = xpath.select1("//simulationcraft/summary/player_by_dps/player/@name", doc).value;
	}
    if(!argv.noweights) {
        int += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Int']/@value", doc).value*weight;
        mastery += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Mastery']/@value", doc).value*weight;
        haste += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Haste']/@value", doc).value*weight;
        crit +=xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Crit']/@value", doc).value*weight;
        sp += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='SP']/@value", doc).value*weight;
        vers += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Vers']/@value", doc).value*weight;
    }
    sum += weight;
});

if(process.argv[2] == "sim_test") {
    simname = "simtest";
}
console.log("Total calibration: " + sum);
console.log("Damage (DPS): " + damage + " (" + dps + ")");
if(!argv.noweights) {
    var sint = int/int;
    var smastery = mastery/int;
    var shaste = haste/int;
    var scrit = crit/int;
    var svers = vers/int;
    console.log("( Pawn: v1: \"" + simname + "_selfsim\": Intellect=" + sint + ", Versatility="+ svers.toFixed(4) + ", HasteRating=" + shaste.toFixed(4) + ", MasteryRating=" + smastery.toFixed(4) + ", CritRating=" + scrit.toFixed(4) + " )");
    if(process.argv[2] == "sim_test") {
        console.log("Expected Values: ( Pawn: v1: \"SL 4-piece\": Intellect=1, MasteryRating=1.36, HasteRating=1.2, CritRating=1.02, Versatility=0.9 )")
        console.log("Differences: int=" + (1-sint) + ", mastery=" + (1.36-smastery) + ", haste=" + (1.2-shaste) + ", crit=" + (1.02-scrit) + ", vers=" + (0.9-svers));
    }
}

console.log("Note: If your calibration values vary greatly from 1, please verify weights/that all sims were run.");
<|endoftext|>


<commit_before>'use strict';

var Token = require('../models/token.js');
var mapper = require('../helpers/mapper.js');
var request = require('request');
var async = require('async');

// Download all contacts
module.exports = function(tokens, since, cb) {
  var pad = function(n, width) {
    width = width || 2;
    n = n + '';
    return n.length >= width ? n : new Array(width - n.length + 1).join(0) + n;
  }

  // Date, formatted as 2007-03-16T00:00:00
  var formattedDate = since.getFullYear() + '-' + pad(since.getMonth() + 1) + '-' + pad(since.getDate()) + 'T' + pad(since.getHours()) + ':' + pad(since.getMinutes()) + ':' + pad(since.getSeconds())

  var params = {
    url: 'https://www.google.com/m8/feeds/contacts/default/full',
    qs: {
      alt: 'json',
      'max-results': 100000,
      'orderby': 'lastmodified',
      'updated-min': formattedDate
    },
    headers: {
      'Authorization': 'OAuth ' + tokens.access_token,
      'GData-Version': '3.0'
    }
  }

  request.get(params, function (err, resp, body) {
    if(resp.statusCode === 401){
      throw new Error("Wrong Authorization provided.");
    }

    var feed = JSON.parse(body);
    var users = feed.feed.entry.map(mapper);

    // Users is now full!
    cb(users);
  });
}
<commit_msg>Refreshing access_token<commit_after>'use strict';

var Token = require('../models/token.js');
var keys = require('../../../keys.js');
var mapper = require('../helpers/mapper.js');
var request = require('request');
var async = require('async');

var refreshAccessToken = function(refresh_token, cb) {
  var params = {
    url: 'https://accounts.google.com/o/oauth2/token',
    form: {
      'refresh_token': refresh_token,
      'client_id': keys.GOOGLE_ID,
      'client_secret': keys.GOOGLE_SECRET,
      'grant_type': 'refresh_token'
    },
    json: true
  }

  request.post(params, function (err, resp, body) {
    if(err){
      throw new Error(err);
    }
    if(resp.statusCode === 401){
      throw new Error("Access to this refresh_token has been revoked.");
    }

    cb(resp.body.access_token);
  });
}

var retrieveContacts = function(access_token, since, cb) {
  var pad = function(n, width) {
    width = width || 2;
    n = n + '';
    return n.length >= width ? n : new Array(width - n.length + 1).join(0) + n;
  }

  // Date, formatted as 2007-03-16T00:00:00
  var formattedDate = since.getFullYear() + '-' + pad(since.getMonth() + 1) + '-' + pad(since.getDate()) + 'T' + pad(since.getHours()) + ':' + pad(since.getMinutes()) + ':' + pad(since.getSeconds())

  var params = {
    url: 'https://www.google.com/m8/feeds/contacts/default/full',
    qs: {
      alt: 'json',
      'max-results': 100000,
      'orderby': 'lastmodified',
      'updated-min': formattedDate
    },
    headers: {
      'Authorization': 'OAuth ' + access_token,
      'GData-Version': '3.0'
    }
  }

  request.get(params, function (err, resp, body) {
    if(resp.statusCode === 401){
      throw new Error("Wrong Authorization provided.");
    }

    var feed = JSON.parse(body);
    var users = feed.feed.entry.map(mapper);

    // Users is now full!
    cb(users);
  });
}

// Download all contacts
module.exports = function(tokens, since, cb) {
  refreshAccessToken(tokens.refresh_token, function(access_token) {
    retrieveContacts(access_token, since, cb);
  })
}
<|endoftext|>


<commit_before>"use strict";

var
  DataFile = require('./DataFile'),
  forge = require('node-forge'),
  rsa = require('node-forge').pki.rsa,
  pki = require('node-forge').pki;

class NoxiousCrypto{
  constructor(obj) {
    this.myPrivKey = null;
    this.pubPem = null;
    this.myPubKey = null;
    this.keySize=0;
    // default size for new keys
    this.newKeySize=3072;

    // accepts either dir, filename or public key
    if(obj['pubPem']) {
      // object has public Key
      this.pubPem = obj.pubPem;
      this.myPubKey = pki.publicKeyFromPem(this.pubPem);
      this.keySize = this.myPubKey.n.bitLength();
    } else {
      // assume it's a dataDir and filename
      let keyData = new DataFile(obj.path);
      if(keyData.has('privPem')) {
        // key already exists
        this.myPrivKey = pki.privateKeyFromPem(keyData.get('privPem'));
        this.pubPem = keyData.get('pubPem');
        this.myPubKey = pki.publicKeyFromPem(this.pubPem);
        this.keySize = this.myPubKey.n.bitLength();
        console.log('[NoxiousCrypto] Existing Key Bits: ', this.keySize);
      } else {
        // key was not on disk, create a new one
        // generate an RSA key pair in steps that attempt to run for a specified period
        // of time on the main JS thread
        var state = rsa.createKeyPairGenerationState(this.newKeySize, 0x10001);
        var step = (function() {
          // run for 100 ms
          if(!rsa.stepKeyPairGenerationState(state, 1000)) {
            console.log('[NoxiousCrypto] Generating Key...');
            process.nextTick(step);
          }
          else {
            // done, turn off progress indicator, use state.keys
            console.log('[NoxiousCrypto] Key Generation Complete.');
            this.pubPem = pki.publicKeyToPem(state.keys.publicKey);
            keyData.set('privPem', pki.privateKeyToPem(state.keys.privateKey));
            keyData.set('pubPem', this.pubPem);
            this.myPrivKey = state.keys.privateKey;
            // make a public key, to be used for encryption
            this.myPubKey = state.keys.publicKey;
            this.keySize = this.newKeySize;
          }
        }).bind(this);
        // turn on progress indicator, schedule generation to run
        process.nextTick(step);
      }
    }
  }
  binEncrypt(plainText) {
    var keySizeBytes = Math.ceil(this.keySize/8);
    var buffer = new Buffer(plainText, 'utf8');
    var maxBufferSize = keySizeBytes - 42; //according to ursa documentation
    var bytesEncrypted = 0;
    var encryptedBuffersList = [];
    //loops through all data buffer encrypting piece by piece
    while(bytesEncrypted < buffer.length){
      //calculates next maximun length for temporary buffer and creates it
      var amountToCopy = Math.min(maxBufferSize, buffer.length - bytesEncrypted);
      var tempBuffer = new Buffer(amountToCopy);
      //copies next chunk of data to the temporary buffer
      buffer.copy(tempBuffer, 0, bytesEncrypted, bytesEncrypted + amountToCopy);
      //encrypts and stores current chunk
      var encryptedBuffer = this.myPubKey.encrypt(tempBuffer, 'RSA-OAEP');
      encryptedBuffersList.push(encryptedBuffer);
      bytesEncrypted += amountToCopy;
    }
    return Buffer.concat(encryptedBuffersList);
  }
  encrypt(plainText) {
    var keySizeBytes = Math.ceil(this.keySize/8);
    var buffer = new Buffer(plainText, 'utf8');
    var maxBufferSize = keySizeBytes - 42; //according to ursa documentation
    var bytesEncrypted = 0;
    var encryptedBuffersList = [];
    //loops through all data buffer encrypting piece by piece
    while(bytesEncrypted < buffer.length){
      //calculates next maximun length for temporary buffer and creates it
      var amountToCopy = Math.min(maxBufferSize, buffer.length - bytesEncrypted);
      var tempBuffer = new Buffer(amountToCopy);
      //copies next chunk of data to the temporary buffer
      buffer.copy(tempBuffer, 0, bytesEncrypted, bytesEncrypted + amountToCopy);
      //encrypts and stores current chunk
      var encryptedBuffer = new Buffer(this.myPubKey.encrypt(tempBuffer, 'RSA-OAEP'), 'binary');
      encryptedBuffersList.push(encryptedBuffer);
      bytesEncrypted += amountToCopy;
    }
    //encryptedBuffersList = [];
    //encryptedBuffersList.push(new Buffer('First Thing'));
    //encryptedBuffersList.push(new Buffer('Second Thing'));
    //console.log('[ebl] ', Buffer.concat(encryptedBuffersList).toString('base64'));
    // console.log('decrypted: ', decrypt(Buffer.concat(encryptedBuffersList).toString('base64')));
    return Buffer.concat(encryptedBuffersList).toString('base64');
    //return new Buffer(this.myPubKey.encrypt(plainText, 'RSA-OAEP'), binary).toString('base64');
  }
  binDecrypt(cipherText) {
    console.log('****** cipherText length: ', cipherText.length);
    var keySizeBytes = Math.ceil(this.keySize/8);
    var encryptedBuffer = new Buffer(binaryString, 'binary');
    console.log('****** ebuffer length: ', encryptedBuffer.length);
    var decryptedBuffers = [];
    //if the plain text was encrypted with a key of size N, the encrypted
    //result is a string formed by the concatenation of strings of N bytes long,
    //so we can find out how many substrings there are by diving the final result
    //size per N
    var totalBuffers = encryptedBuffer.length / keySizeBytes;
    //decrypts each buffer and stores result buffer in an array
    for(var i = 0 ; i < totalBuffers; i++){
      //copies next buffer chunk to be decrypted in a temp buffer
      var tempBuffer = new Buffer(keySizeBytes);
      encryptedBuffer.copy(tempBuffer, 0, i*keySizeBytes, (i+1)*keySizeBytes);
      //decrypts and stores current chunk
      var decryptedBuffer = this.myPrivKey.decrypt(tempBuffer, 'RSA-OAEP');
      // console.log('#### decrypted chunk: ', decryptpedBuffer.toString());
      decryptedBuffers.push(decryptedBuffer);
    }
    //concatenates all decrypted buffers and returns the corresponding String
    return Buffer.concat(decryptedBuffers).toString();
  }
  decrypt(cipherText) {
    console.log('****** base64 length: ', cipherText.length);
    var keySizeBytes = Math.ceil(this.keySize/8);
    var binaryString = new Buffer(cipherText, 'base64').toString('binary');
    console.log('****** binary length: ', binaryString.length);
    var encryptedBuffer = new Buffer(binaryString, 'binary');
    console.log('****** ebuffer length: ', encryptedBuffer.length);
    var decryptedBuffers = [];
    //if the plain text was encrypted with a key of size N, the encrypted
    //result is a string formed by the concatenation of strings of N bytes long,
    //so we can find out how many substrings there are by diving the final result
    //size per N
    var totalBuffers = encryptedBuffer.length / keySizeBytes;
    //decrypts each buffer and stores result buffer in an array
    for(var i = 0 ; i < totalBuffers; i++){
      //copies next buffer chunk to be decrypted in a temp buffer
      var tempBuffer = new Buffer(keySizeBytes);
      encryptedBuffer.copy(tempBuffer, 0, i*keySizeBytes, (i+1)*keySizeBytes);
      //decrypts and stores current chunk
      var decryptedBuffer = this.myPrivKey.decrypt(tempBuffer, 'RSA-OAEP');
      console.log('#### decrypted chunk: ', decryptpedBuffer.toString());
      decryptedBuffers.push(decryptedBuffer);
    }
    //concatenates all decrypted buffers and returns the corresponding String
    return Buffer.concat(decryptedBuffers).toString();
  }
  signString(data) {
    //let signature = this.myPrivKey.hashAndSign('sha256' , new Buffer(data) , undefined, 'base64', true);
    var md = forge.md.sha1.create();
    md.update(data, 'utf8');
    var pss = forge.pss.create({
      md: forge.md.sha1.create(),
      mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
      saltLength: 20
      // optionally pass 'prng' with a custom PRNG implementation
      // optionalls pass 'salt' with a forge.util.ByteBuffer w/custom salt
    });
    // return new Buffer(this.myPrivKey.sign(md, pss)).toString('base64');
    return new Buffer(this.myPrivKey.sign(md, pss), 'binary').toString('base64');
  }
  signatureVerified(data, signature) {
    // verify RSASSA-PSS signature
    let pss = forge.pss.create({
      md: forge.md.sha1.create(),
      mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
      saltLength: 20
      // optionally pass 'prng' with a custom PRNG implementation
    });
    var md = forge.md.sha1.create();
    md.update(data, 'utf8');
    // return this.myPubKey.verify(md.digest().getBytes(), new Buffer(signature).toString('binary'), pss);
    return this.myPubKey.verify(md.digest().getBytes(), new Buffer(signature, 'base64').toString('binary'), pss);
  }
}

module.exports = NoxiousCrypto;
<commit_msg>Save working experiment!!<commit_after>"use strict";

var
  DataFile = require('./DataFile'),
  forge = require('node-forge'),
  rsa = require('node-forge').pki.rsa,
  pki = require('node-forge').pki;

class NoxiousCrypto{
  constructor(obj) {
    this.myPrivKey = null;
    this.pubPem = null;
    this.myPubKey = null;
    this.keySize=0;
    // default size for new keys
    this.newKeySize=3072;

    // accepts either dir, filename or public key
    if(obj['pubPem']) {
      // object has public Key
      this.pubPem = obj.pubPem;
      this.myPubKey = pki.publicKeyFromPem(this.pubPem);
      this.keySize = this.myPubKey.n.bitLength();
    } else {
      // assume it's a dataDir and filename
      let keyData = new DataFile(obj.path);
      if(keyData.has('privPem')) {
        // key already exists
        this.myPrivKey = pki.privateKeyFromPem(keyData.get('privPem'));
        this.pubPem = keyData.get('pubPem');
        this.myPubKey = pki.publicKeyFromPem(this.pubPem);
        this.keySize = this.myPubKey.n.bitLength();
        console.log('[NoxiousCrypto] Existing Key Bits: ', this.keySize);
      } else {
        // key was not on disk, create a new one
        // generate an RSA key pair in steps that attempt to run for a specified period
        // of time on the main JS thread
        var state = rsa.createKeyPairGenerationState(this.newKeySize, 0x10001);
        var step = (function() {
          // run for 100 ms
          if(!rsa.stepKeyPairGenerationState(state, 1000)) {
            console.log('[NoxiousCrypto] Generating Key...');
            process.nextTick(step);
          }
          else {
            // done, turn off progress indicator, use state.keys
            console.log('[NoxiousCrypto] Key Generation Complete.');
            this.pubPem = pki.publicKeyToPem(state.keys.publicKey);
            keyData.set('privPem', pki.privateKeyToPem(state.keys.privateKey));
            keyData.set('pubPem', this.pubPem);
            this.myPrivKey = state.keys.privateKey;
            // make a public key, to be used for encryption
            this.myPubKey = state.keys.publicKey;
            this.keySize = this.newKeySize;
          }
        }).bind(this);
        // turn on progress indicator, schedule generation to run
        process.nextTick(step);
      }
    }
  }
  binEncrypt(plainText) {
    var keySizeBytes = Math.ceil(this.keySize/8);
    var buffer = new Buffer(plainText, 'utf8');
    var maxBufferSize = keySizeBytes - 42; //according to ursa documentation
    var bytesEncrypted = 0;
    var encryptedBuffersList = [];
    //loops through all data buffer encrypting piece by piece
    while(bytesEncrypted < buffer.length){
      //calculates next maximun length for temporary buffer and creates it
      var amountToCopy = Math.min(maxBufferSize, buffer.length - bytesEncrypted);
      var tempBuffer = new Buffer(amountToCopy);
      //copies next chunk of data to the temporary buffer
      buffer.copy(tempBuffer, 0, bytesEncrypted, bytesEncrypted + amountToCopy);
      //encrypts and stores current chunk
      var encryptedBuffer = new Buffer(this.myPubKey.encrypt(tempBuffer, 'RSA-OAEP'), 'binary');
      console.log("encryptedBuffer is a Buffer : ", Buffer.isBuffer(encryptedBuffer));
      encryptedBuffersList.push(encryptedBuffer);
      bytesEncrypted += amountToCopy;
    }
    //let something = new Buffer(encryptedBuffersList.length * keySizeBytes);
    //let bufferCount = 0;
    return Buffer.concat(encryptedBuffersList).toString('base64');
    //console.log("BufferListSize :", encryptedBuffersList.length);
    //for(let buf of encryptedBuffersList) {
    //  console.log("Length of THIS buffer : ", buf.length);
    //  buf.copy(something, bufferCount * keySizeBytes);
    //  bufferCount++;
    //}
    //console.log('Length of Something: ', something.length);
  }
  encrypt(plainText) {
    var keySizeBytes = Math.ceil(this.keySize/8);
    var buffer = new Buffer(plainText, 'utf8');
    var maxBufferSize = keySizeBytes - 42; //according to ursa documentation
    var bytesEncrypted = 0;
    var encryptedBuffersList = [];
    //loops through all data buffer encrypting piece by piece
    while(bytesEncrypted < buffer.length){
      //calculates next maximun length for temporary buffer and creates it
      var amountToCopy = Math.min(maxBufferSize, buffer.length - bytesEncrypted);
      var tempBuffer = new Buffer(amountToCopy);
      //copies next chunk of data to the temporary buffer
      buffer.copy(tempBuffer, 0, bytesEncrypted, bytesEncrypted + amountToCopy);
      //encrypts and stores current chunk
      var encryptedBuffer = new Buffer(this.myPubKey.encrypt(tempBuffer, 'RSA-OAEP'), 'binary');
      encryptedBuffersList.push(encryptedBuffer);
      bytesEncrypted += amountToCopy;
    }
    //encryptedBuffersList = [];
    //encryptedBuffersList.push(new Buffer('First Thing'));
    //encryptedBuffersList.push(new Buffer('Second Thing'));
    //console.log('[ebl] ', Buffer.concat(encryptedBuffersList).toString('base64'));
    // console.log('decrypted: ', decrypt(Buffer.concat(encryptedBuffersList).toString('base64')));
    // Works for binary
    // return Buffer.concat(encryptedBuffersList);
    return Buffer.concat(encryptedBuffersList).toString('base64');
    //return new Buffer(this.myPubKey.encrypt(plainText, 'RSA-OAEP'), binary).toString('base64');
  }
  binDecrypt(cipherText) {
    console.log('****** cipherText length: ', cipherText.length);
    var keySizeBytes = Math.ceil(this.keySize/8);
    // works for binary
    //var encryptedBuffer = new Buffer(cipherText, 'binary');
    var encryptedBuffer = new Buffer(cipherText, 'base64');
    console.log('****** ebuffer length: ', encryptedBuffer.length);
    var decryptedBuffers = [];
    //if the plain text was encrypted with a key of size N, the encrypted
    //result is a string formed by the concatenation of strings of N bytes long,
    //so we can find out how many substrings there are by diving the final result
    //size per N
    var totalBuffers = encryptedBuffer.length / keySizeBytes;
    //decrypts each buffer and stores result buffer in an array
    for(var i = 0 ; i < totalBuffers; i++){
      //copies next buffer chunk to be decrypted in a temp buffer
      var tempBuffer = new Buffer(keySizeBytes);
      encryptedBuffer.copy(tempBuffer, 0, i*keySizeBytes, (i+1)*keySizeBytes);
      //decrypts and stores current chunk
      var decryptedBuffer = this.myPrivKey.decrypt(tempBuffer.toString('binary'), 'RSA-OAEP');
      // console.log('#### decrypted chunk: ', decryptpedBuffer.toString());
      decryptedBuffers.push(new Buffer(decryptedBuffer, 'utf8'));
    }
    //concatenates all decrypted buffers and returns the corresponding String
    return Buffer.concat(decryptedBuffers).toString();
  }
  decrypt(cipherText) {
    console.log('****** base64 length: ', cipherText.length);
    var keySizeBytes = Math.ceil(this.keySize/8);
    var binaryString = new Buffer(cipherText, 'base64').toString('binary');
    console.log('****** binary length: ', binaryString.length);
    var encryptedBuffer = new Buffer(binaryString, 'binary');
    console.log('****** ebuffer length: ', encryptedBuffer.length);
    var decryptedBuffers = [];
    //if the plain text was encrypted with a key of size N, the encrypted
    //result is a string formed by the concatenation of strings of N bytes long,
    //so we can find out how many substrings there are by diving the final result
    //size per N
    var totalBuffers = encryptedBuffer.length / keySizeBytes;
    //decrypts each buffer and stores result buffer in an array
    for(var i = 0 ; i < totalBuffers; i++){
      //copies next buffer chunk to be decrypted in a temp buffer
      var tempBuffer = new Buffer(keySizeBytes);
      encryptedBuffer.copy(tempBuffer, 0, i*keySizeBytes, (i+1)*keySizeBytes);
      //decrypts and stores current chunk
      var decryptedBuffer = this.myPrivKey.decrypt(tempBuffer, 'RSA-OAEP');
      console.log('#### decrypted chunk: ', decryptpedBuffer.toString());
      decryptedBuffers.push(decryptedBuffer);
    }
    //concatenates all decrypted buffers and returns the corresponding String
    return Buffer.concat(decryptedBuffers).toString();
  }
  signString(data) {
    //let signature = this.myPrivKey.hashAndSign('sha256' , new Buffer(data) , undefined, 'base64', true);
    var md = forge.md.sha1.create();
    md.update(data, 'utf8');
    var pss = forge.pss.create({
      md: forge.md.sha1.create(),
      mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
      saltLength: 20
      // optionally pass 'prng' with a custom PRNG implementation
      // optionalls pass 'salt' with a forge.util.ByteBuffer w/custom salt
    });
    // return new Buffer(this.myPrivKey.sign(md, pss)).toString('base64');
    return new Buffer(this.myPrivKey.sign(md, pss), 'binary').toString('base64');
  }
  signatureVerified(data, signature) {
    // verify RSASSA-PSS signature
    let pss = forge.pss.create({
      md: forge.md.sha1.create(),
      mgf: forge.mgf.mgf1.create(forge.md.sha1.create()),
      saltLength: 20
      // optionally pass 'prng' with a custom PRNG implementation
    });
    var md = forge.md.sha1.create();
    md.update(data, 'utf8');
    // return this.myPubKey.verify(md.digest().getBytes(), new Buffer(signature).toString('binary'), pss);
    return this.myPubKey.verify(md.digest().getBytes(), new Buffer(signature, 'base64').toString('binary'), pss);
  }
}

module.exports = NoxiousCrypto;
<|endoftext|>


<commit_before>(function ($) {

  function handleUndefinedOrZeroMetric(metric) {
    return metric == undefined || metric == 0 ? 'None' : metric;
  }

  function appendOrRemoveLink(link, metric) {
    metric = handleUndefinedOrZeroMetric(metric);
    if (metric == 'None') {
      link.html(link.html() + ' ' + metric);
      link.contents().unwrap();
    } else {
      link.append(metric);
    }
  }

  $(document).ready(function() {

    $( ".datepicker" ).datepicker({
      changeMonth: true,
      changeYear: true,
      maxDate: '0',
      dateFormat: 'yy-mm-dd',
      yearRange: "2003:+0"
    });

    //Start Date max date is the entered End Date. End Date min date is the entered Start Date.
    //Both Start and End Dates have a strict maximum of the current day
    $('#dateFilterStartDate').change(function(){
      var minDate = $(this).val() ? $(this).val() : '';
      $('#dateFilterEndDate').datepicker('option', 'minDate', minDate)
    });

    $('#dateFilterEndDate').change(function(){
      var maxDate = $(this).val() ? $(this).val() : '0';
      $('#dateFilterStartDate').datepicker('option', 'maxDate', maxDate)
    });

    $('.search-results-alm').each(function () {
      var $alm = $(this);
      var doi = $alm.data('doi');
      $alm.getArticleSummary(doi, function (almData) { //success function

        var almLinks = $alm.find('a');
        var viewsLink = $(almLinks[0]);
        appendOrRemoveLink(viewsLink, almData.viewed);

        var citationsLink = $(almLinks[1]);
        appendOrRemoveLink(citationsLink, almData.cited);

        var savesLink = $(almLinks[2]);
        appendOrRemoveLink(savesLink, almData.saved);

        var sharesLink = $(almLinks[3]);
        appendOrRemoveLink(sharesLink, almData.shared);

        $alm.siblings('.search-results-alm-loading').fadeOut('slow', function () {
          $alm.fadeIn();
        })
      }, function () { //error function
        $alm.siblings('.search-results-alm-loading').fadeOut('slow', function () {
          $alm.siblings('.search-results-alm-error').fadeIn();
        })
      });
    });

    $('#sortOrder').on('change', function() {
      $(this).parents('form').submit();
    });

    $('#resultsPerPageDropdown').on('change', function() {

      // Due to the way the CSS for the page currently works, it's difficult to have the <form>
      // extend all the way down to this dropdown, so we instead set a hidden field here.
      $('#resultsPerPage').val($('#resultsPerPageDropdown').val());
      $('#searchControlBarForm').submit();
    });

    // Code to make tooltips around disabled controls work on tablets.

    var $visibleTooltip;

    $('[data-js-tooltip-hover=trigger]').on('click', function() {

      // Since the two icons are close together, we have to hide the adjacent
      // tooltip first if it is visible.
      if ($visibleTooltip !== undefined) {
        $visibleTooltip.removeClass('visible');
      }
      $visibleTooltip = $(this).find('[data-js-tooltip-hover=target]');
      $visibleTooltip.addClass('visible');

      // handling click on whole document is not working, and
      // probably not a good idea anyway. So I am auto-hiding the
      // tooltip after a timeout.
      var $toolTip = $visibleTooltip;
      setTimeout(function() {
        $toolTip.removeClass('visible');
      }, 5000);
    });
  });

  // initialize toggle for search filter item list
  plos_toggle.init();

})(jQuery);<commit_msg>DPRO-1673 - add link to checkbox as well<commit_after>(function ($) {

  function handleUndefinedOrZeroMetric(metric) {
    return metric == undefined || metric == 0 ? 'None' : metric;
  }

  function appendOrRemoveLink(link, metric) {
    metric = handleUndefinedOrZeroMetric(metric);
    if (metric == 'None') {
      link.html(link.html() + ' ' + metric);
      link.contents().unwrap();
    } else {
      link.append(metric);
    }
  }

  $(document).ready(function() {

    $( ".datepicker" ).datepicker({
      changeMonth: true,
      changeYear: true,
      maxDate: '0',
      dateFormat: 'yy-mm-dd',
      yearRange: "2003:+0"
    });

    //Start Date max date is the entered End Date. End Date min date is the entered Start Date.
    //Both Start and End Dates have a strict maximum of the current day
    $('#dateFilterStartDate').change(function(){
      var minDate = $(this).val() ? $(this).val() : '';
      $('#dateFilterEndDate').datepicker('option', 'minDate', minDate)
    });

    $('#dateFilterEndDate').change(function(){
      var maxDate = $(this).val() ? $(this).val() : '0';
      $('#dateFilterStartDate').datepicker('option', 'maxDate', maxDate)
    });

    $('.search-results-alm').each(function () {
      var $alm = $(this);
      var doi = $alm.data('doi');
      $alm.getArticleSummary(doi, function (almData) { //success function

        var almLinks = $alm.find('a');
        var viewsLink = $(almLinks[0]);
        appendOrRemoveLink(viewsLink, almData.viewed);

        var citationsLink = $(almLinks[1]);
        appendOrRemoveLink(citationsLink, almData.cited);

        var savesLink = $(almLinks[2]);
        appendOrRemoveLink(savesLink, almData.saved);

        var sharesLink = $(almLinks[3]);
        appendOrRemoveLink(sharesLink, almData.shared);

        $alm.siblings('.search-results-alm-loading').fadeOut('slow', function () {
          $alm.fadeIn();
        })
      }, function () { //error function
        $alm.siblings('.search-results-alm-loading').fadeOut('slow', function () {
          $alm.siblings('.search-results-alm-error').fadeIn();
        })
      });
    });

    $('#sortOrder').on('change', function() {
      $(this).parents('form').submit();
    });

    $('#resultsPerPageDropdown').on('change', function() {

      // Due to the way the CSS for the page currently works, it's difficult to have the <form>
      // extend all the way down to this dropdown, so we instead set a hidden field here.
      $('#resultsPerPage').val($('#resultsPerPageDropdown').val());
      $('#searchControlBarForm').submit();
    });

    // Code to make tooltips around disabled controls work on tablets.

    var $visibleTooltip;

    $('[data-js-tooltip-hover=trigger]').on('click', function() {

      // Since the two icons are close together, we have to hide the adjacent
      // tooltip first if it is visible.
      if ($visibleTooltip !== undefined) {
        $visibleTooltip.removeClass('visible');
      }
      $visibleTooltip = $(this).find('[data-js-tooltip-hover=target]');
      $visibleTooltip.addClass('visible');

      // handling click on whole document is not working, and
      // probably not a good idea anyway. So I am auto-hiding the
      // tooltip after a timeout.
      var $toolTip = $visibleTooltip;
      setTimeout(function() {
        $toolTip.removeClass('visible');
      }, 5000);
    });
  });
  // making the checkbox also link in the filter column
  $('#searchFilters li a input').on('change',function(){
    var filterlink = $(this).parent('a').attr('href');
     window.location.assign(filterlink);
  })

  // initialize toggle for search filter item list
  plos_toggle.init();

})(jQuery);<|endoftext|>


<commit_before>import _ from 'lodash';
import React from 'react';
import stripesForm from '@folio/stripes-form';
import { FieldArray } from 'redux-form';
import { Row, Col } from 'react-flexbox-grid';
import PropTypes from 'prop-types';

import Button from '../../Button';
import List from '../../List';
import EditableItem from './EditableItem';

const propTypes = {
  /**
   * The text for the H3 tag in the header of the component
   */
  label: PropTypes.string,
  /**
   * Label for the 'Add' button
   */
  createButtonLabel: PropTypes.string,
  /**
  * Initial form values
  */
  initialValues: PropTypes.object, // eslint-disable-line react/no-unused-prop-types
  /**
   * Callback for saving editted list items.
   */
  onUpdate: PropTypes.func,
  /**
   * Callback for creating new list items.
   */
  onCreate: PropTypes.func,
  /**
   * Callback for list item deletion.
   */
  onDelete: PropTypes.func,
  /**
   * Array of fields to render. These will also be editable.
   */
  visibleFields: PropTypes.arrayOf(React.PropTypes.string).isRequired,
  /**
   * Object that reflects the shape of list item objects. values should be strings
   * indicating the type: {name:'string'}
   * This is used to create new items.
   */
  itemTemplate: PropTypes.object.isRequired,
  /**
   * Fieldname that includes the unique identifier for the list.
   */
  uniqueField: PropTypes.string.isRequired,
  /**
   * Object containing properties of list action names: 'delete', 'edit' and
   * values of sentinel functions that return booleans based on object
   * properties" { delete: (item) => {return (!item.item.inUse)} }
   */
  actionSuppression: PropTypes.object,
   /**
   * Message to display for an empty list.
   */
  isEmptyMessage: PropTypes.string,
};

const defaultProps = {
  createButtonLabel: '+ Add Item',
  uniqueField: 'id',
  actionSuppression: { delete: () => false, edit: () => false },
};

class EditableListForm extends React.Component {

  constructor(props) {
    super(props);
    this.renderItems = this.renderItems.bind(this);
  }

  onAdd(fields) {
    const { itemTemplate } = this.props;
    const item = { editable: true };
    Object.keys(itemTemplate).forEach(key => (item[key] = ''));
    fields.unshift(item);
  }

  onCancel(fields, index) {
    const { uniqueField, initialValues } = this.props;
    const item = fields.get(index);

    if (item[uniqueField]) {
      const prevItem = initialValues.items[index];
      this.toggleEdit(fields, prevItem, index, false);
    } else {
      fields.remove(index);
    }
  }

  onSave(fields, index) {
    const item = fields.get(index);
    const callback = (item.id) ?
      this.props.onUpdate :
      this.props.onCreate;

    this.toggleEdit(fields, item, index, false);
    callback(_.omit(item, 'editable'));
  }

  onEdit(fields, index) {
    const item = fields.get(index);
    this.toggleEdit(fields, item, index, true);
  }

  onDelete(fields, index) {
    const { uniqueField } = this.props;
    const item = fields.get(index);
    fields.remove(index);
    this.props.onDelete(item[uniqueField]);
  }

  // eslint-disable-next-line class-methods-use-this
  toggleEdit(fields, item, index, editable) {
    fields.remove(index);
    item.editable = editable;
    fields.insert(index, item);
  }

  ItemFormatter = (field, index, fields) => (
    <li style={{ display: 'block' }} key={`item-${index}`}>
      <EditableItem
        key={index}
        field={field}
        item={fields.get(index)}
        actionSuppression={this.props.actionSuppression}
        visibleFields={this.props.visibleFields}
        onCancel={() => this.onCancel(fields, index)}
        onSave={() => this.onSave(fields, index)}
        onEdit={() => this.onEdit(fields, index)}
        onDelete={() => this.onDelete(fields, index)}
      />

  );

  renderItems({ fields }) {
    return (

        <Row between="xs">
          <Col xs>
{this.props.label}
</Col>
          <Col xs>
            <Row end="xs">
              <Col xs>
                <Button onClick={() => this.onAdd(fields)}>
                  {this.props.createButtonLabel}
                </Button>
              </Col>
            </Row>
          </Col>
        </Row>
        <Row>
          <Col xs={12}>
            <List
              items={fields}
              itemFormatter={this.ItemFormatter}
              isEmptyMessage={this.props.isEmptyMessage}
            />
          </Col>
        </Row>

    );
  }

  render() {
    return (
      <form>
        <FieldArray name="items" component={this.renderItems} />
      </form>
    );
  }
}

EditableListForm.propTypes = propTypes;
EditableListForm.defaultProps = defaultProps;

export default stripesForm({
  form: 'editableListForm',
  navigationCheck: true,
  enableReinitialize: true,
  keepDirtyOnReinitialize: true,
})(EditableListForm);
<commit_msg>Finalize work on EditableList<commit_after>import _ from 'lodash';
import React from 'react';
import stripesForm from '@folio/stripes-form';
import { FieldArray } from 'redux-form';
import { Row, Col } from 'react-flexbox-grid';
import PropTypes from 'prop-types';

import Button from '../../Button';
import List from '../../List';
import EditableItem from './EditableItem';

const propTypes = {
  /**
   * The text for the H3 tag in the header of the component
   */
  label: PropTypes.string,
  /**
   * Label for the 'Add' button
   */
  createButtonLabel: PropTypes.string,
  /**
  * Initial form values
  */
  initialValues: PropTypes.object, // eslint-disable-line react/no-unused-prop-types
  /**
   * Callback for saving editted list items.
   */
  onUpdate: PropTypes.func,
  /**
   * Callback for creating new list items.
   */
  onCreate: PropTypes.func,
  /**
   * Callback for list item deletion.
   */
  onDelete: PropTypes.func,
  /**
   * Array of fields to render. These will also be editable.
   */
  visibleFields: PropTypes.arrayOf(React.PropTypes.string).isRequired,
  /**
   * Object that reflects the shape of list item objects. values should be strings
   * indicating the type: {name:'string'}
   * This is used to create new items.
   */
  itemTemplate: PropTypes.object.isRequired,
  /**
   * Fieldname that includes the unique identifier for the list.
   */
  uniqueField: PropTypes.string.isRequired,
  /**
   * Object containing properties of list action names: 'delete', 'edit' and
   * values of sentinel functions that return booleans based on object
   * properties" { delete: (item) => {return (!item.item.inUse)} }
   */
  actionSuppression: PropTypes.object,
   /**
   * Message to display for an empty list.
   */
  isEmptyMessage: PropTypes.string,
};

const defaultProps = {
  createButtonLabel: '+ Add Item',
  uniqueField: 'id',
  actionSuppression: { delete: () => false, edit: () => false },
};

class EditableListForm extends React.Component {

  constructor(props) {
    super(props);
    this.renderItems = this.renderItems.bind(this);
  }

  onAdd(fields) {
    const { itemTemplate } = this.props;
    const item = { editable: true };
    Object.keys(itemTemplate).forEach(key => (item[key] = ''));
    fields.unshift(item);
  }

  onCancel(fields, index) {
    const { uniqueField, initialValues } = this.props;
    const item = fields.get(index);

    if (item[uniqueField]) {
      const prevItem = initialValues.items[index];
      this.toggleEdit(fields, prevItem, index, false);
    } else {
      fields.remove(index);
    }
  }

  onSave(fields, index) {
    const item = fields.get(index);
    const callback = (item.id) ?
      this.props.onUpdate :
      this.props.onCreate;

    this.toggleEdit(fields, item, index, false);
    callback(_.omit(item, 'editable'));
  }

  onEdit(fields, index) {
    const item = fields.get(index);
    this.toggleEdit(fields, item, index, true);
  }

  onDelete(fields, index) {
    const { uniqueField } = this.props;
    const item = fields.get(index);
    fields.remove(index);
    this.props.onDelete(item[uniqueField]);
  }

  // eslint-disable-next-line class-methods-use-this
  toggleEdit(fields, item, index, editable) {
    fields.remove(index);
    item.editable = editable;
    fields.insert(index, item);
  }

  ItemFormatter = (field, index, fields) => (
    <li style={{ display: 'block' }} key={`item-${index}`}>
      <EditableItem
        key={index}
        field={field}
        item={fields.get(index)}
        actionSuppression={this.props.actionSuppression}
        visibleFields={this.props.visibleFields}
        onCancel={() => this.onCancel(fields, index)}
        onSave={() => this.onSave(fields, index)}
        onEdit={() => this.onEdit(fields, index)}
        onDelete={() => this.onDelete(fields, index)}
      />

  );

  renderItems({ fields }) {
    return (

        <Row between="xs">
          <Col xs>
{this.props.label}
</Col>
          <Col xs>
            <Row end="xs">
              <Col xs>
                <Button onClick={() => this.onAdd(fields)}>
                  {this.props.createButtonLabel}
                </Button>
              </Col>
            </Row>
          </Col>
        </Row>
        <Row>
          <Col xs={12}>
            <List
              items={fields}
              itemFormatter={this.ItemFormatter}
              isEmptyMessage={this.props.isEmptyMessage}
            />
          </Col>
        </Row>

    );
  }

  render() {
    return (
      <form>
        <FieldArray name="items" component={this.renderItems} />
      </form>
    );
  }
}

EditableListForm.propTypes = propTypes;
EditableListForm.defaultProps = defaultProps;

export default stripesForm({
  form: 'editableListForm',
  navigationCheck: true,
  enableReinitialize: true,
})(EditableListForm);
<|endoftext|>