<commit_before>var ogreArmaments = {
	"mb":{
		"name":"Main Battery",
		"atk":4,
		"rng":3,
		"def":4
	},
	"sb":{
		"name":"Secondary Battery",
		"atk":3,
		"rng":2,
		"def":3
	},
	"msl":{
		"name":"Missiles",
		"atk":6,
		"rng":5,
		"def":3
	},
	"rack":{
		"name":"Missile Rack",
		"def":4
	},
	"intmsl":{
		"name":"Int. Missiles",
		"atk":6,
		"rng":5,
		"def":null
	},
	"ap":{
		"name":"Antipersonnel",
		"atk":1,
		"rng":1,
		"def":1
	}
}

var ogreTypes =
{
	"mki":{
		"name":"Mark I",
		"cost":4,
		"size":5,
		"movement":{
			"base":3,
			"tread":18
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":4
			}
		}
	},
	"mkii":{
		"name":"Mark II",
		"cost":8,
		"size":6,
		"movement":{
			"base":3,
			"tread":30
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":2
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":6
			}
		}
	},
	"mkiii":{
		"name":"Mark III",
		"cost":17,
		"size":7,
		"movement":{
			"base":3,
			"tread":45
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":4
			},
			"msl":{
				"stats":ogreArmaments.msl,
				"count":2
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":8
			}
		}
	},
	"mkiiib":{
		"name":"Mark III-B",
		"cost":20,
		"size":7,
		"movement":{
			"base":3,
			"tread":48
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":2
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":4
			},
			"msl":{
				"stats":ogreArmaments.msl,
				"count":4
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":8
			}
		}
	},
	"mkiv":{
		"name":"Mark IV",
		"cost":25,
		"size":8,
		"movement":{
			"base":4,
			"tread":56
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":2
			},
			"rack":{
				"stats":ogreArmaments.rack,
				"count":3
			},
			"intmsl":{
				"stats":ogreArmaments.intmsl,
				"count":15
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":8
			}
		}
	},
	"mkv":{
		"name":"Mark V",
		"cost":25,
		"size":8,
		"movement":{
			"base":3,
			"tread":60
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":2
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":6
			},
			"msl":{
				"stats":ogreArmaments.msl,
				"count":6
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":12
			}
		}
	},
	"mkvi":{
		"name":"Mark VI",
		"cost":40,
		"size":9,
		"movement":{
			"base":3,
			"tread":72
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":3
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":6
			},
			"msl":{
				"stats":ogreArmaments.msl,
				"count":6
			},
			"rack":{
				"stats":ogreArmaments.rack,
				"count":3
			},
			"intmsl":{
				"stats":ogreArmaments.intmsl,
				"count":12
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":16
			}
		}
	}
}

<commit_msg>Added Paneuropean Ogres<commit_after>var ogreArmaments = {
	"mb":{
		"name":"Main Battery",
		"atk":4,
		"rng":3,
		"def":4
	},
	"sb":{
		"name":"Secondary Battery",
		"atk":3,
		"rng":2,
		"def":3
	},
	"msl":{
		"name":"Missiles",
		"atk":6,
		"rng":5,
		"def":3
	},
	"rack":{
		"name":"Missile Rack",
		"def":4
	},
	"intmsl":{
		"name":"Int. Missiles",
		"atk":6,
		"rng":5,
		"def":null
	},
	"ap":{
		"name":"Antipersonnel",
		"atk":1,
		"rng":1,
		"def":1
	}
}

var ogreTypes =
{
	"mki":{
		"name":"Mark I",
		"cost":4,
		"size":5,
		"movement":{
			"base":3,
			"tread":18
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":4
			}
		}
	},
	"mkii":{
		"name":"Mark II",
		"cost":8,
		"size":6,
		"movement":{
			"base":3,
			"tread":30
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":2
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":6
			}
		}
	},
	"mkiii":{
		"name":"Mark III",
		"cost":17,
		"size":7,
		"movement":{
			"base":3,
			"tread":45
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":4
			},
			"msl":{
				"stats":ogreArmaments.msl,
				"count":2
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":8
			}
		}
	},
	"mkiiib":{
		"name":"Mark III-B",
		"cost":20,
		"size":7,
		"movement":{
			"base":3,
			"tread":48
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":2
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":4
			},
			"msl":{
				"stats":ogreArmaments.msl,
				"count":4
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":8
			}
		}
	},
	"mkiv":{
		"name":"Mark IV",
		"cost":25,
		"size":8,
		"movement":{
			"base":4,
			"tread":56
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":2
			},
			"rack":{
				"stats":ogreArmaments.rack,
				"count":3
			},
			"intmsl":{
				"stats":ogreArmaments.intmsl,
				"count":15
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":8
			}
		}
	},
	"mkv":{
		"name":"Mark V",
		"cost":25,
		"size":8,
		"movement":{
			"base":3,
			"tread":60
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":2
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":6
			},
			"msl":{
				"stats":ogreArmaments.msl,
				"count":6
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":12
			}
		}
	},
	"mkvi":{
		"name":"Mark VI",
		"cost":40,
		"size":9,
		"movement":{
			"base":3,
			"tread":72
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":3
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":6
			},
			"msl":{
				"stats":ogreArmaments.msl,
				"count":6
			},
			"rack":{
				"stats":ogreArmaments.rack,
				"count":3
			},
			"intmsl":{
				"stats":ogreArmaments.intmsl,
				"count":12
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":16
			}
		}
	},
	"pikeman":{
		"name":"Pikeman",
		"cost":4,
		"size":5,
		"movement":{
			"base":3,
			"tread":18
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":4
			}
		}
	},
	"legionnaier":{
		"name":"Legionnaire",
		"cost":17,
		"size":7,
		"movement":{
			"base":3,
			"tread":45
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":1
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":4
			},
			"msl":{
				"stats":ogreArmaments.msl,
				"count":2
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":8
			}
		}
	},
	"fencer":{
		"name":"Fencer",
		"cost":22,
		"size":8,
		"movement":{
			"base":3,
			"tread":48
		},
		"armament":{
			"sb":{
				"stats":ogreArmaments.sb,
				"count":2
			},
			"rack":{
				"stats":ogreArmaments.rack,
				"count":4
			},
			"intmsl":{
				"stats":ogreArmaments.intmsl,
				"count":20
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":8
			}
		}
	},
	"fencerb":{
		"name":"Fencer-B",
		"cost":23,
		"size":8,
		"movement":{
			"base":3,
			"tread":48
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":2
			},
			"rack":{
				"stats":ogreArmaments.rack,
				"count":4
			},
			"intmsl":{
				"stats":ogreArmaments.intmsl,
				"count":20
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":8
			}
		}
	},
	"doppelsoldner":{
		"name":"Doppelsoldner",
		"cost":40,
		"size":9,
		"movement":{
			"base":3,
			"tread":60
		},
		"armament":{
			"mb":{
				"stats":ogreArmaments.mb,
				"count":2
			},
			"sb":{
				"stats":ogreArmaments.sb,
				"count":8
			},
			"rack":{
				"stats":ogreArmaments.rack,
				"count":6
			},
			"intmsl":{
				"stats":ogreArmaments.intmsl,
				"count":20
			},
			"ap":{
				"stats":ogreArmaments.ap,
				"count":12
			}
		}
	}
}

<|endoftext|>


<commit_before>const iff = (cond, yes, no) => (cond ? yes : no);
module.exports = ({result, siteUrl, membersUrl, emailRecipient}) => `
<!doctype html>

    <meta name="viewport" content="width=device-width">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Your member import is complete</title>

            Your member import is complete

${iff(result.imported > 0, `Your member import is complete`, `Your member import was unsuccessful`)}

                    ${iff(result.imported === 0 && result.errors.length === 0, `

No members were added.

                    `, ``)}
                    ${iff(result.imported > 0, `

A total of ${result.imported} ${iff(result.imported === 1, 'person', 'people')} were successfully added or updated in your list of members, and now have access to your site.

`, ``)}
                    ${iff(result.errors.length > 0, `

                        ${iff(result.imported === 0, `No members were added.`, `<strong style="font-weight: 600;">${result.errors.length}</strong> ${iff(result.errors.length === 1, `member was`, `members were`)} skipped due to errors.`)} There's a validated CSV file attached to this email with the list of errors so that you can fix them and re-upload the CSV to complete the import.

`, '')}

                        ${iff(result.imported > 0, `View members`, `Try again`)}

This email was sent from ${siteUrl.host} to ${emailRecipient}

`;

<commit_msg>Fixed member import email heading spacing<commit_after>const iff = (cond, yes, no) => (cond ? yes : no);
module.exports = ({result, siteUrl, membersUrl, emailRecipient}) => `
<!doctype html>

    <meta name="viewport" content="width=device-width">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Your member import is complete</title>

            Your member import is complete

${iff(result.imported > 0, `Your member import is complete`, `Your member import was unsuccessful`)}

                    ${iff(result.imported === 0 && result.errors.length === 0, `

No members were added.

                    `, ``)}
                    ${iff(result.imported > 0, `

A total of ${result.imported} ${iff(result.imported === 1, 'person', 'people')} were successfully added or updated in your list of members, and now have access to your site.

`, ``)}
                    ${iff(result.errors.length > 0, `

                        ${iff(result.imported === 0, `No members were added.`, `<strong style="font-weight: 600;">${result.errors.length}</strong> ${iff(result.errors.length === 1, `member was`, `members were`)} skipped due to errors.`)} There's a validated CSV file attached to this email with the list of errors so that you can fix them and re-upload the CSV to complete the import.

`, '')}

                        ${iff(result.imported > 0, `View members`, `Try again`)}

This email was sent from ${siteUrl.host} to ${emailRecipient}

`;

<|endoftext|>


<commit_before>  ///////////////////////
 // private variables //
///////////////////////
var board = require("alekhine")
  , crypto = require("crypto")

  , hash = function(d) { return crypto.createHash("sha1").update(d).digest("hex") }

  , clients = {}
  , client_count = 0
  , waiting = [];

// linked list with lookup via hash key
var games = (function() {
  var length = 0
    , nodes = {}
    , head = null
    , tail = null;

  return {
    mk : function(w, b) {
      var gid = hash(w + b)
        , hash_l = 6;

      // shorten gid
      while(nodes[gid.substring(0, hash_l)] > -1) hash_l++;
      gid = gid.substring(0, hash_l);

      // create game object in two parts for closure
      nodes[gid] = { next: null
                   , prev: null
                   , gid: gid
                   , state: { private: { white: w
                                       , black: b
                                       , board: new board()
                                       , watchers: []
                                       }
                            , public: null
                            }
                   };
      nodes[gid].state.public = { gid: gid
                                , fen: nodes[gid].state.private.board.get_fen()
                                , b: clients[nodes[gid].state.private.black].name
                                , w: clients[nodes[gid].state.private.white].name
                                , s_w: ""
                                , s_b: ""
                                };

      // structural changes
      if (length == 0) {
        head = nodes[gid];
        tail = nodes[gid];
      } else {
        tail.next = nodes[gid];
        nodes[gid].prev = tail;

        tail = nodes[gid];
      }
      length++;

      return gid;
    }
  , rm : function(gid) {
      var node = nodes[gid];

      if (node == null) return; // opponent already quit

      if (node.next == null) {
        tail = node.prev;
      } else if (node.prev == null) {
        head = node.next
      } else {
        node.prev.next = node.next
        node.next.prev = node.prev
      }

      delete nodes[gid];
      length--;
    }

  // etc
  , get_node : function(gid) {
      return nodes[gid];
    }
  , get_position : function(gid) {
      if (nodes[gid].prev) return this.get_position(nodes[gid].prev.gid) + 1;
      else return 1;
    }
  , get_next_or_head : function(gid) {
    return (nodes[gid].next) ? nodes[gid].next : head;
  }
  , get_prev_or_tail : function(gid) {
    return (nodes[gid].prev) ? nodes[gid].prev : tai;
  }

  , add_watcher : function(sid) {
      if (head) {
        head.state.private.watchers.push(sid);

        //log.debug("added watcher " + sid + "; game_id " + head.state.public.gid);

        return head.state.public.gid;
      } else {
        //log.debug("added watcher " + sid + "; no games to watch");

        return null
      }
    }
  , mv_watcher : function(sid, from, to) {
      var node = games.get_node(from)
        , watchers = node.state.private.watchers
        , watcher_index = watchers.indexOf(sid)
        , new_gid = null;

      if (watcher_index > -1) node.state.private.watchers = watchers.splice(watcher_index, 1);

      if (to == "h") {
        head.state.private.watchers.push(sid);
        new_gid = head.gid;
      } else if (to == "l") {
        if (node.prev) {
          node.prev.state.private.watchers.push(sid);
          new_gid = node.prev.gid;
        } else {
          tail.state.private.watchers.push(sid);
          new_gid = tail.gid;
        }
      } else if (to == "r") {
        if (node.next) {
          node.next.state.private.watchers.push(sid);
          new_gid = node.next.gid;
        } else {
          head.state.private.watchers.push(sid);
          new_gid = head.gid;
        }
      } else if (to == "t") {
        tail.state.private.watchers.push(sid);
        new_gid = tail.gid;
      }

      return new_gid;
    }

  , set_board : function(gid, board) {
      nodes[gid].state.private.board = board;
    }
  , carry_over : function(gid, piece) {
      var ascii = piece.charCodeAt(0)
        , node = nodes[gid]
        , to_gid;

      if (node.next) {
        to_node = node.next;
      } else if (head.gid != gid) {
        to_node = head;
      } else {
        return; // there is only one game in progress, no carry over
      }

      if (ascii > 64 && ascii < 91) {
        to_node.state.public.s_w += piece;
      } else if (ascii > 96 && ascii < 123) {
        to_node.state.public.s_b += piece;
      }
    }
  , get_states : function(gid) {
      var node = nodes[gid]
        , states = {};

      if (!node) return;

      states["c"] = node.state.public;

      if (node.next) states["r"] = node.next.state.public;
      else if (head && head.gid != node.gid && (node.prev && head.gid != node.prev.gid)) states["r"] = head.state.public;
      else states["r"] = null;

      if (node.prev) states["l"] = node.prev.state.public;
      else if (tail && tail.gid != node.gid && (node.next && tail.gid != node.next.gid)) states["l"] = tail.state.public;
      else states["l"] = null;

      return states;
    }
  , get_watchers : function(game) {
      var node = nodes[game]
        , watchers = [];

      if (!node) return;

      array_union(watchers, node.state.private.watchers);

      // TODO: add adjacent players

      if (node.next) array_union(watchers, node.next.state.private.watchers);
      else if (head) array_union(watchers, head.state.private.watchers);

      if (node.prev) array_union(watchers, node.prev.state.private.watchers);
      else if (tail) array_union(watchers, tail.state.private.watchers);

      return watchers;
    }
  }
})();

  ////////////////////
 // public methods //
////////////////////
exports.join = function(sid, name) {
  if (!clients[sid]) add_client(sid, name);

  if (waiting.length > 0) {
    var opp = waiting.pop()
      , color = (Math.floor(Math.random() * 2) == 0) ? "w" : "b"
      , ret = {}

    if (opp == sid) {
      waiting.push(sid); // get back in line
      return;
    }

    var gid = (color == "w") ? games.mk(sid, opp) : games.mk(opp, sid);

    ret.gid = clients[sid].gid = clients[opp].gid = gid;
    ret.states = games.get_states(gid);
    ret.opp = opp;
    ret[sid] = color;

    //log.info("game " + gid + " created for " + sid + " and " + opp);

    return ret;
  } else {
    waiting.push(sid);

    return null;
  }
}

exports.update = function(sid, from, to, callback) {
  if (!clients[sid]) return; // client disconnected during an update

  var gid = clients[sid].gid
    , node = games.get_node(gid)
    , board = node.state.private.board;

  board.update_state(from, to, function(message, captured) {
    // TODO: promotions

    if (message == "invalid") {
      //log.info("client " + sid + " performed an invalid move; from: " + from + "; to: " + to + "; fen: " + board.get_fen());
      // TODO: handle invalid moves?
    } else if (message == "complete") {
      var w = node.state.private.white
        , b = node.state.private.black
        , opp_id = (sid == w) ? b : w
        , watchers = games.get_watchers(gid)

      if (captured) {
        games.carry_over(gid, captured);
      }

      games.set_board(gid, board);
      games.get_node(gid).state.public.fen = board.get_fen();

      if (callback) callback({ gid: gid, opp_id: opp_id, watchers: watchers, state: games.get_node(gid).state.public });
    }
  });
}

exports.kibitz = function(sid, name) {
  var gid = games.add_watcher(sid);

  add_client(sid, name, gid);

  return games.get_states(gid);
}

exports.mv_watcher = function(sid, to) {
  if (!clients[sid]) return;

  var client = clients[sid]
    , watch = clients[sid].watch
    , new_gid = games.mv_watcher(sid, watch, to);

  client.watch = new_gid;

  if (to === "h" || to === "t") {
    return { states: games.get_states(new_gid) };
  } else {
    if (to === "l") {
      var state = games.get_next_or_head(new_gid).state.public;
    } else if (to === "r") {
      var state = games.get_prev_or_tail(new_gid).state.public;
    }

    return { state: state }
  }
}

exports.quit = function(sid) {
  var gid = clients[sid].gid
    , watch = clients[sid].gid
    , ret = null;

  if (gid) {
    var opp_id = games.rm(gid);
    if (opp_id && clients[opp_id].gid) delete clients[opp_id].gid; // else opponent already quit

    // TODO: notify watchers

    ret = { game: gid, opp_id: opp_id };
  } else if (watch) {
    var idx = games.get_node(watch).state.private.watchers.indexOf(sid);
    if (idx > -1) games.get_node(gid).state.private.watchers = games.get_node(gid).watchers.splice(idx, 1);
  }

  delete clients[sid];
  client_count--;

  return ret;
}

  ///////////////////////
 // private functions //
///////////////////////
function add_client(sid, name, watch) {
  if (clients[sid]) return;

  clients[sid] = { name: name };

  if (watch) clients[sid].watch = watch;

  client_count++;
}

function array_union(a1, a2) {
  for (var i = 0, l = a2.length; i < l; i++) a1.push(a2[i]);
}
<commit_msg>update alekhine api calls<commit_after>  ///////////////////////
 // private variables //
///////////////////////
var board = require("alekhine")
  , crypto = require("crypto")

  , hash = function(d) { return crypto.createHash("sha1").update(d).digest("hex") }

  , clients = {}
  , client_count = 0
  , waiting = [];

// linked list with lookup via hash key
var games = (function() {
  var length = 0
    , nodes = {}
    , head = null
    , tail = null;

  return {
    mk : function(w, b) {
      var gid = hash(w + b)
        , hash_l = 6;

      // shorten gid
      while(nodes[gid.substring(0, hash_l)] > -1) hash_l++;
      gid = gid.substring(0, hash_l);

      // create game object in two parts for closure
      nodes[gid] = { next: null
                   , prev: null
                   , gid: gid
                   , state: { private: { white: w
                                       , black: b
                                       , board: new board()
                                       , watchers: []
                                       }
                            , public: null
                            }
                   };
      nodes[gid].state.public = { gid: gid
                                , fen: nodes[gid].state.private.board.get_fen()
                                , b: clients[nodes[gid].state.private.black].name
                                , w: clients[nodes[gid].state.private.white].name
                                , s_w: ""
                                , s_b: ""
                                };

      // structural changes
      if (length == 0) {
        head = nodes[gid];
        tail = nodes[gid];
      } else {
        tail.next = nodes[gid];
        nodes[gid].prev = tail;

        tail = nodes[gid];
      }
      length++;

      return gid;
    }
  , rm : function(gid) {
      var node = nodes[gid];

      if (node == null) return; // opponent already quit

      if (node.next == null) {
        tail = node.prev;
      } else if (node.prev == null) {
        head = node.next
      } else {
        node.prev.next = node.next
        node.next.prev = node.prev
      }

      delete nodes[gid];
      length--;
    }

  // etc
  , get_node : function(gid) {
      return nodes[gid];
    }
  , get_position : function(gid) {
      if (nodes[gid].prev) return this.get_position(nodes[gid].prev.gid) + 1;
      else return 1;
    }
  , get_next_or_head : function(gid) {
    return (nodes[gid].next) ? nodes[gid].next : head;
  }
  , get_prev_or_tail : function(gid) {
    return (nodes[gid].prev) ? nodes[gid].prev : tai;
  }

  , add_watcher : function(sid) {
      if (head) {
        head.state.private.watchers.push(sid);

        //log.debug("added watcher " + sid + "; game_id " + head.state.public.gid);

        return head.state.public.gid;
      } else {
        //log.debug("added watcher " + sid + "; no games to watch");

        return null
      }
    }
  , mv_watcher : function(sid, from, to) {
      var node = games.get_node(from)
        , watchers = node.state.private.watchers
        , watcher_index = watchers.indexOf(sid)
        , new_gid = null;

      if (watcher_index > -1) node.state.private.watchers = watchers.splice(watcher_index, 1);

      if (to == "h") {
        head.state.private.watchers.push(sid);
        new_gid = head.gid;
      } else if (to == "l") {
        if (node.prev) {
          node.prev.state.private.watchers.push(sid);
          new_gid = node.prev.gid;
        } else {
          tail.state.private.watchers.push(sid);
          new_gid = tail.gid;
        }
      } else if (to == "r") {
        if (node.next) {
          node.next.state.private.watchers.push(sid);
          new_gid = node.next.gid;
        } else {
          head.state.private.watchers.push(sid);
          new_gid = head.gid;
        }
      } else if (to == "t") {
        tail.state.private.watchers.push(sid);
        new_gid = tail.gid;
      }

      return new_gid;
    }

  , set_board : function(gid, board) {
      nodes[gid].state.private.board = board;
    }
  , carry_over : function(gid, piece) {
      var ascii = piece.charCodeAt(0)
        , node = nodes[gid]
        , to_gid;

      if (node.next) {
        to_node = node.next;
      } else if (head.gid != gid) {
        to_node = head;
      } else {
        return; // there is only one game in progress, no carry over
      }

      if (ascii > 64 && ascii < 91) {
        to_node.state.public.s_w += piece;
      } else if (ascii > 96 && ascii < 123) {
        to_node.state.public.s_b += piece;
      }
    }
  , get_states : function(gid) {
      var node = nodes[gid]
        , states = {};

      if (!node) return;

      states["c"] = node.state.public;

      if (node.next) states["r"] = node.next.state.public;
      else if (head && head.gid != node.gid && (node.prev && head.gid != node.prev.gid)) states["r"] = head.state.public;
      else states["r"] = null;

      if (node.prev) states["l"] = node.prev.state.public;
      else if (tail && tail.gid != node.gid && (node.next && tail.gid != node.next.gid)) states["l"] = tail.state.public;
      else states["l"] = null;

      return states;
    }
  , get_watchers : function(game) {
      var node = nodes[game]
        , watchers = [];

      if (!node) return;

      array_union(watchers, node.state.private.watchers);

      // TODO: add adjacent players

      if (node.next) array_union(watchers, node.next.state.private.watchers);
      else if (head) array_union(watchers, head.state.private.watchers);

      if (node.prev) array_union(watchers, node.prev.state.private.watchers);
      else if (tail) array_union(watchers, tail.state.private.watchers);

      return watchers;
    }
  }
})();

  ////////////////////
 // public methods //
////////////////////
exports.join = function(sid, name) {
  if (!clients[sid]) add_client(sid, name);

  if (waiting.length > 0) {
    var opp = waiting.pop()
      , color = (Math.floor(Math.random() * 2) == 0) ? "w" : "b"
      , ret = {}

    if (opp == sid) {
      waiting.push(sid); // get back in line
      return;
    }

    var gid = (color == "w") ? games.mk(sid, opp) : games.mk(opp, sid);

    ret.gid = clients[sid].gid = clients[opp].gid = gid;
    ret.states = games.get_states(gid);
    ret.opp = opp;
    ret[sid] = color;

    //log.info("game " + gid + " created for " + sid + " and " + opp);

    return ret;
  } else {
    waiting.push(sid);

    return null;
  }
}

exports.update = function(sid, from, to, callback) {
  if (!clients[sid]) return; // client disconnected during an update

  var gid = clients[sid].gid
    , node = games.get_node(gid)
    , board = node.state.private.board;

  board.move(from, to, function(message, captured) {
    // TODO: promotions

    if (message == "invalid") {
      //log.info("client " + sid + " performed an invalid move; from: " + from + "; to: " + to + "; fen: " + board.get_fen());
      // TODO: handle invalid moves?
    } else if (message == "complete") {
      var w = node.state.private.white
        , b = node.state.private.black
        , opp_id = (sid == w) ? b : w
        , watchers = games.get_watchers(gid)

      if (captured) {
        games.carry_over(gid, captured);
      }

      games.set_board(gid, board);
      games.get_node(gid).state.public.fen = board.get_fen();

      if (callback) callback({ gid: gid, opp_id: opp_id, watchers: watchers, state: games.get_node(gid).state.public });
    }
  });
}

exports.kibitz = function(sid, name) {
  var gid = games.add_watcher(sid);

  add_client(sid, name, gid);

  return games.get_states(gid);
}

exports.mv_watcher = function(sid, to) {
  if (!clients[sid]) return;

  var client = clients[sid]
    , watch = clients[sid].watch
    , new_gid = games.mv_watcher(sid, watch, to);

  client.watch = new_gid;

  if (to === "h" || to === "t") {
    return { states: games.get_states(new_gid) };
  } else {
    if (to === "l") {
      var state = games.get_next_or_head(new_gid).state.public;
    } else if (to === "r") {
      var state = games.get_prev_or_tail(new_gid).state.public;
    }

    return { state: state }
  }
}

exports.quit = function(sid) {
  var gid = clients[sid].gid
    , watch = clients[sid].gid
    , ret = null;

  if (gid) {
    var opp_id = games.rm(gid);
    if (opp_id && clients[opp_id].gid) delete clients[opp_id].gid; // else opponent already quit

    // TODO: notify watchers

    ret = { game: gid, opp_id: opp_id };
  } else if (watch) {
    var idx = games.get_node(watch).state.private.watchers.indexOf(sid);
    if (idx > -1) games.get_node(gid).state.private.watchers = games.get_node(gid).watchers.splice(idx, 1);
  }

  delete clients[sid];
  client_count--;

  return ret;
}

  ///////////////////////
 // private functions //
///////////////////////
function add_client(sid, name, watch) {
  if (clients[sid]) return;

  clients[sid] = { name: name };

  if (watch) clients[sid].watch = watch;

  client_count++;
}

function array_union(a1, a2) {
  for (var i = 0, l = a2.length; i < l; i++) a1.push(a2[i]);
}
<|endoftext|>


<commit_before>import expect from 'expect.js';
import React from 'react/addons';
let { TestUtils } = React.addons;

import AutoSuggest from '../src/AutoSuggest';

import Asserter from './Asserter';

describe('AutoSuggest', function() {

    var fetchSuggestions = function(value, callback) {
        callback(['one', 'two', 'three']);
    };

    var onSuggestion = function(suggestion) {
        console.info('onSuggestion', suggestion);
    };

    it('should render', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest);
    });

    it('should not display the DropDown', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .assertDropDownNotDisplayed()
        ;
    });

    it('should display the DropDown when text is entered', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );
        new Asserter(autoSuggest)
            .assertDropDownNotDisplayed()
            .enterNewValue('C')
            .assertDropDownDisplayed()
        ;
    });

    it('should display the suggestions when the DropDown is displayed', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should select the next suggestion when the down key is pressed', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSelectedSuggestion()
            .arrowDown()
            .assertSelectedSuggestion('one')
            .arrowDown()
            .assertSelectedSuggestion('two')
            .arrowDown()
            .assertSelectedSuggestion('three')
            .arrowDown()
            .assertSelectedSuggestion()
            .arrowDown()
            .assertSelectedSuggestion('one')
        ;
    });

    it('should select the previous suggestion when the up key is pressed', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSelectedSuggestion()
            .arrowUp()
            .assertSelectedSuggestion('three')
            .arrowUp()
            .assertSelectedSuggestion('two')
            .arrowUp()
            .assertSelectedSuggestion('one')
            .arrowUp()
            .assertSelectedSuggestion()
            .arrowUp()
            .assertSelectedSuggestion('three')
        ;
    });

    it('should display entered text', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat')
            .assertValue('cat');
    });

    it('should pass entered text to suggestions function', function(done) {
        var fetchSuggestions = function(suggestion) {
            expect(suggestion).to.equal('cat');
            done();
        };

        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat')
            .assertValue('cat');
    });

    it('should hide the dropdown when the esc key is pressed', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat')
            .assertDropDownDisplayed()
            .esc()
            .assertDropDownNotDisplayed()
    });

    it('should allow custom dropdowns', function() {
        var Suggestion = React.createClass({
            render() {
                return
{this.props.suggestion}
;
            }
        });

        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <Suggestion />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should allow custom dropdowns for arrays of json', function() {
        var fetchSuggestions = function(value, callback) {
            callback([{title:'one'}, {title:'two'}, {title:'three'}]);
        };

        var Suggestion = React.createClass({
            render() {
                return
{this.props.suggestion.title}
;
            }
        });

        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <Suggestion />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSuggestions(['one', 'two', 'three'])
        ;
    });

/*
    TODO

    it('should handle displaying term from custom dropdowns', function() {

        var fetchSuggestions = function(value, callback) {
            callback([{title:'one'}, {title:'two'}, {title:'three'}]);
        };

        var Suggestion = React.createClass({

            render() {
                var suggestion = this.props.suggestion;
                return
{suggestion.title}
;
            }
        });

        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <Suggestion />
            </AutoSuggest>
        );

        var searchBox = TestUtils.findRenderedDOMComponentWithClass(autoSuggest, 'SearchBox');

        TestUtils.Simulate.change(searchBox.getDOMNode(), {target: {value: 'X'}});

        TestUtils.Simulate.keyDown(searchBox, {keyCode: 40});

        var suggestions = TestUtils.scryRenderedDOMComponentsWithClass(autoSuggest, 'Suggestion');

        expect(suggestions[0].getDOMNode().innerHTML).to.equal('one');

        expect(searchBox.getDOMNode().value).to.equal('one');

    });
*/

});
<commit_msg>write failing test.... next make it green<commit_after>import expect from 'expect.js';
import React from 'react/addons';
let { TestUtils } = React.addons;

import AutoSuggest from '../src/AutoSuggest';

import Asserter from './Asserter';

describe('AutoSuggest', function() {

    var fetchSuggestions = function(value, callback) {
        callback(['one', 'two', 'three']);
    };

    var onSuggestion = function(suggestion) {
        console.info('onSuggestion', suggestion);
    };

    it('should render', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest);
    });

    it('should not display the DropDown', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .assertDropDownNotDisplayed()
        ;
    });

    it('should display the DropDown when text is entered', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );
        new Asserter(autoSuggest)
            .assertDropDownNotDisplayed()
            .enterNewValue('C')
            .assertDropDownDisplayed()
        ;
    });

    it('should display the suggestions when the DropDown is displayed', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should select the next suggestion when the down key is pressed', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSelectedSuggestion()
            .arrowDown()
            .assertSelectedSuggestion('one')
            .arrowDown()
            .assertSelectedSuggestion('two')
            .arrowDown()
            .assertSelectedSuggestion('three')
            .arrowDown()
            .assertSelectedSuggestion()
            .arrowDown()
            .assertSelectedSuggestion('one')
        ;
    });

    it('should select the previous suggestion when the up key is pressed', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSelectedSuggestion()
            .arrowUp()
            .assertSelectedSuggestion('three')
            .arrowUp()
            .assertSelectedSuggestion('two')
            .arrowUp()
            .assertSelectedSuggestion('one')
            .arrowUp()
            .assertSelectedSuggestion()
            .arrowUp()
            .assertSelectedSuggestion('three')
        ;
    });

    it('should display entered text', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat')
            .assertValue('cat');
    });

    it('should pass entered text to suggestions function', function(done) {
        var fetchSuggestions = function(suggestion) {
            expect(suggestion).to.equal('cat');
            done();
        };

        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat')
            .assertValue('cat');
    });

    it('should hide the dropdown when the esc key is pressed', function() {
        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}/>
        );

        new Asserter(autoSuggest)
            .enterNewValue('cat')
            .assertDropDownDisplayed()
            .esc()
            .assertDropDownNotDisplayed()
    });

    it('should allow custom dropdowns', function() {
        var Suggestion = React.createClass({
            render() {
                return
{this.props.suggestion}
;
            }
        });

        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <Suggestion />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should allow custom dropdowns for arrays of json', function() {
        var fetchSuggestions = function(value, callback) {
            callback([{title:'one'}, {title:'two'}, {title:'three'}]);
        };

        var Suggestion = React.createClass({
            render() {
                return
{this.props.suggestion.title}
;
            }
        });

        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <Suggestion />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('C')
            .assertSuggestions(['one', 'two', 'three'])
        ;
    });

    it('should handle displaying term from custom dropdowns', function() {
        var fetchSuggestions = function(value, callback) {
            callback([{title:'one'}, {title:'two'}, {title:'three'}]);
        };

        var Suggestion = React.createClass({
            render() {
                var suggestion = this.props.suggestion;
                return
{suggestion.title}
;
            }
        });

        var autoSuggest = TestUtils.renderIntoDocument(
            <AutoSuggest suggestions={fetchSuggestions} onSuggestion={onSuggestion}>
                <Suggestion />
            </AutoSuggest>
        );

        new Asserter(autoSuggest)
            .enterNewValue('c')
            .assertValue('c')
            .assertSuggestions(['one', 'two', 'three'])
            .arrowDown()
            // TODO
            //.assertValue('one')
        ;
    });

});
<|endoftext|>


<commit_before>var socket;					// for socket.io
var currenttime;
var username='noname';

var width = 600,
	height = 560,
	cellWidth = 48,
	cellHeight = 30, // cell size
	gapWidth = 40;
	gapHeight = 10;

var hour = d3.time.format("%I"),
	minute = d3.time.format("%M"),
	month = d3.time.format("%b"),
	weekday = d3.time.format("%a"),
	date = d3.time.format("%d"),
	ampm = d3.time.format("%p"),
	format = d3.time.format("%b/%d %H:%M%p");

var blockdata = [];

socket = io.connect('http://171.65.102.132:3006');

socket.on('postblocks', function(data){
	for (var i=0;i<=data.length/4;i++){
		var block = new Object();
		block.id = i;

		var d = new Date(0);
		d.setTime(data[i*4]);
		block.time = d;

		block.lock = data[i*4+1];
		block.userid = data[i*4+2];
		block.expid = data[i*4+3];
		blockdata.push(block);
	}
	blockdata.length = blockdata.length-2;
	console.dir(blockdata);
	draw(blockdata);
});

socket.on('message', function(msg){
	console.log(msg);
});

socket.on('connect', function() {
	console.log("Connected!");
	callBlocks(new Date());
});

socket.on('disconnect', function() {
	console.log('disconnected');
	chat.html("Disconnected!");
});

function callBlocks(ticket){
	console.log(month(ticket)+" "+date(ticket)+" "+hour(ticket)+":"+minute(ticket)+ampm(ticket));

	// +- 1 hour blocks range
	var beginT = d3.time.hour.floor(ticket);
	beginT = d3.time.hour.offset(beginT, -1);
	var endT = d3.time.hour.offset(beginT, 3);

	console.log(month(beginT)+" "+date(beginT)+" "+hour(beginT)+":"+minute(beginT)+ampm(beginT));
	console.log(month(endT)+" "+date(endT)+" "+hour(endT)+":"+minute(endT)+ampm(endT));

	socket.emit('timeline', { type: 'callblocks', user:username, begintime: beginT, endtime: endT});
}

function draw(blockdata){
	var tip = d3.tip()
		.attr('class', 'd3-tip')
		.direction('e')
		.offset([0,10])
		.html(function(d) {
			return "time: "+d.time;
		});

	var color = d3.scale.quantize()
    	.domain([-.05, .05])
    	.range(d3.range(11).map(function(d) { return "q" + d + "-11"; }));

	var svg = d3.select("body").append("svg")
		.attr("width", width)
		.attr("height", height)
		.attr("class", "RdYlGn")
	.append("g")
		.attr("transform", "translate(24, 24)");

	svg.call(tip);

	var column = svg.selectAll(".column")
		.data(blockdata)
	.enter().append("g")
		.attr("class", "column")
		.attr("transform", function (d,i)
		{ return "translate("+ (i-i%12)/12 * (cellWidth+gapWidth)+ ","+i%12 * (cellHeight+gapHeight) + ")"; })
		.on('mouseover', tip.show)
		.on('mouseout', tip.hide);

	column.append("rect")
		.attr("width", cellWidth)
		.attr("height", cellHeight);

	column.append("text")
		.attr("class", "block-name")
		.attr("x",cellWidth/2)
		.attr("y",cellHeight/2)
		.attr("dy", ".3em")
		.style("text-anchor", "middle")
		.text(function(d) {
			var h = d.time.getHours();
			h=("0" + h%12).slice(-2);
			var m = d.time.getMinutes();
			m=("0" + m).slice(-2);
			return h+":"+m;
		})
		.on('mouseover', tip.show)
		.on('mouseout', tip.hide);
}<commit_msg>timeline css<commit_after>var socket;					// for socket.io
var currenttime;
var username='noname';

var width = 600,
	height = 560,
	cellWidth = 48,
	cellHeight = 30, // cell size
	gapWidth = 40;
	gapHeight = 10;

var hour = d3.time.format("%I"),
	minute = d3.time.format("%M"),
	month = d3.time.format("%b"),
	weekday = d3.time.format("%a"),
	date = d3.time.format("%d"),
	ampm = d3.time.format("%p"),
	format = d3.time.format("%b/%d %H:%M%p");

var blockdata = [];

socket = io.connect('http://171.65.102.132:3006');

socket.on('postblocks', function(data){
	for (var i=0;i<=data.length/4;i++){
		var block = new Object();
		block.id = i;

		var d = new Date(0);
		d.setTime(data[i*4]);
		block.time = d;

		block.lock = data[i*4+1];
		block.userid = data[i*4+2];
		block.expid = data[i*4+3];
		blockdata.push(block);
	}
	blockdata.length = blockdata.length-2;
	console.dir(blockdata);
	draw(blockdata);
});

socket.on('message', function(msg){
	console.log(msg);
});

socket.on('connect', function() {
	console.log("Connected!");
	callBlocks(new Date());
});

socket.on('disconnect', function() {
	console.log('disconnected');
	chat.html("Disconnected!");
});

function callBlocks(ticket){
	console.log(month(ticket)+" "+date(ticket)+" "+hour(ticket)+":"+minute(ticket)+ampm(ticket));

	// +- 1 hour blocks range
	var beginT = d3.time.hour.floor(ticket);
	beginT = d3.time.hour.offset(beginT, -1);
	var endT = d3.time.hour.offset(beginT, 3);

	console.log(month(beginT)+" "+date(beginT)+" "+hour(beginT)+":"+minute(beginT)+ampm(beginT));
	console.log(month(endT)+" "+date(endT)+" "+hour(endT)+":"+minute(endT)+ampm(endT));

	socket.emit('timeline', { type: 'callblocks', user:username, begintime: beginT, endtime: endT});
}

function draw(blockdata){
	var tip = d3.tip()
		.attr('class', 'd3-tip')
		.direction('e')
		.offset([0,10])
		.html(function(d) {
			return "time: "+d.time;
		});

	var color = d3.scale.quantize()
    	.domain([-.05, .05])
    	.range(d3.range(11).map(function(d) { return "q" + d + "-11"; }));

	var svg = d3.select("#d3Area").append("svg")
		.attr("width", width)
		.attr("height", height)
		.attr("class", "RdYlGn")
	.append("g")
		.attr("transform", "translate(24, 24)");

	svg.call(tip);

	var column = svg.selectAll(".column")
		.data(blockdata)
	.enter().append("g")
		.attr("class", "column")
		.attr("transform", function (d,i)
		{ return "translate("+ (i-i%12)/12 * (cellWidth+gapWidth)+ ","+i%12 * (cellHeight+gapHeight) + ")"; })
		.on('mouseover', tip.show)
		.on('mouseout', tip.hide);

	column.append("rect")
		.attr("width", cellWidth)
		.attr("height", cellHeight);

	column.append("text")
		.attr("class", "block-name")
		.attr("x",cellWidth/2)
		.attr("y",cellHeight/2)
		.attr("dy", ".3em")
		.style("text-anchor", "middle")
		.text(function(d) {
			var h = d.time.getHours();
			h=("0" + h%12).slice(-2);
			var m = d.time.getMinutes();
			m=("0" + m).slice(-2);
			return h+":"+m;
		})
		.on('mouseover', tip.show)
		.on('mouseout', tip.hide);
}<|endoftext|>


<commit_before>(function () {
    'use strict';

    /**
     * A component for split view content editing
     */
    var umbVariantContentEditors = {
        templateUrl: 'views/components/content/umb-variant-content-editors.html',
        bindings: {
            page: "<",
            content: "<",
            culture: "<",
            segment: "<",
            onSelectApp: "&?",
            onSelectAppAnchor: "&?",
            onBack: "&?",
            showBack: "<?"
        },
        controllerAs: 'vm',
        controller: umbVariantContentEditorsController
    };

    function umbVariantContentEditorsController($scope, $location) {

        var prevContentDateUpdated = null;

        var vm = this;

        vm.$onInit = onInit;
        vm.$onChanges = onChanges;
        vm.$doCheck = doCheck;
        vm.$postLink = postLink;

        vm.openSplitView = openSplitView;
        vm.closeSplitView = closeSplitView;
        vm.selectVariant = selectVariant;
        vm.selectApp = selectApp;
        vm.selectAppAnchor = selectAppAnchor;

        //Used to track how many content views there are (for split view there will be 2, it could support more in theory)
        vm.editors = [];

        /** Called when the component initializes */
        function onInit() {
            prevContentDateUpdated = angular.copy(vm.content.updateDate);
            setActiveVariant();
        }

        /** Called when the component has linked all elements, this is when the form controller is available */
        function postLink() {

        }

        /**
         * Watch for model changes
         * @param {any} changes
         */
        function onChanges(changes) {

            if (changes.culture && !changes.culture.isFirstChange() && changes.culture.currentValue !== changes.culture.previousValue) {
                setActiveVariant();
            }
            if (changes.segment && !changes.segment.isFirstChange() && changes.segment.currentValue !== changes.segment.previousValue) {
                setActiveVariant();
            }
        }

        /** Allows us to deep watch whatever we want - executes on every digest cycle */
        function doCheck() {
            if (!angular.equals(vm.content.updateDate, prevContentDateUpdated)) {
                setActiveVariant();
                prevContentDateUpdated = angular.copy(vm.content.updateDate);
            }
        }

        /** This is called when the split view changes based on the umb-variant-content */
        function splitViewChanged() {
            //send an event downwards
            $scope.$broadcast("editors.content.splitViewChanged", { editors: vm.editors });
        }

        /**
         * Set the active variant based on the current culture or segment (query string)
         */
        function setActiveVariant() {
            // set the active variant
            var activeVariant = null;
            _.each(vm.content.variants, function (v) {
                if ((!v.language || v.language.culture === vm.culture) && v.segment === vm.segment) {
                    activeVariant = v;
                }
            });
            if (!activeVariant) {
                // Set the first variant to active if we can't find it.
                // If the content item is invariant, then only one item exists in the array.
                activeVariant = vm.content.variants[0];
            }

            insertVariantEditor(0, activeVariant);

            if (vm.editors.length > 1) {
                //now re-sync any other editor content (i.e. if split view is open)
                for (var s = 1; s < vm.editors.length; s++) {
                    //get the variant from the scope model
                    var variant = _.find(vm.content.variants, function (v) {
                        return (!v.language || v.language.culture === vm.editors[s].content.language.culture) && v.segment === vm.editors[s].content.segment;
                    });
                    vm.editors[s].content = variant;
                }
            }

        }

        /**
         * Updates the editors collection for a given index for the specified variant
         * @param {any} index
         * @param {any} variant
         */
        function insertVariantEditor(index, variant) {

            if (vm.editors[index]) {
                vm.editors[index].content.active = false;
            }
            variant.active = true;

            var variantCulture = variant.language ? variant.language.culture : "invariant";
            var variantSegment = variant.segment;

            //check if the culture at the index is the same, if it's null an editor will be added
            var currentCulture = vm.editors.length === 0 || vm.editors.length <= index ? null : vm.editors[index].culture;
            //check if the segment at the index is the same, if it's null an editor will be added
            var currentSegment = vm.editors.length === 0 || vm.editors.length <= index ? null : vm.editors[index].segment;

            if (currentCulture !== variantCulture || currentSegment !== variantSegment) {

                //Not the current culture which means we need to modify the array.
                //NOTE: It is not good enough to just replace the `content` object at a given index in the array
                // since that would mean that directives are not re-initialized.
                vm.editors.splice(index, 1, {
                    content: variant,
                    //used for "track-by" ng-repeat
                    culture: variantCulture,
                    segment: variantSegment
                });
            }
            else {
                //replace the editor for the same culture
                vm.editors[index].content = variant;
            }
        }

        /**
         * Adds a new editor to the editors array to show content in a split view
         * @param {any} selectedVariant
         */
        function openSplitView(selectedVariant) {

            insertVariantEditor(vm.editors.length, selectedVariant);

            splitViewChanged();

        }

        /** Closes the split view */
        function closeSplitView(editorIndex) {
            // TODO: hacking animation states - these should hopefully be easier to do when we upgrade angular
            var editor = vm.editors[editorIndex];
            vm.editors.splice(editorIndex, 1);
            editor.content.active = false;

            //update the current culture to reflect the last open variant (closing the split view corresponds to selecting the other variant)

            $location.search({"cculture": vm.editors[0].content.language ? vm.editors[0].content.language.culture : null, "csegment": vm.editors[0].content.segment});
            splitViewChanged();
        }

        /**
         * Changes the currently selected variant
         * @param {any} variant This is the model of the variant/language drop down item in the editor header
         * @param {any} editorIndex The index of the editor being changed
         */
        function selectVariant(variant, editorIndex) {

            var variantCulture = variant.language ? variant.language.culture : "invariant";
            var variantSegment = variant.segment || null;

            // Check if we already have this editor open, if so, do nothing.
            if (vm.editors.find((editor) => (!editor.content.language || editor.content.language.culture === variantCulture) && editor.content.segment === variantSegment)) {
                return;
            }

            //if the editor index is zero, then update the query string to track the lang selection, otherwise if it's part
            //of a 2nd split view editor then update the model directly.
            if (editorIndex === 0) {
                //If we've made it this far, then update the query string.
                //The editor will respond to this query string changing.
                $location.search({"cculture": variantCulture, "csegment": variant.segment});
            }
            else {

                //update the editors collection
                insertVariantEditor(editorIndex, variant);

            }
        }

        /**
         * Stores the active app in a variable so we can remember it when changing language
         * @param {any} app This is the model of the selected app
         */
        function selectApp(app) {
            if(vm.onSelectApp) {
                vm.onSelectApp({"app": app});
            }
        }

        function selectAppAnchor(app, anchor) {
            if(vm.onSelectAppAnchor) {
                vm.onSelectAppAnchor({"app": app, "anchor": anchor});
            }
        }

    }

    angular.module('umbraco.directives').component('umbVariantContentEditors', umbVariantContentEditors);

})();
<commit_msg>enforce the content content-app in split view mode.<commit_after>(function () {
    'use strict';

    /**
     * A component for split view content editing
     */
    var umbVariantContentEditors = {
        templateUrl: 'views/components/content/umb-variant-content-editors.html',
        bindings: {
            page: "<",
            content: "<",
            culture: "<",
            segment: "<",
            onSelectApp: "&?",
            onSelectAppAnchor: "&?",
            onBack: "&?",
            showBack: "<?"
        },
        controllerAs: 'vm',
        controller: umbVariantContentEditorsController
    };

    function umbVariantContentEditorsController($scope, $location) {

        var prevContentDateUpdated = null;

        var vm = this;

        vm.$onInit = onInit;
        vm.$onChanges = onChanges;
        vm.$doCheck = doCheck;
        vm.$postLink = postLink;

        vm.openSplitView = openSplitView;
        vm.closeSplitView = closeSplitView;
        vm.selectVariant = selectVariant;
        vm.selectApp = selectApp;
        vm.selectAppAnchor = selectAppAnchor;

        //Used to track how many content views there are (for split view there will be 2, it could support more in theory)
        vm.editors = [];

        /** Called when the component initializes */
        function onInit() {
            prevContentDateUpdated = angular.copy(vm.content.updateDate);
            setActiveVariant();
        }

        /** Called when the component has linked all elements, this is when the form controller is available */
        function postLink() {

        }

        /**
         * Watch for model changes
         * @param {any} changes
         */
        function onChanges(changes) {

            if (changes.culture && !changes.culture.isFirstChange() && changes.culture.currentValue !== changes.culture.previousValue) {
                setActiveVariant();
            }
            if (changes.segment && !changes.segment.isFirstChange() && changes.segment.currentValue !== changes.segment.previousValue) {
                setActiveVariant();
            }
        }

        /** Allows us to deep watch whatever we want - executes on every digest cycle */
        function doCheck() {
            if (!angular.equals(vm.content.updateDate, prevContentDateUpdated)) {
                setActiveVariant();
                prevContentDateUpdated = angular.copy(vm.content.updateDate);
            }
        }

        /** This is called when the split view changes based on the umb-variant-content */
        function splitViewChanged() {
            //send an event downwards
            $scope.$broadcast("editors.content.splitViewChanged", { editors: vm.editors });
        }

        /**
         * Set the active variant based on the current culture or segment (query string)
         */
        function setActiveVariant() {
            // set the active variant
            var activeVariant = null;
            _.each(vm.content.variants, function (v) {
                if ((!v.language || v.language.culture === vm.culture) && v.segment === vm.segment) {
                    activeVariant = v;
                }
            });
            if (!activeVariant) {
                // Set the first variant to active if we can't find it.
                // If the content item is invariant, then only one item exists in the array.
                activeVariant = vm.content.variants[0];
            }

            insertVariantEditor(0, activeVariant);

            if (vm.editors.length > 1) {
                //now re-sync any other editor content (i.e. if split view is open)
                for (var s = 1; s < vm.editors.length; s++) {
                    //get the variant from the scope model
                    var variant = _.find(vm.content.variants, function (v) {
                        return (!v.language || v.language.culture === vm.editors[s].content.language.culture) && v.segment === vm.editors[s].content.segment;
                    });
                    vm.editors[s].content = variant;
                }
            }

        }

        /**
         * Updates the editors collection for a given index for the specified variant
         * @param {any} index
         * @param {any} variant
         */
        function insertVariantEditor(index, variant) {

            if (vm.editors[index]) {
                vm.editors[index].content.active = false;
            }
            variant.active = true;

            var variantCulture = variant.language ? variant.language.culture : "invariant";
            var variantSegment = variant.segment;

            //check if the culture at the index is the same, if it's null an editor will be added
            var currentCulture = vm.editors.length === 0 || vm.editors.length <= index ? null : vm.editors[index].culture;
            //check if the segment at the index is the same, if it's null an editor will be added
            var currentSegment = vm.editors.length === 0 || vm.editors.length <= index ? null : vm.editors[index].segment;

            if (currentCulture !== variantCulture || currentSegment !== variantSegment) {

                //Not the current culture which means we need to modify the array.
                //NOTE: It is not good enough to just replace the `content` object at a given index in the array
                // since that would mean that directives are not re-initialized.
                vm.editors.splice(index, 1, {
                    content: variant,
                    //used for "track-by" ng-repeat
                    culture: variantCulture,
                    segment: variantSegment
                });
            }
            else {
                //replace the editor for the same culture
                vm.editors[index].content = variant;
            }
        }

        /**
         * Adds a new editor to the editors array to show content in a split view
         * @param {any} selectedVariant
         */
        function openSplitView(selectedVariant) {
            // enforce content contentApp in splitview.
            var contentApp = vm.content.apps.find((app) => app.alias === "umbContent");
            if(contentApp) {
                selectApp(contentApp);
            }

            insertVariantEditor(vm.editors.length, selectedVariant);

            splitViewChanged();

        }

        /** Closes the split view */
        function closeSplitView(editorIndex) {
            // TODO: hacking animation states - these should hopefully be easier to do when we upgrade angular
            var editor = vm.editors[editorIndex];
            vm.editors.splice(editorIndex, 1);
            editor.content.active = false;

            //update the current culture to reflect the last open variant (closing the split view corresponds to selecting the other variant)

            $location.search({"cculture": vm.editors[0].content.language ? vm.editors[0].content.language.culture : null, "csegment": vm.editors[0].content.segment});
            splitViewChanged();
        }

        /**
         * Changes the currently selected variant
         * @param {any} variant This is the model of the variant/language drop down item in the editor header
         * @param {any} editorIndex The index of the editor being changed
         */
        function selectVariant(variant, editorIndex) {

            var variantCulture = variant.language ? variant.language.culture : "invariant";
            var variantSegment = variant.segment || null;

            // Check if we already have this editor open, if so, do nothing.
            if (vm.editors.find((editor) => (!editor.content.language || editor.content.language.culture === variantCulture) && editor.content.segment === variantSegment)) {
                return;
            }

            //if the editor index is zero, then update the query string to track the lang selection, otherwise if it's part
            //of a 2nd split view editor then update the model directly.
            if (editorIndex === 0) {
                //If we've made it this far, then update the query string.
                //The editor will respond to this query string changing.
                $location.search({"cculture": variantCulture, "csegment": variant.segment});
            }
            else {

                //update the editors collection
                insertVariantEditor(editorIndex, variant);

            }
        }

        /**
         * Stores the active app in a variable so we can remember it when changing language
         * @param {any} app This is the model of the selected app
         */
        function selectApp(app) {
            if(vm.onSelectApp) {
                vm.onSelectApp({"app": app});
            }
        }

        function selectAppAnchor(app, anchor) {
            if(vm.onSelectAppAnchor) {
                vm.onSelectAppAnchor({"app": app, "anchor": anchor});
            }
        }

    }

    angular.module('umbraco.directives').component('umbVariantContentEditors', umbVariantContentEditors);

})();
<|endoftext|>


<commit_before>(function() {
  var Color, X, Y, Z

chromato = function(x, y, z, m) {
  return new Color(x, y, z, m);
};

chromato.color = function(x, y, z, m) {
  return new Color(x, y, z, m);
};

chromato.hex = function(x) {
  return new Color(x);
};

}).call(this);<commit_msg>Add 'var' statement<commit_after>(function() {
  var Color;

chromato = function(x, y, z, m) {
  return new Color(x, y, z, m);
};

chromato.color = function(x, y, z, m) {
  return new Color(x, y, z, m);
};

chromato.hex = function(x) {
  return new Color(x);
};

}).call(this);<|endoftext|>


<commit_before>'use strict';

var CustomError    = require('es5-ext/lib/Error/custom')
  , copy           = require('es5-ext/lib/Object/copy')
  , d              = require('es5-ext/lib/Object/descriptor')
  , extend         = require('es5-ext/lib/Object/extend')
  , forEach        = require('es5-ext/lib/Object/for-each')
  , contains       = require('es5-ext/lib/String/prototype/contains')
  , makeElement    = require('dom-ext/lib/Document/prototype/make-element')
  , replaceContent = require('dom-ext/lib/Element/prototype/replace-content')
  , SetCollection  = require('set-collection')
  , Db             = require('../')

  , map = Array.prototype.map
  , getRel = function (name) { return this.get(name); }
  , Base = Db.Base
  , Fieldset;

require('./fieldset-item');

module.exports = Fieldset = function (document, list/*, options*/) {
	var options = Object(arguments[2]), className;

	this.document = document;
	this.list = list;
	this.options = options;

	if (list._isLiveList) {
		this.items = list.liveMap(this.renderItem, this);
		this.items.on('change', this.reload);
	} else {
		this.items = list.map(this.renderItem, this);
	}

	this.render();
	this.reload();

	this.dom.classList.add('dbjs');
	if (options.class != null) {
		className = String(options.class).trim();
		if (className) {
			className.split(' ').forEach(function (name) {
				this.add(name);
			}, this.dom.classList);
		}
	}
};

Object.defineProperties(Fieldset.prototype, extend({
	render: d(function () {
		var el = makeElement.bind(this.document);
		this.dom = el('fieldset', el('table',
			this.domItems = el('tbody')));
	}),
	renderItem: d(function (rel) {
		var controlOpts;
		if (this.options.control) controlOpts = copy(this.options.control);
		if (this.options.controls && this.options.controls[rel._id_]) {
			controlOpts = extend(Object(controlOpts),
				this.options.controls[rel._id_]);
		}
		if (this.options.idPostfix != null) {
			controlOpts.id = rel.DOMId + this.options.idPostfix;
		}
		return rel.toDOMFieldsetItem(this.document, controlOpts);
	}),
	toDOM: d(function () { return this.dom; })
}, d.binder({
	reload: d(function () {
		replaceContent.call(this.domItems, this.options.prepend, this.items,
			this.options.append);
	})
})));

Object.defineProperty(Base, 'DOMFieldset', d(Fieldset));

Object.defineProperty(Db.prototype, 'toDOMFieldset',
	d(function (document/*, options*/) {
		var options = Object(arguments[1]), data, list, include, controlOpts
		  , setup, byOrder;

		if (options.names != null) {
			data = options.names._isSet_ ? options.names.values : options.names;
			data = new SetCollection(map.call(data, getRel, this));
		} else {
			data = this.getProperties(options.tag);
		}

		if (options.include) {
			if (options.include._type_ === 'relation') {
				include = new SetCollection();
				include.add(options.include);
			} else if (!options.include._isSet_) {
				include = new SetCollection(options.include);
			} else {
				include = options.include;
			}
			include.forEach(function (rel) {
				if (!rel || (rel._type_ !== 'relation')) {
					throw new CustomError("Include item must be a relation",
						'INVALID_RELATION');
				}
			});
			data = data.union(include);
		}

		if (options.controls) {
			forEach(options.controls, function (value, name, options) {
				if (contains.call(name, ':')) return;
				options[this._id_ + ':' + name] = value;
				delete options[name];
			}, this);
		}
		controlOpts = Object(options.controls);

		byOrder = function (a, b) {
			var aOpts = controlOpts[a._id_], bOpts = controlOpts[b._id_];
			a = (aOpts && !isNaN(aOpts.order)) ? aOpts.order : a.order;
			b = (bOpts && !isNaN(bOpts.order)) ? bOpts.order : b.order;
			return a - b;
		};

		if (data.list) {
			list = data.list(byOrder);
			list.forEach(setup = function (rel) {
				var opts = controlOpts[rel._id_];
				if (opts && !isNaN(opts.order)) return;
				rel._order.on('change', list._sort);
			});
			data.on('add', function (rel) {
				setup(rel);
				list._sort();
			});
			data.on('delete', function (rel) {
				var opts = controlOpts[rel._id_];
				if (opts && !isNaN(opts.order)) return;
				rel._order.off('change', list._sort);
			});
		} else {
			list = data.values.sort(byOrder);
		}

		return new Base.DOMFieldset(document, list, options);
	}));
<commit_msg>Do duck type check for liveMap directly<commit_after>'use strict';

var CustomError    = require('es5-ext/lib/Error/custom')
  , copy           = require('es5-ext/lib/Object/copy')
  , d              = require('es5-ext/lib/Object/descriptor')
  , extend         = require('es5-ext/lib/Object/extend')
  , forEach        = require('es5-ext/lib/Object/for-each')
  , contains       = require('es5-ext/lib/String/prototype/contains')
  , makeElement    = require('dom-ext/lib/Document/prototype/make-element')
  , replaceContent = require('dom-ext/lib/Element/prototype/replace-content')
  , SetCollection  = require('set-collection')
  , Db             = require('../')

  , map = Array.prototype.map
  , getRel = function (name) { return this.get(name); }
  , Base = Db.Base
  , Fieldset;

require('./fieldset-item');

module.exports = Fieldset = function (document, list/*, options*/) {
	var options = Object(arguments[2]), className;

	this.document = document;
	this.list = list;
	this.options = options;

	if (list.liveMap) {
		this.items = list.liveMap(this.renderItem, this);
		this.items.on('change', this.reload);
	} else {
		this.items = list.map(this.renderItem, this);
	}

	this.render();
	this.reload();

	this.dom.classList.add('dbjs');
	if (options.class != null) {
		className = String(options.class).trim();
		if (className) {
			className.split(' ').forEach(function (name) {
				this.add(name);
			}, this.dom.classList);
		}
	}
};

Object.defineProperties(Fieldset.prototype, extend({
	render: d(function () {
		var el = makeElement.bind(this.document);
		this.dom = el('fieldset', el('table',
			this.domItems = el('tbody')));
	}),
	renderItem: d(function (rel) {
		var controlOpts;
		if (this.options.control) controlOpts = copy(this.options.control);
		if (this.options.controls && this.options.controls[rel._id_]) {
			controlOpts = extend(Object(controlOpts),
				this.options.controls[rel._id_]);
		}
		if (this.options.idPostfix != null) {
			controlOpts.id = rel.DOMId + this.options.idPostfix;
		}
		return rel.toDOMFieldsetItem(this.document, controlOpts);
	}),
	toDOM: d(function () { return this.dom; })
}, d.binder({
	reload: d(function () {
		replaceContent.call(this.domItems, this.options.prepend, this.items,
			this.options.append);
	})
})));

Object.defineProperty(Base, 'DOMFieldset', d(Fieldset));

Object.defineProperty(Db.prototype, 'toDOMFieldset',
	d(function (document/*, options*/) {
		var options = Object(arguments[1]), data, list, include, controlOpts
		  , setup, byOrder;

		if (options.names != null) {
			data = options.names._isSet_ ? options.names.values : options.names;
			data = new SetCollection(map.call(data, getRel, this));
		} else {
			data = this.getProperties(options.tag);
		}

		if (options.include) {
			if (options.include._type_ === 'relation') {
				include = new SetCollection();
				include.add(options.include);
			} else if (!options.include._isSet_) {
				include = new SetCollection(options.include);
			} else {
				include = options.include;
			}
			include.forEach(function (rel) {
				if (!rel || (rel._type_ !== 'relation')) {
					throw new CustomError("Include item must be a relation",
						'INVALID_RELATION');
				}
			});
			data = data.union(include);
		}

		if (options.controls) {
			forEach(options.controls, function (value, name, options) {
				if (contains.call(name, ':')) return;
				options[this._id_ + ':' + name] = value;
				delete options[name];
			}, this);
		}
		controlOpts = Object(options.controls);

		byOrder = function (a, b) {
			var aOpts = controlOpts[a._id_], bOpts = controlOpts[b._id_];
			a = (aOpts && !isNaN(aOpts.order)) ? aOpts.order : a.order;
			b = (bOpts && !isNaN(bOpts.order)) ? bOpts.order : b.order;
			return a - b;
		};

		if (data.list) {
			list = data.list(byOrder);
			list.forEach(setup = function (rel) {
				var opts = controlOpts[rel._id_];
				if (opts && !isNaN(opts.order)) return;
				rel._order.on('change', list._sort);
			});
			data.on('add', function (rel) {
				setup(rel);
				list._sort();
			});
			data.on('delete', function (rel) {
				var opts = controlOpts[rel._id_];
				if (opts && !isNaN(opts.order)) return;
				rel._order.off('change', list._sort);
			});
		} else {
			list = data.values.sort(byOrder);
		}

		return new Base.DOMFieldset(document, list, options);
	}));
<|endoftext|>


<commit_before>'use strict';

var assign          = require('es5-ext/object/assign')
  , forEach         = require('es5-ext/object/for-each')
  , startsWith      = require('es5-ext/string/#/starts-with')
  , toArray         = require('es5-ext/array/to-array')
  , d               = require('d')
  , autoBind        = require('d/auto-bind')
  , memoize         = require('memoizee/plain')
  , isObservable    = require('observable-value/is-observable')
  , makeElement     = require('dom-ext/document/#/make-element')
  , castAttribute   = require('dom-ext/element/#/cast-attribute')
  , replaceContent  = require('dom-ext/element/#/replace-content')
  , resolveProperty = require('dbjs/_setup/utils/resolve-property-path')
  , DOMComposite    = require('../_composite')
  , htmlAttributes  = require('../_html-attributes')
  , setup           = require('../')

  , defineProperty = Object.defineProperty
  , getObservable, Fieldset;

getObservable = function (name) {
	var result = resolveProperty(this, name);
	return result.object.getObservable(result.key);
};

Fieldset = function (document, list/*, options*/) {
	var options = Object(arguments[2]);

	this.document = document;
	this.list = list;
	this.items = {};
	this.options = Object(options.control);
	this.customOptions = Object(options.controls);
	this.prepend = options.prepend;
	this.append = options.append;

	this.list = list.map(this.renderItem, this);
	if (isObservable(this.list)) this.list.on('change', this.reload);

	this.render();
	forEach(options, function (value, name) {
		if (!htmlAttributes[name] && !startsWith.call(name, 'data-')) return;
		castAttribute.call(this.dom, name, value);
	}, this);
	this.dom.classList.add('dbjs');
	this.dom._dbjsFieldset = this;

	this.reload();
};

Object.defineProperties(Fieldset.prototype, assign({
	render: d(function () {
		var el = makeElement.bind(this.document);
		this.dom = el('fieldset', this.domItems = el('ul'));
	}),
	renderItem: d(function (observable) {
		var options = this.getOptions(observable.ownDescriptor), field, dom, li;
		field = observable.toDOMInputComponent(this.document, options);
		this.items[observable.dbId] = field;
		dom = field.toDOM(this.document);
		if (dom.nodeName.toLowerCase() === 'li') return dom;
		li = this.document.createElement('li');
		li.appendChild(dom);
		return li;
	}),
	toDOM: d(function () { return this.dom; }),
	getOptions: d(DOMComposite.prototype.getOptions)
}, autoBind({
	reload: d(function () {
		replaceContent.call(this.domItems, this.prepend, this.list, this.append);
	})
})));

module.exports = exports = memoize(function (db) {
	var proto = setup(db).Base.prototype;

	defineProperty(db.Base, 'DOMFieldset', d(Fieldset));

	defineProperty(proto, 'toDOMFieldset', d(function (document/*, options*/) {
		var options = Object(arguments[1]), data, names;

		if (options.names != null) {
			names = options.names;
			if (!names.map) names = toArray(names);
			data = names.map(getObservable, this);
		} else {
			data = this.toSet('key').toArray().map(getObservable, this);
		}

		if (options.include) {
			throw new TypeError("`include` option is (postponed). Fix it");
		}

		return new db.Base.DOMFieldset(document, data, options);
	}));
}, { normalizer: require('memoizee/normalizers/get-1')() });

exports.Fieldset = Fieldset;
<commit_msg>Implement fieldset.getItem<commit_after>'use strict';

var assign          = require('es5-ext/object/assign')
  , ensureString    = require('es5-ext/object/validate-stringifiable-value')
  , forEach         = require('es5-ext/object/for-each')
  , some            = require('es5-ext/object/some')
  , startsWith      = require('es5-ext/string/#/starts-with')
  , endsWith        = require('es5-ext/string/#/ends-with')
  , toArray         = require('es5-ext/array/to-array')
  , d               = require('d')
  , autoBind        = require('d/auto-bind')
  , memoize         = require('memoizee/plain')
  , isObservable    = require('observable-value/is-observable')
  , makeElement     = require('dom-ext/document/#/make-element')
  , castAttribute   = require('dom-ext/element/#/cast-attribute')
  , replaceContent  = require('dom-ext/element/#/replace-content')
  , resolveProperty = require('dbjs/_setup/utils/resolve-property-path')
  , DOMComposite    = require('../_composite')
  , htmlAttributes  = require('../_html-attributes')
  , setup           = require('../')

  , defineProperty = Object.defineProperty
  , getObservable, Fieldset;

getObservable = function (name) {
	var result = resolveProperty(this, name);
	return result.object.getObservable(result.key);
};

Fieldset = function (document, list/*, options*/) {
	var options = Object(arguments[2]);

	this.document = document;
	this.list = list;
	this.items = {};
	this.options = Object(options.control);
	this.customOptions = Object(options.controls);
	this.prepend = options.prepend;
	this.append = options.append;

	this.list = list.map(this.renderItem, this);
	if (isObservable(this.list)) this.list.on('change', this.reload);

	this.render();
	forEach(options, function (value, name) {
		if (!htmlAttributes[name] && !startsWith.call(name, 'data-')) return;
		castAttribute.call(this.dom, name, value);
	}, this);
	this.dom.classList.add('dbjs');
	this.dom._dbjsFieldset = this;

	this.reload();
};

Object.defineProperties(Fieldset.prototype, assign({
	render: d(function () {
		var el = makeElement.bind(this.document);
		this.dom = el('fieldset', this.domItems = el('ul'));
	}),
	renderItem: d(function (observable) {
		var options = this.getOptions(observable.ownDescriptor), field, dom, li;
		field = observable.toDOMInputComponent(this.document, options);
		this.items[observable.dbId] = field;
		dom = field.toDOM(this.document);
		if (dom.nodeName.toLowerCase() === 'li') return dom;
		li = this.document.createElement('li');
		li.appendChild(dom);
		return li;
	}),
	getItem: d(function (path) {
		var result = null;
		path = ensureString(path);
		if (path[0] === '/') path = path.slice(1);
		some(this.items, function (item, key) {
			if ((key === path) || endsWith.call(key, '/' + path)) {
				result = item;
				return true;
			}
		});
		return result;
	}),
	toDOM: d(function () { return this.dom; }),
	getOptions: d(DOMComposite.prototype.getOptions)
}, autoBind({
	reload: d(function () {
		replaceContent.call(this.domItems, this.prepend, this.list, this.append);
	})
})));

module.exports = exports = memoize(function (db) {
	var proto = setup(db).Base.prototype;

	defineProperty(db.Base, 'DOMFieldset', d(Fieldset));

	defineProperty(proto, 'toDOMFieldset', d(function (document/*, options*/) {
		var options = Object(arguments[1]), data, names;

		if (options.names != null) {
			names = options.names;
			if (!names.map) names = toArray(names);
			data = names.map(getObservable, this);
		} else {
			data = this.toSet('key').toArray().map(getObservable, this);
		}

		if (options.include) {
			throw new TypeError("`include` option is (postponed). Fix it");
		}

		return new db.Base.DOMFieldset(document, data, options);
	}));
}, { normalizer: require('memoizee/normalizers/get-1')() });

exports.Fieldset = Fieldset;
<|endoftext|>


<commit_before>import Immutable from 'immutable'
import {isUndefined, contains, _ as lodash, reject} from 'lodash'
import Promise from 'bluebird'
import {v4 as uuid} from 'node-uuid'
import {isTrue} from 'sto-helpers/lib/is'

import randomChar from 'sto-helpers/lib/randomChar'
import {checkScheduleTimeConflicts} from 'sto-sis-time-parser'
import {getCourses} from '../helpers/courses'
import findWarnings from '../helpers/findCourseWarnings'

let ScheduleRecord = Immutable.Record({
	id: uuid(),
	active: false,
	year: 0,
	semester: 0,
	index: 1,
	title: 'Schedule ' + randomChar().toUpperCase(),
	clbids: Immutable.List(),
	_courseData: Promise.resolve(Immutable.List()),
})

class Schedule extends ScheduleRecord {
	constructor(data={}) {
		// console.log('schedule constructor')
		super(data)
		return this.withMutations((sched) => {
			sched = sched.set('id', data.id || uuid())
			sched = sched.set('clbids', Immutable.fromJS(data.clbids || []))
			sched = sched.set('_courseData', getCourses(this.clbids))
			return sched
		})
	}

	// Getters
	get courses() {
		return this._courseData
	}

	// Schedule Maintenance
	move(to={}) {
		// `to` is an object: {year, semester}
		return this.withMutations((sched) => {
			if (to.year)
				sched = sched.set('year', to.year)
			if (to.semester)
				sched = sched.set('semester', to.semester)
			return sched
		})
	}

	reorder(newIndex) {
		return this.set('index', newIndex)
	}

	rename(newTitle) {
		return this.set('title', newTitle)
	}

	// Course Maintenance

	reorderCourse(clbid, newIndex) {
		let oldIndex = this.clbids.findIndex((id) => id === clbid)

		return this.withMutations((sched) => {
			sched = sched.set('clbids', sched.clbids.splice(oldIndex, 1, clbid))
			sched = sched.set('_courseData', getCourses(sched.clbids))
			return sched
		})
	}

	addCourse(clbid) {
		// let start = performance.now()
		// console.log(`adding clbid ${clbid} to schedule ${this.id} (${this.year}-${this.semester}.${this.index})`)

		return this.withMutations((sched) => {
			sched = sched.set('clbids', sched.clbids.push(clbid))
			sched = sched.set('_courseData', getCourses(sched.clbids))
			// sched.get('_courseData').then(d => console.log(`it took ${Math.round(performance.now() - start)}ms to add ${clbid} to ${sched.year}-${sched.semester};`, 'clbids:', sched.clbids.toJS(), 'titles:', d.map(c => c.title)))
			return sched
		})
	}

	removeCourse(clbid) {
		// let start = performance.now()
		// console.log(`removing clbid ${clbid} from schedule ${this.id} (${this.year}-${this.semester}.${this.index})`)

		let index = this.clbids.indexOf(clbid)
		return this.withMutations((sched) => {
			sched = sched.set('clbids', sched.clbids.delete(index))
			sched = sched.set('_courseData', getCourses(sched.clbids))
			// sched.get('_courseData').then(d => console.log(`it took ${Math.round(performance.now() - start)}ms to remove ${clbid} from ${this.year}-${this.semester};`, 'clbids:', sched.clbids.toJS(), 'titles:', d.map(c => c.title)))
			return sched
		})
	}

	// Schedule Validation

	validate() {
		// Checks to see if the schedule is valid
		return this.courses
			 // only check the courses that have data
			.then(courses => reject(courses, isUndefined))
			// Step one: do any times conflict?
			.then((courses) => findWarnings(courses, this.toJS()))
			.then((conflicts) => {
				let hasConflict = lodash(conflicts)
					// flatten the nested arrays
					.flatten()
					// filter to just the non-null/undefined items
					.filter(item => item)
					// grab the 'warning' values
					.pluck('warning')
					// and see if any are true
					.any(isTrue)

				if (hasConflict) {
					console.log('schedule conflicts', conflicts, hasConflict)
				}

				return {
					hasConflict: hasConflict,
					conflicts: conflicts,
				}
			})
	}

	toJSON() {
		let toRemove = ['_courseData']
		let filtered = this.toMap()
			.filterNot((val, key) => contains(toRemove, key))
		return filtered.toJS()
	}
}

export default Schedule
<commit_msg>Bugfix: add when reordering courses, insert the clbid into the correct location<commit_after>import Immutable from 'immutable'
import {isUndefined, contains, _ as lodash, reject} from 'lodash'
import Promise from 'bluebird'
import {v4 as uuid} from 'node-uuid'
import {isTrue} from 'sto-helpers/lib/is'

import randomChar from 'sto-helpers/lib/randomChar'
import {checkScheduleTimeConflicts} from 'sto-sis-time-parser'
import {getCourses} from '../helpers/courses'
import findWarnings from '../helpers/findCourseWarnings'

let ScheduleRecord = Immutable.Record({
	id: uuid(),
	active: false,
	year: 0,
	semester: 0,
	index: 1,
	title: 'Schedule ' + randomChar().toUpperCase(),
	clbids: Immutable.List(),
	_courseData: Promise.resolve(Immutable.List()),
})

class Schedule extends ScheduleRecord {
	constructor(data={}) {
		// console.log('schedule constructor')
		super(data)
		return this.withMutations((sched) => {
			sched = sched.set('id', data.id || uuid())
			sched = sched.set('clbids', Immutable.fromJS(data.clbids || []))
			sched = sched.set('_courseData', getCourses(this.clbids))
			return sched
		})
	}

	// Getters
	get courses() {
		return this._courseData
	}

	// Schedule Maintenance
	move(to={}) {
		// `to` is an object: {year, semester}
		return this.withMutations((sched) => {
			if (to.year)
				sched = sched.set('year', to.year)
			if (to.semester)
				sched = sched.set('semester', to.semester)
			return sched
		})
	}

	reorder(newIndex) {
		return this.set('index', newIndex)
	}

	rename(newTitle) {
		return this.set('title', newTitle)
	}

	// Course Maintenance

	reorderCourse(clbid, newIndex) {
		let oldIndex = this.clbids.findIndex((id) => id === clbid)

		return this.withMutations((sched) => {
			sched = sched.set('clbids', sched.clbids.splice(oldIndex, 1))
			sched = sched.set('clbids', sched.clbids.splice(newIndex, 0, clbid))
			sched = sched.set('_courseData', getCourses(sched.clbids))
			return sched
		})
	}

	addCourse(clbid) {
		// let start = performance.now()
		// console.log(`adding clbid ${clbid} to schedule ${this.id} (${this.year}-${this.semester}.${this.index})`)

		return this.withMutations((sched) => {
			sched = sched.set('clbids', sched.clbids.push(clbid))
			sched = sched.set('_courseData', getCourses(sched.clbids))
			// sched.get('_courseData').then(d => console.log(`it took ${Math.round(performance.now() - start)}ms to add ${clbid} to ${sched.year}-${sched.semester};`, 'clbids:', sched.clbids.toJS(), 'titles:', d.map(c => c.title)))
			return sched
		})
	}

	removeCourse(clbid) {
		// let start = performance.now()
		// console.log(`removing clbid ${clbid} from schedule ${this.id} (${this.year}-${this.semester}.${this.index})`)

		let index = this.clbids.indexOf(clbid)
		return this.withMutations((sched) => {
			sched = sched.set('clbids', sched.clbids.delete(index))
			sched = sched.set('_courseData', getCourses(sched.clbids))
			// sched.get('_courseData').then(d => console.log(`it took ${Math.round(performance.now() - start)}ms to remove ${clbid} from ${this.year}-${this.semester};`, 'clbids:', sched.clbids.toJS(), 'titles:', d.map(c => c.title)))
			return sched
		})
	}

	// Schedule Validation

	validate() {
		// Checks to see if the schedule is valid
		return this.courses
			 // only check the courses that have data
			.then(courses => reject(courses, isUndefined))
			// Step one: do any times conflict?
			.then((courses) => findWarnings(courses, this.toJS()))
			.then((conflicts) => {
				let hasConflict = lodash(conflicts)
					// flatten the nested arrays
					.flatten()
					// filter to just the non-null/undefined items
					.filter(item => item)
					// grab the 'warning' values
					.pluck('warning')
					// and see if any are true
					.any(isTrue)

				if (hasConflict) {
					console.log('schedule conflicts', conflicts, hasConflict)
				}

				return {
					hasConflict: hasConflict,
					conflicts: conflicts,
				}
			})
	}

	toJSON() {
		let toRemove = ['_courseData']
		let filtered = this.toMap()
			.filterNot((val, key) => contains(toRemove, key))
		return filtered.toJS()
	}
}

export default Schedule
<|endoftext|>