<commit_before>'use strict';

var Garage = function (config) {
    var isGarageOpen = undefined,
        isInitial = true,
        garageOpened = undefined,
        statusPin = config.statusPin,
        onOpen = config.onOpen,
        onClose = config.onClose,
        onOpenReminder = config.onOpenReminder,
        reminderInterval = config.reminderInterval || 60000,
        reminded = undefined;

    function isOpen() {
        return isGarageOpen;
    }

    function opened() {
        return garageOpened;
    }

    function open() {
        //todo
    }

    function close() {
        //todo
    }

    function update(value) {
        //console.log('garage status: ' + value);
        isGarageOpen = !value;
        if (isGarageOpen) {
            if (!garageOpened) {
                //opened just now
                garageOpened = reminded = new Date();
                if (onOpen) {
                    onOpen(isInitial);
                }
                isInitial = false;
            }
            else if (onOpenReminder && (new Date() - reminded) > reminderInterval) {
                //already open
                onOpenReminder(garageOpened);
                reminded = new Date();
            }
        }
        else if (garageOpened) {
            //closed just now
            garageOpened = undefined;
            if (onClose) {
                onClose();
            }
        }
    }

    function start() {
        statusPin.poll(update);
    }

    function stop() {
        statusPin.poll(null);
    }

    //constructor
    (function () {
        start();
    })();

    return {
        open: open,
        close: close,
        isOpen: isOpen,
        opened: opened,
        start: start,
        stop: stop
    };
};

module.exports = Garage;<commit_msg>fixed initial garage status retrieval<commit_after>'use strict';

var Garage = function (config) {
    var isGarageOpen = undefined,
        isInitial = true,
        garageOpened = undefined,
        statusPin = config.statusPin,
        onOpen = config.onOpen,
        onClose = config.onClose,
        onOpenReminder = config.onOpenReminder,
        reminderInterval = config.reminderInterval || 60000,
        reminded = undefined;

    function isOpen() {
        return isGarageOpen;
    }

    function opened() {
        return garageOpened;
    }

    function open() {
        //todo
    }

    function close() {
        //todo
    }

    function update(value) {
        //console.log('garage status: ' + value);
        isGarageOpen = !value;
        if (isGarageOpen) {
            if (!garageOpened) {
                //opened just now
                garageOpened = reminded = new Date();
                if (onOpen) {
                    onOpen(isInitial);
                }
                isInitial = false;
            }
            else if (onOpenReminder && (new Date() - reminded) > reminderInterval) {
                //already open
                onOpenReminder(garageOpened);
                reminded = new Date();
            }
        }
        else if (garageOpened) {
            //closed just now
            garageOpened = undefined;
            if (onClose) {
                onClose();
            }
        }
    }

    function start() {
        isInitial = true;
        garageOpened = reminded = undefined;
        update(statusPin.read());
        statusPin.poll(update);
    }

    function stop() {
        statusPin.poll(null);
    }

    //constructor
    (function () {
        start();
    })();

    return {
        open: open,
        close: close,
        isOpen: isOpen,
        opened: opened,
        start: start,
        stop: stop
    };
};

module.exports = Garage;<|endoftext|>


<commit_before>'use strict';

var CharactersService = function () {

    var CHARACTERS_COLLECTION_NAME = 'characters';

    var mongoDbConnection = require('./mongoConnection.js');

    var toCharacterPage = function (items, count, limit, skip) {
        return {
            'totalItems': count,
            'itemsPerPage': limit,
            'currentPage': (skip / limit) + 1,
            'items': items
        };
    };

    var _findOneRandomly = function (callback) {
        mongoDbConnection(function (connection) {
            var collection = connection.collection(CHARACTERS_COLLECTION_NAME);
            // 1- Count characters
            collection.count(function (err, count) {
                if (err) throw new Error(err);
                // 2- Find one character randomly
                var random = Math.floor((Math.random() * count));
                collection.find().skip(random).limit(1).toArray(function (err, items) {
                    if (err) throw new Error(err);
                    callback(items[0]);
                });
            });

        });
    };

    var _findById = function (id, callback) {
        mongoDbConnection(function (connection) {
            connection.collection(CHARACTERS_COLLECTION_NAME).findOne({'_id': id}, function (err, item) {
                if (err) throw new Error(err);
                callback(item);
            });
        });
    };

    var _findByName = function (name, limit, skip, callback) {
        mongoDbConnection(function (connection) {
            var collection = connection.collection(CHARACTERS_COLLECTION_NAME);
            var searchQuery = name ? {"name": new RegExp(name, "i")} : {};
            // 1- Count characters
            collection.count(searchQuery, function (err, count) {
                if (err) throw new Error(err);
                // 2- Search and paginate characters
                collection.find(searchQuery).limit(limit).skip(skip).toArray(function (err, items) {
                    if (err) throw new Error(err);
                    callback(toCharacterPage(items, count, limit, skip));
                });
            });
        });
    };

    var _findAll = function (limit, skip, callback) {
        mongoDbConnection(function (connection) {
            var collection = connection.collection(CHARACTERS_COLLECTION_NAME);
            // 1- Count characters
            collection.count(function (err, count) {
                if (err) throw new Error(err);
                // 2- Paginate characters
                collection.find().limit(limit).skip(skip).toArray(function (err, items) {
                    if (err) throw new Error(err);
                    callback(toCharacterPage(items, count, limit, skip));
                });
            });
        });
    };

    var _putStory = function (id, story, callback) {
        mongoDbConnection(function (connection) {
            var collection = connection.collection(CHARACTERS_COLLECTION_NAME);
            collection.update({ '_id': id }, { '$set': { 'story': story } }, function (err, result) {
                if (err) throw new Error(err);
                callback(result);
            });
        });
    };

    var _create = function (character, callback) {
        mongoDbConnection(function (connection) {
            var collection = connection.collection(CHARACTERS_COLLECTION_NAME);
            character._id = Number(character._id);
            collection.insert(character, function (err, result) {
                if (err) throw new Error(err);
                callback(result);
            });
        });
    }

    var _delete = function (id, callback) {
        mongoDbConnection(function (connection) {
            var collection = connection.collection(CHARACTERS_COLLECTION_NAME);
            collection.remove({ '_id': id }, function (err, result) {
                if (err) throw new Error(err);
                callback(result);
            });
        });
    }

    return {
        findOneRandomly: _findOneRandomly,
        findById: _findById,
        findByName: _findByName,
        findAll: _findAll,
        putStory: _putStory,
        create: _create,
        delete: _delete
    };
};

module.exports = CharactersService;
<commit_msg>Delete code for workshop<commit_after>'use strict';

var CharactersService = function () {

    var CHARACTERS_COLLECTION_NAME = 'characters';

    var mongoDbConnection = require('./mongoConnection.js');

    var toCharacterPage = function (items, count, limit, skip) {
        return {
            'totalItems': count,
            'itemsPerPage': limit,
            'currentPage': (skip / limit) + 1,
            'items': items
        };
    };

    var _findOneRandomly = function (callback) {
        mongoDbConnection(function (connection) {
            // TODO write some code ...
        });
    };

    var _findById = function (id, callback) {
        mongoDbConnection(function (connection) {
            // TODO write some code ...
        });
    };

    var _findByName = function (name, limit, skip, callback) {
        mongoDbConnection(function (connection) {
            // TODO write some code ...
            // Regexp example : var searchQuery = name ? {"name": new RegExp(name, "i")} : {};
            // Must return a page of characters
        });
    };

    var _findAll = function (limit, skip, callback) {
        mongoDbConnection(function (connection) {
            // TODO write some code ...
            // Must return a page of characters
        });
    };

    var _putStory = function (id, story, callback) {
        mongoDbConnection(function (connection) {
            // TODO write some code ...
        });
    };

    var _create = function (character, callback) {
        mongoDbConnection(function (connection) {
            var collection = connection.collection(CHARACTERS_COLLECTION_NAME);
            character._id = Number(character._id);
            // TODO write some code ...
        });
    }

    var _delete = function (id, callback) {
        mongoDbConnection(function (connection) {
            var collection = connection.collection(CHARACTERS_COLLECTION_NAME);
            collection.remove({ '_id': id }, function (err, result) {
                if (err) throw new Error(err);
                callback(result);
            });
        });
    }

    return {
        findOneRandomly: _findOneRandomly,
        findById: _findById,
        findByName: _findByName,
        findAll: _findAll,
        putStory: _putStory,
        create: _create,
        delete: _delete
    };
};

module.exports = CharactersService;
<|endoftext|>


<commit_before>// BEGIN Math.uuid.js

/*!
Math.uuid.js (v1.4)
http://www.broofa.com
mailto:robert@broofa.com

Copyright (c) 2010 Robert Kieffer
Dual licensed under the MIT and GPL licenses.
*/

/*
 * Generate a random uuid.
 *
 * USAGE: Math.uuid(length, radix)
 *   length - the desired number of characters
 *   radix  - the number of allowable values for each character.
 *
 * EXAMPLES:
 *   // No arguments  - returns RFC4122, version 4 ID
 *   >>> Math.uuid()
 *   "92329D39-6F5C-4520-ABFC-AAB64544E172"
 *
 *   // One argument - returns ID of the specified length
 *   >>> Math.uuid(15)     // 15 character ID (default base=62)
 *   "VcydxgltxrVZSTV"
 *
 *   // Two arguments - returns ID of the specified length, and radix. (Radix must be <= 62)
 *   >>> Math.uuid(8, 2)  // 8 character ID (base=2)
 *   "01001010"
 *   >>> Math.uuid(8, 10) // 8 character ID (base=10)
 *   "47473046"
 *   >>> Math.uuid(8, 16) // 8 character ID (base=16)
 *   "098F4D35"
 */
(function() {
  // Private array of chars to use
  var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');

  Math.uuid = function (len, radix) {
    var chars = CHARS, uuid = [];
    radix = radix || chars.length;

    if (len) {
      // Compact form
      for (var i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
    } else {
      // rfc4122, version 4 form
      var r;

      // rfc4122 requires these characters
      uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
      uuid[14] = '4';

      // Fill in random data.  At i==19 set the high bits of clock sequence as
      // per rfc4122, sec. 4.1.5
      for (var i = 0; i < 36; i++) {
        if (!uuid[i]) {
          r = 0 | Math.random()*16;
          uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
        }
      }
    }

    return uuid.join('');
  };

  // A more performant, but slightly bulkier, RFC4122v4 solution.  We boost performance
  // by minimizing calls to random()
  Math.uuidFast = function() {
    var chars = CHARS, uuid = new Array(36), rnd=0, r;
    for (var i = 0; i < 36; i++) {
      if (i==8 || i==13 ||  i==18 || i==23) {
        uuid[i] = '-';
      } else if (i==14) {
        uuid[i] = '4';
      } else {
        if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
        r = rnd & 0xf;
        rnd = rnd >> 4;
        uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
      }
    }
    return uuid.join('');
  };

  // A more compact, but less performant, RFC4122v4 solution:
  Math.uuidCompact = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
      return v.toString(16);
    }).toUpperCase();
  };
})();

// END Math.uuid.js

function getObjectStore (db, name, keypath, callback, errBack) {
  if (db.objectStoreNames.contains(name)) {
    callback(db.objectStore(name));
  } else {
    var request = db.createObjectStore(name, keypath);
    request.onsuccess = function (e) {
      callback(e.result)
    }
    request.onerror = function (err) {
      if (errBack) errBack(err);
    }
  }
}

function getNewSequence (transaction, couch, callback) {
  var range = moz_indexedDB.makeRightBoundKeyRange(couch.seq);
  request = transaction.objectStore("sequence-index").openCursor(range);
  var seq = couch.seq;
  request.onsuccess = function (e) {
    var cursor = e.result;
    if (!cursor) {
      callback(seq + 1);
      return;
    }
    cursor.continue();
  }
  request.onerror = function () {
    // Sequence index is empty.
    callback(1);
  }
}

var MAXINT = 999999999999999999999999999999999999;

function createCouch (options, cb) {
  if (cb) options.success = cb;
  if (!options.name) throw "name attribute is required"
  var request = moz_indexedDB.open(options.name, options.description ? options.description : "a couchdb");
  // Failure handler on getting Database
  request.onerror = function(error) {
    if (options.error) {
      if (error) options.error(error)
      else options.error("Failed to open database.")
    }
  }

  request.onsuccess = function(event) {
    var db = event.result;
    getObjectStore(db, 'document-store', '_id', function (documentStore) {
      getObjectStore(db, 'sequence-index', 'seq', function (sequenceIndex) {

        // Now we create the actual CouchDB
        var couch = {
          get: function (_id, options) {

          }
          , post: function (doc, options) {
            if (!doc._id) doc._id = Math.uuid();
            if (couch.docToSeq[doc._id]) {
              if (!doc._rev) {
                options.error({code:413, message:"Update conflict, no revision information"});
              }
              var transaction = db.transaction(["document-store", "sequence-index"],
                                               Components.interfaces.nsIIDBTransaction.READ_WRITE);
              request = transaction.objectStore("document-store")
                .openCursor(moz_indexedDB.makeSingleKeyRange(doc._id));
              request.onsuccess = function (event) {
                var prevDocCursor = event.result;
                var prev = event.result.value;
                if (prev._rev !== doc._rev) {
                  options.error("Conflict error, revision does not match.")
                  return;
                }
                getNewSequence(transaction, couch, function (seq) {
                  var rev = Math.uuid();
                  request = transaction.objectStore("sequence-index")
                    .openCursor(moz_indexedDB.makeSingleKeyRange(couch.docToSeq[doc._id]));
                  request.onsuccess = function (event) {
                    var oldSequence = event.result.value;
                    if (oldSequence.changes) {
                      oldSequence.changes[event.result.key] = prev
                    } else {
                      oldSequence.changes = {};
                      oldSequence.changes[event.result.key] = prev;
                    }
                    // transaction = db.transaction(["document-store", "sequence-index"],
                    //                                  Components.interfaces.nsIIDBTransaction.READ_WRITE);
                    transaction.objectStore("sequence-index").add({seq:seq, id:doc._id, rev:rev});
                    event.result.remove();
                    doc._rev = rev;
                    prevDocCursor.update(doc);
                    transaction.oncomplete = function () {
                      couch.docToSeq[doc._id] = seq;
                      couch.seq = seq;
                      if (options.success) options.success({id:doc._id, rev:doc._rev, seq:seq});
                    }
                  }
                  request.onerror = function (err) {
                    if (options.error) options.error("Could not open sequence index")
                  }
                })
              }
            } else {
              var transaction = db.transaction(["document-store", "sequence-index"],
                                               Components.interfaces.nsIIDBTransaction.READ_WRITE);
              getNewSequence(transaction, couch, function (seq) {
                doc._rev = Math.uuid();
                transaction.objectStore("sequence-index").add({seq:seq, id:doc._id, rev:doc._rev});
                transaction.objectStore("document-store").add(doc);
                transaction.oncomplete = function () {
                  couch.docToSeq[doc._id] = seq;
                  couch.seq = seq;
                  if (options.success) options.success({id:doc._id, rev:doc._rev, seq:seq});
                }
              })
            }
          }
          , docToSeq : {}
        }

        var request = sequenceIndex.openCursor();
        var seq;
        request.onsuccess = function (e) {
          // Handle iterating on the sequence index to create the reverse map and validate last-seq
          var cursor = e.result;
          if (!cursor) {
            couch.seq = seq;
            options.success(couch)
            return;
          }
          seq = cursor.key
          couch.docToSeq[cursor.value['id']] = seq;
          cursor.continue();
        }
        request.onerror = function (event) {
          // Assume the database is just empty because the error code is broken
          couch.seq = 0;
          options.success(couch);
        }

      }, function () {if (options.error) {options.error('Could not open sequence index.')}})
    }, function () {if (options.error) {options.error('Could not open document store.')}})
  }
}

function removeCouch (options) {
  var request = moz_indexedDB.open(options.name, options.description ? options.description : "a couchdb");
  request.onsuccess = function (event) {
    var db = event.result;
    var successes = 0;
    for (var i=0;i<db.objectStoreNames.length;i+=1) {
      var r = db.removeObjectStore(db.objectStoreNames[i]);
      r.onsuccess = function (event) {
        successes += 1;
        if (successes === db.objectStoreNames.length) options.success();
      }
      r.onerror = function () { options.error("Failed to remove "+db.objectStoreNames[i]); }
    }
  }
  request.onerror = function () {
    options.error("No such database "+options.name);
  }
}
<commit_msg>Adding one more error check.<commit_after>// BEGIN Math.uuid.js

/*!
Math.uuid.js (v1.4)
http://www.broofa.com
mailto:robert@broofa.com

Copyright (c) 2010 Robert Kieffer
Dual licensed under the MIT and GPL licenses.
*/

/*
 * Generate a random uuid.
 *
 * USAGE: Math.uuid(length, radix)
 *   length - the desired number of characters
 *   radix  - the number of allowable values for each character.
 *
 * EXAMPLES:
 *   // No arguments  - returns RFC4122, version 4 ID
 *   >>> Math.uuid()
 *   "92329D39-6F5C-4520-ABFC-AAB64544E172"
 *
 *   // One argument - returns ID of the specified length
 *   >>> Math.uuid(15)     // 15 character ID (default base=62)
 *   "VcydxgltxrVZSTV"
 *
 *   // Two arguments - returns ID of the specified length, and radix. (Radix must be <= 62)
 *   >>> Math.uuid(8, 2)  // 8 character ID (base=2)
 *   "01001010"
 *   >>> Math.uuid(8, 10) // 8 character ID (base=10)
 *   "47473046"
 *   >>> Math.uuid(8, 16) // 8 character ID (base=16)
 *   "098F4D35"
 */
(function() {
  // Private array of chars to use
  var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');

  Math.uuid = function (len, radix) {
    var chars = CHARS, uuid = [];
    radix = radix || chars.length;

    if (len) {
      // Compact form
      for (var i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
    } else {
      // rfc4122, version 4 form
      var r;

      // rfc4122 requires these characters
      uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
      uuid[14] = '4';

      // Fill in random data.  At i==19 set the high bits of clock sequence as
      // per rfc4122, sec. 4.1.5
      for (var i = 0; i < 36; i++) {
        if (!uuid[i]) {
          r = 0 | Math.random()*16;
          uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
        }
      }
    }

    return uuid.join('');
  };

  // A more performant, but slightly bulkier, RFC4122v4 solution.  We boost performance
  // by minimizing calls to random()
  Math.uuidFast = function() {
    var chars = CHARS, uuid = new Array(36), rnd=0, r;
    for (var i = 0; i < 36; i++) {
      if (i==8 || i==13 ||  i==18 || i==23) {
        uuid[i] = '-';
      } else if (i==14) {
        uuid[i] = '4';
      } else {
        if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
        r = rnd & 0xf;
        rnd = rnd >> 4;
        uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
      }
    }
    return uuid.join('');
  };

  // A more compact, but less performant, RFC4122v4 solution:
  Math.uuidCompact = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
      return v.toString(16);
    }).toUpperCase();
  };
})();

// END Math.uuid.js

function getObjectStore (db, name, keypath, callback, errBack) {
  if (db.objectStoreNames.contains(name)) {
    callback(db.objectStore(name));
  } else {
    var request = db.createObjectStore(name, keypath);
    request.onsuccess = function (e) {
      callback(e.result)
    }
    request.onerror = function (err) {
      if (errBack) errBack(err);
    }
  }
}

function getNewSequence (transaction, couch, callback) {
  var range = moz_indexedDB.makeRightBoundKeyRange(couch.seq);
  request = transaction.objectStore("sequence-index").openCursor(range);
  var seq = couch.seq;
  request.onsuccess = function (e) {
    var cursor = e.result;
    if (!cursor) {
      callback(seq + 1);
      return;
    }
    cursor.continue();
  }
  request.onerror = function () {
    // Sequence index is empty.
    callback(1);
  }
}

var MAXINT = 999999999999999999999999999999999999;

function createCouch (options, cb) {
  if (cb) options.success = cb;
  if (!options.name) throw "name attribute is required"
  var request = moz_indexedDB.open(options.name, options.description ? options.description : "a couchdb");
  // Failure handler on getting Database
  request.onerror = function(error) {
    if (options.error) {
      if (error) options.error(error)
      else options.error("Failed to open database.")
    }
  }

  request.onsuccess = function(event) {
    var db = event.result;
    getObjectStore(db, 'document-store', '_id', function (documentStore) {
      getObjectStore(db, 'sequence-index', 'seq', function (sequenceIndex) {

        // Now we create the actual CouchDB
        var couch = {
          get: function (_id, options) {

          }
          , post: function (doc, options) {
            if (!doc._id) doc._id = Math.uuid();
            if (couch.docToSeq[doc._id]) {
              if (!doc._rev) {
                options.error({code:413, message:"Update conflict, no revision information"});
              }
              var transaction = db.transaction(["document-store", "sequence-index"],
                                               Components.interfaces.nsIIDBTransaction.READ_WRITE);
              request = transaction.objectStore("document-store")
                .openCursor(moz_indexedDB.makeSingleKeyRange(doc._id));
              request.onsuccess = function (event) {
                var prevDocCursor = event.result;
                var prev = event.result.value;
                if (prev._rev !== doc._rev) {
                  options.error("Conflict error, revision does not match.")
                  return;
                }
                getNewSequence(transaction, couch, function (seq) {
                  var rev = Math.uuid();
                  request = transaction.objectStore("sequence-index")
                    .openCursor(moz_indexedDB.makeSingleKeyRange(couch.docToSeq[doc._id]));
                  request.onsuccess = function (event) {
                    var oldSequence = event.result.value;
                    if (oldSequence.changes) {
                      oldSequence.changes[event.result.key] = prev
                    } else {
                      oldSequence.changes = {};
                      oldSequence.changes[event.result.key] = prev;
                    }
                    // transaction = db.transaction(["document-store", "sequence-index"],
                    //                                  Components.interfaces.nsIIDBTransaction.READ_WRITE);
                    transaction.objectStore("sequence-index").add({seq:seq, id:doc._id, rev:rev});
                    event.result.remove();
                    doc._rev = rev;
                    prevDocCursor.update(doc);
                    transaction.oncomplete = function () {
                      couch.docToSeq[doc._id] = seq;
                      couch.seq = seq;
                      if (options.success) options.success({id:doc._id, rev:doc._rev, seq:seq});
                    }
                  }
                  request.onerror = function (err) {
                    if (options.error) options.error("Could not open sequence index")
                  }
                })
              }
              request.onerror = function (err) {
                if (options.error) options.error("Could not find document in object store.")
              }
            } else {
              var transaction = db.transaction(["document-store", "sequence-index"],
                                               Components.interfaces.nsIIDBTransaction.READ_WRITE);
              getNewSequence(transaction, couch, function (seq) {
                doc._rev = Math.uuid();
                transaction.objectStore("sequence-index").add({seq:seq, id:doc._id, rev:doc._rev});
                transaction.objectStore("document-store").add(doc);
                transaction.oncomplete = function () {
                  couch.docToSeq[doc._id] = seq;
                  couch.seq = seq;
                  if (options.success) options.success({id:doc._id, rev:doc._rev, seq:seq});
                }
              })
            }
          }
          , docToSeq : {}
        }

        var request = sequenceIndex.openCursor();
        var seq;
        request.onsuccess = function (e) {
          // Handle iterating on the sequence index to create the reverse map and validate last-seq
          var cursor = e.result;
          if (!cursor) {
            couch.seq = seq;
            options.success(couch)
            return;
          }
          seq = cursor.key
          couch.docToSeq[cursor.value['id']] = seq;
          cursor.continue();
        }
        request.onerror = function (event) {
          // Assume the database is just empty because the error code is broken
          couch.seq = 0;
          options.success(couch);
        }

      }, function () {if (options.error) {options.error('Could not open sequence index.')}})
    }, function () {if (options.error) {options.error('Could not open document store.')}})
  }
}

function removeCouch (options) {
  var request = moz_indexedDB.open(options.name, options.description ? options.description : "a couchdb");
  request.onsuccess = function (event) {
    var db = event.result;
    var successes = 0;
    for (var i=0;i<db.objectStoreNames.length;i+=1) {
      var r = db.removeObjectStore(db.objectStoreNames[i]);
      r.onsuccess = function (event) {
        successes += 1;
        if (successes === db.objectStoreNames.length) options.success();
      }
      r.onerror = function () { options.error("Failed to remove "+db.objectStoreNames[i]); }
    }
  }
  request.onerror = function () {
    options.error("No such database "+options.name);
  }
}
<|endoftext|>


<commit_before>/**
 *  * Created by Reza Afzalan.
 e   */
'use strict';
var exec = require('child_process').exec;
function execute(command, callback) {
    exec(command, function (error, stdout) {callback(stdout); });
}
module.exports = function (app, dbma) {
    app.post('/mali/sendstatement', function (req, res) {
        if (((req.connection.remoteAddress === '172.18.1.10') || (req.connection.remoteAddress === '172.18.1.234')) && (req.body.pass === '123')) {
            var callback = function (err) {
                if (err) {
                    console.log('insert statements error=', err);
                }
            };
            var callbackdelete = function (err) {
                if (err) {
                    console.log('delete statements error=', err);
                }
            };
            dbma.serialize(function () {
                for (var id in req.body.datas) {
                    var d = req.body.datas[id];
                    if (req.body.overwrite === 'True') {
                        dbma.run('DELETE FROM statements WHERE (pid=? AND date=?)', [d[0], d[1]], callbackdelete);
                    }
                    dbma.run('INSERT INTO statements (pid,date,data) VALUES (?,?,?)', [d[0], d[1], JSON.stringify(d)], callback);
                }
            });
            res.sendStatus(200);
        } else {
            res.sendStatus(400);
            console.log(req.connection.remoteAddress);
        }
    });
    app.post('/mali/show', function (req, res) {
        var callback = function () {
            //serilize
            //createStatementTex(row);
            execute('xelatex -interaction=batchmode -output-directory=../xelatex  ../xelatex/statement.tex',
                function () {
                    res.sendFile('/home/rfpc/modproject/xelatex/statement.pdf');
                }
            );
        };
        dbma.get('SELECT data FROM statements WHERE pid=? AND date=?', [req.user.pid, req.body.date], callback);
    });
};
<commit_msg>insert satment<commit_after>/**
 *  * Created by Reza Afzalan.
 e   */
'use strict';
var exec = require('child_process').exec;
function execute(command, callback) {
    exec(command, function (error, stdout) {callback(stdout); });
}
module.exports = function (app, dbma) {
    app.post('/mali/sendstatement', function (req, res) {
        if (((req.connection.remoteAddress === '172.18.1.10') || (req.connection.remoteAddress === '172.18.1.234')) && (req.body.pass === '123')) {
            var callback = function (err) {
                if (err) {
                    console.log('insert statements error=', err);
                }
            };
            var callbackdelete = function (err) {
                if (err) {
                    console.log('delete statements error=', err);
                }
            };
            dbma.serialize(function () {
                for (var id in req.body.datas) {
                    var d = req.body.datas[id];
                    if (req.body.overwrite === 'True') {
                        dbma.run('DELETE FROM statements WHERE (pid=? AND date=?)', [d[0], req.body.date], callbackdelete);
                    }
                    dbma.run('INSERT INTO statements (pid,date,data) VALUES (?,?,?)', [d[0], req.body.date, JSON.stringify(d)], callback);
                }
            });
            res.sendStatus(200);
        } else {
            res.sendStatus(400);
            console.log(req.connection.remoteAddress);
        }
    });
    app.post('/mali/show', function (req, res) {
        var callback = function (err, row) {
            //serilize
            //createStatementTex(row);
            var stmdata = JSON.parse(row.data);
            var texcommand = '';
            var j = 97;
            var achar = 'y';
            for (var i in stmdata) {
                texcommand = texcommand + '\\def\\' + achar + String.fromCharCode(j) + '{' + stmdata[i]  + '}\n';
                j += 1;
                if (j === 123) {
                    j = 97;
                    achar = 'z';
                }
            }
            console.log(texcommand);
            execute('xelatex -interaction=batchmode -output-directory=../xelatex  ../xelatex/statement.tex',
                function () {
                    res.sendFile('/home/rfpc/modproject/xelatex/statement.pdf');
                }
            );
        };
        dbma.get('SELECT data FROM statements WHERE pid=? AND date=?', [req.user.pid, req.body.date], callback);
    });

    app.get('/mali/list', function (req, res) {
        var callback = function (err, rows) {
            if (!err) {
                res.json(rows);
            } else {
                console.log('Error get list of dates for pid=' + req.user.pid);
                res.sendStatus(400);
            }
        };
        dbma.all('SELECT date FROM statements WHERE pid=?', [req.user.pid], callback);
    });
};
<|endoftext|>


<commit_before><commit_msg>initTemplateEscape<commit_after><|endoftext|>


<commit_before>const db = require('./models')

module.exports = {

  /** *inner类***房屋管理-增删改查 * */

  /*
    房屋对象挂载
    用户ID ok
    水费抄表ID ok
    水费计费ID ok
    电费抄表ID ok
    电费计费ID ok
    租户ID ok
    收租ID ok
  */

  /** *inner类****水费管理 * */

  /*
    水费抄表对象挂载
    用户ID ok
    房屋ID ok
    水费计费对象挂载
    用户ID ok
    房屋ID ok
  */

  /** *inner类****电费管理 * */

  /*
    电费抄表对象挂载
    用户ID ok
    房屋ID ok
    电费计费对象挂载
    用户ID ok
    房屋ID ok
  */

  /** *inner类****租住管理 * */

  /*
    租户对象挂载
    用户ID ok
    房屋ID ok
  */

  /** *inner类****收租管理 * */

  /*
    收租周期表对象挂载
    用户ID ok
    收租对象挂载
    用户ID ok
    收租周期表ID ok
    房屋ID ok
  */

  // 收租对象 ，存储整个水表计费信息，电表计费信息，租户信息
  //
  //
  // 收租周期表add OK
  // 根据收租周期表list，进列表 OK
  // 根据房屋，带挂载收租信息list，进历史 OK
  //
  //
  // 收租add，更新房屋挂载ID OK
  // 收租type多选：交租、给单据、给房东 OK
  // 收租del，需要回退房屋挂载ID OK
  // 根据收租周期表收租列表list，可添加，可修改收租状态，最新一期可删除收租 OK
  // 根据房屋收租历史list OK

  /** *inner类****其他 * */

  getData: (req, res, callback) => {
    db
      .dbModel('admin')
      .find({ name: req.body.name })
      .exec((err, data) => {
        if (err) {
          callback({
            type: false,
            data: err,
          })
        } else {
          callback({
            type: true,
            data,
          })
        }
      })
  },
  saveData: (req, res, callback) => {
    db
      .dbModel('admin', { name: 'string' })
      .create({ name: req.body.name }, (err, data) => {
        if (err) {
          callback({
            type: false,
            data: err,
          })
        } else {
          callback({
            type: true,
            data,
          })
        }
      })
  },
  removeData: (req, res, callback) => {
    if (!req.body.name) {
      return callback({
        type: false,
        data: '未定义删除关键字',
      })
    }
    return db
      .dbModel('admin')
      .remove({}, (err, data) => {
        if (err) {
          callback({
            type: false,
            data: err,
          })
        } else {
          callback({
            type: true,
            data,
          })
        }
      })
  },
  redisSet: (req, res, callback) => {
    db.redisSet(req.body.setname, req.body.setval, req.body.settime || 0, (err, reply) => {
      if (err) {
        callback({
          type: false,
          data: err,
        })
      } else {
        callback({
          type: true,
          data: reply,
        })
      }
    })
  },
  redisGet: (req, res, callback) => {
    db.redisGet(req.body.setname, (err, reply) => {
      if (err) {
        callback({
          type: false,
          data: err,
        })
      } else {
        callback({
          type: true,
          data: reply,
        })
      }
    })
  },
}
<commit_msg>hotfix-rebuild<commit_after>const db = require('./models')

module.exports = {

  /** *inner类***房屋管理-增删改查 * */

  /*
    房屋对象挂载
    用户ID ok
    水费抄表ID ok
    水费计费ID ok
    电费抄表ID ok
    电费计费ID ok
    租户ID ok
    收租ID ok
  */

  /** *inner类****水费管理 * */

  /*
    水费抄表对象挂载
    用户ID ok
    房屋ID ok
    水费计费对象挂载
    用户ID ok
    房屋ID ok
  */

  /** *inner类****电费管理 * */

  /*
    电费抄表对象挂载
    用户ID ok
    房屋ID ok
    电费计费对象挂载
    用户ID ok
    房屋ID ok
  */

  /** *inner类****租住管理 * */

  /*
    租户对象挂载
    用户ID ok
    房屋ID ok
  */

  /** *inner类****收租管理 * */

  /*
    收租周期表对象挂载
    用户ID ok
    收租对象挂载
    用户ID ok
    收租周期表ID ok
    房屋ID ok
  */

  // 收租对象 ，存储整个水表计费信息，电表计费信息，租户信息
  //
  //
  // 收租周期表add OK
  // 根据收租周期表list，进列表 OK
  // 根据房屋，带挂载收租信息list，进历史 OK
  //
  //
  // 收租add，更新房屋挂载ID OK
  // 收租type多选：交租、给单据、给房东 OK
  // 收租del，需要回退房屋挂载ID OK
  // 根据收租周期表收租列表list，可添加，可修改收租状态，最新一期可删除收租 OK
  // 根据房屋收租历史list OK

  /** *inner类****其他 * */

  getData: (req, res, callback) => {
    db
      .dbModel('admin')
      .find({ name: req.body.name })
      .exec((err, data) => {
        if (err) {
          callback({
            type: false,
            data: err,
          })
        } else {
          callback({
            type: true,
            data,
          })
        }
      })
  },

  saveData: (req, res, callback) => {
    db
      .dbModel('admin', { name: 'string' })
      .create({ name: req.body.name }, (err, data) => {
        if (err) {
          callback({
            type: false,
            data: err,
          })
        } else {
          callback({
            type: true,
            data,
          })
        }
      })
  },

  removeData: (req, res, callback) => {
    if (!req.body.name) {
      return callback({
        type: false,
        data: '未定义删除关键字',
      })
    }
    return db
      .dbModel('admin')
      .remove({}, (err, data) => {
        if (err) {
          callback({
            type: false,
            data: err,
          })
        } else {
          callback({
            type: true,
            data,
          })
        }
      })
  },

  redisSet: (req, res, callback) => {
    db.redisSet(req.body.setname, req.body.setval, req.body.settime || 0, (err, reply) => {
      if (err) {
        callback({
          type: false,
          data: err,
        })
      } else {
        callback({
          type: true,
          data: reply,
        })
      }
    })
  },

  redisGet: (req, res, callback) => {
    db.redisGet(req.body.setname, (err, reply) => {
      if (err) {
        callback({
          type: false,
          data: err,
        })
      } else {
        callback({
          type: true,
          data: reply,
        })
      }
    })
  },
}
<|endoftext|>


<commit_before>// @flow

import { useState } from 'react';
import Select from 'react-select';
import Button from 'app/components/Button';
import Flex from 'app/components/Layout/Flex';
import { ConfirmModalWithParent } from 'app/components/Modal/ConfirmModal';
import type { PhotoConsent } from 'app/models';
import moment from 'moment-timezone';
import {
  getConsent,
  PHOTO_CONSENT_DOMAINS,
  toReadableSemester,
} from 'app/routes/events/utils';
import styles from './PhotoConsents.css';

const ConsentManager = ({
  consent,
  updateConsent,
  isMe,
}: {
  consent: ?PhotoConsent,
  updateConsent: (consent: PhotoConsent) => Promise<*>,
  isMe: boolean,
}) => {
  if (!consent) {
    return null;
  }

  const consentStatus =
    typeof consent.isConsenting === 'boolean'
      ? consent.isConsenting
        ? 'Du ga samtykket den '
        : 'Du trakk samtykket den '
      : 'Du har ikke tatt stilling til samtykket.';

  const presentableDomain =
    consent.domain === PHOTO_CONSENT_DOMAINS.WEBSITE
      ? 'Abakus.no'
      : 'Sosiale medier';
  return (
    <>

{presentableDomain}

        Jeg godtar at Abakus kan legge ut bilder av meg på {presentableDomain} i
        perioden {toReadableSemester(consent)}:

        {consentStatus}
        {typeof consent.isConsenting === 'boolean' ? (
          {moment(consent.updatedAt).format('DD. MMM YYYY')}.
        ) : (
          ''
        )}

        <ConfirmModalWithParent
          closeOnConfirm={true}
          title={`Trekke bildesamtykke på ${presentableDomain}`}
          message={`Er du sikker på at du vil trekke bildesamtykket ditt for ${toReadableSemester(
            consent
          )} på ${presentableDomain}? Dersom du ønsker å fjerne noen spesifikke bilder, kan du i stedet sende en mail til pr@abakus.no med informasjon om hvilke bilder du vil fjerne.`}
          onConfirm={() => updateConsent({ ...consent, isConsenting: false })}
        >
          <Button
            dark
            disabled={!isMe || consent.isConsenting === false}
            className={styles.consentBtn}
          >
            Trekk samtykke
          </Button>
        </ConfirmModalWithParent>
        <Button
          success
          disabled={!isMe || consent.isConsenting === true}
          onClick={() => updateConsent({ ...consent, isConsenting: true })}
          className={styles.consentBtn}
        >
          Behold samtykke
        </Button>

    </>
  );
};

const PhotoConsents = ({
  photoConsents,
  username,
  updatePhotoConsent,
  userId,
  isMe,
}: {
  photoConsents: Array<PhotoConsent>,
  username: string,
  updatePhotoConsent: (
    photoConsent: PhotoConsent,
    username: string,
    userId: Number
  ) => Promise<*>,
  userId: Number,
  isMe: boolean,
}) => {
  const semesterOptions = photoConsents
    .slice(0)
    .filter(
      (photoConsent) => photoConsent.domain === PHOTO_CONSENT_DOMAINS.WEBSITE
    )
    .sort((a, b) => {
      if (a.year === b.year) {
        return a.semester === 'spring' ? 1 : -1;
      }

      return a.year > b.year ? -1 : 1;
    })
    .map((photoConsent) => ({
      label: toReadableSemester(photoConsent),
      value: { year: photoConsent.year, semester: photoConsent.semester },
    }));

  const [selectedSemesterOption, setSelectedSemesterOption] = useState(
    semesterOptions[0]
  );

  const updateConsent = (consent: PhotoConsent) =>
    updatePhotoConsent(consent, username, userId);
  return (
    <Flex column={true}>

Semester

      <Select
        name="select-semester"
        clearable={false}
        options={semesterOptions}
        value={selectedSemesterOption}
        onChange={({ value }) =>
          setSelectedSemesterOption({ label: toReadableSemester(value), value })
        }
      />
      <ConsentManager
        consent={getConsent(
          PHOTO_CONSENT_DOMAINS.SOCIAL_MEDIA,
          selectedSemesterOption.value.year,
          selectedSemesterOption.value.semester,
          photoConsents
        )}
        updateConsent={updateConsent}
        isMe={isMe}
      />
      <ConsentManager
        consent={getConsent(
          PHOTO_CONSENT_DOMAINS.WEBSITE,
          selectedSemesterOption.value.year,
          selectedSemesterOption.value.semester,
          photoConsents
        )}
        updateConsent={updateConsent}
        isMe={isMe}
      />
    </Flex>
  );
};

export default PhotoConsents;
<commit_msg>fix(ConsentManager): apply default styling for select element<commit_after>// @flow

import { useState } from 'react';
import Select from 'react-select';
import Button from 'app/components/Button';
import Flex from 'app/components/Layout/Flex';
import { ConfirmModalWithParent } from 'app/components/Modal/ConfirmModal';
import type { PhotoConsent } from 'app/models';
import moment from 'moment-timezone';
import {
  getConsent,
  PHOTO_CONSENT_DOMAINS,
  toReadableSemester,
} from 'app/routes/events/utils';
import styles from './PhotoConsents.css';

import { selectStyles, selectTheme } from 'app/components/Form/SelectInput';

const ConsentManager = ({
  consent,
  updateConsent,
  isMe,
}: {
  consent: ?PhotoConsent,
  updateConsent: (consent: PhotoConsent) => Promise<*>,
  isMe: boolean,
}) => {
  if (!consent) {
    return null;
  }

  const consentStatus =
    typeof consent.isConsenting === 'boolean'
      ? consent.isConsenting
        ? 'Du ga samtykket den '
        : 'Du trakk samtykket den '
      : 'Du har ikke tatt stilling til samtykket.';

  const presentableDomain =
    consent.domain === PHOTO_CONSENT_DOMAINS.WEBSITE
      ? 'Abakus.no'
      : 'Sosiale medier';
  return (
    <>

{presentableDomain}

        Jeg godtar at Abakus kan legge ut bilder av meg på {presentableDomain} i
        perioden {toReadableSemester(consent)}:

        {consentStatus}
        {typeof consent.isConsenting === 'boolean' ? (
          {moment(consent.updatedAt).format('DD. MMM YYYY')}.
        ) : (
          ''
        )}

        <ConfirmModalWithParent
          closeOnConfirm={true}
          title={`Trekke bildesamtykke på ${presentableDomain}`}
          message={`Er du sikker på at du vil trekke bildesamtykket ditt for ${toReadableSemester(
            consent
          )} på ${presentableDomain}? Dersom du ønsker å fjerne noen spesifikke bilder, kan du i stedet sende en mail til pr@abakus.no med informasjon om hvilke bilder du vil fjerne.`}
          onConfirm={() => updateConsent({ ...consent, isConsenting: false })}
        >
          <Button
            dark
            disabled={!isMe || consent.isConsenting === false}
            className={styles.consentBtn}
          >
            Trekk samtykke
          </Button>
        </ConfirmModalWithParent>
        <Button
          success
          disabled={!isMe || consent.isConsenting === true}
          onClick={() => updateConsent({ ...consent, isConsenting: true })}
          className={styles.consentBtn}
        >
          Behold samtykke
        </Button>

    </>
  );
};

const PhotoConsents = ({
  photoConsents,
  username,
  updatePhotoConsent,
  userId,
  isMe,
}: {
  photoConsents: Array<PhotoConsent>,
  username: string,
  updatePhotoConsent: (
    photoConsent: PhotoConsent,
    username: string,
    userId: Number
  ) => Promise<*>,
  userId: Number,
  isMe: boolean,
}) => {
  const semesterOptions = photoConsents
    .slice(0)
    .filter(
      (photoConsent) => photoConsent.domain === PHOTO_CONSENT_DOMAINS.WEBSITE
    )
    .sort((a, b) => {
      if (a.year === b.year) {
        return a.semester === 'spring' ? 1 : -1;
      }

      return a.year > b.year ? -1 : 1;
    })
    .map((photoConsent) => ({
      label: toReadableSemester(photoConsent),
      value: { year: photoConsent.year, semester: photoConsent.semester },
    }));

  const [selectedSemesterOption, setSelectedSemesterOption] = useState(
    semesterOptions[0]
  );

  const updateConsent = (consent: PhotoConsent) =>
    updatePhotoConsent(consent, username, userId);
  return (
    <Flex column={true}>

Semester

      <Select
        name="select-semester"
        clearable={false}
        options={semesterOptions}
        value={selectedSemesterOption}
        onChange={({ value }) =>
          setSelectedSemesterOption({ label: toReadableSemester(value), value })
        }
        theme={selectTheme}
        styles={selectStyles}
      />
      <ConsentManager
        consent={getConsent(
          PHOTO_CONSENT_DOMAINS.SOCIAL_MEDIA,
          selectedSemesterOption.value.year,
          selectedSemesterOption.value.semester,
          photoConsents
        )}
        updateConsent={updateConsent}
        isMe={isMe}
      />
      <ConsentManager
        consent={getConsent(
          PHOTO_CONSENT_DOMAINS.WEBSITE,
          selectedSemesterOption.value.year,
          selectedSemesterOption.value.semester,
          photoConsents
        )}
        updateConsent={updateConsent}
        isMe={isMe}
      />
    </Flex>
  );
};

export default PhotoConsents;
<|endoftext|>


<commit_before>(function () {

	var logEl,
		isInitialized = false,
		_console = {}; // backup console obj to contain references of overridden fns.
		_options = {
			bgColor: 'black',
			logColor: 'lightgreen',
			infoColor: 'blue',
			warnColor: 'orange',
			errorColor: 'red',
			freeConsole: false,
			css: '',
			autoScroll: true
		};

	function createElement(tag, css) {
		var element = document.createElement(tag);
		element.style.cssText = css;
		return element;
	}

	function createPanel() {
		var div = createElement('div', 'z-index:2147483647;font-family:Helvetica,Arial,sans-serif;font-size:10px;font-weight:bold;padding:5px;text-align:left;opacity:0.8;position:fixed;right:0;top:0;min-width:200px;max-height:50vh;overflow:auto;background:' + _options.bgColor + ';' + _options.css);
		return div;
	}

	function genericLogger(color) {
		return function() {
			var el = createElement('div', 'line-height:18px;min-height:18px;background:' +
				(logEl.children.length % 2 ? 'rgba(255,255,255,0.1)' : '') + ';color:' + color); // zebra lines
			var val = [].slice.call(arguments).reduce(function(prev, arg) {
				return prev + ' ' + (typeof arg === "object" ? JSON.stringify(arg) : arg);
			}, '');
			el.textContent = val;

			logEl.appendChild(el);

			// Scroll to last element, if autoScroll option is set.
			if(_options.autoScroll) {
				logEl.scrollTop = logEl.scrollHeight - logEl.clientHeight;
			}
		};
	}

	function clear() {
		logEl.innerHTML = '';
	}

	function log() {
		return genericLogger(_options.logColor).apply(null, arguments);
	}

	function info() {
		return genericLogger(_options.infoColor).apply(null, arguments);
	}

	function warn() {
		return genericLogger(_options.warnColor).apply(null, arguments);
	}

	function error() {
		return genericLogger(_options.errorColor).apply(null, arguments);
	}

	function setOptions(options) {
		for(var i in options)
			if(options.hasOwnProperty(i) && _options.hasOwnProperty(i)) {
				_options[i] = options[i];
			}
	}

	function init(options) {
		if (isInitialized) { return; }

		isInitialized = true;

		if(options) {
			setOptions(options);
		}

		logEl = createPanel();
		document.body.appendChild(logEl);

		if (!_options.freeConsole) {
			// Backup actual fns to keep it working together
			_console.log = console.log;
			_console.clear = console.clear;
			_console.info = console.info;
			_console.warn = console.warn;
			_console.error = console.error;
			console.log = originalFnCallDecorator(log, 'log');
			console.clear = originalFnCallDecorator(clear, 'clear');
			console.info = originalFnCallDecorator(info, 'info');
			console.warn = originalFnCallDecorator(warn, 'warn');
			console.error = originalFnCallDecorator(error, 'error');
		}
	}

	function destroy() {
		isInitialized = false;
		console.log = _console.log;
		console.clear = _console.clear;
		console.info = _console.info;
		console.warn = _console.warn;
		console.error = _console.error;
		logEl.remove();
	}

	/**
	 * Checking if isInitialized is set
	 */
	function checkInitialized() {
		if (!isInitialized) {
			throw 'You need to call `screenLog.init()` first.';
		}
	}

	/**
	 * Decorator for checking if isInitialized is set
	 * @param  {Function} fn Fn to decorate
	 * @return {Function}      Decorated fn.
	 */
	function checkInitDecorator(fn) {
		return function() {
			checkInitialized();
			return fn.apply(this, arguments);
		};
	}

	/**
	 * Decorator for calling the original console's fn at the end of
	 * our overridden fn definitions.
	 * @param  {Function} fn Fn to decorate
	 * @param  {string} fn Name of original function
	 * @return {Function}      Decorated fn.
	 */
	function originalFnCallDecorator(fn, fnName) {
		return function() {
			fn.apply(this, arguments);
			if (typeof _console[fnName] === 'function') {
				_console[fnName].apply(console, arguments);
			}
		};
	}

	// Public API
	window.screenLog = {
		init: init,
		log: originalFnCallDecorator(checkInitDecorator(log), 'log'),
		clear: originalFnCallDecorator(checkInitDecorator(clear), 'clear'),
		info: originalFnCallDecorator(checkInitDecorator(clear), 'info'),
		warn: originalFnCallDecorator(checkInitDecorator(warn), 'warn'),
		error: originalFnCallDecorator(checkInitDecorator(error), 'error'),
		destroy: checkInitDecorator(destroy)
	};
})();
<commit_msg>padding adjustment<commit_after>(function () {

	var logEl,
		isInitialized = false,
		_console = {}; // backup console obj to contain references of overridden fns.
		_options = {
			bgColor: 'black',
			logColor: 'lightgreen',
			infoColor: 'blue',
			warnColor: 'orange',
			errorColor: 'red',
			freeConsole: false,
			css: '',
			autoScroll: true
		};

	function createElement(tag, css) {
		var element = document.createElement(tag);
		element.style.cssText = css;
		return element;
	}

	function createPanel() {
		var div = createElement('div', 'z-index:2147483647;font-family:Helvetica,Arial,sans-serif;font-size:10px;font-weight:bold;padding:0 5px;text-align:left;opacity:0.8;position:fixed;right:0;top:0;min-width:200px;max-height:50vh;overflow:auto;background:' + _options.bgColor + ';' + _options.css);
		return div;
	}

	function genericLogger(color) {
		return function() {
			var el = createElement('div', 'line-height:18px;min-height:18px;background:' +
				(logEl.children.length % 2 ? 'rgba(255,255,255,0.1)' : '') + ';color:' + color); // zebra lines
			var val = [].slice.call(arguments).reduce(function(prev, arg) {
				return prev + ' ' + (typeof arg === "object" ? JSON.stringify(arg) : arg);
			}, '');
			el.textContent = val;

			logEl.appendChild(el);

			// Scroll to last element, if autoScroll option is set.
			if(_options.autoScroll) {
				logEl.scrollTop = logEl.scrollHeight - logEl.clientHeight;
			}
		};
	}

	function clear() {
		logEl.innerHTML = '';
	}

	function log() {
		return genericLogger(_options.logColor).apply(null, arguments);
	}

	function info() {
		return genericLogger(_options.infoColor).apply(null, arguments);
	}

	function warn() {
		return genericLogger(_options.warnColor).apply(null, arguments);
	}

	function error() {
		return genericLogger(_options.errorColor).apply(null, arguments);
	}

	function setOptions(options) {
		for(var i in options)
			if(options.hasOwnProperty(i) && _options.hasOwnProperty(i)) {
				_options[i] = options[i];
			}
	}

	function init(options) {
		if (isInitialized) { return; }

		isInitialized = true;

		if(options) {
			setOptions(options);
		}

		logEl = createPanel();
		document.body.appendChild(logEl);

		if (!_options.freeConsole) {
			// Backup actual fns to keep it working together
			_console.log = console.log;
			_console.clear = console.clear;
			_console.info = console.info;
			_console.warn = console.warn;
			_console.error = console.error;
			console.log = originalFnCallDecorator(log, 'log');
			console.clear = originalFnCallDecorator(clear, 'clear');
			console.info = originalFnCallDecorator(info, 'info');
			console.warn = originalFnCallDecorator(warn, 'warn');
			console.error = originalFnCallDecorator(error, 'error');
		}
	}

	function destroy() {
		isInitialized = false;
		console.log = _console.log;
		console.clear = _console.clear;
		console.info = _console.info;
		console.warn = _console.warn;
		console.error = _console.error;
		logEl.remove();
	}

	/**
	 * Checking if isInitialized is set
	 */
	function checkInitialized() {
		if (!isInitialized) {
			throw 'You need to call `screenLog.init()` first.';
		}
	}

	/**
	 * Decorator for checking if isInitialized is set
	 * @param  {Function} fn Fn to decorate
	 * @return {Function}      Decorated fn.
	 */
	function checkInitDecorator(fn) {
		return function() {
			checkInitialized();
			return fn.apply(this, arguments);
		};
	}

	/**
	 * Decorator for calling the original console's fn at the end of
	 * our overridden fn definitions.
	 * @param  {Function} fn Fn to decorate
	 * @param  {string} fn Name of original function
	 * @return {Function}      Decorated fn.
	 */
	function originalFnCallDecorator(fn, fnName) {
		return function() {
			fn.apply(this, arguments);
			if (typeof _console[fnName] === 'function') {
				_console[fnName].apply(console, arguments);
			}
		};
	}

	// Public API
	window.screenLog = {
		init: init,
		log: originalFnCallDecorator(checkInitDecorator(log), 'log'),
		clear: originalFnCallDecorator(checkInitDecorator(clear), 'clear'),
		info: originalFnCallDecorator(checkInitDecorator(clear), 'info'),
		warn: originalFnCallDecorator(checkInitDecorator(warn), 'warn'),
		error: originalFnCallDecorator(checkInitDecorator(error), 'error'),
		destroy: checkInitDecorator(destroy)
	};
})();
<|endoftext|>


<commit_before>/* eslint-disable no-param-reassign */
import d3 from 'd3';
import PropTypes from 'prop-types';
import { getScale } from '../../modules/CategoricalColorNamespace';
import { wrapSvgText } from '../../modules/utils';
import './Sunburst.css';

const propTypes = {
  // Each row is an array of [hierarchy-lvl1, hierarchy-lvl2, metric1, metric2]
  // hierarchy-lvls are string. metrics are number
  data: PropTypes.arrayOf(PropTypes.array),
  width: PropTypes.number,
  height: PropTypes.number,
  colorScheme: PropTypes.string,
  metrics: PropTypes.arrayOf(PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object, // The metric object
  ])),
};

function metricLabel(metric) {
  return ((typeof metric) === 'string' || metric instanceof String)
    ? metric
    : metric.label;
}

// Given a node in a partition layout, return an array of all of its ancestor
// nodes, highest first, but excluding the root.
function getAncestors(node) {
  const path = [];
  let current = node;
  while (current.parent) {
    path.unshift(current);
    current = current.parent;
  }
  return path;
}

// Modified from http://bl.ocks.org/kerryrodden/7090426
function Sunburst(element, props) {
  const container = d3.select(element);
  const {
    data,
    width,
    height,
    colorScheme,
    metrics,
  } = props;

  // vars with shared scope within this function
  const margin = { top: 10, right: 5, bottom: 10, left: 5 };
  const containerWidth = width;
  const containerHeight = height;
  const breadcrumbHeight = containerHeight * 0.085;
  const visWidth = containerWidth - margin.left - margin.right;
  const visHeight = containerHeight - margin.top - margin.bottom - breadcrumbHeight;
  const radius = Math.min(visWidth, visHeight) / 2;

  let colorByCategory = true; // color by category if primary/secondary metrics match
  let maxBreadcrumbs;
  let breadcrumbDims; // set based on data
  let totalSize; // total size of all segments; set after loading the data.
  let colorScale;
  let breadcrumbs;
  let vis;
  let arcs;
  let gMiddleText; // dom handles

  const colorFn = getScale(colorScheme).toFunction();

  // Helper + path gen functions
  const partition = d3.layout.partition()
    .size([2 * Math.PI, radius * radius])
    .value(function (d) { return d.m1; });

  const arc = d3.svg.arc()
    .startAngle(d => d.x)
    .endAngle(d => d.x + d.dx)
    .innerRadius(d => Math.sqrt(d.y))
    .outerRadius(d => Math.sqrt(d.y + d.dy));

  const formatNum = d3.format('.1s');
  const formatPerc = d3.format('.1p');

  container.select('svg').remove();

  const svg = container.append('svg:svg')
    .attr('width', containerWidth)
    .attr('height', containerHeight);

  function createBreadcrumbs(firstRowData) {
    // -2 bc row contains 2x metrics, +extra for %label and buffer
    maxBreadcrumbs = (firstRowData.length - 2) + 1;
    breadcrumbDims = {
      width: visWidth / maxBreadcrumbs,
      height: breadcrumbHeight * 0.8, // more margin
      spacing: 3,
      tipTailWidth: 10,
    };

    breadcrumbs = svg.append('svg:g')
      .attr('class', 'breadcrumbs')
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    breadcrumbs.append('svg:text')
      .attr('class', 'end-label');
  }

  // Generate a string that describes the points of a breadcrumb polygon.
  function breadcrumbPoints(d, i) {
    const points = [];
    points.push('0,0');
    points.push(breadcrumbDims.width + ',0');
    points.push(
      breadcrumbDims.width + breadcrumbDims.tipTailWidth + ',' + (breadcrumbDims.height / 2));
    points.push(breadcrumbDims.width + ',' + breadcrumbDims.height);
    points.push('0,' + breadcrumbDims.height);
    if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
      points.push(breadcrumbDims.tipTailWidth + ',' + (breadcrumbDims.height / 2));
    }
    return points.join(' ');
  }

  function updateBreadcrumbs(sequenceArray, percentageString) {
    const g = breadcrumbs.selectAll('g')
      .data(sequenceArray, d => d.name + d.depth);

    // Add breadcrumb and label for entering nodes.
    const entering = g.enter().append('svg:g');

    entering.append('svg:polygon')
        .attr('points', breadcrumbPoints)
        .style('fill', function (d) {
          return colorByCategory ?
            colorFn(d.name) :
            colorScale(d.m2 / d.m1);
        });

    entering.append('svg:text')
        .attr('x', (breadcrumbDims.width + breadcrumbDims.tipTailWidth) / 2)
        .attr('y', breadcrumbDims.height / 4)
        .attr('dy', '0.35em')
        .style('fill', function (d) {
          // Make text white or black based on the lightness of the background
          const col = d3.hsl(colorByCategory ?
            colorFn(d.name) :
            colorScale(d.m2 / d.m1));
          return col.l < 0.5 ? 'white' : 'black';
        })
        .attr('class', 'step-label')
        .text(function (d) { return d.name.replace(/_/g, ' '); })
        .call(wrapSvgText, breadcrumbDims.width, breadcrumbDims.height / 2);

    // Set position for entering and updating nodes.
    g.attr('transform', function (d, i) {
      return 'translate(' + i * (breadcrumbDims.width + breadcrumbDims.spacing) + ', 0)';
    });

    // Remove exiting nodes.
    g.exit().remove();

    // Now move and update the percentage at the end.
    breadcrumbs.select('.end-label')
        .attr('x', (sequenceArray.length + 0.5) * (breadcrumbDims.width + breadcrumbDims.spacing))
        .attr('y', breadcrumbDims.height / 2)
        .attr('dy', '0.35em')
        .text(percentageString);

    // Make the breadcrumb trail visible, if it's hidden.
    breadcrumbs.style('visibility', null);
  }

  // Fade all but the current sequence, and show it in the breadcrumb trail.
  function mouseenter(d) {
    const sequenceArray = getAncestors(d);
    const parentOfD = sequenceArray[sequenceArray.length - 2] || null;

    const absolutePercentage = (d.m1 / totalSize).toPrecision(3);
    const conditionalPercentage = parentOfD ? (d.m1 / parentOfD.m1).toPrecision(3) : null;

    const absolutePercString = formatPerc(absolutePercentage);
    const conditionalPercString = parentOfD ? formatPerc(conditionalPercentage) : '';

    // 3 levels of text if inner-most level, 4 otherwise
    const yOffsets = ['-25', '7', '35', '60'];
    let offsetIndex = 0;

    // If metrics match, assume we are coloring by category
    const metricsMatch = Math.abs(d.m1 - d.m2) < 0.00001;

    gMiddleText.selectAll('*').remove();

    gMiddleText.append('text')
      .attr('class', 'path-abs-percent')
      .attr('y', yOffsets[offsetIndex++])
      .text(absolutePercString + ' of total');

    if (conditionalPercString) {
      gMiddleText.append('text')
        .attr('class', 'path-cond-percent')
        .attr('y', yOffsets[offsetIndex++])
        .text(conditionalPercString + ' of parent');
    }

    gMiddleText.append('text')
      .attr('class', 'path-metrics')
      .attr('y', yOffsets[offsetIndex++])
      .text(`${metricLabel(metrics[0])}: ${formatNum(d.m1)}` + (metricsMatch ? '' : `, ${metricLabel(metrics[1])}: ${formatNum(d.m2)}`));

    gMiddleText.append('text')
      .attr('class', 'path-ratio')
      .attr('y', yOffsets[offsetIndex++])
      .text((metricsMatch ? '' : (`${metricLabel(metrics[1])}/${metricLabel(metrics[0])}: ${formatPerc(d.m2 / d.m1)}`)));

    // Reset and fade all the segments.
    arcs.selectAll('path')
      .style('stroke-width', null)
      .style('stroke', null)
      .style('opacity', 0.3);

    // Then highlight only those that are an ancestor of the current segment.
    arcs.selectAll('path')
      .filter(node => (sequenceArray.indexOf(node) >= 0))
      .style('opacity', 1)
      .style('stroke', '#aaa');

    updateBreadcrumbs(sequenceArray, absolutePercString);
  }

  // Restore everything to full opacity when moving off the visualization.
  function mouseleave() {
    // Hide the breadcrumb trail
    breadcrumbs.style('visibility', 'hidden');

    gMiddleText.selectAll('*').remove();

    // Deactivate all segments during transition.
    arcs.selectAll('path').on('mouseenter', null);

    // Transition each segment to full opacity and then reactivate it.
    arcs.selectAll('path')
      .transition()
      .duration(200)
      .style('opacity', 1)
      .style('stroke', null)
      .style('stroke-width', null)
      .each('end', function () {
        d3.select(this).on('mouseenter', mouseenter);
      });
  }

  function buildHierarchy(rows) {
    const root = {
      name: 'root',
      children: [],
    };

    // each record [groupby1val, groupby2val, (<string> or 0)n, m1, m2]
    for (let i = 0; i < rows.length; i++) {
      const row = rows[i];
      const m1 = Number(row[row.length - 2]);
      const m2 = Number(row[row.length - 1]);
      const levels = row.slice(0, row.length - 2);
      if (Number.isNaN(m1)) { // e.g. if this is a header row
        continue;
      }
      let currentNode = root;
      for (let level = 0; level < levels.length; level++) {
        const children = currentNode.children || [];
        const nodeName = levels[level].toString();
        // If the next node has the name '0', it will
        const isLeafNode = (level >= levels.length - 1) || levels[level + 1] === 0;
        let childNode;
        let currChild;

        if (!isLeafNode) {
          // Not yet at the end of the sequence; move down the tree.
          let foundChild = false;
          for (let k = 0; k < children.length; k++) {
            currChild = children[k];
            if (currChild.name === nodeName &&
                currChild.level === level) {
            // must match name AND level
              childNode = currChild;
              foundChild = true;
              break;
            }
          }
          // If we don't already have a child node for this branch, create it.
          if (!foundChild) {
            childNode = {
              name: nodeName,
              children: [],
              level,
            };
            children.push(childNode);
          }
          currentNode = childNode;
        } else if (nodeName !== 0) {
          // Reached the end of the sequence; create a leaf node.
          childNode = {
            name: nodeName,
            m1,
            m2,
          };
          children.push(childNode);
        }
      }
    }

    function recurse(node) {
      if (node.children) {
        let sums;
        let m1 = 0;
        let m2 = 0;
        for (let i = 0; i < node.children.length; i++) {
          sums = recurse(node.children[i]);
          m1 += sums[0];
          m2 += sums[1];
        }
        node.m1 = m1;
        node.m2 = m2;
      }
      return [node.m1, node.m2];
    }

    recurse(root);
    return root;
  }

  // Main function to draw and set up the visualization, once we have the data.
  function createVisualization(rows) {
    const root = buildHierarchy(rows);

    vis = svg.append('svg:g')
      .attr('class', 'sunburst-vis')
      .attr('transform', (
        'translate(' +
          `${(margin.left + (visWidth / 2))},` +
          `${(margin.top + breadcrumbHeight + (visHeight / 2))}` +
        ')'
      ))
      .on('mouseleave', mouseleave);

    arcs = vis.append('svg:g')
      .attr('id', 'arcs');

    gMiddleText = vis.append('svg:g')
      .attr('class', 'center-label');

    // Bounding circle underneath the sunburst, to make it easier to detect
    // when the mouse leaves the parent g.
    arcs.append('svg:circle')
      .attr('r', radius)
      .style('opacity', 0);

    // For efficiency, filter nodes to keep only those large enough to see.
    const nodes = partition.nodes(root)
      .filter(d => d.dx > 0.005); // 0.005 radians = 0.29 degrees

    let ext;

    if (metrics[0] !== metrics[1] && metrics[1]) {
      colorByCategory = false;
      ext = d3.extent(nodes, d => d.m2 / d.m1);
      colorScale = d3.scale.linear()
        .domain([ext[0], ext[0] + ((ext[1] - ext[0]) / 2), ext[1]])
        .range(['#00D1C1', 'white', '#FFB400']);
    }

    arcs.selectAll('path')
        .data(nodes)
      .enter()
        .append('svg:path')
        .attr('display', d => d.depth ? null : 'none')
        .attr('d', arc)
        .attr('fill-rule', 'evenodd')
        .style('fill', d => colorByCategory
          ? colorFn(d.name)
          : colorScale(d.m2 / d.m1))
        .style('opacity', 1)
        .on('mouseenter', mouseenter);

    // Get total size of the tree = value of root node from partition.
    totalSize = root.value;
  }
  createBreadcrumbs(data[0]);
  createVisualization(data);
}

Sunburst.displayName = 'Sunburst';
Sunburst.propTypes = propTypes;

export default Sunburst;
<commit_msg>Revert old precision formatters (#6056)<commit_after>/* eslint-disable no-param-reassign */
import d3 from 'd3';
import PropTypes from 'prop-types';
import { getScale } from '../../modules/CategoricalColorNamespace';
import { wrapSvgText } from '../../modules/utils';
import './Sunburst.css';

const propTypes = {
  // Each row is an array of [hierarchy-lvl1, hierarchy-lvl2, metric1, metric2]
  // hierarchy-lvls are string. metrics are number
  data: PropTypes.arrayOf(PropTypes.array),
  width: PropTypes.number,
  height: PropTypes.number,
  colorScheme: PropTypes.string,
  metrics: PropTypes.arrayOf(PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object, // The metric object
  ])),
};

function metricLabel(metric) {
  return ((typeof metric) === 'string' || metric instanceof String)
    ? metric
    : metric.label;
}

// Given a node in a partition layout, return an array of all of its ancestor
// nodes, highest first, but excluding the root.
function getAncestors(node) {
  const path = [];
  let current = node;
  while (current.parent) {
    path.unshift(current);
    current = current.parent;
  }
  return path;
}

// Modified from http://bl.ocks.org/kerryrodden/7090426
function Sunburst(element, props) {
  const container = d3.select(element);
  const {
    data,
    width,
    height,
    colorScheme,
    metrics,
  } = props;

  // vars with shared scope within this function
  const margin = { top: 10, right: 5, bottom: 10, left: 5 };
  const containerWidth = width;
  const containerHeight = height;
  const breadcrumbHeight = containerHeight * 0.085;
  const visWidth = containerWidth - margin.left - margin.right;
  const visHeight = containerHeight - margin.top - margin.bottom - breadcrumbHeight;
  const radius = Math.min(visWidth, visHeight) / 2;

  let colorByCategory = true; // color by category if primary/secondary metrics match
  let maxBreadcrumbs;
  let breadcrumbDims; // set based on data
  let totalSize; // total size of all segments; set after loading the data.
  let colorScale;
  let breadcrumbs;
  let vis;
  let arcs;
  let gMiddleText; // dom handles

  const colorFn = getScale(colorScheme).toFunction();

  // Helper + path gen functions
  const partition = d3.layout.partition()
    .size([2 * Math.PI, radius * radius])
    .value(function (d) { return d.m1; });

  const arc = d3.svg.arc()
    .startAngle(d => d.x)
    .endAngle(d => d.x + d.dx)
    .innerRadius(d => Math.sqrt(d.y))
    .outerRadius(d => Math.sqrt(d.y + d.dy));

  const formatNum = d3.format('.3s');
  const formatPerc = d3.format('.3p');

  container.select('svg').remove();

  const svg = container.append('svg:svg')
    .attr('width', containerWidth)
    .attr('height', containerHeight);

  function createBreadcrumbs(firstRowData) {
    // -2 bc row contains 2x metrics, +extra for %label and buffer
    maxBreadcrumbs = (firstRowData.length - 2) + 1;
    breadcrumbDims = {
      width: visWidth / maxBreadcrumbs,
      height: breadcrumbHeight * 0.8, // more margin
      spacing: 3,
      tipTailWidth: 10,
    };

    breadcrumbs = svg.append('svg:g')
      .attr('class', 'breadcrumbs')
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    breadcrumbs.append('svg:text')
      .attr('class', 'end-label');
  }

  // Generate a string that describes the points of a breadcrumb polygon.
  function breadcrumbPoints(d, i) {
    const points = [];
    points.push('0,0');
    points.push(breadcrumbDims.width + ',0');
    points.push(
      breadcrumbDims.width + breadcrumbDims.tipTailWidth + ',' + (breadcrumbDims.height / 2));
    points.push(breadcrumbDims.width + ',' + breadcrumbDims.height);
    points.push('0,' + breadcrumbDims.height);
    if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
      points.push(breadcrumbDims.tipTailWidth + ',' + (breadcrumbDims.height / 2));
    }
    return points.join(' ');
  }

  function updateBreadcrumbs(sequenceArray, percentageString) {
    const g = breadcrumbs.selectAll('g')
      .data(sequenceArray, d => d.name + d.depth);

    // Add breadcrumb and label for entering nodes.
    const entering = g.enter().append('svg:g');

    entering.append('svg:polygon')
        .attr('points', breadcrumbPoints)
        .style('fill', function (d) {
          return colorByCategory ?
            colorFn(d.name) :
            colorScale(d.m2 / d.m1);
        });

    entering.append('svg:text')
        .attr('x', (breadcrumbDims.width + breadcrumbDims.tipTailWidth) / 2)
        .attr('y', breadcrumbDims.height / 4)
        .attr('dy', '0.35em')
        .style('fill', function (d) {
          // Make text white or black based on the lightness of the background
          const col = d3.hsl(colorByCategory ?
            colorFn(d.name) :
            colorScale(d.m2 / d.m1));
          return col.l < 0.5 ? 'white' : 'black';
        })
        .attr('class', 'step-label')
        .text(function (d) { return d.name.replace(/_/g, ' '); })
        .call(wrapSvgText, breadcrumbDims.width, breadcrumbDims.height / 2);

    // Set position for entering and updating nodes.
    g.attr('transform', function (d, i) {
      return 'translate(' + i * (breadcrumbDims.width + breadcrumbDims.spacing) + ', 0)';
    });

    // Remove exiting nodes.
    g.exit().remove();

    // Now move and update the percentage at the end.
    breadcrumbs.select('.end-label')
        .attr('x', (sequenceArray.length + 0.5) * (breadcrumbDims.width + breadcrumbDims.spacing))
        .attr('y', breadcrumbDims.height / 2)
        .attr('dy', '0.35em')
        .text(percentageString);

    // Make the breadcrumb trail visible, if it's hidden.
    breadcrumbs.style('visibility', null);
  }

  // Fade all but the current sequence, and show it in the breadcrumb trail.
  function mouseenter(d) {
    const sequenceArray = getAncestors(d);
    const parentOfD = sequenceArray[sequenceArray.length - 2] || null;

    const absolutePercentage = (d.m1 / totalSize).toPrecision(3);
    const conditionalPercentage = parentOfD ? (d.m1 / parentOfD.m1).toPrecision(3) : null;

    const absolutePercString = formatPerc(absolutePercentage);
    const conditionalPercString = parentOfD ? formatPerc(conditionalPercentage) : '';

    // 3 levels of text if inner-most level, 4 otherwise
    const yOffsets = ['-25', '7', '35', '60'];
    let offsetIndex = 0;

    // If metrics match, assume we are coloring by category
    const metricsMatch = Math.abs(d.m1 - d.m2) < 0.00001;

    gMiddleText.selectAll('*').remove();

    gMiddleText.append('text')
      .attr('class', 'path-abs-percent')
      .attr('y', yOffsets[offsetIndex++])
      .text(absolutePercString + ' of total');

    if (conditionalPercString) {
      gMiddleText.append('text')
        .attr('class', 'path-cond-percent')
        .attr('y', yOffsets[offsetIndex++])
        .text(conditionalPercString + ' of parent');
    }

    gMiddleText.append('text')
      .attr('class', 'path-metrics')
      .attr('y', yOffsets[offsetIndex++])
      .text(`${metricLabel(metrics[0])}: ${formatNum(d.m1)}` + (metricsMatch ? '' : `, ${metricLabel(metrics[1])}: ${formatNum(d.m2)}`));

    gMiddleText.append('text')
      .attr('class', 'path-ratio')
      .attr('y', yOffsets[offsetIndex++])
      .text((metricsMatch ? '' : (`${metricLabel(metrics[1])}/${metricLabel(metrics[0])}: ${formatPerc(d.m2 / d.m1)}`)));

    // Reset and fade all the segments.
    arcs.selectAll('path')
      .style('stroke-width', null)
      .style('stroke', null)
      .style('opacity', 0.3);

    // Then highlight only those that are an ancestor of the current segment.
    arcs.selectAll('path')
      .filter(node => (sequenceArray.indexOf(node) >= 0))
      .style('opacity', 1)
      .style('stroke', '#aaa');

    updateBreadcrumbs(sequenceArray, absolutePercString);
  }

  // Restore everything to full opacity when moving off the visualization.
  function mouseleave() {
    // Hide the breadcrumb trail
    breadcrumbs.style('visibility', 'hidden');

    gMiddleText.selectAll('*').remove();

    // Deactivate all segments during transition.
    arcs.selectAll('path').on('mouseenter', null);

    // Transition each segment to full opacity and then reactivate it.
    arcs.selectAll('path')
      .transition()
      .duration(200)
      .style('opacity', 1)
      .style('stroke', null)
      .style('stroke-width', null)
      .each('end', function () {
        d3.select(this).on('mouseenter', mouseenter);
      });
  }

  function buildHierarchy(rows) {
    const root = {
      name: 'root',
      children: [],
    };

    // each record [groupby1val, groupby2val, (<string> or 0)n, m1, m2]
    for (let i = 0; i < rows.length; i++) {
      const row = rows[i];
      const m1 = Number(row[row.length - 2]);
      const m2 = Number(row[row.length - 1]);
      const levels = row.slice(0, row.length - 2);
      if (Number.isNaN(m1)) { // e.g. if this is a header row
        continue;
      }
      let currentNode = root;
      for (let level = 0; level < levels.length; level++) {
        const children = currentNode.children || [];
        const nodeName = levels[level].toString();
        // If the next node has the name '0', it will
        const isLeafNode = (level >= levels.length - 1) || levels[level + 1] === 0;
        let childNode;
        let currChild;

        if (!isLeafNode) {
          // Not yet at the end of the sequence; move down the tree.
          let foundChild = false;
          for (let k = 0; k < children.length; k++) {
            currChild = children[k];
            if (currChild.name === nodeName &&
                currChild.level === level) {
            // must match name AND level
              childNode = currChild;
              foundChild = true;
              break;
            }
          }
          // If we don't already have a child node for this branch, create it.
          if (!foundChild) {
            childNode = {
              name: nodeName,
              children: [],
              level,
            };
            children.push(childNode);
          }
          currentNode = childNode;
        } else if (nodeName !== 0) {
          // Reached the end of the sequence; create a leaf node.
          childNode = {
            name: nodeName,
            m1,
            m2,
          };
          children.push(childNode);
        }
      }
    }

    function recurse(node) {
      if (node.children) {
        let sums;
        let m1 = 0;
        let m2 = 0;
        for (let i = 0; i < node.children.length; i++) {
          sums = recurse(node.children[i]);
          m1 += sums[0];
          m2 += sums[1];
        }
        node.m1 = m1;
        node.m2 = m2;
      }
      return [node.m1, node.m2];
    }

    recurse(root);
    return root;
  }

  // Main function to draw and set up the visualization, once we have the data.
  function createVisualization(rows) {
    const root = buildHierarchy(rows);

    vis = svg.append('svg:g')
      .attr('class', 'sunburst-vis')
      .attr('transform', (
        'translate(' +
          `${(margin.left + (visWidth / 2))},` +
          `${(margin.top + breadcrumbHeight + (visHeight / 2))}` +
        ')'
      ))
      .on('mouseleave', mouseleave);

    arcs = vis.append('svg:g')
      .attr('id', 'arcs');

    gMiddleText = vis.append('svg:g')
      .attr('class', 'center-label');

    // Bounding circle underneath the sunburst, to make it easier to detect
    // when the mouse leaves the parent g.
    arcs.append('svg:circle')
      .attr('r', radius)
      .style('opacity', 0);

    // For efficiency, filter nodes to keep only those large enough to see.
    const nodes = partition.nodes(root)
      .filter(d => d.dx > 0.005); // 0.005 radians = 0.29 degrees

    let ext;

    if (metrics[0] !== metrics[1] && metrics[1]) {
      colorByCategory = false;
      ext = d3.extent(nodes, d => d.m2 / d.m1);
      colorScale = d3.scale.linear()
        .domain([ext[0], ext[0] + ((ext[1] - ext[0]) / 2), ext[1]])
        .range(['#00D1C1', 'white', '#FFB400']);
    }

    arcs.selectAll('path')
        .data(nodes)
      .enter()
        .append('svg:path')
        .attr('display', d => d.depth ? null : 'none')
        .attr('d', arc)
        .attr('fill-rule', 'evenodd')
        .style('fill', d => colorByCategory
          ? colorFn(d.name)
          : colorScale(d.m2 / d.m1))
        .style('opacity', 1)
        .on('mouseenter', mouseenter);

    // Get total size of the tree = value of root node from partition.
    totalSize = root.value;
  }
  createBreadcrumbs(data[0]);
  createVisualization(data);
}

Sunburst.displayName = 'Sunburst';
Sunburst.propTypes = propTypes;

export default Sunburst;
<|endoftext|>


<commit_before>/**
 * @fileoverview
 * This file contains a lookup table of css properties, the value of the
 * property contains the default value used by Opera if ther is no CSS defined
 * by the author. This is used in the CSS inspector
 * @see stylesheets
 */

css_initial_values =
{
"-apple-dashboard-region": "",
"-o-background-size": "",
"-o-link": "",
"-o-link-source": "",
"-o-table-baseline": "",
"-o-text-overflow": "",
"-wap-accesskey": "",
"-wap-input-format": "",
"-wap-input-required": "",
"-wap-marquee-dir": "",
"-wap-marquee-loop": "",
"-wap-marquee-speed": "",
"-wap-marquee-style": "",
"-xv-interpret-as": "",
"-xv-phonemes": "",
"-xv-voice-balance": "",
"-xv-voice-duration": "",
"-xv-voice-pitch": "",
"-xv-voice-pitch-range": "",
"-xv-voice-rate": "",
"-xv-voice-stress": "",
"-xv-voice-volume": "",
"alignment-baseline": "",
"audio-level": "",
"background": "transparent none repeat scroll 0px 0px",
"background-attachment": "scroll",
"background-color": "transparent",
"background-image": "none",
"background-position": "0px 0px",
"background-repeat": "repeat",
"baseline-shift": "",
"border": "0px #000000",
"border-bottom": "0px #000000",
"border-bottom-color": "#000000",
"border-bottom-style": "none",
"border-bottom-width": "0px",
"border-collapse": "separate",
"border-color": "#000000",
"border-left": "0px #000000",
"border-left-color": "#000000",
"border-left-style": "none",
"border-left-width": "0px",
"border-right": "0px #000000",
"border-right-color": "#000000",
"border-right-style": "none",
"border-right-width": "0px",
"border-spacing": "0px",
"border-style": "none",
"border-top": "0px #000000",
"border-top-color": "#000000",
"border-top-style": "none",
"border-top-width": "0px",
"border-width": "0px",
"bottom": "auto",
"box-sizing": "",
"caption-side": "top",
"cell-spacing": "",
"clear": "none",
"clip": "rect(0px, 0px, 0px, 0px)",
"clip-path": "",
"clip-rule": "",
"color": "#000000",
"color-interpolation": "",
"color-interpolation-filters": "",
"color-profile": "",
"color-rendering": "",
"column-span": "",
"content": "none",
"counter-increment": "none",
"counter-reset": "none",
"cue": "",
"cue-after": "",
"cue-before": "",
"cursor": "default",
"direction": "ltr",
"display": "inline",
"display-align": "",
"dominant-baseline": "",
"empty-cells": "show",
"enable-background": "",
"fill": "#000000",
"fill-opacity": "",
"fill-rule": "",
"filter": "",
"float": "none",
"flood-color": "",
"flood-opacity": "",
"font": "shorthand",
"font-family": "Times New Roman",
"font-size": "16px",
"font-size-adjust": "none",
"font-stretch": "normal",
"font-style": "normal",
"font-variant": "normal",
"font-weight": "400",
"glyph-orientation-horizontal": "",
"glyph-orientation-vertical": "",
"height": "auto",
"image-rendering": "",
"kerning": "",
"left": "auto",
"letter-spacing": "0px",
"lighting-color": "",
"line-height": "normal",
"line-increment": "",
"list-style": "disc outside none",
"list-style-image": "none",
"list-style-position": "outside",
"list-style-type": "disc",
"margin": "0px",
"margin-bottom": "0px",
"margin-left": "0px",
"margin-right": "0px",
"margin-top": "0px",
"mark": "",
"mark-after": "",
"mark-before": "",
"marker": "",
"marker-end": "",
"marker-mid": "",
"marker-offset": "",
"marker-start": "",
"marks": "none",
"mask": "none",
"max-height": "-1px",
"max-width": "-1px",
"min-height": "0px",
"min-width": "0px",
"nav-down": "",
"nav-index": "",
"nav-left": "",
"nav-right": "",
"nav-up": "",
"opacity": "1",
"orphans": "2",
"outline": "3px",
"outline-color": "invert",
"outline-offset": "",
"outline-style": "none",
"outline-width": "3px",
"overflow": "visible",
"overflow-x": "visible",
"overflow-y": "visible",
"padding": "0px",
"padding-bottom": "0px",
"padding-left": "0px",
"padding-right": "0px",
"padding-top": "0px",
"page": "auto",
"page-break-after": "auto",
"page-break-before": "auto",
"page-break-inside": "auto",
"pause": "",
"pause-after": "",
"pause-before": "",
"pointer-events": "",
"position": "static",
"quotes": "none",
"rest": "",
"rest-after": "",
"rest-before": "",
"right": "auto",
"row-span": "",
"scrollbar-3dlight-color": "",
"scrollbar-arrow-color": "",
"scrollbar-base-color": "",
"scrollbar-darkshadow-color": "",
"scrollbar-face-color": "",
"scrollbar-highlight-color": "",
"scrollbar-shadow-color": "",
"scrollbar-track-color": "",
"shape-rendering": "",
"size": "portrait",
"solid-color": "",
"solid-opacity": "",
"speak": "",
"stop-color": "",
"stop-opacity": "",
"stroke": "",
"stroke-dasharray": "",
"stroke-dashoffset": "",
"stroke-linecap": "",
"stroke-linejoin": "",
"stroke-miterlimit": "",
"stroke-opacity": "",
"stroke-width": "",
"table-layout": "auto",
"text-align": "left",
"text-anchor": "",
"text-decoration": "none",
"text-indent": "0px",
"text-rendering": "",
"text-shadow": "",
"text-transform": "none",
"top": "auto",
"unicode-bidi": "normal",
"vector-effect": "",
"vertical-align": "baseline",
"viewport-fill": "",
"viewport-fill-opacity": "",
"visibility": "visible",
"voice-family": "",
"white-space": "normal",
"widows": "2",
"width": "auto",
"word-spacing": "0px",
"writing-mode": "",
"z-index": "auto"
}<commit_msg>comment nitpicking about css initial value ( and testing commit in hg )<commit_after>/**
 * @fileoverview
 * This file contains a lookup table of css properties, the value of the
 * property contains the initial value ( default value is defined in the browser.css )
 * used by Opera if ther is no CSS defined
 * by the author. This is used in the CSS inspector
 * @see stylesheets
 */

css_initial_values =
{
"-apple-dashboard-region": "",
"-o-background-size": "",
"-o-link": "",
"-o-link-source": "",
"-o-table-baseline": "",
"-o-text-overflow": "",
"-wap-accesskey": "",
"-wap-input-format": "",
"-wap-input-required": "",
"-wap-marquee-dir": "",
"-wap-marquee-loop": "",
"-wap-marquee-speed": "",
"-wap-marquee-style": "",
"-xv-interpret-as": "",
"-xv-phonemes": "",
"-xv-voice-balance": "",
"-xv-voice-duration": "",
"-xv-voice-pitch": "",
"-xv-voice-pitch-range": "",
"-xv-voice-rate": "",
"-xv-voice-stress": "",
"-xv-voice-volume": "",
"alignment-baseline": "",
"audio-level": "",
"background": "transparent none repeat scroll 0px 0px",
"background-attachment": "scroll",
"background-color": "transparent",
"background-image": "none",
"background-position": "0px 0px",
"background-repeat": "repeat",
"baseline-shift": "",
"border": "0px #000000",
"border-bottom": "0px #000000",
"border-bottom-color": "#000000",
"border-bottom-style": "none",
"border-bottom-width": "0px",
"border-collapse": "separate",
"border-color": "#000000",
"border-left": "0px #000000",
"border-left-color": "#000000",
"border-left-style": "none",
"border-left-width": "0px",
"border-right": "0px #000000",
"border-right-color": "#000000",
"border-right-style": "none",
"border-right-width": "0px",
"border-spacing": "0px",
"border-style": "none",
"border-top": "0px #000000",
"border-top-color": "#000000",
"border-top-style": "none",
"border-top-width": "0px",
"border-width": "0px",
"bottom": "auto",
"box-sizing": "",
"caption-side": "top",
"cell-spacing": "",
"clear": "none",
"clip": "rect(0px, 0px, 0px, 0px)",
"clip-path": "",
"clip-rule": "",
"color": "#000000",
"color-interpolation": "",
"color-interpolation-filters": "",
"color-profile": "",
"color-rendering": "",
"column-span": "",
"content": "none",
"counter-increment": "none",
"counter-reset": "none",
"cue": "",
"cue-after": "",
"cue-before": "",
"cursor": "default",
"direction": "ltr",
"display": "inline",
"display-align": "",
"dominant-baseline": "",
"empty-cells": "show",
"enable-background": "",
"fill": "#000000",
"fill-opacity": "",
"fill-rule": "",
"filter": "",
"float": "none",
"flood-color": "",
"flood-opacity": "",
"font": "shorthand",
"font-family": "Times New Roman",
"font-size": "16px",
"font-size-adjust": "none",
"font-stretch": "normal",
"font-style": "normal",
"font-variant": "normal",
"font-weight": "400",
"glyph-orientation-horizontal": "",
"glyph-orientation-vertical": "",
"height": "auto",
"image-rendering": "",
"kerning": "",
"left": "auto",
"letter-spacing": "0px",
"lighting-color": "",
"line-height": "normal",
"line-increment": "",
"list-style": "disc outside none",
"list-style-image": "none",
"list-style-position": "outside",
"list-style-type": "disc",
"margin": "0px",
"margin-bottom": "0px",
"margin-left": "0px",
"margin-right": "0px",
"margin-top": "0px",
"mark": "",
"mark-after": "",
"mark-before": "",
"marker": "",
"marker-end": "",
"marker-mid": "",
"marker-offset": "",
"marker-start": "",
"marks": "none",
"mask": "none",
"max-height": "-1px",
"max-width": "-1px",
"min-height": "0px",
"min-width": "0px",
"nav-down": "",
"nav-index": "",
"nav-left": "",
"nav-right": "",
"nav-up": "",
"opacity": "1",
"orphans": "2",
"outline": "3px",
"outline-color": "invert",
"outline-offset": "",
"outline-style": "none",
"outline-width": "3px",
"overflow": "visible",
"overflow-x": "visible",
"overflow-y": "visible",
"padding": "0px",
"padding-bottom": "0px",
"padding-left": "0px",
"padding-right": "0px",
"padding-top": "0px",
"page": "auto",
"page-break-after": "auto",
"page-break-before": "auto",
"page-break-inside": "auto",
"pause": "",
"pause-after": "",
"pause-before": "",
"pointer-events": "",
"position": "static",
"quotes": "none",
"rest": "",
"rest-after": "",
"rest-before": "",
"right": "auto",
"row-span": "",
"scrollbar-3dlight-color": "",
"scrollbar-arrow-color": "",
"scrollbar-base-color": "",
"scrollbar-darkshadow-color": "",
"scrollbar-face-color": "",
"scrollbar-highlight-color": "",
"scrollbar-shadow-color": "",
"scrollbar-track-color": "",
"shape-rendering": "",
"size": "portrait",
"solid-color": "",
"solid-opacity": "",
"speak": "",
"stop-color": "",
"stop-opacity": "",
"stroke": "",
"stroke-dasharray": "",
"stroke-dashoffset": "",
"stroke-linecap": "",
"stroke-linejoin": "",
"stroke-miterlimit": "",
"stroke-opacity": "",
"stroke-width": "",
"table-layout": "auto",
"text-align": "left",
"text-anchor": "",
"text-decoration": "none",
"text-indent": "0px",
"text-rendering": "",
"text-shadow": "",
"text-transform": "none",
"top": "auto",
"unicode-bidi": "normal",
"vector-effect": "",
"vertical-align": "baseline",
"viewport-fill": "",
"viewport-fill-opacity": "",
"visibility": "visible",
"voice-family": "",
"white-space": "normal",
"widows": "2",
"width": "auto",
"word-spacing": "0px",
"writing-mode": "",
"z-index": "auto"
}<|endoftext|>


<commit_before>/*
 * Collapsible widget extension: set
 * https://github.com/filamentgroup/collapsible
 * Copyright (c) 2014 Filament Group, Inc.
 * Licensed under the MIT, GPL licenses.
 */

;(function ($, window, document, undefined) {
	var pluginName = "collapsible";

	// Simple auto-init by selector that runs when the dom is ready. Trigger "enhance" if desirable.
	$( document ).bind( "expand." + pluginName, function( e ){
		var setAttr = "data-" + pluginName + "-set";
		var selector = "." + pluginName + "[" + setAttr + "]";
		var $collapsible = $( e.target );
		if( $collapsible.is( selector ) ){
			var value = $collapsible.attr( setAttr );
			var $set = $( "." + pluginName + "-enhanced[" + setAttr + "='" + value + "']" ).filter(function() {
				return this !== $collapsible[0];
			});

			$set.each(function(){
				$( this ).data( pluginName ).collapse();
			});
			var openItemTop = e.target.offsetTop;
			// from jquery...
			var scroll =  (function() {
				var prop = 'pageYOffset',
					method = 'scrollTop';
				return window ? (prop in window) ? window[ prop ] :
					window.document.documentElement[ method ] :
					window.document.body[ method ];
			}());

			if( scroll > openItemTop ){
				window.scrollTo( 0, openItemTop );
			}
		}
	});

})(jQuery, window, document);
<commit_msg>check if data is present before calling collapse<commit_after>/*
 * Collapsible widget extension: set
 * https://github.com/filamentgroup/collapsible
 * Copyright (c) 2014 Filament Group, Inc.
 * Licensed under the MIT, GPL licenses.
 */

;(function ($, window, document, undefined) {
	var pluginName = "collapsible";

	// Simple auto-init by selector that runs when the dom is ready. Trigger "enhance" if desirable.
	$( document ).bind( "expand." + pluginName, function( e ){
		var setAttr = "data-" + pluginName + "-set";
		var selector = "." + pluginName + "[" + setAttr + "]";
		var $collapsible = $( e.target );
		if( $collapsible.is( selector ) ){
			var value = $collapsible.attr( setAttr );
			var $set = $( "." + pluginName + "-enhanced[" + setAttr + "='" + value + "']" ).filter(function() {
				return this !== $collapsible[0];
			});

			$set.each(function(){
				var thisData = $( this ).data( pluginName );
				if( thisData ){
					thisData.collapse();
				}
			});
			var openItemTop = e.target.offsetTop;
			// from jquery...
			var scroll =  (function() {
				var prop = 'pageYOffset',
					method = 'scrollTop';
				return window ? (prop in window) ? window[ prop ] :
					window.document.documentElement[ method ] :
					window.document.body[ method ];
			}());

			if( scroll > openItemTop ){
				window.scrollTo( 0, openItemTop );
			}
		}
	});

})(jQuery, window, document);
<|endoftext|>