<commit_before>define([
    "jquery",
    "base1/cockpit"
], function($, cockpit) {

    /* SERVICE MANAGEMENT API
     *
     * The "system/service" module lets you monitor and manage a
     * system service on localhost in a simple way.
     *
     * It mainly exists because talking to the systemd D-Bus API is
     * not trivial enough to do it directly.
     *
     * - proxy = service.proxy(name)
     *
     * Create a proxy that represents the service named NAME.
     *
     * The proxy has properties and methods (described below) that
     * allow you to monitor the state of the service, and perform
     * simple actions on it.
     *
     * Initially, any of the properties can be "null" until their
     * actual values have been retrieved in the background.
     *
     * - $(proxy).on('changed', function (event) { ... })
     *
     * The 'changed' event is emitted whenever one of the properties
     * of the proxy changes.
     *
     * - proxy.exists
     *
     * A boolean that tells whether the service is known or not.  A
     * proxy with 'exists == false' will have 'state == undefined' and
     * 'enabled == undefined'.
     *
     * - proxy.state
     *
     * Either 'undefined' when the state can't be retrieved, or a
     * string that has one of the values "starting", "running",
     * "stopping", "stopped", or "failed".
     *
     * - proxy.enabled
     *
     * Either 'undefined' when the value can't be retrieved, or a
     * boolean that tells whether the service is started 'enabled'.
     * What it means exactly for a service to be enabled depends on
     * the service, but a enabled service is usually started on boot,
     * no matter wether other services need it or not.  A disabled
     * service is usually only started when it is needed by some other
     * service.
     *
     * - proxy.unit
     * - proxy.service
     *
     * The raw org.freedesktop.systemd1.Unit and Service D-Bus
     * interface proxies for the service.
     *
     * - promise = proxy.start()
     *
     * Start the service.  The return value is a standard jQuery
     * promise as returned from DBusClient.call.
     *
     * - promise = proxy.stop()
     *
     * Stop the service.
     *
     * - promise = proxy.enable()
     *
     * Enable the service.
     *
     * - promise = proxy.disable()
     *
     * Disable the service.
     */

    var systemd_client;
    var systemd_manager;

    function wait_valid(proxy, callback) {
        proxy.wait(function() {
            if (proxy.valid)
                callback();
        });
    }

    function with_systemd_manager(done) {
        if (!systemd_manager) {
            systemd_client = cockpit.dbus("org.freedesktop.systemd1", { superuser: "try" });
            systemd_manager = systemd_client.proxy("org.freedesktop.systemd1.Manager",
                                                   "/org/freedesktop/systemd1");
            wait_valid(systemd_manager, function() {
                systemd_manager.Subscribe().
                    fail(function (error) {
                        if (error.name != "org.freedesktop.systemd1.AlreadySubscribed" &&
                            error.name != "org.freedesktop.DBus.Error.FileExists")
                            console.warn("Subscribing to systemd signals failed", error);
                    });
            });
        }
        wait_valid(systemd_manager, done);
    }

    function proxy(name) {
        var self = {
            exists: null,
            state: null,
            enabled: null,

            start: start,
            stop: stop,
            restart: restart,

            enable: enable,
            disable: disable
        };

        var unit, service;

        if (name.indexOf(".") == -1)
            name = name + ".service";

        function update_from_unit() {
            self.exists = (unit.LoadState != "not-found" || unit.ActiveState != "inactive");

            if (unit.ActiveState == "activating")
                self.state = "starting";
            else if (unit.ActiveState == "deactivating")
                self.state = "stopping";
            else if (unit.ActiveState == "active" || unit.ActiveState == "reloading")
                self.state = "running";
            else if (unit.ActiveState == "failed")
                self.state = "failed";
            else if (unit.ActiveState == "inactive" && self.exists)
                self.state = "stopped";
            else
                self.state = undefined;

            if (unit.UnitFileState == "enabled" || unit.UnitFileState == "linked")
                self.enabled = true;
            else if (unit.UnitFileState == "disabled" || unit.UnitFileState == "masked")
                self.enabled = false;
            else
                self.enabled = undefined;

            self.unit = unit;

            $(self).triggerHandler('changed');
        }

        function update_from_service() {
            self.service = service;
            $(self).triggerHandler('changed');
        }

        with_systemd_manager(function () {
            systemd_manager.LoadUnit(name).
                done(function (path) {
                    unit = systemd_client.proxy('org.freedesktop.systemd1.Unit', path);
                    $(unit).on('changed', update_from_unit);
                    wait_valid(unit, update_from_unit);

                    service = systemd_client.proxy('org.freedesktop.systemd1.Service', path);
                    $(service).on('changed', update_from_service);
                    wait_valid(service, update_from_service);
                }).
                fail(function (error) {
                    self.exists = false;
                    $(self).triggerHandler('changed');
                });
        });

        function refresh() {
            if (!unit || !service)
                return;

            function refresh_interface(path, iface) {
                systemd_client.call(path,
                                    "org.freedesktop.DBus.Properties", "GetAll", [ iface ]).
                    fail(function (error) {
                        console.log(error);
                    }).
                    done(function (result) {
                        var props = { };
                        for (var p in result[0])
                            props[p] = result[0][p].v;
                        var ifaces = { };
                        ifaces[iface] = props;
                        var data = { };
                        data[unit.path] = ifaces;
                        systemd_client.notify(data);
                    });
            }

            refresh_interface(unit.path, "org.freedesktop.systemd1.Unit");
            refresh_interface(service.path, "org.freedesktop.systemd1.Service");
        }

        /* HACK - https://bugs.freedesktop.org/show_bug.cgi?id=69575
         *
         * We need to explicitly get new property values when getting
         * a UnitNew signal since UnitNew doesn't carry them.
         * However, reacting to UnitNew with GetAll could lead to an
         * infinite loop since systemd emits a UnitNew in reaction to
         * GetAll for units that it doesn't want to keep loaded, such
         * as units without unit files.
         *
         * So we ignore UnitNew and instead assume that the unit state
         * only changes in interesting ways when there is a job for it
         * or when the daemon is reloaded (or when we get a property
         * change notification, of course).
         */

        if (false) {
            // This is what we want to do.

            $(systemd_manager).on("UnitNew", function (event, unit_id, path) {
                if (unit_id == name)
                    refresh();
            });
        } else {
            // This is what we have to do.

            $(systemd_manager).on("Reloading", function (event, reloading) {
                if (!reloading)
                    refresh();
            });

            $(systemd_manager).on("JobNew JobRemoved", function (event, number, path, unit_id, result) {
                if (unit_id == name)
                    refresh();
            });
        }

        /* Actions
         *
         * We don't call methods on the D-Bus proxies here since they
         * might not be ready when these functions are called.
         */

        var pending_jobs = { };

        $(systemd_manager).on("JobRemoved", function (event, number, path, unit_id, result) {
            if (pending_jobs[path]) {
                if (result == "done")
                    pending_jobs[path].resolve();
                else
                    pending_jobs[path].reject(result);
                delete pending_jobs[path];
            }
        });

        function call_manager(method, args) {
            return systemd_client.call("/org/freedesktop/systemd1",
                                       "org.freedesktop.systemd1.Manager",
                                       method, args);
        }

        function call_manager_with_job(method, args) {
            var dfd = $.Deferred();
            call_manager(method, args).
                done(function (results) {
                    var path = results[0];
                    pending_jobs[path] = dfd;
                }).
                fail(function (error) {
                    dfd.reject(error);
                });
            return dfd.promise();
        }

        function call_manager_with_reload(method, args) {
            return call_manager(method, args).then(function () {
                return call_manager("Reload", [ ]);
            });
        }

        function start() {
            return call_manager_with_job("StartUnit", [ name, "replace" ]);
        }

        function stop() {
            return call_manager_with_job("StopUnit", [ name, "replace" ]);
        }

        function restart() {
            return call_manager_with_job("RestartUnit", [ name, "replace" ]);
        }

        function enable() {
            return call_manager_with_reload("EnableUnitFiles", [ [ name ], false, false ]);
        }

        function disable() {
            return call_manager_with_reload("DisableUnitFiles", [ [ name ], false ]);
        }

        return self;
    }

    return {
        proxy: proxy
    };
});
<commit_msg>systemd: Add 'wait' functionality to service proxy.<commit_after>define([
    "jquery",
    "base1/cockpit"
], function($, cockpit) {

    /* SERVICE MANAGEMENT API
     *
     * The "system/service" module lets you monitor and manage a
     * system service on localhost in a simple way.
     *
     * It mainly exists because talking to the systemd D-Bus API is
     * not trivial enough to do it directly.
     *
     * - proxy = service.proxy(name)
     *
     * Create a proxy that represents the service named NAME.
     *
     * The proxy has properties and methods (described below) that
     * allow you to monitor the state of the service, and perform
     * simple actions on it.
     *
     * Initially, any of the properties can be "null" until their
     * actual values have been retrieved in the background.
     *
     * - $(proxy).on('changed', function (event) { ... })
     *
     * The 'changed' event is emitted whenever one of the properties
     * of the proxy changes.
     *
     * - proxy.exists
     *
     * A boolean that tells whether the service is known or not.  A
     * proxy with 'exists == false' will have 'state == undefined' and
     * 'enabled == undefined'.
     *
     * - proxy.state
     *
     * Either 'undefined' when the state can't be retrieved, or a
     * string that has one of the values "starting", "running",
     * "stopping", "stopped", or "failed".
     *
     * - proxy.enabled
     *
     * Either 'undefined' when the value can't be retrieved, or a
     * boolean that tells whether the service is started 'enabled'.
     * What it means exactly for a service to be enabled depends on
     * the service, but a enabled service is usually started on boot,
     * no matter wether other services need it or not.  A disabled
     * service is usually only started when it is needed by some other
     * service.
     *
     * - proxy.unit
     * - proxy.service
     *
     * The raw org.freedesktop.systemd1.Unit and Service D-Bus
     * interface proxies for the service.
     *
     * - promise = proxy.start()
     *
     * Start the service.  The return value is a standard jQuery
     * promise as returned from DBusClient.call.
     *
     * - promise = proxy.stop()
     *
     * Stop the service.
     *
     * - promise = proxy.enable()
     *
     * Enable the service.
     *
     * - promise = proxy.disable()
     *
     * Disable the service.
     */

    var systemd_client;
    var systemd_manager;

    function wait_valid(proxy, callback) {
        proxy.wait(function() {
            if (proxy.valid)
                callback();
        });
    }

    function with_systemd_manager(done) {
        if (!systemd_manager) {
            systemd_client = cockpit.dbus("org.freedesktop.systemd1", { superuser: "try" });
            systemd_manager = systemd_client.proxy("org.freedesktop.systemd1.Manager",
                                                   "/org/freedesktop/systemd1");
            wait_valid(systemd_manager, function() {
                systemd_manager.Subscribe().
                    fail(function (error) {
                        if (error.name != "org.freedesktop.systemd1.AlreadySubscribed" &&
                            error.name != "org.freedesktop.DBus.Error.FileExists")
                            console.warn("Subscribing to systemd signals failed", error);
                    });
            });
        }
        wait_valid(systemd_manager, done);
    }

    function proxy(name) {
        var self = {
            exists: null,
            state: null,
            enabled: null,

            wait: wait,

            start: start,
            stop: stop,
            restart: restart,

            enable: enable,
            disable: disable
        };

        var unit, service;
        var wait_callbacks = $.Callbacks("once memory");

        if (name.indexOf(".") == -1)
            name = name + ".service";

        function update_from_unit() {
            self.exists = (unit.LoadState != "not-found" || unit.ActiveState != "inactive");

            if (unit.ActiveState == "activating")
                self.state = "starting";
            else if (unit.ActiveState == "deactivating")
                self.state = "stopping";
            else if (unit.ActiveState == "active" || unit.ActiveState == "reloading")
                self.state = "running";
            else if (unit.ActiveState == "failed")
                self.state = "failed";
            else if (unit.ActiveState == "inactive" && self.exists)
                self.state = "stopped";
            else
                self.state = undefined;

            if (unit.UnitFileState == "enabled" || unit.UnitFileState == "linked")
                self.enabled = true;
            else if (unit.UnitFileState == "disabled" || unit.UnitFileState == "masked")
                self.enabled = false;
            else
                self.enabled = undefined;

            self.unit = unit;

            $(self).triggerHandler('changed');
            wait_callbacks.fire();
        }

        function update_from_service() {
            self.service = service;
            $(self).triggerHandler('changed');
        }

        with_systemd_manager(function () {
            systemd_manager.LoadUnit(name).
                done(function (path) {
                    unit = systemd_client.proxy('org.freedesktop.systemd1.Unit', path);
                    $(unit).on('changed', update_from_unit);
                    wait_valid(unit, update_from_unit);

                    service = systemd_client.proxy('org.freedesktop.systemd1.Service', path);
                    $(service).on('changed', update_from_service);
                    wait_valid(service, update_from_service);
                }).
                fail(function (error) {
                    self.exists = false;
                    $(self).triggerHandler('changed');
                });
        });

        function refresh() {
            if (!unit || !service)
                return;

            function refresh_interface(path, iface) {
                systemd_client.call(path,
                                    "org.freedesktop.DBus.Properties", "GetAll", [ iface ]).
                    fail(function (error) {
                        console.log(error);
                    }).
                    done(function (result) {
                        var props = { };
                        for (var p in result[0])
                            props[p] = result[0][p].v;
                        var ifaces = { };
                        ifaces[iface] = props;
                        var data = { };
                        data[unit.path] = ifaces;
                        systemd_client.notify(data);
                    });
            }

            refresh_interface(unit.path, "org.freedesktop.systemd1.Unit");
            refresh_interface(service.path, "org.freedesktop.systemd1.Service");
        }

        /* HACK - https://bugs.freedesktop.org/show_bug.cgi?id=69575
         *
         * We need to explicitly get new property values when getting
         * a UnitNew signal since UnitNew doesn't carry them.
         * However, reacting to UnitNew with GetAll could lead to an
         * infinite loop since systemd emits a UnitNew in reaction to
         * GetAll for units that it doesn't want to keep loaded, such
         * as units without unit files.
         *
         * So we ignore UnitNew and instead assume that the unit state
         * only changes in interesting ways when there is a job for it
         * or when the daemon is reloaded (or when we get a property
         * change notification, of course).
         */

        if (false) {
            // This is what we want to do.

            $(systemd_manager).on("UnitNew", function (event, unit_id, path) {
                if (unit_id == name)
                    refresh();
            });
        } else {
            // This is what we have to do.

            $(systemd_manager).on("Reloading", function (event, reloading) {
                if (!reloading)
                    refresh();
            });

            $(systemd_manager).on("JobNew JobRemoved", function (event, number, path, unit_id, result) {
                if (unit_id == name)
                    refresh();
            });
        }

        function wait(callback) {
            wait_callbacks.add(callback);
        }

        /* Actions
         *
         * We don't call methods on the D-Bus proxies here since they
         * might not be ready when these functions are called.
         */

        var pending_jobs = { };

        $(systemd_manager).on("JobRemoved", function (event, number, path, unit_id, result) {
            if (pending_jobs[path]) {
                if (result == "done")
                    pending_jobs[path].resolve();
                else
                    pending_jobs[path].reject(result);
                delete pending_jobs[path];
            }
        });

        function call_manager(method, args) {
            return systemd_client.call("/org/freedesktop/systemd1",
                                       "org.freedesktop.systemd1.Manager",
                                       method, args);
        }

        function call_manager_with_job(method, args) {
            var dfd = $.Deferred();
            call_manager(method, args).
                done(function (results) {
                    var path = results[0];
                    pending_jobs[path] = dfd;
                }).
                fail(function (error) {
                    dfd.reject(error);
                });
            return dfd.promise();
        }

        function call_manager_with_reload(method, args) {
            return call_manager(method, args).then(function () {
                return call_manager("Reload", [ ]);
            });
        }

        function start() {
            return call_manager_with_job("StartUnit", [ name, "replace" ]);
        }

        function stop() {
            return call_manager_with_job("StopUnit", [ name, "replace" ]);
        }

        function restart() {
            return call_manager_with_job("RestartUnit", [ name, "replace" ]);
        }

        function enable() {
            return call_manager_with_reload("EnableUnitFiles", [ [ name ], false, false ]);
        }

        function disable() {
            return call_manager_with_reload("DisableUnitFiles", [ [ name ], false ]);
        }

        return self;
    }

    return {
        proxy: proxy
    };
});
<|endoftext|>


<commit_before>/*
    Basic BDD testing of the superspawn module
 */

var path = require('path');
var fs = require('fs');
var rimraf = require('rimraf');
var assert = require('assert');
var Q = require('q');
var log = console.log;
var spawn = require(path.join(process.cwd(), 'lib/index.js')).spawn;
var iswin32 = 'win32' === process.platform;
var tempPath = path.join(process.cwd(), 'test/temp');

describe('Superspawn', function() {

    before(function(done) {
        fs.mkdir(tempPath, function(err) {
            done(err);
        });
    });

    it('should be able to start the node executable (.exe on Windows)', function() {
        var actual = spawn('node');
        actual.done();
    });

    it('should be able to start a native executable (echo)', function() {
        var actual = spawn(
            'echo'
        );
        actual.done();
    });

    it('should be able to run a .js file', function() {
        var actual = spawn(
            path.join(process.cwd(), 'test/fixtures/env.js')
        );
        actual.done();
    });

    it('should return a promise', function() {
        var actual = spawn(
            'echo'
        );
        assert(Q.isPromise(actual));
        actual.done();
    });

    it('should execute a callback', function(done) {
        spawn(
            'echo'
            ,[]
            ,{}
            ,done
        );
    });

    it('should execute a callback at any position', function(done) {
        spawn(
            'echo'
            ,done
        );
    });

    it('should execute a callback', function(done) {
        spawn('echo', done);
    });

    it('should respect args', function(done) {
        // testing via fs, because we have not tested output capture yet.
        var newDir = path.join(tempPath, 'args');
        spawn(
            iswin32 ? 'md' : 'mkdir'
            ,[newDir]
            ,function(err) {
                if (err) throw err;
                fs.exists(newDir, function(exists) {
                    assert(exists);
                    done();
                });
            }
        );
    });

    it('should capture stdout', function(done) {
        var input = 'buffalo';
        spawn(
            'echo'
            ,[input]
            ,function(err, output) {
                assert.strictEqual(output, input);
                done();
            }
        );
    });

    it('should capture stderr', function(done) {
        spawn(
            iswin32 ? 'dir' : 'ls'
            ,['./i-dont-exist']
            ,function(err, output) {
                if (err) done();
            }
        );
    });

    it('should respect opts', function(done) {
        spawn(
            'echo'
            ,['silence']
            ,{stdio: 'ignore'} // safe for testing because it's simply passed on.
            ,function(err, output) {
                if (err) throw err;
                assert.strictEqual(output, '');
                done();
            }
        );
    });

    it('should accept opts without args', function(done) {
        spawn(
            iswin32 ? 'dir' : 'ls'
            ,{stdio: 'ignore'} // safe for testing because it's simply passed on.
            ,function(err, output) {
                if (err) throw err;
                assert.strictEqual(output, '');
                done();
            }
        );
    });

    it('should handle opts.env', function(done) {
        var input = 'potato';
        spawn(
            path.join(process.cwd(), 'test/fixtures/env.js')
            ,{env: {superspawn: input}} // safe for testing because it's simply passed on.
            ,function(err, output) {
                if (err) throw err;
                assert.strictEqual(output, input);
                done();
            }
        );
    });

    after(function(done) {
        // rm -rf tempPath
        rimraf(tempPath, done);
    });

});
<commit_msg>Add *.cmd test (npm.cmd on Windows)<commit_after>/*
    Basic BDD testing of the superspawn module
 */

var path = require('path');
var fs = require('fs');
var rimraf = require('rimraf');
var assert = require('assert');
var Q = require('q');
var log = console.log;
var spawn = require(path.join(process.cwd(), 'lib/index.js')).spawn;
var iswin32 = 'win32' === process.platform;
var tempPath = path.join(process.cwd(), 'test/temp');

describe('Superspawn', function() {

    before(function(done) {
        fs.mkdir(tempPath, function(err) {
            done(err);
        });
    });

    it('should be able to start the node executable (.exe on Windows)', function() {
        var actual = spawn('node');
        actual.done();
    });

    it('should be able to start the npm executable (.cmd on Windows)', function() {
        var actual = spawn('npm');
        actual.done();
    });

    it('should be able to start a native executable (echo)', function() {
        var actual = spawn(
            'echo'
        );
        actual.done();
    });

    it('should be able to run a .js file', function() {
        var actual = spawn(
            path.join(process.cwd(), 'test/fixtures/env.js')
        );
        actual.done();
    });

    it('should return a promise', function() {
        var actual = spawn(
            'echo'
        );
        assert(Q.isPromise(actual));
        actual.done();
    });

    it('should execute a callback', function(done) {
        spawn(
            'echo'
            ,[]
            ,{}
            ,done
        );
    });

    it('should execute a callback at any position', function(done) {
        spawn(
            'echo'
            ,done
        );
    });

    it('should execute a callback', function(done) {
        spawn('echo', done);
    });

    it('should respect args', function(done) {
        // testing via fs, because we have not tested output capture yet.
        var newDir = path.join(tempPath, 'args');
        spawn(
            iswin32 ? 'md' : 'mkdir'
            ,[newDir]
            ,function(err) {
                if (err) throw err;
                fs.exists(newDir, function(exists) {
                    assert(exists);
                    done();
                });
            }
        );
    });

    it('should capture stdout', function(done) {
        var input = 'buffalo';
        spawn(
            'echo'
            ,[input]
            ,function(err, output) {
                assert.strictEqual(output, input);
                done();
            }
        );
    });

    it('should capture stderr', function(done) {
        spawn(
            iswin32 ? 'dir' : 'ls'
            ,['./i-dont-exist']
            ,function(err, output) {
                if (err) done();
            }
        );
    });

    it('should respect opts', function(done) {
        spawn(
            'echo'
            ,['silence']
            ,{stdio: 'ignore'} // safe for testing because it's simply passed on.
            ,function(err, output) {
                if (err) throw err;
                assert.strictEqual(output, '');
                done();
            }
        );
    });

    it('should accept opts without args', function(done) {
        spawn(
            iswin32 ? 'dir' : 'ls'
            ,{stdio: 'ignore'} // safe for testing because it's simply passed on.
            ,function(err, output) {
                if (err) throw err;
                assert.strictEqual(output, '');
                done();
            }
        );
    });

    it('should handle opts.env', function(done) {
        var input = 'potato';
        spawn(
            path.join(process.cwd(), 'test/fixtures/env.js')
            ,{env: {superspawn: input}} // safe for testing because it's simply passed on.
            ,function(err, output) {
                if (err) throw err;
                assert.strictEqual(output, input);
                done();
            }
        );
    });

    after(function(done) {
        // rm -rf tempPath
        rimraf(tempPath, done);
    });

});
<|endoftext|>


<commit_before>/*jslint node: true, indent: 2, white: true, vars: true */
/*globals suite, test, setup, suiteSetup, suiteTeardown, done, teardown */
'use strict';

var server = require('../web.js');
var util = require('util');
var request = require('request');
var should = require('should');
var fs = require('fs');

var settings = require('../settings-test.js');

var BASEURL = 'http://localhost:' + settings.port + '/api';

suite('Forms', function () {
  var data_paper = { forms: [] };
  var data_all = { forms: [] };
  var surveyId = '123';

  suiteSetup(function (done) {
    fs.readFile('test/data/form_paper.json', function (err, raw) {
      if (err) { return done(err); }
      data_all.forms.push(JSON.parse(raw));
      data_paper.forms.push(JSON.parse(raw));
      fs.readFile('test/data/form_mobile.json', function (err, raw) {
        if (err) { return done(err); }
        data_all.forms.push(JSON.parse(raw));
        server.run(settings, done);
      });
    });
  });

  suiteTeardown(function () {
    server.stop();
  });

  suite('GET', function () {
    var id;

    setup(function (done) {
      request.post({url: BASEURL + '/surveys/' + surveyId + '/forms', json: data_paper},
                   function (error, response, body) {
        if (error) { return done(error); }
        console.log(body);
        id = body.forms[0].id;
        done();
      });
    });

    test('Get all forms for a survey', function (done) {
      request.get({url: BASEURL + '/surveys/' + surveyId + '/forms'},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('forms');
        parsed.forms.length.should.be.above(0);
        var i;
        var index = -1;
        for (i = 0; i < parsed.forms.length; i += 1) {
          parsed.forms[i].survey.should.equal(surveyId);
          if (parsed.forms[i].id === id) {
            index = i;
          }
        }
        // Make sure the form we added is in this set.
        index.should.be.above(-1);

        done();
      });
    });

    test('Get a form by ID', function (done) {
      request.get({url: BASEURL + '/surveys/' + surveyId + '/forms/' + id},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('form');
        parsed.form.should.have.property('survey').equal(surveyId);
        parsed.form.should.have.property('id').equal(id);
        should.deepEqual(parsed.form.parcels, data_paper.forms[0].parcels);
        parsed.form.type.should.equal(data_paper.forms[0].type);

        done();
      });
    });

    test('Get forms by parcel ID', function (done) {
      var parcelId = '03001529.';
      request.get({url: BASEURL + '/surveys/' + surveyId + '/parcels/' + parcelId + '/forms'},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('forms');
        parsed.forms.length.should.be.above(0);
        var i;
        var index = -1;
        var j;
        var parcelIndex;
        for (i = 0; i < parsed.forms.length; i += 1) {
          parsed.forms[i].survey.should.equal(surveyId);
          if (parsed.forms[i].id === id) {
            index = i;
          }
          parcelIndex = -1;

          // Make sure each form returned references the requested parcel ID.
          for (j = 0; j < parsed.forms[i].parcels.length; j += 1) {
            if (parsed.forms[i].parcels[j].parcel_id === parcelId) {
              parcelIndex = j;
            }
          }
          parcelIndex.should.be.above(-1);
        }
        // Make sure the form we just added is in this set.
        index.should.be.above(-1);

        done();
      });
    });
  });

  suite('POST', function () {
    test('Add form to survey', function (done) {
      request.post({url: BASEURL + '/surveys/' + surveyId + '/forms', json: data_all},
                   function (error, response, body) {

        // Basic sanity checks
        should.not.exist(error);
        response.statusCode.should.equal(201);
        response.should.be.json;

        // These apply to all form requests
        body.should.have.property('forms');
        body.forms.should.have.lengthOf(data_all.forms.length);

        var i;
        for (i = 0; i < body.forms.length; i += 1) {
          body.forms[i].should.have.property('survey').equal(surveyId);
          body.forms[i].should.have.property('id');
          body.forms[i].should.have.property('created');
          body.forms[i].should.have.property('type');

          // Test paper and mobile forms separately
          if(body.forms[i].type === "paper"){
            should.deepEqual(body.forms[i].parcels, data_all.forms[i].parcels);
            should.deepEqual(body.forms[i].global, data_all.forms[i].global);
            body.forms[i].type.should.equal(data_paper.forms[i].type);
          }else {
            // Mobile form tests
            should.deepEqual(body.forms[i].questions, data_all.forms[i].questions);
            body.forms[i].type.should.equal(data_all.forms[i].type);
          }
        }

        done();
      });
    });
  });

  suite('DEL', function () {
    var id;
    setup(function (done) {
      request.post({url: BASEURL + '/surveys/' + surveyId + '/forms', json: data_paper},
                   function (error, response, body) {
        if (error) { return done(error); }
        id = body.forms[0].id;
        done();
      });
    });

    test('Delete a form by ID', function (done) {
      request.del({url: BASEURL + '/surveys/' + surveyId + '/forms/' + id},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('count');
        parsed.count.should.equal(1);

        done();
      });
    });

    test('Delete all forms for a survey', function (done) {
      request.del({url: BASEURL + '/surveys/' + surveyId + '/forms'},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('count');
        parsed.count.should.be.above(0);

        done();
      });
    });
  });
});
<commit_msg>Test modification of forms<commit_after>/*jslint node: true, indent: 2, white: true, vars: true */
/*globals suite, test, setup, suiteSetup, suiteTeardown, done, teardown */
'use strict';

var server = require('../web.js');
var util = require('util');
var request = require('request');
var should = require('should');
var fs = require('fs');

var settings = require('../settings-test.js');

var BASEURL = 'http://localhost:' + settings.port + '/api';

suite('Forms', function () {
  var data_paper = { forms: [] };
  var data_all = { forms: [] };
  var surveyId = '123';

  suiteSetup(function (done) {
    fs.readFile('test/data/form_paper.json', function (err, raw) {
      if (err) { return done(err); }
      data_all.forms.push(JSON.parse(raw));
      data_paper.forms.push(JSON.parse(raw));
      fs.readFile('test/data/form_mobile.json', function (err, raw) {
        if (err) { return done(err); }
        data_all.forms.push(JSON.parse(raw));
        server.run(settings, done);
      });
    });
  });

  suiteTeardown(function () {
    server.stop();
  });

  suite('GET', function () {
    var id;

    setup(function (done) {
      request.post({url: BASEURL + '/surveys/' + surveyId + '/forms', json: data_paper},
                   function (error, response, body) {
        if (error) { return done(error); }
        console.log(body);
        id = body.forms[0].id;
        done();
      });
    });

    test('Get all forms for a survey', function (done) {
      request.get({url: BASEURL + '/surveys/' + surveyId + '/forms'},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('forms');
        parsed.forms.length.should.be.above(0);
        var i;
        var index = -1;
        for (i = 0; i < parsed.forms.length; i += 1) {
          parsed.forms[i].survey.should.equal(surveyId);
          if (parsed.forms[i].id === id) {
            index = i;
          }
        }
        // Make sure the form we added is in this set.
        index.should.be.above(-1);

        done();
      });
    });

    test('Get a form by ID', function (done) {
      request.get({url: BASEURL + '/surveys/' + surveyId + '/forms/' + id},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('form');
        parsed.form.should.have.property('survey').equal(surveyId);
        parsed.form.should.have.property('id').equal(id);
        should.deepEqual(parsed.form.parcels, data_paper.forms[0].parcels);
        parsed.form.type.should.equal(data_paper.forms[0].type);

        done();
      });
    });

    test('Get forms by parcel ID', function (done) {
      var parcelId = '03001529.';
      request.get({url: BASEURL + '/surveys/' + surveyId + '/parcels/' + parcelId + '/forms'},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('forms');
        parsed.forms.length.should.be.above(0);
        var i;
        var index = -1;
        var j;
        var parcelIndex;
        for (i = 0; i < parsed.forms.length; i += 1) {
          parsed.forms[i].survey.should.equal(surveyId);
          if (parsed.forms[i].id === id) {
            index = i;
          }
          parcelIndex = -1;

          // Make sure each form returned references the requested parcel ID.
          for (j = 0; j < parsed.forms[i].parcels.length; j += 1) {
            if (parsed.forms[i].parcels[j].parcel_id === parcelId) {
              parcelIndex = j;
            }
          }
          parcelIndex.should.be.above(-1);
        }
        // Make sure the form we just added is in this set.
        index.should.be.above(-1);

        done();
      });
    });
  });

  suite('POST', function () {
    test('Add form to survey', function (done) {
      request.post({url: BASEURL + '/surveys/' + surveyId + '/forms', json: data_all},
                   function (error, response, body) {

        // Basic sanity checks
        should.not.exist(error);
        response.statusCode.should.equal(201);
        response.should.be.json;

        // These apply to all form requests
        body.should.have.property('forms');
        body.forms.should.have.lengthOf(data_all.forms.length);

        var i;
        for (i = 0; i < body.forms.length; i += 1) {
          body.forms[i].should.have.property('survey').equal(surveyId);
          body.forms[i].should.have.property('id');
          body.forms[i].should.have.property('created');
          body.forms[i].should.have.property('type');

          // Test paper and mobile forms separately
          if(body.forms[i].type === "paper"){
            should.deepEqual(body.forms[i].parcels, data_all.forms[i].parcels);
            should.deepEqual(body.forms[i].global, data_all.forms[i].global);
            body.forms[i].type.should.equal(data_paper.forms[i].type);
          }else {
            // Mobile form tests
            should.deepEqual(body.forms[i].questions, data_all.forms[i].questions);
            body.forms[i].type.should.equal(data_all.forms[i].type);
          }
        }

        done();
      });
    });
  });

  suite('DEL', function () {
    var id;
    setup(function (done) {
      request.post({url: BASEURL + '/surveys/' + surveyId + '/forms', json: data_paper},
                   function (error, response, body) {
        if (error) { return done(error); }
        id = body.forms[0].id;
        done();
      });
    });

    test('Delete a form by ID', function (done) {
      request.del({url: BASEURL + '/surveys/' + surveyId + '/forms/' + id},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('count');
        parsed.count.should.equal(1);

        done();
      });
    });

    test('Delete all forms for a survey', function (done) {
      request.del({url: BASEURL + '/surveys/' + surveyId + '/forms'},
                  function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.should.have.property('count');
        parsed.count.should.be.above(0);

        done();
      });
    });
  });

  suite('PUT', function () {
    var id;
    var form;
    setup(function (done) {
      request.post({url: BASEURL + '/surveys/' + surveyId + '/forms', json: data_paper},
                   function (error, response, body) {
        if (error) { return done(error); }
        id = body.forms[0].id;
        fs.readFile('test/data/form_mobile.json', function (err, raw) {
          if (err) { return done(err); }
          form = JSON.parse(raw);
          done();
        });
      });
    });

    test('Modify a mobile form', function (done) {
      var date = new Date();
      form.questions[0].name = 'New Question Name';
      request.put({
        url: BASEURL + '/surveys/' + surveyId + '/forms/' + id,
        json: { form: form }
      }, function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        body.should.have.property('form');
        body.form.should.have.property('survey');
        body.form.survey.should.equal(surveyId);
        body.form.should.have.property('id');
        body.form.id.should.equal(id);

        body.form.should.have.property('type');
        body.form.type.should.equal(form.type);
        body.form.should.have.property('questions');
        body.form.questions.should.have.lengthOf(form.questions.length);
        body.form.questions[0].name.should.equal(form.questions[0].name);

        body.form.should.have.property('created');
        var created = new Date(body.form.created);
        created.getUTCFullYear().should.equal(date.getUTCFullYear());
        created.getUTCMonth().should.equal(date.getUTCMonth());
        created.getUTCDate().should.equal(date.getUTCDate());
        created.getUTCHours().should.equal(date.getUTCHours());

        done();
      });
    });

  });

});
<|endoftext|>


<commit_before>import jsdom from 'jsdom';
import jquery from 'jquery';
import TestUtils from 'react-addons-test-utils';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import reduxThunk from 'redux-thunk';

import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import reducers from '../src/reducers';
import chai, { expect } from 'chai';
import chaiJquery from 'chai-jquery';

const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);

// Set up testing environment to run like a browser in the CL
// same as using window.document in the browser
global.document = jsdom.jsdom('<!doctype html>');
global.window = global.document.defaultView;
const $ = jquery(global.window);

// Build 'renderComponent' helper that should render a given react class
function renderComponent(ComponentClass, props = {}, state = {}) {
  let store = createStoreWithMiddleware(reducers,state);

  const componentInstance = TestUtils.renderIntoDocument(
    <MuiThemeProvider muiTheme={getMuiTheme()}>
      <Provider store={store}>
        <ComponentClass {...props}/>
      </Provider>
    </MuiThemeProvider>
  );
  return $(ReactDOM.findDOMNode(componentInstance)); // produces HTML
}

// Build helper for simulating events
// Adds a function 'simulate' to jquery
$.fn.simulate = function(eventName, value) {
  if(value) { // If a value is provided
    this.val(value); // Use jQuery to set value
  }
  TestUtils.Simulate[eventName](this[0]);
}

// Set up chai-jquery
// Link chaiJquery up to chai, chai utilities, and jquery
chaiJquery(chai, chai.util, $);

export { renderComponent, expect };<commit_msg>[refactor] refactors testHelper to use configureStore function<commit_after>import jsdom from 'jsdom';
import jquery from 'jquery';
import TestUtils from 'react-addons-test-utils';
import React from 'react';
import ReactDOM from 'react-dom';

import { Provider } from 'react-redux';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import reduxThunk from 'redux-thunk';

import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';

import configureStore from '../src/state/configureStore';

import reducers from '../src/reducers';
import chai, { expect } from 'chai';
import chaiJquery from 'chai-jquery';

// Set up testing environment to run like a browser in the CL
// same as using window.document in the browser
global.document = jsdom.jsdom('<!doctype html>');
global.window = global.document.defaultView;
const $ = jquery(global.window);

// Build 'renderComponent' helper that should render a given react class
function renderComponent(ComponentClass, props = {}, state = {}) {
  let store = configureStore(true,state);

  const componentInstance = TestUtils.renderIntoDocument(
    <MuiThemeProvider muiTheme={getMuiTheme()}>
      <Provider store={store}>
        <ComponentClass {...props}/>
      </Provider>
    </MuiThemeProvider>
  );
  return $(ReactDOM.findDOMNode(componentInstance)); // produces HTML
}

// Build helper for simulating events
// Adds a function 'simulate' to jquery
$.fn.simulate = function(eventName, value) {
  if(value) { // If a value is provided
    this.val(value); // Use jQuery to set value
  }
  TestUtils.Simulate[eventName](this[0]);
}

// Set up chai-jquery
// Link chaiJquery up to chai, chai utilities, and jquery
chaiJquery(chai, chai.util, $);

export { renderComponent, expect };<|endoftext|>


<commit_before>Core.Object.extend('Core.DelayedTask',
/**@lends Core.DelayedTask#*/
{
	delay: 1,
	scope: undefined,
	callback: undefined,
	arguments: undefined,
	isDone: false,
	handle: undefined,

	/**
    *   @constructs
	*	@class
    *   @extends Core.Object
	*	@param config.delay
	*	@param config.scope
	*	@param config.arguments
	*	@param config.callback
	*/
	constructor: function(config) {
		if('delay' in config) {
			this.delay = config.delay;
			delete(config.delay);
		}
		if('scope' in config) {
			this.scope = config.scope;
			delete(config.scope);
		}
		if('arguments' in config) {
			this.arguments = config.arguments;
			delete(config.arguments);
		}
		else
			this.arguments = [];
		if('callback' in config) {
			this.callback = config.callback;
			delete(config.callback);
		}
		else
			throw('callback MUST be given for a DelayedTask');
		this.handle = setTimeout(this.onTimeout.bind(this), this.delay * 1000);
	},

	onTimeout: function() {
		this.handle = undefined;
		this.isDone = true;
		this.callback.apply(this.scope, this);
	},

	abort: function() {
		if(this.handle !== undefined) {
			clearTimeout(this.handle);
			this.handle = undefined;
		}
	}
});

<commit_msg>correct miss use of apply which bug in IE<commit_after>Core.Object.extend('Core.DelayedTask',
/**@lends Core.DelayedTask#*/
{
	delay: 1,
	scope: undefined,
	callback: undefined,
	arguments: undefined,
	isDone: false,
	handle: undefined,

	/**
    *   @constructs
	*	@class
    *   @extends Core.Object
	*	@param config.delay
	*	@param config.scope
	*	@param config.arguments
	*	@param config.callback
	*/
	constructor: function(config) {
		if('delay' in config) {
			this.delay = config.delay;
			delete(config.delay);
		}
		if('scope' in config) {
			this.scope = config.scope;
			delete(config.scope);
		}
		if('arguments' in config) {
			this.arguments = config.arguments;
			delete(config.arguments);
		}
		else
			this.arguments = [];
		if('callback' in config) {
			this.callback = config.callback;
			delete(config.callback);
		}
		else
			throw('callback MUST be given for a DelayedTask');
		this.handle = setTimeout(this.onTimeout.bind(this), this.delay * 1000);
	},

	onTimeout: function() {
		this.handle = undefined;
		this.isDone = true;
		this.callback.apply(this.scope, [ this ]);
	},

	abort: function() {
		if(this.handle !== undefined) {
			clearTimeout(this.handle);
			this.handle = undefined;
		}
	}
});

<|endoftext|>


<commit_before>Ui.LBox.extend('Ui.TransitionBox',
/**@lends Ui.TransitionBox#*/
{
	transition: undefined,
	duration: 0.5,
	ease: undefined,
	position: -1,
	transitionClock: undefined,
	current: undefined,
	next: undefined,

	/**
     * @constructs
	 * @class Container that displays only one element at a time and allows differents kind of transition between elements
     * @extends Ui.LBox
     * @param {Number} [config.duration] Transition duration in second (can be float)
	 * @param {String} [config.ease] Transition ease behaviour [linear|bounce|elastic]
	 * @param {String} [config.transition] Transition type [slide|fade|flip]
	 */
	constructor: function(config) {
		this.addEvents('change');

		this.connect(this, 'load', this.onTransitionBoxLoad);
		this.connect(this, 'unload', this.onTransitionBoxUnload);

		this.setClipToBounds(true);
		this.setTransition('fade');
	},

	getPosition: function() {
		return this.position;
	},

	setDuration: function(duration) {
		this.duration = duration;
	},

	setEase: function(ease) {
		this.ease = Anim.EasingFunction.create(ease);
	},

	setTransition: function(transition) {
		this.transition = Ui.Transition.create(transition);
	},

	setCurrent: function(child) {
		var pos = this.getChildPosition(child);
		if(pos != -1)
			this.setCurrentAt(pos);
	},

	setCurrentAt: function(position) {
		if(this.position != position) {
			if(this.next != undefined) {
				if(this.current != undefined) {
					this.current.hide();
					this.current = this.next;
					this.current.show();
					this.next = undefined;
				}
			}
			if(this.transitionClock != undefined) {
				this.disconnect(this.transitionClock, 'complete', this.onTransitionComplete);
				this.transitionClock.stop();
			}

			if(this.position != -1)
				this.current = this.getChildren()[this.position];
			else
				this.current = undefined;

			this.next = this.getChildren()[position];
			this.next.show();

			this.transition.run(this.current, this.next, 0);

			this.transitionClock = new Anim.Clock({ duration: this.duration, scope: this, onTimeupdate: this.onTransitionTick, ease: this.ease });
			this.connect(this.transitionClock, 'complete', this.onTransitionComplete);
			this.transitionClock.begin();

			this.position = position;
		}
	},

	getCurrent: function() {
		if(this.position == -1)
			return undefined;
		else
			return this.getChildren()[this.position].getChildren()[0];
	},

	/**#@+
	 * @private
	 */
	getChildPosition: function(child) {
		for(var i = 0; i < this.getChildren().length; i++) {
			if(this.getChildren()[i].getChildren()[0] == child)
				return i;
		}
		return -1;
	},

	onTransitionBoxLoad: function() {
	},

	onTransitionBoxUnload: function() {
		if(this.transitionClock != undefined) {
			this.transitionClock.stop();
			this.transitionClock = undefined;
		}
	},

	onTransitionTick: function(clock, progress) {
		this.progress = progress;
		this.transition.run(this.current, this.next, progress);
	},

	onTransitionComplete: function(clock) {
		this.transitionClock = undefined;

		if(this.current != undefined)
			this.current.hide();
		this.next = undefined;
		this.fireEvent('change', this, this.position);
	}
	/**#@-*/
}, {
	arrangeCore: function(width, height) {
		Ui.TransitionBox.base.arrangeCore.call(this, width, height);
		// update the transition if needed
		if(this.transitionClock != undefined)
			this.transition.run(this.current, this.next, this.transitionClock.getProgress());
	},

	append: function(child) {
		var content = new Ui.TransitionBoxContent();
		content.append(child);
		content.hide();
		Ui.TransitionBox.base.append.call(this, content);
	},

	prepend: function(child) {
		if(this.position != -1)
			this.position++;

		var content = new Ui.TransitionBoxContent();
		content.append(child);
		content.hide();
		Ui.TransitionBox.base.prepend.call(this, this.position);
	},

	remove: function(child) {
		for(var i = 0; i < this.getChildren().length; i++) {
			if(this.getChildren()[i].getChildren()[0] == child) {
				if(i < this.position)
					this.position--;
				if(i == this.position)
					this.position = -1;
				this.getChildren()[i].remove(child);
				Ui.TransitionBox.base.remove.call(this, this.getChildren()[i]);
				break;
			}
		}
	}
});

Ui.LBox.extend('Ui.TransitionBoxContent', {});

<commit_msg>correct model style in Ui.TransitionBox<commit_after>Ui.LBox.extend('Ui.TransitionBox',
/**@lends Ui.TransitionBox#*/
{
	transition: undefined,
	duration: 0.5,
	ease: undefined,
	position: -1,
	transitionClock: undefined,
	current: undefined,
	next: undefined,

	/**
     * @constructs
	 * @class Container that displays only one element at a time and allows differents kind of transition between elements
     * @extends Ui.LBox
     * @param {Number} [config.duration] Transition duration in second (can be float)
	 * @param {String} [config.ease] Transition ease behaviour [linear|bounce|elastic]
	 * @param {String} [config.transition] Transition type [slide|fade|flip]
	 */
	constructor: function(config) {
		this.addEvents('change');

		this.connect(this, 'load', this.onTransitionBoxLoad);
		this.connect(this, 'unload', this.onTransitionBoxUnload);

		this.setClipToBounds(true);
		this.setTransition('fade');
	},

	getPosition: function() {
		return this.position;
	},

	setDuration: function(duration) {
		this.duration = duration;
	},

	setEase: function(ease) {
		this.ease = Anim.EasingFunction.create(ease);
	},

	setTransition: function(transition) {
		this.transition = Ui.Transition.create(transition);
	},

	setCurrent: function(child) {
		var pos = this.getChildPosition(child);
		if(pos != -1)
			this.setCurrentAt(pos);
	},

	setCurrentAt: function(position) {
		if(this.position != position) {
			if(this.next != undefined) {
				if(this.current != undefined) {
					this.current.hide();
					this.current = this.next;
					this.current.show();
					this.next = undefined;
				}
			}
			if(this.transitionClock != undefined) {
				this.disconnect(this.transitionClock, 'complete', this.onTransitionComplete);
				this.transitionClock.stop();
			}

			if(this.position != -1)
				this.current = this.getChildren()[this.position];
			else
				this.current = undefined;

			this.next = this.getChildren()[position];
			this.next.show();

			this.transition.run(this.current, this.next, 0);

			this.transitionClock = new Anim.Clock({ duration: this.duration, scope: this, onTimeupdate: this.onTransitionTick, ease: this.ease });
			this.connect(this.transitionClock, 'complete', this.onTransitionComplete);
			this.transitionClock.begin();

			this.position = position;
		}
	},

	getCurrent: function() {
		if(this.position == -1)
			return undefined;
		else
			return this.getChildren()[this.position].getChildren()[0];
	},

	/**#@+
	 * @private
	 */
	getChildPosition: function(child) {
		for(var i = 0; i < this.getChildren().length; i++) {
			if(this.getChildren()[i].getChildren()[0] == child)
				return i;
		}
		return -1;
	},

	onTransitionBoxLoad: function() {
	},

	onTransitionBoxUnload: function() {
		if(this.transitionClock != undefined) {
			this.transitionClock.stop();
			this.transitionClock = undefined;
		}
	},

	onTransitionTick: function(clock, progress) {
		this.progress = progress;
		this.transition.run(this.current, this.next, progress);
	},

	onTransitionComplete: function(clock) {
		this.transitionClock = undefined;

		if(this.current != undefined)
			this.current.hide();
		this.next = undefined;
		this.fireEvent('change', this, this.position);
	}
	/**#@-*/
}, {
	arrangeCore: function(width, height) {
		Ui.TransitionBox.base.arrangeCore.call(this, width, height);
		// update the transition if needed
		if(this.transitionClock != undefined)
			this.transition.run(this.current, this.next, this.transitionClock.getProgress());
	},

	append: function(child) {
		child = Ui.Element.create(child, this);
		var content = new Ui.TransitionBoxContent();
		content.append(child);
		content.hide();
		Ui.TransitionBox.base.append.call(this, content);
	},

	prepend: function(child) {
		child = Ui.Element.create(child, this);
		if(this.position != -1)
			this.position++;

		var content = new Ui.TransitionBoxContent();
		content.append(child);
		content.hide();
		Ui.TransitionBox.base.prepend.call(this, this.position);
	},

	remove: function(child) {
		for(var i = 0; i < this.getChildren().length; i++) {
			if(this.getChildren()[i].getChildren()[0] == child) {
				if(i < this.position)
					this.position--;
				if(i == this.position)
					this.position = -1;
				this.getChildren()[i].remove(child);
				Ui.TransitionBox.base.remove.call(this, this.getChildren()[i]);
				break;
			}
		}
	}
});

Ui.LBox.extend('Ui.TransitionBoxContent', {});

<|endoftext|>


<commit_before>var StyleCache = function (prefix) {
	this.prefix = prefix + 'C-'
	this.style = document.createElement('style')
	document.head.appendChild(this.style)
	this.total = 0
	this.stats = {}
	this.classes = {}
	this.classes_total = 0
}

StyleCache.prototype.constructor = StyleCache

StyleCache.prototype.add = function(rule) {
	this.stats[rule] = (this.stats[rule] || 0) + 1
	++this.total
}

StyleCache.prototype.register = function(rules) {
	var key = rules.join('+')
	var classes = this.classes
	var cls = classes[key]
	if (cls !== undefined)
		return cls

	var cls = classes[key] = this.prefix + this.classes_total++
	this.style.appendChild(document.createTextNode(cls + '{' + rules.join(';') + '}'))
	return cls
}

StyleCache.prototype.classify = function(rules) {
	var total = this.total
	if (total < 1000)
		return ''

	rules.sort() //mind vendor prefixes!
	var n = rules.length
	var classes = []
	var classified = []
	var hot = []
	var self = this
	rules.forEach(function(rule, idx) {
		var hits = self.stats[rule]
		var usage = hits / total
		if (usage > 0.01) {
			classified.push(rule)
			hot.push(idx)
		}
	})
	if (!hot.length)
		return ''
	hot.forEach(function(offset, idx) {
		rules.splice(offset - idx, 1)
	})
	return self.register(classified)
}

var registerGenericListener = function(target) {
	var copyArguments = _globals.core.copyArguments
	var prefix = '_domEventHandler_'
	target.onListener('',
		function(name) {
			//log('registering generic event', name)
			var pname = prefix + name
			var callback = target[pname] = function() {
				var args = copyArguments(arguments, 0, name)
				target.emit.apply(target, args)
			}
			target.dom.addEventListener(name, callback)
		},
		function(name) {
			//log('removing generic event', name)
			var pname = prefix + name
			target.dom.removeEventListener(name, target[pname])
		}
	)
}

/**
 * @constructor
 */

exports.Element = function(context, dom) {
	if (!context._styleCache)
		context._styleCache = new StyleCache(context._prefix)

	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom
	this._fragment = []
	this._styles = {}

	registerGenericListener(this)
}

exports.Element.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Element.prototype.constructor = exports.Element

exports.Element.prototype.addClass = function(cls) {
	this.dom.classList.add(cls)
}

exports.Element.prototype.setHtml = function(html) {
	var dom = this.dom
	this._fragment.forEach(function(node) { dom.removeChild(node) })
	this._fragment = []

	if (html === '')
		return

	var fragment = document.createDocumentFragment()
	var temp = document.createElement('div')

	temp.innerHTML = html
	while (temp.firstChild) {
		this._fragment.push(temp.firstChild)
		fragment.appendChild(temp.firstChild)
	}
	dom.appendChild(fragment)
	return dom.children
}

exports.Element.prototype.width = function() {
	return this.dom.clientWidth
}

exports.Element.prototype.height = function() {
	return this.dom.clientHeight
}

exports.Element.prototype.fullWidth = function() {
	return this.dom.scrollWidth
}

exports.Element.prototype.fullHeight = function() {
	return this.dom.scrollHeight
}

exports.Element.prototype.style = function(name, style) {
	if (style !== undefined) {
		if (style !== '') //fixme: replace it with explicit 'undefined' syntax
			this._styles[name] = style
		else
			delete this._styles[name]
		this.updateStyle()
	} else if (name instanceof Object) { //style({ }) assignment
		for(var k in name) {
			var value = name[k]
			if (value !== '') //fixme: replace it with explicit 'undefined' syntax
				this._styles[k] = value
			else
				delete this._styles[k]
		}
		this.updateStyle()
	}
	else
		return this._styles[name]
}

exports.Element.prototype.setAttribute = function(name, value) {
	this.dom.setAttribute(name, value)
}

exports.Element.prototype.updateStyle = function() {
	var element = this.dom
	if (!element)
		return

	/** @const */
	var cssUnits = {
		'left': 'px',
		'top': 'px',
		'width': 'px',
		'height': 'px',

		'border-radius': 'px',
		'border-width': 'px',

		'margin-left': 'px',
		'margin-top': 'px',
		'margin-right': 'px',
		'margin-bottom': 'px',

		'padding-left': 'px',
		'padding-top': 'px',
		'padding-right': 'px',
		'padding-bottom': 'px'
	}

	var cache = this._context._styleCache
	var rules = []
	for(var name in this._styles) {
		var value = this._styles[name]

		var prefixedName = this._context.getPrefixedName(name)
		var ruleName = prefixedName !== false? prefixedName: name
		if (Array.isArray(value))
			value = value.join(',')

		var unit = (typeof value === 'number')? cssUnits[name] || '': ''
		value += unit

		//var prefixedValue = window.Modernizr.prefixedCSSValue(name, value)
		//var prefixedValue = value
		var rule = ruleName + ':' + value //+ (prefixedValue !== false? prefixedValue: value)
		cache.add(rule)

		rules.push(rule)
	}
	var cls = cache.classify(rules)
	if (cls.length > 0 && this._class !== cls) {
		var classList = this.dom.classList
		if (this._class !== undefined)
			classList.remove(this._class)
		this._class = cls
		classList.add(cls)
	}
	this.dom.setAttribute('style', rules.join(';'))
}

exports.Element.prototype.append = function(el) {
	this.dom.appendChild((el instanceof exports.Element)? el.dom: el)
}

exports.Element.prototype.discard = function() {
	_globals.core.EventEmitter.prototype.discard.apply(this)
	this.remove()
}

exports.Element.prototype.remove = function() {
	var dom = this.dom
	dom.parentNode.removeChild(dom)
}

exports.Window = function(context, dom) {
	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom

	registerGenericListener(this)
}

exports.Window.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Window.prototype.constructor = exports.Window

exports.Window.prototype.width = function() {
	return this.dom.innerWidth
}

exports.Window.prototype.height = function() {
	return this.dom.innerHeight
}

exports.Window.prototype.scrollY = function() {
	return this.dom.scrollY
}

exports.getElement = function(tag) {
	var tags = document.getElementsByTagName(tag)
	if (tags.length != 1)
		throw new Error('no tag ' + tag + '/multiple tags')
	return new exports.Element(this, tags[0])
}
<commit_msg>added fixme next to constants, do not normalize usage<commit_after>var StyleCache = function (prefix) {
	this.prefix = prefix + 'C-'
	this.style = document.createElement('style')
	document.head.appendChild(this.style)
	this.total = 0
	this.stats = {}
	this.classes = {}
	this.classes_total = 0
}

StyleCache.prototype.constructor = StyleCache

StyleCache.prototype.add = function(rule) {
	this.stats[rule] = (this.stats[rule] || 0) + 1
	++this.total
}

StyleCache.prototype.register = function(rules) {
	var key = rules.join('+')
	var classes = this.classes
	var cls = classes[key]
	if (cls !== undefined)
		return cls

	var cls = classes[key] = this.prefix + this.classes_total++
	this.style.appendChild(document.createTextNode(cls + '{' + rules.join(';') + '}'))
	return cls
}

StyleCache.prototype.classify = function(rules) {
	var total = this.total
	if (total < 100) //fixme: initial population threshold
		return ''

	rules.sort() //mind vendor prefixes!
	var n = rules.length
	var classes = []
	var classified = []
	var hot = []
	var self = this
	rules.forEach(function(rule, idx) {
		var hits = self.stats[rule]
		var usage = hits
		if (usage > 100) { //fixme: usage threshold
			classified.push(rule)
			hot.push(idx)
		}
	})
	if (!hot.length)
		return ''
	hot.forEach(function(offset, idx) {
		rules.splice(offset - idx, 1)
	})
	return self.register(classified)
}

var registerGenericListener = function(target) {
	var copyArguments = _globals.core.copyArguments
	var prefix = '_domEventHandler_'
	target.onListener('',
		function(name) {
			//log('registering generic event', name)
			var pname = prefix + name
			var callback = target[pname] = function() {
				var args = copyArguments(arguments, 0, name)
				target.emit.apply(target, args)
			}
			target.dom.addEventListener(name, callback)
		},
		function(name) {
			//log('removing generic event', name)
			var pname = prefix + name
			target.dom.removeEventListener(name, target[pname])
		}
	)
}

/**
 * @constructor
 */

exports.Element = function(context, dom) {
	if (!context._styleCache)
		context._styleCache = new StyleCache(context._prefix)

	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom
	this._fragment = []
	this._styles = {}

	registerGenericListener(this)
}

exports.Element.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Element.prototype.constructor = exports.Element

exports.Element.prototype.addClass = function(cls) {
	this.dom.classList.add(cls)
}

exports.Element.prototype.setHtml = function(html) {
	var dom = this.dom
	this._fragment.forEach(function(node) { dom.removeChild(node) })
	this._fragment = []

	if (html === '')
		return

	var fragment = document.createDocumentFragment()
	var temp = document.createElement('div')

	temp.innerHTML = html
	while (temp.firstChild) {
		this._fragment.push(temp.firstChild)
		fragment.appendChild(temp.firstChild)
	}
	dom.appendChild(fragment)
	return dom.children
}

exports.Element.prototype.width = function() {
	return this.dom.clientWidth
}

exports.Element.prototype.height = function() {
	return this.dom.clientHeight
}

exports.Element.prototype.fullWidth = function() {
	return this.dom.scrollWidth
}

exports.Element.prototype.fullHeight = function() {
	return this.dom.scrollHeight
}

exports.Element.prototype.style = function(name, style) {
	if (style !== undefined) {
		if (style !== '') //fixme: replace it with explicit 'undefined' syntax
			this._styles[name] = style
		else
			delete this._styles[name]
		this.updateStyle()
	} else if (name instanceof Object) { //style({ }) assignment
		for(var k in name) {
			var value = name[k]
			if (value !== '') //fixme: replace it with explicit 'undefined' syntax
				this._styles[k] = value
			else
				delete this._styles[k]
		}
		this.updateStyle()
	}
	else
		return this._styles[name]
}

exports.Element.prototype.setAttribute = function(name, value) {
	this.dom.setAttribute(name, value)
}

exports.Element.prototype.updateStyle = function() {
	var element = this.dom
	if (!element)
		return

	/** @const */
	var cssUnits = {
		'left': 'px',
		'top': 'px',
		'width': 'px',
		'height': 'px',

		'border-radius': 'px',
		'border-width': 'px',

		'margin-left': 'px',
		'margin-top': 'px',
		'margin-right': 'px',
		'margin-bottom': 'px',

		'padding-left': 'px',
		'padding-top': 'px',
		'padding-right': 'px',
		'padding-bottom': 'px'
	}

	var cache = this._context._styleCache
	var rules = []
	for(var name in this._styles) {
		var value = this._styles[name]

		var prefixedName = this._context.getPrefixedName(name)
		var ruleName = prefixedName !== false? prefixedName: name
		if (Array.isArray(value))
			value = value.join(',')

		var unit = (typeof value === 'number')? cssUnits[name] || '': ''
		value += unit

		//var prefixedValue = window.Modernizr.prefixedCSSValue(name, value)
		//var prefixedValue = value
		var rule = ruleName + ':' + value //+ (prefixedValue !== false? prefixedValue: value)
		cache.add(rule)

		rules.push(rule)
	}
	var cls = cache.classify(rules)
	if (cls.length > 0 && this._class !== cls) {
		var classList = this.dom.classList
		if (this._class !== undefined)
			classList.remove(this._class)
		this._class = cls
		classList.add(cls)
	}
	this.dom.setAttribute('style', rules.join(';'))
}

exports.Element.prototype.append = function(el) {
	this.dom.appendChild((el instanceof exports.Element)? el.dom: el)
}

exports.Element.prototype.discard = function() {
	_globals.core.EventEmitter.prototype.discard.apply(this)
	this.remove()
}

exports.Element.prototype.remove = function() {
	var dom = this.dom
	dom.parentNode.removeChild(dom)
}

exports.Window = function(context, dom) {
	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom

	registerGenericListener(this)
}

exports.Window.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Window.prototype.constructor = exports.Window

exports.Window.prototype.width = function() {
	return this.dom.innerWidth
}

exports.Window.prototype.height = function() {
	return this.dom.innerHeight
}

exports.Window.prototype.scrollY = function() {
	return this.dom.scrollY
}

exports.getElement = function(tag) {
	var tags = document.getElementsByTagName(tag)
	if (tags.length != 1)
		throw new Error('no tag ' + tag + '/multiple tags')
	return new exports.Element(this, tags[0])
}
<|endoftext|>


<commit_before>module("split")

test("with same parent", function() {
  var doc = $("
").html("
lorem ipsum dolor sit amet.
")
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    startContainer: p.firstChild,
    startOffset: 6,
    endContainer: p.firstChild,
    endOffset: 11
  })

  assertSameNodes(elems, [p.childNodes[1]])
  assertChildNodeTypes(p, [3, 3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum",
    " dolor sit amet."
  ])
})

test("with containing node", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.firstChild,
    startOffset: 6,
    endContainer: p.lastChild,
    endOffset: 4
  })

  assertSameNodes(elems, [
    p.childNodes[1],
    p.childNodes[2],
    p.childNodes[3]
  ])
  assertChildNodeTypes(p, [3, 3, 1, 3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum ",
    "dolor",
    " sit",
    " amet."
  ])
})

test("starting in an inline element", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.childNodes[1].firstChild,
    startOffset: 2,
    endContainer: p.childNodes[2],
    endOffset: 5
  })

  assertSameNodes(elems, [
    p.childNodes[1].lastChild,
    p.childNodes[2]
  ])
  assertChildNodeTypes(p, [3, 1, 3, 3])
  assertChildNodeTypes(p.childNodes[1], [3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum",
    " dolo",
    "r sit amet."
  ])
})

test("ending in an inline element", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.firstChild,
    startOffset: 6,
    endContainer: p.childNodes[1].firstChild,
    endOffset: 3
  })

  assertSameNodes(elems, [
    p.childNodes[1],
    p.childNodes[2].firstChild
  ])
  assertChildNodeTypes(p, [3, 3, 1, 3])
  assertChildNodeTypes(p.childNodes[2], [3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum ",
    "dolor",
    " sit amet."
  ])
})

test("starting and ending in an inline element", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.childNodes[1].firstChild,
    startOffset: 2,
    endContainer: p.childNodes[3].firstChild,
    endOffset: 3
  })

  assertSameNodes(elems, [
    p.childNodes[1].lastChild,
    p.childNodes[2],
    p.childNodes[3].firstChild
  ])
  assertChildNodeTypes(p, [3, 1, 3, 1, 3])
  assertChildNodeTypes(p.childNodes[1], [3, 3])
  assertChildNodeTypes(p.childNodes[3], [3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum",
    " ",
    "dolor",
    " sit amet."
  ])
})

test("spanning nodes", function() {
  var doc = $("
").html('
lorem ipsum dolor

sit amet.
')
  var p1 = doc.find("p:first")[0]
  var p2 = doc.find("p:last")[0]

  var elems = Wapper.split({
    commonAncestorContainer: doc[0],
    startContainer: p1.firstChild,
    startOffset: 6,
    endContainer: p2.firstChild,
    endOffset: 3
  })

  assertSameNodes(elems, [
    p1.childNodes[1],
    p1.childNodes[2],
    p1.childNodes[3],
    p2.firstChild
  ])
  assertChildNodeTypes(p1, [3, 3, 1, 3])
  assertChildNodeText(p1, [
    "lorem ",
    "ipsum ",
    "dolor",
    " "
  ])
  assertChildNodeTypes(p2, [3, 3])
  assertChildNodeText(p2, [
    "sit",
    " amet."
  ])
})

test("spanning more nodes", function() {
  var doc = $("
").html('
lorem ip sum

dolor

blah sit amet.
')
  var p = doc.find("p")
  var p1 = p[0]
  var p2 = p[1]
  var p3 = p[2]

  var elems = Wapper.split({
    commonAncestorContainer: doc[0],
    startContainer: p1.childNodes[1].firstChild,
    startOffset: 1,
    endContainer: p3.childNodes[1].firstChild,
    endOffset: 2
  })

  assertSameNodes(elems, [
    p1.childNodes[1].childNodes[1],
    p1.childNodes[2],
    p2,
    p3.firstChild,
    p3.childNodes[1].firstChild
  ])
  assertChildNodeTypes(p1, [3, 1, 3])
  assertChildNodeText(p1, [
    "lorem ip ",
    "sum",
    " "
  ])
  assertChildNodeTypes(p1.childNodes[1], [3, 3])
  assertChildNodeText(p1.childNodes[1], [
    "s",
    "um",
  ])
  assertChildNodeTypes(p2, [1, 3])
  assertChildNodeText(p2, [
    "dolor",
    " "
  ])
  assertChildNodeTypes(p3, [3, 1, 3])
  assertChildNodeText(p3, [
    "blah ",
    "sit",
    " amet."
  ])
  assertChildNodeTypes(p3.childNodes[1], [3, 3])
  assertChildNodeText(p3.childNodes[1], [
    "si",
    "t"
  ])
})
<commit_msg>A couple more tests for common selections.<commit_after>module("split")

test("with same parent", function() {
  var doc = $("
").html("
lorem ipsum dolor sit amet.
")
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    startContainer: p.firstChild,
    startOffset: 6,
    endContainer: p.firstChild,
    endOffset: 11
  })

  assertSameNodes(elems, [p.childNodes[1]])
  assertChildNodeTypes(p, [3, 3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum",
    " dolor sit amet."
  ])
})

test("with containing node", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.firstChild,
    startOffset: 6,
    endContainer: p.lastChild,
    endOffset: 4
  })

  assertSameNodes(elems, [
    p.childNodes[1],
    p.childNodes[2],
    p.childNodes[3]
  ])
  assertChildNodeTypes(p, [3, 3, 1, 3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum ",
    "dolor",
    " sit",
    " amet."
  ])
})

test("starting in an inline element", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.childNodes[1].firstChild,
    startOffset: 2,
    endContainer: p.childNodes[2],
    endOffset: 5
  })

  assertSameNodes(elems, [
    p.childNodes[1].lastChild,
    p.childNodes[2]
  ])
  assertChildNodeTypes(p, [3, 1, 3, 3])
  assertChildNodeTypes(p.childNodes[1], [3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum",
    " dolo",
    "r sit amet."
  ])
})

test("starting in an inline element, encompassing nested", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.childNodes[1].firstChild,
    startOffset: 2,
    endContainer: p.childNodes[4],
    endOffset: 4
  })

  assertSameNodes(elems, [
    p.childNodes[1].lastChild,
    p.childNodes[2],
    p.childNodes[3],
    p.childNodes[4],
  ])
  assertChildNodeTypes(p, [3, 1, 3, 1, 3, 3])
  assertChildNodeTypes(p.childNodes[1], [3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum",
    " ",
    "dolor",
    " sit",
    " amet."
  ])
})

test("ending in an inline element", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.firstChild,
    startOffset: 6,
    endContainer: p.childNodes[1].firstChild,
    endOffset: 3
  })

  assertSameNodes(elems, [
    p.childNodes[1],
    p.childNodes[2].firstChild
  ])
  assertChildNodeTypes(p, [3, 3, 1, 3])
  assertChildNodeTypes(p.childNodes[2], [3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum ",
    "dolor",
    " sit amet."
  ])
})

test("ending in an inline element, encompassing nested", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.firstChild,
    startOffset: 6,
    endContainer: p.childNodes[3].firstChild,
    endOffset: 2
  })

  assertSameNodes(elems, [
    p.childNodes[1],
    p.childNodes[2],
    p.childNodes[3],
    p.childNodes[4].firstChild,
  ])
  assertChildNodeTypes(p, [3, 3, 1, 3, 1, 3])
  assertChildNodeTypes(p.childNodes[4], [3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum ",
    "dolor",
    " ",
    "sit",
    " amet."
  ])
})

test("starting and ending in an inline element", function() {
  var doc = $("
").html('
lorem ipsum dolor sit amet.
')
  var p = doc.find("p")[0]

  var elems = Wapper.split({
    commonAncestorContainer: p,
    startContainer: p.childNodes[1].firstChild,
    startOffset: 2,
    endContainer: p.childNodes[3].firstChild,
    endOffset: 3
  })

  assertSameNodes(elems, [
    p.childNodes[1].lastChild,
    p.childNodes[2],
    p.childNodes[3].firstChild
  ])
  assertChildNodeTypes(p, [3, 1, 3, 1, 3])
  assertChildNodeTypes(p.childNodes[1], [3, 3])
  assertChildNodeTypes(p.childNodes[3], [3, 3])
  assertChildNodeText(p, [
    "lorem ",
    "ipsum",
    " ",
    "dolor",
    " sit amet."
  ])
})

test("spanning nodes", function() {
  var doc = $("
").html('
lorem ipsum dolor

sit amet.
')
  var p1 = doc.find("p:first")[0]
  var p2 = doc.find("p:last")[0]

  var elems = Wapper.split({
    commonAncestorContainer: doc[0],
    startContainer: p1.firstChild,
    startOffset: 6,
    endContainer: p2.firstChild,
    endOffset: 3
  })

  assertSameNodes(elems, [
    p1.childNodes[1],
    p1.childNodes[2],
    p1.childNodes[3],
    p2.firstChild
  ])
  assertChildNodeTypes(p1, [3, 3, 1, 3])
  assertChildNodeText(p1, [
    "lorem ",
    "ipsum ",
    "dolor",
    " "
  ])
  assertChildNodeTypes(p2, [3, 3])
  assertChildNodeText(p2, [
    "sit",
    " amet."
  ])
})

test("spanning more nodes", function() {
  var doc = $("
").html('
lorem ip sum

dolor

blah sit amet.
')
  var p = doc.find("p")
  var p1 = p[0]
  var p2 = p[1]
  var p3 = p[2]

  var elems = Wapper.split({
    commonAncestorContainer: doc[0],
    startContainer: p1.childNodes[1].firstChild,
    startOffset: 1,
    endContainer: p3.childNodes[1].firstChild,
    endOffset: 2
  })

  assertSameNodes(elems, [
    p1.childNodes[1].childNodes[1],
    p1.childNodes[2],
    p2,
    p3.firstChild,
    p3.childNodes[1].firstChild
  ])
  assertChildNodeTypes(p1, [3, 1, 3])
  assertChildNodeText(p1, [
    "lorem ip ",
    "sum",
    " "
  ])
  assertChildNodeTypes(p1.childNodes[1], [3, 3])
  assertChildNodeText(p1.childNodes[1], [
    "s",
    "um",
  ])
  assertChildNodeTypes(p2, [1, 3])
  assertChildNodeText(p2, [
    "dolor",
    " "
  ])
  assertChildNodeTypes(p3, [3, 1, 3])
  assertChildNodeText(p3, [
    "blah ",
    "sit",
    " amet."
  ])
  assertChildNodeTypes(p3.childNodes[1], [3, 3])
  assertChildNodeText(p3.childNodes[1], [
    "si",
    "t"
  ])
})
<|endoftext|>


<commit_before>var miio = require('./lib');

// var vacuum = python.import('mirobot/protocol');

class MiHome {

  constructor (name, ip, port, token, model) {
    this.ip = ip;
    this.port = port
    this.token = token;
    this.name = name
    this.model = model
    this.device = null;
  }

  load () {
    var that = this;

    return that.loading = new Promise(function(resolve, reject){

      if (that.model == 'vacuum') {
        that.device = miio.createDevice({
          address: that.ip,
          token: that.token,
          model: 'rockrobo.vacuum.v1'
        });

        that.device.init()
        .then(() => {
          console.log(that.model + " init success");
          resolve(that);
        })
        .catch(err => {
          console.error(that.model + " init error: " + err);
          reject(err);
        });
      }
      else {

        if (that.token == null) {
          var para = { address: that.ip };
        }
        else {
          var para = { address: that.ip, token: that.token };
        };

        miio.device(para)
          .then(device => {
              that.device = device;
              console.log(that.name + " " + that.device.type + " init success");
              resolve(that);
            })
          .catch(err => {
          console.error(that.name + " init error: " + err);
          reject(err);
        });
      }
    });
  }

  exec(cmd) {
    var that = this;

    if (cmd == 'Stop') {
      cmd = 'Charge';
    }

    switch (cmd) {
      case 'Start':

        if (this.device.type == 'vacuum') {
          this.device.start()
            .then(res => console.log(this.name + " start result : " + res))
            .catch(err => console.error(this.name + " start error: " + err));
        }

        break;

      case 'Charge':

        if (this.device.type == 'vacuum') {
          this.device.charge()
            .then(res => console.log(this.name + " charge result : " + res))
            .catch(err => console.error(this.name + " charge error: " + err));
        }

        break;

      case 'Pause':

        if (this.device.type == 'vacuum') {
          this.device.pause()
            .then(res => console.log(this.name + " pause result : " + res))
            .catch(err => console.error(this.name + " pause error: " + err));
        }

        break;

      case 'List':
        if (this.device.type == 'gateway') {
          this.list();
        }

        break;

      case 'PowerOff':
          this.powerOff();

        break;

      default:
        console.log('Invalid type:' + cmd);
    }

    return;

  }

  powerOn() {
    var that = this;

    if(this.device.hasCapability('power')) {
      this.device.setPower(true)
        .then(console.log(this.name + ":Power On"))
        .catch(console.error(this.name + "Failed to Power On"));
    }

  }

  powerOff() {
    var that = this;

    if(this.device.type == 'gateway') {
      that.device.on('deviceAvailable', subDevice => {
        if (subDevice.type == 'power-plug') {
            subDevice.setPower(1, false)
            .then(power => console.log(subDevice.type + " Power " + power))
            .catch(err => console.error(subDevice.type + " Failed :" + err));
        }

        return;
      });
    }
    else {

    }

    // if(this.device.hasCapability('power')) {
    //   this.device.setPower(true)
    //     .then(console.log(this.name + ":Power On"))
    //     .catch(console.error(this.name + "Failed to Power On"));
    // }

  }

  list() {
    var that = this;
    console.log('All SubDevice:');

    that.device.on('deviceAvailable', subDevice => {
      console.log('ID: ' + subDevice.id + ' Model:' + subDevice.model + ' Type:' + subDevice.type );
      return;

    });

    if(that.device.devices) {

      that.device.devices.forEach(function(item){
        console.log('-' + item);
      })
    }

  }

};

module.exports = MiHome;<commit_msg>update subdevice find logic<commit_after>var miio = require('./lib');

// var vacuum = python.import('mirobot/protocol');

class MiHome {

  constructor (name, ip, port, token, model) {
    this.ip = ip;
    this.port = port
    this.token = token;
    this.name = name
    this.model = model
    this.device = null;
  }

  load () {
    var that = this;

    return that.loading = new Promise(function(resolve, reject){

      if (that.model == 'vacuum') {
        that.device = miio.createDevice({
          address: that.ip,
          token: that.token,
          model: 'rockrobo.vacuum.v1'
        });

        that.device.init()
        .then(() => {
          console.log(that.model + " init success");
          resolve(that);
        })
        .catch(err => {
          console.error(that.model + " init error: " + err);
          reject(err);
        });
      }
      else {

        if (that.token == null) {
          var para = { address: that.ip };
        }
        else {
          var para = { address: that.ip, token: that.token };
        };

        miio.device(para)
          .then(device => {
              that.device = device;
              console.log(that.name + " " + that.device.type + " init success");
              resolve(that);
            })
          .catch(err => {
          console.error(that.name + " init error: " + err);
          reject(err);
        });
      }
    });
  }

  exec(cmd) {
    var that = this;

    if (cmd == 'Stop') {
      cmd = 'Charge';
    }

    switch (cmd) {
      case 'Start':

        if (this.device.type == 'vacuum') {
          this.device.start()
            .then(res => console.log(this.name + " start result : " + res))
            .catch(err => console.error(this.name + " start error: " + err));
        }

        break;

      case 'Charge':

        if (this.device.type == 'vacuum') {
          this.device.charge()
            .then(res => console.log(this.name + " charge result : " + res))
            .catch(err => console.error(this.name + " charge error: " + err));
        }

        break;

      case 'Pause':

        if (this.device.type == 'vacuum') {
          this.device.pause()
            .then(res => console.log(this.name + " pause result : " + res))
            .catch(err => console.error(this.name + " pause error: " + err));
        }

        break;

      case 'List':
        if (this.device.type == 'gateway') {
          this.list();
        }

        break;

      case 'PowerOff':
          this.powerOff();

        break;

      default:
        console.log('Invalid type:' + cmd);
    }

    return;

  }

  powerOn() {
    var that = this;

    if(this.device.hasCapability('power')) {
      this.device.setPower(true)
        .then(console.log(this.name + ":Power On"))
        .catch(console.error(this.name + "Failed to Power On"));
    }

  }

  powerOff() {
    var that = this;

    if(this.device.type == 'gateway') {
      that.device.on('deviceAvailable', subDevice => {
        if (subDevice.type == 'power-plug') {
            subDevice.setPower(0, false)
            .then(power => console.log(subDevice.type + " Power " + power))
            .catch(err => console.error(subDevice.type + " Failed :" + err));
        }

        return;
      });
    }
    else {

    }

    // if(this.device.hasCapability('power')) {
    //   this.device.setPower(true)
    //     .then(console.log(this.name + ":Power On"))
    //     .catch(console.error(this.name + "Failed to Power On"));
    // }

  }

  list() {
    var that = this;
    console.log('All SubDevice:');

    that.device.on('deviceAvailable', subDevice => {
      console.log('ID: ' + subDevice.id + ' Model:' + subDevice.model + ' Type:' + subDevice.type );
      return;

    });

    if(that.device.devices) {

      that.device.devices.forEach(function(item){
        console.log('-' + item);
      })
    }

  }

};

module.exports = MiHome;<|endoftext|>


<commit_before>import mm723Image from '../../images/MM723_CNL_combo@2x.jpg';
const devices = {
  carelink: {
    instructions: ['Import from CareLink', '(We will not store your credentials)'],
    isFetching: false,
    key: 'carelink',
    name: 'Medtronic',
    // for the device selection list
    selectName: 'Medtronic (from CareLink)',
    source: {type: 'carelink'},
    enabled: {mac: false, win: false} // CareLink is now disabled for all platforms
  },
  medtronic: {
    instructions: 'Connect your Bayer Contour Next Link to your computer',
    image: {
      'src': mm723Image,
      'height': 128,
      'width': 200,
      'alt': 'Bayer Contour Next Link'
    },
    key: 'medtronic',
    name: 'Medtronic - 523, 723 or 530G',
    selectName: 'Medtronic direct from Bayer Contour Next Link',
    source: {type: 'device', driverId: 'Medtronic'},
    enabled: {mac: true, win: true, linux: true}
  },
  omnipod: {
    instructions: 'Plug in PDM with mini-USB and choose .ibf file from PDM',
    key: 'omnipod',
    name: 'Insulet OmniPod',
    source: {type: 'block', driverId: 'InsuletOmniPod', extension: '.ibf'},
    enabled: {mac: true, win: true, linux: true}
  },
  dexcom: {
    instructions: 'Plug in receiver with micro-USB',
    key: 'dexcom',
    name: 'Dexcom',
    source: {type: 'device', driverId: 'Dexcom'},
    enabled: {mac: true, win: true}
  },
  precisionxtra: {
    instructions: 'Plug in meter with cable',
    key: 'precisionxtra',
    name: 'Abbott Precision Xtra',
    source: {type: 'device', driverId: 'AbbottPrecisionXtra'},
    enabled: {mac: false, win: true}
  },
  tandem: {
    instructions: 'Plug in pump with micro-USB',
    key: 'tandem',
    name: 'Tandem',
    source: {type: 'device', driverId: 'Tandem'},
    enabled: {mac: true, win: true}
  },
  abbottfreestylelite: {
    instructions: 'Plug in meter with cable',
    key: 'abbottfreestylelite',
    name: 'Abbott FreeStyle Lite & Freedom Lite',
    source: {type: 'device', driverId: 'AbbottFreeStyleLite'},
    enabled: {mac: false, win: true}
  },
  abbottfreestylelibre: {
    instructions: 'Plug in meter with micro-USB cable',
    key: 'abbottfreestylelibre',
    name: 'Abbott FreeStyle Libre',
    showDriverLink: {linux: false, mac: false, win: false},
    source: {type: 'device', driverId: 'AbbottFreeStyleLibre'},
    enabled: {linux: true, mac: true, win: true}
  },
  bayercontournext: {
    instructions: 'Plug meter into USB port',
    key: 'bayercontournext',
    name: 'Bayer Contour Next',
    source: {type: 'device', driverId: 'BayerContourNext'},
    enabled: {mac: true, win: true, linux: true}
  },
  animas: {
    instructions: 'Suspend and align back of pump with IR dongle front',
    key: 'animas',
    name: 'Animas',
    source: {type: 'device', driverId: 'Animas'},
    enabled: {mac: true, win: true}
  },
  onetouchverio: {
    instructions: 'Plug in meter with micro-USB',
    name: 'OneTouch Verio & Verio Flex',
    key: 'onetouchverio',
    source: {type: 'device', driverId: 'OneTouchVerio'},
    enabled: {linux: true, mac: true, win: false}
  },
  onetouchverioiq: {
    instructions: 'Plug in meter with mini-USB',
    name: 'OneTouch VerioIQ',
    key: 'onetouchverioiq',
    source: {type: 'device', driverId: 'OneTouchVerioIQ'},
    enabled: {mac: true, win: true}
  },
  onetouchultramini: {
    instructions: 'Plug in meter with cable and make sure the meter is switched off',
    name: 'OneTouch UltraMini',
    key: 'onetouchultramini',
    showDriverLink: {mac: true, win: true},
    source: {type: 'device', driverId: 'OneTouchUltraMini'},
    enabled: {mac: true, win: true}
  },
  onetouchultra2: {
    instructions: 'Plug in meter with cable and make sure the meter is switched off',
    name: 'OneTouch Ultra 2',
    key: 'onetouchultra2',
    showDriverLink: {mac: true, win: true},
    source: {type: 'device', driverId: 'OneTouchUltra2'},
    enabled: {mac: true, win: true}
  }
};

export default devices;
<commit_msg>update medtronic direct name in choose devices screen<commit_after>import mm723Image from '../../images/MM723_CNL_combo@2x.jpg';
const devices = {
  carelink: {
    instructions: ['Import from CareLink', '(We will not store your credentials)'],
    isFetching: false,
    key: 'carelink',
    name: 'Medtronic',
    // for the device selection list
    selectName: 'Medtronic (from CareLink)',
    source: {type: 'carelink'},
    enabled: {mac: false, win: false} // CareLink is now disabled for all platforms
  },
  medtronic: {
    instructions: 'Connect your Bayer Contour Next Link to your computer',
    image: {
      'src': mm723Image,
      'height': 128,
      'width': 200,
      'alt': 'Bayer Contour Next Link'
    },
    key: 'medtronic',
    name: 'Medtronic - 523, 723 or 530G',
    selectName: 'Medtronic 523, 723, 530G (using Bayer Contour Next Link)',
    source: {type: 'device', driverId: 'Medtronic'},
    enabled: {mac: true, win: true, linux: true}
  },
  omnipod: {
    instructions: 'Plug in PDM with mini-USB and choose .ibf file from PDM',
    key: 'omnipod',
    name: 'Insulet OmniPod',
    source: {type: 'block', driverId: 'InsuletOmniPod', extension: '.ibf'},
    enabled: {mac: true, win: true, linux: true}
  },
  dexcom: {
    instructions: 'Plug in receiver with micro-USB',
    key: 'dexcom',
    name: 'Dexcom',
    source: {type: 'device', driverId: 'Dexcom'},
    enabled: {mac: true, win: true}
  },
  precisionxtra: {
    instructions: 'Plug in meter with cable',
    key: 'precisionxtra',
    name: 'Abbott Precision Xtra',
    source: {type: 'device', driverId: 'AbbottPrecisionXtra'},
    enabled: {mac: false, win: true}
  },
  tandem: {
    instructions: 'Plug in pump with micro-USB',
    key: 'tandem',
    name: 'Tandem',
    source: {type: 'device', driverId: 'Tandem'},
    enabled: {mac: true, win: true}
  },
  abbottfreestylelite: {
    instructions: 'Plug in meter with cable',
    key: 'abbottfreestylelite',
    name: 'Abbott FreeStyle Lite & Freedom Lite',
    source: {type: 'device', driverId: 'AbbottFreeStyleLite'},
    enabled: {mac: false, win: true}
  },
  abbottfreestylelibre: {
    instructions: 'Plug in meter with micro-USB cable',
    key: 'abbottfreestylelibre',
    name: 'Abbott FreeStyle Libre',
    showDriverLink: {linux: false, mac: false, win: false},
    source: {type: 'device', driverId: 'AbbottFreeStyleLibre'},
    enabled: {linux: true, mac: true, win: true}
  },
  bayercontournext: {
    instructions: 'Plug meter into USB port',
    key: 'bayercontournext',
    name: 'Bayer Contour Next',
    source: {type: 'device', driverId: 'BayerContourNext'},
    enabled: {mac: true, win: true, linux: true}
  },
  animas: {
    instructions: 'Suspend and align back of pump with IR dongle front',
    key: 'animas',
    name: 'Animas',
    source: {type: 'device', driverId: 'Animas'},
    enabled: {mac: true, win: true}
  },
  onetouchverio: {
    instructions: 'Plug in meter with micro-USB',
    name: 'OneTouch Verio & Verio Flex',
    key: 'onetouchverio',
    source: {type: 'device', driverId: 'OneTouchVerio'},
    enabled: {linux: true, mac: true, win: false}
  },
  onetouchverioiq: {
    instructions: 'Plug in meter with mini-USB',
    name: 'OneTouch VerioIQ',
    key: 'onetouchverioiq',
    source: {type: 'device', driverId: 'OneTouchVerioIQ'},
    enabled: {mac: true, win: true}
  },
  onetouchultramini: {
    instructions: 'Plug in meter with cable and make sure the meter is switched off',
    name: 'OneTouch UltraMini',
    key: 'onetouchultramini',
    showDriverLink: {mac: true, win: true},
    source: {type: 'device', driverId: 'OneTouchUltraMini'},
    enabled: {mac: true, win: true}
  },
  onetouchultra2: {
    instructions: 'Plug in meter with cable and make sure the meter is switched off',
    name: 'OneTouch Ultra 2',
    key: 'onetouchultra2',
    showDriverLink: {mac: true, win: true},
    source: {type: 'device', driverId: 'OneTouchUltra2'},
    enabled: {mac: true, win: true}
  }
};

export default devices;
<|endoftext|>