<commit_before>"use strict";

/**
 * Simple script to delete all but the newest entry per revision in a
 * key_rev_value table.
 */

var P = require('bluebird');
var cassandra = P.promisifyAll(require('cassandra-driver'));
var util = require('util');
var preq = require('preq');

if (!process.argv[3]) {
    console.error('Usage: node ' + process.argv[1] + ' <host> <keyspace>');
    process.exit(1);
}

// A custom retry policy
function AlwaysRetry () {}
util.inherits(AlwaysRetry, cassandra.policies.retry.RetryPolicy);
var ARP = AlwaysRetry.prototype;
// Always retry.
ARP.onUnavailable = function(requestInfo) {
    // Reset the connection
    requestInfo.handler.connection.close(function() {
        requestInfo.handler.connection.open(function(){});
    });
    return { decision: 1 };
};
ARP.onWriteTimeout = function() { return { decision: 2 }; };
ARP.onReadTimeout = function(requestInfo) {
    // Reset the connection
    requestInfo.handler.connection.close(function() {
        requestInfo.handler.connection.open(function(){});
    });
    console.log('read retry');
    return { decision: 1 };
};

function makeClient() {
    return new cassandra.Client({
        contactPoints: [process.argv[2]],
        keyspace: process.argv[3],
        authProvider: new cassandra.auth.PlainTextAuthProvider('cassandra', 'cassandra'),
        socketOptions: { connectTimeout: 10000 },
        //policies: {
        //     retry: new AlwaysRetry()
        //},
    });
}
var client = makeClient();

// Force a re-render of a revision by sending no-cache headers. Can be used to
// fix up stored content after temporary snafus in Parsoid or RB.
function reRender(row) {
    var uri = 'http://rest.wikimedia.org/' + row._domain + '/v1/page/html/'
            + encodeURIComponent(row.key) + '/' + row.rev;
    console.log(uri);
    return preq.get({
        uri: uri,
        headers: {
            'if-unmodified-since': 'Fri Apr 24 2015 13:00:00 GMT-0700 (PDT)',
            'cache-control': 'no-cache'
        }
    })
    .catch(console.log);
}

// Row state, used to make row handling decisions in processRow
var counts = {
    title: 0,
    rev: 0,
    render: 0,
};

var keys = {
    title: null,
    rev: null,
};

function processRow (row) {
    // Create a new set of keys
    var newKeys = {
        title: JSON.stringify([row._domain, row.key]),
        rev: JSON.stringify([row._domain, row.key, row.rev])
    };

    // Diff the keys and update counters
    if (newKeys.title !== keys.title) {
        counts.title = 0;
        counts.rev = 0;
        counts.render = 0;
    } else if (newKeys.rev !==  keys.rev) {
        counts.rev++;
        counts.render = 0;
    } else {
        counts.render++;
    }
    keys = newKeys;

    // Now figure out what to do with this row
    if (false && counts.title === 0 && counts.rev === 0 && counts.render === 0) {
    var rowDate = row.tid.getDate();
    if (false && /parsoid_html$/.test(process.argv[3])
        && rowDate > new Date('2015-04-23T23:30-0700')
        && rowDate < new Date('2015-04-24T13:00-0700')) {
        return reRender(row);
    } else {
        return P.resolve();
    }
    console.log(row.tid.getDate());
        //console.log(row);
        // Don't delete the most recent render for this revision
        return P.resolve();
    } else if ((counts.rev > 0 && counts.render > 0)
        || (counts.rev === 0 && counts.render > 10)) {
        console.log(keys.rev, row.tid);
        var delQuery = 'delete from data where "_domain" = :domain and key = :key and rev = :rev and tid = :tid';
        row.domain = row._domain;
        return client.executeAsync(delQuery, row, { prepare: true });
    }

    // Else: nothing to do
    return P.resolve();
}

//var query = 'select "_domain", key, rev, tid from data where token("_domain",key) > -6004964422032836805';
var query = 'select "_domain", key, rev, tid from data';

function nextPage(pageState, retries) {
    //console.log(pageState);
    return client.executeAsync(query, [], {
        prepare: true,
        fetchSize: retries ? 1 : 50,
        pageState: pageState,
    })
    .catch(function(err) {
        console.log(retries, err);
    console.log(pageState);
    if (retries > 10) {
        process.exit(1);
    }
    return nextPage(pageState, (retries || 0) + 1);
    });
}

function processRows(pageState) {
    return nextPage(pageState)
    .then(function(res) {
        return P.resolve(res.rows)
        .each(processRow)
        .then(function() {
            process.nextTick(function() { processRows(res.pageState); });
        })
        .catch(function(e) {
            console.log(res.pageState);
            throw e;
        });
    });
}

var pageState;
if (process.argv[4]) {
    var pageState = process.argv[4];
}
return processRows(pageState);
<commit_msg>Delete all non-current renders that are older than our typical grace_ttl<commit_after>"use strict";

/**
 * Simple script to delete all but the newest entry per revision in a
 * key_rev_value table.
 */

var P = require('bluebird');
var cassandra = P.promisifyAll(require('cassandra-driver'));
var util = require('util');
var preq = require('preq');

if (!process.argv[3]) {
    console.error('Usage: node ' + process.argv[1] + ' <host> <keyspace>');
    process.exit(1);
}

// A custom retry policy
function AlwaysRetry () {}
util.inherits(AlwaysRetry, cassandra.policies.retry.RetryPolicy);
var ARP = AlwaysRetry.prototype;
// Always retry.
ARP.onUnavailable = function(requestInfo) {
    // Reset the connection
    requestInfo.handler.connection.close(function() {
        requestInfo.handler.connection.open(function(){});
    });
    return { decision: 1 };
};
ARP.onWriteTimeout = function() { return { decision: 2 }; };
ARP.onReadTimeout = function(requestInfo) {
    // Reset the connection
    requestInfo.handler.connection.close(function() {
        requestInfo.handler.connection.open(function(){});
    });
    console.log('read retry');
    return { decision: 1 };
};

function makeClient() {
    return new cassandra.Client({
        contactPoints: [process.argv[2]],
        keyspace: process.argv[3],
        authProvider: new cassandra.auth.PlainTextAuthProvider('cassandra', 'cassandra'),
        socketOptions: { connectTimeout: 10000 },
        //policies: {
        //     retry: new AlwaysRetry()
        //},
    });
}
var client = makeClient();

// Force a re-render of a revision by sending no-cache headers. Can be used to
// fix up stored content after temporary snafus in Parsoid or RB.
function reRender(row) {
    var uri = 'http://rest.wikimedia.org/' + row._domain + '/v1/page/html/'
            + encodeURIComponent(row.key) + '/' + row.rev;
    console.log(uri);
    return preq.get({
        uri: uri,
        headers: {
            'if-unmodified-since': 'Fri Apr 24 2015 13:00:00 GMT-0700 (PDT)',
            'cache-control': 'no-cache'
        }
    })
    .catch(console.log);
}

// Row state, used to make row handling decisions in processRow
var counts = {
    title: 0,
    rev: 0,
    render: 0,
};

var keys = {
    title: null,
    rev: null,
};

function processRow (row) {
    // Create a new set of keys
    var newKeys = {
        title: JSON.stringify([row._domain, row.key]),
        rev: JSON.stringify([row._domain, row.key, row.rev])
    };

    // Diff the keys and update counters
    if (newKeys.title !== keys.title) {
        counts.title = 0;
        counts.rev = 0;
        counts.render = 0;
    } else if (newKeys.rev !==  keys.rev) {
        counts.rev++;
        counts.render = 0;
    } else {
        counts.render++;
    }
    keys = newKeys;

    // Now figure out what to do with this row
    if (false && counts.title === 0 && counts.rev === 0 && counts.render === 0) {
    var rowDate = row.tid.getDate();
    if (false && /parsoid_html$/.test(process.argv[3])
        && rowDate > new Date('2015-04-23T23:30-0700')
        && rowDate < new Date('2015-04-24T13:00-0700')) {
        return reRender(row);
    } else {
        return P.resolve();
    }
    console.log(row.tid.getDate());
        //console.log(row);
        // Don't delete the most recent render for this revision
        return P.resolve();
    } else if ((counts.rev > 0 && counts.render > 0)
        || (counts.rev === 0 && counts.render > 0
            // Enforce a grace_ttl of 86400
            && (Date.now() - row.tid.getDate()) > 86400)) {
        console.log(keys.rev, row.tid);
        var delQuery = 'delete from data where "_domain" = :domain and key = :key and rev = :rev and tid = :tid';
        row.domain = row._domain;
        return client.executeAsync(delQuery, row, { prepare: true });
    }

    // Else: nothing to do
    return P.resolve();
}

//var query = 'select "_domain", key, rev, tid from data where token("_domain",key) > -6004964422032836805';
var query = 'select "_domain", key, rev, tid from data';

function nextPage(pageState, retries) {
    //console.log(pageState);
    return client.executeAsync(query, [], {
        prepare: true,
        fetchSize: retries ? 1 : 50,
        pageState: pageState,
    })
    .catch(function(err) {
        console.log(retries, err);
    console.log(pageState);
    if (retries > 10) {
        process.exit(1);
    }
    return nextPage(pageState, (retries || 0) + 1);
    });
}

function processRows(pageState) {
    return nextPage(pageState)
    .then(function(res) {
        return P.resolve(res.rows)
        .each(processRow)
        .then(function() {
            process.nextTick(function() { processRows(res.pageState); });
        })
        .catch(function(e) {
            console.log(res.pageState);
            throw e;
        });
    });
}

var pageState;
if (process.argv[4]) {
    var pageState = process.argv[4];
}
return processRows(pageState);
<|endoftext|>


<commit_before>describe("About Objects", function () {

  describe("Properties", function () {
    var megalomaniac;

    beforeEach(function () {
       megalomaniac = {  mastermind: "Joker", henchwoman: "Harley" };
    });

    it("should confirm objects are collections of properties", function () {
      expect(megalomaniac.mastermind).toBe(FILL_ME_IN);
    });

    it("should confirm that properties are case sensitive", function () {
      expect(megalomaniac.henchwoman).toBe(FILL_ME_IN);
      expect(megalomaniac.henchWoman).toBe(FILL_ME_IN);
    });
  });

  it("should know properties that are functions act like methods", function () {
    var megalomaniac = {
      mastermind : "Brain",
      henchman: "Pinky",
      battleCry: function (noOfBrains) {
        return "They are " + this.henchman + " and the" +
          Array(noOfBrains + 1).join(" " + this.mastermind);
      }
    };

    var battleCry = megalomaniac.battleCry(4);
    expect(FILL_ME_IN).toMatch(battleCry);
  });

  it("should confirm that when a function is attached to an object, 'this' refers to the object", function () {
    var currentDate = new Date();
    var currentYear = (currentDate.getFullYear());
    var megalomaniac = {
      mastermind: "James Wood",
      henchman: "Adam West",
      birthYear: 1970,
      calculateAge: function () {
        return currentYear - this.birthYear;
      }
    };

    expect(currentYear).toBe(FILL_ME_IN);
    expect(megalomaniac.calculateAge()).toBe(FILL_ME_IN);
  });

  describe("'in' keyword", function () {
    var megalomaniac;
    beforeEach(function () {
      megalomaniac = {
        mastermind: "The Monarch",
        henchwoman: "Dr Girlfriend",
        theBomb: true
      };
    });

    it("should have the bomb", function () {

      var hasBomb = "theBomb" in megalomaniac;

      expect(hasBomb).toBe(FILL_ME_IN);
    });

    it("should not have the detonator however", function () {

      var hasDetonator = "theDetonator" in megalomaniac;

      expect(hasDetonator).toBe(FILL_ME_IN);
    });
  });

  it("should know that properties can be added and deleted", function () {
    var megalomaniac = { mastermind : "Agent Smith", henchman: "Agent Smith" };

    expect("secretary" in megalomaniac).toBe(FILL_ME_IN);

    megalomaniac.secretary = "Agent Smith";
    expect("secretary" in megalomaniac).toBe(FILL_ME_IN);

    delete megalomaniac.henchman;
    expect("henchman" in megalomaniac).toBe(FILL_ME_IN);
  });

  it("should use prototype to add to all objects", function () {
      function Circle(radius)
      {
        this.radius = radius;
      }

      var simpleCircle = new Circle(10);
      var colouredCircle = new Circle(5);
      colouredCircle.colour = "red";

      expect(simpleCircle.colour).toBe(FILL_ME_IN);
      expect(colouredCircle.colour).toBe(FILL_ME_IN);

      Circle.prototype.describe = function () {
        return "This circle has a radius of: " + this.radius;
      };

      expect(simpleCircle.describe()).toBe(FILL_ME_IN);
      expect(colouredCircle.describe()).toBe(FILL_ME_IN);
  });
});
<commit_msg>Done with the AboutObjects section<commit_after>describe("About Objects", function () {

  describe("Properties", function () {
    var megalomaniac;

    beforeEach(function () {
       megalomaniac = {  mastermind: "Joker", henchwoman: "Harley" };
    });

    it("should confirm objects are collections of properties", function () {
      expect(megalomaniac.mastermind).toBe("Joker");
    });

    it("should confirm that properties are case sensitive", function () {
      expect(megalomaniac.henchwoman).toBe("Harley");
      expect(megalomaniac.henchWoman).toBe();
    });
  });

  it("should know properties that are functions act like methods", function () {
    var megalomaniac = {
      mastermind : "Brain",
      henchman: "Pinky",
      battleCry: function (noOfBrains) {
        return "They are " + this.henchman + " and the" +
          Array(noOfBrains + 1).join(" " + this.mastermind);
      }
    };

    var battleCry = megalomaniac.battleCry(4);
    expect("They are Pinky and the Brain Brain Brain Brain").toMatch(battleCry);
  });

  it("should confirm that when a function is attached to an object, 'this' refers to the object", function () {
    var currentDate = new Date();
    var currentYear = (currentDate.getFullYear());
    var megalomaniac = {
      mastermind: "James Wood",
      henchman: "Adam West",
      birthYear: 1970,
      calculateAge: function () {
        return currentYear - this.birthYear;
      }
    };

    expect(currentYear).toBe(2016);
    expect(megalomaniac.calculateAge()).toBe(46);
  });

  describe("'in' keyword", function () {
    var megalomaniac;
    beforeEach(function () {
      megalomaniac = {
        mastermind: "The Monarch",
        henchwoman: "Dr Girlfriend",
        theBomb: true
      };
    });

    it("should have the bomb", function () {

      var hasBomb = "theBomb" in megalomaniac;

      expect(hasBomb).toBe(true);
    });

    it("should not have the detonator however", function () {

      var hasDetonator = "theDetonator" in megalomaniac;

      expect(hasDetonator).toBe(false);
    });
  });

  it("should know that properties can be added and deleted", function () {
    var megalomaniac = { mastermind : "Agent Smith", henchman: "Agent Smith" };

    expect("secretary" in megalomaniac).toBe(false);

    megalomaniac.secretary = "Agent Smith";
    expect("secretary" in megalomaniac).toBe(true);

    delete megalomaniac.henchman;
    expect("henchman" in megalomaniac).toBe(false);
  });

  it("should use prototype to add to all objects", function () {
      function Circle(radius)
      {
        this.radius = radius;
      }

      var simpleCircle = new Circle(10);
      var colouredCircle = new Circle(5);
      colouredCircle.colour = "red";

      expect(simpleCircle.colour).toBe();
      expect(colouredCircle.colour).toBe("red");

      Circle.prototype.describe = function () {
        return "This circle has a radius of: " + this.radius;
      };

      expect(simpleCircle.describe()).toBe("This circle has a radius of: 10");
      expect(colouredCircle.describe()).toBe("This circle has a radius of: 5");
  });
});
<|endoftext|>


<commit_before>"use strict";

/**
 * Simple script to delete all but the newest entry per revision in a
 * key_rev_value table.
 */

var P = require('bluebird');
var cassandra = P.promisifyAll(require('cassandra-driver'));
var util = require('util');
var preq = require('preq');

if (!process.argv[3]) {
    console.error('Usage: node ' + process.argv[1] + ' <host> <keyspace>');
    process.exit(1);
}

// A custom retry policy
function AlwaysRetry () {}
util.inherits(AlwaysRetry, cassandra.policies.retry.RetryPolicy);
var ARP = AlwaysRetry.prototype;
// Always retry.
ARP.onUnavailable = function(requestInfo) {
    // Reset the connection
    requestInfo.handler.connection.close(function() {
        requestInfo.handler.connection.open(function(){});
    });
    return { decision: 1 };
};
ARP.onWriteTimeout = function() { return { decision: 2 }; };
ARP.onReadTimeout = function(requestInfo) {
    // Reset the connection
    requestInfo.handler.connection.close(function() {
        requestInfo.handler.connection.open(function(){});
    });
    console.log('read retry');
    return { decision: 1 };
};

function makeClient() {
    return new cassandra.Client({
        contactPoints: [process.argv[2]],
        keyspace: process.argv[3],
        authProvider: new cassandra.auth.PlainTextAuthProvider('cassandra', 'cassandra'),
        socketOptions: { connectTimeout: 10000 },
        //policies: {
        //     retry: new AlwaysRetry()
        //},
    });
}
var client = makeClient();

// Force a re-render of a revision by sending no-cache headers. Can be used to
// fix up stored content after temporary snafus in Parsoid or RB.
function reRender(row) {
    var uri = 'http://rest.wikimedia.org/' + row._domain + '/v1/page/html/'
            + encodeURIComponent(row.key) + '/' + row.rev;
    console.log(uri);
    return preq.get({
        uri: uri,
        headers: {
            'if-unmodified-since': 'Fri Apr 24 2015 13:00:00 GMT-0700 (PDT)',
            'cache-control': 'no-cache'
        }
    })
    .catch(console.log);
}

// Row state, used to make row handling decisions in processRow
var counts = {
    title: 0,
    rev: 0,
    render: 0,
};

var keys = {
    title: null,
    rev: null,
};

function processRow (row) {
    // Create a new set of keys
    var newKeys = {
        title: JSON.stringify([row._domain, row.key]),
        rev: JSON.stringify([row._domain, row.key, row.rev])
    };

    // Diff the keys and update counters
    if (newKeys.title !== keys.title) {
        counts.title = 0;
        counts.rev = 0;
        counts.render = 0;
    } else if (newKeys.rev !==  keys.rev) {
        counts.rev++;
        counts.render = 0;
    } else {
        counts.render++;
    }
    keys = newKeys;

    console.log('processing: ', keys.rev, new Date(row.tid.getDate()));

    // Now figure out what to do with this row
    if (false && counts.title === 0 && counts.rev === 0
            && counts.render === 0) {
        var rowDate = row.tid.getDate();
        if (false && /parsoid_html$/.test(process.argv[3])
            && rowDate > new Date('2015-04-23T23:30-0700')
            && rowDate < new Date('2015-04-24T13:00-0700')) {
            return reRender(row);
        } else {
            return P.resolve();
        }
        console.log(row.tid.getDate());
        //console.log(row);
        // Don't delete the most recent render for this revision
        return P.resolve();
    } else if ((counts.rev > 0 && counts.render > 0)
        || (counts.rev === 0 && counts.render > 0
            // Enforce a grace_ttl of 86400
            && (Date.now() - row.tid.getDate()) > 86400000)
        || (counts.rev > 0 && row.tid.getDate() < Date.parse('2015-07-10T13:00-0700'))) {
        console.log('-- deleting: ', keys.rev, new Date(row.tid.getDate()));
        var delQuery = 'delete from data where "_domain" = :domain and key = :key and rev = :rev and tid = :tid';
        row.domain = row._domain;
        return client.executeAsync(delQuery, row, { prepare: true });
    }

    // Else: nothing to do
    return P.resolve();
}

//var query = 'select "_domain", key, rev, tid from data where token("_domain",key) > -6004964422032836805';
var query = 'select "_domain", key, rev, tid from data';

function nextPage(pageState, retries) {
    //console.log(pageState);
    return client.executeAsync(query, [], {
        prepare: true,
        fetchSize: retries ? 1 : 50,
        pageState: pageState,
    })
    .catch(function(err) {
        console.log(retries, err);
        console.log(pageState);
        if (retries > 10) {
            process.exit(1);
        }
        return nextPage(pageState, (retries || 0) + 1);
    });
}

function processRows(pageState) {
    return nextPage(pageState)
    .then(function(res) {
        return P.resolve(res.rows)
        .each(processRow)
        .then(function() {
            process.nextTick(function() { processRows(res.pageState); });
        })
        .catch(function(e) {
            console.log(res.pageState);
            throw e;
        });
    });
}

var pageState;
if (process.argv[4]) {
    var pageState = process.argv[4];
}
return processRows(pageState);
<commit_msg>Support passing tokens, domain / title *or* pagestate<commit_after>"use strict";

/**
 * Simple script to delete all but the newest entry per revision in a
 * key_rev_value table.
 */

var P = require('bluebird');
var cassandra = P.promisifyAll(require('cassandra-driver'));
var util = require('util');
var preq = require('preq');

if (!process.argv[3]) {
    console.error('Usage: node ' + process.argv[1] + ' <host> <keyspace>');
    process.exit(1);
}

// A custom retry policy
function AlwaysRetry () {}
util.inherits(AlwaysRetry, cassandra.policies.retry.RetryPolicy);
var ARP = AlwaysRetry.prototype;
// Always retry.
ARP.onUnavailable = function(requestInfo) {
    // Reset the connection
    requestInfo.handler.connection.close(function() {
        requestInfo.handler.connection.open(function(){});
    });
    return { decision: 1 };
};
ARP.onWriteTimeout = function() { return { decision: 2 }; };
ARP.onReadTimeout = function(requestInfo) {
    // Reset the connection
    requestInfo.handler.connection.close(function() {
        requestInfo.handler.connection.open(function(){});
    });
    console.log('read retry');
    return { decision: 1 };
};

function makeClient() {
    return new cassandra.Client({
        contactPoints: [process.argv[2]],
        keyspace: process.argv[3],
        authProvider: new cassandra.auth.PlainTextAuthProvider('cassandra', 'cassandra'),
        socketOptions: { connectTimeout: 10000 },
        //policies: {
        //     retry: new AlwaysRetry()
        //},
    });
}
var client = makeClient();

// Force a re-render of a revision by sending no-cache headers. Can be used to
// fix up stored content after temporary snafus in Parsoid or RB.
function reRender(row) {
    var uri = 'http://rest.wikimedia.org/' + row._domain + '/v1/page/html/'
            + encodeURIComponent(row.key) + '/' + row.rev;
    console.log(uri);
    return preq.get({
        uri: uri,
        headers: {
            'if-unmodified-since': 'Fri Apr 24 2015 13:00:00 GMT-0700 (PDT)',
            'cache-control': 'no-cache'
        }
    })
    .catch(console.log);
}

// Row state, used to make row handling decisions in processRow
var counts = {
    title: 0,
    rev: 0,
    render: 0,
};

var keys = {
    title: null,
    rev: null,
};

function processRow (row) {
    // Create a new set of keys
    var newKeys = {
        title: JSON.stringify([row._domain, row.key]),
        rev: JSON.stringify([row._domain, row.key, row.rev])
    };

    // Diff the keys and update counters
    if (newKeys.title !== keys.title) {
        counts.title = 0;
        counts.rev = 0;
        counts.render = 0;
    } else if (newKeys.rev !==  keys.rev) {
        counts.rev++;
        counts.render = 0;
    } else {
        counts.render++;
    }
    keys = newKeys;

    console.log('processing: ', keys.rev, new Date(row.tid.getDate()));

    // Now figure out what to do with this row
    if (false && counts.title === 0 && counts.rev === 0
            && counts.render === 0) {
        var rowDate = row.tid.getDate();
        if (false && /parsoid_html$/.test(process.argv[3])
            && rowDate > new Date('2015-04-23T23:30-0700')
            && rowDate < new Date('2015-04-24T13:00-0700')) {
            return reRender(row);
        } else {
            return P.resolve();
        }
        console.log(row.tid.getDate());
        //console.log(row);
        // Don't delete the most recent render for this revision
        return P.resolve();
    } else if ((counts.rev > 0 && counts.render > 0)
        || (counts.rev === 0 && counts.render > 0
            // Enforce a grace_ttl of 86400
            && (Date.now() - row.tid.getDate()) > 86400000)
        || (counts.rev > 0 && row.tid.getDate() < Date.parse('2015-07-10T13:00-0700'))) {
        console.log('-- deleting: ', keys.rev, new Date(row.tid.getDate()));
        var delQuery = 'delete from data where "_domain" = :domain and key = :key and rev = :rev and tid = :tid';
        row.domain = row._domain;
        return client.executeAsync(delQuery, row, { prepare: true });
    }

    // Else: nothing to do
    return P.resolve();
}

// Parse optional start offsets
var startOffset = {
    token: null,
    domain: null,
    key: null,
    pageState: null,
};

if (parseInt(process.argv[4]) && !process.argv[5]) {
    startOffset.token = parseInt(process.argv[4]);
} else if (process.argv[4] && process.argv[5]) {
    startOffset.domain = process.argv[4];
    startOffset.key = process.argv[5];
} else if (process.argv[4]) {
    startOffset.pageState = process.argv[4];
}

var query = 'select "_domain", key, rev, tid from data';
var params = [];
if (startOffset.token) {
    query += ' where token("_domain",key) > ' + startOffset.token;
} else if (startOffset.domain) {
    query += ' where token("_domain",key) > token(?, ?)';
    params.push(startOffset.domain);
    params.push(startOffset.key);
}

function nextPage(pageState, retries) {
    //console.log(pageState);
    return client.executeAsync(query, params, {
        prepare: true,
        fetchSize: retries ? 1 : 50,
        pageState: pageState,
    })
    .catch(function(err) {
        console.log(retries, err);
        console.log(pageState);
        if (retries > 10) {
            process.exit(1);
        }
        return nextPage(pageState, (retries || 0) + 1);
    });
}

function processRows(pageState) {
    return nextPage(pageState)
    .then(function(res) {
        return P.resolve(res.rows)
        .each(processRow)
        .then(function() {
            process.nextTick(function() { processRows(res.pageState); });
        })
        .catch(function(e) {
            console.log(res.pageState);
            throw e;
        });
    });
}

return processRows(startOffset.pageState);
<|endoftext|>


<commit_before>describe("About Objects", function () {

  describe("Properties", function () {
    var meglomaniac;

    beforeEach(function () {
       meglomaniac = {  mastermind: "Joker", henchwoman: "Harley" };
    });

    it("should confirm objects are collections of properties", function () {
      expect(meglomaniac.mastermind).toBe(FILL_ME_IN);
    });

    it("should confirm that properties are case sensitive", function () {
      expect(meglomaniac.henchwoman).toBe(FILL_ME_IN);
      expect(meglomaniac.henchWoman).toBe(FILL_ME_IN);
    });
  });

  it("should know properties that are functions act like methods", function () {
    var meglomaniac = {
      mastermind : "Brain",
      henchman: "Pinky",
      battleCry: function (noOfBrains) {
        return "They are " + this.henchman + " and the" +
          Array(noOfBrains + 1).join(" " + this.mastermind);
      }
    };

    var battleCry = meglomaniac.battleCry(4);
    expect(FILL_ME_IN).toMatch(battleCry);
  });

  it("should confirm that when a function is attached to an object, 'this' refers to the object", function () {
    var currentDate = new Date()
    var currentYear = (currentDate.getFullYear());
    var meglomaniac = {
      mastermind: "James Wood",
      henchman: "Adam West",
      birthYear: 1970,
      calculateAge: function () {
        return currentYear - this.birthYear;
      }
    };

    expect(currentYear).toBe(FILL_ME_IN);
    expect(meglomaniac.calculateAge()).toBe(FILL_ME_IN);
  });

  describe("'in' keyword", function () {
    var meglomaniac;
    beforeEach(function () {
      meglomaniac = {
        mastermind: "The Monarch",
        henchwoman: "Dr Girlfriend",
        theBomb: true
      };
    });

    it("should have the bomb", function () {

      var hasBomb = "theBomb" in meglomaniac;

      expect(hasBomb).toBe(FILL_ME_IN);
    });

    it("should not have the detonator however", function () {

      var hasDetonator = "theDetonator" in meglomaniac;

      expect(hasDetonator).toBe(FILL_ME_IN);
    });
  });

  it("should know that properties can be added and deleted", function () {
    var meglomaniac = { mastermind : "Agent Smith", henchman: "Agent Smith" };

    expect("secretary" in meglomaniac).toBe(FILL_ME_IN);

    meglomaniac.secretary = "Agent Smith";
    expect("secretary" in meglomaniac).toBe(FILL_ME_IN);

    delete meglomaniac.henchman;
    expect("henchman" in meglomaniac).toBe(FILL_ME_IN);
  });

  it("should use prototype to add to all objects", function () {
    function Circle(radius)
    {
      this.radius = radius;
    }

    var simpleCircle = new Circle(10);
    var colouredCircle = new Circle(5);
    colouredCircle.colour = "red";

    expect(simpleCircle.colour).toBe(FILL_ME_IN);
    expect(colouredCircle.colour).toBe(FILL_ME_IN);

    Circle.prototype.describe = function () {
      return "This circle has a radius of: " + this.radius;
    };

    expect(simpleCircle.describe()).toBe(FILL_ME_IN);
    expect(colouredCircle.describe()).toBe(FILL_ME_IN);
  });
});
<commit_msg>Keys and properties are case sensitive.<commit_after>describe("About Objects", function () {

  describe("Properties", function () {
    var meglomaniac;

    beforeEach(function () {
       meglomaniac = {
        mastermind: "Joker",
        henchwoman: "Harley" };
    });

    it("should confirm objects are collections of properties", function () {
      expect(meglomaniac.mastermind).toBe("Joker");
    });

    it("should confirm that properties are case sensitive", function () {
      expect(meglomaniac.henchwoman).toBe("Harley");
      expect(meglomaniac.henchWoman).toBe(undefined);
    });
  });

  it("should know properties that are functions act like methods", function () {
    var meglomaniac = {
      mastermind : "Brain",
      henchman: "Pinky",
      battleCry: function (noOfBrains) {
        return "They are " + this.henchman + " and the" +
          Array(noOfBrains + 1).join(" " + this.mastermind);
      }
    };

    var battleCry = meglomaniac.battleCry(4);
    expect(FILL_ME_IN).toMatch(battleCry);
  });

  it("should confirm that when a function is attached to an object, 'this' refers to the object", function () {
    var currentDate = new Date()
    var currentYear = (currentDate.getFullYear());
    var meglomaniac = {
      mastermind: "James Wood",
      henchman: "Adam West",
      birthYear: 1970,
      calculateAge: function () {
        return currentYear - this.birthYear;
      }
    };

    expect(currentYear).toBe(FILL_ME_IN);
    expect(meglomaniac.calculateAge()).toBe(FILL_ME_IN);
  });

  describe("'in' keyword", function () {
    var meglomaniac;
    beforeEach(function () {
      meglomaniac = {
        mastermind: "The Monarch",
        henchwoman: "Dr Girlfriend",
        theBomb: true
      };
    });

    it("should have the bomb", function () {

      var hasBomb = "theBomb" in meglomaniac;

      expect(hasBomb).toBe(FILL_ME_IN);
    });

    it("should not have the detonator however", function () {

      var hasDetonator = "theDetonator" in meglomaniac;

      expect(hasDetonator).toBe(FILL_ME_IN);
    });
  });

  it("should know that properties can be added and deleted", function () {
    var meglomaniac = { mastermind : "Agent Smith", henchman: "Agent Smith" };

    expect("secretary" in meglomaniac).toBe(FILL_ME_IN);

    meglomaniac.secretary = "Agent Smith";
    expect("secretary" in meglomaniac).toBe(FILL_ME_IN);

    delete meglomaniac.henchman;
    expect("henchman" in meglomaniac).toBe(FILL_ME_IN);
  });

  it("should use prototype to add to all objects", function () {
    function Circle(radius)
    {
      this.radius = radius;
    }

    var simpleCircle = new Circle(10);
    var colouredCircle = new Circle(5);
    colouredCircle.colour = "red";

    expect(simpleCircle.colour).toBe(FILL_ME_IN);
    expect(colouredCircle.colour).toBe(FILL_ME_IN);

    Circle.prototype.describe = function () {
      return "This circle has a radius of: " + this.radius;
    };

    expect(simpleCircle.describe()).toBe(FILL_ME_IN);
    expect(colouredCircle.describe()).toBe(FILL_ME_IN);
  });
});
<|endoftext|>


<commit_before>'use strict';

var RNFSManager = require('NativeModules').RNFSManager;
var Promise = require('bluebird');
var base64 = require('base-64');

var _readDir = Promise.promisify(RNFSManager.readDir);
var _stat = Promise.promisify(RNFSManager.stat);
var _readFile = Promise.promisify(RNFSManager.readFile);
var _writeFile = Promise.promisify(RNFSManager.writeFile);
var _unlink = Promise.promisify(RNFSManager.unlink);
var _pathForBundle = Promise.promisify(RNFSManager.pathForBundle);

var convertError = (err) => {
  if (err.isOperational) {
    err = err.cause;
  }

  var error = new Error(err.description || err.message);
  error.code = err.code;
  throw error;
};

var NSFileTypeRegular = RNFSManager.NSFileTypeRegular;
var NSFileTypeDirectory = RNFSManager.NSFileTypeDirectory;

var RNFS = {

  readDir(path, rootDir) {
    return _readDir(path, rootDir)
      .catch(convertError);
  },

  stat(filepath) {
    return _stat(filepath)
      .then((result) => {
        return {
          'ctime': new Date(result.ctime*1000),
          'mtime': new Date(result.mtime*1000),
          'size': result.size,
          'mode': result.mode,
          isFile: () => result.type === NSFileTypeRegular,
          isDirectory: () => result.type === NSFileTypeDirectory,
        };
      })
      .catch(convertError);
  },

  readFile(filepath, shouldDecode) {
    var p = _readFile(filepath);

    if (shouldDecode !== false) {
      p = p.then((data) => {
        return base64.decode(data);
      });
    }

    return p.catch(convertError);
  },

  writeFile(filepath, contents, options) {
    return _writeFile(filepath, base64.encode(contents), options)
      .catch(convertError);
  },

  pathForBundle(bundleName) {
  	return _pathForBundle(bundleName);
  }

  unlink(filepath) {
    return _unlink(filepath)
      .catch(convertError);
  },

  MainBundle: RNFSManager.MainBundleDirectory,
  CachesDirectory: RNFSManager.NSCachesDirectory,
  DocumentDirectory: RNFSManager.NSDocumentDirectory,
  CachesDirectoryPath: RNFSManager.NSCachesDirectoryPath,
  DocumentDirectoryPath: RNFSManager.NSDocumentDirectoryPath
};

module.exports = RNFS;
<commit_msg>Add missing semicolon. Switch tab to space.<commit_after>'use strict';

var RNFSManager = require('NativeModules').RNFSManager;
var Promise = require('bluebird');
var base64 = require('base-64');

var _readDir = Promise.promisify(RNFSManager.readDir);
var _stat = Promise.promisify(RNFSManager.stat);
var _readFile = Promise.promisify(RNFSManager.readFile);
var _writeFile = Promise.promisify(RNFSManager.writeFile);
var _unlink = Promise.promisify(RNFSManager.unlink);
var _pathForBundle = Promise.promisify(RNFSManager.pathForBundle);

var convertError = (err) => {
  if (err.isOperational) {
    err = err.cause;
  }

  var error = new Error(err.description || err.message);
  error.code = err.code;
  throw error;
};

var NSFileTypeRegular = RNFSManager.NSFileTypeRegular;
var NSFileTypeDirectory = RNFSManager.NSFileTypeDirectory;

var RNFS = {

  readDir(path, rootDir) {
    return _readDir(path, rootDir)
      .catch(convertError);
  },

  stat(filepath) {
    return _stat(filepath)
      .then((result) => {
        return {
          'ctime': new Date(result.ctime*1000),
          'mtime': new Date(result.mtime*1000),
          'size': result.size,
          'mode': result.mode,
          isFile: () => result.type === NSFileTypeRegular,
          isDirectory: () => result.type === NSFileTypeDirectory,
        };
      })
      .catch(convertError);
  },

  readFile(filepath, shouldDecode) {
    var p = _readFile(filepath);

    if (shouldDecode !== false) {
      p = p.then((data) => {
        return base64.decode(data);
      });
    }

    return p.catch(convertError);
  },

  writeFile(filepath, contents, options) {
    return _writeFile(filepath, base64.encode(contents), options)
      .catch(convertError);
  },

  pathForBundle(bundleName) {
    return _pathForBundle(bundleName);
  },

  unlink(filepath) {
    return _unlink(filepath)
      .catch(convertError);
  },

  MainBundle: RNFSManager.MainBundleDirectory,
  CachesDirectory: RNFSManager.NSCachesDirectory,
  DocumentDirectory: RNFSManager.NSDocumentDirectory,
  CachesDirectoryPath: RNFSManager.NSCachesDirectoryPath,
  DocumentDirectoryPath: RNFSManager.NSDocumentDirectoryPath
};

module.exports = RNFS;
<|endoftext|>


<commit_before>/*
---

name: Touch

description: Provides a replacement for click events on mobile devices

authors: Christoph Pojer (@cpojer)

license: MIT-style license.

requires: [Core/Element.Events, Browser.Features.Touch]

provides: Touch

...
*/

if (Browser.Features.Touch) (function(){

delete Element.NativeEvents.click;

var key = 'touch:count';
var preventDefault = function(event){
	event.preventDefault();
};

Element.Events.click = {

	base: 'touchend',

	condition: function(event){
		if (event.targetTouches.length != 0) return false;

		var touch = event.changedTouches[0],
			target = document.elementFromPoint(touch.clientX, touch.clientY);

		do {
			if (target == this) return true;
		} while ((target = target.parentNode) && target);

		return false;
	},

	onAdd: function(){
		this.store(key, this.retrieve(key, 0) + 1);
		this.addEvent('touchstart', preventDefault);
	},

	onRemove: function(){
		var count = this.retrieve(key, 0);
		this.store(key, --count);

		if (count <= 0) this.removeEvent('touchstart', preventDefault);
	}

};

Element.Events.swipe = {
	onAdd: function(fn){
		Element.Events.swipe.startX,
		Element.Events.swipe.startY,
		Element.Events.swipe.active = false;

		Element.Events.swipe.minDistance = 50; // minimal distance to swipe

		touchStart = function(event){
			Element.Events.swipe.active = true;
			Element.Events.swipe.startX = event.event.touches[0].pageX;
			Element.Events.swipe.startY = event.event.touches[0].pageY;
		};

		touchMove = function(event){
			event.preventDefault();
			endX = event.event.touches[0].pageX;
			endY = event.event.touches[0].pageY;

			swipeDiff = endX - Element.Events.swipe.startX;

			isLeftSwipe = swipeDiff < Element.Events.swipe.minDistance * -1;
			isRightSwipe = swipeDiff > Element.Events.swipe.minDistance;

			// ! TODO: check vertical movement to cancel swipe

			if (Element.Events.swipe.active && (isRightSwipe || isLeftSwipe)){
				Element.Events.swipe.active = false;
				fn.call(this, {
					'direction': isLeftSwipe ? 'left' : 'right'
				});
			}
		};

		this.addEvent('touchstart', touchStart);
		this.addEvent('touchmove', touchMove);
	}
};

// Add Custom Pinch Event
Element.Events.pinch = {
	onAdd: function(fn){
		Element.Events.pinch.active = true;

		gestureStart = function(event){
			Element.Events.pinch.active = true;
		};
		gestureChange = function(event){
			event.preventDefault();

			isPinch = event.scale > 1.5 || event.scale < 0.5;

			if(Element.Events.pinch.active && isPinch)
			{
				Element.Events.pinch.active = false;

				fn.call(this, {
					"type": (event.scale > 1) ? "in" : "out",
					"factor": event.scale
				});
			}
		};

		this.addEvent('gesturestart', gestureStart);
		this.addEvent('gesturechange', gestureChange);
	}
}

})();<commit_msg>* Moving swipe/pinch from Touch.js to separate files<commit_after>/*
---

name: Touch

description: Provides a replacement for click events on mobile devices

authors: Christoph Pojer (@cpojer)

license: MIT-style license.

requires: [Core/Element.Events, Browser.Features.Touch]

provides: Touch

...
*/

if (Browser.Features.Touch) (function(){

delete Element.NativeEvents.click;

var key = 'touch:count';
var preventDefault = function(event){
	event.preventDefault();
};

Element.Events.click = {

	base: 'touchend',

	condition: function(event){
		if (event.targetTouches.length != 0) return false;

		var touch = event.changedTouches[0],
			target = document.elementFromPoint(touch.clientX, touch.clientY);

		do {
			if (target == this) return true;
		} while ((target = target.parentNode) && target);

		return false;
	},

	onAdd: function(){
		this.store(key, this.retrieve(key, 0) + 1);
		this.addEvent('touchstart', preventDefault);
	},

	onRemove: function(){
		var count = this.retrieve(key, 0);
		this.store(key, --count);

		if (count <= 0) this.removeEvent('touchstart', preventDefault);
	}

};

})();<|endoftext|>


<commit_before>// Filter is intendet to be a prototype for filters. It is totally reactive.
KriegslustigFilter = {
  // This should be defined before the object is used. It's the collection that should be queried
  collection: {}
  // The array of filtered items. Never use it to get or set Items. Use getItems instead to be reactive
, _items: []
  // The dep for items
, _itemsDep: new Tracker.Dependency
  // init should be called AFTER collection is set. It set's an autorun on changes in that collection
, init: function () {
    var self = this
  , updateItemsWrapper = function () {
      self._updateItems()
    }
    self._updateItems()
    self.collection.find().observe({
      added: updateItemsWrapper
    , changed: updateItemsWrapper
    , removed: updateItemsWrapper
    })
  }
  // Filters are added to this object when newFilter is called
, subFilters: {}
  // The return values of all subFilters.generateSubFilter are concatinated into this object. It's later used as the actual query
, concatinatedFilter: {}
  // Always use this function to get the filter.items array
, getItems: function () {
    var self = this
    self._itemsDep.depend()
    return self._items
  }
  // this sets the items array and triggers the dep. It's not inteded for use outside this dictionary
, _setItems: function (newValue) {
    var self = this
    self._items = newValue
    self._itemsDep.changed()
  }
  // This regenerates the concatinatedFilter, reruns the query and runs filter._setItems
, _updateItems: function () {
    var self = this
    self.concatinatedFilter = self._getConcatinatedFilters()
    self._setItems(self.queryItems(self.concatinatedFilter))
  }
, _getConcatinatedFilters: function () {
    var self = this
  , concatinatedFilter = {}
    _.each(self.subFilters, function (subFilter) {
      if(subFilter.active) {
        concatinatedFilter = self._deepExtendOwn(concatinatedFilter, subFilter.generateSubFilter())
      }
    })
    return concatinatedFilter
  }
, _deepExtendOwn: function (thisObject, withObject) {
    var self = this
  , returnDict = {}
    for(key in thisObject) {
      if(thisObject.hasOwnProperty(key)) {
        if(withObject.hasOwnProperty(key)) {
          if(typeof thisObject[key] == 'object' && typeof withObject[key] == 'object') {
            returnDict[key] = self._deepExtendOwn(thisObject[key], withObject[key])
          } else {
            returnDict[key] = withObject[key]
          }
        } else {
          returnDict[key] = thisObject[key]
        }
      }
    }
    for(key in withObject) {
      if(withObject.hasOwnProperty(key) && !thisObject.hasOwnProperty(key)) {
        returnDict[key] = withObject[key]
      }
    }
    return returnDict
  }
  // Use this to add a filter newProps is a dictionary that will be concatinated with the filterTemplate read it for further information
, newSubFilter: function (subFilterName, newProps) {
    var self = this
    self.filterTemplate = {
      // this can be used to deactivate the whole filter
      active: true
      // All attributes used in the actual filter (filter.generateSubFilter) should be stored here
    , attributes: {
        // An example to show the pattern that should be used
        sampleAttribute: {
          // When setAttribute is called the typeof newValue is checked against this
          dataType: 'bool'
          // Never manipulate this value directly after initiating the subfilter. Always use setAttribute. That way the change propagetes and _items changes reactively
        , value: true
        }
      }
      // This is just an example and should be overwritten with newProps.generateSubFilter. The return value of this function should match [this](http://docs.meteor.com/#/full/selectors) pattern. [further reference](http://docs.mongodb.org/manual/reference/operator/query/)
    , generateSubFilter: function () {
        var that = this
        return {boolVal: that.attributes.sampleAttribute.value}
      }
    }
    self.subFilters[subFilterName] = _.extend(self.filterTemplate, newProps)
    // This should be called to set attributes (expl.: someFilter.subFilters.usage.setAttribute('sampleAttribute', true))
    _.extend(self.subFilters[subFilterName], {
      setAttribute: function (attribute, newValue) {
        var that = this
        // Check if the attribute matches the pattern
        if(that.attributes[attribute]
          && that.attributes[attribute].dataType) {
          // check if the datatype matches
          if(typeof newValue == that.attributes[attribute].dataType) {
            that.attributes[attribute].value = newValue
            // Update _items and trigger the dep
            self.updateItems()
            return true
          } else {
            console.log('data type should be ' + that.attributes[attribute].dataType + ', ' +  typeof newValue + ' was passed instead')
          }
        } else {
          console.log('Invalid attribute name: ' + attribute)
        }
        return false
      }
    , getAttribute: function (attributeName) {
        var that = this
        return that.attributes[attributeName].value
      }
    , activate: function () {
        var that = this
        that.active = true
        self._updateItems()
      }
    , deactivate: function () {
        var that = this
        that.active = false
        self._updateItems()
      }
    })
  }
  // This executes the query saved in concatinatedFilter. It can be used the get _items as an unreactive array
, queryItems: function () {
    var self = this
    return self.collection.find(self.concatinatedFilter).fetch()
  }
}<commit_msg>Add a toggleState function for subfilters<commit_after>// Filter is intendet to be a prototype for filters. It is totally reactive.
KriegslustigFilter = {
  // This should be defined before the object is used. It's the collection that should be queried
  collection: {}
  // The array of filtered items. Never use it to get or set Items. Use getItems instead to be reactive
, _items: []
  // The dep for items
, _itemsDep: new Tracker.Dependency
  // init should be called AFTER collection is set. It set's an autorun on changes in that collection
, init: function () {
    var self = this
  , updateItemsWrapper = function () {
      self._updateItems()
    }
    self._updateItems()
    self.collection.find().observe({
      added: updateItemsWrapper
    , changed: updateItemsWrapper
    , removed: updateItemsWrapper
    })
  }
  // Filters are added to this object when newFilter is called
, subFilters: {}
  // The return values of all subFilters.generateSubFilter are concatinated into this object. It's later used as the actual query
, concatinatedFilter: {}
  // Always use this function to get the filter.items array
, getItems: function () {
    var self = this
    self._itemsDep.depend()
    return self._items
  }
  // this sets the items array and triggers the dep. It's not inteded for use outside this dictionary
, _setItems: function (newValue) {
    var self = this
    self._items = newValue
    self._itemsDep.changed()
  }
  // This regenerates the concatinatedFilter, reruns the query and runs filter._setItems
, _updateItems: function () {
    var self = this
    self.concatinatedFilter = self._getConcatinatedFilters()
    self._setItems(self.queryItems(self.concatinatedFilter))
  }
, _getConcatinatedFilters: function () {
    var self = this
  , concatinatedFilter = {}
    _.each(self.subFilters, function (subFilter) {
      if(subFilter.active) {
        concatinatedFilter = self._deepExtendOwn(concatinatedFilter, subFilter.generateSubFilter())
      }
    })
    return concatinatedFilter
  }
, _deepExtendOwn: function (thisObject, withObject) {
    var self = this
  , returnDict = {}
    for(key in thisObject) {
      if(thisObject.hasOwnProperty(key)) {
        if(withObject.hasOwnProperty(key)) {
          if(typeof thisObject[key] == 'object' && typeof withObject[key] == 'object') {
            returnDict[key] = self._deepExtendOwn(thisObject[key], withObject[key])
          } else {
            returnDict[key] = withObject[key]
          }
        } else {
          returnDict[key] = thisObject[key]
        }
      }
    }
    for(key in withObject) {
      if(withObject.hasOwnProperty(key) && !thisObject.hasOwnProperty(key)) {
        returnDict[key] = withObject[key]
      }
    }
    return returnDict
  }
  // Use this to add a filter newProps is a dictionary that will be concatinated with the filterTemplate read it for further information
, newSubFilter: function (subFilterName, newProps) {
    var self = this
    self.filterTemplate = {
      // this can be used to deactivate the whole filter
      active: true
      // All attributes used in the actual filter (filter.generateSubFilter) should be stored here
    , attributes: {
        // An example to show the pattern that should be used
        sampleAttribute: {
          // When setAttribute is called the typeof newValue is checked against this
          dataType: 'bool'
          // Never manipulate this value directly after initiating the subfilter. Always use setAttribute. That way the change propagetes and _items changes reactively
        , value: true
        }
      }
      // This is just an example and should be overwritten with newProps.generateSubFilter. The return value of this function should match [this](http://docs.meteor.com/#/full/selectors) pattern. [further reference](http://docs.mongodb.org/manual/reference/operator/query/)
    , generateSubFilter: function () {
        var that = this
        return {boolVal: that.attributes.sampleAttribute.value}
      }
    }
    self.subFilters[subFilterName] = _.extend(self.filterTemplate, newProps)
    // add attributes to the subfilter that shouldn't be overwritten
    _.extend(self.subFilters[subFilterName], {
      // This should be called to set attributes (expl.: someFilter.subFilters.usage.setAttribute('sampleAttribute', true))
      setAttribute: function (attribute, newValue) {
        var that = this
        // Check if the attribute matches the pattern
        if(that.attributes[attribute]
          && that.attributes[attribute].dataType) {
          // check if the datatype matches
          if(typeof newValue == that.attributes[attribute].dataType) {
            that.attributes[attribute].value = newValue
            // Update _items and trigger the dep
            self.updateItems()
            return true
          } else {
            console.log('data type should be ' + that.attributes[attribute].dataType + ', ' +  typeof newValue + ' was passed instead')
          }
        } else {
          console.log('Invalid attribute name: ' + attribute)
        }
        return false
      }
    , getAttribute: function (attributeName) {
        var that = this
        return that.attributes[attributeName].value
      }
    , activate: function () {
        var that = this
        that.active = true
        self._updateItems()
      }
    , deactivate: function () {
        var that = this
        that.active = false
        self._updateItems()
      }
    , toggleActiveState: function () {
        var that = this
        that.active = !that.active
        self._updateItems()
        return that.active
      }
    })
  }
  // This executes the query saved in concatinatedFilter. It can be used the get _items as an unreactive array
, queryItems: function () {
    var self = this
    return self.collection.find(self.concatinatedFilter).fetch()
  }
}<|endoftext|>


<commit_before>/**
 * Aborts requests to external resources or given domains
 *
 * Does not emit any metrics
 */
'use strict';

exports.version = '0.1';

exports.module = function(phantomas) {
	var ourDomain = false,

		// --no-externals
		noExternalsMode = (phantomas.getParam('no-externals') === true),
		// --allow-domain .fastly.net,.googleapis.com
		allowedDomains = phantomas.getParam('allow-domain'),
		allowedDomainsRegExp,
		// --block-domain google-analytics.com
		blockedDomains = phantomas.getParam('block-domain'),
		blockedDomainsRegExp;

	function checkBlock(domain) {
		var blocked = false;

		// --no-externals
		if (noExternalsMode && ourDomain !== false && domain !== ourDomain) {
			blocked = true;
		}

		// match blacklist (--block-domain)
		if (blockedDomainsRegExp && blockedDomainsRegExp.test(domain)) {
			blocked = true;
		}

		// match whitelist (--allow-domain)
		if (allowedDomainsRegExp && allowedDomainsRegExp.test(domain)) {
			blocked = false;
		}

		return blocked;
	}

	// parse settings
	function parseParameter(value) {
		return value.
		split(',').
		map(function(item) {
			return item.trim();
		});
	}

	allowedDomains = (typeof allowedDomains === 'string') ? parseParameter(allowedDomains) : false;
	blockedDomains = (typeof blockedDomains === 'string') ? parseParameter(blockedDomains) : false;

	if (noExternalsMode) {
		phantomas.log('Block domains: working in --no-externals mode');
	}

	if (allowedDomains !== false) {
		phantomas.log('Block domains: whitelist - ' + allowedDomains.join(', '));
		allowedDomainsRegExp = new RegExp('(' + allowedDomains.join('|') + ')$');
	}

	if (blockedDomains !== false) {
		phantomas.log('Block domains: blacklist - ' + blockedDomains.join(', '));
		blockedDomainsRegExp = new RegExp('(' + blockedDomains.join('|') + ')$');
	}

	// get the "main" domain from the first request (see issues #197 and #535)
	phantomas.on('beforeSend', function(entry) {
		if (!ourDomain) {
			ourDomain = entry.domain;
			phantomas.log('Block domains: assuming "%s" to be the main domain (from the first request sent)', ourDomain);
		}
	});

	// check each request before sending
	phantomas.on('beforeSend', function(entry) {
		if (checkBlock(entry.domain)) {
			entry.block();

			// stats
			phantomas.incrMetric('blockedRequests'); // @desc number of requests blocked due to domain filtering @optional
			phantomas.addOffender('blockedRequests', entry.url);
		}
	});
};
<commit_msg>Fix --allow-domain option<commit_after>/**
 * Aborts requests to external resources or given domains
 *
 * Does not emit any metrics
 */
'use strict';

exports.version = '0.1';

exports.module = function(phantomas) {
	var ourDomain = false,

		// --no-externals
		noExternalsMode = (phantomas.getParam('no-externals') === true),
		// --allow-domain .fastly.net,.googleapis.com
		allowedDomains = phantomas.getParam('allow-domain'),
		allowedDomainsRegExp,
		// --block-domain google-analytics.com
		blockedDomains = phantomas.getParam('block-domain'),
		blockedDomainsRegExp;

	function checkBlock(domain) {
		var blocked = false;

		// --no-externals
		if (noExternalsMode && ourDomain !== false && domain !== ourDomain) {
			blocked = true;
		}

		// match blacklist (--block-domain)
		if (blockedDomainsRegExp && blockedDomainsRegExp.test(domain)) {
			blocked = true;
		}

		// match whitelist (--allow-domain)
		if (allowedDomainsRegExp) {
			if (allowedDomainsRegExp.test(domain)) {
				blocked = false;
			} else {
				blocked = true;
			}
		}

		return blocked;
	}

	// parse settings
	function parseParameter(value) {
		return value.
		split(',').
		map(function(item) {
			return item.trim();
		});
	}

	allowedDomains = (typeof allowedDomains === 'string') ? parseParameter(allowedDomains) : false;
	blockedDomains = (typeof blockedDomains === 'string') ? parseParameter(blockedDomains) : false;

	if (noExternalsMode) {
		phantomas.log('Block domains: working in --no-externals mode');
	}

	if (allowedDomains !== false) {
		phantomas.log('Block domains: whitelist - ' + allowedDomains.join(', '));
		allowedDomainsRegExp = new RegExp('(' + allowedDomains.join('|') + ')$');
	}

	if (blockedDomains !== false) {
		phantomas.log('Block domains: blacklist - ' + blockedDomains.join(', '));
		blockedDomainsRegExp = new RegExp('(' + blockedDomains.join('|') + ')$');
	}

	// get the "main" domain from the first request (see issues #197 and #535)
	phantomas.on('beforeSend', function(entry) {
		if (!ourDomain) {
			ourDomain = entry.domain;
			phantomas.log('Block domains: assuming "%s" to be the main domain (from the first request sent)', ourDomain);
		}
	});

	// check each request before sending
	phantomas.on('beforeSend', function(entry) {
		if (checkBlock(entry.domain)) {
			entry.block();

			// stats
			phantomas.incrMetric('blockedRequests'); // @desc number of requests blocked due to domain filtering @optional
			phantomas.addOffender('blockedRequests', entry.url);
		}
	});
};
<|endoftext|>


<commit_before>describe('uiJq', function() {
  var scope;
  scope = null;
  beforeEach(module('ui.directives'));
  beforeEach(inject(function($rootScope) {
    scope = $rootScope.$new();
  }));
  describe('function or plugin isn\'t found', function() {
    it('should throw an error', function() {
      inject(function($compile) {
        var element;
        element = $compile("

")(scope);
        expect(element.text()).toThrow('ui-jq: The "failure" function does not exist');
      });
    });
  });
});
<commit_msg>And now it passes! Baby steps.<commit_after>describe('uiJq', function() {
  var scope;
  scope = null;
  beforeEach(module('ui.directives'));
  beforeEach(inject(function($rootScope) {
    scope = $rootScope.$new();
  }));
  describe('function or plugin isn\'t found', function() {
    it('should throw an error', function() {
      inject(function($compile) {
        expect(function(){
          $compile("

")(scope);
        }).toThrow('ui-jq: The "failure" function does not exist');
      });
    });
  });
});
<|endoftext|>


<commit_before>import Morph from './Morph.js';

export default class VivideList extends Morph {
  async initialize() {
    this.windowTitle = "VivideList";

    this.transformations = [];
    this.defaultTransformation = list => [];

    this.depictions = [];
    this.defaultDepiction = elem => elem;

    this.predecessor = null;
    this.successors = [];
  }

  register(anotherWidget) {
    this.successors.push(anotherWidget);
    anotherWidget.setPredecessor(this);
  }

  trigger() {
    this.show(this.predecessor.output());
  }

  setPredecessor(anotherWidget) {
    this.predecessor = anotherWidget;
  }

  pushTransformation(transformationFunction) {
    this.transformations.push(transformationFunction);
  }

  pushDepiction(depictionFunction) {
    this.depictions.push(depictionFunction);
  }

  toggleSelection(wrapper) {
    return () => {
      wrapper.selected = wrapper.selected ? false : true;
      this.display()
      for(let i in this.successors) {
        this.successors[i].trigger();
      }
    }
  }

  toggleChildren(level, wrapper) {
    return () => {
      if(wrapper.hasChildren) {
        wrapper.children = [];
        wrapper.hasChildren = false;
      } else {
        wrapper.children =
          this.transformations.length > level+1 ?
          this.transformations[level+1](wrapper.object).map(object => this.wrap(object)) :
          this.defaultTransformation(wrapper.object).map(object => this.wrap(object));
        wrapper.hasChildren = true;
      }
      this.display()
    }
  }

  display() {
    // TODO: Better algorithm for textContent
    this.textContent = this.model.map(elem => this.depictions[0](elem.object)).join('
');
    this.get("#content").innerHTML = "";
    for(let i in this.model) {

      this.displayListEntry(0, this.model[i]);

    }
  }

  displayListEntry(level, wrapper) {
    let listentry = document.createElement("div");
    listentry.className = "listentry";

    let listentryPlaceholder = document.createElement("div");
    listentryPlaceholder.className = "listentry-placeholder";
    listentryPlaceholder.style.width = (level * 4) + "em";
    listentryPlaceholder.innerHTML = " ";

    let listentryUnfold = document.createElement("div");
    listentryUnfold.className = "listentry-unfold";
    listentryUnfold.addEventListener("click", this.toggleChildren(level, wrapper));
    listentryUnfold.innerHTML = "(v)";

    let listentryContent = document.createElement("div");
    listentryContent.className = "listentry-content";
    if(wrapper.selected) { listentryContent.classList.add("selected"); }
    listentryContent.addEventListener("click", this.toggleSelection(wrapper));
    listentryContent.innerHTML =
      this.depictions.length > level ?
      this.depictions[level](wrapper.object) :
      this.defaultDepiction(wrapper.object);

    listentry.appendChild(listentryPlaceholder);
    listentry.appendChild(listentryUnfold);
    listentry.appendChild(listentryContent);
    this.get("#content").appendChild(listentry);

    if(wrapper.children) {
      for(let i in wrapper.children) {
        this.displayListEntry(level+1, wrapper.children[i]);
      }
    }
  }

  output() {
    return this.model.filter(
      elem => elem.selected
    ).map(
      elem => elem.object
    );
  }

  wrap(object) {
    let wrapper = {
      object: object,
      selected: false,
      children: [],
      hasChildren: false,
    }
    return wrapper;
  }

  setModel(model) {
    model =
      this.transformations.length > 0 ?
      this.transformations[0](model) :
      this.defaultTransformation(model);
    this.model = model.map(elem => this.wrap(elem));
  }

  show(model) {
    this.setModel(model);
    this.display();
  }

  livelyExample() {
    this.pushTransformation((list) => {
      return list.filter(elem => elem.age < 100);
    });
    this.pushDepiction(elem => elem.name);
    this.pushTransformation(elem => elem.pets);
    this.pushDepiction(elem => elem.toUpperCase());
    this.show([
      {name: "John Doe", age: 25, pets: ["Waldy", "Smokie"]},
      {name: "Jane Doe", age: 24, pets: ["Jaques-the-Bird"]},
      {name: "Manfred Mustermann", age: 50, pets: ["Georg-Musterhund"]},
      {name: "John Wayne", age: 110, pets: []},
    ]);
  }
}<commit_msg>vivide_ multi_level_lists<commit_after>import Morph from './Morph.js';

// TODO: Add children to output
// TODO: Refactor display functions to builder pattern
// TODO: Reference functions for infinite trees

export default class VivideList extends Morph {
  async initialize() {
    this.windowTitle = "VivideList";

    this.transformations = [];
    this.defaultTransformation = list => [];

    this.depictions = [];
    this.defaultDepiction = elem => elem;

    this.predecessor = null;
    this.successors = [];
  }

  register(anotherWidget) {
    this.successors.push(anotherWidget);
    anotherWidget.setPredecessor(this);
  }

  trigger() {
    this.show(this.predecessor.output());
  }

  setPredecessor(anotherWidget) {
    this.predecessor = anotherWidget;
  }

  pushTransformation(transformationFunction) {
    this.transformations.push(transformationFunction);
  }

  transformationAt(index) {
    if(this.transformations.length > index) {
      return this.transformations[index];
    } else {
      return this.defaultTransformation;
    }
  }

  pushDepiction(depictionFunction) {
    this.depictions.push(depictionFunction);
  }

  depictionAt(index) {
    if(this.depictions.length > index) {
      return this.depictions[index];
    } else {
      return this.defaultDepiction;
    }
  }

  toggleSelection(wrapper) {
    return () => {
      wrapper.selected = wrapper.selected ? false : true;
      this.display()
      for(let i in this.successors) {
        this.successors[i].trigger();
      }
    }
  }

  toggleChildren(level, wrapper) {
    return () => {
      if(wrapper.hasChildren) {
        wrapper.children = [];
        wrapper.hasChildren = false;
      } else {
        wrapper.children = this.transformationAt(level+1)(wrapper.object).map(object => this.wrap(object));
        wrapper.hasChildren = true;
      }
      this.display()
    }
  }

  display() {
    this.textContent = this.model.map(elem => this.depictionAt(0)(elem.object)).join('
');
    this.get("#content").innerHTML = "";
    for(let i in this.model) { this.displayListEntry(0, this.model[i]); }
  }

  displayListEntry(level, wrapper) {
    let listentry = document.createElement("div");
    listentry.className = "listentry";

    let listentryPlaceholder = document.createElement("div");
    listentryPlaceholder.className = "listentry-placeholder";
    listentryPlaceholder.style.width = (level * 2) + "em";
    listentryPlaceholder.innerHTML = " ";

    let listentryUnfold = document.createElement("div");
    listentryUnfold.className = "listentry-unfold";
    listentryUnfold.addEventListener("click", this.toggleChildren(level, wrapper));
    listentryUnfold.innerHTML = "(v)";

    let listentryContent = document.createElement("div");
    listentryContent.className = "listentry-content";
    if(wrapper.selected) { listentryContent.classList.add("selected"); }
    listentryContent.addEventListener("click", this.toggleSelection(wrapper));
    listentryContent.innerHTML = this.depictionAt(level)(wrapper.object);
      this.defaultDepiction(wrapper.object);

    listentry.appendChild(listentryPlaceholder);
    listentry.appendChild(listentryUnfold);
    listentry.appendChild(listentryContent);
    this.get("#content").appendChild(listentry);

    for(let i in wrapper.children) {
      this.displayListEntry(level+1, wrapper.children[i]);
    }
  }

  output() {
    return this.model.filter(
      elem => elem.selected
    ).map(
      elem => elem.object
    );
  }

  wrap(object) {
    let wrapper = {
      object: object,
      selected: false,
      children: [],
      hasChildren: false,
    }
    return wrapper;
  }

  setModel(model) {
    model = this.transformationAt(0)(model);
    this.model = model.map(elem => this.wrap(elem));
  }

  show(model) {
    this.setModel(model);
    this.display();
  }

  livelyExample() {
    this.pushTransformation((list) => {
      return list.filter(person => person.age < 100);
    });
    this.pushDepiction(person => person.name);
    this.pushTransformation(person => person.pets);
    this.pushDepiction(pet => pet.name + " the " + pet.type);
    this.pushTransformation(pet => pet.eats);
    this.pushDepiction(meal => "... eats " + meal);
    this.show([
      {name: "John Doe", age: 25, pets: [
        {type: "Dog", name: "Waldy", eats: ["Pasta", "Pizza"]},
        {type: "Cat", name: "Smokie", eats: ["Socks"]}
      ]},
      {name: "Jane Doe", age: 24, pets: [
        {type: "Bird", name: "Jaques", eats: []}
      ]},
      {name: "Manfred Mustermann", age: 50, pets: [
        {type: "Monster", name: "Georg Musterhund", eats: ["Children"]}
      ]},
      {name: "John Wayne", age: 110, pets: []},
    ]);
  }
}<|endoftext|>


<commit_before>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.	If not, see <http://www.gnu.org/licenses/>.

erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
	init: function(wrapper, title) {
		this._super({
			title: title,
			page: wrapper,
			parent: $(wrapper).find('.layout-main'),
			appframe: wrapper.appframe,
			doctypes: ["Company", "Fiscal Year", "Account", "GL Entry", "Cost Center"],
			tree_grid: {
				show: true,
				parent_field: "parent_account",
				formatter: function(item) {
					return repl("\
						%(value)s", {
							value: item.name,
						});
				}
			},
		});
	},
	setup_columns: function() {
		this.columns = [
			{id: "name", name: __("Account"), field: "name", width: 300, cssClass: "cell-title",
				formatter: this.tree_formatter},
			{id: "opening_dr", name: __("Opening (Dr)"), field: "opening_dr", width: 100,
				formatter: this.currency_formatter},
			{id: "opening_cr", name: __("Opening (Cr)"), field: "opening_cr", width: 100,
				formatter: this.currency_formatter},
			{id: "debit", name: __("Debit"), field: "debit", width: 100,
				formatter: this.currency_formatter},
			{id: "credit", name: __("Credit"), field: "credit", width: 100,
				formatter: this.currency_formatter},
			{id: "closing_dr", name: __("Closing (Dr)"), field: "closing_dr", width: 100,
				formatter: this.currency_formatter},
			{id: "closing_cr", name: __("Closing (Cr)"), field: "closing_cr", width: 100,
				formatter: this.currency_formatter}
		];

	},
	filters: [
		{fieldtype: "Select", label: __("Company"), link:"Company", default_value: "Select Company...",
			filter: function(val, item, opts, me) {
				if (item.company == val || val == opts.default_value) {
					return me.apply_zero_filter(val, item, opts, me);
				}
				return false;
			}},
		{fieldtype: "Select", label: __("Fiscal Year"), link:"Fiscal Year",
			default_value: "Select Fiscal Year..."},
		{fieldtype: "Date", label: __("From Date")},
		{fieldtype: "Label", label: __("To")},
		{fieldtype: "Date", label: __("To Date")},
		{fieldtype: "Button", label: __("Refresh"), icon:"icon-refresh icon-white",
		 	cssClass:"btn-info"},
		{fieldtype: "Button", label: __("Reset Filters"), icon: "icon-filter"},
	],
	setup_filters: function() {
		this._super();
		var me = this;
		// default filters
		this.filter_inputs.fiscal_year.change(function() {
			var fy = $(this).val();
			$.each(frappe.report_dump.data["Fiscal Year"], function(i, v) {
				if (v.name==fy) {
					me.filter_inputs.from_date.val(dateutil.str_to_user(v.year_start_date));
					me.filter_inputs.to_date.val(dateutil.str_to_user(v.year_end_date));
				}
			});
			me.set_route();
		});
		me.show_zero_check()
		if(me.ignore_closing_entry) me.ignore_closing_entry();
	},
	prepare_data: function() {
		var me = this;
		if(!this.primary_data) {
			// make accounts list
			me.data = [];
			me.parent_map = {};
			me.item_by_name = {};

			$.each(frappe.report_dump.data["Account"], function(i, v) {
				var d = copy_dict(v);

				me.data.push(d);
				me.item_by_name[d.name] = d;
				if(d.parent_account) {
					me.parent_map[d.name] = d.parent_account;
				}
			});

			me.primary_data = [].concat(me.data);
		}

		me.data = [].concat(me.primary_data);
		$.each(me.data, function(i, d) {
			me.init_account(d);
		});

		this.set_indent();
		this.prepare_balances();

	},
	init_account: function(d) {
		this.reset_item_values(d);
	},

	prepare_balances: function() {
		var gl = frappe.report_dump.data['GL Entry'];
		var me = this;

		this.opening_date = dateutil.user_to_obj(this.filter_inputs.from_date.val());
		this.closing_date = dateutil.user_to_obj(this.filter_inputs.to_date.val());
		this.set_fiscal_year();
		if (!this.fiscal_year) return;

		$.each(this.data, function(i, v) {
			v.opening_dr = v.opening_cr = v.debit
				= v.credit = v.closing_dr = v.closing_cr = 0;
		});

		$.each(gl, function(i, v) {
			var posting_date = dateutil.str_to_obj(v.posting_date);
			var account = me.item_by_name[v.account];
			me.update_balances(account, posting_date, v);
		});

		this.update_groups();
	},
	update_balances: function(account, posting_date, v) {
		var bal = flt(account.opening_dr) - flt(account.opening_cr) + flt(v.debit) - flt(v.credit);
		// opening
		if (posting_date < this.opening_date || v.is_opening === "Yes") {
			if (account.report_type === "Profit and Loss" &&
				posting_date <= dateutil.str_to_obj(this.fiscal_year[1])) {
				// balance of previous fiscal_year should
				//	not be part of opening of pl account balance
			} else {
				this.set_debit_or_credit(account, "opening", bal);
			}
		} else if (this.opening_date <= posting_date && posting_date <= this.closing_date) {
			// in between
			account.debit += flt(v.debit);
			account.credit += flt(v.credit);
		}
		// closing
		this.set_debit_or_credit(account, "closing", bal);
	},
	set_debit_or_credit: function(account, field, balance) {
		if(balance > 0) {
			account[field+"_dr"] = balance;
			account[field+"_cr"] = 0;
		} else {
			account[field+"_cr"] = Math.abs(balance);
			account[field+"_dr"] = 0;
		}
	},
	update_groups: function() {
		// update groups
		var me= this;
		$.each(this.data, function(i, account) {
			// update groups
			if((account.group_or_ledger == "Ledger") || (account.rgt - account.lft == 1)) {
				var parent = me.parent_map[account.name];
				while(parent) {
					var parent_account = me.item_by_name[parent];
					$.each(me.columns, function(c, col) {
						if (col.formatter == me.currency_formatter) {
							if(col.field=="opening_dr") {
								var bal = flt(parent_account.opening_dr) -
									flt(parent_account.opening_cr) +
									flt(account.opening_dr) - flt(account.opening_cr);
								me.set_debit_or_credit(parent_account, "opening", bal);
							} else if(col.field=="closing_dr") {
								var bal = flt(parent_account.closing_dr) -
									flt(parent_account.closing_cr) +
									flt(account.closing_dr) - flt(account.closing_cr);
								me.set_debit_or_credit(parent_account, "closing", bal);
							} else if(in_list(["debit", "credit"], col.field)) {
								parent_account[col.field] = flt(parent_account[col.field]) +
									flt(account[col.field]);
							}
						}
					});
					parent = me.parent_map[parent];
				}
			}
		});
	},

	set_fiscal_year: function() {
		if (this.opening_date > this.closing_date) {
			msgprint(__("Opening Date should be before Closing Date"));
			return;
		}

		this.fiscal_year = null;
		var me = this;
		$.each(frappe.report_dump.data["Fiscal Year"], function(i, v) {
			if (me.opening_date >= dateutil.str_to_obj(v.year_start_date) &&
				me.closing_date <= dateutil.str_to_obj(v.year_end_date)) {
					me.fiscal_year = v;
				}
		});

		if (!this.fiscal_year) {
			msgprint(__("Opening Date and Closing Date should be within same Fiscal Year"));
			return;
		}
	},

	show_general_ledger: function(account) {
		frappe.route_options = {
			account: account,
			company: this.company,
			from_date: this.from_date,
			to_date: this.to_date
		};
		frappe.set_route("query-report", "General Ledger");
	}
});<commit_msg>Closing balance fixes in trial balance. Fixes #1730<commit_after>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.	If not, see <http://www.gnu.org/licenses/>.

erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
	init: function(wrapper, title) {
		this._super({
			title: title,
			page: wrapper,
			parent: $(wrapper).find('.layout-main'),
			appframe: wrapper.appframe,
			doctypes: ["Company", "Fiscal Year", "Account", "GL Entry", "Cost Center"],
			tree_grid: {
				show: true,
				parent_field: "parent_account",
				formatter: function(item) {
					return repl("\
						%(value)s", {
							value: item.name,
						});
				}
			},
		});
	},
	setup_columns: function() {
		this.columns = [
			{id: "name", name: __("Account"), field: "name", width: 300, cssClass: "cell-title",
				formatter: this.tree_formatter},
			{id: "opening_dr", name: __("Opening (Dr)"), field: "opening_dr", width: 100,
				formatter: this.currency_formatter},
			{id: "opening_cr", name: __("Opening (Cr)"), field: "opening_cr", width: 100,
				formatter: this.currency_formatter},
			{id: "debit", name: __("Debit"), field: "debit", width: 100,
				formatter: this.currency_formatter},
			{id: "credit", name: __("Credit"), field: "credit", width: 100,
				formatter: this.currency_formatter},
			{id: "closing_dr", name: __("Closing (Dr)"), field: "closing_dr", width: 100,
				formatter: this.currency_formatter},
			{id: "closing_cr", name: __("Closing (Cr)"), field: "closing_cr", width: 100,
				formatter: this.currency_formatter}
		];

	},
	filters: [
		{fieldtype: "Select", label: __("Company"), link:"Company", default_value: "Select Company...",
			filter: function(val, item, opts, me) {
				if (item.company == val || val == opts.default_value) {
					return me.apply_zero_filter(val, item, opts, me);
				}
				return false;
			}},
		{fieldtype: "Select", label: __("Fiscal Year"), link:"Fiscal Year",
			default_value: "Select Fiscal Year..."},
		{fieldtype: "Date", label: __("From Date")},
		{fieldtype: "Label", label: __("To")},
		{fieldtype: "Date", label: __("To Date")},
		{fieldtype: "Button", label: __("Refresh"), icon:"icon-refresh icon-white",
		 	cssClass:"btn-info"},
		{fieldtype: "Button", label: __("Reset Filters"), icon: "icon-filter"},
	],
	setup_filters: function() {
		this._super();
		var me = this;
		// default filters
		this.filter_inputs.fiscal_year.change(function() {
			var fy = $(this).val();
			$.each(frappe.report_dump.data["Fiscal Year"], function(i, v) {
				if (v.name==fy) {
					me.filter_inputs.from_date.val(dateutil.str_to_user(v.year_start_date));
					me.filter_inputs.to_date.val(dateutil.str_to_user(v.year_end_date));
				}
			});
			me.set_route();
		});
		me.show_zero_check()
		if(me.ignore_closing_entry) me.ignore_closing_entry();
	},
	prepare_data: function() {
		var me = this;
		if(!this.primary_data) {
			// make accounts list
			me.data = [];
			me.parent_map = {};
			me.item_by_name = {};

			$.each(frappe.report_dump.data["Account"], function(i, v) {
				var d = copy_dict(v);

				me.data.push(d);
				me.item_by_name[d.name] = d;
				if(d.parent_account) {
					me.parent_map[d.name] = d.parent_account;
				}
			});

			me.primary_data = [].concat(me.data);
		}

		me.data = [].concat(me.primary_data);
		$.each(me.data, function(i, d) {
			me.init_account(d);
		});

		this.set_indent();
		this.prepare_balances();

	},
	init_account: function(d) {
		this.reset_item_values(d);
	},

	prepare_balances: function() {
		var gl = frappe.report_dump.data['GL Entry'];
		var me = this;

		this.opening_date = dateutil.user_to_obj(this.filter_inputs.from_date.val());
		this.closing_date = dateutil.user_to_obj(this.filter_inputs.to_date.val());
		this.set_fiscal_year();
		if (!this.fiscal_year) return;

		$.each(this.data, function(i, v) {
			v.opening_dr = v.opening_cr = v.debit
				= v.credit = v.closing_dr = v.closing_cr = 0;
		});

		$.each(gl, function(i, v) {
			var posting_date = dateutil.str_to_obj(v.posting_date);
			var account = me.item_by_name[v.account];
			me.update_balances(account, posting_date, v);
		});

		this.update_groups();
	},
	update_balances: function(account, posting_date, v) {
		// opening
		if (posting_date < this.opening_date || v.is_opening === "Yes") {
			if (account.report_type === "Profit and Loss" &&
				posting_date <= dateutil.str_to_obj(this.fiscal_year[1])) {
				// balance of previous fiscal_year should
				//	not be part of opening of pl account balance
			} else {
				var opening_bal = flt(account.opening_dr) - flt(account.opening_cr) +
					flt(v.debit) - flt(v.credit);
				this.set_debit_or_credit(account, "opening", opening_bal);
			}
		} else if (this.opening_date <= posting_date && posting_date <= this.closing_date) {
			// in between
			account.debit += flt(v.debit);
			account.credit += flt(v.credit);
		}
		// closing
		var closing_bal = flt(account.opening_dr) - flt(account.opening_cr) +
			flt(account.debit) - flt(account.credit);
		this.set_debit_or_credit(account, "closing", closing_bal);
	},
	set_debit_or_credit: function(account, field, balance) {
		if(balance > 0) {
			account[field+"_dr"] = balance;
			account[field+"_cr"] = 0;
		} else {
			account[field+"_cr"] = Math.abs(balance);
			account[field+"_dr"] = 0;
		}
	},
	update_groups: function() {
		// update groups
		var me= this;
		$.each(this.data, function(i, account) {
			// update groups
			if((account.group_or_ledger == "Ledger") || (account.rgt - account.lft == 1)) {
				var parent = me.parent_map[account.name];
				while(parent) {
					var parent_account = me.item_by_name[parent];
					$.each(me.columns, function(c, col) {
						if (col.formatter == me.currency_formatter) {
							if(col.field=="opening_dr") {
								var bal = flt(parent_account.opening_dr) -
									flt(parent_account.opening_cr) +
									flt(account.opening_dr) - flt(account.opening_cr);
								me.set_debit_or_credit(parent_account, "opening", bal);
							} else if(col.field=="closing_dr") {
								var bal = flt(parent_account.closing_dr) -
									flt(parent_account.closing_cr) +
									flt(account.closing_dr) - flt(account.closing_cr);
								me.set_debit_or_credit(parent_account, "closing", bal);
							} else if(in_list(["debit", "credit"], col.field)) {
								parent_account[col.field] = flt(parent_account[col.field]) +
									flt(account[col.field]);
							}
						}
					});
					parent = me.parent_map[parent];
				}
			}
		});
	},

	set_fiscal_year: function() {
		if (this.opening_date > this.closing_date) {
			msgprint(__("Opening Date should be before Closing Date"));
			return;
		}

		this.fiscal_year = null;
		var me = this;
		$.each(frappe.report_dump.data["Fiscal Year"], function(i, v) {
			if (me.opening_date >= dateutil.str_to_obj(v.year_start_date) &&
				me.closing_date <= dateutil.str_to_obj(v.year_end_date)) {
					me.fiscal_year = v;
				}
		});

		if (!this.fiscal_year) {
			msgprint(__("Opening Date and Closing Date should be within same Fiscal Year"));
			return;
		}
	},

	show_general_ledger: function(account) {
		frappe.route_options = {
			account: account,
			company: this.company,
			from_date: this.from_date,
			to_date: this.to_date
		};
		frappe.set_route("query-report", "General Ledger");
	}
});
<|endoftext|>