<commit_before>$(document).ready(function() {

    window.Todo = Backbone.Model.extend({
        //Default Attributes
        defaults: {
            content: "new empty todo",
            parent: 0,
            indent: 0,
            position: 0,
            done: 0
        },

        initialize: function() {
            if (!this.get("content")) {
              this.set({"content": this.defaults.content});
            }
        },

		done: function() {
		    if (this.get("done") == 0) {
		        this.save({done: 1});
		    } else {
		        this.save({done: 0});
		    }

		},

		setParent: function(parentTodo) {
			if(!parentTodo) {
				return false;
			}
			this.save({parent: parentTodo.id});
			return true;
		},

		indentBy: function(number) {
			this.save({indent: (this.get('indent') + (number ? number: 0))});
		}
    });

    window.TodoList = Backbone.Collection.extend({
        model: Todo,
//        localStorage: new Store("forgetmenot"),
		url: "fmn.php?",

        nextPosition: function() {
            if (this.length === 0) {
                return 1;
            } else {
                return this.last().get('position') + 1;
            }
        },

		thoseWithParentOf: function(parentId) {
			return _.select(this.models, function(model) {
				return model.get('parent') == parentId;
			});
		},

		thatAreDone: function() {
			return _.select(this.models, function(model) {
				return model.get('done') == true;
			});
		},

        comparator: function(todo) {
          return todo.get('position');
        },

		// Get's passed current todo
		// Returns parent todo
		// Checking to make sure the
		// correct parent todo is returned
		getParentTodo: function(todo, previousTodo) {
			var previousTodo = this.at(Todos.indexOf(previousTodo ? previousTodo : todo) - 1);
			if (!previousTodo) {
				return false;
			} else if (todo.get('indent') == previousTodo.get('indent')) {
				return (previousTodo ? previousTodo : NaN);
			} else {
				return this.getParentTodo(todo, previousTodo);
			}

		},

		maintainChildrenOfParent: function(todo) {
			var childTodo = this.at(Todos.indexOf(childTodo ? childTodo: todo) + 1);
			if (!childTodo) {
				return false;
			} else {
				if(todo.get('indent') == 0 && todo.get('id') == this.getNextTodo(todo).get('parent')) {
					this.getNextTodo(todo).save({parent: todo.get('id'), indent: 1});
				}
				Todos.each(function(model){
					if (todo.get('id') === model.get('parent')) {
						model.save({indent: (todo.get('indent') + 1)});
					}
				});
				return this.maintainChildrenOfParent(childTodo);
			}

		},

		// Gets passed current todo
		// Sets current todo's parent todo Id correctly
		setParentTodo: function(todo) {
			todo.get('position');
			var previousTodo = this.getPreviousTodo(todo);
			if (previousTodo.get('parent') == '0') {
				todo.setParent(previousTodo);
			}
		},

		// Passed in current todo
		// Returns next todo item
		// Based off of todo's 'position' attribute
		getNextTodo: function(todo) {
			return this.at(Todos.indexOf(todo) + 1);
		},

		// Passed in current todo
		// Returns previous todo item
		// Based off of todo's 'position' attribute
		getPreviousTodo: function(todo) {
			return this.at(Todos.indexOf(todo) - 1);
		}

    });

    window.Todos = new TodoList;

    // Each Individual Todo Item View
    window.TodoView = Backbone.View.extend({
        tagName: "li",
        template: _.template($('#item-template').html()),

        events: {
            "dblclick .display"        :      	"edit",
            "click .display"        :      	"edit",
            "keydown .edit input"              :      	"keyboardActions",
			"click input.done"  			    : 		"toggleDone",
            "click .display .delete"					   : 	    "deleteTodo"
        },

        initialize: function() {
            _.bindAll(this, 'render', 'deleteTodo', 'close','save', 'keyboardActions');
            this.model.bind('change', this.render);
            this.el.id = this.model.get("id");
            // Give reverse access to a model's view by setting its 'this'
            // to a new attribute on the model
            this.model.view = this;
        },

        render: function() {
            $(this.el).html(this.template(this.model.toJSON()));
			this.el.className = 'todo indent-' + this.model.get('indent');
            this.input = this.$(".input");
            this.input.bind('blur', this.close);
            return this;
        },

        edit: function() {
            var content = this.model.get('content');
            $(this.el).addClass("editing");
            this.input.val(content);
            this.input.focus();
            //return false;
        },

        save: function(indent) {
			var $inputVal = this.input.val();
            this.model.save({
				content: ($inputVal == '' ? this.model.defaults.content : $inputVal)
			});
			this.model.indentBy(indent);
        },

		close: function() {
			this.save();
			$(this.el).removeClass("editing");
		},

        deleteTodo: function() {
            this.model.destroy();
            this.remove();
        },

        keyboardActions: function(e) {
			/**
				left arrow		37
				up arrow		38
				right arrow		39
				down arrow		40
			**/
			// Enter button
			if (e.keyCode == 13) {
				if(!Todos.getNextTodo(this.model)) {
					// If last todo then close current
					// and create a new todo
					this.close();
					forgetmenot.createNewAfter(this.model);
				} else if (this.model.get('indent') === Todos.getNextTodo(this.model).get('indent')) {
					// If next todo is at same indent
					// Then edit next todo
					this.editNextTodo(e);
				} else if (this.model.get('indent') > Todos.getNextTodo(this.model).get('indent')) {
					// If next todo is closer to indent '0'
					// Then we create a new todo after current
					forgetmenot.createNewAfter(this.model);
				} else {
				// If not last todo then edit next todo
					this.editNextTodo(e);
				}
			}
			// Tab key - move todo to right one
			// And make sub todo of parent (if it exists)
			if (e.keyCode == 9 && !e.shiftKey) {
				this.save();
				if(this.model.setParent(Todos.getParentTodo(this.model))) {
					this.save(1);
					Todos.maintainChildrenOfParent(this.model);
					this.edit();
				}
				e.preventDefault();
			}
			// Shift + Tab key - move todo to left one
			// And un-sub it from parent (if it exists)
			if (e.shiftKey && e.keyCode == 9) {
				this.save();
				if(this.model.get('indent') == 1) {
					this.model.save({
						indent: 0,
						parent: 0
					});
					Todos.maintainChildrenOfParent(this.model);
				} else if (!Todos.getPreviousTodo(this.model) || this.model.get('indent') == 0) {
					return false;
				} else {
					this.model.save({
						parent: Todos.get(this.model.get('parent')).get('parent'),
						indent: Todos.get(this.model.get('parent')).get('indent')
					});
					Todos.maintainChildrenOfParent(this.model);
				}
				this.edit();
				e.preventDefault();
			}
			/**
				Note for Up and Down keys:
				The '- 2' and none increments are
				due to the collection being kept at index 0
				but the position attribute being kept at index 1
			**/
			// Up key - close current todo and open todo above to edit
			if (e.keyCode == 38) {
				this.editPreviousTodo(e);
			}
			// Down key - close current todo and open todo above to edit
			if (e.keyCode == 40) {
				this.editNextTodo(e);
			}
			// Backspace key
			if (e.keyCode == 8) {
				if (this.input.val() == '') {
					this.editPreviousTodo(e);
					this.deleteTodo();
					e.preventDefault();
				}
			}
        },

		editNextTodo: function(e) {
			var todoToOpen = Todos.getNextTodo(this.model);
			// If we're already at the top then preventDefault()
			if (!todoToOpen) {
				e.preventDefault();
			} else {
				this.close();
				todoToOpen.view.edit();
			}
		},

		editPreviousTodo: function(e) {
			var todoToOpen = Todos.getPreviousTodo(this.model);
			// If we're already at the top then preventDefault()
			if (!todoToOpen) {
				e.preventDefault();
			} else {
				this.close();
				todoToOpen.view.edit();
			}
		},

		toggleDone: function() {
			this.model.done();
		}
    });

    window.TodoApp = Backbone.View.extend({
        el: $("#todoApp"),

        events: {
            //"dblclick"          :       "test",
            "click #createNew"  :       "createNew"
        },

        initialize: function() {
//            Backbone.emulateHTTP = true;
//            Backbone.emulateJSON = true;
            _.bindAll(this, 'createNew', 'createNewAfter', 'createNewBefore', 'addOne', 'addAll');
            Todos.bind('reset', this.addAll);
            //Todos.bind('refresh', this.resetPosition);
			Todos.bind('remove', this.resetPosition);
			Todos.bind('add', this.resetPosition);
            Todos.fetch();
			this.resetPosition();
			if (Todos.length == 0) {
				this.createNew({content: 'Start your first Todo!'});
				Todos.at(0).view.edit();
			}
        },

        createNew: function(o) {
            var todo = Todos.create({
				content: o.content ? o.content : ''
			});
            this.addOne(todo, (_.isString(o.topOrBottom) ? o.topOrBottom : 'bottom')).edit();
        },

		createNewAfter: function(todo) {
            var t = Todos.create({
				content: '',
				parent: todo.get('parent'),
				indent: todo.get('indent'),
				position: (todo.get('position') + 1)
			});
			var view = new TodoView({model:t});
			this.$('#' + todo.id).after(view.render().el);
			view.edit();
        },

		createNewBefore: function(todo) {
            var t = Todos.create({
				content: '',
				parent: todo.get('parent'),
				indent: todo.get('indent'),
				position: todo.get('position')
			});
			var view = new TodoView({model:t});
			this.$('#' + todo.id).before(view.render().el);
			view.edit();
        },

        addOne: function(todo, topOrBottom) {
            var view = new TodoView({model:todo});
			if (topOrBottom == 'top') {
				this.$("#todoItemsList").prepend(view.render().el);
			} else if (topOrBottom == 'bottom') {
				var lastTodoPosition = Todos.last().get("position") + 1;
				todo.set({position: lastTodoPosition});
				Todos.sort({silent: true});
				this.$("#todoItemsList").append(view.render().el);
			} else {
				this.$("#todoItemsList").append(view.render().el);
			}
            return view;
        },

        addAll: function() {
            Todos.each(this.addOne);
        },

        resetPosition: function(todo) {
            var position = -1;
            _.each(Todos.models, function(todo) {
                position++;
                todo.set({ position: position});
                todo.save();
            });
            //Todos.refresh();
        }
    });

    window.forgetmenot = new TodoApp;

});<commit_msg>on first TodoApp initiialization we pass our check for if one todo exists by using the fetch() success callback<commit_after>$(document).ready(function() {

    window.Todo = Backbone.Model.extend({
        //Default Attributes
        defaults: {
            content: "new empty todo",
            parent: 0,
            indent: 0,
            position: 0,
            done: 0
        },

        initialize: function() {
            if (!this.get("content")) {
              this.set({"content": this.defaults.content});
            }
        },

		done: function() {
		    if (this.get("done") == 0) {
		        this.save({done: 1});
		    } else {
		        this.save({done: 0});
		    }

		},

		setParent: function(parentTodo) {
			if(!parentTodo) {
				return false;
			}
			this.save({parent: parentTodo.id});
			return true;
		},

		indentBy: function(number) {
			this.save({indent: (this.get('indent') + (number ? number: 0))});
		}
    });

    window.TodoList = Backbone.Collection.extend({
        model: Todo,
//        localStorage: new Store("forgetmenot"),
		url: "fmn.php?",

        nextPosition: function() {
            if (this.length === 0) {
                return 1;
            } else {
                return this.last().get('position') + 1;
            }
        },

		thoseWithParentOf: function(parentId) {
			return _.select(this.models, function(model) {
				return model.get('parent') == parentId;
			});
		},

		thatAreDone: function() {
			return _.select(this.models, function(model) {
				return model.get('done') == true;
			});
		},

        comparator: function(todo) {
          return todo.get('position');
        },

		// Get's passed current todo
		// Returns parent todo
		// Checking to make sure the
		// correct parent todo is returned
		getParentTodo: function(todo, previousTodo) {
			var previousTodo = this.at(Todos.indexOf(previousTodo ? previousTodo : todo) - 1);
			if (!previousTodo) {
				return false;
			} else if (todo.get('indent') == previousTodo.get('indent')) {
				return (previousTodo ? previousTodo : NaN);
			} else {
				return this.getParentTodo(todo, previousTodo);
			}

		},

		maintainChildrenOfParent: function(todo) {
			var childTodo = this.at(Todos.indexOf(childTodo ? childTodo: todo) + 1);
			if (!childTodo) {
				return false;
			} else {
				if(todo.get('indent') == 0 && todo.get('id') == this.getNextTodo(todo).get('parent')) {
					this.getNextTodo(todo).save({parent: todo.get('id'), indent: 1});
				}
				Todos.each(function(model){
					if (todo.get('id') === model.get('parent')) {
						model.save({indent: (todo.get('indent') + 1)});
					}
				});
				return this.maintainChildrenOfParent(childTodo);
			}

		},

		// Gets passed current todo
		// Sets current todo's parent todo Id correctly
		setParentTodo: function(todo) {
			todo.get('position');
			var previousTodo = this.getPreviousTodo(todo);
			if (previousTodo.get('parent') == '0') {
				todo.setParent(previousTodo);
			}
		},

		// Passed in current todo
		// Returns next todo item
		// Based off of todo's 'position' attribute
		getNextTodo: function(todo) {
			return this.at(Todos.indexOf(todo) + 1);
		},

		// Passed in current todo
		// Returns previous todo item
		// Based off of todo's 'position' attribute
		getPreviousTodo: function(todo) {
			return this.at(Todos.indexOf(todo) - 1);
		}

    });

    window.Todos = new TodoList;

    // Each Individual Todo Item View
    window.TodoView = Backbone.View.extend({
        tagName: "li",
        template: _.template($('#item-template').html()),

        events: {
            "dblclick .display"        :      	"edit",
            "click .display"        :      	"edit",
            "keydown .edit input"              :      	"keyboardActions",
			"click input.done"  			    : 		"toggleDone",
            "click .display .delete"					   : 	    "deleteTodo"
        },

        initialize: function() {
            _.bindAll(this, 'render', 'deleteTodo', 'close','save', 'keyboardActions');
            this.model.bind('change', this.render);
            this.el.id = this.model.get("id");
            // Give reverse access to a model's view by setting its 'this'
            // to a new attribute on the model
            this.model.view = this;
        },

        render: function() {
            $(this.el).html(this.template(this.model.toJSON()));
			this.el.className = 'todo indent-' + this.model.get('indent');
            this.input = this.$(".input");
            this.input.bind('blur', this.close);
            return this;
        },

        edit: function() {
            var content = this.model.get('content');
            $(this.el).addClass("editing");
            this.input.val(content);
            this.input.focus();
            //return false;
        },

        save: function(indent) {
			var $inputVal = this.input.val();
            this.model.save({
				content: ($inputVal == '' ? this.model.defaults.content : $inputVal)
			});
			this.model.indentBy(indent);
        },

		close: function() {
			this.save();
			$(this.el).removeClass("editing");
		},

        deleteTodo: function() {
            this.model.destroy();
            this.remove();
        },

        keyboardActions: function(e) {
			/**
				left arrow		37
				up arrow		38
				right arrow		39
				down arrow		40
			**/
			// Enter button
			if (e.keyCode == 13) {
				if(!Todos.getNextTodo(this.model)) {
					// If last todo then close current
					// and create a new todo
					this.close();
					forgetmenot.createNewAfter(this.model);
				} else if (this.model.get('indent') === Todos.getNextTodo(this.model).get('indent')) {
					// If next todo is at same indent
					// Then edit next todo
					this.editNextTodo(e);
				} else if (this.model.get('indent') > Todos.getNextTodo(this.model).get('indent')) {
					// If next todo is closer to indent '0'
					// Then we create a new todo after current
					forgetmenot.createNewAfter(this.model);
				} else {
				// If not last todo then edit next todo
					this.editNextTodo(e);
				}
			}
			// Tab key - move todo to right one
			// And make sub todo of parent (if it exists)
			if (e.keyCode == 9 && !e.shiftKey) {
				this.save();
				if(this.model.setParent(Todos.getParentTodo(this.model))) {
					this.save(1);
					Todos.maintainChildrenOfParent(this.model);
					this.edit();
				}
				e.preventDefault();
			}
			// Shift + Tab key - move todo to left one
			// And un-sub it from parent (if it exists)
			if (e.shiftKey && e.keyCode == 9) {
				this.save();
				if(this.model.get('indent') == 1) {
					this.model.save({
						indent: 0,
						parent: 0
					});
					Todos.maintainChildrenOfParent(this.model);
				} else if (!Todos.getPreviousTodo(this.model) || this.model.get('indent') == 0) {
					return false;
				} else {
					this.model.save({
						parent: Todos.get(this.model.get('parent')).get('parent'),
						indent: Todos.get(this.model.get('parent')).get('indent')
					});
					Todos.maintainChildrenOfParent(this.model);
				}
				this.edit();
				e.preventDefault();
			}
			/**
				Note for Up and Down keys:
				The '- 2' and none increments are
				due to the collection being kept at index 0
				but the position attribute being kept at index 1
			**/
			// Up key - close current todo and open todo above to edit
			if (e.keyCode == 38) {
				this.editPreviousTodo(e);
			}
			// Down key - close current todo and open todo above to edit
			if (e.keyCode == 40) {
				this.editNextTodo(e);
			}
			// Backspace key
			if (e.keyCode == 8) {
				if (this.input.val() == '') {
					this.editPreviousTodo(e);
					this.deleteTodo();
					e.preventDefault();
				}
			}
        },

		editNextTodo: function(e) {
			var todoToOpen = Todos.getNextTodo(this.model);
			// If we're already at the top then preventDefault()
			if (!todoToOpen) {
				e.preventDefault();
			} else {
				this.close();
				todoToOpen.view.edit();
			}
		},

		editPreviousTodo: function(e) {
			var todoToOpen = Todos.getPreviousTodo(this.model);
			// If we're already at the top then preventDefault()
			if (!todoToOpen) {
				e.preventDefault();
			} else {
				this.close();
				todoToOpen.view.edit();
			}
		},

		toggleDone: function() {
			this.model.done();
		}
    });

    window.TodoApp = Backbone.View.extend({
        el: $("#todoApp"),

        events: {
            //"dblclick"          :       "test",
            "click #createNew"  :       "createNew"
        },

        initialize: function() {
//            Backbone.emulateHTTP = true;
//            Backbone.emulateJSON = true;
            _.bindAll(this, 'createNew', 'createNewAfter', 'createNewBefore', 'addOne', 'addAll');
            Todos.bind('reset', this.addAll);
            //Todos.bind('refresh', this.resetPosition);
			Todos.bind('remove', this.resetPosition);
			Todos.bind('add', this.resetPosition);
            Todos.fetch({success: function(){
				if (Todos.length == 0) {
					forgetmenot.createNew({content: 'Start your first Todo!', topOrBottom: 'top'});
					Todos.at(0).view.edit();
				}
			}});
			this.resetPosition();

        },

        createNew: function(o) {
            var todo = Todos.create({
				content: o.content ? o.content : ''
			});
            this.addOne(todo, (_.isString(o.topOrBottom) ? o.topOrBottom : 'bottom')).edit();
        },

		createNewAfter: function(todo) {
            var t = Todos.create({
				content: '',
				parent: todo.get('parent'),
				indent: todo.get('indent'),
				position: (todo.get('position') + 1)
			});
			var view = new TodoView({model:t});
			this.$('#' + todo.id).after(view.render().el);
			view.edit();
        },

		createNewBefore: function(todo) {
            var t = Todos.create({
				content: '',
				parent: todo.get('parent'),
				indent: todo.get('indent'),
				position: todo.get('position')
			});
			var view = new TodoView({model:t});
			this.$('#' + todo.id).before(view.render().el);
			view.edit();
        },

        addOne: function(todo, topOrBottom) {
            var view = new TodoView({model:todo});
			if (topOrBottom == 'top') {
				this.$("#todoItemsList").prepend(view.render().el);
			} else if (topOrBottom == 'bottom') {
				var lastTodoPosition = Todos.last().get("position") + 1;
				todo.set({position: lastTodoPosition});
				Todos.sort({silent: true});
				this.$("#todoItemsList").append(view.render().el);
			} else {
				this.$("#todoItemsList").append(view.render().el);
			}
            return view;
        },

        addAll: function() {
            Todos.each(this.addOne);
        },

        resetPosition: function(todo) {
            var position = -1;
            _.each(Todos.models, function(todo) {
                position++;
                todo.set({ position: position});
                todo.save();
            });
            //Todos.refresh();
        }
    });

    window.forgetmenot = new TodoApp;

});<|endoftext|>


<commit_before>var pull = require('pull-stream')
var many = require('pull-many')
var cat  = require('pull-cat')
var u    = require('./util')
var codec = require('./codec')
var pvstruct = require('pull-varstruct')

module.exports = function (sbs, opts, cb) {
  if('function' === typeof opts)
    cb = opts, opts = {}

  opts = opts || {}

  var source = many()

  //source: stream {id: hash(pubkey), sequence: latest}
  //pairs, then {okay: true} to show you are at the end.
  source.add(cat([sbs.latest(), pull.once({okay: true})]))

  //sink: filter out metadata, and write the actual data.
  var sink = pull(
    pull.filter(function (data) {
      if(data.author) return true
      else if(u.isHash(data.id) && u.isInteger(data.sequence)) {
        source.add(sbs.createHistoryStream(data.id, data.sequence, opts.live))
      }
      else if(data && data.okay === true)
        source.cap()
    }),
    sbs.createWriteStream(cb)
  )

  return {
    source: pull(source, pvstruct.encode(codec)),
    sink: pull(pvstruct.decode(codec), sink)
  }
}
<commit_msg>fixed off-by-one error in replication history stream creation<commit_after>var pull = require('pull-stream')
var many = require('pull-many')
var cat  = require('pull-cat')
var u    = require('./util')
var codec = require('./codec')
var pvstruct = require('pull-varstruct')

module.exports = function (sbs, opts, cb) {
  if('function' === typeof opts)
    cb = opts, opts = {}

  opts = opts || {}

  var source = many()

  //source: stream {id: hash(pubkey), sequence: latest}
  //pairs, then {okay: true} to show you are at the end.
  source.add(cat([sbs.latest(), pull.once({okay: true})]))

  //sink: filter out metadata, and write the actual data.
  var sink = pull(
    pull.filter(function (data) {
      if(data.author) return true
      else if(u.isHash(data.id) && u.isInteger(data.sequence)) {
        source.add(sbs.createHistoryStream(data.id, data.sequence + 1, opts.live))
      }
      else if(data && data.okay === true)
        source.cap()
    }),
    sbs.createWriteStream(cb)
  )

  return {
    source: pull(source, pvstruct.encode(codec)),
    sink: pull(pvstruct.decode(codec), sink)
  }
}
<|endoftext|>


<commit_before>'use strict';

function check_neighbor_match(position){
    var match = false;
    var next_positions = [
      [-23, -40,],
      [-46, 0,],
      [-23, 40,],
      [23, -40,],
      [46, 0,],
      [23, 40,],
    ];
    next_position_loop:
    for(var next_position in next_positions){
        if(position['y'] % 80){
            next_positions[next_position][0] += 46;
        }

        var new_next_position = select_hexagon(
          select_y_mod(
            position['x'] + next_positions[next_position][0],
            position['y'] + next_positions[next_position][1]
          ),
          position['y'] + next_positions[next_position][1]
        );
        for(var hexagon in hexagons){
            if(hexagons[hexagon]['x'] === new_next_position['x']
              && hexagons[hexagon]['y'] === new_next_position['y']
              && hexagons[hexagon]['color'] === players[player_ids[turn]]['color']){
                match = true;
                break next_position_loop;
            }
        }
    }

    return match;
}

function conquer_hexagon(hexagon){
    if(hexagons[hexagon]['color'] !== players[player_ids[turn]]['color']){
        if(hexagons[hexagon]['color'] === settings_settings['default-color']
          || random_boolean()){
            var old_color = hexagons[hexagon]['color'];
            hexagons[hexagon]['color'] = players[player_ids[turn]]['color'];
            players[player_ids[turn]]['hexagons'] += 1;
            for(var player in players){
                if(old_color === players[player]['color']){
                    lose_hexagon(player);
                    break;
                }
            }
        }
    }
}

function create_hexagon(position, size){
    // Only create a hexagon if one doesn't already exist at this x,y.
    for(var hexagon in hexagons){
        if(hexagons[hexagon]['x'] === position['x']
          && hexagons[hexagon]['y'] === position['y']){
            return;
        }
    }

    hexagons.push({
      'color': settings_settings['default-color'],
      'size': size,
      'x': position['x'],
      'y': position['y'],
    });
}

function create_player(properties){
    properties = properties || {};
    properties = {
      'ai': properties['ai'] || false,
      'color': properties['color'] || random_hex(),
      'hexagons': 1,
      'name': '',
    };

    var hexagon = random_integer(hexagons.length);
    while(hexagons[hexagon]['color'] !== settings_settings['default-color']){
        hexagon = random_integer(hexagons.length);
    }
    hexagons[hexagon]['color'] = properties['color'];

    player_ids.push(player_count);
    properties['name'] = (properties['ai']
      ? 'AI'
      : 'P')
      + player_count;
    players[player_count] = properties;
    player_count += 1;
}

function draw_hexagon(x, y, size, color){
    if(y % 80){
        x += 23;
    }

    var vertices = [];
    for(var i = 0; i < 6; i++){
        var angle = math_degrees_to_radians({
          'degrees': 30 + i * 60,
        });
        vertices.push({
          'type': i === 0
            ? 'moveTo'
            : 'lineTo',
          'x': x + Math.cos(angle) * size + canvas_x,
          'y': y + Math.sin(angle) * size + canvas_y,
        });
    }
    canvas_draw_path(
      vertices,
      {
        'fillStyle': color,
      }
    );
}

function draw_logic(){
    // Save the current buffer state.
    canvas_buffer.save();

    // Translate to camera position.
    canvas_buffer.translate(
      camera['x'],
      camera['y']
    );

    // Draw selection if not AI turn.
    if(!players[player_ids[turn]]['ai']){
        draw_hexagon(
          mouse_x,
          mouse_y,
          30,
          players[player_ids[turn]]['color']
        );
    }

    // Draw hexagons.
    for(var hexagon in hexagons){
        draw_hexagon(
          hexagons[hexagon]['x'],
          hexagons[hexagon]['y'],
          hexagons[hexagon]['size'],
          hexagons[hexagon]['color']
        );
    }

    // Restore the buffer state.
    canvas_buffer.restore();

    // Draw turn.
    canvas_buffer.fillStyle = '#fff';
    canvas_buffer.fillText(
      'Turn: ' + players[player_ids[turn]]['name'],
      0,
      25
    );

    if(player_count === 1){
        canvas_buffer.fillText(
          players[player_ids[turn]]['name'] + ' wins!',
          0,
          75
        );
    }

    var x = 50;
    for(var player in scoreboard){
        if(!players[scoreboard[player]['id']]){
            continue;
        }

        canvas_buffer.fillStyle = players[scoreboard[player]['id']]['color'];
        canvas_buffer.fillText(
          players[scoreboard[player]['id']]['name']  + ': ' + scoreboard[player]['hexagons'],
          0,
          x
        );
        x += 25;
    }
}

function end_turn(){
    if(turn >= player_ids.length - 1){
        turn = 0;

    }else{
        turn += 1;
    }

    if(!players[player_ids[turn]]){
        end_turn();
    }
}

function handle_ai_turn(){
    if(!players[player_ids[turn]]['ai']){
        return;
    }

    var options = [];
    for(var hexagon in hexagons){
        if(hexagons[hexagon]['color'] === players[player_ids[turn]]['color']){
            continue;
        }

        if(check_neighbor_match({
          'x': hexagons[hexagon]['x'],
          'y': hexagons[hexagon]['y'],
        })){
            if(hexagons[hexagon]['color'] === settings_settings['default-color']){
                conquer_hexagon(hexagon);
                break;

            }else{
                options.push(hexagon);
            }
        }
    }
    if(options.length > 0){
        options.sort(function(a, b){
            return Math.random();
        });
        var loop_counter = scoreboard.length - 1;
        scoreboard_loop:
        do{
            if(!players[scoreboard[loop_counter]['id']]){
                continue;
            }

            for(var option in options){
                if(hexagons[options[option]]['color'] === players[scoreboard[loop_counter]['id']]['color']){
                    conquer_hexagon(options[option]);
                    break scoreboard_loop;
                }
            }
        }while(loop_counter--);
    }

    end_turn();
}

function logic(){
    if(canvas_menu){
        return;
    }

    // Move camera down.
    if(key_down){
        camera['y'] -= settings_settings['scroll-speed'];
    }

    // Move camera left.
    if(key_left){
        camera['x'] += settings_settings['scroll-speed'];
    }

    // Move camera right.
    if(key_right){
        camera['x'] -= settings_settings['scroll-speed'];
    }

    // Move camera up.
    if(key_up){
        camera['y'] += settings_settings['scroll-speed'];
    }

    handle_ai_turn();
    update_scoreboard();
}

function lose_hexagon(player){
    players[player]['hexagons'] -= 1;
    if(players[player]['hexagons'] <= 0){
        delete players[player];
        player_count -= 1;
    }
}

function select_hexagon(x, y){
    return {
      'x': Math.ceil((x - 23) / 46) * 46,
      'y': Math.ceil((y - 20) / 40) * 40,
    };
}

function select_y_mod(x, y, move){
    move = move || -23;

    var y_mod = Math.abs(y % 80);
    if(y_mod > 20
      && y_mod < 60){
        x += move;
    }

    return x;
}

function setmode_logic(newgame){
    camera = {
      'x': 0,
      'y': 0,
    };
    hexagons = [];
    player_count = 0;
    player_ids = [];
    players = {};
    scoreboard = [];
    turn = 0;

    // Main menu mode.
    if(canvas_mode === 0){
        document.body.innerHTML = '

New Game

'
          + '

<input id=camera-keys maxlength=4>Camera ↑←↓→
'
          + '<input id=delete-player>Delete Player
'
          + '<input id=end-turn-key>End Turn
'
          + '<input disabled value=ESC>Menu

'
          + '
<input id=ai>AI
'
          + '<input id=default-color type=color>Default Color
'
          + '<input id=height>Height
'
          + '<input id=hexagons>Hexagons
'
          + '<input id=players>Players
'
          + '<input id=scroll-speed>Scroll Speed
'
          + '<input id=width>Width
'
          + 'Reset Settings

';
        settings_update();

    // New game mode.
    }else if(newgame){
        settings_save();

        key_down = false;
        key_left = false;
        key_right = false;
        key_up = false;
    }
}

function update_scoreboard(){
    scoreboard = [];
    for(var player in players){
        scoreboard.push({
          'hexagons': players[player]['hexagons'],
          'id': player,
        });
    }
    scoreboard.sort(function(a, b){
        if(a['hexagons'] > b['hexagons']){
            return -1;
        }
        if(a['hexagons'] < b['hexagons']){
            return 1;
        }
        return 0;
    });
}

var camera = {};
var hexagons = [];
var key_down = false;
var key_left = false;
var key_right = false;
var key_up = false;
var mouse_x = 0;
var mouse_y = 0;
var player_count = 0;
var player_ids = [];
var players = {};
var scoreboard = [];
var turn = 0;

window.onload = function(){
    settings_init(
      'Hexagons-2D.htm-',
      {
        'ai': 4,
        'camera-keys': 'WASD',
        'default-color': '#ffffff',
        'delete-player': 'P',
        'end-turn-key': 'H',
        'height': 500,
        'hexagons': 100,
        'players': 1,
        'scroll-speed': 5,
        'width': 500,
      }
    );
    canvas_init();

    window.onkeydown = function(e){
        if(canvas_mode <= 0){
            return;
        }

        var key = e.keyCode || e.which;

        // ESC: return to main menu.
        if(key === 27){
            canvas_menu_toggle();
            return;
        }

        key = String.fromCharCode(key);

        if(key === settings_settings['camera-keys'][1]){
            key_left = true;

        }else if(key === settings_settings['camera-keys'][3]){
            key_right = true;

        }else if(key === settings_settings['camera-keys'][2]){
            key_down = true;

        }else if(key === settings_settings['camera-keys'][0]){
            key_up = true;

        }else if(key === 'Q'){
            canvas_menu_quit();

        }else if(!players[player_ids[turn]]['ai']){
            if(key === settings_settings['end-turn-key']){
                end_turn();

            }else if(key === 'P'){
                if(player_count > 1){
                    for(var hexagon in hexagons){
                        if(!players[player_ids[turn]]){
                            break;
                        }
                        if(hexagons[hexagon]['color'] === players[player_ids[turn]]['color']){
                            hexagons[hexagon]['color'] = settings_settings['default-color'];
                            lose_hexagon(player_ids[turn]);
                        }
                    }
                    end_turn();
                }
            }
        }
    };

    window.onkeyup = function(e){
        var key = String.fromCharCode(e.keyCode || e.which);

        if(key === settings_settings['camera-keys'][1]){
            key_left = false;

        }else if(key === settings_settings['camera-keys'][3]){
            key_right = false;

        }else if(key === settings_settings['camera-keys'][2]){
            key_down = false;

        }else if(key === settings_settings['camera-keys'][0]){
            key_up = false;
        }
    };

    window.onmousedown = function(e){
        if(canvas_mode <= 0
          || players[player_ids[turn]]['ai']){
            return;
        }

        var position = select_hexagon(
          mouse_x,
          mouse_y
        );

        // Check if a hexagon exists at this location
        //   with a different color.
        var target = false;
        for(var hexagon in hexagons){
            if(hexagons[hexagon]['x'] === position['x']
              && hexagons[hexagon]['y'] === position['y']
              && hexagons[hexagon]['color'] !== players[player_ids[turn]]['color']){
                target = hexagon;
                break;
            }
        }
        if(target === false){
            return;
        }

        // Check if current player has a hexagon next to target hexagon.
        if(!check_neighbor_match({
          'x': hexagons[target]['x'],
          'y': hexagons[target]['y'],
        })){
            return;
        }

        // Attempt to conquer the hexagon.
        conquer_hexagon(hexagon);

        end_turn();
    };

    window.onmousemove = function(e){
        var x = e.pageX - canvas_x - camera['x'];
        var y = e.pageY - canvas_y - camera['y'];
        var position = select_hexagon(
          select_y_mod(
            x,
            y
          ),
          y
        );
        mouse_x = position['x'];
        mouse_y = position['y'];
    };
};
<commit_msg>added turn counter<commit_after>'use strict';

function check_neighbor_match(position){
    var match = false;
    var next_positions = [
      [-23, -40,],
      [-46, 0,],
      [-23, 40,],
      [23, -40,],
      [46, 0,],
      [23, 40,],
    ];
    next_position_loop:
    for(var next_position in next_positions){
        if(position['y'] % 80){
            next_positions[next_position][0] += 46;
        }

        var new_next_position = select_hexagon(
          select_y_mod(
            position['x'] + next_positions[next_position][0],
            position['y'] + next_positions[next_position][1]
          ),
          position['y'] + next_positions[next_position][1]
        );
        for(var hexagon in hexagons){
            if(hexagons[hexagon]['x'] === new_next_position['x']
              && hexagons[hexagon]['y'] === new_next_position['y']
              && hexagons[hexagon]['color'] === players[player_ids[turn]]['color']){
                match = true;
                break next_position_loop;
            }
        }
    }

    return match;
}

function conquer_hexagon(hexagon){
    if(hexagons[hexagon]['color'] !== players[player_ids[turn]]['color']){
        if(hexagons[hexagon]['color'] === settings_settings['default-color']
          || random_boolean()){
            var old_color = hexagons[hexagon]['color'];
            hexagons[hexagon]['color'] = players[player_ids[turn]]['color'];
            players[player_ids[turn]]['hexagons'] += 1;
            for(var player in players){
                if(old_color === players[player]['color']){
                    lose_hexagon(player);
                    break;
                }
            }
        }
    }
}

function create_hexagon(position, size){
    // Only create a hexagon if one doesn't already exist at this x,y.
    for(var hexagon in hexagons){
        if(hexagons[hexagon]['x'] === position['x']
          && hexagons[hexagon]['y'] === position['y']){
            return;
        }
    }

    hexagons.push({
      'color': settings_settings['default-color'],
      'size': size,
      'x': position['x'],
      'y': position['y'],
    });
}

function create_player(properties){
    properties = properties || {};
    properties = {
      'ai': properties['ai'] || false,
      'color': properties['color'] || random_hex(),
      'hexagons': 1,
      'name': '',
    };

    var hexagon = random_integer(hexagons.length);
    while(hexagons[hexagon]['color'] !== settings_settings['default-color']){
        hexagon = random_integer(hexagons.length);
    }
    hexagons[hexagon]['color'] = properties['color'];

    player_ids.push(player_count);
    properties['name'] = (properties['ai']
      ? 'AI'
      : 'P')
      + player_count;
    players[player_count] = properties;
    player_count += 1;
}

function draw_hexagon(x, y, size, color){
    if(y % 80){
        x += 23;
    }

    var vertices = [];
    for(var i = 0; i < 6; i++){
        var angle = math_degrees_to_radians({
          'degrees': 30 + i * 60,
        });
        vertices.push({
          'type': i === 0
            ? 'moveTo'
            : 'lineTo',
          'x': x + Math.cos(angle) * size + canvas_x,
          'y': y + Math.sin(angle) * size + canvas_y,
        });
    }
    canvas_draw_path(
      vertices,
      {
        'fillStyle': color,
      }
    );
}

function draw_logic(){
    // Save the current buffer state.
    canvas_buffer.save();

    // Translate to camera position.
    canvas_buffer.translate(
      camera['x'],
      camera['y']
    );

    // Draw selection if not AI turn.
    if(!players[player_ids[turn]]['ai']){
        draw_hexagon(
          mouse_x,
          mouse_y,
          30,
          players[player_ids[turn]]['color']
        );
    }

    // Draw hexagons.
    for(var hexagon in hexagons){
        draw_hexagon(
          hexagons[hexagon]['x'],
          hexagons[hexagon]['y'],
          hexagons[hexagon]['size'],
          hexagons[hexagon]['color']
        );
    }

    // Restore the buffer state.
    canvas_buffer.restore();

    // Draw turns.
    canvas_buffer.fillStyle = '#fff';
    canvas_buffer.fillText(
      'Turns: ' + turns,
      0,
      25
    );
    canvas_buffer.fillText(
      'Turn: ' + players[player_ids[turn]]['name'],
      0,
      50
    );

    if(player_count === 1){
        canvas_buffer.fillText(
          players[player_ids[turn]]['name'] + ' wins!',
          0,
          100
        );
    }

    var x = 75;
    for(var player in scoreboard){
        if(!players[scoreboard[player]['id']]){
            continue;
        }

        canvas_buffer.fillStyle = players[scoreboard[player]['id']]['color'];
        canvas_buffer.fillText(
          players[scoreboard[player]['id']]['name']  + ': ' + scoreboard[player]['hexagons'],
          0,
          x
        );
        x += 25;
    }
}

function end_turn(){
    if(turn >= player_ids.length - 1){
        turn = 0;

    }else{
        turn += 1;
    }

    turns += 1;

    if(!players[player_ids[turn]]){
        end_turn();
    }
}

function handle_ai_turn(){
    if(!players[player_ids[turn]]['ai']){
        return;
    }

    var options = [];
    for(var hexagon in hexagons){
        if(hexagons[hexagon]['color'] === players[player_ids[turn]]['color']){
            continue;
        }

        if(check_neighbor_match({
          'x': hexagons[hexagon]['x'],
          'y': hexagons[hexagon]['y'],
        })){
            if(hexagons[hexagon]['color'] === settings_settings['default-color']){
                conquer_hexagon(hexagon);
                break;

            }else{
                options.push(hexagon);
            }
        }
    }
    if(options.length > 0){
        options.sort(function(a, b){
            return Math.random();
        });
        var loop_counter = scoreboard.length - 1;
        scoreboard_loop:
        do{
            if(!players[scoreboard[loop_counter]['id']]){
                continue;
            }

            for(var option in options){
                if(hexagons[options[option]]['color'] === players[scoreboard[loop_counter]['id']]['color']){
                    conquer_hexagon(options[option]);
                    break scoreboard_loop;
                }
            }
        }while(loop_counter--);
    }

    end_turn();
}

function logic(){
    if(canvas_menu){
        return;
    }

    // Move camera down.
    if(key_down){
        camera['y'] -= settings_settings['scroll-speed'];
    }

    // Move camera left.
    if(key_left){
        camera['x'] += settings_settings['scroll-speed'];
    }

    // Move camera right.
    if(key_right){
        camera['x'] -= settings_settings['scroll-speed'];
    }

    // Move camera up.
    if(key_up){
        camera['y'] += settings_settings['scroll-speed'];
    }

    handle_ai_turn();
    update_scoreboard();
}

function lose_hexagon(player){
    players[player]['hexagons'] -= 1;
    if(players[player]['hexagons'] <= 0){
        delete players[player];
        player_count -= 1;
    }
}

function select_hexagon(x, y){
    return {
      'x': Math.ceil((x - 23) / 46) * 46,
      'y': Math.ceil((y - 20) / 40) * 40,
    };
}

function select_y_mod(x, y, move){
    move = move || -23;

    var y_mod = Math.abs(y % 80);
    if(y_mod > 20
      && y_mod < 60){
        x += move;
    }

    return x;
}

function setmode_logic(newgame){
    camera = {
      'x': 0,
      'y': 0,
    };
    hexagons = [];
    player_count = 0;
    player_ids = [];
    players = {};
    scoreboard = [];
    turn = 0;
    turns = 0;

    // Main menu mode.
    if(canvas_mode === 0){
        document.body.innerHTML = '

New Game

'
          + '

<input id=camera-keys maxlength=4>Camera ↑←↓→
'
          + '<input id=delete-player>Delete Player
'
          + '<input id=end-turn-key>End Turn
'
          + '<input disabled value=ESC>Menu

'
          + '
<input id=ai>AI
'
          + '<input id=default-color type=color>Default Color
'
          + '<input id=height>Height
'
          + '<input id=hexagons>Hexagons
'
          + '<input id=players>Players
'
          + '<input id=scroll-speed>Scroll Speed
'
          + '<input id=width>Width
'
          + 'Reset Settings

';
        settings_update();

    // New game mode.
    }else if(newgame){
        settings_save();

        key_down = false;
        key_left = false;
        key_right = false;
        key_up = false;
    }
}

function update_scoreboard(){
    scoreboard = [];
    for(var player in players){
        scoreboard.push({
          'hexagons': players[player]['hexagons'],
          'id': player,
        });
    }
    scoreboard.sort(function(a, b){
        if(a['hexagons'] > b['hexagons']){
            return -1;
        }
        if(a['hexagons'] < b['hexagons']){
            return 1;
        }
        return 0;
    });
}

var camera = {};
var hexagons = [];
var key_down = false;
var key_left = false;
var key_right = false;
var key_up = false;
var mouse_x = 0;
var mouse_y = 0;
var player_count = 0;
var player_ids = [];
var players = {};
var scoreboard = [];
var turn = 0;
var turns = 0;

window.onload = function(){
    settings_init(
      'Hexagons-2D.htm-',
      {
        'ai': 4,
        'camera-keys': 'WASD',
        'default-color': '#ffffff',
        'delete-player': 'P',
        'end-turn-key': 'H',
        'height': 500,
        'hexagons': 100,
        'players': 1,
        'scroll-speed': 5,
        'width': 500,
      }
    );
    canvas_init();

    window.onkeydown = function(e){
        if(canvas_mode <= 0){
            return;
        }

        var key = e.keyCode || e.which;

        // ESC: return to main menu.
        if(key === 27){
            canvas_menu_toggle();
            return;
        }

        key = String.fromCharCode(key);

        if(key === settings_settings['camera-keys'][1]){
            key_left = true;

        }else if(key === settings_settings['camera-keys'][3]){
            key_right = true;

        }else if(key === settings_settings['camera-keys'][2]){
            key_down = true;

        }else if(key === settings_settings['camera-keys'][0]){
            key_up = true;

        }else if(key === 'Q'){
            canvas_menu_quit();

        }else if(!players[player_ids[turn]]['ai']){
            if(key === settings_settings['end-turn-key']){
                end_turn();

            }else if(key === 'P'){
                if(player_count > 1){
                    for(var hexagon in hexagons){
                        if(!players[player_ids[turn]]){
                            break;
                        }
                        if(hexagons[hexagon]['color'] === players[player_ids[turn]]['color']){
                            hexagons[hexagon]['color'] = settings_settings['default-color'];
                            lose_hexagon(player_ids[turn]);
                        }
                    }
                    end_turn();
                }
            }
        }
    };

    window.onkeyup = function(e){
        var key = String.fromCharCode(e.keyCode || e.which);

        if(key === settings_settings['camera-keys'][1]){
            key_left = false;

        }else if(key === settings_settings['camera-keys'][3]){
            key_right = false;

        }else if(key === settings_settings['camera-keys'][2]){
            key_down = false;

        }else if(key === settings_settings['camera-keys'][0]){
            key_up = false;
        }
    };

    window.onmousedown = function(e){
        if(canvas_mode <= 0
          || players[player_ids[turn]]['ai']){
            return;
        }

        var position = select_hexagon(
          mouse_x,
          mouse_y
        );

        // Check if a hexagon exists at this location
        //   with a different color.
        var target = false;
        for(var hexagon in hexagons){
            if(hexagons[hexagon]['x'] === position['x']
              && hexagons[hexagon]['y'] === position['y']
              && hexagons[hexagon]['color'] !== players[player_ids[turn]]['color']){
                target = hexagon;
                break;
            }
        }
        if(target === false){
            return;
        }

        // Check if current player has a hexagon next to target hexagon.
        if(!check_neighbor_match({
          'x': hexagons[target]['x'],
          'y': hexagons[target]['y'],
        })){
            return;
        }

        // Attempt to conquer the hexagon.
        conquer_hexagon(hexagon);

        end_turn();
    };

    window.onmousemove = function(e){
        var x = e.pageX - canvas_x - camera['x'];
        var y = e.pageY - canvas_y - camera['y'];
        var position = select_hexagon(
          select_y_mod(
            x,
            y
          ),
          y
        );
        mouse_x = position['x'];
        mouse_y = position['y'];
    };
};
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

lib.rtdep('lib.f');

/**
 * First draft of the interface between the terminal and a third party dialog.
 *
 * This is rough.  It's just the terminal->dialog layer.  To complete things
 * we'll also need a command->terminal layer.  That will have to facilitate
 * command->terminal->dialog or direct command->dialog communication.
 *
 * I imagine this class will change significantly when that happens.
 */

/**
 * Construct a new frame for the given terminal.
 *
 * @param terminal {hterm.Terminal} The parent terminal object.
 * @param url {String} The url to load in the frame.
 * @param opt_options {Object} Optional options for the frame.  Not implemented.
 */
hterm.Frame = function(terminal, url, opt_options) {
  this.terminal_ = terminal;
  this.div_ = terminal.div_;
  this.url = url;
  this.options = opt_options || {};
  this.iframe_ = null;
  this.container_ = null;
  this.messageChannel_ = null;
};

/**
 * Handle messages from the iframe.
 */
hterm.Frame.prototype.onMessage_ = function(e) {
  if (e.data.name != 'ipc-init-ok') {
    console.log('Unknown message from frame:', e.data);
    return;
  }

  this.sendTerminalInfo_();
  this.messageChannel_.port1.onmessage = this.onMessage.bind(this);
  this.onLoad();
};

/**
 * Clients could override this, I guess.
 *
 * It doesn't support multiple listeners, but I'm not sure that would make sense
 * here.  It's probably better to speak directly to our parents.
 */
hterm.Frame.prototype.onMessage = function() {};

/**
 * Handle iframe onLoad event.
 */
hterm.Frame.prototype.onLoad_ = function() {
  this.messageChannel_ = new MessageChannel();
  this.messageChannel_.port1.onmessage = this.onMessage_.bind(this);
  this.messageChannel_.port1.start();
  this.iframe_.contentWindow.postMessage(
      {name: 'ipc-init', argv: [{messagePort: this.messageChannel_.port2}]},
      [this.messageChannel_.port2], this.url);
};

/**
 * Clients may override this.
 */
hterm.Frame.prototype.onLoad = function() {};

/**
 * Sends the terminal-info message to the iframe.
 */
hterm.Frame.prototype.sendTerminalInfo_ = function() {
  lib.f.getAcceptLanguages(function(languages) {
      this.postMessage('terminal-info', [{
         acceptLanguages: languages,
         foregroundColor: this.terminal_.getForegroundColor(),
         backgroundColor: this.terminal_.getBackgroundColor(),
         cursorColor: this.terminal_.getCursorColor(),
         fontSize: this.terminal_.getFontSize(),
         fontFamily: this.terminal_.getFontFamily(),
         baseURL: lib.f.getURL('/')
          }]
        );
    }.bind(this));
};

/**
 * User clicked the close button on the frame decoration.
 */
hterm.Frame.prototype.onCloseClicked_ = function() {
  this.close();
};

/**
 * Close this frame.
 */
hterm.Frame.prototype.close = function() {
  if (!this.container_ || !this.container_.parentNode)
      return;

  this.container_.parentNode.removeChild(this.container_);
  this.onClose();
};

/**
 * Clients may override this.
 */
hterm.Frame.prototype.onClose = function() {};

/**
 * Send a message to the iframe.
 */
hterm.Frame.prototype.postMessage = function(name, argv) {
  if (!this.messageChannel_)
    throw new Error('Message channel is not set up.');

  this.messageChannel_.port1.postMessage({name: name, argv: argv});
};

/**
 * Show the UI for this frame.
 *
 * The iframe src is not loaded until this method is called.
 */
hterm.Frame.prototype.show = function() {
  var self = this;

  function opt(name, defaultValue) {
    if (name in self.options)
      return self.options[name];

    return defaultValue;
  }

  var self = this;

  if (this.container_ && this.container_.parentNode) {
    console.error('Frame already visible');
    return;
  }

  var headerHeight = '16px';

  var divSize = hterm.getClientSize(this.div_);

  var width = opt('width', 640);
  var height = opt('height', 480);
  var left = (divSize.width - width) / 2;
  var top = (divSize.height - height) / 2;

  var document = this.terminal_.document_;

  var container = this.container_ = document.createElement('div');
  container.style.cssText = (
      'position: absolute;' +
      'display: -webkit-flex;' +
      '-webkit-flex-direction: column;' +
      'top: 10%;' +
      'left: 4%;' +
      'width: 90%;' +
      'height: 80%;' +
      'box-shadow: 0 0 2px ' + this.terminal_.getForegroundColor() + ';' +
      'border: 2px ' + this.terminal_.getForegroundColor() + ' solid;');

  var header = document.createElement('div');
  header.style.cssText = (
      'display: -webkit-flex;' +
      '-webkit-justify-content: flex-end;' +
      'height: ' + headerHeight + ';' +
      'background-color: ' + this.terminal_.getForegroundColor() + ';' +
      'color: ' + this.terminal_.getBackgroundColor() + ';' +
      'font-size: 16px;' +
      'font-family: ' + this.terminal_.getFontFamily());
  container.appendChild(header);

  if (false) {
    // No use for the close button.
    var button = document.createElement('div');
    button.setAttribute('role', 'button');
    button.style.cssText = (
        'margin-top: -3px;' +
        'margin-right: 3px;' +
        'cursor: pointer;');
    button.textContent = '\u2a2f';
    button.addEventListener('click', this.onCloseClicked_.bind(this));
    header.appendChild(button);
  }

  var iframe = this.iframe_ = document.createElement('iframe');
  iframe.onload = this.onLoad_.bind(this);
  iframe.style.cssText = (
      'display: -webkit-flex;' +
      '-webkit-flex: 1;' +
      'width: 100%');
  iframe.setAttribute('src', this.url);
  iframe.setAttribute('seamless', true);
  container.appendChild(iframe);

  this.div_.appendChild(container);
};
<commit_msg>hterm: fix order of parameters to postMessage<commit_after>// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

lib.rtdep('lib.f');

/**
 * First draft of the interface between the terminal and a third party dialog.
 *
 * This is rough.  It's just the terminal->dialog layer.  To complete things
 * we'll also need a command->terminal layer.  That will have to facilitate
 * command->terminal->dialog or direct command->dialog communication.
 *
 * I imagine this class will change significantly when that happens.
 */

/**
 * Construct a new frame for the given terminal.
 *
 * @param terminal {hterm.Terminal} The parent terminal object.
 * @param url {String} The url to load in the frame.
 * @param opt_options {Object} Optional options for the frame.  Not implemented.
 */
hterm.Frame = function(terminal, url, opt_options) {
  this.terminal_ = terminal;
  this.div_ = terminal.div_;
  this.url = url;
  this.options = opt_options || {};
  this.iframe_ = null;
  this.container_ = null;
  this.messageChannel_ = null;
};

/**
 * Handle messages from the iframe.
 */
hterm.Frame.prototype.onMessage_ = function(e) {
  if (e.data.name != 'ipc-init-ok') {
    console.log('Unknown message from frame:', e.data);
    return;
  }

  this.sendTerminalInfo_();
  this.messageChannel_.port1.onmessage = this.onMessage.bind(this);
  this.onLoad();
};

/**
 * Clients could override this, I guess.
 *
 * It doesn't support multiple listeners, but I'm not sure that would make sense
 * here.  It's probably better to speak directly to our parents.
 */
hterm.Frame.prototype.onMessage = function() {};

/**
 * Handle iframe onLoad event.
 */
hterm.Frame.prototype.onLoad_ = function() {
  this.messageChannel_ = new MessageChannel();
  this.messageChannel_.port1.onmessage = this.onMessage_.bind(this);
  this.messageChannel_.port1.start();
  this.iframe_.contentWindow.postMessage(
      {name: 'ipc-init', argv: [{messagePort: this.messageChannel_.port2}]},
      this.url, [this.messageChannel_.port2]);
};

/**
 * Clients may override this.
 */
hterm.Frame.prototype.onLoad = function() {};

/**
 * Sends the terminal-info message to the iframe.
 */
hterm.Frame.prototype.sendTerminalInfo_ = function() {
  lib.f.getAcceptLanguages(function(languages) {
      this.postMessage('terminal-info', [{
         acceptLanguages: languages,
         foregroundColor: this.terminal_.getForegroundColor(),
         backgroundColor: this.terminal_.getBackgroundColor(),
         cursorColor: this.terminal_.getCursorColor(),
         fontSize: this.terminal_.getFontSize(),
         fontFamily: this.terminal_.getFontFamily(),
         baseURL: lib.f.getURL('/')
          }]
        );
    }.bind(this));
};

/**
 * User clicked the close button on the frame decoration.
 */
hterm.Frame.prototype.onCloseClicked_ = function() {
  this.close();
};

/**
 * Close this frame.
 */
hterm.Frame.prototype.close = function() {
  if (!this.container_ || !this.container_.parentNode)
      return;

  this.container_.parentNode.removeChild(this.container_);
  this.onClose();
};

/**
 * Clients may override this.
 */
hterm.Frame.prototype.onClose = function() {};

/**
 * Send a message to the iframe.
 */
hterm.Frame.prototype.postMessage = function(name, argv) {
  if (!this.messageChannel_)
    throw new Error('Message channel is not set up.');

  this.messageChannel_.port1.postMessage({name: name, argv: argv});
};

/**
 * Show the UI for this frame.
 *
 * The iframe src is not loaded until this method is called.
 */
hterm.Frame.prototype.show = function() {
  var self = this;

  function opt(name, defaultValue) {
    if (name in self.options)
      return self.options[name];

    return defaultValue;
  }

  var self = this;

  if (this.container_ && this.container_.parentNode) {
    console.error('Frame already visible');
    return;
  }

  var headerHeight = '16px';

  var divSize = hterm.getClientSize(this.div_);

  var width = opt('width', 640);
  var height = opt('height', 480);
  var left = (divSize.width - width) / 2;
  var top = (divSize.height - height) / 2;

  var document = this.terminal_.document_;

  var container = this.container_ = document.createElement('div');
  container.style.cssText = (
      'position: absolute;' +
      'display: -webkit-flex;' +
      '-webkit-flex-direction: column;' +
      'top: 10%;' +
      'left: 4%;' +
      'width: 90%;' +
      'height: 80%;' +
      'box-shadow: 0 0 2px ' + this.terminal_.getForegroundColor() + ';' +
      'border: 2px ' + this.terminal_.getForegroundColor() + ' solid;');

  var header = document.createElement('div');
  header.style.cssText = (
      'display: -webkit-flex;' +
      '-webkit-justify-content: flex-end;' +
      'height: ' + headerHeight + ';' +
      'background-color: ' + this.terminal_.getForegroundColor() + ';' +
      'color: ' + this.terminal_.getBackgroundColor() + ';' +
      'font-size: 16px;' +
      'font-family: ' + this.terminal_.getFontFamily());
  container.appendChild(header);

  if (false) {
    // No use for the close button.
    var button = document.createElement('div');
    button.setAttribute('role', 'button');
    button.style.cssText = (
        'margin-top: -3px;' +
        'margin-right: 3px;' +
        'cursor: pointer;');
    button.textContent = '\u2a2f';
    button.addEventListener('click', this.onCloseClicked_.bind(this));
    header.appendChild(button);
  }

  var iframe = this.iframe_ = document.createElement('iframe');
  iframe.onload = this.onLoad_.bind(this);
  iframe.style.cssText = (
      'display: -webkit-flex;' +
      '-webkit-flex: 1;' +
      'width: 100%');
  iframe.setAttribute('src', this.url);
  iframe.setAttribute('seamless', true);
  container.appendChild(iframe);

  this.div_.appendChild(container);
};
<|endoftext|>


<commit_before>$( document ).ready(function() {
  $( '.jshide' ).addClass('hidden').removeClass('jshide');
  $( '#date' ).bind('input', function(){
    //window.location.replace('index.php?date=' + $('#date').val());
    $.ajax({
      url: ('http://submit-iit-timesheet.geekkidconsulting.com/default.aspx?action=pull&date=' + $('#date').val()),
      crossDomain: true,
      xhrFields: {
        withCredentials: true
      },
      success: function(output, status, xhr) {
        alert(readcookie($('#date').val()) );
      },
      error: function() { $( '.hidden' ).addClass('.jshide').removeClass('hidden'); }
    });
  })
});
/*function setinfo(){
  alert( readcookie($('#date').val()) );
}*/
function readcookie(cookiename) {
  var realname = cookiename + "=";
  var cookarray = document.cookie.split(';');
  for(var i=0; i<cookarray.length; i++) {
    var resp = cookarray[i];
    while (resp.charAt(0)==' ') resp = resp.substring(1);
    if (resp.indexOf(realname) == 0) return resp.substring(realname.length,resp.length);
  }
  return "";
}<commit_msg>Removed XHR field.<commit_after>$( document ).ready(function() {
  $( '.jshide' ).addClass('hidden').removeClass('jshide');
  $( '#date' ).bind('input', function(){
    //window.location.replace('index.php?date=' + $('#date').val());
    $.ajax({
      url: ('http://submit-iit-timesheet.geekkidconsulting.com/default.aspx?action=pull&date=' + $('#date').val()),
      crossDomain: true,
      success: function(output, status, xhr) {
        alert(readcookie($('#date').val()) );
      },
      error: function() { $( '.hidden' ).addClass('.jshide').removeClass('hidden'); }
    });
  })
});
/*function setinfo(){
  alert( readcookie($('#date').val()) );
}*/
function readcookie(cookiename) {
  var realname = cookiename + "=";
  var cookarray = document.cookie.split(';');
  for(var i=0; i<cookarray.length; i++) {
    var resp = cookarray[i];
    while (resp.charAt(0)==' ') resp = resp.substring(1);
    if (resp.indexOf(realname) == 0) return resp.substring(realname.length,resp.length);
  }
  return "";
}<|endoftext|>


<commit_before>/**
 * @flow
 *
 * Building Hours "report a problem" screen.
 */

import React from 'react'
import {ScrollView, Text, StyleSheet} from 'react-native'
import {TableView, Section, Cell} from 'react-native-tableview-simple'
import type {BuildingType, NamedBuildingScheduleType} from '../types'
import type {TopLevelViewPropsType} from '../../types'

export class BuildingHoursProblemReportView extends React.PureComponent {
  static navigationOptions = {
    title: 'Report a Problem',
  }

  state: {
    building: BuildingType,
  } = {
    building: this.props.navigation.state.params.initialBuilding,
  }

  props: TopLevelViewPropsType & {
    navigation: {state: {params: {initialBuilding: BuildingType}}},
  }

  onChangeSchedule = (index: number, newSchedule: NamedBuildingScheduleType) => {
    this.setState(state => {
      const schedule = [...state.building.schedule]
      schedule.splice(index, 1, newSchedule)
      return {building: {...state.building, schedule}}
    })
  }

  render() {
    console.log(this.state.building)

    return (
      <ScrollView>
        <Text>
          Thanks for spotting a problem! If you could tell us what the new times
          are, we'd greatly appreciate it.
        </Text>

        <TableView>
          {this.state.building.schedule}
          <EditableSchedule schedule={{}} />
        </TableView>
      </ScrollView>
    )
  }
}

class EditableSchedule extends React.PureComponent {
  render() {
    return (

        <TitleCell text="Kitchen" />

        <TimesCell days="Mon-Thu" times="7:30AM — 8PM" />
        <TimesCell days="Friday" times="7:30AM — 8PM" />
        <TimesCell days="Saturday" times="9AM — 8PM" />
        <TimesCell days="Sunday" times="9AM — 8PM" />

        <AddScheduleCell />

        <NotesCell text="The kitchen stops cooking at 8 p.m." />

    )
  }
}

// "Title" will become a textfield like the login form
const TitleCell = ({text, onChange}) =>
  <Cell title="Title" cellStyle="RightDetail" detail={text} />

// "Notes" will become a big textarea
const NotesCell = ({text, onChange}) =>
  <Cell title="Notes" cellStyle="Subtitle" detail={text} />

const AddScheduleCell = () =>
  <Cell title="Add Schedule" accessory="DisclosureIndicator" />

const TimesCell = ({days, times, onPress}) =>
  <Cell
    title={days}
    accessory="Detail"
    cellStyle="RightDetail"
    detail={times}
  />
<commit_msg>add initial actual data display to ReportProblem<commit_after>/**
 * @flow
 *
 * Building Hours "report a problem" screen.
 */

import React from 'react'
import {ScrollView, Text, StyleSheet} from 'react-native'
import moment from 'moment-timezone'
import {TableView, Section, Cell} from 'react-native-tableview-simple'
import type {BuildingType, NamedBuildingScheduleType} from '../types'
import type {TopLevelViewPropsType} from '../../types'
import {summarizeDays, formatBuildingTimes} from '../lib'

export class BuildingHoursProblemReportView extends React.PureComponent {
  static navigationOptions = {
    title: 'Report a Problem',
  }

  state: {
    building: BuildingType,
  } = {
    building: this.props.navigation.state.params.initialBuilding,
  }

  props: TopLevelViewPropsType & {
    navigation: {state: {params: {initialBuilding: BuildingType}}},
  }

  onChangeSchedule = (
    index: number,
    newSchedule: NamedBuildingScheduleType,
  ) => {
    this.setState(state => {
      const schedule = [...state.building.schedule]
      schedule.splice(index, 1, newSchedule)
      return {building: {...state.building, schedule}}
    })
  }

  render() {
    const schedules = this.state.building.schedule || []

    return (
      <ScrollView>
        <Text>
          Thanks for spotting a problem! If you could tell us what the new times
          are, we'd greatly appreciate it.
        </Text>

        <TableView>
          {schedules.map(s => <EditableSchedule key={s.title} schedule={s} />)}
        </TableView>
      </ScrollView>
    )
  }
}

class EditableSchedule extends React.PureComponent {
  props: {
    schedule: NamedBuildingScheduleType,
  }

  render() {
    const {schedule} = this.props
    const now = moment()

    return (

        <TitleCell text={schedule.title} />

        {schedule.hours.map((set, i) =>
          <TimesCell
            key={i}
            days={summarizeDays(set.days)}
            times={formatBuildingTimes(set, now)}
          />,
        )}

        <AddScheduleCell />

        <NotesCell text={schedule.notes} />

    )
  }
}

// "Title" will become a textfield like the login form
const TitleCell = ({text, onChange}) =>
  <Cell title="Title" cellStyle="RightDetail" detail={text} />

// "Notes" will become a big textarea
const NotesCell = ({text, onChange}) =>
  <Cell title="Notes" cellStyle="Subtitle" detail={text} />

const AddScheduleCell = () =>
  <Cell title="Add Schedule" accessory="DisclosureIndicator" />

const TimesCell = ({days, times, onPress}) =>
  <Cell
    title={days}
    accessory="Detail"
    cellStyle="RightDetail"
    detail={times}
  />
<|endoftext|>


<commit_before>// @flow
// Copied from https://github.com/xgfe/react-native-datepicker

/*
The MIT License (MIT)

Copyright (c) 2016 鲜果FE

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

import React from 'react'
import {
  View,
  Text,
  Modal,
  TouchableHighlight,
  DatePickerAndroid,
  TimePickerAndroid,
  DatePickerIOS,
  Platform,
  Animated,
  Keyboard,
  StyleSheet,
} from 'react-native'
import moment from 'moment'

const FORMATS = {
  date: 'YYYY-MM-DD',
  datetime: 'YYYY-MM-DD HH:mm',
  time: 'HH:mm',
}

const SUPPORTED_ORIENTATIONS = [
  'portrait',
  'portrait-upside-down',
  'landscape',
  'landscape-left',
  'landscape-right',
]

type StyleSheetRules = Object | number | Array<StyleSheetRules>

type Props = {
  androidMode: 'calendar' | 'spinner' | 'default',
  cancelBtnText: string,
  confirmBtnText: string,
  customStyle: {[key: string]: StyleSheetRules},
  date: string | Date,
  disabled: boolean,
  duration: number,
  format?: string,
  height: number,
  hideText: boolean,
  is24Hour?: boolean,
  maxDate?: string | Date,
  minDate?: string | Date,
  minuteInterval?: 1 | 2 | 3 | 4 | 5 | 6 | 10 | 12 | 15 | 20 | 30,
  modalOnResponderTerminationRequest: () => boolean,
  mode: 'date' | 'datetime' | 'time',
  onCloseModal?: () => any,
  onDateChange: (string, Date) => any,
  onOpenModal?: () => any,
  onPressMask?: () => any,
  placeholder: string,
  style: StyleSheetRules,
  timeZoneOffsetInMinutes?: number,
  TouchableComponent: Class<React$Component<*, *, *>>,
}

type State = {
  date: Date,
  modalVisible: boolean,
  animatedHeight: Animated.Value,
  allowPointerEvents: boolean,
}

type DatePickerResponse = {
  action: string,
  year: number,
  month: number,
  day: number,
}

type TimePickerResponse = {
  action: string,
  hour: number,
  minute: number,
}

export class DatePicker extends React.PureComponent<any, Props, State> {
  static defaultProps = {
    mode: 'date',
    androidMode: 'default',
    date: '',

    // component height: 216(DatePickerIOS) + 1(borderTop) + 42(marginTop), IOS only
    height: 259,

    // slide animation duration time, default to 300ms, IOS only
    duration: 300,
    confirmBtnText: 'Confirm',
    cancelBtnText: 'Cancel',
    customStyle: {},

    disabled: false,
    hideText: false,
    placeholder: '',
    TouchableComponent: TouchableHighlight,
    modalOnResponderTerminationRequest: () => true,

    onDateChange: () => null,
  }

  state = {
    date: this.getDate(),
    modalVisible: false,
    animatedHeight: new Animated.Value(0),
    allowPointerEvents: true,
  }

  // componentWillMount() {
  //   // ignore the warning of Failed propType for date of DatePickerIOS, will remove after being fixed by official
  //   if (!console.ignoredYellowBox) {
  //     console.ignoredYellowBox = []
  //   }
  //   console.ignoredYellowBox.push('Warning: Failed propType')
  // }

  componentWillReceiveProps(nextProps: Props) {
    if (nextProps.date !== this.props.date) {
      this.setState(() => ({date: this.getDate(nextProps.date)}))
    }
  }

  setModalVisible = () => {
    const {height, duration} = this.props

    // slide animation
    this.setState(() => ({modalVisible: true}))
    return Animated.timing(this.state.animatedHeight, {
      toValue: height,
      duration: duration,
    }).start()
  }

  setModalHidden = () => {
    const {duration} = this.props

    return Animated.timing(this.state.animatedHeight, {
      toValue: 0,
      duration: duration,
    }).start(() => {
      this.setState(() => ({modalVisible: false}))
    })
  }

  onStartShouldSetResponder = () => {
    return true
  }

  onMoveShouldSetResponder = () => {
    return true
  }

  onPressMask = () => {
    if (this.props.onPressMask) {
      this.props.onPressMask()
    } else {
      this.onPressCancel()
    }
  }

  onPressCancel = () => {
    this.setModalHidden()

    if (this.props.onCloseModal) {
      this.props.onCloseModal()
    }
  }

  onPressConfirm = () => {
    this.datePicked()
    this.setModalHidden()

    if (this.props.onCloseModal) {
      this.props.onCloseModal()
    }
  }

  getDate = (date: string | Date = this.props.date) => {
    const {mode, minDate, maxDate, format = FORMATS[mode]} = this.props

    if (!date) {
      let now = new Date()
      if (minDate) {
        let _minDate = this.getDate(minDate)

        if (now < _minDate) {
          return _minDate
        }
      }

      if (maxDate) {
        let _maxDate = this.getDate(maxDate)

        if (now > _maxDate) {
          return _maxDate
        }
      }

      return now
    }

    if (date instanceof Date) {
      return date
    }

    return moment(date, format).toDate()
  }

  getDateStr = (date: string | Date = this.props.date): string => {
    const {mode, format = FORMATS[mode]} = this.props

    if (date instanceof Date) {
      return moment(date).format(format)
    } else {
      return moment(this.getDate(date)).format(format)
    }
  }

  datePicked = () => {
    this.props.onDateChange(this.getDateStr(this.state.date), this.state.date)
  }

  getTitleElement = () => {
    const {date, placeholder, customStyle} = this.props

    if (!date && placeholder) {
      return (
        <Text
          style={[defaultStyle.placeholderText, customStyle.placeholderText]}
        >
          {placeholder}
        </Text>
      )
    }

    return (
      <Text style={[defaultStyle.dateText, customStyle.dateText]}>
        {this.getDateStr()}
      </Text>
    )
  }

  onDateChange = (date: Date) => {
    this.setState(() => ({
      allowPointerEvents: false,
      date: date,
    }))
    const timeoutId = setTimeout(() => {
      this.setState(() => ({allowPointerEvents: true}))
      clearTimeout(timeoutId)
    }, 200)
  }

  onDatePicked = ({action, year, month, day}: DatePickerResponse) => {
    if (action !== DatePickerAndroid.dismissedAction) {
      this.setState(() => ({date: new Date(year, month, day)}))
      this.datePicked()
    } else {
      this.onPressCancel()
    }
  }

  onTimePicked = ({action, hour, minute}: TimePickerResponse) => {
    if (action !== DatePickerAndroid.dismissedAction) {
      this.setState(() => ({date: moment().hour(hour).minute(minute).toDate()}))
      this.datePicked()
    } else {
      this.onPressCancel()
    }
  }

  onDateTimePicked = ({action, year, month, day}: DatePickerResponse) => {
    const {
      mode,
      androidMode,
      format = FORMATS[mode],
      is24Hour = !format.match(/h|a/),
    } = this.props

    if (action !== DatePickerAndroid.dismissedAction) {
      let timeMoment = moment(this.state.date)

      TimePickerAndroid.open({
        hour: timeMoment.hour(),
        minute: timeMoment.minutes(),
        is24Hour: is24Hour,
        mode: androidMode,
      }).then(this.onDatetimeTimePicked.bind(this, year, month, day))
    } else {
      this.onPressCancel()
    }
  }

  onDatetimeTimePicked = (
    year: number,
    month: number,
    day: number,
    {action, hour, minute}: TimePickerResponse,
  ) => {
    if (action !== DatePickerAndroid.dismissedAction) {
      this.setState(() => ({date: new Date(year, month, day, hour, minute)}))
      this.datePicked()
    } else {
      this.onPressCancel()
    }
  }

  showAndroidPicker = () => {
    const {
      mode,
      androidMode,
      format = FORMATS[mode],
      minDate,
      maxDate,
      is24Hour = !format.match(/h|a/),
    } = this.props

    if (mode === 'date') {
      DatePickerAndroid.open({
        date: this.state.date,
        minDate: minDate && this.getDate(minDate),
        maxDate: maxDate && this.getDate(maxDate),
        mode: androidMode,
      }).then(this.onDatePicked)
    } else if (mode === 'time') {
      let timeMoment = moment(this.state.date)

      TimePickerAndroid.open({
        hour: timeMoment.hour(),
        minute: timeMoment.minutes(),
        is24Hour: is24Hour,
      }).then(this.onTimePicked)
    } else if (mode === 'datetime') {
      DatePickerAndroid.open({
        date: this.state.date,
        minDate: minDate && this.getDate(minDate),
        maxDate: maxDate && this.getDate(maxDate),
        mode: androidMode,
      }).then(this.onDateTimePicked)
    }
  }

  onPressDate = () => {
    if (this.props.disabled) {
      return true
    }

    Keyboard.dismiss()

    // reset state
    this.setState(() => ({
      date: this.getDate(),
    }))

    if (Platform.OS === 'ios') {
      this.setModalVisible()
    } else {
      this.showAndroidPicker()
    }

    if (this.props.onOpenModal) {
      this.props.onOpenModal()
    }
  }

  _renderios = () => {
    const {
      mode,
      customStyle,
      minDate,
      maxDate,
      minuteInterval,
      timeZoneOffsetInMinutes,
      cancelBtnText,
      confirmBtnText,
      TouchableComponent,
    } = this.props

    const picker = (
      <View pointerEvents={this.state.allowPointerEvents ? 'auto' : 'none'}>
        <DatePickerIOS
          date={this.state.date}
          mode={mode}
          minimumDate={minDate ? this.getDate(minDate) : undefined}
          maximumDate={maxDate ? this.getDate(maxDate) : undefined}
          onDateChange={this.onDateChange}
          minuteInterval={minuteInterval}
          timeZoneOffsetInMinutes={timeZoneOffsetInMinutes}
          style={[defaultStyle.datePicker, customStyle.datePicker]}
        />
      </View>
    )

    const cancel = (
      <TouchableComponent
        underlayColor="transparent"
        onPress={this.onPressCancel}
        style={[
          defaultStyle.btnText,
          defaultStyle.btnCancel,
          customStyle.btnCancel,
        ]}
      >
        <Text
          style={[
            defaultStyle.btnTextText,
            defaultStyle.btnTextCancel,
            customStyle.btnTextCancel,
          ]}
        >
          {cancelBtnText}
        </Text>
      </TouchableComponent>
    )

    const confirm = (
      <TouchableComponent
        underlayColor="transparent"
        onPress={this.onPressConfirm}
        style={[
          defaultStyle.btnText,
          defaultStyle.btnConfirm,
          customStyle.btnConfirm,
        ]}
      >
        <Text style={[defaultStyle.btnTextText, customStyle.btnTextConfirm]}>
          {confirmBtnText}
        </Text>
      </TouchableComponent>
    )

    return (
      <Modal
        transparent={true}
        animationType="none"
        visible={this.state.modalVisible}
        supportedOrientations={SUPPORTED_ORIENTATIONS}
        onRequestClose={this.setModalHidden}
      >
        <View style={defaultStyle.flex}>
          <TouchableComponent
            style={defaultStyle.datePickerMask}
            activeOpacity={1}
            underlayColor="#00000077"
            onPress={this.onPressMask}
          >
            <TouchableComponent underlayColor="#fff" style={defaultStyle.flex}>
              <Animated.View
                style={[
                  defaultStyle.datePickerCon,
                  {height: this.state.animatedHeight},
                  customStyle.datePickerCon,
                ]}
              >
                {picker}
                {cancel}
                {confirm}
              </Animated.View>
            </TouchableComponent>
          </TouchableComponent>
        </View>
      </Modal>
    )
  }

  render() {
    const {style, customStyle, disabled, TouchableComponent} = this.props

    const dateInputStyle = [
      defaultStyle.dateInput,
      customStyle.dateInput,
      disabled && defaultStyle.disabled,
      disabled && customStyle.disabled,
    ]

    return (
      <TouchableComponent
        style={[defaultStyle.dateTouch, style]}
        underlayColor="transparent"
        onPress={this.onPressDate}
      >
        <View style={[defaultStyle.dateTouchBody, customStyle.dateTouchBody]}>
          {!this.props.hideText
            ? <View style={dateInputStyle}>
                {this.getTitleElement()}
              </View>
            : null}
          {Platform.OS === 'ios' && this._renderios()}
        </View>
      </TouchableComponent>
    )
  }
}

const defaultStyle = StyleSheet.create({
  dateTouch: {
    width: 142,
  },
  flex: {
    flex: 1,
  },
  dateTouchBody: {
    flexDirection: 'row',
    height: 40,
    alignItems: 'center',
    justifyContent: 'center',
  },
  dateInput: {
    flex: 1,
    height: 40,
    borderWidth: 1,
    borderColor: '#aaa',
    alignItems: 'center',
    justifyContent: 'center',
  },
  dateText: {
    color: '#333',
  },
  placeholderText: {
    color: '#c9c9c9',
  },
  datePickerMask: {
    flex: 1,
    alignItems: 'flex-end',
    flexDirection: 'row',
    backgroundColor: '#00000077',
  },
  datePickerCon: {
    backgroundColor: '#fff',
    height: 0,
    overflow: 'hidden',
  },
  btnText: {
    position: 'absolute',
    top: 0,
    height: 42,
    padding: 20,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
  },
  btnTextText: {
    fontSize: 16,
    color: '#46cf98',
  },
  btnTextCancel: {
    color: '#666',
  },
  btnCancel: {
    left: 0,
  },
  btnConfirm: {
    right: 0,
  },
  datePicker: {
    marginTop: 42,
    borderTopColor: '#ccc',
    borderTopWidth: 1,
  },
  disabled: {
    backgroundColor: '#eee',
  },
})
<commit_msg>apply a pending patch from rn-datepicker<commit_after>// @flow
// Copied from https://github.com/xgfe/react-native-datepicker

/*
The MIT License (MIT)

Copyright (c) 2016 鲜果FE

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

import React from 'react'
import {
  View,
  Text,
  Modal,
  TouchableHighlight,
  DatePickerAndroid,
  TimePickerAndroid,
  DatePickerIOS,
  Platform,
  Animated,
  Keyboard,
  StyleSheet,
} from 'react-native'
import moment from 'moment'

const FORMATS = {
  date: 'YYYY-MM-DD',
  datetime: 'YYYY-MM-DD HH:mm',
  time: 'HH:mm',
}

const SUPPORTED_ORIENTATIONS = [
  'portrait',
  'portrait-upside-down',
  'landscape',
  'landscape-left',
  'landscape-right',
]

type StyleSheetRules = Object | number | Array<StyleSheetRules>

type Props = {
  androidMode: 'calendar' | 'spinner' | 'default',
  cancelBtnText: string,
  confirmBtnText: string,
  customStyle: {[key: string]: StyleSheetRules},
  date: string | Date,
  disabled: boolean,
  duration: number,
  format?: string,
  height: number,
  hideText: boolean,
  is24Hour?: boolean,
  maxDate?: string | Date,
  minDate?: string | Date,
  minuteInterval?: 1 | 2 | 3 | 4 | 5 | 6 | 10 | 12 | 15 | 20 | 30,
  modalOnResponderTerminationRequest: () => boolean,
  mode: 'date' | 'datetime' | 'time',
  onCloseModal?: () => any,
  onDateChange: (string, Date) => any,
  onOpenModal?: () => any,
  onPressMask?: () => any,
  placeholder: string,
  style: StyleSheetRules,
  timeZoneOffsetInMinutes?: number,
  TouchableComponent: Class<React$Component<*, *, *>>,
}

type State = {
  date: Date,
  modalVisible: boolean,
  animatedHeight: Animated.Value,
  allowPointerEvents: boolean,
}

type DatePickerResponse = {
  action: string,
  year: number,
  month: number,
  day: number,
}

type TimePickerResponse = {
  action: string,
  hour: number,
  minute: number,
}

export class DatePicker extends React.PureComponent<any, Props, State> {
  static defaultProps = {
    mode: 'date',
    androidMode: 'default',
    date: '',

    // component height: 216(DatePickerIOS) + 1(borderTop) + 42(marginTop), IOS only
    height: 259,

    // slide animation duration time, default to 300ms, IOS only
    duration: 300,
    confirmBtnText: 'Confirm',
    cancelBtnText: 'Cancel',
    customStyle: {},

    disabled: false,
    hideText: false,
    placeholder: '',
    TouchableComponent: TouchableHighlight,
    modalOnResponderTerminationRequest: () => true,

    onDateChange: () => null,
  }

  state = {
    date: this.getDate(),
    modalVisible: false,
    animatedHeight: new Animated.Value(0),
    allowPointerEvents: true,
  }

  // componentWillMount() {
  //   // ignore the warning of Failed propType for date of DatePickerIOS, will remove after being fixed by official
  //   if (!console.ignoredYellowBox) {
  //     console.ignoredYellowBox = []
  //   }
  //   console.ignoredYellowBox.push('Warning: Failed propType')
  // }

  componentWillReceiveProps(nextProps: Props) {
    if (nextProps.date !== this.props.date) {
      this.setState(() => ({date: this.getDate(nextProps.date)}))
    }
  }

  setModalVisible = () => {
    const {height, duration} = this.props

    // slide animation
    this.setState(() => ({modalVisible: true}))
    return Animated.timing(this.state.animatedHeight, {
      toValue: height,
      duration: duration,
    }).start()
  }

  setModalHidden = () => {
    const {duration} = this.props

    return Animated.timing(this.state.animatedHeight, {
      toValue: 0,
      duration: duration,
    }).start(() => {
      this.setState(() => ({modalVisible: false}))
    })
  }

  onStartShouldSetResponder = () => {
    return true
  }

  onMoveShouldSetResponder = () => {
    return true
  }

  onPressMask = () => {
    if (this.props.onPressMask) {
      this.props.onPressMask()
    } else {
      this.onPressCancel()
    }
  }

  onPressCancel = () => {
    this.setModalHidden()

    if (this.props.onCloseModal) {
      this.props.onCloseModal()
    }
  }

  onPressConfirm = () => {
    this.datePicked()
    this.setModalHidden()

    if (this.props.onCloseModal) {
      this.props.onCloseModal()
    }
  }

  getDate = (date: string | Date = this.props.date) => {
    const {mode, minDate, maxDate, format = FORMATS[mode]} = this.props

    if (!date) {
      let now = new Date()
      if (minDate) {
        let _minDate = this.getDate(minDate)

        if (now < _minDate) {
          return _minDate
        }
      }

      if (maxDate) {
        let _maxDate = this.getDate(maxDate)

        if (now > _maxDate) {
          return _maxDate
        }
      }

      return now
    }

    if (date instanceof Date) {
      return date
    }

    return moment(date, format).toDate()
  }

  getDateStr = (date: string | Date = this.props.date): string => {
    const {mode, format = FORMATS[mode]} = this.props

    if (date instanceof Date) {
      return moment(date).format(format)
    } else {
      return moment(this.getDate(date)).format(format)
    }
  }

  datePicked = () => {
    this.props.onDateChange(this.getDateStr(this.state.date), this.state.date)
  }

  getTitleElement = () => {
    const {date, placeholder, customStyle} = this.props

    if (!date && placeholder) {
      return (
        <Text
          style={[defaultStyle.placeholderText, customStyle.placeholderText]}
        >
          {placeholder}
        </Text>
      )
    }

    return (
      <Text style={[defaultStyle.dateText, customStyle.dateText]}>
        {this.getDateStr()}
      </Text>
    )
  }

  onDateChange = (date: Date) => {
    this.setState(() => ({
      allowPointerEvents: false,
      date: date,
    }))
    const timeoutId = setTimeout(() => {
      this.setState(() => ({allowPointerEvents: true}))
      clearTimeout(timeoutId)
    }, 200)
  }

  onDatePicked = ({action, year, month, day}: DatePickerResponse) => {
    if (action !== DatePickerAndroid.dismissedAction) {
      this.setState(() => ({date: new Date(year, month, day)}))
      this.datePicked()
    } else {
      this.onPressCancel()
    }
  }

  onTimePicked = ({action, hour, minute}: TimePickerResponse) => {
    if (action !== DatePickerAndroid.dismissedAction) {
      this.setState(() => ({date: moment().hour(hour).minute(minute).toDate()}))
      this.datePicked()
    } else {
      this.onPressCancel()
    }
  }

  onDateTimePicked = ({action, year, month, day}: DatePickerResponse) => {
    const {
      mode,
      androidMode,
      format = FORMATS[mode],
      is24Hour = !format.match(/h|a/),
    } = this.props

    if (action !== DatePickerAndroid.dismissedAction) {
      let timeMoment = moment(this.state.date)

      TimePickerAndroid.open({
        hour: timeMoment.hour(),
        minute: timeMoment.minutes(),
        is24Hour: is24Hour,
        mode: androidMode,
      }).then(this.onDatetimeTimePicked.bind(this, year, month, day))
    } else {
      this.onPressCancel()
    }
  }

  onDatetimeTimePicked = (
    year: number,
    month: number,
    day: number,
    {action, hour, minute}: TimePickerResponse,
  ) => {
    if (action !== DatePickerAndroid.dismissedAction) {
      this.setState(() => ({date: new Date(year, month, day, hour, minute)}))
      this.datePicked()
    } else {
      this.onPressCancel()
    }
  }

  showAndroidPicker = () => {
    const {
      mode,
      androidMode,
      format = FORMATS[mode],
      minDate,
      maxDate,
      is24Hour = !format.match(/h|a/),
    } = this.props

    if (mode === 'date') {
      DatePickerAndroid.open({
        date: this.state.date,
        minDate: minDate && this.getDate(minDate),
        maxDate: maxDate && this.getDate(maxDate),
        mode: androidMode,
      }).then(this.onDatePicked)
    } else if (mode === 'time') {
      let timeMoment = moment(this.state.date)

      TimePickerAndroid.open({
        hour: timeMoment.hour(),
        minute: timeMoment.minutes(),
        is24Hour: is24Hour,
      }).then(this.onTimePicked)
    } else if (mode === 'datetime') {
      DatePickerAndroid.open({
        date: this.state.date,
        minDate: minDate && this.getDate(minDate),
        maxDate: maxDate && this.getDate(maxDate),
        mode: androidMode,
      }).then(this.onDateTimePicked)
    }
  }

  onPressDate = () => {
    if (this.props.disabled) {
      return true
    }

    Keyboard.dismiss()

    // reset state
    this.setState(() => ({
      date: this.getDate(),
    }))

    if (Platform.OS === 'ios') {
      this.setModalVisible()
    } else {
      this.showAndroidPicker()
    }

    if (this.props.onOpenModal) {
      this.props.onOpenModal()
    }
  }

  _renderios = () => {
    const {
      mode,
      customStyle,
      minDate,
      maxDate,
      minuteInterval,
      timeZoneOffsetInMinutes,
      cancelBtnText,
      confirmBtnText,
      TouchableComponent,
    } = this.props

    const picker = (
      <View pointerEvents={this.state.allowPointerEvents ? 'auto' : 'none'}>
        <DatePickerIOS
          date={this.state.date}
          mode={mode}
          minimumDate={minDate ? this.getDate(minDate) : undefined}
          maximumDate={maxDate ? this.getDate(maxDate) : undefined}
          onDateChange={this.onDateChange}
          minuteInterval={minuteInterval}
          timeZoneOffsetInMinutes={timeZoneOffsetInMinutes}
          style={[defaultStyle.datePicker, customStyle.datePicker]}
        />
      </View>
    )

    const cancel = (
      <TouchableComponent
        underlayColor="transparent"
        onPress={this.onPressCancel}
        style={[
          defaultStyle.btnText,
          defaultStyle.btnCancel,
          customStyle.btnCancel,
        ]}
      >
        <Text
          style={[
            defaultStyle.btnTextText,
            defaultStyle.btnTextCancel,
            customStyle.btnTextCancel,
          ]}
        >
          {cancelBtnText}
        </Text>
      </TouchableComponent>
    )

    const confirm = (
      <TouchableComponent
        underlayColor="transparent"
        onPress={this.onPressConfirm}
        style={[
          defaultStyle.btnText,
          defaultStyle.btnConfirm,
          customStyle.btnConfirm,
        ]}
      >
        <Text style={[defaultStyle.btnTextText, customStyle.btnTextConfirm]}>
          {confirmBtnText}
        </Text>
      </TouchableComponent>
    )

    return (
      <Modal
        transparent={true}
        animationType="none"
        visible={this.state.modalVisible}
        supportedOrientations={SUPPORTED_ORIENTATIONS}
        onRequestClose={this.setModalHidden}
      >
        <View style={defaultStyle.flex}>
          <TouchableComponent
            style={defaultStyle.datePickerMask}
            activeOpacity={1}
            underlayColor="#00000077"
            onPress={this.onPressMask}
          >
            <TouchableComponent underlayColor="#fff" style={defaultStyle.flex}>
              <Animated.View
                style={[
                  defaultStyle.datePickerCon,
                  {height: this.state.animatedHeight},
                  customStyle.datePickerCon,
                ]}
              >
                {picker}
                {cancel}
                {confirm}
              </Animated.View>
            </TouchableComponent>
          </TouchableComponent>
        </View>
      </Modal>
    )
  }

  render() {
    const {style, customStyle, disabled, TouchableComponent} = this.props

    const dateInputStyle = [
      defaultStyle.dateInput,
      customStyle.dateInput,
      disabled && defaultStyle.disabled,
      disabled && customStyle.disabled,
    ]

    return (
      <TouchableComponent
        style={[defaultStyle.dateTouch, style]}
        underlayColor="transparent"
        onPress={this.onPressDate}
      >
        <View style={[defaultStyle.dateTouchBody, customStyle.dateTouchBody]}>
          {!this.props.hideText
            ? <View style={dateInputStyle}>
                {this.getTitleElement()}
              </View>
            : null}
          {Platform.OS === 'ios' && this._renderios()}
        </View>
      </TouchableComponent>
    )
  }
}

const defaultStyle = StyleSheet.create({
  dateTouch: {
    flexDirection: 'row',
  },
  flex: {
    flex: 1,
  },
  dateTouchBody: {
    flexDirection: 'row',
    height: 40,
    alignItems: 'center',
    justifyContent: 'center',
  },
  dateInput: {
    flex: 1,
    height: 40,
    borderWidth: 1,
    borderColor: '#aaa',
    alignItems: 'center',
    justifyContent: 'center',
  },
  dateText: {
    color: '#333',
  },
  placeholderText: {
    color: '#c9c9c9',
  },
  datePickerMask: {
    flex: 1,
    alignItems: 'flex-end',
    flexDirection: 'row',
    backgroundColor: '#00000077',
  },
  datePickerCon: {
    backgroundColor: '#fff',
    height: 0,
    overflow: 'hidden',
  },
  btnText: {
    position: 'absolute',
    top: 0,
    height: 42,
    padding: 20,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
  },
  btnTextText: {
    fontSize: 16,
    color: '#46cf98',
  },
  btnTextCancel: {
    color: '#666',
  },
  btnCancel: {
    left: 0,
  },
  btnConfirm: {
    right: 0,
  },
  datePicker: {
    marginTop: 42,
    borderTopColor: '#ccc',
    borderTopWidth: 1,
  },
  disabled: {
    backgroundColor: '#eee',
  },
})
<|endoftext|>