<commit_before>/**
 * Created by Scott on 2/25/2015.
 */
define(['testharness', '../lib/request', '../lib/squaredb/squaredb'],
    function(harness, request, squareDB) {
        return {
            run: function () {
                "use strict";

                var db, table, query;

                harness.test(function () {
                    db = squareDB.createDB('Library');
                    harness.assert_true(squareDB.getDB('Library') !== undefined);
                }, "CREATE DATABASE Library  => happens in each test, checking here");

                harness.test(function () {
                    db = squareDB.dropDB('Library');
                    harness.assert_true(db && squareDB.getDB('Library') === undefined);
                }, "DROP DATABASE Library  => happens in each test, checking here");

                harness.test(function () {
                    db = squareDB.createDB('Library');
                    table = db.createTable('Books')('title');
                    harness.assert_true(table.hasOwnProperty('title'));
                }, "CREATE TABLE Books (title)");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');

                harness.test(function () {
                    db.createTable('Books')('title');
                    db.insertInto('Books')('title').values('Book of Foo');
                    query = db.select('title').from('Books').go();
                    harness.assert_true(query[0].title === 'Book of Foo');
                }, "INSERT INTO Books (title) VALUES ('Book of Foo')");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title');

                harness.test(function () {
                    var testPassed,
                        titlesArray;

                    db.insertInto('Books')('title').values('Alphabet Soup').values('Aliens').values('Baseball').values('Bats').values('Cats').values('Soup for the Soul');
                    titlesArray = db.select('title').from('Books').go();
                    testPassed = titlesArray.every(function (rowObj) {
                        var title = rowObj.title,
                            a = ['Alphabet Soup', 'Aliens', 'Baseball', 'Bats', 'Cats', 'Soup for the Soul'];

                        return title && a.indexOf(title) >= 0;
                    });
                    harness.assert_true(testPassed);
                }, "INSERT INTO Books (title) VALUES ('Alphabet Soup'),('Aliens'),('Baseball'),('Bats'),('Cats'),('Soup for the Soul')");

                // next test uses prerequisites from previous test...
                harness.test(function () {
                    query = db.select('title').from('Books').where('title', '===', 'Aliens').go();
                    harness.assert_true(query[0] && query[0].title === 'Aliens');
                }, "SELECT title FROM Books WHERE title = 'Aliens'");

                // keep going...
                harness.test(function () {
                    var passedTest;

                    query = db.select('title').from('Books').where('title', 'like', 'soup').go();
                    passedTest = query.length === 0;
                    if (passedTest) {
                        query = db.select('title').from('Books').where('title', 'like', '.*soup').go();
                        passedTest = query.length === 1 && query[0].title === 'Alphabet Soup';
                    }
                    if (passedTest) {
                        query = db.select('title').from('Books').where('title', 'like', 'ba.*').go();
                        passedTest = query.length === 2 && query[0].title === 'Baseball' && query[1].title === 'Bats';
                    }
                    if (passedTest) {
                        query = db.select('title').from('Books').where('title', 'like', '.*ou.*ou.*').go();
                        passedTest = query.length === 1 && query[0].title === 'Soup for the Soul';
                    }
                    harness.assert_true(passedTest);
                }, "SELECT title FROM Books WHERE title LIKE %ou%ou%  => plus 3 more 'LIKE' tests");

                harness.test(function () {
                    var passedTest;

                    query = db.select('title').from('Books').where('title', 'not like', '.*b.*').go();
                    passedTest = query.length === 3 && query[0].title === 'Aliens' && query[1].title === 'Cats' && query[2].title === 'Soup for the Soul';
                    harness.assert_true(passedTest);
                }, "SELECT title FROM Books WHERE title NOT LIKE %b%");

                harness.test(function () {
                    query = db.select('*').from('Books').go();
                    harness.assert_true(query.length === 6);
                }, "SELECT * FROM Books");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author');

                harness.test(function () {
                    var testPassed, returnSet;

                    db.insertInto('Books')('title', 'author').values('Baseball', 'Hank Aaron').values('Alphabet Soup', 'Abe Jones').values('Aliens', 'Abe Jones')
                        .values('Coffee Break', 'Bob Aaron').values('Bats', 'Wes Stacks').values('Cats', 'Kaitlyn Rose').values('Soup for the Soul', 'Flora Ivy');
                    returnSet = db.select('author').from('Books').where('author', 'like', '.*o.*s.*').go();
                    testPassed = returnSet.length === 3 && returnSet[0]['author'] === 'Abe Jones' && returnSet[1]['author'] === 'Abe Jones' && returnSet[2]['author'] === 'Kaitlyn Rose';
                    harness.assert_true(testPassed);
                }, "SELECT title FROM Books WHERE author LIKE %o%s%  => condition based on field not in SELECT");

                harness.test(function () {
                    // todo: special case when select is called empty?
                    // todo: missing where!!!!!!
                    var returnSet = db.select().distinct('author').from('Books').go(),
                        testPassed;
                    console.log('returnSet', returnSet);
                    testPassed = returnSet.length === 6 && returnSet[0]['author'] === 'Hank Aaron' && returnSet[1]['author'] === 'Abe Jones' && returnSet[2]['author'] === 'Bob Aaron';
                    harness.assert_true(testPassed);
                }, "SELECT DISTINCT author FROM Books WHERE author = 'Creepy Guy'");

                harness.test(function () {
                    db.update('Books').set('title', 'Green Eggs & Ham')('author', 'Dr. Seuss').where('title', '===', 'Bats').go();
                    query = db.select('title', 'author').from('Books').go();
                    harness.assert_true(query.length === 7 && query[4].title === 'Green Eggs & Ham' && query[4]['author'] === 'Dr. Seuss');
                }, "UPDATE Books SET title = 'Green Eggs & Ham', author = 'Dr. Seuss' WHERE title = 'Bats'");

                harness.test(function() {
                    query = db.select('title').from('Books').where('title', 'like', '.*break').and('author', 'like', '.*Aaron').go();
                    harness.assert_true(query[0].title === 'Coffee Break');
                }, "SELECT title FROM Books WHERE title like %break AND author like %Aaron");

                harness.test(function() {
                    query = db.select('title').from('Books').where('title', '===', 'Aliens').or('author', '===', 'Kaitlyn Rose').go();
                    harness.assert_true(query.length === 2 && query[0].title === 'Aliens' && query[1].title === 'Cats');
                }, "SELECT title FROM Books WHERE title = 'Aliens' OR author = 'Kaitlyn Rose'");

                harness.test(function() {
                    var d = db.delete().from('Books').where('author', '===', 'Hank Aaron').or('author', 'like', '.*ivy').go();
                    query = db.select('title', 'author').from('Books').where('author', '===', 'Hank Aaron').or('author', 'like', '.*ivy').go();
                    console.log('delete 1st and last title', Array.from(d.title));
                    console.log('delete 1st and last author', Array.from(d['author']));
                    harness.assert_true(query.length === 0);
                }, "DELETE FROM Books WHERE author === 'Hank Aaron' OR author like %ivy");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author');
                db.insertInto('Books')('title', 'author').values('Baseball', 'Hank Aaron').values('Alphabet Soup', 'Abe Jones').values('Aliens', 'Corey Dorey')
                    .values('Coffee Break', 'Bob Aaron').values('Bats', 'Creepy Guy').values('Cats', 'Kaitlyn Rose').values('Soup for the Soul', 'Flora Ivy');

                harness.test(function() {
                    var d = db.delete('author').from('Books').go();
                    query = db.select('author').from('Books').go();
                    console.log('delete author', Array.from(d['author']));
                    console.log('did not delete title', db.select('title').from('Books').go());
                    harness.assert_true(query.length === 0);
                }, "DELETE author FROM Books");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author');
                db.insertInto('Books')('title', 'author').values('Baseball', 'Hank Aaron').values('Alphabet Soup', 'Abe Jones').values('Aliens', 'Corey Dorey')
                    .values('Coffee Break', 'Bob Aaron').values('Bats', 'Creepy Guy').values('Cats', 'Kaitlyn Rose').values('Soup for the Soul', 'Flora Ivy');

                harness.test(function() {
                    var d = db.delete('*').from('Books').go();  // same as db.delete().from('Books').go();
                    var q1 = db.select('title').from('Books').go(),
                        q2 = db.select('author').from('Books').go();

                    console.log('delete title and author data from Books - Title', Array.from(d.title));
                    console.log('delete title and author data from Books - Author', Array.from(d.title));
                    harness.assert_true(q1.length === 0 && q2.length === 0);
                }, "DELETE * FROM Books => same as DELETE FROM Books");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author');
                db.insertInto('Books')('title', 'author').values('Baseball', 'Hank Aaron').values('Alphabet Soup', 'Abe Jones').values('Aliens', 'Corey Dorey')
                    .values('Coffee Break', 'Bob Aaron').values('Bats', 'Creepy Guy').values('Cats', 'Kaitlyn Rose').values('Soup for the Soul', 'Flora Ivy');

                harness.test(function() {
                    db.alterTable('Books').add('pages');
                    query = db.select('pages').from('Books').go();
                    harness.assert_true(query.length === 7);
                }, "ALTER TABLE Books ADD pages");

                harness.test(function() {
                    db.alterTable('Books').drop('pages');
                    query = db.select('pages').from('Books').go();
                    console.log('table', table);
                    harness.assert_true(query.length === 0);
                }, "ALTER TABLE Books DROP pages");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author', 'pages');
                db.insertInto('Books')('title', 'author', 'pages').values('Baseball', 'Hank Aaron', 1200).values('Alphabet Soup', 'Abe Jones', 540).values('Aliens', 'Corey Dorey', 210)
                    .values('Coffee Break', 'Bob Aaron', 65).values('Bats', 'Creepy Guy', 300).values('Cats', 'Kaitlyn Rose', 829).values('Soup for the Soul', 'Flora Ivy', 1200);

                harness.test(function() {
                    query = db.select().min('pages').from('Books').go();
                    harness.assert_true(query['pages'] === 65);
                }, "SELECT MIN('pages') FROM Books");

                harness.test(function() {
                    query = db.select().max('pages').from('Books').go();
                    harness.assert_true(query['pages'] === 1200);
                }, "SELECT MAX('pages') FROM Books");

                harness.test(function() {
                    query = db.select().sum('pages').from('Books').go();
                    harness.assert_true(query['pages'] === 3234);
                }, "SELECT SUM('pages') FROM Books");

                harness.test(function() {
                    query = db.select().avg('pages').from('Books').go();
                    harness.assert_true(query['pages'] === 462);
                }, "SELECT AVG('pages') FROM Books");

                harness.test(function() {
                    query = db.select().count('title').from('Books').go();
                    harness.assert_true(query['title'] === 7);
                }, "SELECT COUNT('title') FROM Books");

                harness.test(function() {
                    // total all not null rows in all columns inside this table
                    query = db.select().count('*').from('Books').go();
                    harness.assert_true(query === 21);
                }, "SELECT COUNT(*) FROM Books");

                harness.test(function() {
                    query = db.select('title').min('pages').from('Books').go();
                    console.log('Test 26', query);
                    harness.assert_true(query['title'] === 'Coffee Break');
                }, "SELECT title, COUNT(pages) FROM Books");

            }
        };
    }
);
<commit_msg>api update<commit_after>/**
 * Created by Scott on 2/25/2015.
 */
define(['testharness', '../lib/request', '../lib/squaredb/squaredb'],
    function(harness, request, squareDB) {
        return {
            run: function () {
                "use strict";

                var db, table, query;

                harness.test(function () {
                    db = squareDB.createDB('Library');
                    harness.assert_true(squareDB.use('Library') !== undefined);
                }, "CREATE DATABASE Library  => happens in each test, checking here");

                harness.test(function () {
                    db = squareDB.dropDB('Library');
                    harness.assert_true(db && squareDB.use('Library') === undefined);
                }, "DROP DATABASE Library  => happens in each test, checking here");

                harness.test(function () {
                    db = squareDB.createDB('Library');
                    table = db.createTable('Books')('title');
                    harness.assert_true(table.hasOwnProperty('title'));
                }, "CREATE TABLE Books (title)");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');

                harness.test(function () {
                    db.createTable('Books')('title');
                    db.insertInto('Books')('title').values('Book of Foo');
                    query = db.select('title').from('Books').go();
                    harness.assert_true(query[0].title === 'Book of Foo');
                }, "INSERT INTO Books (title) VALUES ('Book of Foo')");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title');

                harness.test(function () {
                    var testPassed,
                        titlesArray;

                    db.insertInto('Books')('title').values('Alphabet Soup').values('Aliens').values('Baseball').values('Bats').values('Cats').values('Soup for the Soul');
                    titlesArray = db.select('title').from('Books').go();
                    testPassed = titlesArray.every(function (rowObj) {
                        var title = rowObj.title,
                            a = ['Alphabet Soup', 'Aliens', 'Baseball', 'Bats', 'Cats', 'Soup for the Soul'];

                        return title && a.indexOf(title) >= 0;
                    });
                    harness.assert_true(testPassed);
                }, "INSERT INTO Books (title) VALUES ('Alphabet Soup'),('Aliens'),('Baseball'),('Bats'),('Cats'),('Soup for the Soul')");

                // next test uses prerequisites from previous test...
                harness.test(function () {
                    query = db.select('title').from('Books').where('title', '===', 'Aliens').go();
                    harness.assert_true(query[0] && query[0].title === 'Aliens');
                }, "SELECT title FROM Books WHERE title = 'Aliens'");

                // keep going...
                harness.test(function () {
                    var passedTest;

                    query = db.select('title').from('Books').where('title', 'like', 'soup').go();
                    passedTest = query.length === 0;
                    if (passedTest) {
                        query = db.select('title').from('Books').where('title', 'like', '.*soup').go();
                        passedTest = query.length === 1 && query[0].title === 'Alphabet Soup';
                    }
                    if (passedTest) {
                        query = db.select('title').from('Books').where('title', 'like', 'ba.*').go();
                        passedTest = query.length === 2 && query[0].title === 'Baseball' && query[1].title === 'Bats';
                    }
                    if (passedTest) {
                        query = db.select('title').from('Books').where('title', 'like', '.*ou.*ou.*').go();
                        passedTest = query.length === 1 && query[0].title === 'Soup for the Soul';
                    }
                    harness.assert_true(passedTest);
                }, "SELECT title FROM Books WHERE title LIKE %ou%ou%  => plus 3 more 'LIKE' tests");

                harness.test(function () {
                    var passedTest;

                    query = db.select('title').from('Books').where('title', 'not like', '.*b.*').go();
                    passedTest = query.length === 3 && query[0].title === 'Aliens' && query[1].title === 'Cats' && query[2].title === 'Soup for the Soul';
                    harness.assert_true(passedTest);
                }, "SELECT title FROM Books WHERE title NOT LIKE %b%");

                harness.test(function () {
                    query = db.select('*').from('Books').go();
                    harness.assert_true(query.length === 6);
                }, "SELECT * FROM Books");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author');

                harness.test(function () {
                    var testPassed, returnSet;

                    db.insertInto('Books')('title', 'author').values('Baseball', 'Hank Aaron').values('Alphabet Soup', 'Abe Jones').values('Aliens', 'Abe Jones')
                        .values('Coffee Break', 'Bob Aaron').values('Bats', 'Wes Stacks').values('Cats', 'Kaitlyn Rose').values('Soup for the Soul', 'Flora Ivy');
                    returnSet = db.select('author').from('Books').where('author', 'like', '.*o.*s.*').go();
                    testPassed = returnSet.length === 3 && returnSet[0]['author'] === 'Abe Jones' && returnSet[1]['author'] === 'Abe Jones' && returnSet[2]['author'] === 'Kaitlyn Rose';
                    harness.assert_true(testPassed);
                }, "SELECT title FROM Books WHERE author LIKE %o%s%  => condition based on field not in SELECT");

                harness.test(function () {
                    // todo: special case when select is called empty?
                    // todo: missing where!!!!!!
                    var returnSet = db.select().distinct('author').from('Books').go(),
                        testPassed;
                    console.log('returnSet', returnSet);
                    testPassed = returnSet.length === 6 && returnSet[0]['author'] === 'Hank Aaron' && returnSet[1]['author'] === 'Abe Jones' && returnSet[2]['author'] === 'Bob Aaron';
                    harness.assert_true(testPassed);
                }, "SELECT DISTINCT author FROM Books WHERE author = 'Creepy Guy'");

                harness.test(function () {
                    db.update('Books').set('title', 'Green Eggs & Ham')('author', 'Dr. Seuss').where('title', '===', 'Bats').go();
                    query = db.select('title', 'author').from('Books').go();
                    harness.assert_true(query.length === 7 && query[4].title === 'Green Eggs & Ham' && query[4]['author'] === 'Dr. Seuss');
                }, "UPDATE Books SET title = 'Green Eggs & Ham', author = 'Dr. Seuss' WHERE title = 'Bats'");

                harness.test(function() {
                    query = db.select('title').from('Books').where('title', 'like', '.*break').and('author', 'like', '.*Aaron').go();
                    harness.assert_true(query[0].title === 'Coffee Break');
                }, "SELECT title FROM Books WHERE title like %break AND author like %Aaron");

                harness.test(function() {
                    query = db.select('title').from('Books').where('title', '===', 'Aliens').or('author', '===', 'Kaitlyn Rose').go();
                    harness.assert_true(query.length === 2 && query[0].title === 'Aliens' && query[1].title === 'Cats');
                }, "SELECT title FROM Books WHERE title = 'Aliens' OR author = 'Kaitlyn Rose'");

                harness.test(function() {
                    var d = db.delete().from('Books').where('author', '===', 'Hank Aaron').or('author', 'like', '.*ivy').go();
                    query = db.select('title', 'author').from('Books').where('author', '===', 'Hank Aaron').or('author', 'like', '.*ivy').go();
                    console.log('delete 1st and last title', Array.from(d.title));
                    console.log('delete 1st and last author', Array.from(d['author']));
                    harness.assert_true(query.length === 0);
                }, "DELETE FROM Books WHERE author === 'Hank Aaron' OR author like %ivy");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author');
                db.insertInto('Books')('title', 'author').values('Baseball', 'Hank Aaron').values('Alphabet Soup', 'Abe Jones').values('Aliens', 'Corey Dorey')
                    .values('Coffee Break', 'Bob Aaron').values('Bats', 'Creepy Guy').values('Cats', 'Kaitlyn Rose').values('Soup for the Soul', 'Flora Ivy');

                harness.test(function() {
                    var d = db.delete('author').from('Books').go();
                    query = db.select('author').from('Books').go();
                    console.log('delete author', Array.from(d['author']));
                    console.log('did not delete title', db.select('title').from('Books').go());
                    harness.assert_true(query.length === 0);
                }, "DELETE author FROM Books");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author');
                db.insertInto('Books')('title', 'author').values('Baseball', 'Hank Aaron').values('Alphabet Soup', 'Abe Jones').values('Aliens', 'Corey Dorey')
                    .values('Coffee Break', 'Bob Aaron').values('Bats', 'Creepy Guy').values('Cats', 'Kaitlyn Rose').values('Soup for the Soul', 'Flora Ivy');

                harness.test(function() {
                    var d = db.delete('*').from('Books').go();  // same as db.delete().from('Books').go();
                    var q1 = db.select('title').from('Books').go(),
                        q2 = db.select('author').from('Books').go();

                    console.log('delete title and author data from Books - Title', Array.from(d.title));
                    console.log('delete title and author data from Books - Author', Array.from(d.title));
                    harness.assert_true(q1.length === 0 && q2.length === 0);
                }, "DELETE * FROM Books => same as DELETE FROM Books");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author');
                db.insertInto('Books')('title', 'author').values('Baseball', 'Hank Aaron').values('Alphabet Soup', 'Abe Jones').values('Aliens', 'Corey Dorey')
                    .values('Coffee Break', 'Bob Aaron').values('Bats', 'Creepy Guy').values('Cats', 'Kaitlyn Rose').values('Soup for the Soul', 'Flora Ivy');

                harness.test(function() {
                    db.alterTable('Books').add('pages');
                    query = db.select('pages').from('Books').go();
                    harness.assert_true(query.length === 7);
                }, "ALTER TABLE Books ADD pages");

                harness.test(function() {
                    db.alterTable('Books').drop('pages');
                    query = db.select('pages').from('Books').go();
                    console.log('table', table);
                    harness.assert_true(query.length === 0);
                }, "ALTER TABLE Books DROP pages");

                // tear-down & setup for next test
                db = squareDB.dropDB('Library');
                db = squareDB.createDB('Library');
                table = db.createTable('Books')('title', 'author', 'pages');
                db.insertInto('Books')('title', 'author', 'pages').values('Baseball', 'Hank Aaron', 1200).values('Alphabet Soup', 'Abe Jones', 540).values('Aliens', 'Corey Dorey', 210)
                    .values('Coffee Break', 'Bob Aaron', 65).values('Bats', 'Creepy Guy', 300).values('Cats', 'Kaitlyn Rose', 829).values('Soup for the Soul', 'Flora Ivy', 1200);

                harness.test(function() {
                    query = db.select().min('pages').from('Books').go();
                    harness.assert_true(query['pages'] === 65);
                }, "SELECT MIN('pages') FROM Books");

                harness.test(function() {
                    query = db.select().max('pages').from('Books').go();
                    harness.assert_true(query['pages'] === 1200);
                }, "SELECT MAX('pages') FROM Books");

                harness.test(function() {
                    query = db.select().sum('pages').from('Books').go();
                    harness.assert_true(query['pages'] === 3234);
                }, "SELECT SUM('pages') FROM Books");

                harness.test(function() {
                    query = db.select().avg('pages').from('Books').go();
                    harness.assert_true(query['pages'] === 462);
                }, "SELECT AVG('pages') FROM Books");

                harness.test(function() {
                    query = db.select().count('title').from('Books').go();
                    harness.assert_true(query['title'] === 7);
                }, "SELECT COUNT('title') FROM Books");

                harness.test(function() {
                    // total all not null rows in all columns inside this table
                    query = db.select().count('*').from('Books').go();
                    harness.assert_true(query === 21);
                }, "SELECT COUNT(*) FROM Books");

                harness.test(function() {
                    query = db.select('title').min('pages').from('Books').go();
                    console.log('Test 26', query);
                    harness.assert_true(query['title'] === 'Coffee Break');
                }, "SELECT title, COUNT(pages) FROM Books");

            }
        };
    }
);
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

couchTests.stats = function(debug) {

  function newDb(name, doSetup) {
    var db = new CouchDB(name, {"X-Couch-Full-Commit": "false"});
    if(doSetup) {
      db.deleteDb();
      db.createDb();
    }
    return db;
  };

  function getStat(mod, key) {
    return CouchDB.requestStats(mod, key, true);
  };

  function doView(db) {
    var designDoc = {
      _id:"_design/test", // turn off couch.js id escaping?
      language: "javascript",
      views: {
        all_docs: {map: "function(doc) {emit(doc.integer, null);}"},
      }
    };
    db.save(designDoc);
    db.view("test/all_docs");
  };

  function runTest(mod, key, funcs) {
    var db = newDb("test_suite_db", true);
    if(funcs.setup) funcs.setup(db);
    var before = getStat(mod, key).current;
    if(funcs.run) funcs.run(db);
    var after = getStat(mod, key).current;
    if(funcs.test) funcs.test(before, after);
  }

  if (debug) debugger;

  (function() {
    var db = newDb("test_suite_db");
    db.deleteDb();

    var before = getStat("couchdb", "open_databases").current;
    db.createDb();
    var after = getStat("couchdb", "open_databases").current;
    TEquals(before+1, after, "Creating a db increments open db count.");
  })();

  runTest("couchdb", "open_databases", {
    setup: function() {restartServer();},
    run: function(db) {db.open("123");},
    test: function(before, after) {
      TEquals(before+1, after, "Opening a db increments open db count.");
    }
  });

  runTest("couchdb", "open_databases", {
    run: function(db) {db.deleteDb();},
    test: function(before, after) {
      TEquals(before-1, after, "Deleting a db decrements open db count.");
    }
  });

  (function() {
    restartServer();
    var max = 5;

    var testFun = function() {
      var pre_dbs = getStat("couchdb", "open_databases").current || 0;
      var pre_files = getStat("couchdb", "open_os_files").current || 0;

      for(var i = 0; i < max*2; i++) {
        newDb("test_suite_db_" + i, true);
      }

      var open_dbs = getStat("couchdb", "open_databases").current;
      TEquals(open_dbs > 0, true, "We actually opened some dbs.");
      TEquals(open_dbs, max, "We only have max db's open.");

      for(var i = 0; i < max * 2; i++) {
        newDb("test_suite_db_" + i).deleteDb();
      }

      var post_dbs = getStat("couchdb", "open_databases").current;
      var post_files = getStat("couchdb", "open_os_files").current;
      TEquals(pre_dbs, post_dbs, "We have the same number of open dbs.");
      TEquals(pre_files, post_files, "We have the same number of open files.");
    };

    run_on_modified_server(
      [{section: "couchdb", key: "max_dbs_open", value: "5"}],
      testFun
    );
  })();

  // Just fetching the before value is the extra +1 in test
  runTest("httpd", "requests", {
    run: function() {CouchDB.request("GET", "/");},
    test: function(before, after) {
      TEquals(before+2, after, "Request counts are incremented properly.");
    }
  });

  runTest("couchdb", "database_reads", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {db.open("test");},
    test: function(before, after) {
      TEquals(before+1, after, "Reading a doc increments docs reads.");
    }
  });

  runTest("couchdb", "database_reads", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {db.request("GET", "/");},
    test: function(before, after) {
      TEquals(before, after, "Only doc reads increment doc reads.");
    }
  });

  runTest("couchdb", "database_reads", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {db.open("test", {"open_revs": "all"});},
    test: function(before, after) {
      TEquals(before+1, after, "Reading doc revs increments docs reads.");
    }
  });

  runTest("couchdb", "database_writes", {
    run: function(db) {db.save({"a": "1"});},
    test: function(before, after) {
      TEquals(before+1, after, "Saving docs incrememnts doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    run: function(db) {
      CouchDB.request("POST", "/test_suite_db", {body: '{"a": "1"}'})
    },
    test: function(before, after) {
      TEquals(before+1, after, "POST'ing new docs increments doc writes.");
    }
  })

  runTest("couchdb", "database_writes", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {var doc = db.open("test"); db.save(doc);},
    test: function(before, after) {
      TEquals(before+1, after, "Updating docs incrememnts doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {var doc = db.open("test"); db.deleteDoc(doc);},
    test: function(before, after) {
      TEquals(before+1, after, "Deleting docs increments doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {
      CouchDB.request("COPY", "/test_suite_db/test", {
        headers: {"Destination": "copy_of_test"}
      });
    },
    test: function(before, after) {
      TEquals(before+1, after, "Copying docs increments doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    run: function() {
      CouchDB.request("PUT", "/test_suite_db/bin_doc2/foo2.txt", {
        body: "This is no base64 encoded test",
        headers: {"Content-Type": "text/plain;charset=utf-8"}
      });
    },
    test: function(before, after) {
      TEquals(before+1, after, "Create with attachment increments doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {
      var doc = db.open("test");
      CouchDB.request("PUT", "/test_suite_db/test/foo2.txt?rev=" + doc._rev, {
        body: "This is no base64 encoded text",
        headers: {"Content-Type": "text/plainn;charset=utf-8"}
      });
    },
    test: function(before, after) {
      TEquals(before+1, after, "Adding attachment increments doc writes.");
    }
  });

  runTest("httpd", "bulk_requests", {
    run: function(db) {db.bulkSave(makeDocs(5));},
    test: function(before, after) {
      TEquals(before+1, after, "The bulk_requests counter is incremented.");
    }
  });

  runTest("httpd", "view_reads", {
    run: function(db) {doView(db);},
    test: function(before, after) {
      TEquals(before+1, after, "Reading a view increments view reads.");
    }
  });

  runTest("httpd", "view_reads", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {db.open("test");},
    test: function(before, after) {
      TEquals(before, after, "Reading a doc doesn't increment view reads.");
    }
  });

  runTest("httpd", "temporary_view_reads", {
    run: function(db) {db.query(function(doc) {emit(doc._id)})},
    test: function(before, after) {
      TEquals(before+1, after, "Temporary views have their own counter.");
    }
  });

  runTest("httpd", "temporary_view_reads", {
    run: function(db) {doView(db);},
    test: function(before, after) {
      TEquals(before, after, "Permanent views don't affect temporary views.");
    }
  });

  runTest("httpd", "view_reads", {
    run: function(db) {db.query(function(doc) {emit(doc._id)});},
    test: function(before, after) {
      TEquals(before, after, "Temporary views don't affect permanent views.");
    }
  });

  // Relies on getting the stats values being GET requests.
  runTest("httpd_request_methods", "GET", {
    test: function(before, after) {
      TEquals(before+1, after, "Get requests are incremented properly.");
    }
  });

  runTest("httpd_request_methods", "GET", {
    run: function() {CouchDB.request("POST", "/");},
    test: function(before, after) {
      TEquals(before+1, after, "POST requests don't affect GET counter.");
    }
  });

  runTest("httpd_request_methods", "POST", {
    run: function() {CouchDB.request("POST", "/");},
    test: function(before, after) {
      TEquals(before+1, after, "POST requests are incremented properly.");
    }
  });

  runTest("httpd_status_codes", "404", {
    run: function() {CouchDB.request("GET", "/nonexistant_db");},
    test: function(before, after) {
      TEquals(before+1, after, "Increments 404 counter on db not found.");
    }
  });

  runTest("httpd_status_codes", "404", {
    run: function() {CouchDB.request("GET", "/");},
    test: function(before, after) {
      TEquals(before, after, "Getting DB info doesn't increment 404's");
    }
  });

  (function() {
    var aggregates = [
      "current",
      "description",
      "mean",
      "min",
      "max",
      "stddev",
      "sum"
    ];
    var summary = JSON.parse(CouchDB.request("GET", "/_stats", {
      headers: {"Accept": "application/json"}
    }).responseText);
    for(var i in summary) {
      for(var j in summary[i]) {
        for(var k in summary[i][j]) {
          T(aggregates.indexOf(k) >= 0, "Unknown property name: " + j);
        }
        for(var k in aggregates) {
          var mesg = "Missing required property: " + aggregates[k];
          T(summary[i][j][aggregates[k]] !== undefined, mesg);
        }
      }
    }
  })();
};
<commit_msg>Fixed a bug preventing active_dbs from working.<commit_after>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

couchTests.stats = function(debug) {

  function newDb(name, doSetup) {
    var db = new CouchDB(name, {"X-Couch-Full-Commit": "false"});
    if(doSetup) {
      db.deleteDb();
      db.createDb();
    }
    return db;
  };

  function getStat(mod, key) {
    return CouchDB.requestStats(mod, key, true);
  };

  function doView(db) {
    var designDoc = {
      _id:"_design/test", // turn off couch.js id escaping?
      language: "javascript",
      views: {
        all_docs: {map: "function(doc) {emit(doc.integer, null);}"},
      }
    };
    db.save(designDoc);
    db.view("test/all_docs");
  };

  function runTest(mod, key, funcs) {
    var db = newDb("test_suite_db", true);
    if(funcs.setup) funcs.setup(db);
    var before = getStat(mod, key).current;
    if(funcs.run) funcs.run(db);
    var after = getStat(mod, key).current;
    if(funcs.test) funcs.test(before, after);
  }

  if (debug) debugger;

  (function() {
    var db = newDb("test_suite_db");
    db.deleteDb();

    var before = getStat("couchdb", "open_databases").current;
    db.createDb();
    var after = getStat("couchdb", "open_databases").current;
    TEquals(before+1, after, "Creating a db increments open db count.");
  })();

  runTest("couchdb", "open_databases", {
    setup: function() {restartServer();},
    run: function(db) {db.open("123");},
    test: function(before, after) {
      TEquals(before+1, after, "Opening a db increments open db count.");
    }
  });

  runTest("couchdb", "open_databases", {
    run: function(db) {db.deleteDb();},
    test: function(before, after) {
      TEquals(before-1, after, "Deleting a db decrements open db count.");
    }
  });

  (function() {
    restartServer();
    var max = 5;

    var testFun = function() {
      var pre_dbs = getStat("couchdb", "open_databases").current || 0;
      var pre_files = getStat("couchdb", "open_os_files").current || 0;

      // We have to make sure that as we open the max'th database
      // that we've waited for more than 1 second since opening
      // the first database so that any delayed commits will be
      // flushed.
      var times = [];
      for(var i = 0; i < max*2; i++) {
        if(i >= max) {
          var msecs = (new Date()).getTime() - times[i-max];
          if(msecs < 1000) {
            CouchDB.request("GET", "/_sleep?time=" + (msecs+250));
          }
        }
        db = newDb("test_suite_db_" + i, true);
        times.push((new Date()).getTime());

        // Trigger a delayed commit
        db.save({_id: "" + i, "lang": "Awesome!"});
      }

      var open_dbs = getStat("couchdb", "open_databases").current;
      TEquals(open_dbs > 0, true, "We actually opened some dbs.");
      TEquals(open_dbs, max, "We only have max db's open.");

      for(var i = 0; i < max * 2; i++) {
        newDb("test_suite_db_" + i).deleteDb();
      }

      var post_dbs = getStat("couchdb", "open_databases").current;
      var post_files = getStat("couchdb", "open_os_files").current;
      TEquals(pre_dbs, post_dbs, "We have the same number of open dbs.");
      TEquals(pre_files, post_files, "We have the same number of open files.");
    };

    run_on_modified_server(
      [{section: "couchdb", key: "max_dbs_open", value: "5"}],
      testFun
    );
  })();

  // Just fetching the before value is the extra +1 in test
  runTest("httpd", "requests", {
    run: function() {CouchDB.request("GET", "/");},
    test: function(before, after) {
      TEquals(before+2, after, "Request counts are incremented properly.");
    }
  });

  runTest("couchdb", "database_reads", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {db.open("test");},
    test: function(before, after) {
      TEquals(before+1, after, "Reading a doc increments docs reads.");
    }
  });

  runTest("couchdb", "database_reads", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {db.request("GET", "/");},
    test: function(before, after) {
      TEquals(before, after, "Only doc reads increment doc reads.");
    }
  });

  runTest("couchdb", "database_reads", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {db.open("test", {"open_revs": "all"});},
    test: function(before, after) {
      TEquals(before+1, after, "Reading doc revs increments docs reads.");
    }
  });

  runTest("couchdb", "database_writes", {
    run: function(db) {db.save({"a": "1"});},
    test: function(before, after) {
      TEquals(before+1, after, "Saving docs incrememnts doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    run: function(db) {
      CouchDB.request("POST", "/test_suite_db", {body: '{"a": "1"}'})
    },
    test: function(before, after) {
      TEquals(before+1, after, "POST'ing new docs increments doc writes.");
    }
  })

  runTest("couchdb", "database_writes", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {var doc = db.open("test"); db.save(doc);},
    test: function(before, after) {
      TEquals(before+1, after, "Updating docs incrememnts doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {var doc = db.open("test"); db.deleteDoc(doc);},
    test: function(before, after) {
      TEquals(before+1, after, "Deleting docs increments doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {
      CouchDB.request("COPY", "/test_suite_db/test", {
        headers: {"Destination": "copy_of_test"}
      });
    },
    test: function(before, after) {
      TEquals(before+1, after, "Copying docs increments doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    run: function() {
      CouchDB.request("PUT", "/test_suite_db/bin_doc2/foo2.txt", {
        body: "This is no base64 encoded test",
        headers: {"Content-Type": "text/plain;charset=utf-8"}
      });
    },
    test: function(before, after) {
      TEquals(before+1, after, "Create with attachment increments doc writes.");
    }
  });

  runTest("couchdb", "database_writes", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {
      var doc = db.open("test");
      CouchDB.request("PUT", "/test_suite_db/test/foo2.txt?rev=" + doc._rev, {
        body: "This is no base64 encoded text",
        headers: {"Content-Type": "text/plainn;charset=utf-8"}
      });
    },
    test: function(before, after) {
      TEquals(before+1, after, "Adding attachment increments doc writes.");
    }
  });

  runTest("httpd", "bulk_requests", {
    run: function(db) {db.bulkSave(makeDocs(5));},
    test: function(before, after) {
      TEquals(before+1, after, "The bulk_requests counter is incremented.");
    }
  });

  runTest("httpd", "view_reads", {
    run: function(db) {doView(db);},
    test: function(before, after) {
      TEquals(before+1, after, "Reading a view increments view reads.");
    }
  });

  runTest("httpd", "view_reads", {
    setup: function(db) {db.save({"_id": "test"});},
    run: function(db) {db.open("test");},
    test: function(before, after) {
      TEquals(before, after, "Reading a doc doesn't increment view reads.");
    }
  });

  runTest("httpd", "temporary_view_reads", {
    run: function(db) {db.query(function(doc) {emit(doc._id)})},
    test: function(before, after) {
      TEquals(before+1, after, "Temporary views have their own counter.");
    }
  });

  runTest("httpd", "temporary_view_reads", {
    run: function(db) {doView(db);},
    test: function(before, after) {
      TEquals(before, after, "Permanent views don't affect temporary views.");
    }
  });

  runTest("httpd", "view_reads", {
    run: function(db) {db.query(function(doc) {emit(doc._id)});},
    test: function(before, after) {
      TEquals(before, after, "Temporary views don't affect permanent views.");
    }
  });

  // Relies on getting the stats values being GET requests.
  runTest("httpd_request_methods", "GET", {
    test: function(before, after) {
      TEquals(before+1, after, "Get requests are incremented properly.");
    }
  });

  runTest("httpd_request_methods", "GET", {
    run: function() {CouchDB.request("POST", "/");},
    test: function(before, after) {
      TEquals(before+1, after, "POST requests don't affect GET counter.");
    }
  });

  runTest("httpd_request_methods", "POST", {
    run: function() {CouchDB.request("POST", "/");},
    test: function(before, after) {
      TEquals(before+1, after, "POST requests are incremented properly.");
    }
  });

  runTest("httpd_status_codes", "404", {
    run: function() {CouchDB.request("GET", "/nonexistant_db");},
    test: function(before, after) {
      TEquals(before+1, after, "Increments 404 counter on db not found.");
    }
  });

  runTest("httpd_status_codes", "404", {
    run: function() {CouchDB.request("GET", "/");},
    test: function(before, after) {
      TEquals(before, after, "Getting DB info doesn't increment 404's");
    }
  });

  (function() {
    var aggregates = [
      "current",
      "description",
      "mean",
      "min",
      "max",
      "stddev",
      "sum"
    ];
    var summary = JSON.parse(CouchDB.request("GET", "/_stats", {
      headers: {"Accept": "application/json"}
    }).responseText);
    for(var i in summary) {
      for(var j in summary[i]) {
        for(var k in summary[i][j]) {
          T(aggregates.indexOf(k) >= 0, "Unknown property name: " + j);
        }
        for(var k in aggregates) {
          var mesg = "Missing required property: " + aggregates[k];
          T(summary[i][j][aggregates[k]] !== undefined, mesg);
        }
      }
    }
  })();
};
<|endoftext|>


<commit_before>/*
 * @package jsDAV
 * @subpackage DAV
 * @copyright Copyright(c) 2011 Ajax.org B.V. <info AT ajax DOT org>
 * @author Mike de Boer <info AT mikedeboer DOT nl>
 * @license http://github.com/mikedeboer/jsDAV/blob/master/LICENSE MIT License
 */

var Http   = require("http"),
    Url    = require("url"),
    Sys    = require("sys"),
    Fs     = require("fs"),
    Exc    = require("./exceptions"),

    // DAV classes used directly by the Server object
    jsDAV                             = require("./../jsdav"),
    jsDAV_Handler                     = require("./handler").jsDAV_Handler,
    jsDAV_SimpleDirectory             = require("./simpleDirectory").jsDAV_SimpleDirectory,
    jsDAV_ObjectTree                  = require("./objectTree").jsDAV_ObjectTree,
    jsDAV_Tree_Filesystem             = require("./tree/filesystem").jsDAV_Tree_Filesystem;

exports.DEFAULT_PORT   = 41197;
exports.DEFAULT_HOST   = "127.0.0.1";
exports.DEFAULT_TMPDIR = (function() {
    var value,
        def     = "/tmp",
        envVars = ["TMPDIR", "TMP", "TEMP"],
        i       = 0,
        l       = envVars.length;
    for(; i < l; ++i) {
        value = process.env[envVars[i]];
        if (value)
            return Fs.realpathSync(value).replace(/\/+$/, "");
    }
    return Fs.realpathSync(def).replace(/\/+$/, "");
})();
/**
 * jsDAV version number
 */
exports.VERSION = "0.1";

function Server(options) {
    /**
     * This is a flag that allow or not showing file, line and code
     * of the exception in the returned XML
     *
     * @var bool
     */
    this.debugExceptions = exports.debugMode;
    if (options && typeof options.standalone == "undefined")
        options.standalone = true;
    this.options = options;

    if (options && typeof options.tree == "object" && options.tree.hasFeature(jsDAV.__TREE__)) {
        this.tree = options.tree;
    }
    else if (options && typeof options.node == "object" && options.node.hasFeature(jsDAV.__INODE__)) {
        this.tree = new jsDAV_ObjectTree(options.node, options);
    }
    else if (options && typeof options.type == "string") {
        if (options.type == "sftp") {
            var jsDAV_Tree_Sftp = require("./tree/sftp").jsDAV_Tree_Sftp;
            this.tree = new jsDAV_Tree_Sftp(options);
        }
        else if (options.type == "ftp") {
            var jsDAV_Tree_Ftp = require("./tree/ftp").jsDAV_Tree_Ftp;
            this.tree = new jsDAV_Tree_Ftp(options);
        }
    }
    else if (options && typeof options.node == "string" && options.node.indexOf("/") > -1) {
        this.tree = new jsDAV_Tree_Filesystem(options.node, options);
    }
    else if (!options) {
        var root  = new jsDAV_SimpleDirectory("root");
        this.tree = new jsDAV_ObjectTree(root, options);
    }
    else {
        throw new Exc.jsDAV_Exception("Invalid argument passed to constructor. "
            + "Argument must either be an instance of jsDAV_Tree, jsDAV_iNode, "
            + "a valid path to a location on the local filesystem or null");
    }

    this.tmpDir = ((options && options.tmpDir) || exports.DEFAULT_TMPDIR).replace(/\/+$/, "");

    if (options.server && options.mount) { //use an existing server object
        var _self = this;
        this.setBaseUri("/" + options.mount.replace(/^\/+/, ""));

        if (options.standalone) {
            var listeners = options.server.listeners("request");
            options.server.removeAllListeners("request");

            options.server.addListener("request", function(req, resp) {
                var path = Url.parse(req.url).pathname;
                if (path.charAt(path.length - 1) != "/")
                    path = path + "/";
                if (path.indexOf(_self.baseUri) === 0) {
                    _self.exec(req, resp);
                }
                else {
                    for (var i = 0, len = listeners.length; i < len; ++i)
                        listeners[i].call(options.server, req, resp);
                }
            });
        }
    }
    else {
        this.setBaseUri(this.guessBaseUri());

        Http.Server.call(this, this.exec);
    }

}

(function() {
    /**
     * The base uri
     *
     * @var string
     */
    this.baseUri = "/";

    /**
     * The tree object
     *
     * @var jsDAV_Tree
     */
    this.tree = null;

    /**
     * The list of plugins
     *
     * @var array
     */
    this.plugins = {};

    /**
     * Called when an http request comes in, pass it on to the Handler
     *
     * @param {ServerRequest}  req
     * @param {ServerResponse} resp
     * @return void
     */
    this.exec = function(req, resp) {
        new jsDAV_Handler(this, req, resp);
    };

    /**
     * Sets the base server uri
     *
     * @param  {String} uri
     * @return {void}
     */
    this.setBaseUri = function(uri) {
        // If the baseUri does not end with a slash, we must add it
        if (uri.charAt(uri.length - 1) !== "/")
            uri += "/";

        this.baseUri = uri;
    };

    /**
     * Returns the base responding uri
     *
     * @return {String}
     */
    this.getBaseUri = function() {
        return this.baseUri;
    };

    /**
     * This method attempts to detect the base uri.
     * Only the PATH_INFO variable is considered.
     *
     * If this variable is not set, the root (/) is assumed.
     *
     * @return {String}
     * @throws {Error}
     */
    this.guessBaseUri = function(handler) {
        var pos, pathInfo, uri;

        if (handler && handler.httpRequest) {
            uri      = handler.httpRequest.url;
            pathInfo = Url.parse(uri).pathname;
        }

        // If PATH_INFO is not found, we just return /
        if (pathInfo) {
            // We need to make sure we ignore the QUERY_STRING part
            if ((pos = uri.indexOf("?")) > -1)
                uri = uri.substr(0, pos);

            // PATH_INFO is only set for urls, such as: /example.php/path
            // in that case PATH_INFO contains "/path".
            // Note that REQUEST_URI is percent encoded, while PATH_INFO is
            // not, Therefore they are only comparable if we first decode
            // REQUEST_INFO as well.
            var decodedUri = unescape(uri);

            // A simple sanity check:
            if (decodedUri.substr(decodedUri.length - pathInfo.length) === pathInfo) {
                var baseUri = decodedUri.substr(0, decodedUri.length - pathInfo.length);
                return Util.rtrim(baseUri, "/") + "/";
            }

            throw new Exc.jsDAV_Exception("The REQUEST_URI (" + uri
                + ") did not end with the contents of PATH_INFO (" + pathInfo
                + "). This server might be misconfigured.");
        }

        // The fallback is that we're just going to assume the server root.
        return "/";
    };

    /**
     * Auto-load bundled plugins.
     */
    var _self = this;
    Fs.readdirSync(__dirname + "/plugins").forEach(function(filename){
        if (/\.js$/.test(filename)) {
            var name = filename.substr(0, filename.lastIndexOf('.'));
            _self.plugins[name] = require("./plugins/" + name);
        }
    });
}).call(Server.prototype = Http.Server.prototype);

exports.createServer = function(options, port, host) {
    port = port || exports.DEFAULT_PORT;
    host = host || exports.DEFAULT_HOST;

    var server = new Server(options);
    server.listen(port, host, function() {
        Sys.puts("jsDAV server running on '" + host + "' port " + port);
    });
    return server;
};

exports.mount = function(options) {
    var s = new Server(options);
    s.unmount = function() {
        if (this.tree.unmount)
            this.tree.unmount();
    };
    return s;
};
<commit_msg>make url detectable by c9<commit_after>/*
 * @package jsDAV
 * @subpackage DAV
 * @copyright Copyright(c) 2011 Ajax.org B.V. <info AT ajax DOT org>
 * @author Mike de Boer <info AT mikedeboer DOT nl>
 * @license http://github.com/mikedeboer/jsDAV/blob/master/LICENSE MIT License
 */

var Http   = require("http"),
    Url    = require("url"),
    Sys    = require("sys"),
    Fs     = require("fs"),
    Exc    = require("./exceptions"),

    // DAV classes used directly by the Server object
    jsDAV                             = require("./../jsdav"),
    jsDAV_Handler                     = require("./handler").jsDAV_Handler,
    jsDAV_SimpleDirectory             = require("./simpleDirectory").jsDAV_SimpleDirectory,
    jsDAV_ObjectTree                  = require("./objectTree").jsDAV_ObjectTree,
    jsDAV_Tree_Filesystem             = require("./tree/filesystem").jsDAV_Tree_Filesystem;

exports.DEFAULT_PORT   = 41197;
exports.DEFAULT_HOST   = "127.0.0.1";
exports.DEFAULT_TMPDIR = (function() {
    var value,
        def     = "/tmp",
        envVars = ["TMPDIR", "TMP", "TEMP"],
        i       = 0,
        l       = envVars.length;
    for(; i < l; ++i) {
        value = process.env[envVars[i]];
        if (value)
            return Fs.realpathSync(value).replace(/\/+$/, "");
    }
    return Fs.realpathSync(def).replace(/\/+$/, "");
})();
/**
 * jsDAV version number
 */
exports.VERSION = "0.1";

function Server(options) {
    /**
     * This is a flag that allow or not showing file, line and code
     * of the exception in the returned XML
     *
     * @var bool
     */
    this.debugExceptions = exports.debugMode;
    if (options && typeof options.standalone == "undefined")
        options.standalone = true;
    this.options = options;

    if (options && typeof options.tree == "object" && options.tree.hasFeature(jsDAV.__TREE__)) {
        this.tree = options.tree;
    }
    else if (options && typeof options.node == "object" && options.node.hasFeature(jsDAV.__INODE__)) {
        this.tree = new jsDAV_ObjectTree(options.node, options);
    }
    else if (options && typeof options.type == "string") {
        if (options.type == "sftp") {
            var jsDAV_Tree_Sftp = require("./tree/sftp").jsDAV_Tree_Sftp;
            this.tree = new jsDAV_Tree_Sftp(options);
        }
        else if (options.type == "ftp") {
            var jsDAV_Tree_Ftp = require("./tree/ftp").jsDAV_Tree_Ftp;
            this.tree = new jsDAV_Tree_Ftp(options);
        }
    }
    else if (options && typeof options.node == "string" && options.node.indexOf("/") > -1) {
        this.tree = new jsDAV_Tree_Filesystem(options.node, options);
    }
    else if (!options) {
        var root  = new jsDAV_SimpleDirectory("root");
        this.tree = new jsDAV_ObjectTree(root, options);
    }
    else {
        throw new Exc.jsDAV_Exception("Invalid argument passed to constructor. "
            + "Argument must either be an instance of jsDAV_Tree, jsDAV_iNode, "
            + "a valid path to a location on the local filesystem or null");
    }

    this.tmpDir = ((options && options.tmpDir) || exports.DEFAULT_TMPDIR).replace(/\/+$/, "");

    if (options.server && options.mount) { //use an existing server object
        var _self = this;
        this.setBaseUri("/" + options.mount.replace(/^\/+/, ""));

        if (options.standalone) {
            var listeners = options.server.listeners("request");
            options.server.removeAllListeners("request");

            options.server.addListener("request", function(req, resp) {
                var path = Url.parse(req.url).pathname;
                if (path.charAt(path.length - 1) != "/")
                    path = path + "/";
                if (path.indexOf(_self.baseUri) === 0) {
                    _self.exec(req, resp);
                }
                else {
                    for (var i = 0, len = listeners.length; i < len; ++i)
                        listeners[i].call(options.server, req, resp);
                }
            });
        }
    }
    else {
        this.setBaseUri(this.guessBaseUri());

        Http.Server.call(this, this.exec);
    }

}

(function() {
    /**
     * The base uri
     *
     * @var string
     */
    this.baseUri = "/";

    /**
     * The tree object
     *
     * @var jsDAV_Tree
     */
    this.tree = null;

    /**
     * The list of plugins
     *
     * @var array
     */
    this.plugins = {};

    /**
     * Called when an http request comes in, pass it on to the Handler
     *
     * @param {ServerRequest}  req
     * @param {ServerResponse} resp
     * @return void
     */
    this.exec = function(req, resp) {
        new jsDAV_Handler(this, req, resp);
    };

    /**
     * Sets the base server uri
     *
     * @param  {String} uri
     * @return {void}
     */
    this.setBaseUri = function(uri) {
        // If the baseUri does not end with a slash, we must add it
        if (uri.charAt(uri.length - 1) !== "/")
            uri += "/";

        this.baseUri = uri;
    };

    /**
     * Returns the base responding uri
     *
     * @return {String}
     */
    this.getBaseUri = function() {
        return this.baseUri;
    };

    /**
     * This method attempts to detect the base uri.
     * Only the PATH_INFO variable is considered.
     *
     * If this variable is not set, the root (/) is assumed.
     *
     * @return {String}
     * @throws {Error}
     */
    this.guessBaseUri = function(handler) {
        var pos, pathInfo, uri;

        if (handler && handler.httpRequest) {
            uri      = handler.httpRequest.url;
            pathInfo = Url.parse(uri).pathname;
        }

        // If PATH_INFO is not found, we just return /
        if (pathInfo) {
            // We need to make sure we ignore the QUERY_STRING part
            if ((pos = uri.indexOf("?")) > -1)
                uri = uri.substr(0, pos);

            // PATH_INFO is only set for urls, such as: /example.php/path
            // in that case PATH_INFO contains "/path".
            // Note that REQUEST_URI is percent encoded, while PATH_INFO is
            // not, Therefore they are only comparable if we first decode
            // REQUEST_INFO as well.
            var decodedUri = unescape(uri);

            // A simple sanity check:
            if (decodedUri.substr(decodedUri.length - pathInfo.length) === pathInfo) {
                var baseUri = decodedUri.substr(0, decodedUri.length - pathInfo.length);
                return Util.rtrim(baseUri, "/") + "/";
            }

            throw new Exc.jsDAV_Exception("The REQUEST_URI (" + uri
                + ") did not end with the contents of PATH_INFO (" + pathInfo
                + "). This server might be misconfigured.");
        }

        // The fallback is that we're just going to assume the server root.
        return "/";
    };

    /**
     * Auto-load bundled plugins.
     */
    var _self = this;
    Fs.readdirSync(__dirname + "/plugins").forEach(function(filename){
        if (/\.js$/.test(filename)) {
            var name = filename.substr(0, filename.lastIndexOf('.'));
            _self.plugins[name] = require("./plugins/" + name);
        }
    });
}).call(Server.prototype = Http.Server.prototype);

exports.createServer = function(options, port, host) {
    port = port || exports.DEFAULT_PORT;
    host = host || exports.DEFAULT_HOST;

    var server = new Server(options);
    server.listen(port, host, function() {
        Sys.puts("jsDAV server running on http://" + host + ":" + port);
    });
    return server;
};

exports.mount = function(options) {
    var s = new Server(options);
    s.unmount = function() {
        if (this.tree.unmount)
            this.tree.unmount();
    };
    return s;
};
<|endoftext|>


<commit_before>var ElementType = require("./ElementType.js");

function DomHandler(callback, options){
	this.dom = [];
	this._done = false;
	this._tagStack = [];
	if(typeof callback === "object") this._options = callback;
	else {
		if(options) this._options = options; //otherwise, the prototype is used
		if(callback) this._callback = callback;
	}
}

//default options
DomHandler.prototype._options = {
	ignoreWhitespace: false //Keep whitespace-only text nodes
};

//Resets the handler back to starting state
DomHandler.prototype.onreset = DomHandler;

//Signals the handler that parsing is done
DomHandler.prototype.onend = function(){
	if(this._done) return;
	this._done = true;
	this._handleCallback(null);
};

DomHandler.prototype._handleCallback =
DomHandler.prototype.onerror = function(error){
	if(typeof this._callback === "function"){
		this._callback(error, this.dom);
	} else {
		if(error) throw error;
	}
};

DomHandler.prototype.onclosetag = function(name){
	if(this._tagStack.pop().name !== name) this._handleCallback(Error("tagname didn't match!"));
};

DomHandler.prototype._addDomElement = function(element){
	var lastChild,
		lastTag = this._tagStack[this._tagStack.length - 1];

	if(lastTag){ //There are parent elements
		if(!lastTag.children){
			lastTag.children = [element];
			return;
		}
		lastChild = lastTag.children[lastTag.children.length - 1];
		if(element.type === ElementType.Text && lastChild.type === ElementType.Text){
			lastChild.data += element.data;
		} else {
			lastTag.children.push(element);
		}
	}
	else {
		this.dom.push(element);
	}
};

DomHandler.prototype.onopentag = function(name, attribs){
	var element = {
		type: name === "script" ? ElementType.Script : name === "style" ? ElementType.Style : ElementType.Tag,
		name: name
	};
	//for some reason, an if doesn't work
	for(var i in attribs){
		element.attribs = attribs;
		break;
	}
	this._addDomElement(element);
	this._tagStack.push(element);
};

DomHandler.prototype.ontext = function(data){
	if(this._options.ignoreWhitespace && data.trim() === "") return;
	this._addDomElement({
		data: data,
		type: ElementType.Text
	});
};

DomHandler.prototype.oncomment = function(data){
	var lastTag = this._tagStack[this._tagStack.length - 1];

	if(lastTag && lastTag.type === ElementType.Comment){
		lastTag.data += data;
		return;
	}

	var element = {
		data: data,
		 type: ElementType.Comment
	};

	if(!lastTag) this.dom.push(element);
	else if(!lastTag.children) lastTag.children = [element];
	else lastTag.children.push(element);

	this._tagStack.push(element);
};

DomHandler.prototype.oncommentend = function(){
	this._tagStack.pop();
};

DomHandler.prototype.onprocessinginstruction = function(name, data){
	this._addDomElement({
		name: name,
		data: data,
		type: ElementType.Directive
	});
};

module.exports = DomHandler;<commit_msg>Use onopentagname and onattribute events in DomHandler<commit_after>var ElementType = require("./ElementType.js");

function DomHandler(callback, options){
	this.dom = [];
	this._done = false;
	this._tagStack = [];
	if(typeof callback === "object") this._options = callback;
	else {
		if(options) this._options = options; //otherwise, the prototype is used
		if(callback) this._callback = callback;
	}
}

//default options
DomHandler.prototype._options = {
	ignoreWhitespace: false //Keep whitespace-only text nodes
};

//Resets the handler back to starting state
DomHandler.prototype.onreset = DomHandler;

//Signals the handler that parsing is done
DomHandler.prototype.onend = function(){
	if(this._done) return;
	this._done = true;
	this._handleCallback(null);
};

DomHandler.prototype._handleCallback =
DomHandler.prototype.onerror = function(error){
	if(typeof this._callback === "function"){
		this._callback(error, this.dom);
	} else {
		if(error) throw error;
	}
};

DomHandler.prototype.onclosetag = function(name){
	if(this._tagStack.pop().name !== name) this._handleCallback(Error("tagname didn't match!"));
};

DomHandler.prototype._addDomElement = function(element){
	var lastChild,
		lastTag = this._tagStack[this._tagStack.length - 1];

	if(lastTag){ //There are parent elements
		if(!lastTag.children){
			lastTag.children = [element];
			return;
		}
		lastChild = lastTag.children[lastTag.children.length - 1];
		if(element.type === ElementType.Text && lastChild.type === ElementType.Text){
			lastChild.data += element.data;
		} else {
			lastTag.children.push(element);
		}
	}
	else {
		this.dom.push(element);
	}
};

DomHandler.prototype.onopentagname = function(name){
	var element = {
		type: name === "script" ? ElementType.Script : name === "style" ? ElementType.Style : ElementType.Tag,
		name: name
	};
	this._addDomElement(element);
	this._tagStack.push(element);
};

DomHandler.prototype.onattribute = function(name, value){
	var element = this._tagStack[this._tagStack.length-1];
	if(!("attribs" in element)) element.attribs = {};
	element.attribs[name] = value;
};

DomHandler.prototype.ontext = function(data){
	if(this._options.ignoreWhitespace && data.trim() === "") return;
	this._addDomElement({
		data: data,
		type: ElementType.Text
	});
};

DomHandler.prototype.oncomment = function(data){
	var lastTag = this._tagStack[this._tagStack.length - 1];

	if(lastTag && lastTag.type === ElementType.Comment){
		lastTag.data += data;
		return;
	}

	var element = {
		data: data,
		 type: ElementType.Comment
	};

	if(!lastTag) this.dom.push(element);
	else if(!lastTag.children) lastTag.children = [element];
	else lastTag.children.push(element);

	this._tagStack.push(element);
};

DomHandler.prototype.oncommentend = function(){
	this._tagStack.pop();
};

DomHandler.prototype.onprocessinginstruction = function(name, data){
	this._addDomElement({
		name: name,
		data: data,
		type: ElementType.Directive
	});
};

module.exports = DomHandler;<|endoftext|>


<commit_before>import {EventEmitter} from 'events';
import axios from 'axios';
import _ from 'lodash';
import AppDispatcher from './AppDispatcher';

class PageStore extends EventEmitter {
	constructor(){
		super();
		AppDispatcher.register(function(payload){
			switch(payload.eventName){
				case 'loadPage':
					console.log('load page ..');
					PageStore.clearPage();
					PageStore.loadPage(payload.pageId);
					console.log('load page .. ok');
					break;
				case 'createPage':
					this.createPage();
					this.reload();
					break;
			}
			return true;
		}.bind(this));
	};
	loadPageList(magazineId) {
		this.magazineId=magazineId;
		axios.get('/api/pageList/' + magazineId).then(function(response) {
			this.pageList = response.data.pageItems;
			this.emit('pageListLoadded');
		}.bind(this)).catch(function(response) {
			console.log('>>>load page list error! '); //TODO 整体解决错误处理
		});
	};
	loadPage(pageId) {
		axios.get('/api/page/' + pageId).then(function(response) {
			this.page = response.data.page;
			this.emit('pageLoadded');
		}.bind(this)).catch(function(response) {
			console.log('>>>load page error!'); //TODO 整体解决错误处理
		});
	};
	getFirstPageId() {
		if (this.pageList && this.pageList.length > 0) {
			return this.pageList[0].id;
		}
		return null;
	};
	clearPage() {
		this.page = null;
	};
	createPage() {
		axios.post('/api/page', {
				name: ''
			})
			.then(function(response) {
				console.log('>>>creat ok!');
			})
			.catch(function(response) {
				console.log('>>>load page error!'); //TODO 整体解决错误处理
			});
	};
	reload(){
		this.page=null;
		this.pageList=null;
		this.loadPageList(this.magazineId);
	}
}

let _PageStore = new PageStore();
export default _PageStore;<commit_msg>修正使用Dispatch时的错误<commit_after>import {EventEmitter} from 'events';
import axios from 'axios';
import _ from 'lodash';
import AppDispatcher from './AppDispatcher';

class PageStore extends EventEmitter {
	constructor(){
		super();
		AppDispatcher.register(function(payload){
			switch(payload.eventName){
				case 'loadPage':
					console.log('load page ..');
					this.clearPage();
					this.loadPage(payload.pageId);
					console.log('load page .. ok');
					break;
				case 'createPage':
					this.createPage();
					this.reload();
					break;
			}
			return true;
		}.bind(this));
	};
	loadPageList(magazineId) {
		this.magazineId=magazineId;
		axios.get('/api/pageList/' + magazineId).then(function(response) {
			this.pageList = response.data.pageItems;
			this.emit('pageListLoadded');
		}.bind(this)).catch(function(response) {
			console.log('>>>load page list error! '); //TODO 整体解决错误处理
		});
	};
	loadPage(pageId) {
		axios.get('/api/page/' + pageId).then(function(response) {
			this.page = response.data.page;
			this.emit('pageLoadded');
		}.bind(this)).catch(function(response) {
			console.log('>>>load page error!'); //TODO 整体解决错误处理
		});
	};
	getFirstPageId() {
		if (this.pageList && this.pageList.length > 0) {
			return this.pageList[0].id;
		}
		return null;
	};
	clearPage() {
		this.page = null;
	};
	createPage() {
		axios.post('/api/page', {
				name: ''
			})
			.then(function(response) {
				console.log('>>>creat ok!');
			})
			.catch(function(response) {
				console.log('>>>load page error!'); //TODO 整体解决错误处理
			});
	};
	reload(){
		this.page=null;
		this.pageList=null;
		this.loadPageList(this.magazineId);
	}
}

let _PageStore = new PageStore();
export default _PageStore;<|endoftext|>


<commit_before>// @flow
import {default as React, PropTypes} from 'react';
import ReactDOM from 'react-dom';
// $FlowIgnore
import classNames from 'classnames';
import {createUIEvent, createCSSTransform, createSVGTransform} from './utils/domFns';
import {canDragX, canDragY, getBoundPosition} from './utils/positionFns';
import {dontSetMe} from './utils/shims';
import DraggableCore from './DraggableCore';
import log from './utils/log';

import type {CoreEvent} from './utils/domFns';
export type CoreEventHandler = (e: Event, coreEvent: CoreEvent) => void | false;
export type DraggableState = {
  dragging: boolean,
  dragged: boolean,
  clientX: number, clientY: number,
  slackX: number, slackY: number,
  isElementSVG: boolean
};

//
// Define <Draggable>
//

export default class Draggable extends React.Component {

  static displayName = 'Draggable';

  static propTypes = {
    // Accepts all props <DraggableCore> accepts.
    ...DraggableCore.propTypes,

    /**
     * `axis` determines which axis the draggable can move.
     *
     *  Note that all callbacks will still return data as normal. This only
     *  controls flushing to the DOM.
     *
     * 'both' allows movement horizontally and vertically.
     * 'x' limits movement to horizontal axis.
     * 'y' limits movement to vertical axis.
     * 'none' limits all movement.
     *
     * Defaults to 'both'.
     */
    axis: PropTypes.oneOf(['both', 'x', 'y', 'none']),

    /**
     * `bounds` determines the range of movement available to the element.
     * Available values are:
     *
     * 'parent' restricts movement within the Draggable's parent node.
     *
     * Alternatively, pass an object with the following properties, all of which are optional:
     *
     * {left: LEFT_BOUND, right: RIGHT_BOUND, bottom: BOTTOM_BOUND, top: TOP_BOUND}
     *
     * All values are in px.
     *
     * Example:
     *
     * ```jsx
     *   let App = React.createClass({
     *       render: function () {
     *         return (
     *            <Draggable bounds={{right: 300, bottom: 300}}>
     *              <div>Content</div>
     *           </Draggable>
     *         );
     *       }
     *   });
     * ```
     */
    bounds: PropTypes.oneOfType([
      PropTypes.shape({
        left: PropTypes.Number,
        right: PropTypes.Number,
        top: PropTypes.Number,
        bottom: PropTypes.Number
      }),
      PropTypes.string,
      PropTypes.oneOf([false])
    ]),

    /**
     * `start` specifies the x and y that the dragged item should start at
     *
     * Example:
     *
     * ```jsx
     *      let App = React.createClass({
     *          render: function () {
     *              return (
     *                  <Draggable start={{x: 25, y: 25}}>
     *                      <div>I start with transformX: 25px and transformY: 25px;</div>
     *                  </Draggable>
     *              );
     *          }
     *      });
     * ```
     */
    start: PropTypes.shape({
      x: PropTypes.number,
      y: PropTypes.number
    }),

    /**
     * `zIndex` specifies the zIndex to use while dragging.
     *
     * Example:
     *
     * ```jsx
     *   let App = React.createClass({
     *       render: function () {
     *           return (
     *               <Draggable zIndex={100}>
     *                   <div>I have a zIndex</div>
     *               </Draggable>
     *           );
     *       }
     *   });
     * ```
     */
    zIndex: PropTypes.number,

    /**
     * These properties should be defined on the child, not here.
     */
    className: dontSetMe,
    style: dontSetMe,
    transform: dontSetMe
  };

  static defaultProps = {
    ...DraggableCore.defaultProps,
    axis: 'both',
    bounds: false,
    start: {x: 0, y: 0},
    zIndex: NaN
  };

  state: DraggableState = {
    // Whether or not we are currently dragging.
    dragging: false,

    // Whether or not we have been dragged before.
    dragged: false,

    // Current transform x and y.
    clientX: this.props.start.x, clientY: this.props.start.y,

    // Used for compensating for out-of-bounds drags
    slackX: 0, slackY: 0,

    // Can only determine if SVG after mounting
    isElementSVG: false
  };

  componentDidMount() {
    // Check to see if the element passed is an instanceof SVGElement
    if(ReactDOM.findDOMNode(this) instanceof SVGElement) {
      this.setState({ isElementSVG: true });
    }
  }

  componentWillUnmount() {
    this.setState({dragging: false}); // prevents invariant if unmounted while dragging
  }

  onDragStart: CoreEventHandler = (e, coreEvent) => {
    log('Draggable: onDragStart: %j', coreEvent.position);

    // Short-circuit if user's callback killed it.
    let shouldStart = this.props.onStart(e, createUIEvent(this, coreEvent));
    // Kills start event on core as well, so move handlers are never bound.
    if (shouldStart === false) return false;

    this.setState({dragging: true, dragged: true});
  };

  onDrag: CoreEventHandler = (e, coreEvent) => {
    if (!this.state.dragging) return false;
    log('Draggable: onDrag: %j', coreEvent.position);

    let uiEvent = createUIEvent(this, coreEvent);

    let newState = {
      clientX: uiEvent.position.left,
      clientY: uiEvent.position.top
    };

    // Keep within bounds.
    if (this.props.bounds) {
      // Save original x and y.
      let {clientX, clientY} = newState;

      // Add slack to the values used to calculate bound position. This will ensure that if
      // we start removing slack, the element won't react to it right away until it's been
      // completely removed.
      newState.clientX += this.state.slackX;
      newState.clientY += this.state.slackY;

      // Get bound position. This will ceil/floor the x and y within the boundaries.
      [newState.clientX, newState.clientY] = getBoundPosition(this, newState.clientX, newState.clientY);

      // Recalculate slack by noting how much was shaved by the boundPosition handler.
      newState.slackX = this.state.slackX + (clientX - newState.clientX);
      newState.slackY = this.state.slackY + (clientY - newState.clientY);

      // Update the event we fire.
      uiEvent.position.left = clientX;
      uiEvent.position.top = clientY;
    }

    // Short-circuit if user's callback killed it.
    let shouldUpdate = this.props.onDrag(e, uiEvent);
    if (shouldUpdate === false) return false;

    this.setState(newState);
  };

  onDragStop: CoreEventHandler = (e, coreEvent) => {
    if (!this.state.dragging) return false;

    // Short-circuit if user's callback killed it.
    let shouldStop = this.props.onStop(e, createUIEvent(this, coreEvent));
    if (shouldStop === false) return false;

    log('Draggable: onDragStop: %j', coreEvent.position);

    this.setState({
      dragging: false,
      slackX: 0,
      slackY: 0
    });
  };

  render(): ReactElement {
    let style = {}, svgTransform = null;

    // Add a CSS transform to move the element around. This allows us to move the element around
    // without worrying about whether or not it is relatively or absolutely positioned.
    // If the item you are dragging already has a transform set, wrap it in a <span> so <Draggable>
    // has a clean slate.
    const transformOpts = {
      // Set left if horizontal drag is enabled
      x: canDragX(this) ?
        this.state.clientX :
        this.props.start.x,

      // Set top if vertical drag is enabled
      y: canDragY(this) ?
        this.state.clientY :
        this.props.start.y
    };

    // If this element was SVG, we use the `transform` attribute.
    if (this.state.isElementSVG) {
      svgTransform = createSVGTransform(transformOpts);
    } else {
      style = createCSSTransform(transformOpts);
    }

    // zIndex option
    if (this.state.dragging && !isNaN(this.props.zIndex)) {
      style.zIndex = this.props.zIndex;
    }

    // Mark with class while dragging
    let className = classNames((this.props.children.props.className || ''), 'react-draggable', {
      'react-draggable-dragging': this.state.dragging,
      'react-draggable-dragged': this.state.dragged
    });

    // Reuse the child provided
    // This makes it flexible to use whatever element is wanted (div, ul, etc)
    return (
      <DraggableCore {...this.props} onStart={this.onDragStart} onDrag={this.onDrag} onStop={this.onDragStop}>
        {React.cloneElement(React.Children.only(this.props.children), {
          className: className,
          style: {...this.props.children.props.style, ...style},
          transform: svgTransform
        })}
      </DraggableCore>
    );
  }
}

<commit_msg>Also modify outgoing deltaX/Y on bounds hit.<commit_after>// @flow
import {default as React, PropTypes} from 'react';
import ReactDOM from 'react-dom';
// $FlowIgnore
import classNames from 'classnames';
import {createUIEvent, createCSSTransform, createSVGTransform} from './utils/domFns';
import {canDragX, canDragY, getBoundPosition} from './utils/positionFns';
import {dontSetMe} from './utils/shims';
import DraggableCore from './DraggableCore';
import log from './utils/log';

import type {CoreEvent} from './utils/domFns';
export type CoreEventHandler = (e: Event, coreEvent: CoreEvent) => void | false;
export type DraggableState = {
  dragging: boolean,
  dragged: boolean,
  clientX: number, clientY: number,
  slackX: number, slackY: number,
  isElementSVG: boolean
};

//
// Define <Draggable>
//

export default class Draggable extends React.Component {

  static displayName = 'Draggable';

  static propTypes = {
    // Accepts all props <DraggableCore> accepts.
    ...DraggableCore.propTypes,

    /**
     * `axis` determines which axis the draggable can move.
     *
     *  Note that all callbacks will still return data as normal. This only
     *  controls flushing to the DOM.
     *
     * 'both' allows movement horizontally and vertically.
     * 'x' limits movement to horizontal axis.
     * 'y' limits movement to vertical axis.
     * 'none' limits all movement.
     *
     * Defaults to 'both'.
     */
    axis: PropTypes.oneOf(['both', 'x', 'y', 'none']),

    /**
     * `bounds` determines the range of movement available to the element.
     * Available values are:
     *
     * 'parent' restricts movement within the Draggable's parent node.
     *
     * Alternatively, pass an object with the following properties, all of which are optional:
     *
     * {left: LEFT_BOUND, right: RIGHT_BOUND, bottom: BOTTOM_BOUND, top: TOP_BOUND}
     *
     * All values are in px.
     *
     * Example:
     *
     * ```jsx
     *   let App = React.createClass({
     *       render: function () {
     *         return (
     *            <Draggable bounds={{right: 300, bottom: 300}}>
     *              <div>Content</div>
     *           </Draggable>
     *         );
     *       }
     *   });
     * ```
     */
    bounds: PropTypes.oneOfType([
      PropTypes.shape({
        left: PropTypes.Number,
        right: PropTypes.Number,
        top: PropTypes.Number,
        bottom: PropTypes.Number
      }),
      PropTypes.string,
      PropTypes.oneOf([false])
    ]),

    /**
     * `start` specifies the x and y that the dragged item should start at
     *
     * Example:
     *
     * ```jsx
     *      let App = React.createClass({
     *          render: function () {
     *              return (
     *                  <Draggable start={{x: 25, y: 25}}>
     *                      <div>I start with transformX: 25px and transformY: 25px;</div>
     *                  </Draggable>
     *              );
     *          }
     *      });
     * ```
     */
    start: PropTypes.shape({
      x: PropTypes.number,
      y: PropTypes.number
    }),

    /**
     * `zIndex` specifies the zIndex to use while dragging.
     *
     * Example:
     *
     * ```jsx
     *   let App = React.createClass({
     *       render: function () {
     *           return (
     *               <Draggable zIndex={100}>
     *                   <div>I have a zIndex</div>
     *               </Draggable>
     *           );
     *       }
     *   });
     * ```
     */
    zIndex: PropTypes.number,

    /**
     * These properties should be defined on the child, not here.
     */
    className: dontSetMe,
    style: dontSetMe,
    transform: dontSetMe
  };

  static defaultProps = {
    ...DraggableCore.defaultProps,
    axis: 'both',
    bounds: false,
    start: {x: 0, y: 0},
    zIndex: NaN
  };

  state: DraggableState = {
    // Whether or not we are currently dragging.
    dragging: false,

    // Whether or not we have been dragged before.
    dragged: false,

    // Current transform x and y.
    clientX: this.props.start.x, clientY: this.props.start.y,

    // Used for compensating for out-of-bounds drags
    slackX: 0, slackY: 0,

    // Can only determine if SVG after mounting
    isElementSVG: false
  };

  componentDidMount() {
    // Check to see if the element passed is an instanceof SVGElement
    if(ReactDOM.findDOMNode(this) instanceof SVGElement) {
      this.setState({ isElementSVG: true });
    }
  }

  componentWillUnmount() {
    this.setState({dragging: false}); // prevents invariant if unmounted while dragging
  }

  onDragStart: CoreEventHandler = (e, coreEvent) => {
    log('Draggable: onDragStart: %j', coreEvent.position);

    // Short-circuit if user's callback killed it.
    let shouldStart = this.props.onStart(e, createUIEvent(this, coreEvent));
    // Kills start event on core as well, so move handlers are never bound.
    if (shouldStart === false) return false;

    this.setState({dragging: true, dragged: true});
  };

  onDrag: CoreEventHandler = (e, coreEvent) => {
    if (!this.state.dragging) return false;
    log('Draggable: onDrag: %j', coreEvent.position);

    let uiEvent = createUIEvent(this, coreEvent);

    let newState = {
      clientX: uiEvent.position.left,
      clientY: uiEvent.position.top
    };

    // Keep within bounds.
    if (this.props.bounds) {
      // Save original x and y.
      let {clientX, clientY} = newState;

      // Add slack to the values used to calculate bound position. This will ensure that if
      // we start removing slack, the element won't react to it right away until it's been
      // completely removed.
      newState.clientX += this.state.slackX;
      newState.clientY += this.state.slackY;

      // Get bound position. This will ceil/floor the x and y within the boundaries.
      [newState.clientX, newState.clientY] = getBoundPosition(this, newState.clientX, newState.clientY);

      // Recalculate slack by noting how much was shaved by the boundPosition handler.
      newState.slackX = this.state.slackX + (clientX - newState.clientX);
      newState.slackY = this.state.slackY + (clientY - newState.clientY);

      // Update the event we fire to reflect what really happened after bounds took effect.
      uiEvent.position.left = clientX;
      uiEvent.position.top = clientY;
      uiEvent.deltaX = newState.clientX - this.state.clientX;
      uiEvent.deltaY = newState.clientY - this.state.clientY;
    }

    // Short-circuit if user's callback killed it.
    let shouldUpdate = this.props.onDrag(e, uiEvent);
    if (shouldUpdate === false) return false;

    this.setState(newState);
  };

  onDragStop: CoreEventHandler = (e, coreEvent) => {
    if (!this.state.dragging) return false;

    // Short-circuit if user's callback killed it.
    let shouldStop = this.props.onStop(e, createUIEvent(this, coreEvent));
    if (shouldStop === false) return false;

    log('Draggable: onDragStop: %j', coreEvent.position);

    this.setState({
      dragging: false,
      slackX: 0,
      slackY: 0
    });
  };

  render(): ReactElement {
    let style = {}, svgTransform = null;

    // Add a CSS transform to move the element around. This allows us to move the element around
    // without worrying about whether or not it is relatively or absolutely positioned.
    // If the item you are dragging already has a transform set, wrap it in a <span> so <Draggable>
    // has a clean slate.
    const transformOpts = {
      // Set left if horizontal drag is enabled
      x: canDragX(this) ?
        this.state.clientX :
        this.props.start.x,

      // Set top if vertical drag is enabled
      y: canDragY(this) ?
        this.state.clientY :
        this.props.start.y
    };

    // If this element was SVG, we use the `transform` attribute.
    if (this.state.isElementSVG) {
      svgTransform = createSVGTransform(transformOpts);
    } else {
      style = createCSSTransform(transformOpts);
    }

    // zIndex option
    if (this.state.dragging && !isNaN(this.props.zIndex)) {
      style.zIndex = this.props.zIndex;
    }

    // Mark with class while dragging
    let className = classNames((this.props.children.props.className || ''), 'react-draggable', {
      'react-draggable-dragging': this.state.dragging,
      'react-draggable-dragged': this.state.dragged
    });

    // Reuse the child provided
    // This makes it flexible to use whatever element is wanted (div, ul, etc)
    return (
      <DraggableCore {...this.props} onStart={this.onDragStart} onDrag={this.onDrag} onStop={this.onDragStop}>
        {React.cloneElement(React.Children.only(this.props.children), {
          className: className,
          style: {...this.props.children.props.style, ...style},
          transform: svgTransform
        })}
      </DraggableCore>
    );
  }
}

<|endoftext|>