<commit_before>/* globals process */

var Client = require("../../lib/client/");
var Crypto = require("../../www/bower_components/chainpad-crypto");
var Mailbox = Crypto.Mailbox;
var Nacl = require("tweetnacl");
var nThen = require("nthen");
var Pinpad = require("../../www/common/pinpad");
var Rpc = require("../../www/common/rpc");
var Hash = require("../../www/common/common-hash");
var CpNetflux = require("../../www/bower_components/chainpad-netflux");
var Roster = require("./roster");

var createMailbox = function (config, cb) {
    var webchannel;

    CpNetflux.start({
        network: config.network,
        channel: config.channel,
        crypto: config.crypto,
        owners: [ config.edPublic ],

        noChainPad: true,
        onConnect: function (wc /*, sendMessage */) {
            webchannel = wc;
        },
        onMessage: function (/* msg, user, vKey, isCp, hash, author */) {

        },
        onReady: function () {
            cb(void 0, webchannel);
        },
    });
};

process.on('unhandledRejection', function (err) {
    console.error(err);
});

var makeCurveKeys = function () {
    var pair = Nacl.box.keyPair();
    return {
        curvePrivate: Nacl.util.encodeBase64(pair.secretKey),
        curvePublic: Nacl.util.encodeBase64(pair.publicKey),
    };
};

var makeEdKeys = function () {
    var keys = Nacl.sign.keyPair.fromSeed(Nacl.randomBytes(Nacl.sign.seedLength));
    return {
        edPrivate: Nacl.util.encodeBase64(keys.secretKey),
        edPublic: Nacl.util.encodeBase64(keys.publicKey),
    };
};

var EMPTY_ARRAY_HASH = 'slspTLTetp6gCkw88xE5BIAbYBXllWvQGahXCx/h1gQOlE7zze4W0KRlA8puZZol8hz5zt3BPzUqPJgTjBXWrw==';

var createUser = function (config, cb) {
    // config should contain keys for a team rpc (ed)
        // teamEdKeys
        // rosterHash

    var user;
    nThen(function (w) {
        Client.create(w(function (err, client) {
            if (err) {
                w.abort();
                return void cb(err);
            }
            user = client;
        }));
    }).nThen(function (w) {
        // make all the parameters you'll need

        var network = user.network = user.config.network;
        user.edKeys = makeEdKeys();

        user.curveKeys = makeCurveKeys();
        user.mailbox = Mailbox.createEncryptor(user.curveKeys);
        user.mailboxChannel = Hash.createChannelId();

        // create an anon rpc for alice
        Rpc.createAnonymous(network, w(function (err, rpc) {
            if (err) {
                w.abort();
                user.shutdown();
                return void console.error('ANON_RPC_CONNECT_ERR');
            }
            user.anonRpc = rpc;
        }));

        Pinpad.create(network, user.edKeys, w(function (err, rpc) {
            if (err) {
                w.abort();
                user.shutdown();
                console.error(err);
                return console.log('RPC_CONNECT_ERR');
            }
            user.rpc = rpc;
        }));

        Pinpad.create(network, config.teamEdKeys, w(function (err, rpc) {
            if (err) {
                w.abort();
                user.shutdown();
                return console.log('RPC_CONNECT_ERR');
            }
            user.team_rpc = rpc;
        }));
    }).nThen(function (w) {
        user.rpc.reset([], w(function (err, hash) {
            if (err) {
                w.abort();
                user.shutdown();
                return console.log("RESET_ERR");
            }
            if (!hash || hash !== EMPTY_ARRAY_HASH) {
                throw new Error("EXPECTED EMPTY ARRAY HASH");
            }
        }));
    }).nThen(function (w) {
        // some basic sanity checks...
        user.rpc.getServerHash(w(function (err, hash) {
            if (err) {
                w.abort();
                return void cb(err);
            }
            if (hash !== EMPTY_ARRAY_HASH) {
                console.error("EXPECTED EMPTY ARRAY HASH");
                process.exit(1);
            }
        }));
    }).nThen(function (w) {
        // create and subscribe to your mailbox
        createMailbox({
            network: user.network,
            channel: user.mailboxChannel,
            crypto: user.mailbox,
            edPublic: user.edKeys.edPublic,
        }, w(function (err, wc) {
            if (err) {
                w.abort();
                console.error("Mailbox creation error");
                process.exit(1);
            }
            wc.leave();
        }));
    }).nThen(function (w) {
        // confirm that you own your mailbox
        user.anonRpc.send("GET_METADATA", user.mailboxChannel, w(function (err, data) {
            if (err) {
                w.abort();
                return void cb(err);
            }
            try {
                if (data[0].owners[0] !== user.edKeys.edPublic) {
                    throw new Error("INCORRECT MAILBOX OWNERSHIP METADATA");
                }
            } catch (err2) {
                w.abort();
                return void cb(err2);
            }
        }));
    }).nThen(function (w) {
        // pin your mailbox
        user.rpc.pin([user.mailboxChannel], w(function (err, hash) {
            if (err) {
                w.abort();
                return void cb(err);
            }

            console.log('PIN_RESPONSE', hash);

            if (hash[0] === EMPTY_ARRAY_HASH) { throw new Error("PIN_DIDNT_WORK"); }
            user.latestPinHash = hash;
        }));
    }).nThen(function () {
/*
        // XXX race condition because both users try to pin things...
        user.team_rpc.getServerHash(w(function (err, hash) {
            if (err) {
                w.abort();
                return void cb(err);
            }

            /*
            if (!hash || hash[0] !== EMPTY_ARRAY_HASH) {
                console.error("EXPECTED EMPTY ARRAY HASH");
                process.exit(1);
            }
        }));
*/
    }).nThen(function () {
        // TODO check your quota usage

    }).nThen(function (w) {
        user.rpc.unpin([user.mailboxChannel], w(function (err, hash) {
            if (err) {
                w.abort();
                return void cb(err);
            }

            if (hash[0] !== EMPTY_ARRAY_HASH) {
                console.log('UNPIN_RESPONSE', hash);
                throw new Error("UNPIN_DIDNT_WORK");
            }
            user.latestPinHash = hash[0];
        }));
    }).nThen(function (w) {
        // clean up the pin list to avoid lots of accounts on the server
        user.rpc.removePins(w(function (err) {
            if (err) {
                w.abort();
                return void cb(err);
            }
        }));
    }).nThen(function () {
        user.cleanup = function (cb) {
            // TODO remove your mailbox

            cb = cb;
        };

        cb(void 0, user);
    });
};

var alice, bob, oscar;

var sharedConfig = {
    teamEdKeys: makeEdKeys(),
    teamCurveKeys: makeCurveKeys(),
    rosterChannel: Hash.createChannelId(),
    //rosterHash: makeRosterHash(),
};

nThen(function  (w) {
    // oscar will be the owner of the team
    createUser(sharedConfig, w(function (err, _oscar) {
        if (err) {
            w.abort();
            return void console.log(err);
        }
        oscar = _oscar;
        oscar.name = 'oscar';
    }));
}).nThen(function (w) {
    // TODO oscar creates the team roster
    //Roster = Roster;

    // user edPublic (for ownership)
    // user curve keys (for encryption and authentication)

    // roster curve keys (for encryption and decryption)
    // roster signing/validate keys (ed)

    // channel
    // network
    // owners:

    var team = {
        curve: sharedConfig.teamCurveKeys,
        ed: sharedConfig.teamEdKeys,
    };

    Roster.create({
        network: oscar.network,
        channel: sharedConfig.rosterChannel,
        owners: [
            oscar.edKeys.edPublic
        ],
        keys: {
            myCurvePublic: oscar.curveKeys.curvePublic,
            myCurvePrivate: oscar.curveKeys.curvePrivate,

            teamCurvePublic: team.curve.curvePublic,
            teamCurvePrivate: team.curve.curvePrivate,

            teamEdPrivate: team.ed.edPrivate,
            teamEdPublic: team.ed.edPublic,
        },
        anon_rpc: oscar.anonRpc,
        lastKnownHash: void 0,
    }, w(function (err, roster) {
        if (err) {
            w.abort();
            return void console.trace(err);
        }
        oscar.roster = roster;
    }));
}).nThen(function (w) {
    var roster = oscar.roster;

    roster.on('change', function () {
        setTimeout(function () {

            console.log("\nCHANGE");
            console.log("roster.getState()", roster.getState());
            console.log();
        });
    });

    var state = roster.getState();
    console.log("CURRENT ROSTER STATE:", state);

    roster.init({
        name: oscar.name,
        //profile: '',
        // mailbox: '',
        //title: '',
    }, w(function (err) {
        if (err) { return void console.error(err); }
    }));
}).nThen(function (w) {
    console.log("ALICE && BOB");
    createUser(sharedConfig, w(function (err, _alice) {
        if (err) {
            w.abort();
            return void console.log(err);
        }
        alice = _alice;
        alice.name = 'alice';
        console.log("Initialized Alice");
    }));
    createUser(sharedConfig, w(function (err, _bob) {
        if (err) {
            w.abort();
            return void console.log(err);
        }
        bob = _bob;
        bob.name = 'bob';
        console.log("Initialized Bob");
    }));
}).nThen(function () {
    // TODO oscar adds alice and bob to the team as members
    var roster = oscar.roster;

    var data = {};
    data[alice.curveKeys.curvePublic] = {
        name: alice.name,
        role: 'MEMBER',
    };
    data[bob.curveKeys.curvePublic] = {
        name: bob.name,
        role: 'MEMBER',
    };

    roster.add(data, function (err) {
        if (err) {
            return void console.error(err);
        }
        console.log("SENT ADD COMMAND");
    });
}).nThen(function () {
    // TODO alice and bob describe themselves...

}).nThen(function () {
    // TODO Oscar promotes Alice to 'ADMIN'

}).nThen(function () {

}).nThen(function (w) {
    var message = alice.mailbox.encrypt(JSON.stringify({
        type: "CHEESE",
        author: alice.curveKeys.curvePublic,
        content: {
            text: "CAMEMBERT",
        }
    }), bob.curveKeys.curvePublic);

    alice.anonRpc.send('WRITE_PRIVATE_MESSAGE', [bob.mailboxChannel, message], w(function (err, response) {
        if (err) {
            return void console.error(err);
        }

        // TODO validate that the write was actually successful by checking its size
        response = response;
        // shutdown doesn't work, so we need to do this instead
    }));
}).nThen(function () {

    nThen(function () {

    }).nThen(function () {
        // make a drive
            // pin it
    }).nThen(function () { // MAILBOXES
        // write to your mailbox
            // pin your mailbox
    }).nThen(function () {
        // create an owned pad
            // pin the pad
        // write to it
    }).nThen(function () {
        // get pinned usage
            // remember the usage
    }).nThen(function () {
        // upload a file
            // remember its size
    }).nThen(function () {
        // get pinned usage
            // check that it is consistent with the size of your uploaded file
    }).nThen(function () {
        // delete your uploaded file
        // unpin your owned file
    }).nThen(function () { // EDITABLE METADATA
        //
    }).nThen(function () {

    });
}).nThen(function () {
    alice.shutdown();
    bob.shutdown();
});

<commit_msg>use latest Team Crypto methods<commit_after>/* globals process */

var Client = require("../../lib/client/");
var Crypto = require("../../www/bower_components/chainpad-crypto");
var Mailbox = Crypto.Mailbox;
var Nacl = require("tweetnacl");
var nThen = require("nthen");
var Pinpad = require("../../www/common/pinpad");
var Rpc = require("../../www/common/rpc");
var Hash = require("../../www/common/common-hash");
var CpNetflux = require("../../www/bower_components/chainpad-netflux");
var Roster = require("./roster");

var createMailbox = function (config, cb) {
    var webchannel;

    CpNetflux.start({
        network: config.network,
        channel: config.channel,
        crypto: config.crypto,
        owners: [ config.edPublic ],

        noChainPad: true,
        onConnect: function (wc /*, sendMessage */) {
            webchannel = wc;
        },
        onMessage: function (/* msg, user, vKey, isCp, hash, author */) {

        },
        onReady: function () {
            cb(void 0, webchannel);
        },
    });
};

process.on('unhandledRejection', function (err) {
    console.error(err);
});

var makeCurveKeys = function () {
    var pair = Nacl.box.keyPair();
    return {
        curvePrivate: Nacl.util.encodeBase64(pair.secretKey),
        curvePublic: Nacl.util.encodeBase64(pair.publicKey),
    };
};

var makeEdKeys = function () {
    var keys = Nacl.sign.keyPair.fromSeed(Nacl.randomBytes(Nacl.sign.seedLength));
    return {
        edPrivate: Nacl.util.encodeBase64(keys.secretKey),
        edPublic: Nacl.util.encodeBase64(keys.publicKey),
    };
};

var EMPTY_ARRAY_HASH = 'slspTLTetp6gCkw88xE5BIAbYBXllWvQGahXCx/h1gQOlE7zze4W0KRlA8puZZol8hz5zt3BPzUqPJgTjBXWrw==';

var createUser = function (config, cb) {
    // config should contain keys for a team rpc (ed)
        // teamEdKeys
        // rosterHash

    var user;
    nThen(function (w) {
        Client.create(w(function (err, client) {
            if (err) {
                w.abort();
                return void cb(err);
            }
            user = client;
        }));
    }).nThen(function (w) {
        // make all the parameters you'll need

        var network = user.network = user.config.network;
        user.edKeys = makeEdKeys();

        user.curveKeys = makeCurveKeys();
        user.mailbox = Mailbox.createEncryptor(user.curveKeys);
        user.mailboxChannel = Hash.createChannelId();

        // create an anon rpc for alice
        Rpc.createAnonymous(network, w(function (err, rpc) {
            if (err) {
                w.abort();
                user.shutdown();
                return void console.error('ANON_RPC_CONNECT_ERR');
            }
            user.anonRpc = rpc;
        }));

        Pinpad.create(network, user.edKeys, w(function (err, rpc) {
            if (err) {
                w.abort();
                user.shutdown();
                console.error(err);
                return console.log('RPC_CONNECT_ERR');
            }
            user.rpc = rpc;
        }));

        Pinpad.create(network, config.teamEdKeys, w(function (err, rpc) {
            if (err) {
                w.abort();
                user.shutdown();
                return console.log('RPC_CONNECT_ERR');
            }
            user.team_rpc = rpc;
        }));
    }).nThen(function (w) {
        user.rpc.reset([], w(function (err, hash) {
            if (err) {
                w.abort();
                user.shutdown();
                return console.log("RESET_ERR");
            }
            if (!hash || hash !== EMPTY_ARRAY_HASH) {
                throw new Error("EXPECTED EMPTY ARRAY HASH");
            }
        }));
    }).nThen(function (w) {
        // some basic sanity checks...
        user.rpc.getServerHash(w(function (err, hash) {
            if (err) {
                w.abort();
                return void cb(err);
            }
            if (hash !== EMPTY_ARRAY_HASH) {
                console.error("EXPECTED EMPTY ARRAY HASH");
                process.exit(1);
            }
        }));
    }).nThen(function (w) {
        // create and subscribe to your mailbox
        createMailbox({
            network: user.network,
            channel: user.mailboxChannel,
            crypto: user.mailbox,
            edPublic: user.edKeys.edPublic,
        }, w(function (err, wc) {
            if (err) {
                w.abort();
                console.error("Mailbox creation error");
                process.exit(1);
            }
            wc.leave();
        }));
    }).nThen(function (w) {
        // confirm that you own your mailbox
        user.anonRpc.send("GET_METADATA", user.mailboxChannel, w(function (err, data) {
            if (err) {
                w.abort();
                return void cb(err);
            }
            try {
                if (data[0].owners[0] !== user.edKeys.edPublic) {
                    throw new Error("INCORRECT MAILBOX OWNERSHIP METADATA");
                }
            } catch (err2) {
                w.abort();
                return void cb(err2);
            }
        }));
    }).nThen(function (w) {
        // pin your mailbox
        user.rpc.pin([user.mailboxChannel], w(function (err, hash) {
            if (err) {
                w.abort();
                return void cb(err);
            }

            console.log('PIN_RESPONSE', hash);

            if (hash[0] === EMPTY_ARRAY_HASH) { throw new Error("PIN_DIDNT_WORK"); }
            user.latestPinHash = hash;
        }));
    }).nThen(function () {
/*
        // XXX race condition because both users try to pin things...
        user.team_rpc.getServerHash(w(function (err, hash) {
            if (err) {
                w.abort();
                return void cb(err);
            }

            /*
            if (!hash || hash[0] !== EMPTY_ARRAY_HASH) {
                console.error("EXPECTED EMPTY ARRAY HASH");
                process.exit(1);
            }
        }));
*/
    }).nThen(function () {
        // TODO check your quota usage

    }).nThen(function (w) {
        user.rpc.unpin([user.mailboxChannel], w(function (err, hash) {
            if (err) {
                w.abort();
                return void cb(err);
            }

            if (hash[0] !== EMPTY_ARRAY_HASH) {
                console.log('UNPIN_RESPONSE', hash);
                throw new Error("UNPIN_DIDNT_WORK");
            }
            user.latestPinHash = hash[0];
        }));
    }).nThen(function (w) {
        // clean up the pin list to avoid lots of accounts on the server
        user.rpc.removePins(w(function (err) {
            if (err) {
                w.abort();
                return void cb(err);
            }
        }));
    }).nThen(function () {
        user.cleanup = function (cb) {
            // TODO remove your mailbox

            cb = cb;
        };

        cb(void 0, user);
    });
};

var alice, bob, oscar;

var sharedConfig = {
    teamEdKeys: makeEdKeys(),
    teamCurveKeys: makeCurveKeys(),
    rosterChannel: Hash.createChannelId(),
    //rosterHash: makeRosterHash(),
};

nThen(function  (w) {
    // oscar will be the owner of the team
    createUser(sharedConfig, w(function (err, _oscar) {
        if (err) {
            w.abort();
            return void console.log(err);
        }
        oscar = _oscar;
        oscar.name = 'oscar';
    }));
}).nThen(function (w) {
    // TODO oscar creates the team roster
    //Roster = Roster;

    // user edPublic (for ownership)
    // user curve keys (for encryption and authentication)

    // roster curve keys (for encryption and decryption)
    // roster signing/validate keys (ed)

    // channel
    // network
    // owners:

    var team = {
        curve: sharedConfig.teamCurveKeys,
        ed: sharedConfig.teamEdKeys,
    };

    var rosterSeed = Crypto.Team.createSeed();
    var rosterKeys = Crypto.Team.deriveMemberKeys(rosterSeed, oscar.curveKeys);

    Roster.create({
        network: oscar.network,
        channel: rosterKeys.channel, //sharedConfig.rosterChannel,
        owners: [
            oscar.edKeys.edPublic
        ],
        keys: rosterKeys,
        anon_rpc: oscar.anonRpc,
        lastKnownHash: void 0,
    }, w(function (err, roster) {
        if (err) {
            w.abort();
            return void console.trace(err);
        }
        oscar.roster = roster;
    }));
}).nThen(function (w) {
    var roster = oscar.roster;

    roster.on('change', function () {
        setTimeout(function () {

            console.log("\nCHANGE");
            console.log("roster.getState()", roster.getState());
            console.log();
        });
    });

    var state = roster.getState();
    console.log("CURRENT ROSTER STATE:", state);

    roster.init({
        name: oscar.name,
        //profile: '',
        // mailbox: '',
        //title: '',
    }, w(function (err) {
        if (err) { return void console.error(err); }
    }));
}).nThen(function (w) {
    console.log("ALICE && BOB");
    createUser(sharedConfig, w(function (err, _alice) {
        if (err) {
            w.abort();
            return void console.log(err);
        }
        alice = _alice;
        alice.name = 'alice';
        console.log("Initialized Alice");
    }));
    createUser(sharedConfig, w(function (err, _bob) {
        if (err) {
            w.abort();
            return void console.log(err);
        }
        bob = _bob;
        bob.name = 'bob';
        console.log("Initialized Bob");
    }));
}).nThen(function () {
    // TODO oscar adds alice and bob to the team as members
    var roster = oscar.roster;

    var data = {};
    data[alice.curveKeys.curvePublic] = {
        name: alice.name,
        role: 'MEMBER',
    };
    data[bob.curveKeys.curvePublic] = {
        name: bob.name,
        role: 'MEMBER',
    };

    roster.add(data, function (err) {
        if (err) {
            return void console.error(err);
        }
        console.log("SENT ADD COMMAND");
    });
}).nThen(function () {
    // TODO alice and bob describe themselves...

}).nThen(function () {
    // TODO Oscar promotes Alice to 'ADMIN'

}).nThen(function () {

}).nThen(function (w) {
    var message = alice.mailbox.encrypt(JSON.stringify({
        type: "CHEESE",
        author: alice.curveKeys.curvePublic,
        content: {
            text: "CAMEMBERT",
        }
    }), bob.curveKeys.curvePublic);

    alice.anonRpc.send('WRITE_PRIVATE_MESSAGE', [bob.mailboxChannel, message], w(function (err, response) {
        if (err) {
            return void console.error(err);
        }

        // TODO validate that the write was actually successful by checking its size
        response = response;
        // shutdown doesn't work, so we need to do this instead
    }));
}).nThen(function () {

    nThen(function () {

    }).nThen(function () {
        // make a drive
            // pin it
    }).nThen(function () { // MAILBOXES
        // write to your mailbox
            // pin your mailbox
    }).nThen(function () {
        // create an owned pad
            // pin the pad
        // write to it
    }).nThen(function () {
        // get pinned usage
            // remember the usage
    }).nThen(function () {
        // upload a file
            // remember its size
    }).nThen(function () {
        // get pinned usage
            // check that it is consistent with the size of your uploaded file
    }).nThen(function () {
        // delete your uploaded file
        // unpin your owned file
    }).nThen(function () { // EDITABLE METADATA
        //
    }).nThen(function () {

    });
}).nThen(function () {
    alice.shutdown();
    bob.shutdown();
});

<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var URL = require('url');
var pkg = require('../package.json');

var toFileUrl = require('./jsdom/utils').toFileUrl;
var defineGetter = require('./jsdom/utils').defineGetter;
var defineSetter = require('./jsdom/utils').defineSetter;
var style = require('./jsdom/level2/style');
var features = require('./jsdom/browser/documentfeatures');
var dom = exports.dom = require('./jsdom/living/index').dom;
var createWindow = exports.createWindow = require('./jsdom/browser/index').createWindow;

var request = function(options, cb) {
  request = require('request');
  return request(options, cb);
}

exports.defaultLevel = dom.living.html;
exports.browserAugmentation = require('./jsdom/browser/index').browserAugmentation;
exports.windowAugmentation = require('./jsdom/browser/index').windowAugmentation;

// Proxy feature functions to features module.
['availableDocumentFeatures',
 'defaultDocumentFeatures',
 'applyDocumentFeatures'].forEach(function (propName) {
  defineGetter(exports, propName, function () {
    return features[propName];
  });
  defineSetter(exports, propName, function (val) {
    return features[propName] = val;
  });
});

exports.debugMode = false;

defineGetter(exports, 'version', function() {
  return pkg.version;
});

exports.level = function (level, feature) {
  if(!feature) {
    feature = 'core';
  }

  if (String(level) === '1' || String(level) === '2' || String(level) === '3') {
    level = 'level' + level;
  }

  return require('./jsdom/' + level + '/' + feature).dom[level][feature];
};

exports.jsdom = function (html, level, options) {

  options = options || {};
  if(typeof level == 'string') {
    level = exports.level(level, 'html');
  } else {
    level   = level || exports.defaultLevel;
  }

  if (!options.url) {
    options.url = (module.parent.id === 'jsdom') ?
                  module.parent.parent.filename  :
                  module.parent.filename;
    options.url = options.url.replace(/\\/g, '/');
    if (options.url[0] !== '/') {
      options.url = '/' + options.url;
    }
    options.url = 'file://' + options.url;
  }

  var browser = exports.browserAugmentation(level, options),
      doc     = (browser.HTMLDocument)             ?
                 new browser.HTMLDocument(options) :
                 new browser.Document(options);

  require('./jsdom/selectors/index').applyQuerySelectorPrototype(level);

  features.applyDocumentFeatures(doc, options.features);

  if (typeof html === 'undefined' || html === null ||
      (html.trim && !html.trim())) {
    doc.write('');
  } else {
    doc.write(html + '');
  }

  if (doc.close && !options.deferClose) {
    doc.close();
  }

  // Kept for backwards-compatibility. The window is lazily created when
  // document.parentWindow or document.defaultView is accessed.
  doc.createWindow = function() {
    // Remove ourself
    if (doc.createWindow) {
      delete doc.createWindow;
    }
    return doc.parentWindow;
  };

  return doc;
};

exports.html = function(html, level, options) {
  html += '';

  // TODO: cache a regex and use it here instead
  //       or make the parser handle it
  var htmlLowered = html.toLowerCase();

  // body
  if (!~htmlLowered.indexOf('<body')) {
    html = '' + html + '';
  }

  // html
  if (!~htmlLowered.indexOf('<html')) {
    html = '' + html + '';
  }
  return exports.jsdom(html, level, options);
};

exports.jQueryify = exports.jsdom.jQueryify = function (window /* path [optional], callback */) {

  if (!window || !window.document) { return; }

  var args = Array.prototype.slice.call(arguments),
      callback = (typeof(args[args.length - 1]) === 'function') && args.pop(),
      path,
      jQueryTag = window.document.createElement('script');
      jQueryTag.className = 'jsdom';

  if (args.length > 1 && typeof args[1] === 'string') {
    path = args[1];
  }

  var features = window.document.implementation._features;

  window.document.implementation.addFeature('FetchExternalResources', ['script']);
  window.document.implementation.addFeature('ProcessExternalResources', ['script']);
  window.document.implementation.addFeature('MutationEvents', ['2.0']);
  jQueryTag.src = path || 'http://code.jquery.com/jquery-latest.js';
  window.document.body.appendChild(jQueryTag);

  jQueryTag.onload = function() {
    if (callback) {
      callback(window, window.jQuery);
    }

    window.document.implementation._features = features;
  };

  return window;
};

exports.env = exports.jsdom.env = function () {
  var config = getConfigFromArguments(arguments);
  var callback = config.done;

  if (config.file) {
    fs.readFile(config.file, 'utf-8', function (err, text) {
      if (err) {
        return callback(err);
      }

      config.html = text;
      processHTML(config);
    });
  } else if (config.html) {
    processHTML(config);
  } else if (config.url) {
    handleUrl(config);
  } else if (config.somethingToAutodetect) {
    var url = URL.parse(config.somethingToAutodetect);
    if (url.protocol && url.hostname) {
      config.url = config.somethingToAutodetect;
      handleUrl(config.somethingToAutodetect);
    } else {
      fs.readFile(config.somethingToAutodetect, 'utf-8', function (err, text) {
        if (err) {
          if (err.code === 'ENOENT' || err.code === 'ENAMETOOLONG') {
            config.html = config.somethingToAutodetect;
            processHTML(config);
          } else {
            callback(err);
          }
        } else {
          config.html = text;
          config.url = toFileUrl(config.somethingToAutodetect);
          processHTML(config);
        }
      });
    }
  }

  function handleUrl() {
    var options = {
      uri: config.url,
      encoding: config.encoding || 'utf8',
      headers: config.headers || {},
      proxy: config.proxy || null,
      jar: config.jar !== undefined ? config.jar : true
    };

    request(options, function (err, res, responseText) {
      if (err) {
        return callback(err);
      }

      // The use of `res.request.uri.href` ensures that `window.location.href`
      // is updated when `request` follows redirects.
      config.html = responseText;
      config.url = res.request.uri.href;
      processHTML(config);
    });
  }
};

function processHTML(config) {
  var callback = config.done;
  var options = {
    features: config.features,
    url: config.url,
    parser: config.parser
  };

  if (config.document) {
    options.referrer = config.document.referrer;
    options.cookie = config.document.cookie;
    options.cookieDomain = config.document.cookieDomain;
  }

  var window = exports.html(config.html, null, options).createWindow();
  var features = JSON.parse(JSON.stringify(window.document.implementation._features));

  var docsLoaded = 0;
  var totalDocs = config.scripts.length + config.src.length;
  var readyState = null;
  var errors = [];

  if (!window || !window.document) {
    return callback(new Error('JSDOM: a window object could not be created.'));
  }

  window.document.implementation.addFeature('FetchExternalResources', ['script']);
  window.document.implementation.addFeature('ProcessExternalResources', ['script']);
  window.document.implementation.addFeature('MutationEvents', ['2.0']);

  function scriptComplete() {
    docsLoaded++;

    if (docsLoaded >= totalDocs) {
      window.document.implementation._features = features;

      errors = errors.concat(window.document.errors || []);
      if (errors.length === 0) {
        errors = null;
      }

      process.nextTick(function() {
        callback(errors, window);
      });
    }
  }

  function handleScriptError(e) {
    if (!errors) {
      errors = [];
    }
    errors.push(e.error || e.message);

    // nextTick so that an exception within scriptComplete won't cause
    // another script onerror (which would be an infinite loop)
    process.nextTick(scriptComplete);
  }

  if (config.scripts.length > 0 || config.src.length > 0) {
    config.scripts.forEach(function (scriptSrc) {
      var script = window.document.createElement('script');
      script.className = 'jsdom';
      script.onload = scriptComplete;
      script.onerror = handleScriptError;
      script.src = scriptSrc;

      try {
        // protect against invalid dom
        // ex: http://www.google.com/foo#bar
        window.document.documentElement.appendChild(script);
      } catch (e) {
        handleScriptError(e);
      }
    });

    config.src.forEach(function (scriptText) {
      var script = window.document.createElement('script');
      script.onload = scriptComplete;
      script.onerror = handleScriptError;
      script.text = scriptText;

      window.document.documentElement.appendChild(script);
      window.document.documentElement.removeChild(script);
    });
  } else {
    scriptComplete();
  }
}

function getConfigFromArguments(args, callback) {
  var config = {};
  if (typeof args[0] === 'object') {
    var configToClone = args[0];
    Object.keys(configToClone).forEach(function (key) {
      config[key] = configToClone[key];
    });
  } else {
    var stringToAutodetect = null;

    Array.prototype.forEach.call(args, function (arg) {
      switch (typeof arg) {
        case 'string':
          config.somethingToAutodetect = arg;
          break;
        case 'function':
          config.done = arg;
          break;
        case 'object':
          if (Array.isArray(arg)) {
            config.scripts = arg;
          } else {
            extend(config, arg);
          }
          break;
      }
    });
  }

  if (!config.done) {
    throw new Error('Must pass a "done" option or a callback to jsdom.env.');
  }

  if (!config.somethingToAutodetect && !config.html && !config.file && !config.url) {
    throw new Error('Must pass a "html", "file", or "url" option, or a string, to jsdom.env');
  }

  config.scripts = ensureArray(config.scripts);
  config.src = ensureArray(config.src);

  config.features = config.features || {
    FetchExternalResources: false,
    ProcessExternalResources: false,
    SkipExternalResources: false
  };

  if (!config.url && config.file) {
    config.url = toFileUrl(config.file);
  }

  return config;
}

function ensureArray(value) {
  var array = value || [];
  if (typeof array === 'string') {
    array = [array];
  }
  return array;
}

function extend(config, overrides) {
  Object.keys(overrides).forEach(function (key) {
    config[key] = overrides[key];
  });
}
<commit_msg>Use correct default jQuery URL. Fixes #816.<commit_after>var fs = require('fs');
var path = require('path');
var URL = require('url');
var pkg = require('../package.json');

var toFileUrl = require('./jsdom/utils').toFileUrl;
var defineGetter = require('./jsdom/utils').defineGetter;
var defineSetter = require('./jsdom/utils').defineSetter;
var style = require('./jsdom/level2/style');
var features = require('./jsdom/browser/documentfeatures');
var dom = exports.dom = require('./jsdom/living/index').dom;
var createWindow = exports.createWindow = require('./jsdom/browser/index').createWindow;

var request = function(options, cb) {
  request = require('request');
  return request(options, cb);
}

exports.defaultLevel = dom.living.html;
exports.browserAugmentation = require('./jsdom/browser/index').browserAugmentation;
exports.windowAugmentation = require('./jsdom/browser/index').windowAugmentation;

// Proxy feature functions to features module.
['availableDocumentFeatures',
 'defaultDocumentFeatures',
 'applyDocumentFeatures'].forEach(function (propName) {
  defineGetter(exports, propName, function () {
    return features[propName];
  });
  defineSetter(exports, propName, function (val) {
    return features[propName] = val;
  });
});

exports.debugMode = false;

defineGetter(exports, 'version', function() {
  return pkg.version;
});

exports.level = function (level, feature) {
  if(!feature) {
    feature = 'core';
  }

  if (String(level) === '1' || String(level) === '2' || String(level) === '3') {
    level = 'level' + level;
  }

  return require('./jsdom/' + level + '/' + feature).dom[level][feature];
};

exports.jsdom = function (html, level, options) {

  options = options || {};
  if(typeof level == 'string') {
    level = exports.level(level, 'html');
  } else {
    level   = level || exports.defaultLevel;
  }

  if (!options.url) {
    options.url = (module.parent.id === 'jsdom') ?
                  module.parent.parent.filename  :
                  module.parent.filename;
    options.url = options.url.replace(/\\/g, '/');
    if (options.url[0] !== '/') {
      options.url = '/' + options.url;
    }
    options.url = 'file://' + options.url;
  }

  var browser = exports.browserAugmentation(level, options),
      doc     = (browser.HTMLDocument)             ?
                 new browser.HTMLDocument(options) :
                 new browser.Document(options);

  require('./jsdom/selectors/index').applyQuerySelectorPrototype(level);

  features.applyDocumentFeatures(doc, options.features);

  if (typeof html === 'undefined' || html === null ||
      (html.trim && !html.trim())) {
    doc.write('');
  } else {
    doc.write(html + '');
  }

  if (doc.close && !options.deferClose) {
    doc.close();
  }

  // Kept for backwards-compatibility. The window is lazily created when
  // document.parentWindow or document.defaultView is accessed.
  doc.createWindow = function() {
    // Remove ourself
    if (doc.createWindow) {
      delete doc.createWindow;
    }
    return doc.parentWindow;
  };

  return doc;
};

exports.html = function(html, level, options) {
  html += '';

  // TODO: cache a regex and use it here instead
  //       or make the parser handle it
  var htmlLowered = html.toLowerCase();

  // body
  if (!~htmlLowered.indexOf('<body')) {
    html = '' + html + '';
  }

  // html
  if (!~htmlLowered.indexOf('<html')) {
    html = '' + html + '';
  }
  return exports.jsdom(html, level, options);
};

exports.jQueryify = exports.jsdom.jQueryify = function (window /* path [optional], callback */) {

  if (!window || !window.document) { return; }

  var args = Array.prototype.slice.call(arguments),
      callback = (typeof(args[args.length - 1]) === 'function') && args.pop(),
      path,
      jQueryTag = window.document.createElement('script');
      jQueryTag.className = 'jsdom';

  if (args.length > 1 && typeof args[1] === 'string') {
    path = args[1];
  }

  var features = window.document.implementation._features;

  window.document.implementation.addFeature('FetchExternalResources', ['script']);
  window.document.implementation.addFeature('ProcessExternalResources', ['script']);
  window.document.implementation.addFeature('MutationEvents', ['2.0']);
  jQueryTag.src = path || 'http://code.jquery.com/jquery.js';
  window.document.body.appendChild(jQueryTag);

  jQueryTag.onload = function() {
    if (callback) {
      callback(window, window.jQuery);
    }

    window.document.implementation._features = features;
  };

  return window;
};

exports.env = exports.jsdom.env = function () {
  var config = getConfigFromArguments(arguments);
  var callback = config.done;

  if (config.file) {
    fs.readFile(config.file, 'utf-8', function (err, text) {
      if (err) {
        return callback(err);
      }

      config.html = text;
      processHTML(config);
    });
  } else if (config.html) {
    processHTML(config);
  } else if (config.url) {
    handleUrl(config);
  } else if (config.somethingToAutodetect) {
    var url = URL.parse(config.somethingToAutodetect);
    if (url.protocol && url.hostname) {
      config.url = config.somethingToAutodetect;
      handleUrl(config.somethingToAutodetect);
    } else {
      fs.readFile(config.somethingToAutodetect, 'utf-8', function (err, text) {
        if (err) {
          if (err.code === 'ENOENT' || err.code === 'ENAMETOOLONG') {
            config.html = config.somethingToAutodetect;
            processHTML(config);
          } else {
            callback(err);
          }
        } else {
          config.html = text;
          config.url = toFileUrl(config.somethingToAutodetect);
          processHTML(config);
        }
      });
    }
  }

  function handleUrl() {
    var options = {
      uri: config.url,
      encoding: config.encoding || 'utf8',
      headers: config.headers || {},
      proxy: config.proxy || null,
      jar: config.jar !== undefined ? config.jar : true
    };

    request(options, function (err, res, responseText) {
      if (err) {
        return callback(err);
      }

      // The use of `res.request.uri.href` ensures that `window.location.href`
      // is updated when `request` follows redirects.
      config.html = responseText;
      config.url = res.request.uri.href;
      processHTML(config);
    });
  }
};

function processHTML(config) {
  var callback = config.done;
  var options = {
    features: config.features,
    url: config.url,
    parser: config.parser
  };

  if (config.document) {
    options.referrer = config.document.referrer;
    options.cookie = config.document.cookie;
    options.cookieDomain = config.document.cookieDomain;
  }

  var window = exports.html(config.html, null, options).createWindow();
  var features = JSON.parse(JSON.stringify(window.document.implementation._features));

  var docsLoaded = 0;
  var totalDocs = config.scripts.length + config.src.length;
  var readyState = null;
  var errors = [];

  if (!window || !window.document) {
    return callback(new Error('JSDOM: a window object could not be created.'));
  }

  window.document.implementation.addFeature('FetchExternalResources', ['script']);
  window.document.implementation.addFeature('ProcessExternalResources', ['script']);
  window.document.implementation.addFeature('MutationEvents', ['2.0']);

  function scriptComplete() {
    docsLoaded++;

    if (docsLoaded >= totalDocs) {
      window.document.implementation._features = features;

      errors = errors.concat(window.document.errors || []);
      if (errors.length === 0) {
        errors = null;
      }

      process.nextTick(function() {
        callback(errors, window);
      });
    }
  }

  function handleScriptError(e) {
    if (!errors) {
      errors = [];
    }
    errors.push(e.error || e.message);

    // nextTick so that an exception within scriptComplete won't cause
    // another script onerror (which would be an infinite loop)
    process.nextTick(scriptComplete);
  }

  if (config.scripts.length > 0 || config.src.length > 0) {
    config.scripts.forEach(function (scriptSrc) {
      var script = window.document.createElement('script');
      script.className = 'jsdom';
      script.onload = scriptComplete;
      script.onerror = handleScriptError;
      script.src = scriptSrc;

      try {
        // protect against invalid dom
        // ex: http://www.google.com/foo#bar
        window.document.documentElement.appendChild(script);
      } catch (e) {
        handleScriptError(e);
      }
    });

    config.src.forEach(function (scriptText) {
      var script = window.document.createElement('script');
      script.onload = scriptComplete;
      script.onerror = handleScriptError;
      script.text = scriptText;

      window.document.documentElement.appendChild(script);
      window.document.documentElement.removeChild(script);
    });
  } else {
    scriptComplete();
  }
}

function getConfigFromArguments(args, callback) {
  var config = {};
  if (typeof args[0] === 'object') {
    var configToClone = args[0];
    Object.keys(configToClone).forEach(function (key) {
      config[key] = configToClone[key];
    });
  } else {
    var stringToAutodetect = null;

    Array.prototype.forEach.call(args, function (arg) {
      switch (typeof arg) {
        case 'string':
          config.somethingToAutodetect = arg;
          break;
        case 'function':
          config.done = arg;
          break;
        case 'object':
          if (Array.isArray(arg)) {
            config.scripts = arg;
          } else {
            extend(config, arg);
          }
          break;
      }
    });
  }

  if (!config.done) {
    throw new Error('Must pass a "done" option or a callback to jsdom.env.');
  }

  if (!config.somethingToAutodetect && !config.html && !config.file && !config.url) {
    throw new Error('Must pass a "html", "file", or "url" option, or a string, to jsdom.env');
  }

  config.scripts = ensureArray(config.scripts);
  config.src = ensureArray(config.src);

  config.features = config.features || {
    FetchExternalResources: false,
    ProcessExternalResources: false,
    SkipExternalResources: false
  };

  if (!config.url && config.file) {
    config.url = toFileUrl(config.file);
  }

  return config;
}

function ensureArray(value) {
  var array = value || [];
  if (typeof array === 'string') {
    array = [array];
  }
  return array;
}

function extend(config, overrides) {
  Object.keys(overrides).forEach(function (key) {
    config[key] = overrides[key];
  });
}
<|endoftext|>


<commit_before>import { createStore, applyMiddleware, compose } from 'redux';
import { syncHistory } from 'react-router-redux';
import thunkMiddleware from 'redux-thunk';
import reducers from './../reducers';

const history = process.env.RUNTIME_ENV === 'client'
                  ? require('react-router').browserHistory
                  : require('history/lib/createMemoryHistory')();
const reduxRouterMiddleware = syncHistory(history);

let middlewares = [
  thunkMiddleware,
  reduxRouterMiddleware,
];
let enhancers = [];

if (process.env.NODE_ENV === 'development') {
  const logger = require('redux-logger')({ level: 'info' });
  const { persistState } = require('redux-devtools');

  middlewares = [
    ...middlewares,
    logger,
  ];
  enhancers = [
    ...enhancers,
    require('./../components/main/debug').default.instrument(),
    persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)),
  ];
}

const finalCreateStore = compose(
  applyMiddleware(...middlewares),
  ...enhancers
)(createStore);

export default function configureStore(initialState = {}) {
  const store = finalCreateStore(reducers, initialState);

  // Required for replaying actions from devtools to work
  reduxRouterMiddleware.listenForReplays(store);

  if (module.hot) {
    module.hot.accept('../reducers', () =>
      store.replaceReducer(require('../reducers').default)
    );
  }

  return store;
}
<commit_msg>Re-enable server rendering for store<commit_after>import { createStore, applyMiddleware, compose } from 'redux';
import { syncHistory } from 'react-router-redux';
import thunkMiddleware from 'redux-thunk';
import reducers from './../reducers';

const history = process.env.RUNTIME_ENV === 'client'
                  ? require('react-router').browserHistory
                  : require('history/lib/createMemoryHistory')();
const reduxRouterMiddleware = syncHistory(history);

let middlewares = [
  thunkMiddleware,
  reduxRouterMiddleware,
];
let enhancers = [];

if (process.env.NODE_ENV === 'development' && !process.env.SERVER_RENDERING) {
  const logger = require('redux-logger')({ level: 'info' });
  const { persistState } = require('redux-devtools');

  middlewares = [
    ...middlewares,
    logger,
  ];
  enhancers = [
    ...enhancers,
    require('./../components/main/debug').default.instrument(),
    persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)),
  ];
}

const finalCreateStore = compose(
  applyMiddleware(...middlewares),
  ...enhancers
)(createStore);

export default function configureStore(initialState = {}) {
  const store = finalCreateStore(reducers, initialState);

  // Required for replaying actions from devtools to work
  reduxRouterMiddleware.listenForReplays(store);

  if (module.hot) {
    module.hot.accept('../reducers', () =>
      store.replaceReducer(require('../reducers').default)
    );
  }

  return store;
}
<|endoftext|>


<commit_before>var Store = require('connect').session.Store,
    cradle = require('cradle'),
    db = null;

var ConnectCouchDB = function (opts) {
  opts = opts || {};
  Store.call(this, opts);

  this.reapInterval = opts.reapInterval || 600000;
  if (this.reapInterval !== -1) {
    setInterval(function(self){
      self.reap(self.maxAge);
    }, this.reapInterval, this);
  }

  ConnectCouchDB.setup.call(this, opts.database, opts);
};

ConnectCouchDB.setup = function (database, opts) {
  opts = opts || {};
  if (!database) {
    throw "You must define a database";
  }

  db = new(cradle.Connection)(opts).database(database);
  db.exists(function (err, exists) {
    if (err) throw err;
    if (!exists) {
      db.create();
      ConnectCouchDB.install();
    }
  });
  return ConnectCouchDB;
};

ConnectCouchDB.install = function () {
  if (db === null) {
    throw "You must call setup() function before install";
  }
  db.save('_design/connect-sessions', {
    all: {
      map: function (doc) {
        if (doc.type == 'connect-session') emit(null, doc);
      }
    },
    valid: {
      map: function (doc) {
        if (doc.type == 'connect-session' && doc.ttl < (+new Date - doc.ctime)) {
          emit(null, doc);
        }
      }
    }
});
};

require('util').inherits(ConnectCouchDB, Store);

ConnectCouchDB.prototype.get = function(sid, fn){
  db.get(sid, function (err, doc) {
    if (err) return fn && fn(err);
    if (doc) {
      if (doc.ttl > (+new Date - doc.ctime)) {
        fn(null, doc.datas);
      }
      else {
        db.remove(sid);
        fn(null, null);
      }
    }
    else {
      fn(null, null);
    }
  });
};

ConnectCouchDB.prototype.set = function(sid, sess, fn) {
  fn = fn || function () {};
  var self = this;
  this.get(sid, function (err, doc) {
    if (!doc) {
      doc = {ctime: +new Date,
             ttl: self.reapInterval,
             datas: sess};
      db.save(sid, doc, fn);
    }
    else {
      db.merge(sid, {datas: sess}, fn);
    }
  });
};

ConnectCouchDB.prototype.destroy = function(sid, fn){
  db.remove(sid, fn);
};

ConnectCouchDB.prototype.clear = function(fn){
  var self = this;
  db.view('sessions/all', function (err, docs) {
    if (err) return fn && fn(err);
    docs.forEach(function (doc) {
      self.remove(doc._id);
    });
    fn();
  });
};

ConnectCouchDB.prototype.reap = function(ms){
  var threshold = +new Date - ms;
  var self = this;
  db.view('sessions/all', function (err, docs) {
    if (err) return fn && fn(err);
    docs.forEach(function (doc) {
      if (doc.ctime < threshold) {
        self.remove(doc._id);
      }
    });
  });
};

ConnectCouchDB.prototype.length = function(fn){
  db.view('sessions/valid', function (err, docs) {
    fn && fn(err, docs.length);
  });
};

module.exports = ConnectCouchDB;<commit_msg>Changed CouchDB view setup to use _views.<commit_after>var Store = require('connect').session.Store,
    cradle = require('cradle'),
    db = null;

var ConnectCouchDB = function (opts) {
  opts = opts || {};
  Store.call(this, opts);

  this.reapInterval = opts.reapInterval || 600000;
  if (this.reapInterval !== -1) {
    setInterval(function(self){
      self.reap(self.maxAge);
    }, this.reapInterval, this);
  }

  ConnectCouchDB.setup.call(this, opts.database, opts);
};

ConnectCouchDB.setup = function (database, opts) {
  opts = opts || {};
  if (!database) {
    throw "You must define a database";
  }

  db = new(cradle.Connection)(opts).database(database);
  db.exists(function (err, exists) {
    if (err) throw err;
    if (!exists) {
      db.create();
      ConnectCouchDB.install();
    }
  });
  return ConnectCouchDB;
};

ConnectCouchDB.install = function () {
  if (db === null) {
    throw "You must call setup() function before install";
  }
  db.save('_design/connect-sessions', {
    _views: {
      all: {
        map: function (doc) {
          if (doc.type == 'connect-session') emit(null, doc);
        }
      },
      valid: {
        map: function (doc) {
          if (doc.type == 'connect-session' && doc.ttl < (+new Date - doc.ctime)) {
            emit(null, doc);
          }
        }
      }
    }
  });
};

require('util').inherits(ConnectCouchDB, Store);

ConnectCouchDB.prototype.get = function(sid, fn){
  db.get(sid, function (err, doc) {
    if (err) return fn && fn(err);
    if (doc) {
      if (doc.ttl > (+new Date - doc.ctime)) {
        fn(null, doc.datas);
      }
      else {
        db.remove(sid);
        fn(null, null);
      }
    }
    else {
      fn(null, null);
    }
  });
};

ConnectCouchDB.prototype.set = function(sid, sess, fn) {
  fn = fn || function () {};
  var self = this;
  this.get(sid, function (err, doc) {
    if (!doc) {
      doc = {ctime: +new Date,
             ttl: self.reapInterval,
             datas: sess};
      db.save(sid, doc, fn);
    }
    else {
      db.merge(sid, {datas: sess}, fn);
    }
  });
};

ConnectCouchDB.prototype.destroy = function(sid, fn){
  db.remove(sid, fn);
};

ConnectCouchDB.prototype.clear = function(fn){
  var self = this;
  db.view('sessions/all', function (err, docs) {
    if (err) return fn && fn(err);
    docs.forEach(function (doc) {
      self.remove(doc._id);
    });
    fn();
  });
};

ConnectCouchDB.prototype.reap = function(ms){
  var threshold = +new Date - ms;
  var self = this;
  db.view('sessions/all', function (err, docs) {
    if (err) return fn && fn(err);
    docs.forEach(function (doc) {
      if (doc.ctime < threshold) {
        self.remove(doc._id);
      }
    });
  });
};

ConnectCouchDB.prototype.length = function(fn){
  db.view('sessions/valid', function (err, docs) {
    fn && fn(err, docs.length);
  });
};

module.exports = ConnectCouchDB;
<|endoftext|>


<commit_before>(function(){
  'use strict';
  angular.module('civic.states', ['ui.router'])
    .config(routesConfig);

  // @ngInject
  function routesConfig($stateProvider, $urlRouterProvider, $stickyStateProvider) {
    $stickyStateProvider.enableDebug(true);

    // 404
    $urlRouterProvider.otherwise('home');

    // abstract state redirects
    $urlRouterProvider.when("/events/genes/:geneId", "/events/genes/:geneId/summary");

    // static frontend pages
    $stateProvider
      .state('home', {
        url: '/home',
        controller: 'HomeCtrl',
        templateUrl: '/civic-client/pages/home.tpl.html'
      })
      .state('collaborate', {
        url: '/collaborate',
        controller: 'CollaborateCtrl',
        templateUrl: '/civic-client/pages/collaborate.tpl.html'
      })
      .state('help', {
        url: '/help',
        controller: 'HelpCtrl',
        templateUrl: '/civic-client/pages/help.tpl.html'
      })
      .state('contact', {
        url: '/contact',
        controller: 'ContactCtrl',
        templateUrl: '/civic-client/pages/contact.tpl.html'
      });

    // browse
    $stateProvider
      .state('browse', {
        url: '/browse',
        controller: 'BrowseCtrl',
        templateUrl: '/civic-client/views/browse/browse.tpl.html'
      });

    // EVENT VIEWS
    // gene, variant, evidence & associated activity views
    $stateProvider
      .state('events', {
        url: '/events',
        controller: 'EventsViewCtrl',
        templateUrl: '/civic-client/views/events/eventsView.tpl.html'
      })
      .state('events.genes', {
        abstract: true,
        url: '/genes/:geneId',
        controller: 'GenesViewCtrl',
        templateUrl: '/civic-client/views/events/genes/genesView.tpl.html'
      })
      .state('events.genes.summary', {
        url: '/summary',
        template: '<gene-summary class="col-xs-12"></gene-summary>',
        deepStateRedirect: true
      })
      .state('events.genes.talk', {
        url: '/talk',
        template: '<gene-talk></gene-talk>'
      })
      .state('events.genes.edit', {
        url: '/edit',
        template: '<gene-edit class="col-xs-12"></gene-edit>',
        controller: 'GeneEditCtrl'
      })
      .state('events.genes.summary.variants', {
        abtract: true,
        url: '/variants/:variantId',
        controller: 'VariantsViewCtrl',
        templateUrl: '/civic-client/views/events/variants/variantsView.tpl.html'
      })
      .state('events.genes.summary.variants.summary', {
        url: '/summary',
        template: '<variant-summary></variant-summary>',
        deepStateRedirect: true
      })
      .state('events.genes.summary.variants.talk', {
        url:'/talk',
        template: '<variant-talk></variant-talk>'
      })
      .state('events.genes.summary.variants.edit', {
        url:'/edit',
        template: '
Edit Variant {{ variant.name }}
'
      })
      .state('events.genes.summary.variants.summary.evidence', {
        abstract: true,
        url: '/evidence/:evidenceId',
        controller: 'EvidenceViewCtrl',
        templateUrl: '/civic-client/views/events/evidence/evidenceView.tpl.html'
      })
      .state('events.genes.summary.variants.summary.evidence.summary',{
        url: '/summary',
        template: '<evidence-summary></evidence-summary>'
      })
      .state('events.genes.summary.variants.summary.evidence.talk', {
        url: '/talk',
        template: '<evidence-talk></evidence-talk>'
      })
      .state('events.genes.summary.variants.summary.evidence.edit', {
        url: '/edit',
        template: '
Edit Evidence {{ evidence.id }}
'
      });
  }
})();
<commit_msg>deactivated deepStateRedirects as they're causing problems with the variants menu links<commit_after>(function(){
  'use strict';
  angular.module('civic.states', ['ui.router'])
    .config(routesConfig);

  // @ngInject
  function routesConfig($stateProvider, $urlRouterProvider, $stickyStateProvider) {
    $stickyStateProvider.enableDebug(true);

    // 404
    $urlRouterProvider.otherwise('home');

    // abstract state redirects
    $urlRouterProvider.when("/events/genes/:geneId", "/events/genes/:geneId/summary");

    // static frontend pages
    $stateProvider
      .state('home', {
        url: '/home',
        controller: 'HomeCtrl',
        templateUrl: '/civic-client/pages/home.tpl.html'
      })
      .state('collaborate', {
        url: '/collaborate',
        controller: 'CollaborateCtrl',
        templateUrl: '/civic-client/pages/collaborate.tpl.html'
      })
      .state('help', {
        url: '/help',
        controller: 'HelpCtrl',
        templateUrl: '/civic-client/pages/help.tpl.html'
      })
      .state('contact', {
        url: '/contact',
        controller: 'ContactCtrl',
        templateUrl: '/civic-client/pages/contact.tpl.html'
      });

    // browse
    $stateProvider
      .state('browse', {
        url: '/browse',
        controller: 'BrowseCtrl',
        templateUrl: '/civic-client/views/browse/browse.tpl.html'
      });

    // EVENT VIEWS
    // gene, variant, evidence & associated activity views
    $stateProvider
      .state('events', {
        url: '/events',
        controller: 'EventsViewCtrl',
        templateUrl: '/civic-client/views/events/eventsView.tpl.html'
      })
      .state('events.genes', {
        abstract: true,
        url: '/genes/:geneId',
        controller: 'GenesViewCtrl',
        templateUrl: '/civic-client/views/events/genes/genesView.tpl.html'
      })
      .state('events.genes.summary', {
        url: '/summary',
        template: '<gene-summary class="col-xs-12"></gene-summary>'
      })
      .state('events.genes.talk', {
        url: '/talk',
        template: '<gene-talk></gene-talk>'
      })
      .state('events.genes.edit', {
        url: '/edit',
        template: '<gene-edit class="col-xs-12"></gene-edit>',
        controller: 'GeneEditCtrl'
      })
      .state('events.genes.summary.variants', {
        abtract: true,
        url: '/variants/:variantId',
        controller: 'VariantsViewCtrl',
        templateUrl: '/civic-client/views/events/variants/variantsView.tpl.html'
      })
      .state('events.genes.summary.variants.summary', {
        url: '/summary',
        template: '<variant-summary></variant-summary>'
      })
      .state('events.genes.summary.variants.talk', {
        url:'/talk',
        template: '<variant-talk></variant-talk>'
      })
      .state('events.genes.summary.variants.edit', {
        url:'/edit',
        template: '
Edit Variant {{ variant.name }}
'
      })
      .state('events.genes.summary.variants.summary.evidence', {
        abstract: true,
        url: '/evidence/:evidenceId',
        controller: 'EvidenceViewCtrl',
        templateUrl: '/civic-client/views/events/evidence/evidenceView.tpl.html'
      })
      .state('events.genes.summary.variants.summary.evidence.summary',{
        url: '/summary',
        template: '<evidence-summary></evidence-summary>'
      })
      .state('events.genes.summary.variants.summary.evidence.talk', {
        url: '/talk',
        template: '<evidence-talk></evidence-talk>'
      })
      .state('events.genes.summary.variants.summary.evidence.edit', {
        url: '/edit',
        template: '
Edit Evidence {{ evidence.id }}
'
      });
  }
})();
<|endoftext|>


<commit_before>/**
 * A Model for each single Player
 *
 * @return PlayerModel
 * @author Erik E. Lorenz <erik@tuvero.de>
 * @license MIT License
 * @see LICENSE
 */

define(["lib/extend", "core/model"], function (extend, Model) {

  /**
   * remove extra white spaces from a player name
   *
   * @param name
   *          the name
   * @return a trimmed version of the name
   */
  function trimName(name) {
    return name.trim().replace(/\s+/g, " ");
  }

  /**
   * Constructor
   *
   * @param name
   *          the player name
   */
  function PlayerModel(name) {
    PlayerModel.superconstructor.call(this);

    this.club = "";
    this.email = "";
    this.license = "";
    this.firstname = "";
    this.lastname = "";
    this.elo = 0
    this.rankingpoints = 0;
    this.alias = PlayerModel.NONAME;

    this.setName(name);
  }
  extend(PlayerModel, Model);
  PlayerModel.NONAME = "noname";

  /**
   * retrieve a copy of the player name
   *
   * @return a copy of the player name
   */
  PlayerModel.prototype.getName = function () {
    return this.alias.slice(0);
  };

  /**
   * change the player name. Invalid player names (empty or whitespace only)
   * will be ignored
   *
   * @param name
   *          the new name
   */
  PlayerModel.prototype.setName = function (name) {
    name = trimName(name || "");
    if (name && name !== this.alias) {
      this.alias = name;
      this.emit("update");
    }
  };

  PlayerModel.prototype.SAVEFORMAT = Object
    .create(PlayerModel.superclass.SAVEFORMAT);
  PlayerModel.prototype.SAVEFORMAT.n = String;

  /**
   * prepares a serializable data object, which can later be used for restoring
   * the current state using the restore() function
   *
   * @return a serializable data object, which can be used for restoring
   */
  PlayerModel.prototype.save = function () {
    var data = PlayerModel.superclass.save.call(this);

    data.n = this.getName();

    if (this.club) {
      data.club = this.club;
    }

    if (this.email) {
      data.email = this.email;
    }

    if (this.license) {
      data.license = this.license;
    }

    if (this.firstname) {
      data.firstname = this.firstname;
    }

    if (this.lastname) {
      data.lastname = this.lastname;
    }

    if (this.elo) {
      data.elo = this.elo;
    }

    if (this.rankingpoints) {
      data.rankingpoints = this.rankingpoints;
    }

    return data;
  };

  /**
   * restore a previously saved state from a serializable data object
   *
   * @param data
   *          a data object, that was previously written by save()
   * @return true on success, false otherwise
   */
  PlayerModel.prototype.restore = function (data) {
    if (!PlayerModel.superclass.restore.call(this, data)) {
      return false;
    }

    this.setName(data.n);

    if (data.club) {
      this.club = data.club;
    }

    if (data.email) {
      this.email = data.email;
    }

    if (data.license) {
      this.license = data.license;
    }

    if (data.firstname) {
      this.firstname = data.firstname;
    }

    if (data.lastname) {
      this.lastname = data.lastname;
    }

    if (data.lastname) {
      this.elo = data.elo;
    }

    if (data.rankingpoints) {
      this.rankingpoints = data.rankingpoints;
    }

    return true;
  };

  return PlayerModel;
});
<commit_msg>Add missing semicolon in playermodel<commit_after>/**
 * A Model for each single Player
 *
 * @return PlayerModel
 * @author Erik E. Lorenz <erik@tuvero.de>
 * @license MIT License
 * @see LICENSE
 */

define(["lib/extend", "core/model"], function (extend, Model) {

  /**
   * remove extra white spaces from a player name
   *
   * @param name
   *          the name
   * @return a trimmed version of the name
   */
  function trimName(name) {
    return name.trim().replace(/\s+/g, " ");
  }

  /**
   * Constructor
   *
   * @param name
   *          the player name
   */
  function PlayerModel(name) {
    PlayerModel.superconstructor.call(this);

    this.club = "";
    this.email = "";
    this.license = "";
    this.firstname = "";
    this.lastname = "";
    this.elo = 0;
    this.rankingpoints = 0;
    this.alias = PlayerModel.NONAME;

    this.setName(name);
  }
  extend(PlayerModel, Model);
  PlayerModel.NONAME = "noname";

  /**
   * retrieve a copy of the player name
   *
   * @return a copy of the player name
   */
  PlayerModel.prototype.getName = function () {
    return this.alias.slice(0);
  };

  /**
   * change the player name. Invalid player names (empty or whitespace only)
   * will be ignored
   *
   * @param name
   *          the new name
   */
  PlayerModel.prototype.setName = function (name) {
    name = trimName(name || "");
    if (name && name !== this.alias) {
      this.alias = name;
      this.emit("update");
    }
  };

  PlayerModel.prototype.SAVEFORMAT = Object
    .create(PlayerModel.superclass.SAVEFORMAT);
  PlayerModel.prototype.SAVEFORMAT.n = String;

  /**
   * prepares a serializable data object, which can later be used for restoring
   * the current state using the restore() function
   *
   * @return a serializable data object, which can be used for restoring
   */
  PlayerModel.prototype.save = function () {
    var data = PlayerModel.superclass.save.call(this);

    data.n = this.getName();

    if (this.club) {
      data.club = this.club;
    }

    if (this.email) {
      data.email = this.email;
    }

    if (this.license) {
      data.license = this.license;
    }

    if (this.firstname) {
      data.firstname = this.firstname;
    }

    if (this.lastname) {
      data.lastname = this.lastname;
    }

    if (this.elo) {
      data.elo = this.elo;
    }

    if (this.rankingpoints) {
      data.rankingpoints = this.rankingpoints;
    }

    return data;
  };

  /**
   * restore a previously saved state from a serializable data object
   *
   * @param data
   *          a data object, that was previously written by save()
   * @return true on success, false otherwise
   */
  PlayerModel.prototype.restore = function (data) {
    if (!PlayerModel.superclass.restore.call(this, data)) {
      return false;
    }

    this.setName(data.n);

    if (data.club) {
      this.club = data.club;
    }

    if (data.email) {
      this.email = data.email;
    }

    if (data.license) {
      this.license = data.license;
    }

    if (data.firstname) {
      this.firstname = data.firstname;
    }

    if (data.lastname) {
      this.lastname = data.lastname;
    }

    if (data.lastname) {
      this.elo = data.elo;
    }

    if (data.rankingpoints) {
      this.rankingpoints = data.rankingpoints;
    }

    return true;
  };

  return PlayerModel;
});
<|endoftext|>


<commit_before>(function() {
  window.shared || (window.shared = {});
  var dom = window.shared.ReactHelpers.dom;
  var createEl = window.shared.ReactHelpers.createEl;
  var merge = window.shared.ReactHelpers.merge;

  var PropTypes = window.shared.PropTypes;

  var styles = {
    dialog: {
      border: '1px solid #ccc',
      borderRadius: 2,
      padding: 20,
      marginBottom: 20,
      marginTop: 10
    },
    date: {
      paddingRight: 10,
      fontWeight: 'bold',
      display: 'inline-block'
    },
    educator: {
      paddingLeft: 5,
      display: 'inline-block'
    },
    textarea: {
      fontSize: 14,
      border: '1px solid #eee',
      width: '100%' //overriding strange global CSS, should cleanup
    },
    cancelTakeNotesButton: { // overidding CSS
      color: 'black',
      background: '#eee',
      marginLeft: 10,
      marginRight: 10
    },
    serviceButton: {
      background: '#eee', // override CSS
      color: 'black',
      // shrinking:
      width: '12em',
      fontSize: 12,
      padding: 8
    }
  };

  /*
  Pure UI form for taking notes about an event, tracking its own local state
  and submitting it to prop callbacks.
  */
  var TakeNotes = window.shared.TakeNotes = React.createClass({
    propTypes: {
      nowMoment: React.PropTypes.object.isRequired,
      eventNoteTypesIndex: React.PropTypes.object.isRequired,
      onSave: React.PropTypes.func.isRequired,
      onCancel: React.PropTypes.func.isRequired,
      currentEducator: React.PropTypes.object.isRequired,
      requestState: PropTypes.nullable(React.PropTypes.string.isRequired)
    },

    getInitialState: function() {
      return {
        eventNoteTypeId: null,
        text: null
      }
    },

    // Focus on note-taking text area when it first appears.
    componentDidMount: function(prevProps, prevState) {
      this.textareaRef.focus();
    },

    onChangeText: function(event) {
      this.setState({ text: event.target.value });
    },

    onClickNoteType: function(noteTypeId, event) {
      this.setState({ eventNoteTypeId: noteTypeId });
    },

    onClickCancel: function(event) {
      this.props.onCancel();
    },

    onClickSave: function(event) {
      var params = _.pick(this.state, 'eventNoteTypeId', 'text');
      this.props.onSave(params);
    },

    render: function() {
      return dom.div({ className: 'TakeNotes', style: styles.dialog },
        this.renderNoteHeader({
          noteMoment: this.props.nowMoment,
          educatorEmail: this.props.currentEducator.email
        }),
        dom.textarea({
          rows: 10,
          style: styles.textarea,
          ref: function(ref) { this.textareaRef = ref; }.bind(this),
          value: this.state.text,
          onChange: this.onChangeText
        }),
        dom.div({ style: { marginBottom: 5, marginTop: 20 } }, 'What are these notes from?'),
        dom.div({ style: { display: 'flex' } },
          dom.div({ style: { flex: 1 } },
            this.renderNoteButton(300),
            this.renderNoteButton(301)
          ),
          dom.div({ style: { flex: 1 } },
            this.renderNoteButton(302)
          ),
          dom.div({ style: { flex: 'auto' } },
            this.renderNoteButton(304)
          )
        ),
        dom.button({
          style: {
            marginTop: 20,
            background: (this.state.eventNoteTypeId === null) ? '#ccc' : undefined
          },
          disabled: (this.state.eventNoteTypeId === null),
          className: 'btn save',
          onClick: this.onClickSave
        }, 'Save notes'),
        dom.button({
          className: 'btn cancel',
          style: styles.cancelTakeNotesButton,
          onClick: this.onClickCancel
        }, 'Cancel'),
        (this.props.requestState === 'pending') ? dom.span({}, 'Saving...') : null,
        (this.props.requestState === 'error') ? dom.span({}, 'Try again!') : null
      );
    },

    renderNoteHeader: function(header) {
      return dom.div({},
        dom.span({ style: styles.date }, header.noteMoment.format('MMMM D, YYYY')),
        '|',
        dom.span({ style: styles.educator }, header.educatorEmail)
      );
    },

    // TODO(kr) extract button UI
    renderNoteButton: function(eventNoteTypeId) {
      var eventNoteType = this.props.eventNoteTypesIndex[eventNoteTypeId];
      return dom.button({
        className: 'btn note-type',
        onClick: this.onClickNoteType.bind(this, eventNoteTypeId),
        tabIndex: -1,
        style: merge(styles.serviceButton, {
          background: '#eee',
          outline: 0,
          border: (this.state.eventNoteTypeId === eventNoteTypeId)
            ? '4px solid rgba(49, 119, 201, 0.75)'
            : '4px solid white'
        })
      }, eventNoteType.name);
    }
  });
})();
<commit_msg>Sketch out UI for adding link<commit_after>(function() {
  window.shared || (window.shared = {});
  var dom = window.shared.ReactHelpers.dom;
  var createEl = window.shared.ReactHelpers.createEl;
  var merge = window.shared.ReactHelpers.merge;

  var PropTypes = window.shared.PropTypes;

  var styles = {
    dialog: {
      border: '1px solid #ccc',
      borderRadius: 2,
      padding: 20,
      marginBottom: 20,
      marginTop: 10
    },
    date: {
      paddingRight: 10,
      fontWeight: 'bold',
      display: 'inline-block'
    },
    educator: {
      paddingLeft: 5,
      display: 'inline-block'
    },
    textarea: {
      fontSize: 14,
      border: '1px solid #eee',
      width: '100%' //overriding strange global CSS, should cleanup
    },
    input: {
      fontSize: 14,
      border: '1px solid #eee',
      width: '100%'
    },
    cancelTakeNotesButton: { // overidding CSS
      color: 'black',
      background: '#eee',
      marginLeft: 10,
      marginRight: 10
    },
    serviceButton: {
      background: '#eee', // override CSS
      color: 'black',
      // shrinking:
      width: '12em',
      fontSize: 12,
      padding: 8
    }
  };

  /*
  Pure UI form for taking notes about an event, tracking its own local state
  and submitting it to prop callbacks.
  */
  var TakeNotes = window.shared.TakeNotes = React.createClass({
    propTypes: {
      nowMoment: React.PropTypes.object.isRequired,
      eventNoteTypesIndex: React.PropTypes.object.isRequired,
      onSave: React.PropTypes.func.isRequired,
      onCancel: React.PropTypes.func.isRequired,
      currentEducator: React.PropTypes.object.isRequired,
      requestState: PropTypes.nullable(React.PropTypes.string.isRequired)
    },

    getInitialState: function() {
      return {
        eventNoteTypeId: null,
        text: null
      }
    },

    // Focus on note-taking text area when it first appears.
    componentDidMount: function(prevProps, prevState) {
      this.textareaRef.focus();
    },

    onChangeText: function(event) {
      this.setState({ text: event.target.value });
    },

    onClickNoteType: function(noteTypeId, event) {
      this.setState({ eventNoteTypeId: noteTypeId });
    },

    onClickCancel: function(event) {
      this.props.onCancel();
    },

    onClickSave: function(event) {
      var params = _.pick(this.state, 'eventNoteTypeId', 'text');
      this.props.onSave(params);
    },

    render: function() {
      return dom.div({ className: 'TakeNotes', style: styles.dialog },
        this.renderNoteHeader({
          noteMoment: this.props.nowMoment,
          educatorEmail: this.props.currentEducator.email
        }),
        dom.textarea({
          rows: 10,
          style: styles.textarea,
          ref: function(ref) { this.textareaRef = ref; }.bind(this),
          value: this.state.text,
          onChange: this.onChangeText
        }),
        dom.div({ style: { marginBottom: 5, marginTop: 20 } }, 'What are these notes from?'),
        dom.div({ style: { display: 'flex' } },
          dom.div({ style: { flex: 1 } },
            this.renderNoteButton(300),
            this.renderNoteButton(301)
          ),
          dom.div({ style: { flex: 1 } },
            this.renderNoteButton(302)
          ),
          dom.div({ style: { flex: 'auto' } },
            this.renderNoteButton(304)
          )
        ),
        dom.div({ style: { marginBottom: 5, marginTop: 20 } },
          'Add a link (i.e. to an attachment on Google Drive):'
        ),
        dom.input({ style: styles.input }),
        dom.br({}),
        dom.button({
          style: {
            marginTop: 20,
            background: (this.state.eventNoteTypeId === null) ? '#ccc' : undefined
          },
          disabled: (this.state.eventNoteTypeId === null),
          className: 'btn save',
          onClick: this.onClickSave
        }, 'Save notes'),
        dom.button({
          className: 'btn cancel',
          style: styles.cancelTakeNotesButton,
          onClick: this.onClickCancel
        }, 'Cancel'),
        (this.props.requestState === 'pending') ? dom.span({}, 'Saving...') : null,
        (this.props.requestState === 'error') ? dom.span({}, 'Try again!') : null
      );
    },

    renderNoteHeader: function(header) {
      return dom.div({},
        dom.span({ style: styles.date }, header.noteMoment.format('MMMM D, YYYY')),
        '|',
        dom.span({ style: styles.educator }, header.educatorEmail)
      );
    },

    // TODO(kr) extract button UI
    renderNoteButton: function(eventNoteTypeId) {
      var eventNoteType = this.props.eventNoteTypesIndex[eventNoteTypeId];
      return dom.button({
        className: 'btn note-type',
        onClick: this.onClickNoteType.bind(this, eventNoteTypeId),
        tabIndex: -1,
        style: merge(styles.serviceButton, {
          background: '#eee',
          outline: 0,
          border: (this.state.eventNoteTypeId === eventNoteTypeId)
            ? '4px solid rgba(49, 119, 201, 0.75)'
            : '4px solid white'
        })
      }, eventNoteType.name);
    }
  });
})();
<|endoftext|>


<commit_before>
var dom      = exports.dom = require("./jsdom/level3/index").dom,
    request  = require('request'),
    fs       = require("fs"),
    pkg      = JSON.parse(fs.readFileSync(__dirname + "/../package.json"));
    URL      = require('url');

exports.defaultLevel = dom.level3.html;
exports.browserAugmentation = require("./jsdom/browser/index").browserAugmentation;
exports.windowAugmentation = require("./jsdom/browser/index").windowAugmentation;

exports.debugMode = false;

var createWindow = exports.createWindow = require("./jsdom/browser/index").createWindow;

exports.__defineGetter__('version', function() {
  return pkg.version;
});

exports.jsdom = function (html, level, options) {

  options = options || {};
  level   = level || exports.defaultLevel;

  if (!options.url) {
    options.url = module.parent.id == 'jsdom'   ?
                  module.parent.parent.filename :
                  module.parent.filename;
  }

  if (options.features && options.features.QuerySelector) {
    require("./jsdom/selectors/index").applyQuerySelectorPrototype(level);
  }

  var browser = exports.browserAugmentation(level, options),
      doc     = (browser.HTMLDocument)             ?
                 new browser.HTMLDocument(options) :
                 new browser.Document(options);

  exports.applyDocumentFeatures(doc, options.features);

  if (!!html) {
    doc.write(html + '')
  } else {
    doc.write('');
  }

  if (doc.close && !options.deferClose) {
    doc.close();
  }

  // Kept for backwards-compatibility. The window is lazily created when
  // document.parentWindow or document.defaultView is accessed.
  doc.createWindow = function() {
    // Remove ourself
    if (doc.createWindow) {
      delete doc.createWindow;
    }
    return doc.parentWindow;
  };

  return doc;
};

exports.html = function(html, level, options) {
  html += '';
  // body
  if (!~html.toLowerCase().indexOf('<body')) {
    html = '' + html + '';
  }

  // html
  if (!~html.toLowerCase().indexOf('<html')) {
    html = '' + html + '';
  }
  return exports.jsdom(html, level, options);
};

exports.availableDocumentFeatures = [
  'FetchExternalResources',
  'ProcessExternalResources',
  'MutationEvents',
  'QuerySelector'
];

exports.defaultDocumentFeatures = {
  "FetchExternalResources"   : ['script'/*, 'img', 'css', 'frame', 'link'*/],
  "ProcessExternalResources" : ['script'/*, 'frame', 'iframe'*/],
  "MutationEvents"           : '2.0',
  "QuerySelector"            : false
};

exports.applyDocumentFeatures = function(doc, features) {
  var i, maxFeatures = exports.availableDocumentFeatures.length,
      defaultFeatures = exports.defaultDocumentFeatures,
      j,
      k,
      featureName,
      featureSource;

  features = features || {};

  for (i=0; i<maxFeatures; i++) {
    featureName = exports.availableDocumentFeatures[i];
    if (typeof features[featureName] !== 'undefined') {
      featureSource = features[featureName];
    } else if (defaultFeatures[featureName]) {
      featureSource = defaultFeatures[featureName];
    } else {
      continue;
    }

    doc.implementation.removeFeature(featureName);

    if (typeof featureSource !== 'undefined') {
      if (featureSource instanceof Array) {
        k = featureSource.length;
        for (j=0; j<k; j++) {
          doc.implementation.addFeature(featureName, featureSource[j]);
        }
      } else {
        doc.implementation.addFeature(featureName, featureSource);
      }
    }
  }
};

exports.jQueryify = exports.jsdom.jQueryify = function (window /* path [optional], callback */) {

  if (!window || !window.document) { return; }

  var args = Array.prototype.slice.call(arguments),
      callback = (typeof(args[args.length - 1]) === 'function') && args.pop(),
      path,
      jQueryTag = window.document.createElement("script");

  if (args.length > 1 && typeof(args[1] === 'string')) {
    path = args[1];
  }

  var features = window.document.implementation._features;

  window.document.implementation.addFeature('FetchExternalResources', ['script']);
  window.document.implementation.addFeature('ProcessExternalResources', ['script']);
  window.document.implementation.addFeature('MutationEvents', ["1.0"]);
  jQueryTag.src = path || 'http://code.jquery.com/jquery-latest.js';
  window.document.body.appendChild(jQueryTag);

  jQueryTag.onload = function() {
    if (callback) {
      callback(window, window.jQuery);
    }

    window.document.implementation._features = features;
  };

  return window;
};

exports.env = exports.jsdom.env = function() {
  var
  args        = Array.prototype.slice.call(arguments),
  config      = exports.env.processArguments(args),
  callback    = config.done,
  processHTML = function(err, html) {

    html += '';
    if(err) {
      return callback(err);
    }

    config.scripts = config.scripts || [];
    if (typeof config.scripts === 'string') {
      config.scripts = [config.scripts];
    }

    var
    window     = exports.html(html, null, {
      features : {
        'FetchExternalResources' : false,
        'ProcessExternalResources' : false
      }
    }).createWindow(),
    features   = window.document.implementation._features,
    docsLoaded = 0,
    totalDocs  = config.scripts.length;
    readyState = null,
    errors     = null;

    if (!window || !window.document) {
      return callback(new Error('JSDOM: a window object could not be created.'));
    }

    window.document.implementation.addFeature('FetchExternalResources', ['script']);
    window.document.implementation.addFeature('ProcessExternalResources', ['script']);
    window.document.implementation.addFeature('MutationEvents', ['1.0']);

    if (config.scripts.length > 0) {
      config.scripts.forEach(function(src) {
        var
        script = window.document.createElement('script'),
        scriptComplete = function() {
          docsLoaded++;
          if (docsLoaded >= totalDocs) {
            window.document.implementation._features = features;
            callback(errors, window);
          }
        }

        script.onload = function() {
          scriptComplete()
        };

        script.onerror = function(e) {
          if (!errors) {
            errors = [];
          }
          errors.push(e.error);
          scriptComplete();
        };

        script.src = src;
        window.document.documentElement.appendChild(script);
      });
    } else {
      callback(errors, window);
    }
  };

  config.html += '';

  // Handle markup
  if (config.html.indexOf("\n") > 0 || config.html.match(/^\W*</)) {
    processHTML(null, config.html);

  // Handle url/file
  } else {
    var url = URL.parse(config.html);
    if (url.hostname) {
      request({uri: url}, function(err, request, body) {
        processHTML(err, body);
      });
    } else {
      fs.readFile(url.pathname, processHTML);
    }
  }
};

/*
  Since jsdom.env() is a helper for quickly and easily setting up a
  window with scripts and such already loaded into it, the arguments
  should be fairly flexible.  Here are the requirements

  1) collect `html` (url, string, or file on disk)  (STRING)
  2) load `code` into the window (array of scripts) (ARRAY)
  3) callback when resources are `done`             (FUNCTION)
  4) configuration                                  (OBJECT)

  Rules:
  + if there is one argument it had better be an object with atleast
    a `html` and `done` property (other properties are gravy)

  + arguments above are pulled out of the arguments and put into the
    config object that is returned
*/
exports.env.processArguments = function(args) {
  if (!args || !args.length || args.length < 1) {
    throw new Error('No arguments passed to jsdom.env().');
  }

  var
  props = {
    'html'    : true,
    'done'    : true,
    'scripts' : false,
    'config'  : false
  },
  propKeys = Object.keys(props),
  config = {
    code : []
  },
  l    = args.length
  ;
  if (l === 1) {
    config = args[0];
  } else {
    args.forEach(function(v) {
      var type = typeof v;
      if (!v) {
        return;
      }
      if (type === 'string' || v + '' === v) {
        config.html = v;
      } else if (type === 'object') {
        // Array
        if (v.length && v[0]) {
          config.scripts = v;
        } else {
          // apply missing required properties if appropriate
          propKeys.forEach(function(req) {

            if (typeof v[req] !== 'undefined' &&
                typeof config[req] === 'undefined') {

              config[req] = v[req];
              delete v[req];
            }
          })
          config.config = v;
        }
      } else if (type === 'function') {
        config.done = v;
      }
    });
  }

  propKeys.forEach(function(req) {
    var required = props[req];
    if (required && typeof config[req] === 'undefined') {
      throw new Error("jsdom.env requires a '" + req + "' argument");
    }
  });
  return config;
};<commit_msg>more html/body insertion completion<commit_after>
var dom      = exports.dom = require("./jsdom/level3/index").dom,
    request  = require('request'),
    fs       = require("fs"),
    pkg      = JSON.parse(fs.readFileSync(__dirname + "/../package.json"));
    URL      = require('url');

exports.defaultLevel = dom.level3.html;
exports.browserAugmentation = require("./jsdom/browser/index").browserAugmentation;
exports.windowAugmentation = require("./jsdom/browser/index").windowAugmentation;

exports.debugMode = false;

var createWindow = exports.createWindow = require("./jsdom/browser/index").createWindow;

exports.__defineGetter__('version', function() {
  return pkg.version;
});

exports.jsdom = function (html, level, options) {

  options = options || {};
  level   = level || exports.defaultLevel;

  if (!options.url) {
    options.url = module.parent.id == 'jsdom'   ?
                  module.parent.parent.filename :
                  module.parent.filename;
  }

  if (options.features && options.features.QuerySelector) {
    require("./jsdom/selectors/index").applyQuerySelectorPrototype(level);
  }

  var browser = exports.browserAugmentation(level, options),
      doc     = (browser.HTMLDocument)             ?
                 new browser.HTMLDocument(options) :
                 new browser.Document(options);

  exports.applyDocumentFeatures(doc, options.features);

  if (!!html) {
    doc.write(html + '')
  } else {
    doc.write('');
  }

  if (doc.close && !options.deferClose) {
    doc.close();
  }

  // Kept for backwards-compatibility. The window is lazily created when
  // document.parentWindow or document.defaultView is accessed.
  doc.createWindow = function() {
    // Remove ourself
    if (doc.createWindow) {
      delete doc.createWindow;
    }
    return doc.parentWindow;
  };

  return doc;
};

exports.html = function(html, level, options) {
  html += '';
  var htmlLowered = html.toLowerCase();
  // body
  if (!~htmlLowered.indexOf('<body')) {
    html = '' + html + '';
  }

  // html
  if (!~htmlLowered.indexOf('<html')) {
    html = '' + html + '';
  }
  return exports.jsdom(html, level, options);
};

exports.availableDocumentFeatures = [
  'FetchExternalResources',
  'ProcessExternalResources',
  'MutationEvents',
  'QuerySelector'
];

exports.defaultDocumentFeatures = {
  "FetchExternalResources"   : ['script'/*, 'img', 'css', 'frame', 'link'*/],
  "ProcessExternalResources" : ['script'/*, 'frame', 'iframe'*/],
  "MutationEvents"           : '2.0',
  "QuerySelector"            : false
};

exports.applyDocumentFeatures = function(doc, features) {
  var i, maxFeatures = exports.availableDocumentFeatures.length,
      defaultFeatures = exports.defaultDocumentFeatures,
      j,
      k,
      featureName,
      featureSource;

  features = features || {};

  for (i=0; i<maxFeatures; i++) {
    featureName = exports.availableDocumentFeatures[i];
    if (typeof features[featureName] !== 'undefined') {
      featureSource = features[featureName];
    } else if (defaultFeatures[featureName]) {
      featureSource = defaultFeatures[featureName];
    } else {
      continue;
    }

    doc.implementation.removeFeature(featureName);

    if (typeof featureSource !== 'undefined') {
      if (featureSource instanceof Array) {
        k = featureSource.length;
        for (j=0; j<k; j++) {
          doc.implementation.addFeature(featureName, featureSource[j]);
        }
      } else {
        doc.implementation.addFeature(featureName, featureSource);
      }
    }
  }
};

exports.jQueryify = exports.jsdom.jQueryify = function (window /* path [optional], callback */) {

  if (!window || !window.document) { return; }

  var args = Array.prototype.slice.call(arguments),
      callback = (typeof(args[args.length - 1]) === 'function') && args.pop(),
      path,
      jQueryTag = window.document.createElement("script");

  if (args.length > 1 && typeof(args[1] === 'string')) {
    path = args[1];
  }

  var features = window.document.implementation._features;

  window.document.implementation.addFeature('FetchExternalResources', ['script']);
  window.document.implementation.addFeature('ProcessExternalResources', ['script']);
  window.document.implementation.addFeature('MutationEvents', ["1.0"]);
  jQueryTag.src = path || 'http://code.jquery.com/jquery-latest.js';
  window.document.body.appendChild(jQueryTag);

  jQueryTag.onload = function() {
    if (callback) {
      callback(window, window.jQuery);
    }

    window.document.implementation._features = features;
  };

  return window;
};

exports.env = exports.jsdom.env = function() {
  var
  args        = Array.prototype.slice.call(arguments),
  config      = exports.env.processArguments(args),
  callback    = config.done,
  processHTML = function(err, html) {

    html += '';
    if(err) {
      return callback(err);
    }

    config.scripts = config.scripts || [];
    if (typeof config.scripts === 'string') {
      config.scripts = [config.scripts];
    }

    var
    window     = exports.html(html, null, {
      features : {
        'FetchExternalResources' : false,
        'ProcessExternalResources' : false
      }
    }).createWindow(),
    features   = window.document.implementation._features,
    docsLoaded = 0,
    totalDocs  = config.scripts.length;
    readyState = null,
    errors     = null;

    if (!window || !window.document) {
      return callback(new Error('JSDOM: a window object could not be created.'));
    }

    window.document.implementation.addFeature('FetchExternalResources', ['script']);
    window.document.implementation.addFeature('ProcessExternalResources', ['script']);
    window.document.implementation.addFeature('MutationEvents', ['1.0']);

    if (config.scripts.length > 0) {
      config.scripts.forEach(function(src) {
        var
        script = window.document.createElement('script'),
        scriptComplete = function() {
          docsLoaded++;
          if (docsLoaded >= totalDocs) {
            window.document.implementation._features = features;
            callback(errors, window);
          }
        }

        script.onload = function() {
          scriptComplete()
        };

        script.onerror = function(e) {
          if (!errors) {
            errors = [];
          }
          errors.push(e.error);
          scriptComplete();
        };

        script.src = src;
        window.document.documentElement.appendChild(script);
      });
    } else {
      callback(errors, window);
    }
  };

  config.html += '';

  // Handle markup
  if (config.html.indexOf("\n") > 0 || config.html.match(/^\W*</)) {
    processHTML(null, config.html);

  // Handle url/file
  } else {
    var url = URL.parse(config.html);
    if (url.hostname) {
      request({uri: url}, function(err, request, body) {
        processHTML(err, body);
      });
    } else {
      fs.readFile(url.pathname, processHTML);
    }
  }
};

/*
  Since jsdom.env() is a helper for quickly and easily setting up a
  window with scripts and such already loaded into it, the arguments
  should be fairly flexible.  Here are the requirements

  1) collect `html` (url, string, or file on disk)  (STRING)
  2) load `code` into the window (array of scripts) (ARRAY)
  3) callback when resources are `done`             (FUNCTION)
  4) configuration                                  (OBJECT)

  Rules:
  + if there is one argument it had better be an object with atleast
    a `html` and `done` property (other properties are gravy)

  + arguments above are pulled out of the arguments and put into the
    config object that is returned
*/
exports.env.processArguments = function(args) {
  if (!args || !args.length || args.length < 1) {
    throw new Error('No arguments passed to jsdom.env().');
  }

  var
  props = {
    'html'    : true,
    'done'    : true,
    'scripts' : false,
    'config'  : false
  },
  propKeys = Object.keys(props),
  config = {
    code : []
  },
  l    = args.length
  ;
  if (l === 1) {
    config = args[0];
  } else {
    args.forEach(function(v) {
      var type = typeof v;
      if (!v) {
        return;
      }
      if (type === 'string' || v + '' === v) {
        config.html = v;
      } else if (type === 'object') {
        // Array
        if (v.length && v[0]) {
          config.scripts = v;
        } else {
          // apply missing required properties if appropriate
          propKeys.forEach(function(req) {

            if (typeof v[req] !== 'undefined' &&
                typeof config[req] === 'undefined') {

              config[req] = v[req];
              delete v[req];
            }
          })
          config.config = v;
        }
      } else if (type === 'function') {
        config.done = v;
      }
    });
  }

  propKeys.forEach(function(req) {
    var required = props[req];
    if (required && typeof config[req] === 'undefined') {
      throw new Error("jsdom.env requires a '" + req + "' argument");
    }
  });
  return config;
};<|endoftext|>