<commit_before>var _ = require('underscore')
;

var Job = function (vals) {
  var me = this;
  this.is_job = true;

  _.each(vals, function (v, k) {
    if (k === 'func' && !_.isFunction(v)) {
      me[k] = function (j) {
        var args = _.toArray(v);
        var o = args.shift();
        var m = args.shift();
        args.push(j);
        return o[m].apply( o, args );
      }
    } else {
      me[k] = v;
    }
  });

  me.finish = function () {
    var args = _.toArray(arguments);
    args.unshift(me);
    me.river.finish.apply(me.river, args);
    return this;
  };

  me.not_found = function (msg) {
    me.stop('not_found', msg);
    return me;
  };

  me.invalid = function (msg) {
    me.stop('invalid', msg);
    return me;
  };

  me.error = function (msg) {
    me.stop('error', msg);
    return me;
  };

};

Job.new = function (vals) {
  var j = new Job(vals);
  return j;
};

Job.prototype.concat = function (fin) {
  var me = this;
  var new_j = {
    finish : function (result) {
      me.finish(result);
    }
  };

  _.each(['not_found', 'invalid', 'error', 'is_dup'], function (v) {
    new_j[v] = me[v];
  });

  return new_j;
};
Job.prototype.stop = function (type, msg) {
  var me            = this;
  me[type + '_msg'] = msg;

  if (type === 'not_found' && me.continue_on_not_found) {
    var arr   = me.continue_on_not_found;
    var f     = arr.pop();
    var id    = arr.pop();
    var group = arr.pop();
    me.continue_on_not_found = null;
    var j = Job.new({
      group  : group,
      id     : id,
      func   : f,
      river  : me.river,
      events : me.events
    });
    j.func(j);
    return me;
  }

  var funcs = me.events[type] || [];
  me.river.stop();

  if (!funcs.length && type !== 'error') {
    if (!this.river.parent_job)
      throw new Error(msg);
  }

  if (_.isArray(msg))
    msg = msg.join(' ');

  _.each(funcs, function (e) {
    e(msg, me);
  });

  if (type === 'error' || this.river.parent_job)
    return this.river.emit(type, msg, me);

  return this;
};

var River = exports.River = function () {};
River.uniq_id = 0;
River.valid_event_names = {
  'river': [
    'error',
    'finish',
    'job finish',
    'before job'
  ],
  'job': [
    'not_found',
    'invalid',
    'error'
  ]};

var E_R   = River.valid_event_names['river'];
var E_JOB = River.valid_event_names['job'];

River.validate_event_name = function (list, r, name) {
  if (_.contains(list, name))
    return r;

  r.error("Invalid " + cat + " event name: " + name);
};

River.new = function () {

  var me = new River();
  me.continue_on_not_found = null;
  me.job_list     = [];
  me.input_list   = [];
  me.waits        = [];
  me.events       = { finish: [] };
  me.replys       = [];
  me.named_replys ={};
  me.data         = {};
  me.uniq_job_id  = 0;
  me.is_river     = true;

  var args = _.toArray(arguments);
  while(args.length) {
    var temp = args.shift();
    if (_.isString(temp)) {
      if (!me.group)
        me.group = temp;
      else if (!me.id)
        me.id = temp;
    } else if (temp && temp.length) {
      me.parent_job = _.find(temp, function (v) {
        return v && v.is_job;
      });
    } else  if (_.isNull(temp) && !me.parent_job)
      me.parent_job = temp;
    else if (temp && temp.is_job)
      me.parent_job = temp;
    else
      me.error('Unknown argument: ' + temp);
  }

  if (!me.group)
    me.group = "no group name";

  if (me.id === undefined || me.id === null)
    me.id = ++River.uniq_id;

  return me;
};

River.prototype.validate_event_name = function (name) {
  River.validate_event_name(E_R, this, name);
  return this;
};

River.prototype.job = function () {

  var me     = this;
  var events = me.for_next_job || {};
  var args   = _.toArray(arguments);
  var args_l = args.length;
  var job    = null;

  switch (args_l) {
    case 2:
      var func  = args.pop();
      var group = args.pop();
      var id    = args.pop();
      break;
    default:
      var func  = args.pop();
      var id    = args.pop();
      var group = args.pop();
  };

  if (group === undefined)
    group = 'no group';
  if (id === undefined)
    id = ++this.uniq_job_id;

  me.job_list.push(job = Job.new({
    group     : group,
    id        : id,
    func      : func,
    river     : me,
    events    : events,
    continue_on_not_found : me.continue_on_not_found
  }));

  me.continue_on_not_found = null;

  return me;
};

River.prototype.continue_on_job = function (name, job_name, job_id, func) {
  if (name !== 'not_found')
    return this.error('River can\'t handle continues for: ' + name);

  if (this.continue_on_not_found)
    return this.error('Already defined: continue_on_job ' + name);

  var args = _.toArray(arguments);
  if (_.isFunction(job_id)) {
    func   = job_id;
    job_id = ++this.uniq_job_id;
  }

  this.continue_on_not_found = [name, job_name, job_id, func];

  return this;
};

River.prototype.on_job = function (name, func) {
  River.validate_event_name(E_JOB, this, name);
  var e = this.for_next_job;
  if (!e)
    e = this.for_next_job = {};
  if (!e[name])
    e[name] = [];
  e[name].push( func );

  return this;
};

River.prototype.on_error = function (f) {
  return this.on('error', f);
};

River.prototype.on_finish = function (f) {
  return this.on('finish', f);
};

River.prototype.on = function (name, f) {
  this.validate_event_name(name);

  var events = this.events[name];
  if (!events) {
    events = this.events[name] = [];
  }

  events.push(f);
  return this;
};

River.prototype.error = function (msg, job) {
  var err = null;
  if (_.isString(msg))
    err = new Error(msg);
  else if (msg && msg.error_msg)
    err = new Error(msg.error_msg);
  else
    err = msg;

  this.emit('error', err, job);
  return this;
};

River.prototype.emit = function (name, msg, job_or_me) {
  var error = 'error';

  if(_.contains(E_JOB, name) && this.parent_job) {
    var result = this.parent_job[name](msg);
    if (name !== error)
      return result;
  }

  if (!_.contains(E_R, name)) {
    msg = new Error('Invalid event name: ' + name);
    name = error;
  }

  var e = this.events[name] || [];
  var me = this, i = 0, l = e.length;

  if ( l === 0 && name === error && !this.parent_job) {
    if (_.isString(msg))
      throw new Error(msg);
    else
      throw msg;
  }

  while( i < l ) {
    if (msg)
      e[i](msg, job_or_me);
    else
      e[i](job_or_me);
    ++i;
  }

  if ( me.parent_job  && me.parent_job[name] ) {
    if (name === 'finish' && !e.length)
      return me.parent_job[name](msg || me.last_reply(), job_or_me);
  }

  return true;
};

River.prototype.stop = function () {
  this.is_stopped = true;
  return this;
};

River.prototype.reply_counter = -1;
River.prototype.reply = function () {
  var me = this;

  var group = 'save reply', id = ++me.reply_counter, func;
  _.each(arguments, function (v) {
    if (_.isString(v)) {
      if (group)
        id = v;
      else
        group = v;
    } else {
      func = v;
    }
  });

  me.job(group, id, function (j) {
    j.finish(func(j.river.last_reply(), me));
  });

  return me;
};

River.prototype.reply_for = function (group, id) {
  var me = this;
  var reply = me.reply_s_for(group, id);
  return reply[0];
};

River.prototype.reply_s_for = function (group, id) {
  var me = this;
  var replys = [];
  var use_both = _.compact([group, id]).length === 2;
  _.find(me.named_replys, function (arr, name) {
    if (use_both) {
      if (name === group+':'+id) {
        replys = arr;
        return true;
      }
    } else {
      if (name.index_of(group+':') > -1)
        replys.push(arr);
    }
    return false;
  });

  return replys;
};

River.prototype.first_reply = function () {
  return _.first(this.replys)[0];
};

River.prototype.last_reply = function () {
  return ( _.last(this.replys) || [] )[0];
};

River.prototype.finish = function () {
  if (this.is_stopped || (this.parent_job && this.parent_job.river.is_finish))
    return this;

  if (this.is_finish) {
    this.error(new Error('Job already finished: ' +
                         this.group + ': ' + this.id));
  }

  var job    = arguments[0];
  var replys = _.toArray(arguments).slice(1);
  this.waits.shift();

  this.replys.push(replys);
  this.named_replys[job.group + ':' + job.id] = replys;

  this.emit('job finish', job);

  if (this.waits.length < 1) {
    this.is_finish = true;
    this.emit('finish', null, this);
  } else {
    ++this.current_job_i;
    this.run_job(this.current_job_i);
  }

  return this;
};

River.prototype.for_each_finish = function (f) {
  this.on('job finish', f);
  return this;
};

River.prototype.verbose = function () {
  this.on('before job', function (j) {
    console['log'](j.group, j.id);
  });
  return this;
};

River.prototype.run_job = function (i) {
  var me = this;
  var current_job = this.job_list[i];
  me.emit('before job', current_job);
  if (current_job.func.length === 2)
    current_job.func(current_job, me.last_reply());
  else
    current_job.func(current_job);
};

River.prototype.run = function (f) {
  if (f)
    this.on_finish(f);

  var target, method_name;
  var l              = this.job_list.length;
  var i              = 0;
  var me             = this;
  this.current_job_i = 0;

  if ( l === 0 ) {
  } else {
    while (i < l) {
      this.waits.push(i);
      ++i;
    }

    me.run_job(this.current_job_i);
  }

  return this;
};

<commit_msg>Fixed: new finish function was not being called.<commit_after>var _ = require('underscore')
;

var Job = function (vals) {
  var me = this;
  this.is_job = true;

  _.each(vals, function (v, k) {
    if (k === 'func' && !_.isFunction(v)) {
      me[k] = function (j) {
        var args = _.toArray(v);
        var o = args.shift();
        var m = args.shift();
        args.push(j);
        return o[m].apply( o, args );
      }
    } else {
      me[k] = v;
    }
  });

  me.finish = function () {
    var args = _.toArray(arguments);
    args.unshift(me);
    me.river.finish.apply(me.river, args);
    return this;
  };

  me.not_found = function (msg) {
    me.stop('not_found', msg);
    return me;
  };

  me.invalid = function (msg) {
    me.stop('invalid', msg);
    return me;
  };

  me.error = function (msg) {
    me.stop('error', msg);
    return me;
  };

};

Job.new = function (vals) {
  var j = new Job(vals);
  return j;
};

Job.prototype.concat = function (fin) {
  var me = this;
  var new_j = {
    finish : function (result) {
      fin(result);
    }
  };

  _.each(['not_found', 'invalid', 'error', 'is_dup'], function (v) {
    new_j[v] = me[v];
  });

  return new_j;
};
Job.prototype.stop = function (type, msg) {
  var me            = this;
  me[type + '_msg'] = msg;

  if (type === 'not_found' && me.continue_on_not_found) {
    var arr   = me.continue_on_not_found;
    var f     = arr.pop();
    var id    = arr.pop();
    var group = arr.pop();
    me.continue_on_not_found = null;
    var j = Job.new({
      group  : group,
      id     : id,
      func   : f,
      river  : me.river,
      events : me.events
    });
    j.func(j);
    return me;
  }

  var funcs = me.events[type] || [];
  me.river.stop();

  if (!funcs.length && type !== 'error') {
    if (!this.river.parent_job)
      throw new Error(msg);
  }

  if (_.isArray(msg))
    msg = msg.join(' ');

  _.each(funcs, function (e) {
    e(msg, me);
  });

  if (type === 'error' || this.river.parent_job)
    return this.river.emit(type, msg, me);

  return this;
};

var River = exports.River = function () {};
River.uniq_id = 0;
River.valid_event_names = {
  'river': [
    'error',
    'finish',
    'job finish',
    'before job'
  ],
  'job': [
    'not_found',
    'invalid',
    'error'
  ]};

var E_R   = River.valid_event_names['river'];
var E_JOB = River.valid_event_names['job'];

River.validate_event_name = function (list, r, name) {
  if (_.contains(list, name))
    return r;

  r.error("Invalid " + cat + " event name: " + name);
};

River.new = function () {

  var me = new River();
  me.continue_on_not_found = null;
  me.job_list     = [];
  me.input_list   = [];
  me.waits        = [];
  me.events       = { finish: [] };
  me.replys       = [];
  me.named_replys ={};
  me.data         = {};
  me.uniq_job_id  = 0;
  me.is_river     = true;

  var args = _.toArray(arguments);
  while(args.length) {
    var temp = args.shift();
    if (_.isString(temp)) {
      if (!me.group)
        me.group = temp;
      else if (!me.id)
        me.id = temp;
    } else if (temp && temp.length) {
      me.parent_job = _.find(temp, function (v) {
        return v && v.is_job;
      });
    } else  if (_.isNull(temp) && !me.parent_job)
      me.parent_job = temp;
    else if (temp && temp.is_job)
      me.parent_job = temp;
    else
      me.error('Unknown argument: ' + temp);
  }

  if (!me.group)
    me.group = "no group name";

  if (me.id === undefined || me.id === null)
    me.id = ++River.uniq_id;

  return me;
};

River.prototype.validate_event_name = function (name) {
  River.validate_event_name(E_R, this, name);
  return this;
};

River.prototype.job = function () {

  var me     = this;
  var events = me.for_next_job || {};
  var args   = _.toArray(arguments);
  var args_l = args.length;
  var job    = null;

  switch (args_l) {
    case 2:
      var func  = args.pop();
      var group = args.pop();
      var id    = args.pop();
      break;
    default:
      var func  = args.pop();
      var id    = args.pop();
      var group = args.pop();
  };

  if (group === undefined)
    group = 'no group';
  if (id === undefined)
    id = ++this.uniq_job_id;

  me.job_list.push(job = Job.new({
    group     : group,
    id        : id,
    func      : func,
    river     : me,
    events    : events,
    continue_on_not_found : me.continue_on_not_found
  }));

  me.continue_on_not_found = null;

  return me;
};

River.prototype.continue_on_job = function (name, job_name, job_id, func) {
  if (name !== 'not_found')
    return this.error('River can\'t handle continues for: ' + name);

  if (this.continue_on_not_found)
    return this.error('Already defined: continue_on_job ' + name);

  var args = _.toArray(arguments);
  if (_.isFunction(job_id)) {
    func   = job_id;
    job_id = ++this.uniq_job_id;
  }

  this.continue_on_not_found = [name, job_name, job_id, func];

  return this;
};

River.prototype.on_job = function (name, func) {
  River.validate_event_name(E_JOB, this, name);
  var e = this.for_next_job;
  if (!e)
    e = this.for_next_job = {};
  if (!e[name])
    e[name] = [];
  e[name].push( func );

  return this;
};

River.prototype.on_error = function (f) {
  return this.on('error', f);
};

River.prototype.on_finish = function (f) {
  return this.on('finish', f);
};

River.prototype.on = function (name, f) {
  this.validate_event_name(name);

  var events = this.events[name];
  if (!events) {
    events = this.events[name] = [];
  }

  events.push(f);
  return this;
};

River.prototype.error = function (msg, job) {
  var err = null;
  if (_.isString(msg))
    err = new Error(msg);
  else if (msg && msg.error_msg)
    err = new Error(msg.error_msg);
  else
    err = msg;

  this.emit('error', err, job);
  return this;
};

River.prototype.emit = function (name, msg, job_or_me) {
  var error = 'error';

  if(_.contains(E_JOB, name) && this.parent_job) {
    var result = this.parent_job[name](msg);
    if (name !== error)
      return result;
  }

  if (!_.contains(E_R, name)) {
    msg = new Error('Invalid event name: ' + name);
    name = error;
  }

  var e = this.events[name] || [];
  var me = this, i = 0, l = e.length;

  if ( l === 0 && name === error && !this.parent_job) {
    if (_.isString(msg))
      throw new Error(msg);
    else
      throw msg;
  }

  while( i < l ) {
    if (msg)
      e[i](msg, job_or_me);
    else
      e[i](job_or_me);
    ++i;
  }

  if ( me.parent_job  && me.parent_job[name] ) {
    if (name === 'finish' && !e.length)
      return me.parent_job[name](msg || me.last_reply(), job_or_me);
  }

  return true;
};

River.prototype.stop = function () {
  this.is_stopped = true;
  return this;
};

River.prototype.reply_counter = -1;
River.prototype.reply = function () {
  var me = this;

  var group = 'save reply', id = ++me.reply_counter, func;
  _.each(arguments, function (v) {
    if (_.isString(v)) {
      if (group)
        id = v;
      else
        group = v;
    } else {
      func = v;
    }
  });

  me.job(group, id, function (j) {
    j.finish(func(j.river.last_reply(), me));
  });

  return me;
};

River.prototype.reply_for = function (group, id) {
  var me = this;
  var reply = me.reply_s_for(group, id);
  return reply[0];
};

River.prototype.reply_s_for = function (group, id) {
  var me = this;
  var replys = [];
  var use_both = _.compact([group, id]).length === 2;
  _.find(me.named_replys, function (arr, name) {
    if (use_both) {
      if (name === group+':'+id) {
        replys = arr;
        return true;
      }
    } else {
      if (name.index_of(group+':') > -1)
        replys.push(arr);
    }
    return false;
  });

  return replys;
};

River.prototype.first_reply = function () {
  return _.first(this.replys)[0];
};

River.prototype.last_reply = function () {
  return ( _.last(this.replys) || [] )[0];
};

River.prototype.finish = function () {
  if (this.is_stopped || (this.parent_job && this.parent_job.river.is_finish))
    return this;

  if (this.is_finish) {
    this.error(new Error('Job already finished: ' +
                         this.group + ': ' + this.id));
  }

  var job    = arguments[0];
  var replys = _.toArray(arguments).slice(1);
  this.waits.shift();

  this.replys.push(replys);
  this.named_replys[job.group + ':' + job.id] = replys;

  this.emit('job finish', job);

  if (this.waits.length < 1) {
    this.is_finish = true;
    this.emit('finish', null, this);
  } else {
    ++this.current_job_i;
    this.run_job(this.current_job_i);
  }

  return this;
};

River.prototype.for_each_finish = function (f) {
  this.on('job finish', f);
  return this;
};

River.prototype.verbose = function () {
  this.on('before job', function (j) {
    console['log'](j.group, j.id);
  });
  return this;
};

River.prototype.run_job = function (i) {
  var me = this;
  var current_job = this.job_list[i];
  me.emit('before job', current_job);
  if (current_job.func.length === 2)
    current_job.func(current_job, me.last_reply());
  else
    current_job.func(current_job);
};

River.prototype.run = function (f) {
  if (f)
    this.on_finish(f);

  var target, method_name;
  var l              = this.job_list.length;
  var i              = 0;
  var me             = this;
  this.current_job_i = 0;

  if ( l === 0 ) {
  } else {
    while (i < l) {
      this.waits.push(i);
      ++i;
    }

    me.run_job(this.current_job_i);
  }

  return this;
};

<|endoftext|>


<commit_before>/**
 * Multipage Router
 *
 * Backbone.Router를 확장하여 여러 페이지로 구성된 웹앱에서 라우팅을 통하여 페이지 이동이 쉽도록 지원한다.
 * 기존의 Router와는 다르게 routes 속성 대신 pages 속성으로 라우팅을 정의한다.
 */

( function( root, factory ) {

	// Require.js가 있을 경우
	if ( typeof define === 'function' && define.amd )
		define( [ 'backbone', 'underscore', 'jquery', 'transition' ], factory );
	else
		root.MultipageRouter = factory( root.Backbone, root._, root.$, root.Transition );

} )( window, function( Backbone, _, $, Transition ) {

	return Backbone.Router.extend( {

		constructor: function( attributes, options ) {

			var self = this;

			// Data Attribute를 사용하면 기존 pages 속성은 삭제되고, 마크업에 의해서 pages 속성이 다시 작성된다.
			if ( this.useDataAttributes ) {

				this.pages = {};

				_.each( $( '[data-url],[data-default-page]' ), function( value, key ) {

					var defaultPage = $( value ).attr( 'data-default-page' );
					var url = $( value ).attr( 'data-url' );
					var fragments = [];

					if ( defaultPage == 'true' ) fragments.push( '' );
					if ( url ) fragments.push( url );

					self.pages[ 'page' + key ] = {
						fragment: fragments,
						el: value
					};
				} );
			}

			/* routes 대신 pages 속성으로 라우팅을 정의한다.
			 * 해당 페이지가 시작되면 먼저 render가 실행되고, Transition이 일어난 후, active가 실행된다.
			 *
			 * pages: {
			 *   page-id: {
			 *     fragment: fragment ID의 패턴, 기존 routes 속성의 key와 동일한 값이며, 여러개를 배열로 지정할 수도 있다.
			 *     el: 현재 페이지를 나타내는 selector
			 *     render: 현재 페이지가 그려져야 할 때 실행될 callback 함수 또는 함수명
			 *     active: 현재 페이지가 완전히 활성화될 때 실행될 callback 함수 또는 함수명
			 *     inactive: 현재 페이지가 비활성화될 때 실행될 callback 함수 또는 함수명
			 *   }
			 * }
			 *
			 * transitions: {
			 *   'from-page-id:to-page-id': 'transition 종류',
			 *   'from-page-id:to-page-id': { type: 'transition 종류', duration: 시간(ms) }
			 * }
			 */
			if ( this.pages ) {

				var routes = this.routes = {};

				_.each( this.pages, function( value, key ) {

					if ( key == 'default' ) {
						if ( value ) routes[ '*path' ] = key + '_handler';
					}
					else if ( value.fragment ) {
						if ( !_.isArray( value.fragment ) ) value.fragment = [ value.fragment ];

						_.each( value.fragment, function( fragment ) {
							routes[ fragment ] = key + '_handler';
						} );
					}

					// 기본적으로 pages와 연결된 el은 숨긴다.
					value.el && $( value.el ).hide();

					self[ key + '_handler' ] = function() {

						var handlerArg = arguments;

						self.runInactive( key );

						var prevPageId = self.currentPageId, prevPage = self.currentPage;
						self.currentPageId = key;
						self.currentPage = value;
						self.runRender.apply( self, arguments );

						// Transition 설정이 있으면 Transition 실행
						var transition = self.findTransition( prevPageId, key );

						if ( transition ) {

							var params = {
								transitionType: transition.type,
								inTarget: { el: value.el },
								outTarget: { el: prevPage.el },
								isReverse: transition.reverse || false,
								done: function() {
									// Transition 완료 후에 active 실행
									self.runActive.apply( self, handlerArg );
								}
							};

							if ( transition.duration ) {
								params.inTarget.duration = transition.duration / 2;
								params.outTarget.duration = transition.duration / 2;
							}

							Transition.launcher( params );
						}
						// Transition 설정이 없으면 그냥 이전 페이지를 숨기고 새 페이지를 보여준다.
						else {
							prevPage && prevPage.el && $( prevPage.el ).hide();
							value.el && $( value.el ).show();

							self.runActive.apply( self, arguments );
						}
					};
				} );
			}

			// data-transition을 사용해서 지정된 Transition 설정을 보관한다.
			// data-transition을 사용하면 현재 페이지는 알 수 있으나 도착 페이지는 현재 시점에서는 알 수 없다.
			// 반대 방향의 설정이 없을 때 동일한 type의 reverse transition을 적용하기 위해 보관한다.
			// data-transition은 click이 가능한 요소면 어디든 붙일 수 있다.
			this.dataTransitions = {};

			$( document ).on( 'click', '[data-transition]', function( e ) {

				var transition = {
					type: $( this ).attr( 'data-transition' )
				};

				var dataDuration = $( this ).attr( 'data-duration' );
				if ( dataDuration ) transition.duration = parseInt( dataDuration );

				self.dataTransitions[ self.currentPageId ] = transition;
				e.stopPropagation();
			} );

			$( document ).on( 'click', ':not([data-transition])', function( e ) {
				delete self.dataTransitions[ self.currentPageId ];
				e.stopPropagation();
			} );

			// 여기서 extend 된 Router들의 initialize가 실행된다.
			// 따라서 extend 된 Router들의 initialize보다 먼저 실행하려면 앞쪽에, 나중에 실행하려면 뒷쪽에 코드를 작성한다.
			Backbone.Router.apply( this, arguments );
		},

		_findDataTransitionObject: function( from ) {
			var transition = this.dataTransitions[ from ];
			return _.isString( transition ) ? { type: transition } : transition;
		},

		_findTransitionObject: function( from, to ) {
			if ( !this.transitions ) return null;
			var transition = this.transitions[ from + ':' + to ];
			return _.isString( transition ) ? { type: transition } : transition;
		},

		// from 페이지에서 to 페이지로 이동할 때의 Transition 설정을 찾는다.
		// returns {
		//   type: transition의 종류
		//   duration: 생략 가능, 이동 시간 (ms)
		//   reverse: 생략 가능, true/false
		// }
		findTransition: function( from, to ) {

			var transition;

			if ( transition = this._findDataTransitionObject( from ) ) {
				transition.reverse = false;
				return transition;
			}
			if ( transition = this._findDataTransitionObject( to ) ) {
				transition.reverse = true;
				return transition;
			}

			// from -> to 방향의 Transition 설정이 없으면 to -> from 방향을 찾는다.
			if ( transition = this._findTransitionObject( from, to ) ) {
				transition.reverse = false;
				return transition;
			}
			if ( transition = this._findTransitionObject( to, from ) ) {
				transition.reverse = true;
				return transition;
			}

			return null;
		},

		// 현재 페이지의 render callback을 실행한다.
		runRender: function() {

			if ( !this.currentPage ) return;

			$( this.currentPage.el ).trigger( 'render', _.values( arguments ) );

			if ( !this.currentPage.render ) return;

			// render는 함수 또는 함수 이름을 지정할 수 있다.
			var renderCallback = _.isFunction( this.currentPage.render ) ? this.currentPage.render : this[ this.currentPage.render ];

			_.isFunction( renderCallback ) && renderCallback.apply( this, arguments );
		},

		// 현재 페이지의 active callback을 실행한다.
		runActive: function() {

			if ( !this.currentPage ) return;

			$( this.currentPage.el ).trigger( 'active', _.values( arguments ) );

			if ( !this.currentPage.active ) return;

			// active는 함수 또는 함수 이름을 지정할 수 있다.
			var activeCallback = _.isFunction( this.currentPage.active ) ? this.currentPage.active : this[ this.currentPage.active ];

			_.isFunction( activeCallback ) && activeCallback.apply( this, arguments );
		},

		// 현재 페이지의 inactive callback을 실행한다.
		runInactive: function( nextPageId ) {

			if ( !this.currentPage ) return;

			$( this.currentPage.el ).trigger( 'inactive', _.values( arguments ) );

			if ( !this.currentPage.inactive ) return;

			// inactive는 함수 또는 함수 이름을 지정할 수 있다.
			var inactiveCallback = _.isFunction( this.currentPage.inactive ) ? this.currentPage.inactive : this[ this.currentPage.inactive ];

			// 새로 변경될 화면의 id를 넘겨준다.
			_.isFunction( inactiveCallback ) && inactiveCallback( nextPageId );
		}
	} );
} );
<commit_msg>Multipage Router: data-transition이 없는 요소를 클릭할 때 stopPropagation 삭제<commit_after>/**
 * Multipage Router
 *
 * Backbone.Router를 확장하여 여러 페이지로 구성된 웹앱에서 라우팅을 통하여 페이지 이동이 쉽도록 지원한다.
 * 기존의 Router와는 다르게 routes 속성 대신 pages 속성으로 라우팅을 정의한다.
 */

( function( root, factory ) {

	// Require.js가 있을 경우
	if ( typeof define === 'function' && define.amd )
		define( [ 'backbone', 'underscore', 'jquery', 'transition' ], factory );
	else
		root.MultipageRouter = factory( root.Backbone, root._, root.$, root.Transition );

} )( window, function( Backbone, _, $, Transition ) {

	return Backbone.Router.extend( {

		constructor: function( attributes, options ) {

			var self = this;

			// Data Attribute를 사용하면 기존 pages 속성은 삭제되고, 마크업에 의해서 pages 속성이 다시 작성된다.
			if ( this.useDataAttributes ) {

				this.pages = {};

				_.each( $( '[data-url],[data-default-page]' ), function( value, key ) {

					var defaultPage = $( value ).attr( 'data-default-page' );
					var url = $( value ).attr( 'data-url' );
					var fragments = [];

					if ( defaultPage == 'true' ) fragments.push( '' );
					if ( url ) fragments.push( url );

					self.pages[ 'page' + key ] = {
						fragment: fragments,
						el: value
					};
				} );
			}

			/* routes 대신 pages 속성으로 라우팅을 정의한다.
			 * 해당 페이지가 시작되면 먼저 render가 실행되고, Transition이 일어난 후, active가 실행된다.
			 *
			 * pages: {
			 *   page-id: {
			 *     fragment: fragment ID의 패턴, 기존 routes 속성의 key와 동일한 값이며, 여러개를 배열로 지정할 수도 있다.
			 *     el: 현재 페이지를 나타내는 selector
			 *     render: 현재 페이지가 그려져야 할 때 실행될 callback 함수 또는 함수명
			 *     active: 현재 페이지가 완전히 활성화될 때 실행될 callback 함수 또는 함수명
			 *     inactive: 현재 페이지가 비활성화될 때 실행될 callback 함수 또는 함수명
			 *   }
			 * }
			 *
			 * transitions: {
			 *   'from-page-id:to-page-id': 'transition 종류',
			 *   'from-page-id:to-page-id': { type: 'transition 종류', duration: 시간(ms) }
			 * }
			 */
			if ( this.pages ) {

				var routes = this.routes = {};

				_.each( this.pages, function( value, key ) {

					if ( key == 'default' ) {
						if ( value ) routes[ '*path' ] = key + '_handler';
					}
					else if ( value.fragment ) {
						if ( !_.isArray( value.fragment ) ) value.fragment = [ value.fragment ];

						_.each( value.fragment, function( fragment ) {
							routes[ fragment ] = key + '_handler';
						} );
					}

					// 기본적으로 pages와 연결된 el은 숨긴다.
					value.el && $( value.el ).hide();

					self[ key + '_handler' ] = function() {

						var handlerArg = arguments;

						self.runInactive( key );

						var prevPageId = self.currentPageId, prevPage = self.currentPage;
						self.currentPageId = key;
						self.currentPage = value;
						self.runRender.apply( self, arguments );

						// Transition 설정이 있으면 Transition 실행
						var transition = self.findTransition( prevPageId, key );

						if ( transition ) {

							var params = {
								transitionType: transition.type,
								inTarget: { el: value.el },
								outTarget: { el: prevPage.el },
								isReverse: transition.reverse || false,
								done: function() {
									// Transition 완료 후에 active 실행
									self.runActive.apply( self, handlerArg );
								}
							};

							if ( transition.duration ) {
								params.inTarget.duration = transition.duration / 2;
								params.outTarget.duration = transition.duration / 2;
							}

							Transition.launcher( params );
						}
						// Transition 설정이 없으면 그냥 이전 페이지를 숨기고 새 페이지를 보여준다.
						else {
							prevPage && prevPage.el && $( prevPage.el ).hide();
							value.el && $( value.el ).show();

							self.runActive.apply( self, arguments );
						}
					};
				} );
			}

			// data-transition을 사용해서 지정된 Transition 설정을 보관한다.
			// data-transition을 사용하면 현재 페이지는 알 수 있으나 도착 페이지는 현재 시점에서는 알 수 없다.
			// 반대 방향의 설정이 없을 때 동일한 type의 reverse transition을 적용하기 위해 보관한다.
			// data-transition은 click이 가능한 요소면 어디든 붙일 수 있다.
			this.dataTransitions = {};

			$( document ).on( 'click', '[data-transition]', function( e ) {

				var transition = {
					type: $( this ).attr( 'data-transition' )
				};

				var dataDuration = $( this ).attr( 'data-duration' );
				if ( dataDuration ) transition.duration = parseInt( dataDuration );

				self.dataTransitions[ self.currentPageId ] = transition;
				e.stopPropagation();
			} );

			$( document ).on( 'click', ':not([data-transition])', function( e ) {
				delete self.dataTransitions[ self.currentPageId ];
			} );

			// 여기서 extend 된 Router들의 initialize가 실행된다.
			// 따라서 extend 된 Router들의 initialize보다 먼저 실행하려면 앞쪽에, 나중에 실행하려면 뒷쪽에 코드를 작성한다.
			Backbone.Router.apply( this, arguments );
		},

		_findDataTransitionObject: function( from ) {
			var transition = this.dataTransitions[ from ];
			return _.isString( transition ) ? { type: transition } : transition;
		},

		_findTransitionObject: function( from, to ) {
			if ( !this.transitions ) return null;
			var transition = this.transitions[ from + ':' + to ];
			return _.isString( transition ) ? { type: transition } : transition;
		},

		// from 페이지에서 to 페이지로 이동할 때의 Transition 설정을 찾는다.
		// returns {
		//   type: transition의 종류
		//   duration: 생략 가능, 이동 시간 (ms)
		//   reverse: 생략 가능, true/false
		// }
		findTransition: function( from, to ) {

			var transition;

			if ( transition = this._findDataTransitionObject( from ) ) {
				transition.reverse = false;
				return transition;
			}
			if ( transition = this._findDataTransitionObject( to ) ) {
				transition.reverse = true;
				return transition;
			}

			// from -> to 방향의 Transition 설정이 없으면 to -> from 방향을 찾는다.
			if ( transition = this._findTransitionObject( from, to ) ) {
				transition.reverse = false;
				return transition;
			}
			if ( transition = this._findTransitionObject( to, from ) ) {
				transition.reverse = true;
				return transition;
			}

			return null;
		},

		// 현재 페이지의 render callback을 실행한다.
		runRender: function() {

			if ( !this.currentPage ) return;

			$( this.currentPage.el ).trigger( 'render', _.values( arguments ) );

			if ( !this.currentPage.render ) return;

			// render는 함수 또는 함수 이름을 지정할 수 있다.
			var renderCallback = _.isFunction( this.currentPage.render ) ? this.currentPage.render : this[ this.currentPage.render ];

			_.isFunction( renderCallback ) && renderCallback.apply( this, arguments );
		},

		// 현재 페이지의 active callback을 실행한다.
		runActive: function() {

			if ( !this.currentPage ) return;

			$( this.currentPage.el ).trigger( 'active', _.values( arguments ) );

			if ( !this.currentPage.active ) return;

			// active는 함수 또는 함수 이름을 지정할 수 있다.
			var activeCallback = _.isFunction( this.currentPage.active ) ? this.currentPage.active : this[ this.currentPage.active ];

			_.isFunction( activeCallback ) && activeCallback.apply( this, arguments );
		},

		// 현재 페이지의 inactive callback을 실행한다.
		runInactive: function( nextPageId ) {

			if ( !this.currentPage ) return;

			$( this.currentPage.el ).trigger( 'inactive', _.values( arguments ) );

			if ( !this.currentPage.inactive ) return;

			// inactive는 함수 또는 함수 이름을 지정할 수 있다.
			var inactiveCallback = _.isFunction( this.currentPage.inactive ) ? this.currentPage.inactive : this[ this.currentPage.inactive ];

			// 새로 변경될 화면의 id를 넘겨준다.
			_.isFunction( inactiveCallback ) && inactiveCallback( nextPageId );
		}
	} );
} );
<|endoftext|>


<commit_before>var bignum = require('bignum'),
  async = require('async'),
  assert = require('assert'),
  rlp = require('rlp'),
  utils = require('../lib/utils'),
  Account = require('../lib/account.js'),
  Block = require('../lib/block.js');

var testUtils = exports;

/**
 * verifyAccountPostConditions using JSON from tests repo
 * @param {[type]}   state    DB/trie
 * @param {[type]}   account  to verify
 * @param {[type]}   acctData postconditions JSON from tests repo
 * @param {Function} cb       completion callback
 */
exports.verifyAccountPostConditions = function(state, account, acctData, cb) {
  // validate the postcondition of account
  assert.strictEqual(testUtils.toDecimal(account.balance), acctData.balance, 'balance mismatch');
  assert.strictEqual(testUtils.toDecimal(account.nonce), acctData.nonce, 'nonce mismatch');

  // validate storage
  var storageKeys = Object.keys(acctData.storage);
  if (storageKeys.length > 0 && false) {
    state.root = account.stateRoot.toString('hex');
    storageKeys.forEach(function(skey) {
      state.get(testUtils.fromAddress(skey), function(err, data) {
        assert(!err);
        assert.strictEqual(rlp.decode(data).toString('hex'),
          acctData.storage[skey].slice(2), 'storage mismatch');
        cb();
      });
    });
  } else {
    cb();
  }
};

/**
 * makeRunCallData - helper to create the object for VM.runCall using
 *   the exec object specified in the tests repo
 * @param {Object} testData    object from the tests repo
 * @param {Object} block   that the transaction belongs to
 * @return {Object}        object that will be passed to VM.runCall function
 */
exports.makeRunCallData = function(testData, block) {
  var exec = testData.exec,
    acctData = testData.pre[exec.caller],
    account = new Account();

  account.nonce = testUtils.fromDecimal(acctData.nonce);
  account.balance = testUtils.fromDecimal(acctData.balance);

  return {
    fromAccount: account,
    origin: new Buffer(exec.origin, 'hex'),
    data: new Buffer(exec.code.slice(2), 'hex'), // slice off 0x
    value: bignum(exec.value),
    from: new Buffer(exec.caller, 'hex'),
    to: new Buffer(exec.address, 'hex'),
    gas: exec.gas,
    block: block
  };
};

/**
 * makeRunCallDataWithAccount - helper to create the object for VM.runCall using
 *   the exec object specified in the tests repo
 * @param {Object} testData    object from the tests repo
 * @param {Object} account that is making the call
 * @param {Object} block   that the transaction belongs to
 * @return {Object}        object that will be passed to VM.runCall function
 */
exports.makeRunCallDataWithAccount = function(testData, account, block) {
  var exec = testData.exec;
  return {
    fromAccount: account,
    origin: new Buffer(exec.origin, 'hex'),
    data: new Buffer(exec.code.slice(2), 'hex'), // slice off 0x
    value: bignum(exec.value),
    from: new Buffer(exec.caller, 'hex'),
    to: new Buffer(exec.address, 'hex'),
    gas: exec.gas,
    block: block
  };
};

/**
 * toDecimal - converts buffer to decimal string, no leading zeroes
 * @param  {Buffer}
 * @return {String}
 */
exports.toDecimal = function(buffer) {
  return bignum.fromBuffer(buffer).toString();
};

/**
 * fromDecimal - converts decimal string to buffer
 * @param {String}
 *  @return {Buffer}
 */
exports.fromDecimal = function(string) {
  return utils.intToBuffer(parseInt(string, 10));
};

/**
 * fromAddress - converts hexString address to 256-bit buffer
 * @param  {String} hexString address for example '0x03'
 * @return {Buffer}
 */
exports.fromAddress = function(hexString) {
  hexString = hexString.substring(2);
  return utils.pad256(bignum(hexString, 16).toBuffer());
};

/**
 * makeBlockFromEnv - helper to create a block from the env object in tests repo
 * @param {Object} env object from tests repo
 * @return {Object}  the block
 */
exports.makeBlockFromEnv = function(env) {
  var block = new Block();
  block.header.timestamp = testUtils.fromDecimal(env.currentTimestamp);
  block.header.gasLimit = testUtils.fromDecimal(env.currentGasLimit);
  block.header.parentHash = new Buffer(env.previousHash, 'hex');
  block.header.coinbase = new Buffer(env.currentCoinbase, 'hex');
  block.header.difficulty = testUtils.fromDecimal(env.currentDifficulty);
  block.header.number = testUtils.fromDecimal(env.currentNumber);

  return block;
};

/**
 * makeRunCodeData - helper to create the object for VM.runCode using
 *   the exec object specified in the tests repo
 * @param {Object} exec    object from the tests repo
 * @param {Object} account that the executing code belongs to
 * @param {Object} block   that the transaction belongs to
 * @return {Object}        object that will be passed to VM.runCode function
 */
exports.makeRunCodeData = function(exec, account, block) {
  return {
    account: account,
    origin: new Buffer(exec.origin, 'hex'),
    code: new Buffer(exec.code.slice(2), 'hex'), // slice off 0x
    value: testUtils.fromDecimal(exec.value),
    address: new Buffer(exec.address, 'hex'),
    from: new Buffer(exec.caller, 'hex'),
    data: new Buffer(exec.data.slice(2), 'hex'), // slice off 0x
    gasLimit: exec.gas,
    gasPrice: testUtils.fromDecimal(exec.gasPrice),
    block: block
  };
};

/**
 * storeCode for a given account
 * @param {[type]}   state    trie/DB
 * @param {[type]}   address  of account
 * @param {[type]}   account  for which code belongs to
 * @param {[type]}   code     to store
 * @param {Function} callback completion
 */
exports.storeCode = function(state, address, account, code, callback) {
  account.storeCode(state, code, function(err, codeHash) {
    if (err) {
      callback(err);
    } else {
      account.codeHash = codeHash;
      state.put(new Buffer(address, 'hex'), account.serialize(), callback);
    }
  });
};

/**
 * setupPreConditions given JSON testData
 * @param {[type]}   state    - the state DB/trie
 * @param {[type]}   testData - JSON from tests repo
 * @param {Function} done     - callback when function is completed
 */
exports.setupPreConditions = function(state, testData, done) {
  var keysOfPre = Object.keys(testData.pre),
    acctData,
    account;

  async.eachSeries(keysOfPre, function(key, callback) {
    acctData = testData.pre[key];

    //convert to buffer
    acctData.code = bignum(acctData.code.slice(2), 16).toBuffer();

    account = new Account();
    account.nonce = testUtils.fromDecimal(acctData.nonce);
    account.balance = testUtils.fromDecimal(acctData.balance);

    async.series([
      function(cb2) {
        if (acctData.code.toString('hex') !== '00') {
          account.storeCode(state, acctData.code, cb2);
        } else {
          cb2();
        }
      },
      function(cb2){
        state.put(new Buffer(key, 'hex'), account.serialize(), cb2);
      }
    ], callback);

  }, done);
};
<commit_msg>util for converting toCodeHash<commit_after>const bignum = require('bignum'),
  async = require('async'),
  assert = require('assert'),
  SHA3 = require('sha3'),
  rlp = require('rlp'),
  utils = require('../lib/utils'),
  Account = require('../lib/account.js'),
  Block = require('../lib/block.js');

const testUtils = exports;

/**
 * verifyAccountPostConditions using JSON from tests repo
 * @param {[type]}   state    DB/trie
 * @param {[type]}   account  to verify
 * @param {[type]}   acctData postconditions JSON from tests repo
 * @param {Function} cb       completion callback
 */
exports.verifyAccountPostConditions = function(state, account, acctData, cb) {
  // validate the postcondition of account
  assert.strictEqual(testUtils.toDecimal(account.balance), acctData.balance, 'balance mismatch');
  assert.strictEqual(testUtils.toDecimal(account.nonce), acctData.nonce, 'nonce mismatch');

  // validate storage
  var storageKeys = Object.keys(acctData.storage);
  if (storageKeys.length > 0 && false) {
    state.root = account.stateRoot.toString('hex');
    storageKeys.forEach(function(skey) {
      state.get(testUtils.fromAddress(skey), function(err, data) {
        assert(!err);
        assert.strictEqual(rlp.decode(data).toString('hex'),
          acctData.storage[skey].slice(2), 'storage mismatch');
        cb();
      });
    });
  } else {
    cb();
  }
};

/**
 * makeRunCallData - helper to create the object for VM.runCall using
 *   the exec object specified in the tests repo
 * @param {Object} testData    object from the tests repo
 * @param {Object} block   that the transaction belongs to
 * @return {Object}        object that will be passed to VM.runCall function
 */
exports.makeRunCallData = function(testData, block) {
  var exec = testData.exec,
    acctData = testData.pre[exec.caller],
    account = new Account();

  account.nonce = testUtils.fromDecimal(acctData.nonce);
  account.balance = testUtils.fromDecimal(acctData.balance);

  return {
    fromAccount: account,
    origin: new Buffer(exec.origin, 'hex'),
    data: new Buffer(exec.code.slice(2), 'hex'), // slice off 0x
    value: bignum(exec.value),
    from: new Buffer(exec.caller, 'hex'),
    to: new Buffer(exec.address, 'hex'),
    gas: exec.gas,
    block: block
  };
};

/**
 * makeRunCallDataWithAccount - helper to create the object for VM.runCall using
 *   the exec object specified in the tests repo
 * @param {Object} testData    object from the tests repo
 * @param {Object} account that is making the call
 * @param {Object} block   that the transaction belongs to
 * @return {Object}        object that will be passed to VM.runCall function
 */
exports.makeRunCallDataWithAccount = function(testData, account, block) {
  var exec = testData.exec;
  return {
    fromAccount: account,
    origin: new Buffer(exec.origin, 'hex'),
    data: new Buffer(exec.code.slice(2), 'hex'), // slice off 0x
    value: bignum(exec.value),
    from: new Buffer(exec.caller, 'hex'),
    to: new Buffer(exec.address, 'hex'),
    gas: exec.gas,
    block: block
  };
};

/**
 * toDecimal - converts buffer to decimal string, no leading zeroes
 * @param  {Buffer}
 * @return {String}
 */
exports.toDecimal = function(buffer) {
  return bignum.fromBuffer(buffer).toString();
};

/**
 * fromDecimal - converts decimal string to buffer
 * @param {String}
 *  @return {Buffer}
 */
exports.fromDecimal = function(string) {
  return utils.intToBuffer(parseInt(string, 10));
};

/**
 * fromAddress - converts hexString address to 256-bit buffer
 * @param  {String} hexString address for example '0x03'
 * @return {Buffer}
 */
exports.fromAddress = function(hexString) {
  hexString = hexString.substring(2);
  return utils.pad256(bignum(hexString, 16).toBuffer());
};

/**
 * toCodeHash - applies sha3 to hexCode
 * @param {String} hexCode string from tests repo
 * @return {Buffer}
 */
exports.toCodeHash = function(hexCode) {
  hexCode = hexCode.substring(2);
  var hash = new SHA3.SHA3Hash(256);
  hash.update(hexCode, 'hex');
  return new Buffer(hash.digest('hex'), 'hex');
};

/**
 * makeBlockFromEnv - helper to create a block from the env object in tests repo
 * @param {Object} env object from tests repo
 * @return {Object}  the block
 */
exports.makeBlockFromEnv = function(env) {
  var block = new Block();
  block.header.timestamp = testUtils.fromDecimal(env.currentTimestamp);
  block.header.gasLimit = testUtils.fromDecimal(env.currentGasLimit);
  block.header.parentHash = new Buffer(env.previousHash, 'hex');
  block.header.coinbase = new Buffer(env.currentCoinbase, 'hex');
  block.header.difficulty = testUtils.fromDecimal(env.currentDifficulty);
  block.header.number = testUtils.fromDecimal(env.currentNumber);

  return block;
};

/**
 * makeRunCodeData - helper to create the object for VM.runCode using
 *   the exec object specified in the tests repo
 * @param {Object} exec    object from the tests repo
 * @param {Object} account that the executing code belongs to
 * @param {Object} block   that the transaction belongs to
 * @return {Object}        object that will be passed to VM.runCode function
 */
exports.makeRunCodeData = function(exec, account, block) {
  return {
    account: account,
    origin: new Buffer(exec.origin, 'hex'),
    code: new Buffer(exec.code.slice(2), 'hex'), // slice off 0x
    value: testUtils.fromDecimal(exec.value),
    address: new Buffer(exec.address, 'hex'),
    from: new Buffer(exec.caller, 'hex'),
    data: new Buffer(exec.data.slice(2), 'hex'), // slice off 0x
    gasLimit: exec.gas,
    gasPrice: testUtils.fromDecimal(exec.gasPrice),
    block: block
  };
};

/**
 * storeCode for a given account
 * @param {[type]}   state    trie/DB
 * @param {[type]}   address  of account
 * @param {[type]}   account  for which code belongs to
 * @param {[type]}   code     to store
 * @param {Function} callback completion
 */
exports.storeCode = function(state, address, account, code, callback) {
  account.storeCode(state, code, function(err, codeHash) {
    if (err) {
      callback(err);
    } else {
      account.codeHash = codeHash;
      state.put(new Buffer(address, 'hex'), account.serialize(), callback);
    }
  });
};

/**
 * setupPreConditions given JSON testData
 * @param {[type]}   state    - the state DB/trie
 * @param {[type]}   testData - JSON from tests repo
 * @param {Function} done     - callback when function is completed
 */
exports.setupPreConditions = function(state, testData, done) {
  var keysOfPre = Object.keys(testData.pre),
    acctData,
    account;

  async.eachSeries(keysOfPre, function(key, callback) {
    acctData = testData.pre[key];

    //convert to buffer
    acctData.code = bignum(acctData.code.slice(2), 16).toBuffer();

    account = new Account();
    account.nonce = testUtils.fromDecimal(acctData.nonce);
    account.balance = testUtils.fromDecimal(acctData.balance);

    async.series([
      function(cb2) {
        if (acctData.code.toString('hex') !== '00') {
          account.storeCode(state, acctData.code, cb2);
        } else {
          cb2();
        }
      },
      function(cb2){
        state.put(new Buffer(key, 'hex'), account.serialize(), cb2);
      }
    ], callback);

  }, done);
};
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Fri Oct 16 2015 02:26:28 GMT+0000 (UTC)

module.exports = function(config) {
  'use strict';

  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha', 'chai-as-promised', 'chai-dom', 'chai', 'sinon-chai'],

    client: {
      mocha: {
        timeout: 5000
      }
    },

    // list of files / patterns to load in the browser
    files: [
      'test/sitekey.js',
      'bower_components/angular/angular.js',
      'angular-g-recaptcha.js',
      'bower_components/angular-mocks/angular-mocks.js',

      'test/**/*Spec.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'angular-g-recaptcha.js': 'coverage'
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['nyan', 'coverage'],

    // web server port
    port: process.env.PORT,

    // web server hostname
    hostname: process.env.IP,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS', 'Chrome'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  })
}
<commit_msg>Add default port and hostname for karma conf.<commit_after>// Karma configuration
// Generated on Fri Oct 16 2015 02:26:28 GMT+0000 (UTC)

module.exports = function(config) {
  'use strict';

  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha', 'chai-as-promised', 'chai-dom', 'chai', 'sinon-chai'],

    client: {
      mocha: {
        timeout: 5000
      }
    },

    // list of files / patterns to load in the browser
    files: [
      'test/sitekey.js',
      'bower_components/angular/angular.js',
      'angular-g-recaptcha.js',
      'bower_components/angular-mocks/angular-mocks.js',

      'test/**/*Spec.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'angular-g-recaptcha.js': 'coverage'
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['nyan', 'coverage'],

    // web server port
    port: process.env.PORT || 9876,

    // web server hostname
    hostname: process.env.IP || 'localhost',

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS', 'Chrome'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  })
}
<|endoftext|>


<commit_before>module.exports = function (config) {
  const configuration = {

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'jasmine/build/bundle.js'
    ],

    coverageReporter: {
      type: 'lcov',
      dir: 'coverage/'
    },

    // plugins: ['karma-coverage'],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'src/inverted-index.js': ['coverage']
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage', 'coveralls', 'verbose'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    // browsers: ['Chrome'],
    browsers: process.env.TRAVIS ? ['Chrome_travis_ci'] : ['Chrome'],
    // Custom launchers for travis.
    customLaunchers: {
      Chrome_travis_ci: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  };

  // if (process.env.TRAVIS) {
  //   configuration.browsers = ['Chrome_travis_ci'];
  // }

  config.set(configuration);
};
<commit_msg>update eslintrc<commit_after>module.exports = function (config) {
  const configuration = {

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'src/inverted-index.js',
      'jasmine/build/bundle.js'
    ],

    coverageReporter: {
      type: 'lcov',
      dir: 'coverage/'
    },

    // plugins: ['karma-coverage'],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'src/inverted-index.js': ['coverage']
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage', 'coveralls', 'verbose'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    // browsers: ['Chrome'],
    browsers: process.env.TRAVIS ? ['Chrome_travis_ci'] : ['Chrome'],
    // Custom launchers for travis.
    customLaunchers: {
      Chrome_travis_ci: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  };

  // if (process.env.TRAVIS) {
  //   configuration.browsers = ['Chrome_travis_ci'];
  // }

  config.set(configuration);
};
<|endoftext|>


<commit_before>/*jshint camelcase: false */
'use strict';

var strains = new Bloodhound({
    datumTokenizer: function(d) {
        return Bloodhound.tokenizers.whitespace(d.genus).concat(Bloodhound.tokenizers.whitespace(d.species));
    },
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 15,
    // Goog Docs -> JSON via https://blaiprat.github.io/tsvToJson/
    prefetch: 'data/93genomes.json'
});
// Start loading the .json
var strain_promise = strains.initialize();

var typeahead_field = $('.typeahead');

typeahead_field.typeahead(null, {
    name: 'strains',
    displayKey: function(strain) { return strain.genus + ' ' + strain.species; },
    source: strains.ttAdapter()
});

// Helper via https://stackoverflow.com/questions/6466135/adding-extra-zeros-in-front-of-a-number-using-jquery
function pad(str, max) {
    str = str.toString();
    return str.length < max ? pad('0' + str, max) : str;
}

var factbox = $('.factbox');

function renderPage(datum) {
    console.log('Render triggered.');
    $('.jumbotron').hide();
    factbox.show();
    $('#browserButton').addClass('active');

    // Fill in our data table
    factbox.find('#dynamic-title').text(datum.genus + ' ' + datum.species);
    factbox.find('#dynamic-phylum').text(datum.phylum);
    if (datum.atcc_id) {
        factbox.find('#dynamic-atcc a').attr('href', 'https://www.atcc.org/products/all/' + datum.atcc_id + '.aspx');
        factbox.find('#dynamic-atcc a').text(datum.atcc_id);

    } else {
        factbox.find('#dynamic-atcc').text('None');
    }

    if (datum.dsmz_id) {
        factbox.find('#dynamic-dsmz a').attr('href', 'https://www.dsmz.de/catalogues/details/culture/DSM-' + datum.dsmz_id + '.html');
        factbox.find('#dynamic-dsmz a').text(datum.dsmz_id);

    } else {
        factbox.find('#dynamic-dsmz').text('None');
    }

    factbox.find('#dynamic-well').text(datum.well);

    var factbox_references = factbox.find('#dynamic-references');
    factbox_references.text('');

    if (datum.ncbi_assembly) {
        factbox_references.append('
View NCBI Assembly
');
        factbox_references.find('#dynamic-references-ncbi').attr('href', datum.ncbi_assembly);
    }
    if (datum.taxonomy_url) {
        factbox_references.append('
Taxonomic Data & References
');
        factbox_references.find('#dynamic-references-taxonomy').attr('href', datum.taxonomy_url);
    }

    // Make our RefSeq ID table
    factbox.find('#dynamic-refseq-select').find('option').remove();
    if (datum.refseq_ids || datum.wgs_contigs) {
        $('#sv1').show();
        factbox.find('#dynamic-refseq-select').prop('disabled', false);

        if (datum.refseq_ids) {
            var refseq_ids = datum.refseq_ids.split('|');
            var individual_refseq;
            $.each(refseq_ids, function(index, chunk) {
                individual_refseq = chunk.split(',');
                factbox.find('#dynamic-refseq-select').append('<option value="' + individual_refseq[1] + '">' + individual_refseq[1] + ' (' + individual_refseq[0] + ')</option>');
            });
        }

        if (datum.wgs_contigs) {
            var wgs_contigs = datum.wgs_contigs.split('-');
            console.log('Parsing WGS range: ' + wgs_contigs);
            var lower_array = wgs_contigs[0].split(/[A-Z]/);
            var upper_array = wgs_contigs[1].split(/[A-Z]/);

            var lower_prefix = wgs_contigs[0].substr(0, lower_array.length - 1);
            var upper_prefix = wgs_contigs[1].substr(0, upper_array.length - 1);
            if (lower_prefix !== upper_prefix) {
                alert('ERROR: Unable to parse contigs! ' + wgs_contigs);
            }

            var zeropad = lower_array[lower_array.length - 1].length;
            var lower_limit = parseInt(lower_array[lower_array.length - 1]);
            var upper_limit = parseInt(upper_array[upper_array.length - 1]);

            var wgs_entry;
            for (var i = lower_limit; i <= upper_limit; i += 1) {
                wgs_entry = lower_prefix + pad(i, zeropad);
                factbox.find('#dynamic-refseq-select').append('<option value="' + wgs_entry + '">' + wgs_entry + ' (contig)</option>');
            }
        }

        renderNCBIviewer(factbox.find('#dynamic-refseq-select').val());
    } else {
        factbox.find('#dynamic-refseq-select').append('<option>(None currently available)</option>').prop('disabled', true);
        $('#sv1').hide();
    }
}

// Listen to manual select dropdown changes
$('#dynamic-refseq-select').change(function(data) { renderNCBIviewer($( this ).val()); } );

var app;
function renderNCBIviewer(id) {
    console.log('Rendering NCBI id: ' + id);
    if (app) {
        app.GI = id;
        app.loadAccession();
    } else {
        Ext.onReady(function () {
            console.log('Creating new app.');
            app = new SeqView.App('sv1');
            app.load('embedded=true&multipanel=true&slim=false&appname=GordonLabMicrobialomics&tracks=[key:gene_model_track,Options:ShowAll,CDSProductFeats:true]&id=' + id);
        });
    }
}

typeahead_field.bind('typeahead:selected', function(obj, datum, name) { renderPage(datum); });

typeahead_field.bind('typeahead:autocompleted', function(obj, datum, name) { renderPage(datum); });

// Hilarious button work for the modal
$('#strain-table').on('show.bs.modal', function (e) { $('#strainListButton').addClass('active'); });

$('#strain-table').on('hide.bs.modal', function (e) { $('#strainListButton').removeClass('active'); });

// Pre-load our 93G table modal.
$(window).load(function(){
    // Promise less important since we don't load from Bloodhound
    strain_promise.done(function(){
        $.getJSON( "data/93genomes.json", function( data ) {
            var strain_table_body = $("#strain-table-body");
            $.each( data, function( key, val ) {
                strain_table_body.append("
" + val.well + "	" + val.genus + " " + val.species +
                    "	" + val.phylum + "	" + val.atcc_id + "	" +  val.dsmz_id + "
");
                // Hack to patch up null values
                strain_table_body.html(strain_table_body.html().replace('null','N/A'));
            });
            $("#strain-table-itself").dataTable();
        });
    });
});

// TODO: REMOVE THESE LINES WHEN IN PROD!
// strains.clearPrefetchCache();
<commit_msg>JS hint suppression<commit_after>/*jshint camelcase:false, devel:true*/
/*global Bloodhound:false, renderNCBIviewer:false, Ext:false, SeqView:false*/
'use strict';

var strains = new Bloodhound({
    datumTokenizer: function(d) {
        return Bloodhound.tokenizers.whitespace(d.genus).concat(Bloodhound.tokenizers.whitespace(d.species));
    },
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 15,
    // Goog Docs -> JSON via https://blaiprat.github.io/tsvToJson/
    prefetch: 'data/93genomes.json'
});
// Start loading the .json
var strain_promise = strains.initialize();

var typeahead_field = $('.typeahead');

typeahead_field.typeahead(null, {
    name: 'strains',
    displayKey: function(strain) { return strain.genus + ' ' + strain.species; },
    source: strains.ttAdapter()
});

// Helper via https://stackoverflow.com/questions/6466135/adding-extra-zeros-in-front-of-a-number-using-jquery
function pad(str, max) {
    str = str.toString();
    return str.length < max ? pad('0' + str, max) : str;
}

var factbox = $('.factbox');

function renderPage(datum) {
    console.log('Render triggered.');
    $('.jumbotron').hide();
    factbox.show();
    $('#browserButton').addClass('active');

    // Fill in our data table
    factbox.find('#dynamic-title').text(datum.genus + ' ' + datum.species);
    factbox.find('#dynamic-phylum').text(datum.phylum);
    if (datum.atcc_id) {
        factbox.find('#dynamic-atcc a').attr('href', 'https://www.atcc.org/products/all/' + datum.atcc_id + '.aspx');
        factbox.find('#dynamic-atcc a').text(datum.atcc_id);

    } else {
        factbox.find('#dynamic-atcc').text('None');
    }

    if (datum.dsmz_id) {
        factbox.find('#dynamic-dsmz a').attr('href', 'https://www.dsmz.de/catalogues/details/culture/DSM-' + datum.dsmz_id + '.html');
        factbox.find('#dynamic-dsmz a').text(datum.dsmz_id);

    } else {
        factbox.find('#dynamic-dsmz').text('None');
    }

    factbox.find('#dynamic-well').text(datum.well);

    var factbox_references = factbox.find('#dynamic-references');
    factbox_references.text('');

    if (datum.ncbi_assembly) {
        factbox_references.append('
View NCBI Assembly
');
        factbox_references.find('#dynamic-references-ncbi').attr('href', datum.ncbi_assembly);
    }
    if (datum.taxonomy_url) {
        factbox_references.append('
Taxonomic Data & References
');
        factbox_references.find('#dynamic-references-taxonomy').attr('href', datum.taxonomy_url);
    }

    // Make our RefSeq ID table
    factbox.find('#dynamic-refseq-select').find('option').remove();
    if (datum.refseq_ids || datum.wgs_contigs) {
        $('#sv1').show();
        factbox.find('#dynamic-refseq-select').prop('disabled', false);

        if (datum.refseq_ids) {
            var refseq_ids = datum.refseq_ids.split('|');
            var individual_refseq;
            $.each(refseq_ids, function(index, chunk) {
                individual_refseq = chunk.split(',');
                factbox.find('#dynamic-refseq-select').append('<option value="' + individual_refseq[1] + '">' + individual_refseq[1] + ' (' + individual_refseq[0] + ')</option>');
            });
        }

        if (datum.wgs_contigs) {
            var wgs_contigs = datum.wgs_contigs.split('-');
            console.log('Parsing WGS range: ' + wgs_contigs);
            var lower_array = wgs_contigs[0].split(/[A-Z]/);
            var upper_array = wgs_contigs[1].split(/[A-Z]/);

            var lower_prefix = wgs_contigs[0].substr(0, lower_array.length - 1);
            var upper_prefix = wgs_contigs[1].substr(0, upper_array.length - 1);
            if (lower_prefix !== upper_prefix) {
                alert('ERROR: Unable to parse contigs! ' + wgs_contigs);
            }

            var zeropad = lower_array[lower_array.length - 1].length;
            var lower_limit = parseInt(lower_array[lower_array.length - 1]);
            var upper_limit = parseInt(upper_array[upper_array.length - 1]);

            var wgs_entry;
            for (var i = lower_limit; i <= upper_limit; i += 1) {
                wgs_entry = lower_prefix + pad(i, zeropad);
                factbox.find('#dynamic-refseq-select').append('<option value="' + wgs_entry + '">' + wgs_entry + ' (contig)</option>');
            }
        }

        renderNCBIviewer(factbox.find('#dynamic-refseq-select').val());
    } else {
        factbox.find('#dynamic-refseq-select').append('<option>(None currently available)</option>').prop('disabled', true);
        $('#sv1').hide();
    }
}

// Listen to manual select dropdown changes
$('#dynamic-refseq-select').change(function(data) { renderNCBIviewer($( this ).val()); } );

var app;
function renderNCBIviewer(id) {
    console.log('Rendering NCBI id: ' + id);
    if (app) {
        app.GI = id;
        app.loadAccession();
    } else {
        Ext.onReady(function () {
            console.log('Creating new app.');
            app = new SeqView.App('sv1');
            app.load('embedded=true&multipanel=true&slim=false&appname=GordonLabMicrobialomics&tracks=[key:gene_model_track,Options:ShowAll,CDSProductFeats:true]&id=' + id);
        });
    }
}

typeahead_field.bind('typeahead:selected', function(obj, datum, name) { renderPage(datum); });

typeahead_field.bind('typeahead:autocompleted', function(obj, datum, name) { renderPage(datum); });

// Hilarious button work for the modal
$('#strain-table').on('show.bs.modal', function (e) { $('#strainListButton').addClass('active'); });

$('#strain-table').on('hide.bs.modal', function (e) { $('#strainListButton').removeClass('active'); });

// Pre-load our 93G table modal.
$(window).load(function(){
    // Promise less important since we don't load from Bloodhound
    strain_promise.done(function(){
        $.getJSON('data/93genomes.json', function( data ) {
            var strain_table_body = $('#strain-table-body');
            $.each( data, function( key, val ) {
                strain_table_body.append('
' + val.well + '	' + val.genus + ' ' + val.species +
                    '	' + val.phylum + '	' + val.atcc_id + '	' +  val.dsmz_id + '
');
                // Hack to patch up null values
                strain_table_body.html(strain_table_body.html().replace('null','N/A'));
            });
            $('#strain-table-itself').dataTable();
        });
    });
});

// TODO: REMOVE THESE LINES WHEN IN PROD!
// strains.clearPrefetchCache();
<|endoftext|>


<commit_before>// Karma configuration
// http://karma-runner.github.io/0.12/config/configuration-file.html

module.exports = function(config) {
  'use strict';

  config.set({
    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // base path, that will be used to resolve files and exclude
    basePath: './',

    // testing framework to use (jasmine/mocha/qunit/...)
    // as well as any additional frameworks (requirejs/chai/sinon/...)
    frameworks: [
      "jasmine"
    ],

    // list of files / patterns to load in the browser
    files: [
      // bower:js
      'bower_components/jquery/dist/jquery.js',
      'bower_components/angular/angular.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/affix.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/alert.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/button.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/carousel.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/collapse.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/dropdown.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/tab.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/transition.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/scrollspy.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/modal.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/tooltip.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/popover.js',
      'bower_components/angular-resource/angular-resource.js',
      'bower_components/angular-sanitize/angular-sanitize.js',
      'bower_components/angular-touch/angular-touch.js',
      'bower_components/angular-ui-router/release/angular-ui-router.js',
      'bower_components/angular-esri-map/dist/angular-esri-map.js',
      'bower_components/angular-animate/angular-animate.js',
      'bower_components/angularjs-toaster/toaster.js',
      'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
      'bower_components/angular-ui-select/dist/select.js',
      'bower_components/moment/moment.js',
      'bower_components/leaflet/dist/leaflet-src.js',
      'bower_components/angular-simple-logger/dist/angular-simple-logger.js',
      'bower_components/angular-mocks/angular-mocks.js',
      // endbower
      'app/vendor/jcs-auto-validate.min.js',
      'app/scripts/**/*.js',
      'test/spec/**/*.js'
    ],

    preprocessors: {
      'app/scripts/**/*.js': ['coverage']
    },

    reporters: [
      'spec',
      'coverage'
    ],

    specReporter: {
      maxLogLines: 10,         // limit number of lines logged per test
      suppressErrorSummary: true,  // do not print error summary
      suppressFailed: false,  // do not print information about failed tests
      suppressPassed: true,  // do not print information about passed tests
      suppressSkipped: true,  // do not print information about skipped tests
      showSpecTiming: false // print the time elapsed for each spec
    },

    coverageReporter: {
      dir: 'coverage/',
      reporters: [
        {type: 'text-summary'},
        {type: 'html'}
      ]
    },

    // list of files / patterns to exclude
    exclude: [
    ],

    // web server port
    port: 8080,

    browsers: [
      "PhantomJS"
    ],

    // Continuous Integration mode
    // if true, it capture browsers, run tests and exit
    singleRun: false,

    colors: true,

    // level of logging
    // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
    logLevel: config.LOG_INFO

    // Uncomment the following lines if you are using grunt's server to run the tests
    // proxies: {
    //   '/': 'http://localhost:9000/'
    // },
    // URL root prevent conflicts with the site root
    // urlRoot: '_karma_'
  });
};
<commit_msg>Fix ui-leaflet in karma<commit_after>// Karma configuration
// http://karma-runner.github.io/0.12/config/configuration-file.html

module.exports = function(config) {
  'use strict';

  config.set({
    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // base path, that will be used to resolve files and exclude
    basePath: './',

    // testing framework to use (jasmine/mocha/qunit/...)
    // as well as any additional frameworks (requirejs/chai/sinon/...)
    frameworks: [
      "jasmine"
    ],

    // list of files / patterns to load in the browser
    files: [
      // bower:js
      'bower_components/jquery/dist/jquery.js',
      'bower_components/angular/angular.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/affix.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/alert.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/button.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/carousel.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/collapse.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/dropdown.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/tab.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/transition.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/scrollspy.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/modal.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/tooltip.js',
      'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/popover.js',
      'bower_components/angular-resource/angular-resource.js',
      'bower_components/angular-sanitize/angular-sanitize.js',
      'bower_components/angular-touch/angular-touch.js',
      'bower_components/angular-ui-router/release/angular-ui-router.js',
      'bower_components/angular-esri-map/dist/angular-esri-map.js',
      'bower_components/angular-animate/angular-animate.js',
      'bower_components/angularjs-toaster/toaster.js',
      'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
      'bower_components/angular-ui-select/dist/select.js',
      'bower_components/moment/moment.js',
      'bower_components/leaflet/dist/leaflet-src.js',
      'bower_components/angular-simple-logger/dist/angular-simple-logger.js',
      'bower_components/angular-mocks/angular-mocks.js',
      // endbower
      'app/vendor/ui-leaflet.js',
      'app/vendor/jcs-auto-validate.min.js',
      'app/scripts/**/*.js',
      'test/spec/**/*.js'
    ],

    preprocessors: {
      'app/scripts/**/*.js': ['coverage']
    },

    reporters: [
      'spec',
      'coverage'
    ],

    specReporter: {
      maxLogLines: 10,         // limit number of lines logged per test
      suppressErrorSummary: true,  // do not print error summary
      suppressFailed: false,  // do not print information about failed tests
      suppressPassed: true,  // do not print information about passed tests
      suppressSkipped: true,  // do not print information about skipped tests
      showSpecTiming: false // print the time elapsed for each spec
    },

    coverageReporter: {
      dir: 'coverage/',
      reporters: [
        {type: 'text-summary'},
        {type: 'html'}
      ]
    },

    // list of files / patterns to exclude
    exclude: [
    ],

    // web server port
    port: 8080,

    browsers: [
      "PhantomJS"
    ],

    // Continuous Integration mode
    // if true, it capture browsers, run tests and exit
    singleRun: false,

    colors: true,

    // level of logging
    // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
    logLevel: config.LOG_INFO

    // Uncomment the following lines if you are using grunt's server to run the tests
    // proxies: {
    //   '/': 'http://localhost:9000/'
    // },
    // URL root prevent conflicts with the site root
    // urlRoot: '_karma_'
  });
};
<|endoftext|>


<commit_before>"use strict";

var strains = new Bloodhound({
    datumTokenizer: function(d) {
        return Bloodhound.tokenizers.whitespace(d['genus']).concat(Bloodhound.tokenizers.whitespace(d['species']));
    },
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 15,
    // Goog Docs -> JSON via https://blaiprat.github.io/tsvToJson/
    prefetch: 'data/93genomes.json'
});
// Start loading the .json
var strain_promise = strains.initialize();

var typeahead_field = $('.typeahead');

typeahead_field.typeahead(null, {
    name: 'strains',
    displayKey: function(strain) { return strain['genus'] + " " + strain['species']; },
    source: strains.ttAdapter()
});

// Helper via https://stackoverflow.com/questions/6466135/adding-extra-zeros-in-front-of-a-number-using-jquery
function pad(str, max) {
    str = str.toString();
    return str.length < max ? pad("0" + str, max) : str;
}

var factbox = $('.factbox');

function renderPage(datum) {
    console.log('Render triggered.');
    $('.jumbotron').hide();
    factbox.show();
    $('#browserButton').addClass('active');

    // Fill in our data table
    factbox.find('#dynamic-title').text(datum['genus'] + " " + datum['species']);
    factbox.find('#dynamic-phylum').text(datum['phylum']);
    if (datum['atcc_id']) {
        factbox.find('#dynamic-atcc a').attr('href', 'https://www.atcc.org/products/all/' + datum['atcc_id'] + '.aspx');
        factbox.find('#dynamic-atcc a').text(datum['atcc_id']);

    } else {
        factbox.find('#dynamic-atcc').text("None");
    }

    if (datum['dsmz_id']) {
        factbox.find('#dynamic-dsmz a').attr('href', 'https://www.dsmz.de/catalogues/details/culture/DSM-' + datum['dsmz_id'] + '.html');
        factbox.find('#dynamic-dsmz a').text(datum['dsmz_id']);

    } else {
        factbox.find('#dynamic-dsmz').text("None");
    }

    factbox.find('#dynamic-well').text(datum['well']);

    var factbox_references = factbox.find('#dynamic-references');
    factbox_references.text('');

    if (datum['ncbi_assembly']) {
        factbox_references.append('
View NCBI Assembly
');
        factbox_references.find('#dynamic-references-ncbi').attr('href', datum['ncbi_assembly']);
    }
    if (datum['taxonomy_url']) {
        factbox_references.append('
Taxonomic Data & References
');
        factbox_references.find('#dynamic-references-taxonomy').attr('href', datum['taxonomy_url']);
    }

    // Make our RefSeq ID table
    factbox.find('#dynamic-refseq-select').find('option').remove();
    if (datum['refseq_ids'] || datum['wgs_contigs']) {
        $('#sv1').show();
        factbox.find('#dynamic-refseq-select').prop('disabled', false);

        if (datum['refseq_ids']) {
            var refseq_ids = datum['refseq_ids'].split('|');
            var individual_refseq;
            $.each(refseq_ids, function(index, chunk) {
                individual_refseq = chunk.split(',');
                factbox.find('#dynamic-refseq-select').append('<option value="' + individual_refseq[1] + '">' + individual_refseq[1] + ' (' + individual_refseq[0] + ')</option>');
            });
        }

        if (datum['wgs_contigs']) {
            var wgs_contigs = datum['wgs_contigs'].split('-');
            console.log('Parsing WGS range: ' + wgs_contigs);
            var lower_array = wgs_contigs[0].split(/[A-Z]/);
            var upper_array = wgs_contigs[1].split(/[A-Z]/);

            var lower_prefix = wgs_contigs[0].substr(0, lower_array.length - 1);
            var upper_prefix = wgs_contigs[1].substr(0, upper_array.length - 1);
            if (lower_prefix !== upper_prefix) {
                alert('ERROR: Unable to parse contigs! ' + wgs_contigs);
            }

            var zeropad = lower_array[lower_array.length - 1].length;
            var lower_limit = parseInt(lower_array[lower_array.length - 1]);
            var upper_limit = parseInt(upper_array[upper_array.length - 1]);

            var wgs_entry;
            for (var i = lower_limit; i <= upper_limit; i += 1) {
                wgs_entry = lower_prefix + pad(i, zeropad);
                factbox.find('#dynamic-refseq-select').append('<option value="' + wgs_entry + '">' + wgs_entry + ' (contig)</option>');
            }
        }

        renderNCBIviewer(factbox.find('#dynamic-refseq-select').val());
    } else {
        factbox.find('#dynamic-refseq-select').append('<option>(None currently available)</option>').prop('disabled', true);
        $('#sv1').hide();
    }
}

// Listen to manual select dropdown changes
$('#dynamic-refseq-select').change(function(data) { renderNCBIviewer($( this ).val()); } );

var app;
function renderNCBIviewer(id) {
    console.log('Rendering NCBI id: ' + id);
    if (app) {
        app.GI = id;
        app.loadAccession();
    } else {
        Ext.onReady(function () {
            console.log('Creating new app.');
            app = new SeqView.App('sv1');
            app.load('embedded=true&multipanel=true&slim=false&appname=GordonLabMicrobialomics&tracks=[key:gene_model_track,Options:ShowAll,CDSProductFeats:true]&id=' + id);
        });
    }
}

typeahead_field.bind('typeahead:selected', function(obj, datum, name) { renderPage(datum); });

typeahead_field.bind('typeahead:autocompleted', function(obj, datum, name) { renderPage(datum); });

// Hilarious button work for the modal
$('#strain-table').on('show.bs.modal', function (e) { $('#strainListButton').addClass('active'); });

$('#strain-table').on('hide.bs.modal', function (e) { $('#strainListButton').removeClass('active'); });

// Pre-load our 93G table modal.
$(window).load(function(){
    // Promise less important since we don't load from Bloodhound
    strain_promise.done(function(){
        $.getJSON( "data/93genomes.json", function( data ) {
            var strain_table_body = $("#strain-table-body");
            $.each( data, function( key, val ) {
                strain_table_body.append("
" + val['well'] + "	" + val['genus'] + " " + val['species'] +
                    "	" + val['phylum'] + "	" + val['atcc_id'] + "	" +  val['dsmz_id'] + "
");
                // Hack to patch up null values
                strain_table_body.html(strain_table_body.html().replace('null','N/A'));
            });
            $("#strain-table-itself").dataTable();
        });
    });
});

// TODO: REMOVE THESE LINES WHEN IN PROD!
// strains.clearPrefetchCache();
<commit_msg>Start jshint cleanups.<commit_after>"use strict";

var strains = new Bloodhound({
    datumTokenizer: function(d) {
        return Bloodhound.tokenizers.whitespace(d.genus).concat(Bloodhound.tokenizers.whitespace(d.species));
    },
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 15,
    // Goog Docs -> JSON via https://blaiprat.github.io/tsvToJson/
    prefetch: 'data/93genomes.json'
});
// Start loading the .json
var strain_promise = strains.initialize();

var typeahead_field = $('.typeahead');

typeahead_field.typeahead(null, {
    name: 'strains',
    displayKey: function(strain) { return strain.genus + " " + strain.species; },
    source: strains.ttAdapter()
});

// Helper via https://stackoverflow.com/questions/6466135/adding-extra-zeros-in-front-of-a-number-using-jquery
function pad(str, max) {
    str = str.toString();
    return str.length < max ? pad("0" + str, max) : str;
}

var factbox = $('.factbox');

function renderPage(datum) {
    console.log('Render triggered.');
    $('.jumbotron').hide();
    factbox.show();
    $('#browserButton').addClass('active');

    // Fill in our data table
    factbox.find('#dynamic-title').text(datum.genus + " " + datum.species);
    factbox.find('#dynamic-phylum').text(datum.phylum);
    if (datum.atcc_id) {
        factbox.find('#dynamic-atcc a').attr('href', 'https://www.atcc.org/products/all/' + datum.atcc_id + '.aspx');
        factbox.find('#dynamic-atcc a').text(datum.atcc_id);

    } else {
        factbox.find('#dynamic-atcc').text("None");
    }

    if (datum.dsmz_id) {
        factbox.find('#dynamic-dsmz a').attr('href', 'https://www.dsmz.de/catalogues/details/culture/DSM-' + datum.dsmz_id + '.html');
        factbox.find('#dynamic-dsmz a').text(datum.dsmz_id);

    } else {
        factbox.find('#dynamic-dsmz').text("None");
    }

    factbox.find('#dynamic-well').text(datum.well);

    var factbox_references = factbox.find('#dynamic-references');
    factbox_references.text('');

    if (datum.ncbi_assembly) {
        factbox_references.append('
View NCBI Assembly
');
        factbox_references.find('#dynamic-references-ncbi').attr('href', datum.ncbi_assembly);
    }
    if (datum.taxonomy_url) {
        factbox_references.append('
Taxonomic Data & References
');
        factbox_references.find('#dynamic-references-taxonomy').attr('href', datum.taxonomy_url);
    }

    // Make our RefSeq ID table
    factbox.find('#dynamic-refseq-select').find('option').remove();
    if (datum.refseq_ids || datum.wgs_contigs) {
        $('#sv1').show();
        factbox.find('#dynamic-refseq-select').prop('disabled', false);

        if (datum.refseq_ids) {
            var refseq_ids = datum.refseq_ids.split('|');
            var individual_refseq;
            $.each(refseq_ids, function(index, chunk) {
                individual_refseq = chunk.split(',');
                factbox.find('#dynamic-refseq-select').append('<option value="' + individual_refseq[1] + '">' + individual_refseq[1] + ' (' + individual_refseq[0] + ')</option>');
            });
        }

        if (datum.wgs_contigs) {
            var wgs_contigs = datum.wgs_contigs.split('-');
            console.log('Parsing WGS range: ' + wgs_contigs);
            var lower_array = wgs_contigs[0].split(/[A-Z]/);
            var upper_array = wgs_contigs[1].split(/[A-Z]/);

            var lower_prefix = wgs_contigs[0].substr(0, lower_array.length - 1);
            var upper_prefix = wgs_contigs[1].substr(0, upper_array.length - 1);
            if (lower_prefix !== upper_prefix) {
                alert('ERROR: Unable to parse contigs! ' + wgs_contigs);
            }

            var zeropad = lower_array[lower_array.length - 1].length;
            var lower_limit = parseInt(lower_array[lower_array.length - 1]);
            var upper_limit = parseInt(upper_array[upper_array.length - 1]);

            var wgs_entry;
            for (var i = lower_limit; i <= upper_limit; i += 1) {
                wgs_entry = lower_prefix + pad(i, zeropad);
                factbox.find('#dynamic-refseq-select').append('<option value="' + wgs_entry + '">' + wgs_entry + ' (contig)</option>');
            }
        }

        renderNCBIviewer(factbox.find('#dynamic-refseq-select').val());
    } else {
        factbox.find('#dynamic-refseq-select').append('<option>(None currently available)</option>').prop('disabled', true);
        $('#sv1').hide();
    }
}

// Listen to manual select dropdown changes
$('#dynamic-refseq-select').change(function(data) { renderNCBIviewer($( this ).val()); } );

var app;
function renderNCBIviewer(id) {
    console.log('Rendering NCBI id: ' + id);
    if (app) {
        app.GI = id;
        app.loadAccession();
    } else {
        Ext.onReady(function () {
            console.log('Creating new app.');
            app = new SeqView.App('sv1');
            app.load('embedded=true&multipanel=true&slim=false&appname=GordonLabMicrobialomics&tracks=[key:gene_model_track,Options:ShowAll,CDSProductFeats:true]&id=' + id);
        });
    }
}

typeahead_field.bind('typeahead:selected', function(obj, datum, name) { renderPage(datum); });

typeahead_field.bind('typeahead:autocompleted', function(obj, datum, name) { renderPage(datum); });

// Hilarious button work for the modal
$('#strain-table').on('show.bs.modal', function (e) { $('#strainListButton').addClass('active'); });

$('#strain-table').on('hide.bs.modal', function (e) { $('#strainListButton').removeClass('active'); });

// Pre-load our 93G table modal.
$(window).load(function(){
    // Promise less important since we don't load from Bloodhound
    strain_promise.done(function(){
        $.getJSON( "data/93genomes.json", function( data ) {
            var strain_table_body = $("#strain-table-body");
            $.each( data, function( key, val ) {
                strain_table_body.append("
" + val.well + "	" + val.genus + " " + val.species +
                    "	" + val.phylum + "	" + val.atcc_id + "	" +  val.dsmz_id + "
");
                // Hack to patch up null values
                strain_table_body.html(strain_table_body.html().replace('null','N/A'));
            });
            $("#strain-table-itself").dataTable();
        });
    });
});

// TODO: REMOVE THESE LINES WHEN IN PROD!
// strains.clearPrefetchCache();
<|endoftext|>


<commit_before>goog.provide('ngeo.interaction.MeasureArea');

goog.require('ngeo.interaction.Measure');
goog.require('ol.geom.Polygon');
goog.require('ol.interaction.Draw');
goog.require('ol.sphere.WGS84');

/**
 * @classdesc
 * Interaction dedicated to measure length.
 *
 * @constructor
 * @extends {ngeo.interaction.Measure}
 * @param {ngeox.interaction.MeasureOptions=} opt_options Options
 */
ngeo.interaction.MeasureArea = function(opt_options) {

  var options = goog.isDef(opt_options) ? opt_options : {};

  goog.base(this, options);

  /**
   * Message to show after the first point is clicked.
   * @type {Element}
   * @private
   */
  this.continueMsg_ = goog.isDef(options.continueMsg) ? options.continueMsg :
      goog.dom.createDom(goog.dom.TagName.SPAN, {},
          'Click to continue drawing the polygon.',
          goog.dom.createDom(goog.dom.TagName.BR),
          'Double-click or click starting point to finish.');

};
goog.inherits(ngeo.interaction.MeasureArea, ngeo.interaction.Measure);

/**
 * @inheritDoc
 */
ngeo.interaction.MeasureArea.prototype.getDrawInteraction = function(style,
    overlay) {

  return new ol.interaction.Draw(
      /** @type {olx.interaction.DrawOptions} */ ({
        type: 'Polygon',
        features: overlay.getFeatures(),
        style: style
      }));

};

/**
 * @inheritDoc
 */
ngeo.interaction.MeasureArea.prototype.handleMeasure = function(callback) {
  var geom = /** @type {ol.geom.Polygon} */
      (this.sketchFeature.getGeometry());
  var output = this.formatMeasure_(geom);
  var verticesCount = geom.getCoordinates()[0].length;
  var helpMsg = this.continueMsg_;
  var coord = null;
  if (verticesCount > 2) {
    coord = geom.getInteriorPoint().getCoordinates();
  }
  callback(output, coord, helpMsg);
};

/**
 * Format measure output.
 * @param {ol.geom.Polygon} polygon
 * @return {string}
 * @private
 */
ngeo.interaction.MeasureArea.prototype.formatMeasure_ = function(polygon) {
  var map = this.getMap();
  var sourceProj = map.getView().getProjection();
  var geom = /** @type {ol.geom.Polygon} */ (polygon.clone().transform(
      sourceProj, 'EPSG:4326'));
  var coordinates = geom.getLinearRing(0).getCoordinates();
  var area = Math.abs(ol.sphere.WGS84.geodesicArea(coordinates));
  var output;
  if (area > 10000) {
    output = (Math.round(area / 1000000 * 100) / 100) +
        ' ' + 'km2';
  } else {
    output = (Math.round(area * 100) / 100) +
        ' ' + 'm2';
  }
  return output;
};
<commit_msg>Switch to kilometers for larger values and lower precision<commit_after>goog.provide('ngeo.interaction.MeasureArea');

goog.require('ngeo.interaction.Measure');
goog.require('ol.geom.Polygon');
goog.require('ol.interaction.Draw');
goog.require('ol.sphere.WGS84');

/**
 * @classdesc
 * Interaction dedicated to measure length.
 *
 * @constructor
 * @extends {ngeo.interaction.Measure}
 * @param {ngeox.interaction.MeasureOptions=} opt_options Options
 */
ngeo.interaction.MeasureArea = function(opt_options) {

  var options = goog.isDef(opt_options) ? opt_options : {};

  goog.base(this, options);

  /**
   * Message to show after the first point is clicked.
   * @type {Element}
   * @private
   */
  this.continueMsg_ = goog.isDef(options.continueMsg) ? options.continueMsg :
      goog.dom.createDom(goog.dom.TagName.SPAN, {},
          'Click to continue drawing the polygon.',
          goog.dom.createDom(goog.dom.TagName.BR),
          'Double-click or click starting point to finish.');

};
goog.inherits(ngeo.interaction.MeasureArea, ngeo.interaction.Measure);

/**
 * @inheritDoc
 */
ngeo.interaction.MeasureArea.prototype.getDrawInteraction = function(style,
    overlay) {

  return new ol.interaction.Draw(
      /** @type {olx.interaction.DrawOptions} */ ({
        type: 'Polygon',
        features: overlay.getFeatures(),
        style: style
      }));

};

/**
 * @inheritDoc
 */
ngeo.interaction.MeasureArea.prototype.handleMeasure = function(callback) {
  var geom = /** @type {ol.geom.Polygon} */
      (this.sketchFeature.getGeometry());
  var output = this.formatMeasure_(geom);
  var verticesCount = geom.getCoordinates()[0].length;
  var helpMsg = this.continueMsg_;
  var coord = null;
  if (verticesCount > 2) {
    coord = geom.getInteriorPoint().getCoordinates();
  }
  callback(output, coord, helpMsg);
};

/**
 * Format measure output.
 * @param {ol.geom.Polygon} polygon
 * @return {string}
 * @private
 */
ngeo.interaction.MeasureArea.prototype.formatMeasure_ = function(polygon) {
  var map = this.getMap();
  var sourceProj = map.getView().getProjection();
  var geom = /** @type {ol.geom.Polygon} */ (polygon.clone().transform(
      sourceProj, 'EPSG:4326'));
  var coordinates = geom.getLinearRing(0).getCoordinates();
  var area = Math.abs(ol.sphere.WGS84.geodesicArea(coordinates));
  var output;
  if (area > 1000000) {
    output = parseFloat((area / 1000000).toPrecision(3)) +
        ' ' + 'km2';
  } else {
    output = parseFloat(area.toPrecision(3)) + ' ' + 'm2';
  }
  return output;
};
<|endoftext|>