<commit_before>/* eslint-env mocha */
'use strict'

const expect = require('chai').expect
const Block = require('../src').Block
const BlockService = require('../src').BlockService

module.exports = (repo) => {
  describe('block-service', () => {
    let bs

    it('create a block-service', (done) => {
      bs = new BlockService(repo)
      expect(bs).to.exist
      done()
    })

    it('store and get a block', (done) => {
      const b = new Block('A random data block')
      bs.addBlock(b, (err) => {
        expect(err).to.not.exist
        bs.getBlock(b.key, (err, block) => {
          expect(err).to.not.exist
          expect(b.data.equals(block.data)).to.equal(true)
          expect(b.key.equals(block.key)).to.equal(true)
          done()
        })
      })
    })

    it('store and get a block, with custom extension', (done) => {
      const b = new Block('A random data block', 'ext')
      bs.addBlock(b, (err) => {
        expect(err).to.not.exist
        bs.getBlock(b.key, 'ext', (err, block) => {
          expect(err).to.not.exist
          expect(b.data.equals(block.data)).to.equal(true)
          expect(b.key.equals(block.key)).to.equal(true)
          done()
        })
      })
    })

    it('get a non existent block', (done) => {
      const b = new Block('Not stored')
      bs.getBlock(b.key, (err, block) => {
        expect(err).to.exist
        done()
      })
    })

    it('store many blocks', (done) => {
      const b1 = new Block('1')
      const b2 = new Block('2')
      const b3 = new Block('3')

      bs.addBlocks([b1, b2, b3], (err) => {
        expect(err).to.not.exist
        done()
      })
    })

    it('addBlocks: bad invocation', (done) => {
      const b1 = new Block('1')

      bs.addBlocks(b1, (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })

    it('getBlock: bad invocation', (done) => {
      bs.getBlock(null, (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })

    it('getBlocks: bad invocation', (done) => {
      bs.getBlocks(null, 'protobuf', (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })

    it('get many blocks', (done) => {
      const b1 = new Block('1')
      const b2 = new Block('2')
      const b3 = new Block('3')

      bs.addBlocks([b1, b2, b3], (err) => {
        expect(err).to.not.exist

        bs.getBlocks([b1.key, b2.key, b3.key], (err, blocks) => {
          expect(err).to.not.exist
          expect(blocks).to.have.lengthOf(3)
          done()
        })
      })
    })

    it('get many blocks: partial success', (done) => {
      const b1 = new Block('a1')
      const b2 = new Block('a2')
      const b3 = new Block('a3')

      bs.addBlocks([b1, b3], (err) => {
        expect(err).to.not.exist

        bs.getBlocks([b1.key, b2.key, b3.key], (err, blocks) => {
          expect(err).to.exist
          expect(blocks).to.have.lengthOf(0)
          done()
        })
      })
    })

    it('delete a block', (done) => {
      const b = new Block('Will not live that much')
      bs.addBlock(b, (err) => {
        expect(err).to.not.exist
        bs.deleteBlock(b.key, (err) => {
          expect(err).to.not.exist
          bs.getBlock(b.key, (err, block) => {
            expect(err).to.exist
            done()
          })
        })
      })
    })

    it('deleteBlock: bad invocation', (done) => {
      bs.deleteBlock(null, (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })

    it('delete a block, with custom extension', (done) => {
      const b = new Block('Will not live that much', 'ext')
      bs.addBlock(b, (err) => {
        expect(err).to.not.exist
        bs.deleteBlock(b.key, 'ext', (err) => {
          expect(err).to.not.exist
          bs.getBlock(b.key, 'ext', (err, block) => {
            expect(err).to.exist
            done()
          })
        })
      })
    })

    it('delete a non existent block', (done) => {
      const b = new Block('I do not exist')
      bs.deleteBlock(b.key, (err) => {
        expect(err).to.not.exist
        done()
      })
    })

    it('delete many blocks', (done) => {
      const b1 = new Block('1')
      const b2 = new Block('2')
      const b3 = new Block('3')

      bs.deleteBlocks([b1, b2, b3], 'data', (err) => {
        expect(err).to.not.exist
        done()
      })
    })

    it('deleteBlocks: bad invocation', (done) => {
      bs.deleteBlocks(null, (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })
  })
}
<commit_msg>Handles async properly in test.<commit_after>/* eslint-env mocha */
'use strict'

const expect = require('chai').expect
const Block = require('../src').Block
const BlockService = require('../src').BlockService

module.exports = (repo) => {
  describe('block-service', () => {
    let bs

    it('create a block-service', (done) => {
      bs = new BlockService(repo)
      expect(bs).to.exist
      done()
    })

    it('store and get a block', (done) => {
      const b = new Block('A random data block')
      bs.addBlock(b, (err) => {
        expect(err).to.not.exist
        bs.getBlock(b.key, (err, block) => {
          expect(err).to.not.exist
          expect(b.data.equals(block.data)).to.equal(true)
          expect(b.key.equals(block.key)).to.equal(true)
          done()
        })
      })
    })

    it('store and get a block, with custom extension', (done) => {
      const b = new Block('A random data block', 'ext')
      bs.addBlock(b, (err) => {
        expect(err).to.not.exist
        bs.getBlock(b.key, 'ext', (err, block) => {
          expect(err).to.not.exist
          expect(b.data.equals(block.data)).to.equal(true)
          expect(b.key.equals(block.key)).to.equal(true)
          done()
        })
      })
    })

    it('get a non existent block', (done) => {
      const b = new Block('Not stored')
      bs.getBlock(b.key, (err, block) => {
        expect(err).to.exist
        done()
      })
    })

    it('store many blocks', (done) => {
      const b1 = new Block('1')
      const b2 = new Block('2')
      const b3 = new Block('3')

      bs.addBlocks([b1, b2, b3], (err) => {
        expect(err).to.not.exist
        done()
      })
    })

    it('addBlocks: bad invocation', (done) => {
      const b1 = new Block('1')

      bs.addBlocks(b1, (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })

    it('getBlock: bad invocation', (done) => {
      bs.getBlock(null, (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })

    it('getBlocks: bad invocation', (done) => {
      bs.getBlocks(null, 'protobuf', (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })

    it('get many blocks', (done) => {
      const b1 = new Block('1')
      const b2 = new Block('2')
      const b3 = new Block('3')

      bs.addBlocks([b1, b2, b3], (err) => {
        expect(err).to.not.exist

        bs.getBlocks([b1.key, b2.key, b3.key], (err, blocks) => {
          expect(err).to.not.exist
          expect(blocks).to.have.lengthOf(3)
          done()
        })
      })
    })

    it('get many blocks: partial success', (done) => {
      const b1 = new Block('a1')
      const b2 = new Block('a2')
      const b3 = new Block('a3')

      bs.addBlocks([b1, b3], (err) => {
        expect(err).to.not.exist

        bs.getBlocks([b1.key, b2.key, b3.key], (err, blocks) => {
          expect(err).to.exist
          expect(blocks).to.have.length.below(3)
          done()
        })
      })
    })

    it('delete a block', (done) => {
      const b = new Block('Will not live that much')
      bs.addBlock(b, (err) => {
        expect(err).to.not.exist
        bs.deleteBlock(b.key, (err) => {
          expect(err).to.not.exist
          bs.getBlock(b.key, (err, block) => {
            expect(err).to.exist
            done()
          })
        })
      })
    })

    it('deleteBlock: bad invocation', (done) => {
      bs.deleteBlock(null, (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })

    it('delete a block, with custom extension', (done) => {
      const b = new Block('Will not live that much', 'ext')
      bs.addBlock(b, (err) => {
        expect(err).to.not.exist
        bs.deleteBlock(b.key, 'ext', (err) => {
          expect(err).to.not.exist
          bs.getBlock(b.key, 'ext', (err, block) => {
            expect(err).to.exist
            done()
          })
        })
      })
    })

    it('delete a non existent block', (done) => {
      const b = new Block('I do not exist')
      bs.deleteBlock(b.key, (err) => {
        expect(err).to.not.exist
        done()
      })
    })

    it('delete many blocks', (done) => {
      const b1 = new Block('1')
      const b2 = new Block('2')
      const b3 = new Block('3')

      bs.deleteBlocks([b1, b2, b3], 'data', (err) => {
        expect(err).to.not.exist
        done()
      })
    })

    it('deleteBlocks: bad invocation', (done) => {
      bs.deleteBlocks(null, (err) => {
        expect(err).to.be.an('error')
        done()
      })
    })
  })
}
<|endoftext|>


<commit_before>/**
* Extension work: Addy Osmani. I want to create a getUserMedia.js polyfill.
* Initial cam plugin by Robert Eisele (robert@xarg.org)
**/

/*
recompiling the flash binaries:

apt-get install swfmill mtasc
vim src/jscam.xml
make
*/
(function ($) {

    var webcam = {

	extern: null, // external select token to support jQuery dialogs
	append: true, // append object instead of overwriting

	width: 320,
	height: 240,

	mode: "callback", // callback | save | stream

	swffile: "resource/jscam.swf",
	quality: 85,

	debug:	    function () {},
	onCapture:  function () {},
	onTick:	    function () {},
	onSave:	    function () {},
	onLoad:	    function () {}    //The onLoad callback is called as soon as the registration of the interface is done
    };

    window.webcam = webcam;

    $.fn.webcam = function(options) {

	if (typeof options === "object") {
	    for (var ndx in webcam) {
		if (options[ndx] !== undefined) {
		    webcam[ndx] = options[ndx];
		}
	    }
	}

	var source = '<object id="XwebcamXobjectX" type="application/x-shockwave-flash" data="'+webcam.swffile+'" width="'+webcam.width+'" height="'+webcam.height+'"><param name="movie" value="'+webcam.swffile+'" /><param name="FlashVars" value="mode='+webcam.mode+'&quality='+webcam.quality+'" /><param name="allowScriptAccess" value="always" /></object>';

	if (null !== webcam.extern) {
	    $(webcam.extern)[webcam.append ? "append" : "html"](source);
	} else {
	    this[webcam.append ? "append" : "html"](source);
	}

	(_register = function(run) {

	    var cam = document.getElementById('XwebcamXobjectX');

	    if (cam.capture !== undefined) {

		/* Simple callback methods are not allowed :-/ */
		webcam.capture = function(x) {
		    try {
			return cam.capture(x);
		    } catch(e) {}
		}
		webcam.save = function(x) {
		    try {
			return cam.save(x);
		    } catch(e) {}
		}
		webcam.setCamera = function(x) {
		    try {
			return cam.setCamera(x);
		    } catch(e) {}
		}
		webcam.getCameraList = function() {
		    try {
			return cam.getCameraList();
		    } catch(e) {}
		}

		webcam.onLoad();
	    } else if (0 == run) {
		webcam.debug("error", "Flash movie not yet registered!");
	    } else {
		/* Flash interface not ready yet */
		window.setTimeout(_register, 1000 * (4 - run), run - 1);
	    }
	})(3);
    }

})(jQuery);
<commit_msg>comments update<commit_after>/**
* Extension work: Addy Osmani. I want to create a getUserMedia.js polyfill.
* Initial cam plugin by Robert Eisele (robert@xarg.org)
**/

/*
recompiling the flash binaries:
apt-get install swfmill mtasc
vim src/jscam.xml
make
*/
(function ($) {

    var webcam = {

	extern: null, // external select token to support jQuery dialogs
	append: true, // append object instead of overwriting

	width: 320,
	height: 240,

	mode: "callback", // callback | save | stream

	swffile: "resource/jscam.swf",
	quality: 85,

	debug:	    function () {},
	onCapture:  function () {},
	onTick:	    function () {},
	onSave:	    function () {},
	onLoad:	    function () {}    //The onLoad callback is called as soon as the registration of the interface is done
    };

    window.webcam = webcam;

    $.fn.webcam = function(options) {

	if (typeof options === "object") {
	    for (var ndx in webcam) {
		if (options[ndx] !== undefined) {
		    webcam[ndx] = options[ndx];
		}
	    }
	}

	var source = '<object id="XwebcamXobjectX" type="application/x-shockwave-flash" data="'+webcam.swffile+'" width="'+webcam.width+'" height="'+webcam.height+'"><param name="movie" value="'+webcam.swffile+'" /><param name="FlashVars" value="mode='+webcam.mode+'&quality='+webcam.quality+'" /><param name="allowScriptAccess" value="always" /></object>';

	if (null !== webcam.extern) {
	    $(webcam.extern)[webcam.append ? "append" : "html"](source);
	} else {
	    this[webcam.append ? "append" : "html"](source);
	}

	(_register = function(run) {

	    var cam = document.getElementById('XwebcamXobjectX');

	    if (cam.capture !== undefined) {

		/* Simple callback methods are not allowed :-/ */
		webcam.capture = function(x) {
		    try {
			return cam.capture(x);
		    } catch(e) {}
		}
		webcam.save = function(x) {
		    try {
			return cam.save(x);
		    } catch(e) {}
		}
		webcam.setCamera = function(x) {
		    try {
			return cam.setCamera(x);
		    } catch(e) {}
		}
		webcam.getCameraList = function() {
		    try {
			return cam.getCameraList();
		    } catch(e) {}
		}

		webcam.onLoad();
	    } else if (0 == run) {
		webcam.debug("error", "Flash movie not yet registered!");
	    } else {
		/* Flash interface not ready yet */
		window.setTimeout(_register, 1000 * (4 - run), run - 1);
	    }
	})(3);
    }

})(jQuery);
<|endoftext|>


<commit_before>'use strict';

describe('chain context tests', function() {
  var contextUtils = require('../src/chain_context');
  var utils = require('../src/utils');

  it ('should create a first chain context with a local context', function () {
    var firstChain = contextUtils.extendChainContext({name:'test', age: 8});
    expect(firstChain).to.not.be.undefined;
    expect(firstChain.currentIndex).to.equals(0);
    expect(firstChain[0]).to.not.be.undefined;
  });

  it ('should create a second chain context with local contexts', function () {
    var firstChain = contextUtils.extendChainContext({name:'test', age: 8});
    var secondChain = contextUtils.extendChainContext({max: 8, min: 16}, firstChain);
    expect(secondChain).to.not.be.undefined;
    expect(firstChain.currentIndex).to.equals(0);
    expect(secondChain.currentIndex).to.equals(1);
    expect(secondChain[0]).to.not.be.undefined;
    expect(secondChain[1]).to.not.be.undefined;
  });

  it ('should create a first chain context without a local context', function () {
    var firstChain = contextUtils.extendChainContext();
    expect(firstChain).to.not.be.undefined;
    expect(firstChain.currentIndex).to.equals(0);
    expect(firstChain[0]).to.be.undefined;
  });

  it ('should create a second chain context without local contexts', function () {
    var firstChain = contextUtils.extendChainContext();
    var secondChain = contextUtils.extendChainContext(null, firstChain);
    expect(secondChain).to.not.be.undefined;
    expect(firstChain.currentIndex).to.equals(0);
    expect(secondChain.currentIndex).to.equals(1);
    expect(secondChain[0]).to.be.undefined;
    expect(secondChain[1]).to.be.undefined;
  });

  describe('retrieve local context', function () {
    var firstLocalContext = {
      max: 8,
      min: 2
    };
    var secondContext = null;
    var thirdContext = {
      minimize: function (e) {
        return Math.min(e, 4);
      }
    }

    var firstChain = contextUtils.extendChainContext(firstLocalContext);
    var secondChain = contextUtils.extendChainContext(secondContext, firstChain);
    var thirdChain = contextUtils.extendChainContext(thirdContext, secondChain);

    it ('should retrieve first context from first chain', function () {
      var c = contextUtils.currentContextFromChainContext(firstChain);
      expect(c).to.not.be.undefined;
      expect(c.max).to.equals(8);
      expect(c.min).to.equals(2);
    });

    it ('should retrieve second context from second chain', function () {
      var c = contextUtils.currentContextFromChainContext(secondChain);
      expect(c).to.be.undefined;
    });

    it ('should retrieve third context from third chain', function () {
      var c = contextUtils.currentContextFromChainContext(thirdChain);
      expect(c).to.not.be.undefined;
      expect(c.minimize).to.not.be.undefined;
      expect(c.minimize(5)).to.equals(4);
      expect(c.minimize(3)).to.equals(3);
      expect(c.minimize(1)).to.equals(1);
    });

    it ('should should generate a sibling chain from second chain', function () {
      var thirdContextBis = {
        maximize: function (e) {
          return Math.min(e, 4);
        }
      }
      var thirdChainBis = contextUtils.extendChainContext(thirdContextBis, secondChain);

      var c = contextUtils.currentContextFromChainContext(thirdChain);
      expect(c).to.not.be.undefined;
      expect(c.minimize).to.not.be.undefined;
      expect(c.minimize(5)).to.equals(4);
      expect(c.minimize(3)).to.equals(3);
      expect(c.minimize(1)).to.equals(1);

      var cBis = contextUtils.currentContextFromChainContext(thirdChainBis);
      expect(cBis).to.not.be.undefined;
      expect(cBis.maximize).to.not.be.undefined;
      expect(cBis.maximize(5)).to.equals(5);
      expect(cBis.maximize(3)).to.equals(4);
      expect(cBis.maximize(1)).to.equals(4);
    });
  });

  describe('expand contexts with functions', function () {
    var chainContext;
    var chainFirstLocalContext;

    before(function () {
      var localContext = {
        minimize: function (e) {
          return Math.min(e, 4);
        },
        adder: function (a, b) {
          return Math.min(e, 4);
        },
        random: function () {
          return Math.random();
        },
        foo: function (a, b, c, d, e, f) {
          return a + b + c * d / e + f;
        }
      }
      chainContext = contextUtils.extendChainContext(localContext);
      chainFirstLocalContext = chainContext[0];
    });

    it ('should parse functions on local context', function () {
      var c = chainFirstLocalContext;
      expect(utils.isFunction(c.minimize)).to.be.false;
      expect(utils.isFunction(c.adder)).to.be.false;
      expect(utils.isFunction(c.random)).to.be.false;
      expect(utils.isFunction(c.foo)).to.be.false;
    });

    it ('should obtain functions on local context', function () {
      var c = contextUtils.currentContextFromChainContext(chainContext);
      expect(utils.isFunction(c.minimize)).to.be.true;
      expect(utils.isFunction(c.adder)).to.be.true;
      expect(utils.isFunction(c.random)).to.be.true;
      expect(utils.isFunction(c.foo)).to.be.true;
    });
  });
});<commit_msg>[6.4.1] fixes chain context tests.<commit_after>'use strict';

describe('chain context tests', function() {
  var contextUtils = require('../src/chain_context');
  var utils = require('../src/utils');

  it ('should create a first chain context with a local context', function () {
    var firstChain = contextUtils.extendChainContext({name:'test', age: 8});
    expect(firstChain).to.not.be.undefined;
    expect(firstChain.currentIndex).to.equals(0);
    expect(firstChain[0]).to.not.be.undefined;
  });

  it ('should create a second chain context with local contexts', function () {
    var firstChain = contextUtils.extendChainContext({name:'test', age: 8});
    var secondChain = contextUtils.extendChainContext({max: 8, min: 16}, firstChain);
    expect(secondChain).to.not.be.undefined;
    expect(firstChain.currentIndex).to.equals(0);
    expect(secondChain.currentIndex).to.equals(1);
    expect(secondChain[0]).to.not.be.undefined;
    expect(secondChain[1]).to.not.be.undefined;
  });

  it ('should create a first chain context without a local context', function () {
    var firstChain = contextUtils.extendChainContext();
    expect(firstChain).to.not.be.undefined;
    expect(firstChain.currentIndex).to.equals(0);
    expect(firstChain[0]).to.be.undefined;
  });

  it ('should create a second chain context without local contexts', function () {
    var firstChain = contextUtils.extendChainContext();
    var secondChain = contextUtils.extendChainContext(null, firstChain);
    expect(secondChain).to.not.be.undefined;
    expect(firstChain.currentIndex).to.equals(0);
    expect(secondChain.currentIndex).to.equals(1);
    expect(secondChain[0]).to.be.undefined;
    expect(secondChain[1]).to.be.undefined;
  });

  describe('retrieve local context', function () {
    var firstLocalContext = {
      max: 8,
      min: 2
    };
    var secondContext = null;
    var thirdContext = {
      minimize: function (e) {
        return Math.min(e, 4);
      }
    }

    var firstChain = contextUtils.extendChainContext(firstLocalContext);
    var secondChain = contextUtils.extendChainContext(secondContext, firstChain);
    var thirdChain = contextUtils.extendChainContext(thirdContext, secondChain);

    it ('should retrieve first context from first chain', function () {
      var c = contextUtils.currentContextFromChainContext(firstChain);
      expect(c).to.not.be.undefined;
      expect(c.max).to.equals(8);
      expect(c.min).to.equals(2);
    });

    it ('should retrieve second context from second chain', function () {
      var c = contextUtils.currentContextFromChainContext(secondChain);
      expect(c).to.be.undefined;
    });

    it ('should retrieve third context from third chain', function () {
      var c = contextUtils.currentContextFromChainContext(thirdChain);
      expect(c).to.not.be.undefined;
      expect(c.minimize).to.not.be.undefined;
      expect(c.minimize(5)).to.equals(4);
      expect(c.minimize(3)).to.equals(3);
      expect(c.minimize(1)).to.equals(1);
    });

    it ('should should generate a sibling chain from second chain', function () {
      var thirdContextBis = {
        maximize: function (e) {
          return Math.max(e, 4);
        }
      }
      var thirdChainBis = contextUtils.extendChainContext(thirdContextBis, secondChain);

      var c = contextUtils.currentContextFromChainContext(thirdChain);
      expect(c).to.not.be.undefined;
      expect(c.minimize).to.not.be.undefined;
      expect(c.minimize(5)).to.equals(4);
      expect(c.minimize(3)).to.equals(3);
      expect(c.minimize(1)).to.equals(1);

      var cBis = contextUtils.currentContextFromChainContext(thirdChainBis);
      expect(cBis).to.not.be.undefined;
      expect(cBis.maximize).to.not.be.undefined;
      expect(cBis.maximize(5)).to.equals(5);
      expect(cBis.maximize(3)).to.equals(4);
      expect(cBis.maximize(1)).to.equals(4);
    });
  });

  describe('expand contexts with functions', function () {
    var chainContext;
    var chainFirstLocalContext;

    before(function () {
      var localContext = {
        minimize: function (e) {
          return Math.min(e, 4);
        },
        adder: function (a, b) {
          return Math.min(e, 4);
        },
        random: function () {
          return Math.random();
        },
        foo: function (a, b, c, d, e, f) {
          return a + b + c * d / e + f;
        }
      }
      chainContext = contextUtils.extendChainContext(localContext);
      chainFirstLocalContext = chainContext[0];
    });

    it ('should parse functions on local context', function () {
      var c = chainFirstLocalContext;
      expect(utils.isFunction(c.minimize)).to.be.false;
      expect(utils.isFunction(c.adder)).to.be.false;
      expect(utils.isFunction(c.random)).to.be.false;
      expect(utils.isFunction(c.foo)).to.be.false;
    });

    it ('should obtain functions on local context', function () {
      var c = contextUtils.currentContextFromChainContext(chainContext);
      expect(utils.isFunction(c.minimize)).to.be.true;
      expect(utils.isFunction(c.adder)).to.be.true;
      expect(utils.isFunction(c.random)).to.be.true;
      expect(utils.isFunction(c.foo)).to.be.true;
    });
  });
});<|endoftext|>


<commit_before>findPlayer = require("./findPlayer.js")

module.exports = function(controller, io) {
	controller.hears(['inventory'], "direct_message", function(bot, message) {
		controller.storage.users.get(message.user, function(err, user_game) {
			if (user_game.gameActive == true) {
				bot.reply(message, printPlayerInventory(user_game.map))
				controller.storage.users.save({
					id: message.user,
					map: user_game.map,
					gameActive: true,
					ritual: user_game.ritual,
					ritual_progress: user_game.ritual_progress,
					level: user_game.level
				})
			} else {
				bot.reply(message, "you don't have a new game started! Start one with `new game`")
			}
		})
	})
	controller.hears(["pick up (.*)", "take (.*)", "grab (.*)", "get (.*)", "collect (.*)"], "direct_message", function(bot, message) {
		controller.storage.users.get(message.user, function(err, user_game) {
			if (user_game.gameActive == true) {
				bot.reply(message, attemptToGet(message.match[1], user_game.map, user_game.ritual_progress, io, message.user))
				controller.storage.users.save({
					id: message.user,
					map: user_game.map,
					gameActive: true,
					ritual: user_game.ritual,
					ritual_progress: user_game.ritual_progress,
					level: user_game.level
				})
			} else {
				bot.reply(message, "you don't have a new game started! Start one with `new game`")
			}
		})
	})
	controller.hears(["drop (.*) on floor", "drop (.*) on ground", "place (.*) on floor", "place (.*) on ground", "put (.*) on floor", "put (.*) on ground", "drop (.*)"], "direct_message", function(bot, message) {
		controller.storage.users.get(message.user, function(err, user_game) {
			if (user_game.gameActive == true) {
				bot.reply(message, attemptToDrop(message.match[1], user_game.map, user_game.ritual_progress, io, message.user))
				controller.storage.users.save({
					id: message.user,
					map: user_game.map,
					gameActive: true,
					ritual: user_game.ritual,
					ritual_progress: user_game.ritual_progress,
					level: user_game.level
				})
			} else {
				bot.reply(message, "you don't have a new game started! Start one with `new game`")
			}
		})

	})
}

function attemptToGet(itemString, map, ritual_progress, io, user) {
	playerLocation = findPlayer(map)
	playerObject = null
	for (object of map[playerLocation.x][playerLocation.y].objects) {
		if (object.id == "player") {
			playerObject = object
		}
	}
	thingsInRoom = map[playerLocation.x][playerLocation.y].objects
	for (var i = 0; i < thingsInRoom.length; i++) {
		if (thingsInRoom[i].displayName.includes(itemString)) {
			if (!thingsInRoom[i].pickuppable) {
				return {
					text: "You can't pick up " + thingsInRoom[i].displayName + "!"
				}
			} else {
				// pop off room objects list, push onto player inventory list
				item = thingsInRoom.splice(i, 1)[0]
				putInInventory(item, map)
				io.emit("play_audio", {
					for: user,
					effect: "pickup"
				})
				return {
					text: "You pick up " + playerObject.inventory[playerObject.inventory.length - 1].displayName + " and put it in your pack."
				}
			}
		}
		if (thingsInRoom[i].id == "altar") {
			for (var j = 0; j < thingsInRoom[i].inventory.length; j++) {
				if (thingsInRoom[i].inventory[j].displayName.includes(itemString)) {
					item = thingsInRoom[i].inventory.splice(j, 1)[0]
					putInInventory(item, map)
					if (ritual_progress.length > 0) {
						fizzleText = "\nYou hear a fizzling sound, and the remaining items on the altar stop glowing."
					} else {
						fizzleText = ""
					}
					ritual_progress.length = 0
					io.emit("play_audio", {
						for: message.user,
						effect: "pickup"
					})
					if (fizzleText != "") {
						io.emit("play_audio", {
							for: message.user,
							effect: "fizzle"
						})
					}
					return {
						text: "You pick " + item.displayName + " up off the altar and place it in your pack." + fizzleText
					}
				}
			}
		}
	}

	if (itemString[0].match(/[aeiou]/)) {
		return "There isn't an " + itemString + " in the room."
	} else {
		return "There isn't a " + itemString + " in the room."
	}
}

function putInInventory(object, map) {
	playerLocation = findPlayer(map)
	for (var i = 0; i < map[playerLocation.x][playerLocation.y].objects.length; i++) {
		if (map[playerLocation.x][playerLocation.y].objects[i].id == "player") {
			map[playerLocation.x][playerLocation.y].objects[i].inventory.push(object)
		}
	}
}

function attemptToDrop(itemString, map, ritual_progress, io, user) {
	playerLocation = findPlayer(map)
	playerObject = null
	for (object of map[playerLocation.x][playerLocation.y].objects) {
		if (object.id == "player") {
			playerObject = object
		}
	}
	thingsInRoom = map[playerLocation.x][playerLocation.y].objects
	for (var i = 0; i < playerObject.inventory.length; i++) {
		if (playerObject.inventory[i].displayName.includes(itemString)) {
			// pop off room objects list, push onto player inventory list
			thingsInRoom.push(playerObject.inventory.splice(i, 1)[0])
			io.emit("play_audio", {
				for: user,
				effect: "drop"
			})
			return {
				text: "You take " + thingsInRoom[thingsInRoom.length - 1].displayName + " out of your pack and drop it on the floor."
			}
		}
	}
	if (itemString[0].match(/[aeiou]/)) {
		return "There isn't an " + itemString + " in your inventory."
	} else {
		return "There isn't a " + itemString + " in your inventory."
	}
}

function printPlayerInventory(map) {
	playerLocation = findPlayer(map)
	playerObject = null

	for (object of map[playerLocation.x][playerLocation.y].objects) {
		if (object.id == "player") {
			playerObject = object
		}
	}
	if (playerObject.inventory.length == 0) {
		return "You don't have anything in your inventory."
	} else if (playerObject.inventory.length == 1) {
		return "You have " + playerObject.inventory[0].displayName + " in your inventory."
	} else if (playerObject.inventory.length == 2) {
		return "You have " + playerObject.inventory[0].displayName + " and " + playerObject.inventory[1].displayName + " in your inventory."
	} else {
		nMinusOneObjects = playerObject.inventory.slice(0, playerObject.inventory.length - 1)
		displayNames = []
		for (object of nMinusOneObjects) {
			displayNames.push(object.displayName)
		}
		return "You have " + displayNames.join(", ") + " and " + playerObject.inventory[playerObject.inventory.length - 1].displayName + " in your inventory."
	}
}
<commit_msg>fixing error when picking thing off altar<commit_after>findPlayer = require("./findPlayer.js")

module.exports = function(controller, io) {
	controller.hears(['inventory'], "direct_message", function(bot, message) {
		controller.storage.users.get(message.user, function(err, user_game) {
			if (user_game.gameActive == true) {
				bot.reply(message, printPlayerInventory(user_game.map))
				controller.storage.users.save({
					id: message.user,
					map: user_game.map,
					gameActive: true,
					ritual: user_game.ritual,
					ritual_progress: user_game.ritual_progress,
					level: user_game.level
				})
			} else {
				bot.reply(message, "you don't have a new game started! Start one with `new game`")
			}
		})
	})
	controller.hears(["pick up (.*)", "take (.*)", "grab (.*)", "get (.*)", "collect (.*)"], "direct_message", function(bot, message) {
		controller.storage.users.get(message.user, function(err, user_game) {
			if (user_game.gameActive == true) {
				bot.reply(message, attemptToGet(message.match[1], user_game.map, user_game.ritual_progress, io, message.user))
				controller.storage.users.save({
					id: message.user,
					map: user_game.map,
					gameActive: true,
					ritual: user_game.ritual,
					ritual_progress: user_game.ritual_progress,
					level: user_game.level
				})
			} else {
				bot.reply(message, "you don't have a new game started! Start one with `new game`")
			}
		})
	})
	controller.hears(["drop (.*) on floor", "drop (.*) on ground", "place (.*) on floor", "place (.*) on ground", "put (.*) on floor", "put (.*) on ground", "drop (.*)"], "direct_message", function(bot, message) {
		controller.storage.users.get(message.user, function(err, user_game) {
			if (user_game.gameActive == true) {
				bot.reply(message, attemptToDrop(message.match[1], user_game.map, user_game.ritual_progress, io, message.user))
				controller.storage.users.save({
					id: message.user,
					map: user_game.map,
					gameActive: true,
					ritual: user_game.ritual,
					ritual_progress: user_game.ritual_progress,
					level: user_game.level
				})
			} else {
				bot.reply(message, "you don't have a new game started! Start one with `new game`")
			}
		})

	})
}

function attemptToGet(itemString, map, ritual_progress, io, user) {
	playerLocation = findPlayer(map)
	playerObject = null
	for (object of map[playerLocation.x][playerLocation.y].objects) {
		if (object.id == "player") {
			playerObject = object
		}
	}
	thingsInRoom = map[playerLocation.x][playerLocation.y].objects
	for (var i = 0; i < thingsInRoom.length; i++) {
		if (thingsInRoom[i].displayName.includes(itemString)) {
			if (!thingsInRoom[i].pickuppable) {
				return {
					text: "You can't pick up " + thingsInRoom[i].displayName + "!"
				}
			} else {
				// pop off room objects list, push onto player inventory list
				item = thingsInRoom.splice(i, 1)[0]
				putInInventory(item, map)
				io.emit("play_audio", {
					for: user,
					effect: "pickup"
				})
				return {
					text: "You pick up " + playerObject.inventory[playerObject.inventory.length - 1].displayName + " and put it in your pack."
				}
			}
		}
		if (thingsInRoom[i].id == "altar") {
			for (var j = 0; j < thingsInRoom[i].inventory.length; j++) {
				if (thingsInRoom[i].inventory[j].displayName.includes(itemString)) {
					item = thingsInRoom[i].inventory.splice(j, 1)[0]
					putInInventory(item, map)
					if (ritual_progress.length > 0) {
						fizzleText = "\nYou hear a fizzling sound, and the remaining items on the altar stop glowing."
					} else {
						fizzleText = ""
					}
					ritual_progress.length = 0
					io.emit("play_audio", {
						for: user,
						effect: "pickup"
					})
					if (fizzleText != "") {
						io.emit("play_audio", {
							for: user,
							effect: "fizzle"
						})
					}
					return {
						text: "You pick " + item.displayName + " up off the altar and place it in your pack." + fizzleText
					}
				}
			}
		}
	}

	if (itemString[0].match(/[aeiou]/)) {
		return "There isn't an " + itemString + " in the room."
	} else {
		return "There isn't a " + itemString + " in the room."
	}
}

function putInInventory(object, map) {
	playerLocation = findPlayer(map)
	for (var i = 0; i < map[playerLocation.x][playerLocation.y].objects.length; i++) {
		if (map[playerLocation.x][playerLocation.y].objects[i].id == "player") {
			map[playerLocation.x][playerLocation.y].objects[i].inventory.push(object)
		}
	}
}

function attemptToDrop(itemString, map, ritual_progress, io, user) {
	playerLocation = findPlayer(map)
	playerObject = null
	for (object of map[playerLocation.x][playerLocation.y].objects) {
		if (object.id == "player") {
			playerObject = object
		}
	}
	thingsInRoom = map[playerLocation.x][playerLocation.y].objects
	for (var i = 0; i < playerObject.inventory.length; i++) {
		if (playerObject.inventory[i].displayName.includes(itemString)) {
			// pop off room objects list, push onto player inventory list
			thingsInRoom.push(playerObject.inventory.splice(i, 1)[0])
			io.emit("play_audio", {
				for: user,
				effect: "drop"
			})
			return {
				text: "You take " + thingsInRoom[thingsInRoom.length - 1].displayName + " out of your pack and drop it on the floor."
			}
		}
	}
	if (itemString[0].match(/[aeiou]/)) {
		return "There isn't an " + itemString + " in your inventory."
	} else {
		return "There isn't a " + itemString + " in your inventory."
	}
}

function printPlayerInventory(map) {
	playerLocation = findPlayer(map)
	playerObject = null

	for (object of map[playerLocation.x][playerLocation.y].objects) {
		if (object.id == "player") {
			playerObject = object
		}
	}
	if (playerObject.inventory.length == 0) {
		return "You don't have anything in your inventory."
	} else if (playerObject.inventory.length == 1) {
		return "You have " + playerObject.inventory[0].displayName + " in your inventory."
	} else if (playerObject.inventory.length == 2) {
		return "You have " + playerObject.inventory[0].displayName + " and " + playerObject.inventory[1].displayName + " in your inventory."
	} else {
		nMinusOneObjects = playerObject.inventory.slice(0, playerObject.inventory.length - 1)
		displayNames = []
		for (object of nMinusOneObjects) {
			displayNames.push(object.displayName)
		}
		return "You have " + displayNames.join(", ") + " and " + playerObject.inventory[playerObject.inventory.length - 1].displayName + " in your inventory."
	}
}
<|endoftext|>


<commit_before>// Global config
var GRAPHIC_DEFAULT_WIDTH = 600;
var MOBILE_THRESHOLD = 500;

// Global vars
var pymChild = null;
var isMobile = false;
var graphicData = null;
var graphicConfig = null;
var numFormat = d3.format(",");

/*
 * Initialize the graphic.
 */
var onWindowLoaded = function() {
    if (Modernizr.svg) {
        graphicConfig = GRAPHIC_CONFIG;
        loadLocalData(GRAPHIC_DATA);
        //loadCSV('data.csv')
    } else {
        pymChild = new pym.Child({});
    }
}

/*
 * Load graphic data from a local source.
 */
var loadLocalData = function(data) {
    graphicData = data;

    formatData();

    pymChild = new pym.Child({
        renderCallback: render
    });
}

/*
 * Load graphic data from a CSV.
 */
var loadCSV = function(url) {
    d3.csv(GRAPHIC_DATA_URL, function(error, data) {
        graphicData = data;

        formatData();

        pymChild = new pym.Child({
            renderCallback: render
        });
    });
}

/*
 * Format graphic data for processing by D3.
 */
var formatData = function() {
    graphicData.forEach(function(d) {
        d['key'] = d['Group'];
        d['values'] = [];

        _.each(d, function(v, k) {
            if (_.contains(['Group', 'key', 'values'], k)) {
                return;
            }

            d['values'].push({ 'label': k, 'amt': +v });
            delete d[k];
        });

        delete d['Group'];
    });
}

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function(containerWidth) {
    if (!containerWidth) {
        containerWidth = GRAPHIC_DEFAULT_WIDTH;
    }

    if (containerWidth <= MOBILE_THRESHOLD) {
        isMobile = true;
    } else {
        isMobile = false;
    }

    // Render the chart!
    renderGroupedBarChart({
        container: '#graphic',
        width: containerWidth,
        data: graphicData
    });

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
}

/*
 * Render a bar chart.
 */
var renderGroupedBarChart = function(config) {
    /*
     * Setup chart container.
     */
    var labelColumn = 'label';
    var valueColumn = 'amt';

    var numGroups = config['data'].length;
    var numGroupBars = config['data'][0]['values'].length;

    var barHeight = parseInt(graphicConfig.barHeight || 25, 10);
    var barGapInner = parseInt(graphicConfig.barGap || 2, 10);
    var barGap = parseInt(graphicConfig.barGap || 10, 10);
    var labelWidth = parseInt(graphicConfig.labelWidth || 85, 10);
    var labelMargin = parseInt(graphicConfig.labelMargin || 6, 10);
    var valueGap = parseInt(graphicConfig.valueGap || 6, 10);
    var groupHeight = (barHeight * numGroupBars) + (barGapInner * (numGroupBars - 1)) + 30;

    var margins = {
        top: 0,
        right: 15,
        bottom: 20,
        left: (labelWidth + labelMargin)
    };

    if (graphicConfig.marginTop) {
        margins.top = parseInt(graphicConfig.marginTop, 10);
    }

    if (graphicConfig.marginRight) {
        margins.right = parseInt(graphicConfig.marginRight, 10);
    }

    if (graphicConfig.marginBottom) {
        margins.bottom = parseInt(graphicConfig.marginBottom, 10);
    }

    if (graphicConfig.marginLeft) {
        margins.left = parseInt(graphicConfig.marginLeft, 10);
    }

    var ticksX = parseInt(graphicConfig.ticksX || 7, 10);
    var roundTicksFactor = parseInt(graphicConfig.roundTicksFactor || 5, 10);

    // Clear existing graphic (for redraw)
    var containerElement = d3.select(config['container']);
    containerElement.html('');

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphic-wrapper');

     // Calculate actual chart dimensions
    var innerWidth = chartWrapper.node().getBoundingClientRect().width;
    var chartWidth = innerWidth - margins['left'] - margins['right'];
    var chartHeight = (((((barHeight + barGapInner) * numGroupBars) - barGapInner) + barGap) * numGroups) - barGap + barGapInner;

    chartHeight = (groupHeight + 20) * (numGroups - 1);
    /*
     * Create D3 scale objects.
     */
    var min = d3.min(config['data'], function(d) {
        return d3.min(d['values'], function(v) {
            return Math.floor(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    });

    if ('minX' in graphicConfig) {
        min = parseFloat(graphicConfig.minX, 10);
    } else if (min > 0) {
        min = 0;
    }

    var max = d3.max(config['data'], function(d) {
        return d3.max(d['values'], function(v) {
            return Math.ceil(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    })

    if ('maxX' in graphicConfig) {
        max = parseFloat(graphicConfig.maxX, 10);
    }

    var xScale = d3.scale.linear()
        .domain([
            min,
            max
        ])
        .range([0, chartWidth]);

    var yScale = d3.scale.linear()
        .range([chartHeight, 0]);

    var colorList = colorArray(graphicConfig, monochromeColors);
    var colorScale = d3.scale.ordinal()
        .domain(_.pluck(config['data'][0]['values'], labelColumn))
        .range(colorList);

    var chartElement = chartWrapper.append('svg')
        .attr('width', chartWidth + margins['left'] + margins['right'])
        .attr('height', chartHeight + margins['top'] + margins['bottom'])
        .append('g')
        .attr('transform', 'translate(' + margins['left'] + ',' + margins['top'] + ')');

    /*
     * Create D3 axes.
     */
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom')
        .ticks(ticksX)
        .tickFormat(function(d) {
            return d.toFixed(0) + '%';
        });

    /*
     * Render axes to chart.
     */
    // chartElement.append('g')
    //     .attr('class', 'x axis')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxis);

    /*
     * Render grid to chart.
     */
    var xAxisGrid = function() {
        return xAxis;
    };

    // chartElement.append('g')
    //     .attr('class', 'x grid')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxisGrid()
    //         .tickSize(-chartHeight, 0, 0)
    //         .tickFormat('')
    //     );

    /*
     * Render bars to chart.
     */
    var barGroups = chartElement.selectAll('.bars')
        .data(config['data'])
        .enter()
        .append('g')
            .attr('class', 'g bars')
            .attr('transform', function(d, i) {
                return makeTranslate(0, (groupHeight + barGap) * i + 20);
            });

    barGroups.selectAll('rect')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('rect')
            .attr('x', function(d) {
                if (d[valueColumn] >= 0) {
                    return xScale(0);
                }

                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                if (i == 0) {
                    return 0;
                }

                return (barHeight * i) + (barGapInner * i);
            })
            .attr('width', function(d) {
                return Math.abs(xScale(0) - xScale(d[valueColumn]));
            })
            .attr('height', barHeight)
            .attr('fill', function(d) {
            	return colorScale(d[labelColumn]);
            })
            .attr('class', function(d) {
                return 'y-' + d[labelColumn];
            });

    barGroups.append('text')
            .attr('x', 0)
            .attr('y', -8)
            .attr('class', 'group-label')
            .text(function (d) { return d.key; });

    var labelData = [];
    _.each(config['data'], function (d) {
        _.each(d.values, function (e) {
            labelData.push(e.label)
        });
    });

    /*
     * Render bar labels.
     */
    chartWrapper.append('ul')
        .attr('class', 'labels')
        .attr('style', formatStyle({
            'width': labelWidth + 'px',
            'top': margins['top'] + 'px',
            'left': '0'
        }))
        .selectAll('li')
        .data(labelData)
        .enter()
        .append('li')
            .attr('style', function(d,i) {
                var index = Math.floor(i / 2);
                var top = (groupHeight + barGap) * index;

                if (i & 1 === 1) {
                    top += barHeight + barGapInner;
                }

                if (i == 0) {
                    top = 0;
                }

                top += 20;

                return formatStyle({
                    'width': (labelWidth - 10) + 'px',
                    'height': barHeight + 'px',
                    'left': '0px',
                    'top': top + 'px;'
                });
            })
            .attr('class', function(d,i) {
                return classify(d);//['key']);
            })
            .append('span')
                .text(function(d) {
                    return d;//['key']
                });

    if (graphicConfig.theme == "highlight") {
        chartWrapper.on("mousemove", function (e) {
            var pos = d3.mouse(chartWrapper.node())
            var gh = groupHeight + 10;
            var index = Math.floor(pos[1] / gh);
            var relativeY = pos[1] - index * gh;
            var barIndex = Math.floor((relativeY - 20) / (barHeight + barGapInner));

            chartWrapper.selectAll(".value text.over").classed('over', false);
            chartWrapper.selectAll(".bars rect").attr('fill', function (d, i) {
                return colorScale(i);
            });

            if (relativeY <= 20) {
                // in the group heading
                return;
            }

            chartWrapper.selectAll(".bars:nth-child("+(index+1)+") rect:nth-child("+(barIndex+1)+")")
                .attr('fill', highlightColor);
            chartWrapper.selectAll(".bars:nth-child("+(index+1)+") .value text.out:nth-child("+(barIndex+1)+")")
                .classed('over', true);
        });
    }

    /*
     * Render bar values.
     */
    barGroups.append('g')
        .attr('class', 'value')
        .selectAll('text')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('text')
            .text(function(d) {
                return (graphicConfig.prefixX || '') + numFormat(d[valueColumn]) + (graphicConfig.suffixX || '');
            })
            .attr('x', function(d) {
                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                if (i == 0) {
                    return 0;
                }

                return (barHeight * i) + barGapInner;
            })
            .attr('dx', function(d) {
                var xStart = xScale(d[valueColumn]);
                var textWidth = this.getComputedTextLength()

                // Negative case
                if (d[valueColumn] < 0) {
                    var outsideOffset = -(valueGap + textWidth);

                    if (xStart + outsideOffset < 0) {
                        d3.select(this).classed('in', true)
                        return valueGap;
                    } else {
                        d3.select(this).classed('out', true)
                        return outsideOffset;
                    }
                // Positive case
                } else {
                    if (xStart + valueGap + textWidth > chartWidth) {
                        d3.select(this).classed('in', true)
                        return -(valueGap + textWidth);
                    } else {
                        d3.select(this).classed('out', true)
                        return valueGap;
                    }
                }
            })
            .attr('dy', (barHeight / 2) + 4);
}

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;
<commit_msg>Copied across improvements from Bar Chart to Grouped Bar chart that handle empty values better<commit_after>// Global config
var GRAPHIC_DEFAULT_WIDTH = 600;
var MOBILE_THRESHOLD = 500;

// Global vars
var pymChild = null;
var isMobile = false;
var graphicData = null;
var graphicConfig = null;
var numFormat = d3.format(",");

/*
 * Initialize the graphic.
 */
var onWindowLoaded = function() {
    if (Modernizr.svg) {
        graphicConfig = GRAPHIC_CONFIG;
        loadLocalData(GRAPHIC_DATA);
        //loadCSV('data.csv')
    } else {
        pymChild = new pym.Child({});
    }
}

/*
 * Load graphic data from a local source.
 */
var loadLocalData = function(data) {
    graphicData = data;

    formatData();

    pymChild = new pym.Child({
        renderCallback: render
    });
}

/*
 * Load graphic data from a CSV.
 */
var loadCSV = function(url) {
    d3.csv(GRAPHIC_DATA_URL, function(error, data) {
        graphicData = data;

        formatData();

        pymChild = new pym.Child({
            renderCallback: render
        });
    });
}

/*
 * Format graphic data for processing by D3.
 */
var formatData = function() {
    graphicData.forEach(function(d) {
        d['key'] = d['Group'];
        d['values'] = [];

        _.each(d, function(v, k) {
            if (_.contains(['Group', 'key', 'values'], k)) {
                return;
            }

            d['values'].push({ 'label': k, 'amt': +v });
            delete d[k];
        });

        delete d['Group'];
    });
}

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function(containerWidth) {
    if (!containerWidth) {
        containerWidth = GRAPHIC_DEFAULT_WIDTH;
    }

    if (containerWidth <= MOBILE_THRESHOLD) {
        isMobile = true;
    } else {
        isMobile = false;
    }

    // Render the chart!
    renderGroupedBarChart({
        container: '#graphic',
        width: containerWidth,
        data: graphicData
    });

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
}

/*
 * Render a bar chart.
 */
var renderGroupedBarChart = function(config) {
    /*
     * Setup chart container.
     */
    var labelColumn = 'label';
    var valueColumn = 'amt';

    var numGroups = config['data'].length;
    var numGroupBars = config['data'][0]['values'].length;

    var barHeight = parseInt(graphicConfig.barHeight || 25, 10);
    var barGapInner = parseInt(graphicConfig.barGap || 2, 10);
    var barGap = parseInt(graphicConfig.barGap || 10, 10);
    var labelWidth = parseInt(graphicConfig.labelWidth || 85, 10);
    var labelMargin = parseInt(graphicConfig.labelMargin || 6, 10);
    var valueGap = parseInt(graphicConfig.valueGap || 6, 10);
    var groupHeight = (barHeight * numGroupBars) + (barGapInner * (numGroupBars - 1)) + 30;

    var margins = {
        top: parseInt(graphicConfig.marginTop || 0, 10),
        right: parseInt(graphicConfig.marginRight || 15, 10),
        bottom: parseInt(graphicConfig.marginBottom || 20, 10),
        left: parseInt(graphicConfig.marginLeft || (labelWidth + labelMargin), 10)
    };

    var ticksX = parseInt(graphicConfig.ticksX || 7, 10);
    var roundTicksFactor = parseInt(graphicConfig.roundTicksFactor || 5, 10);

    // Clear existing graphic (for redraw)
    var containerElement = d3.select(config['container']);
    containerElement.html('');

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphic-wrapper');

     // Calculate actual chart dimensions
    var innerWidth = chartWrapper.node().getBoundingClientRect().width;
    var chartWidth = innerWidth - margins['left'] - margins['right'];
    var chartHeight = (((((barHeight + barGapInner) * numGroupBars) - barGapInner) + barGap) * numGroups) - barGap + barGapInner;

    chartHeight = (groupHeight + 20) * (numGroups - 1);
    /*
     * Create D3 scale objects.
     */
    var min = d3.min(config['data'], function(d) {
        return d3.min(d['values'], function(v) {
            return Math.floor(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    });

    if ('minX' in graphicConfig && graphicConfig.minX !== '') {
        min = parseFloat(graphicConfig.minX, 10);
    } else if (min > 0) {
        min = 0;
    }

    var max = d3.max(config['data'], function(d) {
        return d3.max(d['values'], function(v) {
            return Math.ceil(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    })

    if ('maxX' in graphicConfig && graphicConfig.maxX !== '') {
        max = parseFloat(graphicConfig.maxX, 10);
    }

    var xScale = d3.scale.linear()
        .domain([
            min,
            max
        ])
        .range([0, chartWidth]);

    var yScale = d3.scale.linear()
        .range([chartHeight, 0]);

    var colorList = colorArray(graphicConfig, monochromeColors);
    var colorScale = d3.scale.ordinal()
        .domain(_.pluck(config['data'][0]['values'], labelColumn))
        .range(colorList);

    var chartElement = chartWrapper.append('svg')
        .attr('width', chartWidth + margins['left'] + margins['right'])
        .attr('height', chartHeight + margins['top'] + margins['bottom'])
        .append('g')
        .attr('transform', 'translate(' + margins['left'] + ',' + margins['top'] + ')');

    /*
     * Create D3 axes.
     */
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom')
        .ticks(ticksX)
        .tickFormat(function(d) {
            return d.toFixed(0) + '%';
        });

    /*
     * Render axes to chart.
     */
    // chartElement.append('g')
    //     .attr('class', 'x axis')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxis);

    /*
     * Render grid to chart.
     */
    var xAxisGrid = function() {
        return xAxis;
    };

    // chartElement.append('g')
    //     .attr('class', 'x grid')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxisGrid()
    //         .tickSize(-chartHeight, 0, 0)
    //         .tickFormat('')
    //     );

    /*
     * Render bars to chart.
     */
    var barGroups = chartElement.selectAll('.bars')
        .data(config['data'])
        .enter()
        .append('g')
            .attr('class', 'g bars')
            .attr('transform', function(d, i) {
                return makeTranslate(0, (groupHeight + barGap) * i + 20);
            });

    barGroups.selectAll('rect')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('rect')
            .attr('x', function(d) {
                if (d[valueColumn] >= 0) {
                    return xScale(0);
                }

                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                if (i == 0) {
                    return 0;
                }

                return (barHeight * i) + (barGapInner * i);
            })
            .attr('width', function(d) {
                return Math.abs(xScale(0) - xScale(d[valueColumn]));
            })
            .attr('height', barHeight)
            .attr('fill', function(d) {
            	return colorScale(d[labelColumn]);
            })
            .attr('class', function(d) {
                return 'y-' + d[labelColumn];
            });

    barGroups.append('text')
            .attr('x', 0)
            .attr('y', -8)
            .attr('class', 'group-label')
            .text(function (d) { return d.key; });

    var labelData = [];
    _.each(config['data'], function (d) {
        _.each(d.values, function (e) {
            labelData.push(e.label)
        });
    });

    /*
     * Render bar labels.
     */
    chartWrapper.append('ul')
        .attr('class', 'labels')
        .attr('style', formatStyle({
            'width': labelWidth + 'px',
            'top': margins['top'] + 'px',
            'left': '0'
        }))
        .selectAll('li')
        .data(labelData)
        .enter()
        .append('li')
            .attr('style', function(d,i) {
                var index = Math.floor(i / 2);
                var top = (groupHeight + barGap) * index;

                if (i & 1 === 1) {
                    top += barHeight + barGapInner;
                }

                if (i == 0) {
                    top = 0;
                }

                top += 20;

                return formatStyle({
                    'width': (labelWidth - 10) + 'px',
                    'height': barHeight + 'px',
                    'left': '0px',
                    'top': top + 'px;'
                });
            })
            .attr('class', function(d,i) {
                return classify(d);//['key']);
            })
            .append('span')
                .text(function(d) {
                    return d;//['key']
                });

    if (graphicConfig.theme == "highlight") {
        chartWrapper.on("mousemove", function (e) {
            var pos = d3.mouse(chartWrapper.node())
            var gh = groupHeight + 10;
            var index = Math.floor(pos[1] / gh);
            var relativeY = pos[1] - index * gh;
            var barIndex = Math.floor((relativeY - 20) / (barHeight + barGapInner));

            chartWrapper.selectAll(".value text.over").classed('over', false);
            chartWrapper.selectAll(".bars rect").attr('fill', function (d, i) {
                return colorScale(i);
            });

            if (relativeY <= 20) {
                // in the group heading
                return;
            }

            chartWrapper.selectAll(".bars:nth-child("+(index+1)+") rect:nth-child("+(barIndex+1)+")")
                .attr('fill', highlightColor);
            chartWrapper.selectAll(".bars:nth-child("+(index+1)+") .value text.out:nth-child("+(barIndex+1)+")")
                .classed('over', true);
        });
    }

    /*
     * Render bar values.
     */
    barGroups.append('g')
        .attr('class', 'value')
        .selectAll('text')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('text')
            .text(function(d) {
                return (graphicConfig.prefixX || '') + numFormat(d[valueColumn]) + (graphicConfig.suffixX || '');
            })
            .attr('x', function(d) {
                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                if (i == 0) {
                    return 0;
                }

                return (barHeight * i) + barGapInner;
            })
            .attr('dx', function(d) {
                var xStart = xScale(d[valueColumn]);
                var textWidth = this.getComputedTextLength()

                // Negative case
                if (d[valueColumn] < 0) {
                    var outsideOffset = -(valueGap + textWidth);

                    if (xStart + outsideOffset < 0) {
                        d3.select(this).classed('in', true)
                        return valueGap;
                    } else {
                        d3.select(this).classed('out', true)
                        return outsideOffset;
                    }
                // Positive case
                } else {
                    if (xStart + valueGap + textWidth > chartWidth) {
                        d3.select(this).classed('in', true)
                        return -(valueGap + textWidth);
                    } else {
                        d3.select(this).classed('out', true)
                        return valueGap;
                    }
                }
            })
            .attr('dy', (barHeight / 2) + 4);
}

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;
<|endoftext|>


<commit_before>var UISwitchControl=new Class({
	Extends:UIControl,
	knob:null,
	initialize:function(element, options)
	{

		var initialOptions=Object.append({
		slideImage:false,
		slideImageWidth:18,
		slideImageHeight:15,
		slideImageKnobTopLeft:[1,1],		//expecting a border to push it down. and to the right. but only show border on right.
		slideImageKnobBottomRight:[20,17],  //border is still expected, but it only pushes right and down, not from bottom left.
											//so here we give extra space to acounnt for a 1px border on all sides.
		slideLimitX:false,
		slideLimitY:false,
		scaleControl:false,
		peepholeWidth:35,
		peepholeHeight:15,
		steps:1, //steps can be an integer, or if 0, a linear value based on the pixel location is used
		increment:1, //increment number of pixels to move at a time.
		axis:"horizontal" //not used!

	},options);

		initialOptions.knobStyles=Object.append({
			'position':'absolute'
		}, options.knobStyles);

		initialOptions.peepholeStyles=Object.append({
			//'overflow':'hidden',
			'position':'relative'
		}, options.peepholeStyles);

		this.initialOptions=Object.append({},initialOptions);
		this.parent(element, initialOptions); //parent calls this._render!
	},
	_render:function(container){
		var me=this;
		if(!me.options.peepholeWidth){
			me.options.peepholeWidth=me.options.slideImageKnobBottomRight[0];
		}

		var peephole=new Element('div', {"class":"ui-switch"});
		peephole.setStyle('height', me.options.peepholeHeight+'px');
		peephole.setStyle('width', me.options.peepholeWidth+'px');
		peephole.setStyles(me.options.peepholeStyles);
		me.peephole=peephole;

		var initialPostion=me._contraintsForState(me.state);
		var slideImage=me.options.slideImage?(new Element('img', {src:me.options.slideImage})):(new Element('div'));
		slideImage.addClass('ui-ctrl-handle');

		slideImage.setStyle('left', initialPostion[0]+'px');
		slideImage.setStyle('top', initialPostion[1]+'px');
		slideImage.setStyle('width', me.options.slideImageWidth+'px');
		slideImage.setStyle('height', me.options.slideImage?'auto':me.options.slideImageHeight+'px');
		slideImage.setStyles(me.options.knobStyles);

		container.appendChild(peephole);
		peephole.appendChild(slideImage);
		me.knob=slideImage;
		if(me.state){me.element.addClass('active');}
		me.element.addClass('Boolean');
	},
	_disableUserInteraction:function(){
		var me=this;
		me.peephole.removeEvents();
		me.knob.removeEvents();
		me.knob.setStyle('opacity','0.4');
		me.dragMove=null;
		me.peephole.addClass('disabled');
	},
	_enableUserInteraction:function(){
		var me=this;
		var slideImage=me.knob;
		me.knob.setStyle('opacity','1');
		var dragMove=slideImage.makeDraggable({
			handle:slideImage,
			limit:{
				'x': me.options.slideLimitX,
				'y': me.options.slideLimitY //no vertical control
			},
			onStart:function(){
				me.element.addClass('dragging');
			},
			onComplete: function(el){ 	//el -> drag-able element (not used)
				me.toggle();
				me.element.removeClass('dragging');
			}
		});
		me.peephole.addEvent('click',function(){me.toggle();});
		me.dragMove=dragMove;
		me.peephole.removeClass('disabled');
	},
	setScale:function(value){
		var me=this;
		me.options=Object.merge({},
			me.options,
				Object.merge({},{
					slideImage:false,
					slideImageWidth:130,
					slideImageHeight:22,
					slideImageKnobTopLeft:[48,2],
					slideImageKnobBottomRight:[80,21],
					slideLimitX:false,
					slideLimitY:false,
					scaleControl:false,
					peepholeWidth:75,
					peepholeHeight:20,
					steps:1, //steps can be an integer, or if 0, a linear value based on the pixel location is used
					increment:1, //increment number of pixels to move at a time.
					axis:"horizontal" //not used!
					},
				{scaleControl:value})
			);

		me.options.slideLimitX=false;
		me.options.slideLimitY=false;
		me.element.removeChild(me.peephole);
		me.knob=null;
		me.peephole=null;
		me.stateMap=null;

		me._generateStateMap();
		me._render(me.element);
		//JSConsole(['scaled slider', Object.merge({},me.options, {})]);

	},
	_generateStateMap:function(){
		var me=this;
		if(me.options.scaleControl){
			if(me.options.slideImageWidth){
				me.options.slideImageWidth=Math.round(me.options.slideImageWidth*me.options.scaleControl);
			}
			if(me.options.slideImageHeight){
				me.options.slideImageHeight=Math.round(me.options.slideImageHeight*me.options.scaleControl);
			}
			if(me.options.slideImageKnobTopLeft){

				me.options.slideImageKnobTopLeft[0]=Math.round(me.options.slideImageKnobTopLeft[0]*me.options.scaleControl);
				me.options.slideImageKnobTopLeft[1]=Math.round(me.options.slideImageKnobTopLeft[1]*me.options.scaleControl);
				//console.debug(["MooSlider Top Left",[Math.round(me.options.slideImageKnobTopLeft[1]*me.options.scaleControl),me.options.slideImageKnobTopLeft[1],me.options.scaleControl]]);

			}
			if(me.options.slideImageKnobBottomRight){
				me.options.slideImageKnobBottomRight[0]=me.options.slideImageKnobBottomRight[0]*me.options.scaleControl;
				me.options.slideImageKnobBottomRight[1]=me.options.slideImageKnobBottomRight[1]*me.options.scaleControl;
			}

			if(me.options.slideLimitX){
				me.options.slideLimitX=Math.round(me.options.slideLimitX*me.options.scaleControl);
			}
			if(me.options.slideLimitY){
				me.options.slideLimitY=Math.round(me.options.slideLimitY*me.options.scaleControl);
			}
			if(me.options.peepholeWidth){
				me.options.peepholeWidth=Math.round(me.options.peepholeWidth*me.options.scaleControl);
			}
			if(me.options.peepholeHeight){
				me.options.peepholeHeight=Math.round(me.options.peepholeHeight*me.options.scaleControl);
			}
		}

		if(!me.options.slideLimitX){

			var xOffset=0;
			var xWidth=me.options.slideImageKnobBottomRight[0]-me.options.slideImageKnobTopLeft[0];
			if(me.options.peepholeWidth){
					xOffset=me.options.peepholeWidth-(xWidth);
			}
			me.options.slideLimitX=[-me.options.slideImageKnobTopLeft[0], xOffset]; //left limit is always knobleft knob right may need an offset
		}
		if(!me.options.slideLimitY){
			/*
			 * not needed.
			var yOffset=0;
			if(me.options.peepholeHeight){
				var yHeight=me.options.slideImageKnobBottomRight[1]-me.options.slideImageKnobTopLeft[1];
			}
			*/

			me.options.slideLimitY=[-me.options.slideImageKnobTopLeft[1], -me.options.slideImageKnobTopLeft[1]];
		}
		var stateMap={x:[me.options.slideLimitX[0],me.options.slideLimitX[1]], y:[me.options.slideLimitY[0], me.options.slideLimitY[1]]};
		me.stateMap=stateMap;
	},
	_contraintsForState:function(state){
		var me=this;
		return [me.stateMap.x[me.range.indexOf(state)], me.stateMap.y[me.range.indexOf(state)]];
	},
	_stateChangeTo:function(state, suppressEvents){
		return this._animateStateChangeTo(state,suppressEvents);
	},
	_animateStateChangeTo:function(state, suppressEvents){
		var me=this;

		var pos=me._contraintsForState(state);

		var slide=new Fx.Tween(me.knob,{property:'left', duration:'short'});
		//new Fx.Tween(me.knob, 'left', {transition: Fx.Transitions.Elastic.easeOut, duration:500});
		me.element.addClass('transition');
		slide.start(pos[0]).chain(function(){
			me.element.removeClass('transition');
			me.state=state;
			if(me.state){
				me.element.addClass('active');
			}else{
				me.element.removeClass('active');
			}
			if(!suppressEvents){
				me.fireEvent('onChange', me.state);
				if(me.state===true||me.steps==1&&me.state===1){
					me.fireEvent('onEnable');
					//window.parent.console.debug(me.element);

				}
				if(me.state===false||me.steps==1&&me.state===0){
					me.fireEvent('onDisable');

				}
			}
		});

		return state;
	},
	toggle:function(){
		this._toggle();
	}
});<commit_msg>use Drag<commit_after>var UISwitchControl=new Class({
	Extends:UIControl,
	knob:null,
	initialize:function(element, options)
	{

		var initialOptions=Object.append({
		slideImage:false,
		slideImageWidth:18,
		slideImageHeight:15,
		slideImageKnobTopLeft:[1,1],		//expecting a border to push it down. and to the right. but only show border on right.
		slideImageKnobBottomRight:[20,17],  //border is still expected, but it only pushes right and down, not from bottom left.
											//so here we give extra space to acounnt for a 1px border on all sides.
		slideLimitX:false,
		slideLimitY:false,
		scaleControl:false,
		peepholeWidth:35,
		peepholeHeight:15,
		steps:1, //steps can be an integer, or if 0, a linear value based on the pixel location is used
		increment:1, //increment number of pixels to move at a time.
		axis:"horizontal" //not used!

	},options);

		initialOptions.knobStyles=Object.append({
			'position':'absolute'
		}, options.knobStyles);

		initialOptions.peepholeStyles=Object.append({
			//'overflow':'hidden',
			'position':'relative'
		}, options.peepholeStyles);

		this.initialOptions=Object.append({},initialOptions);
		this.parent(element, initialOptions); //parent calls this._render!
	},
	_render:function(container){
		var me=this;
		if(!me.options.peepholeWidth){
			me.options.peepholeWidth=me.options.slideImageKnobBottomRight[0];
		}

		var peephole=new Element('div', {"class":"ui-switch"});
		peephole.setStyle('height', me.options.peepholeHeight+'px');
		peephole.setStyle('width', me.options.peepholeWidth+'px');
		peephole.setStyles(me.options.peepholeStyles);
		me.peephole=peephole;

		var initialPostion=me._contraintsForState(me.state);
		var slideImage=me.options.slideImage?(new Element('img', {src:me.options.slideImage})):(new Element('div'));
		slideImage.addClass('ui-ctrl-handle');

		slideImage.setStyle('left', initialPostion[0]+'px');
		slideImage.setStyle('top', initialPostion[1]+'px');
		slideImage.setStyle('width', me.options.slideImageWidth+'px');
		slideImage.setStyle('height', me.options.slideImage?'auto':me.options.slideImageHeight+'px');
		slideImage.setStyles(me.options.knobStyles);

		container.appendChild(peephole);
		peephole.appendChild(slideImage);
		me.knob=slideImage;
		if(me.state){me.element.addClass('active');}
		me.element.addClass('Boolean');
	},
	_disableUserInteraction:function(){
		var me=this;
		me.peephole.removeEvents();
		me.knob.removeEvents();
		me.knob.setStyle('opacity','0.4');
		me.dragMove=null;
		me.peephole.addClass('disabled');
	},
	_enableUserInteraction:function(){
		var me=this;
		var slideImage=me.knob;
		me.knob.setStyle('opacity','1');
		var dragMove=new Drag(slideImage,{
			limit:{
				'x': me.options.slideLimitX,
				'y': me.options.slideLimitY //no vertical control
			},
			onStart:function(){
				me.element.addClass('dragging');
			},
			onComplete: function(el){ 	//el -> drag-able element (not used)
				me.toggle();
				me.element.removeClass('dragging');
			}
		});
		me.peephole.addEvent('click',function(){me.toggle();});
		me.dragMove=dragMove;
		me.peephole.removeClass('disabled');
	},
	setScale:function(value){
		var me=this;
		me.options=Object.merge({},
			me.options,
				Object.merge({},{
					slideImage:false,
					slideImageWidth:130,
					slideImageHeight:22,
					slideImageKnobTopLeft:[48,2],
					slideImageKnobBottomRight:[80,21],
					slideLimitX:false,
					slideLimitY:false,
					scaleControl:false,
					peepholeWidth:75,
					peepholeHeight:20,
					steps:1, //steps can be an integer, or if 0, a linear value based on the pixel location is used
					increment:1, //increment number of pixels to move at a time.
					axis:"horizontal" //not used!
					},
				{scaleControl:value})
			);

		me.options.slideLimitX=false;
		me.options.slideLimitY=false;
		me.element.removeChild(me.peephole);
		me.knob=null;
		me.peephole=null;
		me.stateMap=null;

		me._generateStateMap();
		me._render(me.element);
		//JSConsole(['scaled slider', Object.merge({},me.options, {})]);

	},
	_generateStateMap:function(){
		var me=this;
		if(me.options.scaleControl){
			if(me.options.slideImageWidth){
				me.options.slideImageWidth=Math.round(me.options.slideImageWidth*me.options.scaleControl);
			}
			if(me.options.slideImageHeight){
				me.options.slideImageHeight=Math.round(me.options.slideImageHeight*me.options.scaleControl);
			}
			if(me.options.slideImageKnobTopLeft){

				me.options.slideImageKnobTopLeft[0]=Math.round(me.options.slideImageKnobTopLeft[0]*me.options.scaleControl);
				me.options.slideImageKnobTopLeft[1]=Math.round(me.options.slideImageKnobTopLeft[1]*me.options.scaleControl);
				//console.debug(["MooSlider Top Left",[Math.round(me.options.slideImageKnobTopLeft[1]*me.options.scaleControl),me.options.slideImageKnobTopLeft[1],me.options.scaleControl]]);

			}
			if(me.options.slideImageKnobBottomRight){
				me.options.slideImageKnobBottomRight[0]=me.options.slideImageKnobBottomRight[0]*me.options.scaleControl;
				me.options.slideImageKnobBottomRight[1]=me.options.slideImageKnobBottomRight[1]*me.options.scaleControl;
			}

			if(me.options.slideLimitX){
				me.options.slideLimitX=Math.round(me.options.slideLimitX*me.options.scaleControl);
			}
			if(me.options.slideLimitY){
				me.options.slideLimitY=Math.round(me.options.slideLimitY*me.options.scaleControl);
			}
			if(me.options.peepholeWidth){
				me.options.peepholeWidth=Math.round(me.options.peepholeWidth*me.options.scaleControl);
			}
			if(me.options.peepholeHeight){
				me.options.peepholeHeight=Math.round(me.options.peepholeHeight*me.options.scaleControl);
			}
		}

		if(!me.options.slideLimitX){

			var xOffset=0;
			var xWidth=me.options.slideImageKnobBottomRight[0]-me.options.slideImageKnobTopLeft[0];
			if(me.options.peepholeWidth){
					xOffset=me.options.peepholeWidth-(xWidth);
			}
			me.options.slideLimitX=[-me.options.slideImageKnobTopLeft[0], xOffset]; //left limit is always knobleft knob right may need an offset
		}
		if(!me.options.slideLimitY){
			/*
			 * not needed.
			var yOffset=0;
			if(me.options.peepholeHeight){
				var yHeight=me.options.slideImageKnobBottomRight[1]-me.options.slideImageKnobTopLeft[1];
			}
			*/

			me.options.slideLimitY=[-me.options.slideImageKnobTopLeft[1], -me.options.slideImageKnobTopLeft[1]];
		}
		var stateMap={x:[me.options.slideLimitX[0],me.options.slideLimitX[1]], y:[me.options.slideLimitY[0], me.options.slideLimitY[1]]};
		me.stateMap=stateMap;
	},
	_contraintsForState:function(state){
		var me=this;
		return [me.stateMap.x[me.range.indexOf(state)], me.stateMap.y[me.range.indexOf(state)]];
	},
	_stateChangeTo:function(state, suppressEvents){
		return this._animateStateChangeTo(state,suppressEvents);
	},
	_animateStateChangeTo:function(state, suppressEvents){
		var me=this;

		var pos=me._contraintsForState(state);

		var slide=new Fx.Tween(me.knob,{property:'left', duration:'short'});
		//new Fx.Tween(me.knob, 'left', {transition: Fx.Transitions.Elastic.easeOut, duration:500});
		me.element.addClass('transition');
		slide.start(pos[0]).chain(function(){
			me.element.removeClass('transition');
			me.state=state;
			if(me.state){
				me.element.addClass('active');
			}else{
				me.element.removeClass('active');
			}
			if(!suppressEvents){
				me.fireEvent('onChange', me.state);
				if(me.state===true||me.steps==1&&me.state===1){
					me.fireEvent('onEnable');
					//window.parent.console.debug(me.element);

				}
				if(me.state===false||me.steps==1&&me.state===0){
					me.fireEvent('onDisable');

				}
			}
		});

		return state;
	},
	toggle:function(){
		this._toggle();
	}
});<|endoftext|>


<commit_before>// Global vars
var pymChild = null;
var isMobile = false;

/*
 * Initialize the graphic.
 */
var onWindowLoaded = function() {
    if (Modernizr.svg) {
        formatData();

        pymChild = new pym.Child({
            renderCallback: render
        });
    } else {
        pymChild = new pym.Child({});
    }
}

/*
 * Format graphic data for processing by D3.
 */
var formatData = function() {
    DATA.forEach(function(d) {
        d['key'] = d['Group'];
        d['values'] = [];

        _.each(d, function(v, k) {
            if (_.contains(['Group', 'key', 'values'], k)) {
                return;
            }

            d['values'].push({ 'label': k, 'amt': +v });
            delete d[k];
        });

        delete d['Group'];
    });
}

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function(containerWidth) {
    if (!containerWidth) {
        containerWidth = DEFAULT_WIDTH;
    }

    if (containerWidth <= MOBILE_THRESHOLD) {
        isMobile = true;
    } else {
        isMobile = false;
    }

    // Render the chart!
    renderGroupedBarChart({
        container: '#grouped-bar-chart',
        width: containerWidth,
        data: DATA
    });

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
}

/*
 * Render a bar chart.
 */
var renderGroupedBarChart = function(config) {
    /*
     * Setup chart container.
     */
    var labelColumn = 'label';
    var valueColumn = 'amt';

    var numGroups = config.data.length;
    var numGroupBars = config.data[0].values.length;

    var barHeight = parseInt(LABELS.barHeight || 25, 10);
    var barGap = parseInt(LABELS.barGap || 2, 10);
    var groupGap = parseInt(LABELS.groupGap || 30, 10);
    var labelWidth = parseInt(LABELS.labelWidth || 85, 10);
    var labelMargin = parseInt(LABELS.labelMargin || 6, 10);
    var valueGap = parseInt(LABELS.valueGap || 6, 10);
    var groupHeight = (barHeight + barGap) * numGroupBars - barGap;

    var margins = {
        top: parseInt(LABELS.marginTop || 0, 10),
        right: parseInt(LABELS.marginRight || 15, 10),
        bottom: parseInt(LABELS.marginBottom || 20, 10),
        left: parseInt(LABELS.marginLeft || (labelWidth + labelMargin), 10),
    };

    var ticksX = parseInt(LABELS.ticksX || 7, 10);
    var roundTicksFactor = parseInt(LABELS.roundTicksFactor || 5, 10);

    // Clear existing graphic (for redraw)
    var containerElement = d3.select(config['container']);
    containerElement.html('');

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphic-wrapper');

     // Calculate actual chart dimensions
    var innerWidth = chartWrapper.node().getBoundingClientRect().width;
    var chartWidth = innerWidth - margins['left'] - margins['right'];
    var chartHeight = (groupHeight + groupGap) * numGroups - groupGap;

    /*
     * Create D3 scale objects.
     */
    var min = d3.min(config['data'], function(d) {
        return d3.min(d['values'], function(v) {
            return Math.floor(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    });

    if ('minX' in LABELS && LABELS.minX !== '') {
        min = parseFloat(LABELS.minX, 10);
    } else if (min > 0) {
        min = 0;
    }

    var max = d3.max(config['data'], function(d) {
        return d3.max(d['values'], function(v) {
            return Math.ceil(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    })

    if ('maxX' in LABELS && LABELS.maxX !== '') {
        max = parseFloat(LABELS.maxX, 10);
    }

    var xScale = d3.scale.linear()
        .domain([
            min,
            max
        ])
        .range([0, chartWidth]);

    var yScale = d3.scale.linear()
        .range([chartHeight, 0]);

    var colorList = colorArray(LABELS, monochromeColors);
    var colorScale = d3.scale.ordinal()
        .domain(_.pluck(config['data'][0]['values'], labelColumn))
        .range(colorList);

    var chartElement = chartWrapper.append('svg')
        .attr('width', chartWidth + margins['left'] + margins['right'])
        .attr('height', chartHeight + margins['top'] + margins['bottom'])
        .append('g')
        .attr('transform', 'translate(' + margins['left'] + ',' + margins['top'] + ')');

    /*
     * Create D3 axes.
     */
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom')
        .ticks(ticksX)
        .tickFormat(function(d) {
            return d.toFixed(0) + '%';
        });

    /*
     * Render axes to chart.
     */
    // chartElement.append('g')
    //     .attr('class', 'x axis')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxis);

    /*
     * Render grid to chart.
     */
    var xAxisGrid = function() {
        return xAxis;
    };

    // chartElement.append('g')
    //     .attr('class', 'x grid')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxisGrid()
    //         .tickSize(-chartHeight, 0, 0)
    //         .tickFormat('')
    //     );

    /*
     * Render bars to chart.
     */
    var barGroups = chartElement.selectAll('.bars')
        .data(config['data'])
        .enter()
        .append('g')
            .attr('class', 'g bars')
            .attr('transform', function(d, i) {
                return makeTranslate(0, (groupHeight + groupGap) * i + 20);
            });

    barGroups.selectAll('rect')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('rect')
            .attr('x', function(d) {
                if (d[valueColumn] >= 0) {
                    return xScale(0);
                }

                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                if (i == 0) {
                    return 0;
                }

                return (barHeight * i) + (barGap * i);
            })
            .attr('width', function(d) {
                return Math.abs(xScale(0) - xScale(d[valueColumn]));
            })
            .attr('height', barHeight)
            .attr('fill', function(d) {
            	return colorScale(d[labelColumn]);
            })
            .attr('class', function(d) {
                return 'y-' + d[labelColumn];
            });

    barGroups.append('text')
            .attr('x', 0)
            .attr('y', -8)
            .attr('class', 'group-label')
            .text(function (d) { return d.key; });

    var labelData = [];
    _.each(config['data'], function (d) {
        _.each(d.values, function (e) {
            labelData.push(e.label)
        });
    });

    /*
     * Render bar labels.
     */
    chartWrapper.append('ul')
        .attr('class', 'labels')
        .attr('style', formatStyle({
            'width': labelWidth + 'px',
            'top': margins['top'] + 'px',
            'left': '0'
        }))
        .selectAll('li')
        .data(labelData)
        .enter()
        .append('li')
            .attr('style', function(d,i) {
                var index = Math.floor(i / numGroupBars);
                var top = (groupHeight + groupGap) * index;
                top += (i % numGroupBars) * (barHeight + barGap);
                top += 20;

                return formatStyle({
                    'width': (labelWidth - 10) + 'px',
                    'height': barHeight + 'px',
                    'left': '0',
                    'top': top + 'px;'
                });
            })
            .attr('class', function(d,i) {
                return classify(d);//['key']);
            })
            .append('span')
                .text(function(d) {
                    return d;//['key']
                });

    if (LABELS.theme == "highlight") {
        chartWrapper.on("mousemove", function (e) {
            var pos = d3.mouse(chartWrapper.node())
            var gh = groupHeight + 10;
            var index = Math.floor(pos[1] / gh);
            var relativeY = pos[1] - index * gh;
            var barIndex = Math.floor((relativeY - 20) / (barHeight + barGap));

            chartWrapper.selectAll(".value text.over").classed('over', false);
            chartWrapper.selectAll(".bars rect").attr('fill', function (d, i) {
                return colorScale(i);
            });

            if (relativeY <= 20) {
                // in the group heading
                return;
            }

            chartWrapper.selectAll(".bars:nth-child("+(index+1)+") rect:nth-child("+(barIndex+1)+")")
                .attr('fill', highlightColor);
            chartWrapper.selectAll(".bars:nth-child("+(index+1)+") .value text.out:nth-child("+(barIndex+1)+")")
                .classed('over', true);
        });
    }

    /*
     * Render bar values.
     */
    barGroups.append('g')
        .attr('class', 'value')
        .selectAll('text')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('text')
            .text(function(d) {
                return formattedNumber(d[valueColumn], LABELS.prefixX, LABELS.suffixX, LABELS.maxDecimalPlaces);
            })
            .attr('x', function(d) {
                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                return (barHeight + barGap) * i;
            })
            .attr('dx', function(d) {
                var xStart = xScale(d[valueColumn]);
                var textWidth = this.getComputedTextLength()

                // Negative case
                if (d[valueColumn] < 0) {
                    var outsideOffset = -(valueGap + textWidth);

                    if (xStart + outsideOffset < 0) {
                        d3.select(this).classed('in', true)
                        return valueGap;
                    } else {
                        d3.select(this).classed('out', true)
                        return outsideOffset;
                    }
                // Positive case
                } else {
                    if (xStart + valueGap + textWidth > chartWidth) {
                        d3.select(this).classed('in', true)
                        return -(valueGap + textWidth);
                    } else {
                        d3.select(this).classed('out', true)
                        return valueGap;
                    }
                }
            })
            .attr('dy', (barHeight / 2) + 4);
}

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;
<commit_msg>Lint and tidy grouped bar chart JS<commit_after>// Global vars
var pymChild = null;
var isMobile = false;

/*
 * Initialize the graphic.
 */
var onWindowLoaded = function () {
    if (Modernizr.svg) {
        formatData();

        pymChild = new pym.Child({
            renderCallback: render,
        });
    } else {
        pymChild = new pym.Child({});
    }
};

/*
 * Format graphic data for processing by D3.
 */
var formatData = function() {
    DATA.forEach(function(d) {
        d.key = d['Group'];
        d.values = [];

        _.each(d, function (v, k) {
            if (_.contains(['Group', 'key', 'values'], k)) {
                return;
            }

            d.values.push({
                label: k,
                amt: +v,
            });
            delete d[k];
        });

        delete d['Group'];
    });
};

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function (containerWidth) {
    if (!containerWidth) {
        containerWidth = DEFAULT_WIDTH;
    }

    if (containerWidth <= MOBILE_THRESHOLD) {
        isMobile = true;
    } else {
        isMobile = false;
    }

    // Render the chart!
    renderGroupedBarChart({
        container: '#grouped-bar-chart',
        width: containerWidth,
    });

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
};

/*
 * Render a bar chart.
 */
var renderGroupedBarChart = function (config) {
    /*
     * Setup chart container.
     */
    var labelColumn = 'label';
    var valueColumn = 'amt';

    var numGroups = DATA.length;
    var numGroupBars = DATA[0].values.length;

    var barHeight = parseInt(LABELS.barHeight || 25, 10);
    var barGap = parseInt(LABELS.barGap || 2, 10);
    var groupGap = parseInt(LABELS.groupGap || 30, 10);
    var labelWidth = parseInt(LABELS.labelWidth || 85, 10);
    var labelMargin = parseInt(LABELS.labelMargin || 6, 10);
    var valueGap = parseInt(LABELS.valueGap || 6, 10);
    var groupHeight = (barHeight + barGap) * numGroupBars - barGap;

    var margins = {
        top: parseInt(LABELS.marginTop || 0, 10),
        right: parseInt(LABELS.marginRight || 15, 10),
        bottom: parseInt(LABELS.marginBottom || 20, 10),
        left: parseInt(LABELS.marginLeft || (labelWidth + labelMargin), 10),
    };

    var ticksX = parseInt(LABELS.ticksX || 7, 10);
    var roundTicksFactor = parseInt(LABELS.roundTicksFactor || 5, 10);

    // Clear existing graphic (for redraw)
    var containerElement = d3.select(config.container);
    containerElement.html('');

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphic-wrapper');

     // Calculate actual chart dimensions
    var innerWidth = chartWrapper.node().getBoundingClientRect().width;
    var chartWidth = innerWidth - margins.left - margins.right;
    var chartHeight = (groupHeight + groupGap) * numGroups - groupGap;

    /*
     * Create D3 scale objects.
     */
    var min = d3.min(DATA, function(d) {
        return d3.min(d.values, function(v) {
            return Math.floor(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    });

    if ('minX' in LABELS && LABELS.minX !== '') {
        min = parseFloat(LABELS.minX, 10);
    } else if (min > 0) {
        min = 0;
    }

    var max = d3.max(DATA, function (d) {
        return d3.max(d.values, function (v) {
            return Math.ceil(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    });

    if ('maxX' in LABELS && LABELS.maxX !== '') {
        max = parseFloat(LABELS.maxX, 10);
    }

    var xScale = d3.scale.linear()
        .domain([min, max])
        .range([0, chartWidth]);

    var yScale = d3.scale.linear()
        .range([chartHeight, 0]);

    var colorList = colorArray(LABELS, monochromeColors);
    var colorScale = d3.scale.ordinal()
        .domain(_.pluck(DATA[0].values, labelColumn))
        .range(colorList);

    var chartElement = chartWrapper.append('svg')
        .attr({
            width: chartWidth + margins.left + margins.right,
            height: chartHeight + margins.top + margins.bottom,
        })
        .append('g')
            .attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');

    /*
     * Create D3 axes.
     */
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom')
        .ticks(ticksX)
        .tickFormat(function (d) {
            return d.toFixed(0) + '%';
        });

    /*
     * Render axes to chart.
     */
    /*
    // chartElement.append('g')
    //     .attr('class', 'x axis')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxis);
    */

    /*
     * Render grid to chart.
     */
    var xAxisGrid = function () {
        return xAxis;
    };

    /*
    // chartElement.append('g')
    //     .attr('class', 'x grid')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxisGrid()
    //         .tickSize(-chartHeight, 0, 0)
    //         .tickFormat('')
    //     );
    */

    /*
     * Render bars to chart.
     */
    var barGroups = chartElement.selectAll('.bars')
        .data(DATA)
        .enter()
        .append('g')
            .attr('class', 'g bars')
            .attr('transform', function (d, i) {
                return makeTranslate(0, (groupHeight + groupGap) * i + 20);
            });

    barGroups.selectAll('rect')
        .data(function (d) {
            return d.values;
        })
        .enter()
        .append('rect')
            .attr('x', function (d) {
                if (d[valueColumn] >= 0) {
                    return xScale(0);
                }

                return xScale(d[valueColumn]);
            })
            .attr('y', function (d, i) {
                if (i === 0) {
                    return 0;
                }

                return (barHeight * i) + (barGap * i);
            })
            .attr('width', function (d) {
                return Math.abs(xScale(0) - xScale(d[valueColumn]));
            })
            .attr('height', barHeight)
            .attr('fill', function (d) {
                return colorScale(d[labelColumn]);
            })
            .attr('class', function (d) {
                return 'y-' + d[labelColumn];
            });

    barGroups.append('text')
            .attr('x', 0)
            .attr('y', -8)
            .attr('class', 'group-label')
            .text(function (d) { return d.key; });

    var labelData = [];
    _.each(DATA, function (d) {
        _.each(d.values, function (e) {
            labelData.push(e.label);
        });
    });

    /*
     * Render bar labels.
     */
    chartWrapper.append('ul')
        .attr('class', 'labels')
        .attr('style', formatStyle({
            width: labelWidth + 'px',
            top: margins.top + 'px',
            left: 0,
        }))
        .selectAll('li')
        .data(labelData)
        .enter()
        .append('li')
            .attr('style', function(d,i) {
                var index = Math.floor(i / numGroupBars);
                var top = (groupHeight + groupGap) * index;
                top += (i % numGroupBars) * (barHeight + barGap);
                top += 20;

                return formatStyle({
                    width: (labelWidth - 10) + 'px',
                    height: barHeight + 'px',
                    left: 0,
                    top: top + 'px'
                });
            })
            .attr('class', function(d,i) {
                return classify(d);//.key);
            })
            .append('span')
                .text(function(d) {
                    return d;//.key
                });

    if (LABELS.theme == 'highlight') {
        chartWrapper.on('mousemove', function (e) {
            var pos = d3.mouse(chartWrapper.node());
            var gh = groupHeight + 10;
            var index = Math.floor(pos[1] / gh);
            var relativeY = pos[1] - index * gh;
            var barIndex = Math.floor((relativeY - 20) / (barHeight + barGap));

            chartWrapper.selectAll('.value text.over')
                .classed('over', false);
            chartWrapper.selectAll('.bars rect')
                .attr('fill', function (d, i) {
                    return colorScale(i);
                });

            if (relativeY <= 20) {
                // in the group heading
                return;
            }

            chartWrapper.selectAll('.bars:nth-child('+(index+1)+') rect:nth-child('+(barIndex+1)+')')
                .attr('fill', highlightColor);
            chartWrapper.selectAll('.bars:nth-child('+(index+1)+') .value text.out:nth-child('+(barIndex+1)+')')
                .classed('over', true);
        });
    }

    /*
     * Render bar values.
     */
    barGroups.append('g')
        .attr('class', 'value')
        .selectAll('text')
        .data(function(d) {
            return d.values;
        })
        .enter()
        .append('text')
            .text(function(d) {
                return formattedNumber(d[valueColumn], LABELS.prefixX, LABELS.suffixX, LABELS.maxDecimalPlaces);
            })
            .attr('x', function(d) {
                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                return (barHeight + barGap) * i;
            })
            .attr('dx', function(d) {
                var xStart = xScale(d[valueColumn]);
                var textWidth = this.getComputedTextLength();

                // Negative case
                if (d[valueColumn] < 0) {
                    var outsideOffset = -(valueGap + textWidth);

                    if (xStart + outsideOffset < 0) {
                        d3.select(this).classed('in', true);
                        return valueGap;
                    } else {
                        d3.select(this).classed('out', true);
                        return outsideOffset;
                    }
                // Positive case
                } else {
                    if (xStart + valueGap + textWidth > chartWidth) {
                        d3.select(this).classed('in', true);
                        return -(valueGap + textWidth);
                    } else {
                        d3.select(this).classed('out', true);
                        return valueGap;
                    }
                }
            })
            .attr('dy', (barHeight / 2) + 4);
};

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;
<|endoftext|>