<commit_before>/**
 * guy
 * @class BI.TextEditor
 * @extends BI.Single
 */
BI.TextEditor = BI.inherit(BI.Widget, {
    _defaultConfig: function (config) {
        var conf = BI.TextEditor.superclass._defaultConfig.apply(this, arguments);
        return BI.extend(conf, {
            extraCls: "bi-text-editor bi-focus-shadow " + (config.simple ? "bi-border-bottom" : "bi-border"),
            hgap: 4,
            vgap: 2,
            lgap: 0,
            rgap: 0,
            tgap: 0,
            bgap: 0,
            validationChecker: BI.emptyFn,
            quitChecker: BI.emptyFn,
            allowBlank: false,
            watermark: "",
            errorText: "",
            height: 24
        });
    },

    _init: function () {
        BI.TextEditor.superclass._init.apply(this, arguments);
        var self = this, o = this.options;
        var border = o.simple ? 1 : 2;
        if (BI.isNumber(o.height)) {
            this.element.css({height: o.height - border});
        }
        if (BI.isNumber(o.width)) {
            this.element.css({width: o.width - border});
        }
        this.editor = BI.createWidget({
            type: "bi.editor",
            simple: o.simple,
            height: o.height - border,
            hgap: o.hgap,
            vgap: o.vgap,
            lgap: o.lgap,
            rgap: o.rgap,
            tgap: o.tgap,
            bgap: o.bgap,
            value: o.value,
            title: o.title,
            tipType: o.tipType,
            validationChecker: o.validationChecker,
            quitChecker: o.quitChecker,
            allowBlank: o.allowBlank,
            watermark: o.watermark,
            errorText: o.errorText
        });
        this.editor.on(BI.Controller.EVENT_CHANGE, function () {
            self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
        });

        this.editor.on(BI.Editor.EVENT_FOCUS, function () {
            self.fireEvent(BI.TextEditor.EVENT_FOCUS);
        });
        this.editor.on(BI.Editor.EVENT_BLUR, function () {
            self.fireEvent(BI.TextEditor.EVENT_BLUR);
        });
        this.editor.on(BI.Editor.EVENT_CLICK, function () {
            self.fireEvent(BI.TextEditor.EVENT_CLICK);
        });
        this.editor.on(BI.Editor.EVENT_CHANGE, function () {
            self.fireEvent(BI.TextEditor.EVENT_CHANGE);
        });
        this.editor.on(BI.Editor.EVENT_KEY_DOWN, function (v) {
            self.fireEvent(BI.TextEditor.EVENT_KEY_DOWN);
        });
        this.editor.on(BI.Editor.EVENT_SPACE, function (v) {
            self.fireEvent(BI.TextEditor.EVENT_SPACE);
        });
        this.editor.on(BI.Editor.EVENT_BACKSPACE, function (v) {
            self.fireEvent(BI.TextEditor.EVENT_BACKSPACE);
        });

        this.editor.on(BI.Editor.EVENT_VALID, function () {
            self.element.removeClass("error");
            self.fireEvent(BI.TextEditor.EVENT_VALID);
        });
        this.editor.on(BI.Editor.EVENT_CONFIRM, function () {
            self.fireEvent(BI.TextEditor.EVENT_CONFIRM);
        });
        this.editor.on(BI.Editor.EVENT_CHANGE_CONFIRM, function () {
            self.fireEvent(BI.TextEditor.EVENT_CHANGE_CONFIRM);
        });
        this.editor.on(BI.Editor.EVENT_REMOVE, function (v) {
            self.fireEvent(BI.TextEditor.EVENT_REMOVE);
        });
        this.editor.on(BI.Editor.EVENT_START, function () {
            self.fireEvent(BI.TextEditor.EVENT_START);
        });
        this.editor.on(BI.Editor.EVENT_PAUSE, function () {
            self.fireEvent(BI.TextEditor.EVENT_PAUSE);
        });
        this.editor.on(BI.Editor.EVENT_STOP, function () {
            self.fireEvent(BI.TextEditor.EVENT_STOP);
        });
        this.editor.on(BI.Editor.EVENT_ERROR, function () {
            self.element.addClass("error");
            self.fireEvent(BI.TextEditor.EVENT_ERROR, arguments);
        });
        this.editor.on(BI.Editor.EVENT_ENTER, function () {
            self.fireEvent(BI.TextEditor.EVENT_ENTER);
        });
        this.editor.on(BI.Editor.EVENT_RESTRICT, function () {
            self.fireEvent(BI.TextEditor.EVENT_RESTRICT);
        });
        this.editor.on(BI.Editor.EVENT_EMPTY, function () {
            self.fireEvent(BI.TextEditor.EVENT_EMPTY);
        });
        BI.createWidget({
            type: "bi.vertical",
            scrolly: false,
            element: this,
            items: [this.editor]
        });
    },

    setWaterMark: function (v) {
        this.options.watermark = v;
        this.editor.setWaterMark(v);
    },

    focus: function () {
        this.editor.focus();
    },

    blur: function () {
        this.editor.blur();
    },

    setErrorText: function (text) {
        this.editor.setErrorText(text);
    },

    getErrorText: function () {
        return this.editor.getErrorText();
    },

    isValid: function () {
        return this.editor.isValid();
    },

    setValue: function (v) {
        this.editor.setValue(v);
    },

    getValue: function () {
        return this.editor.getValue();
    }
});
BI.TextEditor.EVENT_CHANGE = "EVENT_CHANGE";
BI.TextEditor.EVENT_FOCUS = "EVENT_FOCUS";
BI.TextEditor.EVENT_BLUR = "EVENT_BLUR";
BI.TextEditor.EVENT_CLICK = "EVENT_CLICK";
BI.TextEditor.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
BI.TextEditor.EVENT_SPACE = "EVENT_SPACE";
BI.TextEditor.EVENT_BACKSPACE = "EVENT_BACKSPACE";

BI.TextEditor.EVENT_START = "EVENT_START";
BI.TextEditor.EVENT_PAUSE = "EVENT_PAUSE";
BI.TextEditor.EVENT_STOP = "EVENT_STOP";
BI.TextEditor.EVENT_CONFIRM = "EVENT_CONFIRM";
BI.TextEditor.EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM";
BI.TextEditor.EVENT_VALID = "EVENT_VALID";
BI.TextEditor.EVENT_ERROR = "EVENT_ERROR";
BI.TextEditor.EVENT_ENTER = "EVENT_ENTER";
BI.TextEditor.EVENT_RESTRICT = "EVENT_RESTRICT";
BI.TextEditor.EVENT_REMOVE = "EVENT_REMOVE";
BI.TextEditor.EVENT_EMPTY = "EVENT_EMPTY";

BI.shortcut("bi.text_editor", BI.TextEditor);<commit_msg>Pull request #2546: chore: TextEditor支持传入inputType和autocomplete<commit_after>/**
 * guy
 * @class BI.TextEditor
 * @extends BI.Single
 */
BI.TextEditor = BI.inherit(BI.Widget, {
    _defaultConfig: function (config) {
        var conf = BI.TextEditor.superclass._defaultConfig.apply(this, arguments);
        return BI.extend(conf, {
            extraCls: "bi-text-editor bi-focus-shadow " + (config.simple ? "bi-border-bottom" : "bi-border"),
            hgap: 4,
            vgap: 2,
            lgap: 0,
            rgap: 0,
            tgap: 0,
            bgap: 0,
            validationChecker: BI.emptyFn,
            quitChecker: BI.emptyFn,
            allowBlank: false,
            watermark: "",
            errorText: "",
            height: 24
        });
    },

    _init: function () {
        BI.TextEditor.superclass._init.apply(this, arguments);
        var self = this, o = this.options;
        var border = o.simple ? 1 : 2;
        if (BI.isNumber(o.height)) {
            this.element.css({height: o.height - border});
        }
        if (BI.isNumber(o.width)) {
            this.element.css({width: o.width - border});
        }
        this.editor = BI.createWidget({
            type: "bi.editor",
            simple: o.simple,
            height: o.height - border,
            hgap: o.hgap,
            vgap: o.vgap,
            lgap: o.lgap,
            rgap: o.rgap,
            tgap: o.tgap,
            bgap: o.bgap,
            value: o.value,
            title: o.title,
            tipType: o.tipType,
            validationChecker: o.validationChecker,
            quitChecker: o.quitChecker,
            allowBlank: o.allowBlank,
            watermark: o.watermark,
            errorText: o.errorText,
            inputType: o.inputType,
            autocomplete: o.autocomplete
        });
        this.editor.on(BI.Controller.EVENT_CHANGE, function () {
            self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
        });

        this.editor.on(BI.Editor.EVENT_FOCUS, function () {
            self.fireEvent(BI.TextEditor.EVENT_FOCUS);
        });
        this.editor.on(BI.Editor.EVENT_BLUR, function () {
            self.fireEvent(BI.TextEditor.EVENT_BLUR);
        });
        this.editor.on(BI.Editor.EVENT_CLICK, function () {
            self.fireEvent(BI.TextEditor.EVENT_CLICK);
        });
        this.editor.on(BI.Editor.EVENT_CHANGE, function () {
            self.fireEvent(BI.TextEditor.EVENT_CHANGE);
        });
        this.editor.on(BI.Editor.EVENT_KEY_DOWN, function (v) {
            self.fireEvent(BI.TextEditor.EVENT_KEY_DOWN);
        });
        this.editor.on(BI.Editor.EVENT_SPACE, function (v) {
            self.fireEvent(BI.TextEditor.EVENT_SPACE);
        });
        this.editor.on(BI.Editor.EVENT_BACKSPACE, function (v) {
            self.fireEvent(BI.TextEditor.EVENT_BACKSPACE);
        });

        this.editor.on(BI.Editor.EVENT_VALID, function () {
            self.element.removeClass("error");
            self.fireEvent(BI.TextEditor.EVENT_VALID);
        });
        this.editor.on(BI.Editor.EVENT_CONFIRM, function () {
            self.fireEvent(BI.TextEditor.EVENT_CONFIRM);
        });
        this.editor.on(BI.Editor.EVENT_CHANGE_CONFIRM, function () {
            self.fireEvent(BI.TextEditor.EVENT_CHANGE_CONFIRM);
        });
        this.editor.on(BI.Editor.EVENT_REMOVE, function (v) {
            self.fireEvent(BI.TextEditor.EVENT_REMOVE);
        });
        this.editor.on(BI.Editor.EVENT_START, function () {
            self.fireEvent(BI.TextEditor.EVENT_START);
        });
        this.editor.on(BI.Editor.EVENT_PAUSE, function () {
            self.fireEvent(BI.TextEditor.EVENT_PAUSE);
        });
        this.editor.on(BI.Editor.EVENT_STOP, function () {
            self.fireEvent(BI.TextEditor.EVENT_STOP);
        });
        this.editor.on(BI.Editor.EVENT_ERROR, function () {
            self.element.addClass("error");
            self.fireEvent(BI.TextEditor.EVENT_ERROR, arguments);
        });
        this.editor.on(BI.Editor.EVENT_ENTER, function () {
            self.fireEvent(BI.TextEditor.EVENT_ENTER);
        });
        this.editor.on(BI.Editor.EVENT_RESTRICT, function () {
            self.fireEvent(BI.TextEditor.EVENT_RESTRICT);
        });
        this.editor.on(BI.Editor.EVENT_EMPTY, function () {
            self.fireEvent(BI.TextEditor.EVENT_EMPTY);
        });
        BI.createWidget({
            type: "bi.vertical",
            scrolly: false,
            element: this,
            items: [this.editor]
        });
    },

    setWaterMark: function (v) {
        this.options.watermark = v;
        this.editor.setWaterMark(v);
    },

    focus: function () {
        this.editor.focus();
    },

    blur: function () {
        this.editor.blur();
    },

    setErrorText: function (text) {
        this.editor.setErrorText(text);
    },

    getErrorText: function () {
        return this.editor.getErrorText();
    },

    isValid: function () {
        return this.editor.isValid();
    },

    setValue: function (v) {
        this.editor.setValue(v);
    },

    getValue: function () {
        return this.editor.getValue();
    }
});
BI.TextEditor.EVENT_CHANGE = "EVENT_CHANGE";
BI.TextEditor.EVENT_FOCUS = "EVENT_FOCUS";
BI.TextEditor.EVENT_BLUR = "EVENT_BLUR";
BI.TextEditor.EVENT_CLICK = "EVENT_CLICK";
BI.TextEditor.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
BI.TextEditor.EVENT_SPACE = "EVENT_SPACE";
BI.TextEditor.EVENT_BACKSPACE = "EVENT_BACKSPACE";

BI.TextEditor.EVENT_START = "EVENT_START";
BI.TextEditor.EVENT_PAUSE = "EVENT_PAUSE";
BI.TextEditor.EVENT_STOP = "EVENT_STOP";
BI.TextEditor.EVENT_CONFIRM = "EVENT_CONFIRM";
BI.TextEditor.EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM";
BI.TextEditor.EVENT_VALID = "EVENT_VALID";
BI.TextEditor.EVENT_ERROR = "EVENT_ERROR";
BI.TextEditor.EVENT_ENTER = "EVENT_ENTER";
BI.TextEditor.EVENT_RESTRICT = "EVENT_RESTRICT";
BI.TextEditor.EVENT_REMOVE = "EVENT_REMOVE";
BI.TextEditor.EVENT_EMPTY = "EVENT_EMPTY";

BI.shortcut("bi.text_editor", BI.TextEditor);<|endoftext|>


<commit_before>angular.module('app.services')
.provider('formElementProvider', function() {
	'use strict';

	var templates = {
		'String': function(field) {
			if(field && field.validation.allowedHtml) {
				return 'partials/inputs/tinymce-input.html';
			} else if(field && field.validation.oneOf) {
				return 'partials/inputs/enum-input.html';
			} else if(field && field.validation.maxlength && field.validation.maxlength > 140) {
				return 'partials/inputs/textarea-input.html';
			} else {
				return 'partials/inputs/default-input.html';
			}
		},
		'Link': function(field) {
			if(field.kind.isArray) {
				return 'partials/inputs/link-multiple-input.html';
			}
			else {
				return 'partials/inputs/link-input.html';
			}
		},
		'Date': 'partials/inputs/date-input.html',
		'Boolean': 'partials/inputs/boolean-input.html',
		'Number': 'partials/inputs/number-input.html'
	};

	return {
		$get: function() {
			return {
				getTemplateUrl: function(field) {
					var t = templates[field.kind.name];
					if(_.isFunction(t)) {
						return t(field);
					} else {
						return t || templates['String']();
					}
				}
			};
		}
	};
});
<commit_msg>FormElementProvider: Never give email and url fields a textarea<commit_after>angular.module('app.services')
.provider('formElementProvider', function() {
	'use strict';

	var templates = {
		'String': function(field) {
			if(field && field.validation.allowedHtml) {
				return 'partials/inputs/tinymce-input.html';
			} else if(field && field.validation.oneOf) {
				return 'partials/inputs/enum-input.html';
			} else if(field && useTextarea(field)) {
				return 'partials/inputs/textarea-input.html';
			} else {
				return 'partials/inputs/default-input.html';
			}
		},
		'Link': function(field) {
			if(field.kind.isArray) {
				return 'partials/inputs/link-multiple-input.html';
			}
			else {
				return 'partials/inputs/link-input.html';
			}
		},
		'Date': 'partials/inputs/date-input.html',
		'Boolean': 'partials/inputs/boolean-input.html',
		'Number': 'partials/inputs/number-input.html'
	};

	return {
		$get: function() {
			return {
				getTemplateUrl: function(field) {
					var t = templates[field.kind.name];
					if(_.isFunction(t)) {
						return t(field);
					} else {
						return t || templates['String']();
					}
				}
			};
		}
	};
});

function useTextarea(field) {
	var acceptsLongValues = (field.validation.maxlength || 0) > 140;
	var acceptsEmailOrUrl = field.validation.url || field.validation.email;

	// For the rationale behind never giving email and url fields a
	// textarea, see https://github.com/TechAtNYU/intranet/issues/102
	return acceptsLongValues && !acceptsEmailOrUrl;
}
<|endoftext|>


<commit_before>
// @require core/widget/widget.js

(function ( $, _, Svelto, Widgets, Factory ) {

  /* CONFIG */

  let config = {
    name: 'timeAgo',
    plugin: true,
    selector: '.timeago, .time-ago',
    options: {
      timestamp: false, // UNIX timestamp
      title: false, // Update the title or the text?
      abort: { // Abort the loop if it has to wait more than `threshold`
        enabled: true, // Whether the abort functionality should be enabled (recommended, at least to avoid overflowing the delay https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout)
        threshold: 604800, // 1 week
      },
      datas: {
        timestamp: 'timestamp'
      },
      callbacks: {
        change: _.noop
      }
    }
  };

  /* TIME AGO */

  class TimeAgo extends Widgets.Widget {

    /* SPECIAL */

    _variables () {

      this.$timeAgoElement = this.$element;

    }

    _init () {

      if ( !this.options.timestamp ) {

        this.options.timestamp = this.$timeAgoElement.data ( this.options.datas.timestamp );

      }

      if ( this.isEnabled () ) {

        this._loop ();

      }

    }

    _destroy () {

      clearTimeout ( this.loopId );

    }

    /* LOOP */

    _loop ( seconds = 0 ) {

      if ( this.options.abort.enabled && seconds > this.options.abort.threshold ) return;

      this.loopId = this._delay ( function () {

        this._loop ( this._update ().next );

      }, seconds * 1000 );

    }

    /* UPDATE */

    _update () {

      let timeAgo = _.timeAgo ( this.options.timestamp );

      if ( this.options.title ) {

        this.$timeAgoElement.attr ( 'title', timeAgo.str );

      } else {

        this.$timeAgoElement.text ( timeAgo.str );

      }

      this._trigger ( 'change' );

      return timeAgo;

    }

    /* API OVERRIDES */

    enable () {

      super.enable ();

      this._loop ();

    }

    disable () {

      super.disable ();

      clearTimeout ( this.loopId );

    }

  }

  /* FACTORY */

  Factory.make ( TimeAgo, config );

}( Svelto.$, Svelto._, Svelto, Svelto.Widgets, Svelto.Factory, Svelto.Timer ));
<commit_msg>TimeAgo: updating even if disabled<commit_after>
// @require core/widget/widget.js

(function ( $, _, Svelto, Widgets, Factory ) {

  /* CONFIG */

  let config = {
    name: 'timeAgo',
    plugin: true,
    selector: '.timeago, .time-ago',
    options: {
      timestamp: false, // UNIX timestamp
      title: false, // Update the title or the text?
      abort: { // Abort the loop if it has to wait more than `threshold`
        enabled: true, // Whether the abort functionality should be enabled (recommended, at least to avoid overflowing the delay https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout)
        threshold: 604800, // 1 week
      },
      datas: {
        timestamp: 'timestamp'
      },
      callbacks: {
        change: _.noop
      }
    }
  };

  /* TIME AGO */

  class TimeAgo extends Widgets.Widget {

    /* SPECIAL */

    _variables () {

      this.$timeAgoElement = this.$element;

    }

    _init () {

      if ( !this.options.timestamp ) {

        this.options.timestamp = this.$timeAgoElement.data ( this.options.datas.timestamp );

      }

      this._loop ();

    }

    _destroy () {

      clearTimeout ( this.loopId );

    }

    /* LOOP */

    _loop ( seconds = 0 ) {

      if ( this.options.abort.enabled && seconds > this.options.abort.threshold ) return;

      this.loopId = this._delay ( function () {

        this._loop ( this._update ().next );

      }, seconds * 1000 );

    }

    /* UPDATE */

    _update () {

      let timeAgo = _.timeAgo ( this.options.timestamp );

      if ( this.options.title ) {

        this.$timeAgoElement.attr ( 'title', timeAgo.str );

      } else {

        this.$timeAgoElement.text ( timeAgo.str );

      }

      this._trigger ( 'change' );

      return timeAgo;

    }

    /* API OVERRIDES */

    enable () {

      super.enable ();

      this._loop ();

    }

    disable () {

      super.disable ();

      clearTimeout ( this.loopId );

    }

  }

  /* FACTORY */

  Factory.make ( TimeAgo, config );

}( Svelto.$, Svelto._, Svelto, Svelto.Widgets, Svelto.Factory, Svelto.Timer ));
<|endoftext|>


<commit_before>'use strict';

var isDate   = require('es5-ext/date/is-date')
  , copy     = require('es5-ext/object/copy')
  , assign   = require('es5-ext/object/assign')
  , d        = require('d/d')
  , DOMInput = require('./_controls/input')

  , defineProperties = Object.defineProperties
  , re = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}))?$/
  , castControlAttribute = DOMInput.prototype.castControlAttribute
  , Input;

Input = function (document, type/*, options*/) {
	DOMInput.apply(this, arguments);
	this.control.addEventListener('input', this.onChange, false);
};

Input.prototype = Object.create(DOMInput.prototype, {
	constructor: d(Input),
	controlAttributes: d(assign(copy(DOMInput.prototype.controlAttributes),
		{ autocomplete: true, list: true, max: true, min: true, readonly: true,
			required: true, step: true })),
	dbAttributes: d(assign(copy(DOMInput.prototype.dbAttributes),
		{ max: true, min: true, required: true, step: true })),
	dateAttributes: d({ min: true, max: true }),
	_render: d(function () {
		var input = this.control = this.dom = this.document.createElement('input');
		input.setAttribute('type', 'datetime-local');
	}),
	castControlAttribute: d(function (name, value) {
		if (this.dateAttributes[name]) {
			if (isDate(value)) value = this.type.toInputValue(value);
			else if (!isFinite(value)) value = null;
		}
		castControlAttribute.call(this, name, value);
	})
});

module.exports = exports = function (db) {
	defineProperties(db.DateTime, {
		fromInputValue: d(function (value) {
			var match;
			if (value == null) return null;
			value = value.trim();
			if (!value) return null;
			match = value.match(re);
			if (!match) return null;
			return this.normalize(new Date(Number(match[1]), Number(match[2]) - 1,
				Number(match[3]), match[4] ? Number(match[4]) : 0,
				match[5] ? Number(match[5]) : 0));
		}),
		toInputValue: d(function (value) {
			return (value == null) ? null : value.toISOString().slice(0, 16);
		}),
		DOMInput: d(Input)
	});
};

exports.Input = Input;
<commit_msg>Make sure to convert step to day step<commit_after>'use strict';

var isDate   = require('es5-ext/date/is-date')
  , copy     = require('es5-ext/object/copy')
  , assign   = require('es5-ext/object/assign')
  , d        = require('d/d')
  , DOMInput = require('./_controls/input')

  , defineProperties = Object.defineProperties, round = Math.round
  , re = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}))?$/
  , dayStep = 1000 * 60 * 60 * 24
  , castControlAttribute = DOMInput.prototype.castControlAttribute
  , Input;

Input = function (document, type/*, options*/) {
	DOMInput.apply(this, arguments);
	this.control.addEventListener('input', this.onChange, false);
};

Input.prototype = Object.create(DOMInput.prototype, {
	constructor: d(Input),
	controlAttributes: d(assign(copy(DOMInput.prototype.controlAttributes),
		{ autocomplete: true, list: true, max: true, min: true, readonly: true,
			required: true, step: true })),
	dbAttributes: d(assign(copy(DOMInput.prototype.dbAttributes),
		{ max: true, min: true, required: true, step: true })),
	dateAttributes: d({ min: true, max: true }),
	_render: d(function () {
		var input = this.control = this.dom = this.document.createElement('input');
		input.setAttribute('type', 'datetime-local');
	}),
	castControlAttribute: d(function (name, value) {
		if (this.dateAttributes[name]) {
			if (isDate(value)) value = this.type.toInputValue(value);
			else if (!isFinite(value)) value = null;
		}
		if ((name === 'step') && value && isFinite(value)) {
			value = round(value / dayStep);
			if (value === 1) value = null;
		}
		castControlAttribute.call(this, name, value);
	})
});

module.exports = exports = function (db) {
	defineProperties(db.DateTime, {
		fromInputValue: d(function (value) {
			var match;
			if (value == null) return null;
			value = value.trim();
			if (!value) return null;
			match = value.match(re);
			if (!match) return null;
			return this.normalize(new Date(Number(match[1]), Number(match[2]) - 1,
				Number(match[3]), match[4] ? Number(match[4]) : 0,
				match[5] ? Number(match[5]) : 0));
		}),
		toInputValue: d(function (value) {
			return (value == null) ? null : value.toISOString().slice(0, 16);
		}),
		DOMInput: d(Input)
	});
};

exports.Input = Input;
<|endoftext|>


<commit_before>var EnemyTypes = {
  outlaw: {
    name: 'outlaw',
    speed: 20,
    stateCounter: 2000,
    fireCounter: 1500,
    spread: 15,
    bulletLifespan: 1500,
    counters: 'bandito'
  },
  cowboy: {
    name: 'cowboy',
    speed: 30,
    stateCounter: 1500,
    fireCounter: 1000,
    spread: 20,
    bulletLifespan: 1300,
    counters: 'outlaw'
  },
  bandito: {
    name: 'bandito',
    speed: 15,
    stateCounter: 2500,
    fireCounter: 1000,
    spread: 10,
    bulletLifespan: 2000,
    counters: 'cowboy'
  }
};

var Enemy = function(
  game, group, bulletGroup, hitGroup, friendlyGroup,
  bgGroup,
  x, y, enemyType, isEnemy, wave) {
  var key = enemyType.name;
  if (!isEnemy) {
    key += '_ally';
  }
  Phaser.Sprite.call(this, game, x, y, key);
  group.add(this);
  game.physics.enable(this, Phaser.Physics.ARCADE);
  this.body.collideWorldBounds = true;
  this.body.bounce.set(1);
  this.anchor.setTo(0.5);
  this.enemyType = enemyType;
  this.isEnemy = isEnemy;
  this.wave = wave;
  this.captured = false;

  // animations
  this.animations.add('fire', [0, 1], 10, false);
  this.animations.add('idle', [2, 3], 2, true);
  this.animations.add('walk', [4, 5], 10, true);

  // State machine
  // Initialise state to 'entering' - running onto
  // the field
  this.state = 'entering';
  this.stateCounter = Math.random() * 500 + 500;
  this.animations.play('walk');
  if (this.isEnemy) {
    this.body.velocity.y = this.speed() * 2;
  } else {
    this.body.velocity.y = -this.speed() * 2;
  }
  this.fireCounter = 0;
  this.fireDirection = null;

  this.bulletGroup = bulletGroup;
  this.game = game;
  this.hitGroup = hitGroup;
  this.friendlyGroup = friendlyGroup;
  this.bgGroup = bgGroup;
};
Enemy.prototype = Object.create(Phaser.Sprite.prototype);
Enemy.prototype.constructor = Enemy;

Enemy.prototype.update = function() {
  if (!this.alive) {
    return;
  }
  if (this.captured) {
    return;
  }
  this.stateCounter -= this.game.time.elapsed;
  var stateChange = false;
  if (this.stateCounter <= 0) {
    // Switch state
    this.state = choose(['idle', 'move', 'fire']);
    this.stateCounter = this.enemyType.stateCounter;
    stateChange = true;
  }
  switch (this.state) {
    case 'entering':
    // State change managed in constructor
    break;
    case 'idle':
    if (stateChange) {
      this.body.velocity.setTo(0);
      this.animations.play('idle');
      this.stateCounter *= this.wave.idleStateCounter();
    }
    break;
    case 'move':
    if (stateChange) {
      var target = null;
      if (Math.random() < 0.5) {
        // Move towards an enemy
        target = this.getClosestTarget();
      }
      var v;
      if (target) {
        // Stay in front of the enemy
        var targetPosition = target.position.clone();
        if (this.isEnemy) {
          targetPosition.y -= 20;
        } else {
          // Friendlies more aggro
          //targetPosition.y += 20;
        }
        v = Phaser.Point.subtract(
          targetPosition, this.position
        );
        // Add a random offset
        v.add(
          (Math.random() - 0.5) * this.enemyType.spread,
          (Math.random() - 0.5) * this.enemyType.spread
        );
      } else {
        // Wander randomly
        v = new Phaser.Point(
          choose([-1, 1]), Math.random() - 0.5);
      }
      v = v.normalize();
      v.x *= this.speed();
      v.y *= this.speed();
      this.body.velocity = v;
      this.animations.play('walk');
    }
    break;
    case 'fire':
    if (stateChange) {
      // Find a new direction to fire
      // Randomly choose to fire in the general direction
      // of the player
      var target = null;
      if (Math.random() < 0.5) {
        // Fire at closest target
        target = this.getClosestTarget();
      }
      var targetPosition;
      if (!target) {
        // target a random point on the field
        if (this.isEnemy) {
          // Target lower half of field
          targetPosition = new Phaser.Point(
            Math.random() * SCREEN_WIDTH,
            (Math.random() / 2 + 0.5) * SCREEN_HEIGHT
          );
        } else {
          // Target upper half of field
          targetPosition = new Phaser.Point(
            Math.random() * SCREEN_WIDTH,
            Math.random() / 2 * SCREEN_HEIGHT
          );
        }
      } else {
        targetPosition = target.position.clone();
      }
      this.fireDirection = Phaser.Point.subtract(
        targetPosition, this.position
      );
      // Add a random offset
      if (!target ||
        target.key !== this.enemyType.counters) {
        this.fireDirection.add(
          (Math.random() - 0.5) * this.enemyType.spread,
          (Math.random() - 0.5) * this.enemyType.spread
        );
      }
      var magnitude = this.wave.bulletSpeed();
      // Hard counter
      if (target &&
        target.key === this.enemyType.counters) {
        var lifespan = this.wave.bulletLifespan() * this.enemyType.bulletLifespan;
        var idealMagnitude = this.fireDirection.getMagnitude() / (lifespan / 1000.0);
        if (idealMagnitude < magnitude * 2) {
          magnitude = idealMagnitude;
        }
      }
      this.fireDirection = this.fireDirection.normalize();
      this.fireDirection.setMagnitude(magnitude);
    }
    this.fire();
    this.body.velocity.setTo(0);
    break;
  }
};

Enemy.prototype.getClosestTarget = function() {
  var target = null;
  var minDistance = -1;
  this.friendlyGroup.forEachAlive(function(friendly) {
    var distance = this.game.physics.arcade.distanceBetween(
      this, friendly
    );
    if (target === null || minDistance > distance) {
      target = friendly;
      minDistance = distance;
    }
  }, this);
  return target;
};

Enemy.prototype.fire = function() {
  if (this.fireCounter > 0) {
    this.fireCounter -= this.game.time.elapsed;
  } else {
    this.fireCounter = this.wave.fireCounter() * this.enemyType.fireCounter;
    var bulletKey = 'enemy_bullet';
    var explosionKey = 'enemy_explosion';
    if (!this.isEnemy) {
      bulletKey = 'ally_bullet';
      explosionKey = 'ally_explosion';
    }
    new Bullet(
      this.game, this.bulletGroup, this.hitGroup,
      this.wave.bulletLifespan() * this.enemyType.bulletLifespan,
      bulletKey, explosionKey, this.x, this.y,
      this.fireDirection.x, this.fireDirection.y
    );
    this.animations.play('fire');
  }
};

Enemy.prototype.capture = function() {
  this.captured = true;
  this.body.velocity.setTo(0, LASSO_DY);
  this.lifespan = LASSO_LIFESPAN;
  this.animations.stop();
  this.frame = 6;
};

Enemy.prototype.killAndLeaveCorpse = function() {
  this.kill();
  // Leave a corpse on the background layer
  var corpse = this.game.make.sprite(
    this.x, this.y, this.key);
  corpse.anchor.setTo(0.5);
  corpse.animations.add('die', [6, 7], 2, false);
  corpse.animations.play('die');
  this.bgGroup.add(corpse);
};

Enemy.prototype.speed = function() {
  return this.wave.speed() * this.enemyType.speed;
};
<commit_msg>Tint corpses<commit_after>var EnemyTypes = {
  outlaw: {
    name: 'outlaw',
    speed: 20,
    stateCounter: 2000,
    fireCounter: 1500,
    spread: 15,
    bulletLifespan: 1500,
    counters: 'bandito'
  },
  cowboy: {
    name: 'cowboy',
    speed: 30,
    stateCounter: 1500,
    fireCounter: 1000,
    spread: 20,
    bulletLifespan: 1300,
    counters: 'outlaw'
  },
  bandito: {
    name: 'bandito',
    speed: 15,
    stateCounter: 2500,
    fireCounter: 1000,
    spread: 10,
    bulletLifespan: 2000,
    counters: 'cowboy'
  }
};

var Enemy = function(
  game, group, bulletGroup, hitGroup, friendlyGroup,
  bgGroup,
  x, y, enemyType, isEnemy, wave) {
  var key = enemyType.name;
  if (!isEnemy) {
    key += '_ally';
  }
  Phaser.Sprite.call(this, game, x, y, key);
  group.add(this);
  game.physics.enable(this, Phaser.Physics.ARCADE);
  this.body.collideWorldBounds = true;
  this.body.bounce.set(1);
  this.anchor.setTo(0.5);
  this.enemyType = enemyType;
  this.isEnemy = isEnemy;
  this.wave = wave;
  this.captured = false;

  // animations
  this.animations.add('fire', [0, 1], 10, false);
  this.animations.add('idle', [2, 3], 2, true);
  this.animations.add('walk', [4, 5], 10, true);

  // State machine
  // Initialise state to 'entering' - running onto
  // the field
  this.state = 'entering';
  this.stateCounter = Math.random() * 500 + 500;
  this.animations.play('walk');
  if (this.isEnemy) {
    this.body.velocity.y = this.speed() * 2;
  } else {
    this.body.velocity.y = -this.speed() * 2;
  }
  this.fireCounter = 0;
  this.fireDirection = null;

  this.bulletGroup = bulletGroup;
  this.game = game;
  this.hitGroup = hitGroup;
  this.friendlyGroup = friendlyGroup;
  this.bgGroup = bgGroup;
};
Enemy.prototype = Object.create(Phaser.Sprite.prototype);
Enemy.prototype.constructor = Enemy;

Enemy.prototype.update = function() {
  if (!this.alive) {
    return;
  }
  if (this.captured) {
    return;
  }
  this.stateCounter -= this.game.time.elapsed;
  var stateChange = false;
  if (this.stateCounter <= 0) {
    // Switch state
    this.state = choose(['idle', 'move', 'fire']);
    this.stateCounter = this.enemyType.stateCounter;
    stateChange = true;
  }
  switch (this.state) {
    case 'entering':
    // State change managed in constructor
    break;
    case 'idle':
    if (stateChange) {
      this.body.velocity.setTo(0);
      this.animations.play('idle');
      this.stateCounter *= this.wave.idleStateCounter();
    }
    break;
    case 'move':
    if (stateChange) {
      var target = null;
      if (Math.random() < 0.5) {
        // Move towards an enemy
        target = this.getClosestTarget();
      }
      var v;
      if (target) {
        // Stay in front of the enemy
        var targetPosition = target.position.clone();
        if (this.isEnemy) {
          targetPosition.y -= 20;
        } else {
          // Friendlies more aggro
          //targetPosition.y += 20;
        }
        v = Phaser.Point.subtract(
          targetPosition, this.position
        );
        // Add a random offset
        v.add(
          (Math.random() - 0.5) * this.enemyType.spread,
          (Math.random() - 0.5) * this.enemyType.spread
        );
      } else {
        // Wander randomly
        v = new Phaser.Point(
          choose([-1, 1]), Math.random() - 0.5);
      }
      v = v.normalize();
      v.x *= this.speed();
      v.y *= this.speed();
      this.body.velocity = v;
      this.animations.play('walk');
    }
    break;
    case 'fire':
    if (stateChange) {
      // Find a new direction to fire
      // Randomly choose to fire in the general direction
      // of the player
      var target = null;
      if (Math.random() < 0.5) {
        // Fire at closest target
        target = this.getClosestTarget();
      }
      var targetPosition;
      if (!target) {
        // target a random point on the field
        if (this.isEnemy) {
          // Target lower half of field
          targetPosition = new Phaser.Point(
            Math.random() * SCREEN_WIDTH,
            (Math.random() / 2 + 0.5) * SCREEN_HEIGHT
          );
        } else {
          // Target upper half of field
          targetPosition = new Phaser.Point(
            Math.random() * SCREEN_WIDTH,
            Math.random() / 2 * SCREEN_HEIGHT
          );
        }
      } else {
        targetPosition = target.position.clone();
      }
      this.fireDirection = Phaser.Point.subtract(
        targetPosition, this.position
      );
      // Add a random offset
      if (!target ||
        target.key !== this.enemyType.counters) {
        this.fireDirection.add(
          (Math.random() - 0.5) * this.enemyType.spread,
          (Math.random() - 0.5) * this.enemyType.spread
        );
      }
      var magnitude = this.wave.bulletSpeed();
      // Hard counter
      if (target &&
        target.key === this.enemyType.counters) {
        var lifespan = this.wave.bulletLifespan() * this.enemyType.bulletLifespan;
        var idealMagnitude = this.fireDirection.getMagnitude() / (lifespan / 1000.0);
        if (idealMagnitude < magnitude * 2) {
          magnitude = idealMagnitude;
        }
      }
      this.fireDirection = this.fireDirection.normalize();
      this.fireDirection.setMagnitude(magnitude);
    }
    this.fire();
    this.body.velocity.setTo(0);
    break;
  }
};

Enemy.prototype.getClosestTarget = function() {
  var target = null;
  var minDistance = -1;
  this.friendlyGroup.forEachAlive(function(friendly) {
    var distance = this.game.physics.arcade.distanceBetween(
      this, friendly
    );
    if (target === null || minDistance > distance) {
      target = friendly;
      minDistance = distance;
    }
  }, this);
  return target;
};

Enemy.prototype.fire = function() {
  if (this.fireCounter > 0) {
    this.fireCounter -= this.game.time.elapsed;
  } else {
    this.fireCounter = this.wave.fireCounter() * this.enemyType.fireCounter;
    var bulletKey = 'enemy_bullet';
    var explosionKey = 'enemy_explosion';
    if (!this.isEnemy) {
      bulletKey = 'ally_bullet';
      explosionKey = 'ally_explosion';
    }
    new Bullet(
      this.game, this.bulletGroup, this.hitGroup,
      this.wave.bulletLifespan() * this.enemyType.bulletLifespan,
      bulletKey, explosionKey, this.x, this.y,
      this.fireDirection.x, this.fireDirection.y
    );
    this.animations.play('fire');
  }
};

Enemy.prototype.capture = function() {
  this.captured = true;
  this.body.velocity.setTo(0, LASSO_DY);
  this.lifespan = LASSO_LIFESPAN;
  this.animations.stop();
  this.frame = 6;
};

Enemy.prototype.killAndLeaveCorpse = function() {
  this.kill();
  // Leave a corpse on the background layer
  var corpse = this.game.make.sprite(
    this.x, this.y, this.key);
  corpse.anchor.setTo(0.5);
  corpse.animations.add('die', [6, 7], 2, false);
  corpse.animations.play('die');
  corpse.tint = 0x666666;
  this.bgGroup.add(corpse);
};

Enemy.prototype.speed = function() {
  return this.wave.speed() * this.enemyType.speed;
};
<|endoftext|>


<commit_before>
// ----------------------------------------------------------------
// ----------------------------------------------------------------
// ----------------------------------------------------------------

(function(jsonList){
	// ----------------------------------------------------------------
	// ユーティリティ

	var util = (function(){
		var util = {};
		util.canvas = null;
		util.context = null;
		util.update = false;
		util.target = null;
		return util;
	})();

	// ----------------------------------------------------------------
	// ローダー

	var loader = (function(){
		var loader = {};
		loader.json = [];
		loader.imgs = [];

		// サーバと通信する
		loader.send = function(url, json, callback){
			var xhr = new XMLHttpRequest();
			xhr.open((json == null ? "GET" : "POST"), url, true);
			xhr.setRequestHeader("Content-Type", "application/json");
			xhr.responseType = "json";
			xhr.onreadystatechange = function(e){
				if(xhr.readyState == 4){
					if(xhr.status == 200 && xhr.response != null){
						// 通信成功
						callback(xhr.response);
					}else{
						// 通信失敗
						console.error("sendPost error");
					}
				}
			};
			xhr.send(json == null ? null : JSON.stringify(json));
		};

		// 読み込まれていない画像の読み込み
		loader.loadImage = function(json){
			for(var i = 0; i < json.length; i++){
				if(json[i].imgs == null){continue;}
				for(var j = 0; j < json[i]["imgs"].length; j++){
					var prefixId = json[i]["imgs"][j]["prefixId"];
					var prefix = json[i]["prefixes"][prefixId];
					var src = prefix + json[i]["imgs"][j]["src"];
					if(loader.imgs[src] != null){continue;}
					var img = new Image();
					img.onload = function(){util.update = true;};
					img.src = "/tool_image/" + src;
					loader.imgs[src] = img;
				}
			}
		};

		// json書式確認
		var checkType = function(type, obj){return (Object.prototype.toString.call(obj).slice(8, -1) === type);};
		var setError = function(err){console.error(err);};
		var validate = function(json){
			if(!checkType("Array", json)){setError("validateError json"); return false;}
			for(var i = 0; i < json.length; i++){
				// 書式確認
				if(!checkType("Object", json[i])){setError("validateError json[" + i + "]"); return false;}
				if(!checkType("String", json[i]["dst"])){setError("validateError json[" + i + "]['dst']"); return false;}
				if(!checkType("String", json[i]["tag"])){setError("validateError json[" + i + "]['tag']"); return false;}
				if(!checkType("Object", json[i]["prefixes"])){setError("validateError json[" + i + "]['prefixes']"); return false;}
				for(var j in json[i]["prefixes"]){if(!checkType("String", json[i]["prefixes"][j])){setError("validateError json[" + i + "]['prefixes']['" + j + "']"); return false;}}
				// 同一タグが既に存在するか確認
				var isDuplicate = false;
				for(var j = 0; j < i; j++){if(json[i]["tag"] == json[j]["tag"]){isDuplicate = true; break;}}
				if(isDuplicate){continue;}
				// 書式確認
				if(!checkType("Number", json[i]["width"])){setError("validateError json[" + i + "]['width']"); return false;}
				if(!checkType("Number", json[i]["height"])){setError("validateError json[" + i + "]['height']"); return false;}
				if(!checkType("Array", json[i]["imgs"])){setError("validateError json[" + i + "]['imgs']"); return false;}
				for(var j = 0; j < json[i]["imgs"].length; j++){
					if(!checkType("Object", json[i]["imgs"][j])){setError("validateError json[" + i + "]['imgs'][" + j + "]"); return false;}
					if(!checkType("String", json[i]["imgs"][j]["tag"])){setError("validateError json[" + i + "]['imgs'][" + j + "]['tag']"); return false;}
					if(!checkType("Number", json[i]["imgs"][j]["x"])){setError("validateError json[" + i + "]['imgs'][" + j + "]['x']"); return false;}
					if(!checkType("Number", json[i]["imgs"][j]["y"])){setError("validateError json[" + i + "]['imgs'][" + j + "]['y']"); return false;}
					if(json[i]["imgs"][j]["w"] != null && !checkType("Number", json[i]["imgs"][j]["w"])){setError("validateError json[" + i + "]['imgs'][" + j + "]['w']"); return false;}
					if(json[i]["imgs"][j]["h"] != null && !checkType("Number", json[i]["imgs"][j]["h"])){setError("validateError json[" + i + "]['imgs'][" + j + "]['h']"); return false;}
					if(json[i]["imgs"][j]["isTurn"] != null && !checkType("Boolean", json[i]["imgs"][j]["isTurn"])){setError("validateError json[" + i + "]['imgs'][" + j + "]['isTurn']"); return false;}
					if(!checkType("String", json[i]["imgs"][j]["prefixId"])){setError("validateError json[" + i + "]['imgs'][" + j + "]['prefixId']"); return false;}
					if(!checkType("String", json[i]["imgs"][j]["src"])){setError("validateError json[" + i + "]['imgs'][" + j + "]['src']"); return false;}
				}
			}
			return true;
		};

		// json設定
		loader.setJson = function(json){
			if(!validate(json)){return;}
			loader.json = json;
			loader.loadImage(json);

			// セレクトボックス作成
			var selects = "";
			for(var i = 0; i < json.length; i++){selects += "<option>" + json[i]["dst"] + "</option>"}
			document.getElementById("domSelectDst").innerHTML = selects;
		};

		return loader;
	})();

	// ----------------------------------------------------------------

	// 計算処理
	var calc = function(){
		if(util.target == null || util.target.dst != selectedDst){
			// DOM確認
			var domSelectDst = document.getElementById("domSelectDst");
			var selectedDst = domSelectDst.options[domSelectDst.selectedIndex].innerHTML;

			for(var i = 0; i < loader.json.length; i++){
				if(loader.json[i]["dst"] != selectedDst){continue;}
				// データコピー
				util.target = {};
				util.target.dst = loader.json[i]["dst"];
				util.target.tag = loader.json[i]["tag"];
				util.target.prefixes = loader.json[i]["prefixes"];
				if(loader.json[i]["imgs"] != null){
					util.target.width = loader.json[i]["width"];
					util.target.height = loader.json[i]["height"];
					util.target.imgs = loader.json[i]["imgs"];
				}else{
					for(var j = 0; j < i; j++){
						if(loader.json[i]["tag"] != loader.json[j]["tag"]){continue;}
						util.target.width = loader.json[j]["width"];
						util.target.height = loader.json[j]["height"];
						util.target.imgs = loader.json[j]["imgs"];
					}
				}
				// オブジェクトのディープコピー
				util.target.prefixes = JSON.parse(JSON.stringify(util.target.prefixes));
				util.target.imgs = JSON.parse(JSON.stringify(util.target.imgs));
				break;
			}
		}
	};

	// 情報保存ボタン押下時
	document.getElementById("domButtonSaveHeader").addEventListener("click", function(){
		// 情報作成
		var data = "// image data\n";
		var w = util.target.width;
		var h = util.target.height;
		data += "#define TEXSIZ_" + util.target.tag + "_W " + w + "\n";
		data += "#define TEXSIZ_" + util.target.tag + "_H " + h + "\n";
		data += "#define TEXSIZ_" + util.target.tag + "_WH " + w + ", " + h + "\n";
		for(var i = 0; i < util.target.imgs.length; i++){
			var tag = util.target.tag + "_" + util.target.imgs[i]["tag"];
			var prefixId = util.target.imgs[i]["prefixId"];
			var prefix = util.target.prefixes[prefixId];
			var src = prefix + util.target.imgs[i]["src"];
			var img = loader.imgs[src];
			var x = util.target.imgs[i]["x"];
			var y = util.target.imgs[i]["y"];
			var w = util.target.imgs[i]["w"] || img.width;
			var h = util.target.imgs[i]["h"] || img.height;
			data += "#define TEXPOS_" + tag + "_X " + x + "\n";
			data += "#define TEXPOS_" + tag + "_Y " + y + "\n";
			data += "#define TEXPOS_" + tag + "_W " + w + "\n";
			data += "#define TEXPOS_" + tag + "_H " + h + "\n";
			data += "#define TEXPOS_" + tag + "_XYWH " + x + ", " + y + ", " + w + ", " + h + "\n";
		}
		// 情報送信
		loader.send("/tool_image/save_text", {
			"path": util.target.tag + ".h",
			"data": data
		}, function(json){
			console.log("callback success");
		});
	});

	// 画像保存ボタン押下時
	document.getElementById("domButtonSaveImage").addEventListener("click", function(){
		// 画像送信
		loader.send("/tool_image/save_image", {
			"path": util.target.dst,
			"data": util.canvas.toDataURL()
		}, function(json){
			console.log("callback success");
		});
	});

	// DOM切り替え時処理
	document.getElementById("domSelectDst").addEventListener("change", function(){util.update = true;});
	document.getElementById("domSelectScale").addEventListener("change", function(){util.update = true;});
	document.getElementById("domToggleBack").addEventListener("change", function(){util.update = true;});

	// ----------------------------------------------------------------

	// 描画処理
	var draw = function(){
		if(!util.update){return;}
		util.update = false;

		// DOM確認
		var domSelectScale = document.getElementById("domSelectScale");
		var domToggleBack = document.getElementById("domToggleBack");
		var selectedScale = parseFloat(domSelectScale.options[domSelectScale.selectedIndex].value);
		var isBack = domToggleBack.checked;

		// キャンバス作成
		if(util.canvas == null || util.canvas.width != util.target.width || util.canvas.height != util.target.height){
			// キャンバス作成
			var canvasDiv = document.getElementById("domCanvas");
			if(util.canvas != null){canvasDiv.removeChild(util.canvas);}
			util.canvas = document.createElement("canvas");
			util.canvas.width = util.target.width;
			util.canvas.height = util.target.height;
			util.canvas.style.border = "solid 2px black"
			util.context = util.canvas.getContext("2d");
			canvasDiv.appendChild(util.canvas);
		}
		// キャンバス設定
		util.canvas.style.width = Math.floor(selectedScale * util.target.width) + "px";
		util.canvas.style.height = Math.floor(selectedScale * util.target.height) + "px";

		// 画面クリア
		util.context.clearRect(0, 0, util.canvas.width, util.canvas.height);

		// 画像描画
		for(var i = 0; i < util.target.imgs.length; i++){
			//var tag = util.target.tag + "_" + util.target.imgs[j]["tag"];
			var prefixId = util.target.imgs[i]["prefixId"];
			var prefix = util.target.prefixes[prefixId];
			var src = prefix + util.target.imgs[i]["src"];
			var img = loader.imgs[src];
			if(!img.complete){continue;}
			var isTurn = util.target.imgs[i]["isTurn"];
			var x = util.target.imgs[i]["x"] + (isTurn ? img.height : 0);
			var y = util.target.imgs[i]["y"];
			var w = util.target.imgs[i]["w"] || img.width;
			var h = util.target.imgs[i]["h"] || img.height;
			var r = (isTurn ? 90 : 0);
			util.context.save();
			util.context.translate(x, y);
			util.context.rotate(r / 180 * Math.PI);
			if(isBack){util.context.fillStyle = "black"; util.context.fillRect(0, 0, w, h);}
			util.context.drawImage(img, 0, 0, w, h);
			util.context.restore();
		}
	};

	// ----------------------------------------------------------------

	// メインループ
	var mainloop = function(){
		calc();
		draw();
		setTimeout(mainloop, 16);
	};

	// 読み込みが終わったら処理開始
	document.addEventListener("DOMContentLoaded", function(e){
		loader.setJson(jsonList);
		mainloop();
	});

	// ----------------------------------------------------------------
})(document.imageDataJsonList = document.imageDataJsonList || []);

// ----------------------------------------------------------------
// ----------------------------------------------------------------
// ----------------------------------------------------------------

<commit_msg>画像ツールエラー処理変更<commit_after>
// ----------------------------------------------------------------
// ----------------------------------------------------------------
// ----------------------------------------------------------------

(function(jsonList){
	// ----------------------------------------------------------------
	// ユーティリティ

	var util = (function(){
		var util = {};
		util.canvas = null;
		util.context = null;
		util.update = false;
		util.target = null;
		return util;
	})();

	// ----------------------------------------------------------------
	// ローダー

	var loader = (function(){
		var loader = {};
		loader.json = [];
		loader.imgs = [];

		// サーバと通信する
		loader.send = function(url, json, callback){
			var xhr = new XMLHttpRequest();
			xhr.open((json == null ? "GET" : "POST"), url, true);
			xhr.setRequestHeader("Content-Type", "application/json");
			xhr.responseType = "json";
			xhr.onreadystatechange = function(e){
				if(xhr.readyState == 4){
					if(xhr.status == 200 && xhr.response != null){
						// 通信成功
						callback(xhr.response);
					}else{
						// 通信失敗
						console.error("sendPost error");
					}
				}
			};
			xhr.send(json == null ? null : JSON.stringify(json));
		};

		// 読み込まれていない画像の読み込み
		loader.loadImage = function(json){
			for(var i = 0; i < json.length; i++){
				if(json[i].imgs == null){continue;}
				for(var j = 0; j < json[i]["imgs"].length; j++){
					var prefixId = json[i]["imgs"][j]["prefixId"];
					var prefix = json[i]["prefixes"][prefixId];
					var src = prefix + json[i]["imgs"][j]["src"];
					if(loader.imgs[src] != null){continue;}
					var img = new Image();
					img.onload = function(){util.update = true;};
					img.src = "/tool_image/" + src;
					loader.imgs[src] = img;
				}
			}
		};

		// json書式確認
		var checkType = function(type, obj){return (Object.prototype.toString.call(obj).slice(8, -1) === type);};

		var validate = function(json){
			var countJson = 0;
			var isErrorJson = false;
			if(!checkType("Array", json)){setError("validateError json"); isErrorJson = true;}
			else{
				for(var i = 0; i < json.length; i++){
					// データの書式確認
					var isErrorData = false;
					var setErrorData = function(err){console.error(err); isErrorData = true;};
					if(!checkType("Object", json[i])){setErrorData("validateError json[" + i + "]");}
					else{
						var isDuplicate = false;
						if(!checkType("String", json[i]["dst"])){setErrorData("validateError json[" + i + "]['dst']");}
						if(!checkType("String", json[i]["tag"])){setErrorData("validateError json[" + i + "]['tag']");}
						else{for(var j = 0; j < i; j++){if(json[i]["tag"] == json[j]["tag"]){isDuplicate = true; break;}}} // 同一タグが既に存在するか確認
						if(!checkType("Object", json[i]["prefixes"])){setErrorData("validateError json[" + i + "]['prefixes']");}
						else{for(var j in json[i]["prefixes"]){if(!checkType("String", json[i]["prefixes"][j])){setErrorData("validateError json[" + i + "]['prefixes']['" + j + "']");}}}
						if(!isDuplicate){
							// 同一タグが存在しなければ書式確認を続ける
							if(!checkType("Number", json[i]["width"])){setErrorData("validateError json[" + i + "]['width']");}
							if(!checkType("Number", json[i]["height"])){setErrorData("validateError json[" + i + "]['height']");}
							if(!checkType("Array", json[i]["imgs"])){setErrorData("validateError json[" + i + "]['imgs']");}
							else{
								for(var j = 0; j < json[i]["imgs"].length; j++){
									if(!checkType("Object", json[i]["imgs"][j])){setErrorData("validateError json[" + i + "]['imgs'][" + j + "]");}
									if(!checkType("String", json[i]["imgs"][j]["tag"])){setErrorData("validateError json[" + i + "]['imgs'][" + j + "]['tag']");}
									if(!checkType("Number", json[i]["imgs"][j]["x"])){setErrorData("validateError json[" + i + "]['imgs'][" + j + "]['x']");}
									if(!checkType("Number", json[i]["imgs"][j]["y"])){setErrorData("validateError json[" + i + "]['imgs'][" + j + "]['y']");}
									if(json[i]["imgs"][j]["w"] != null && !checkType("Number", json[i]["imgs"][j]["w"])){setErrorData("validateError json[" + i + "]['imgs'][" + j + "]['w']");}
									if(json[i]["imgs"][j]["h"] != null && !checkType("Number", json[i]["imgs"][j]["h"])){setErrorData("validateError json[" + i + "]['imgs'][" + j + "]['h']");}
									if(json[i]["imgs"][j]["isTurn"] != null && !checkType("Boolean", json[i]["imgs"][j]["isTurn"])){setErrorData("validateError json[" + i + "]['imgs'][" + j + "]['isTurn']");}
									if(!checkType("String", json[i]["imgs"][j]["prefixId"])){setErrorData("validateError json[" + i + "]['imgs'][" + j + "]['prefixId']");}
									if(!checkType("String", json[i]["imgs"][j]["src"])){setErrorData("validateError json[" + i + "]['imgs'][" + j + "]['src']");}
								}
							}
						}
					}

					if(!isErrorData){
						// 正しいデータ
						json[i]["isExist"] = true;
						countJson++;
					}else{
						// エラーデータ
						json[i]["isExist"] = false;
						isErrorJson = true;
					}
				}
			}
			//return !isErrorJson; // エラーデータを一つも許さない場合
			return (countJson > 0); // 一つ以上の正しいデータがあれば良い場合
		};

		// json設定
		loader.setJson = function(json){
			if(!validate(json)){return;}
			loader.json = json;
			loader.loadImage(json);

			// セレクトボックス作成
			var selects = "";
			for(var i = 0; i < json.length; i++){if(json[i]["isExist"]){selects += "<option>" + json[i]["dst"] + "</option>";}}
			document.getElementById("domSelectDst").innerHTML = selects;
		};

		return loader;
	})();

	// ----------------------------------------------------------------

	// 計算処理
	var calc = function(){
		// DOM確認
		var domSelectDst = document.getElementById("domSelectDst");
		var selectedDst = domSelectDst.options[domSelectDst.selectedIndex].innerHTML;

		if(util.target == null || util.target.dst != selectedDst){
			for(var i = 0; i < loader.json.length; i++){
				if(loader.json[i]["dst"] != selectedDst){continue;}
				if(!loader.json[i]["isExist"]){break;}
				// データコピー
				util.target = {};
				util.target.dst = loader.json[i]["dst"];
				util.target.tag = loader.json[i]["tag"];
				util.target.prefixes = loader.json[i]["prefixes"];
				if(loader.json[i]["imgs"] != null){
					util.target.width = loader.json[i]["width"];
					util.target.height = loader.json[i]["height"];
					util.target.imgs = loader.json[i]["imgs"];
				}else{
					for(var j = 0; j < i; j++){
						if(loader.json[i]["tag"] != loader.json[j]["tag"]){continue;}
						util.target.width = loader.json[j]["width"];
						util.target.height = loader.json[j]["height"];
						util.target.imgs = loader.json[j]["imgs"];
					}
				}
				// オブジェクトのディープコピー
				util.target.prefixes = JSON.parse(JSON.stringify(util.target.prefixes));
				util.target.imgs = JSON.parse(JSON.stringify(util.target.imgs));
				break;
			}
		}
	};

	// 情報保存ボタン押下時
	document.getElementById("domButtonSaveHeader").addEventListener("click", function(){
		// 情報作成
		var data = "// image data\n";
		var w = util.target.width;
		var h = util.target.height;
		data += "#define TEXSIZ_" + util.target.tag + "_W " + w + "\n";
		data += "#define TEXSIZ_" + util.target.tag + "_H " + h + "\n";
		data += "#define TEXSIZ_" + util.target.tag + "_WH " + w + ", " + h + "\n";
		for(var i = 0; i < util.target.imgs.length; i++){
			var tag = util.target.tag + "_" + util.target.imgs[i]["tag"];
			var prefixId = util.target.imgs[i]["prefixId"];
			var prefix = util.target.prefixes[prefixId];
			var src = prefix + util.target.imgs[i]["src"];
			var img = loader.imgs[src];
			var x = util.target.imgs[i]["x"];
			var y = util.target.imgs[i]["y"];
			var w = util.target.imgs[i]["w"] || img.width;
			var h = util.target.imgs[i]["h"] || img.height;
			data += "#define TEXPOS_" + tag + "_X " + x + "\n";
			data += "#define TEXPOS_" + tag + "_Y " + y + "\n";
			data += "#define TEXPOS_" + tag + "_W " + w + "\n";
			data += "#define TEXPOS_" + tag + "_H " + h + "\n";
			data += "#define TEXPOS_" + tag + "_XYWH " + x + ", " + y + ", " + w + ", " + h + "\n";
		}
		// 情報送信
		loader.send("/tool_image/save_text", {
			"path": util.target.tag + ".h",
			"data": data
		}, function(json){
			console.log("callback success");
		});
	});

	// 画像保存ボタン押下時
	document.getElementById("domButtonSaveImage").addEventListener("click", function(){
		// 画像送信
		loader.send("/tool_image/save_image", {
			"path": util.target.dst,
			"data": util.canvas.toDataURL()
		}, function(json){
			console.log("callback success");
		});
	});

	// DOM切り替え時処理
	document.getElementById("domSelectDst").addEventListener("change", function(){util.update = true;});
	document.getElementById("domSelectScale").addEventListener("change", function(){util.update = true;});
	document.getElementById("domToggleBack").addEventListener("change", function(){util.update = true;});

	// ----------------------------------------------------------------

	// 描画処理
	var draw = function(){
		if(!util.update){return;}
		util.update = false;

		// DOM確認
		var domSelectScale = document.getElementById("domSelectScale");
		var domToggleBack = document.getElementById("domToggleBack");
		var selectedScale = parseFloat(domSelectScale.options[domSelectScale.selectedIndex].value);
		var isBack = domToggleBack.checked;

		// キャンバス作成
		if(util.canvas == null || util.canvas.width != util.target.width || util.canvas.height != util.target.height){
			// キャンバス作成
			var canvasDiv = document.getElementById("domCanvas");
			if(util.canvas != null){canvasDiv.removeChild(util.canvas);}
			util.canvas = document.createElement("canvas");
			util.canvas.width = util.target.width;
			util.canvas.height = util.target.height;
			util.canvas.style.border = "solid 2px black"
			util.context = util.canvas.getContext("2d");
			canvasDiv.appendChild(util.canvas);
		}
		// キャンバス設定
		util.canvas.style.width = Math.floor(selectedScale * util.target.width) + "px";
		util.canvas.style.height = Math.floor(selectedScale * util.target.height) + "px";

		// 画面クリア
		util.context.clearRect(0, 0, util.canvas.width, util.canvas.height);

		// 画像描画
		for(var i = 0; i < util.target.imgs.length; i++){
			//var tag = util.target.tag + "_" + util.target.imgs[j]["tag"];
			var prefixId = util.target.imgs[i]["prefixId"];
			var prefix = util.target.prefixes[prefixId];
			var src = prefix + util.target.imgs[i]["src"];
			var img = loader.imgs[src];
			if(!img.complete){continue;}
			var isTurn = util.target.imgs[i]["isTurn"];
			var x = util.target.imgs[i]["x"] + (isTurn ? img.height : 0);
			var y = util.target.imgs[i]["y"];
			var w = util.target.imgs[i]["w"] || img.width;
			var h = util.target.imgs[i]["h"] || img.height;
			var r = (isTurn ? 90 : 0);
			util.context.save();
			util.context.translate(x, y);
			util.context.rotate(r / 180 * Math.PI);
			if(isBack){util.context.fillStyle = "black"; util.context.fillRect(0, 0, w, h);}
			util.context.drawImage(img, 0, 0, w, h);
			util.context.restore();
		}
	};

	// ----------------------------------------------------------------

	// メインループ
	var mainloop = function(){
		calc();
		draw();
		setTimeout(mainloop, 16);
	};

	// 読み込みが終わったら処理開始
	document.addEventListener("DOMContentLoaded", function(e){
		loader.setJson(jsonList);
		mainloop();
	});

	// ----------------------------------------------------------------
})(document.imageDataJsonList = document.imageDataJsonList || []);

// ----------------------------------------------------------------
// ----------------------------------------------------------------
// ----------------------------------------------------------------

<|endoftext|>


<commit_before>var paste = document.getElementById("paste");
var pasteBtn = document.getElementById("paste-btn");
var browse = document.getElementById('browse');

window.addEventListener('dragenter', dragNOP, false);
window.addEventListener('dragleave', dragNOP, false);
window.addEventListener('dragover', dragNOP, false);
window.addEventListener('drop', handleDragDrop, false);

document.querySelector(".target").addEventListener("click", function(e) {
    e.preventDefault();
    browse.click();
});

browse.addEventListener("change", function(e) {
    for (var i = 0; i < browse.files.length; i++) {
        var f = browse.files[i];
        var progress = addRow(f);
        uploadFile(f, progress);
    }
});

document.getElementById("reset-key").addEventListener("click", function(e) {
    e.target.setAttribute("disabled", "disabled");
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/api/resetkey");
    xhr.onload = function() {
        e.target.removeAttribute("disabled");
        var key = JSON.parse(xhr.responseText).key;
        var keys = document.querySelectorAll(".apikey");
        for (var i = 0; i < keys.length; i++) {
            keys[i].textContent = key;
        }
        document.getElementById("syntaxKey").innerHTML =
            "curl \\\n    -F key=" + key + " \\\n    -F file=@example.png \\\n    " + window.root + "/api/upload";
        window.api_key = key;
        var q = document.getElementById("qrcode");
        q.innerHTML = "";
        new QRCode(q, "srht:" + window.api_key);
    };
    var form = new FormData();
    form.append("key", window.api_key);
    xhr.send(form);
});

function addRow(file) {
    var row = document.createElement("tr");
    var name = document.createElement("td");
    name.textContent = file.name;
    var progressCell = document.createElement("td");
    var progress = document.createElement("div");
    progressCell.appendChild(progress);
    progress.className = "progress";
    var progressBar = document.createElement("div");
    progressBar.style.width = "0%";
    progressBar.textContent = "0%";
    progressBar.className = "progress-bar";
    progress.appendChild(progressBar);
    row.appendChild(name);
    row.appendChild(progressCell);
    document.getElementById("files").appendChild(row);
    return progressCell;
}

function dragNOP(e) {
    e.stopPropagation();
    e.preventDefault();
}

function handleDragDrop(e) {
    dragNOP(e);
    for (var i = 0; i < e.dataTransfer.files.length; i++) {
        var file = e.dataTransfer.files[i];
        var progress = addRow(file);
        uploadFile(file, progress);
    }
}

paste.addEventListener("keydown", function() {
    pasteBtn.style.display = 'block';
});
pasteBtn.addEventListener("click", function(e) {
    e.preventDefault();
    var blob = new Blob([paste.value], {type: "text/plain"});
    var file = new File([blob], "paste.txt");
    var progress = addRow(file);
    uploadFile(file, progress);
});

function uploadFile(file, progress) {
    var bar = progress.querySelector(".progress-bar");
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/api/upload");
    xhr.onload = function() {
        var response = JSON.parse(xhr.responseText);
        progress.innerHTML = "" + response.url + "";
    };
    xhr.upload.onprogress = function(e) {
        if (e.lengthComputable) {
            var progress = Math.floor((e.loaded / e.total) * 100);
            bar.style.width = progress + "%";
            bar.textContent = progress + "%";
        }
    };
    var form = new FormData();
    form.append("key", window.api_key);
    form.append("file", file);
    xhr.send(form);
}

new QRCode(document.getElementById("qrcode"), "srht:" + window.api_key);
<commit_msg>Expand upon qr code<commit_after>var paste = document.getElementById("paste");
var pasteBtn = document.getElementById("paste-btn");
var browse = document.getElementById('browse');

window.addEventListener('dragenter', dragNOP, false);
window.addEventListener('dragleave', dragNOP, false);
window.addEventListener('dragover', dragNOP, false);
window.addEventListener('drop', handleDragDrop, false);

document.querySelector(".target").addEventListener("click", function(e) {
    e.preventDefault();
    browse.click();
});

browse.addEventListener("change", function(e) {
    for (var i = 0; i < browse.files.length; i++) {
        var f = browse.files[i];
        var progress = addRow(f);
        uploadFile(f, progress);
    }
});

document.getElementById("reset-key").addEventListener("click", function(e) {
    e.target.setAttribute("disabled", "disabled");
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/api/resetkey");
    xhr.onload = function() {
        e.target.removeAttribute("disabled");
        var key = JSON.parse(xhr.responseText).key;
        var keys = document.querySelectorAll(".apikey");
        for (var i = 0; i < keys.length; i++) {
            keys[i].textContent = key;
        }
        document.getElementById("syntaxKey").innerHTML =
            "curl \\\n    -F key=" + key + " \\\n    -F file=@example.png \\\n    " + window.root + "/api/upload";
        window.api_key = key;
        var q = document.getElementById("qrcode");
        q.innerHTML = "";
        new QRCode(q, "srht:" + window.location.hostname + ":" + window.api_key);
    };
    var form = new FormData();
    form.append("key", window.api_key);
    xhr.send(form);
});

function addRow(file) {
    var row = document.createElement("tr");
    var name = document.createElement("td");
    name.textContent = file.name;
    var progressCell = document.createElement("td");
    var progress = document.createElement("div");
    progressCell.appendChild(progress);
    progress.className = "progress";
    var progressBar = document.createElement("div");
    progressBar.style.width = "0%";
    progressBar.textContent = "0%";
    progressBar.className = "progress-bar";
    progress.appendChild(progressBar);
    row.appendChild(name);
    row.appendChild(progressCell);
    document.getElementById("files").appendChild(row);
    return progressCell;
}

function dragNOP(e) {
    e.stopPropagation();
    e.preventDefault();
}

function handleDragDrop(e) {
    dragNOP(e);
    for (var i = 0; i < e.dataTransfer.files.length; i++) {
        var file = e.dataTransfer.files[i];
        var progress = addRow(file);
        uploadFile(file, progress);
    }
}

paste.addEventListener("keydown", function() {
    pasteBtn.style.display = 'block';
});
pasteBtn.addEventListener("click", function(e) {
    e.preventDefault();
    var blob = new Blob([paste.value], {type: "text/plain"});
    var file = new File([blob], "paste.txt");
    var progress = addRow(file);
    uploadFile(file, progress);
});

function uploadFile(file, progress) {
    var bar = progress.querySelector(".progress-bar");
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/api/upload");
    xhr.onload = function() {
        var response = JSON.parse(xhr.responseText);
        progress.innerHTML = "" + response.url + "";
    };
    xhr.upload.onprogress = function(e) {
        if (e.lengthComputable) {
            var progress = Math.floor((e.loaded / e.total) * 100);
            bar.style.width = progress + "%";
            bar.textContent = progress + "%";
        }
    };
    var form = new FormData();
    form.append("key", window.api_key);
    form.append("file", file);
    xhr.send(form);
}

new QRCode(document.getElementById("qrcode"), "srht:" + window.api_key);
<|endoftext|>


<commit_before>$('.nav-link').click(function(e) {
  if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')) {
    var target = $(this.hash);
    target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
       if (target.length) {
         $('#parallax-box').scrollTo(target);
        return false;
    }
  }
  e.preventDefault();
});

$('.speaker-link').click(function(e){
  window.open('http://twitter.com/'+this.id);
});
// $('.speaker-link-pg').click(function(e){
//   window.open('https://www.cs.auckland.ac.nz/~pgut001/');
// });

$('.tickets-box table').click(function(){
  window.open('https://codemania-2017.lilregie.com/');
});<commit_msg>Don’t need it. Delete it.<commit_after>$('.nav-link').click(function(e) {
  if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')) {
    var target = $(this.hash);
    target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
       if (target.length) {
         $('#parallax-box').scrollTo(target);
        return false;
    }
  }
  e.preventDefault();
});

$('.speaker-link').click(function(e){
  window.open('http://twitter.com/'+this.id);
});

$('.tickets-box table').click(function(){
  window.open('https://codemania-2017.lilregie.com/');
});<|endoftext|>


<commit_before>// CONSTANTS
var fieldWidth = 54, // the width of every of the 24 fields
    offsetBetweenFields = 5, // between small field(white) and big field(dark)
    boardSide = 324, // the width of one side of the board
    boardBar = 34, // the thing between the board sides in pixels
    boardFrameOffset = 18, // the offset in the corners between the board itself and the frame in pixels
    checkerHeight = 42, // the height of the checker after being fully drawn in pixels
    checkerRadius = 21, // full width = 44; outer stroke 1px
    checkerInnerCircleRadius = 10;

// GAME ELEMENTS - canvas, ctx, keystate
var canvas,
    ctx,
    clickedField;

// DEFINING GAME OBJECTS - player, checker, dice, board(not if it is just a background) ...
var player = (function () {
    var player = {
        init: function (name, color) {
            this.name = name;
            this.color = color;
            this.currentCheckersCount = 15;
            this.hasHitChecker = false;
            this.hitCheckersCount = 0;
            this.canTakeChecker = false;
            return this;
        },
        tookChecker: function () {
            // TODO: Validate depending on call.
            this.currentCheckersCount -= 1;
        }
    };

    Object.defineProperty(player, 'name', {
        get: function () {
            return this._name;
        },
        set: function (value) {
            //TODO: Validate
            this._name = value;
        }
    });

    Object.defineProperty(player, 'currentCheckersCount', {
        get: function () {
            return this._currentCheckersCount;
        },
        set: function (value) {
            //TODO: Validate
            this._currentCheckersCount = value;
        }
    });

    Object.defineProperty(player, 'color', {
        get: function () {
            return this._color;
        },
        set: function (value) {
            // TODO: Validate
            this._color = value;
        }
    });

    Object.defineProperty(player, 'hasHitChecker', {
        get: function () {
            return this._hasHitChecker;
        },
        set: function (value) {
            // TODO: Validate
            this._hasHitChecker = value;
        }
    });

    Object.defineProperty(player, 'hitCheckersCount', {
        get: function () {
            return this._hitCheckersCount;
        },
        set: function (value) {
            // TODO: Validate
            this._hitCheckersCount = value;
        }
    });

    Object.defineProperty(player, 'canTakeChecker', {
        get: function () {
            return this._canTakeChecker;
        },
        set: function (value) {
            // TODO: Validate
            this._canTakeChecker = value;
        }
    });

    return player;
}());

var field = function () { /* the board is filled with 24 fields,
 IT IS A RESPONSIBILITY OF THE FIELDS TO GIVE COORDINATES TO THE CHECKERS, WITH RESPECT TO THEIR OWN X,Y COORDINATES
 IN THIS WAY WE DON'T CARE HOW TO STACK CHECKERS AND TO TAKE INTO ACCOUNT THEIR Xs AND Ys,
 THE FIELD JUST STACKS THEM DEPENDING ON THE COUNT IT IS HOLDING, GIVING HIS OWN X, Y */
    var field = {
        init: function (x, y) {
            this.checkers = [];
            this.x = x;
            this.y = y;
            /* if field is 0~11 y = canvas.height - boardFrameOffset; else y = boardFrameOffset;
             this is needed only for knowing at which side are we, so we can draw the checkers properly */
            return this;
        },
        addChecker: function (checker) {
            this.checkers.push(checker);
        },
        removeChecker: function () {
            this.checkers.pop();
        },
        draw: function () {
            var count = this.checkers.length, // count checkers, if more than 5 draw stacked
                i,
                fieldCenter;
            for (i = 0; i < count; i += 1) {
                if (i > 4) {
                    // draw stacked
                    ctx.save();
                    ctx.fillStyle = 'red';
                    ctx.font = "12px Verdana";
                    ctx.fillText('x' + (i - 3), (this.x - 6) + fieldWidth / 2, this.y + checkerRadius + 4);
                    // x2 for the 6th checker because it represents how many checkers are stacked
                    // 6 and 4 are just a magic numbers to position the text in the center of the checker, because the text has also its own offset
                    ctx.restore();
                } else {
                    if (this.y < canvas.height / 2) { // if field is in the upper half of the board, should stack them up to bottom
                        fieldCenter = this.x + (fieldWidth / 2); // x coordinate for the checker center
                        this.checkers[i].draw(fieldCenter, this.y + i * checkerHeight + checkerRadius);
                    } else { // when the field is in the lower part, should stack them from bottom to up
                        fieldCenter = this.x + (fieldWidth / 2); // x coordinate for the checker center
                        this.checkers[i].draw(fieldCenter, this.y - i * checkerHeight + checkerRadius)
                    }
                }
            }
        }
    };

    Object.defineProperty(field, 'checkers', {
        get: function () {
            return this._checkers;
        },
        set: function (value) {
            this._checkers = value;
        }
    });

    Object.defineProperty(field, 'x', {
        get: function () {
            return this._x;
        },
        set: function (value) {
            this._x = value;
        }
    });

    Object.defineProperty(field, 'y', {
        get: function () {
            return this._y;
        },
        set: function (value) {
            this._y = value;
        }
    });

    return field;
}();

var checker = function () {
    var checker = {
        init: function (color) {
            this.color = color; // can be only 'black' or 'white'
            return this;
        },
        draw: function (x, y) {
            ctx.save();
            if (this.color === 'black') {
                ctx.strokeStyle = 'white';
            }
            // outer circle
            ctx.beginPath();
            ctx.fillStyle = this.color;
            ctx.lineWidth = 1;
            ctx.arc(x, y, checkerRadius, 0, 2 * Math.PI);
            ctx.fill();
            ctx.stroke();

            // inner circle
            ctx.beginPath();
            ctx.strokeStyle = '#808080';
            ctx.lineWidth = 2;
            ctx.arc(x, y, checkerInnerCircleRadius, 0, 2 * Math.PI);
            ctx.stroke();

            ctx.restore();
        }
    };

    Object.defineProperty(checker, 'color', {
        get: function () {
            return this._color;
        },
        set: function (value) {
            this._color = value;
        }
    });

    return checker;
}();

var board = function () {
    var bottomSideAllSet, // flag to indicate that we must start with a field having 5 checkers in it(the 12th field)
    // at bottom we start with  12. 5 3 5 2 .23
    // at top we start with     11. 5 3 5 2 .0
        patternToPutColors = []; // colors are inverted in the opposite sides of the field, sorry it truly looks ugly..

    var gameboard = {
        init: function () {
            this.fields = [];
            bottomSideAllSet = false;
            patternToPutColors[0] = 1;
            patternToPutColors[5] = 0;
            patternToPutColors[7] = 0;
            patternToPutColors[11] = 1;
            var self = this;
            setupHalfGameBoard(self, bottomSideAllSet);
            bottomSideAllSet = true;
            setupHalfGameBoard(self, bottomSideAllSet);
            return this;
        },
        addField: function (value) {
            this.fields.push(value);
        }
    };

    function setupHalfGameBoard(self, bottomSideAllSet) {
        var i,
            len,
            x,
            y,
            currentField,
            patternOfTheNumberOfCheckersToPut = [2, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 5],// on 0 field put 2, on field 1 put 0, etc.. the setup rules..
            j,  // needed to go trough the patternToPutCheckers
            colorToPut,
            countPassedFields = 0; // needed to add a boardBar offset to the X coordinate

        if (bottomSideAllSet) {
            i = 12;
            j = 11;
        } else {
            i = 0;
            j = 0;
        }
        for (len = i + 12; i < len; i += 1, (i >= 12) ? j -= 1 : j += 1, countPassedFields += 1) {
            if (countPassedFields >= 6) {  // after every 6 fields we must consider the boardBar also, in both the upper and the lower side of the board
                x = (!bottomSideAllSet) * canvas.width - boardFrameOffset - ((countPassedFields + !bottomSideAllSet) * fieldWidth) - boardBar;
            } else {
                x = (!bottomSideAllSet) * canvas.width - boardFrameOffset - ((countPassedFields + !bottomSideAllSet) * fieldWidth);
                // if we are at the bottom we must consider the canvas width and to subtract from it
            }
            y = (!bottomSideAllSet) * (canvas.height - checkerHeight) - boardFrameOffset;

            if (x < 0) { // because we are extracting everything from the canvas width or height
                x *= -1; // when we are at the bottom side, we start from the MOST-RIGHT field(our 0 field) so canvas height, and width must be considered,
            }            // while when we are at the upper side of the board, the MOST-LEFT field(the 12th field) must be filled first,
            if (y < 0) { // so canvas width and height in the upper side are 0, and we get a negative number but we actually need that number to be positive,
                y *= -1; // to go to the right.. to fill the rest of the fields.. it's all because of how the fields position in backgammon
            }
            if (patternToPutColors[j] === 1) {
                colorToPut = 'white';
                patternToPutColors[j] = 0; // swapping the pattern for the next half of the board
            } else if (patternToPutColors[j] === 0) {
                colorToPut = 'black';
                patternToPutColors[j] = 1;
            }
            currentField = Object.create(field).init(x, y); // creating one of the 24 fields in the board
            putCheckersOnField(patternOfTheNumberOfCheckersToPut[j], currentField, colorToPut); // assigning checkers to it with respect to the Backgammon setup rules
            self.addField(currentField); // adding the field to the board, used 'self' because the scope of this has changed
        }
    }

    function putCheckersOnField(checkersToPut, givenField, color) {
        var i;
        for (i = 0; i < checkersToPut; i += 1) {
            givenField.addChecker(Object.create(checker).init(color));
        }
    }

    return gameboard;
}();

// function newGame() or main()
// create initiate and append game canvas
// keep track of keyboard presses
// INITIATE GAME OBJECTS;
// GAME LOOP, STARTS THE ANIMATION;
function newGame() {
    canvas = document.getElementById('board-canvas');
    ctx = canvas.getContext('2d');

    // INIT GAME OBJECTS, SETS UP GAME
    board.init();
}
newGame();
// canvas.addEventListener('click', findPressedField);

function play() {
    // GAME LOOP - game logic, updating the fields, players turns, dices etc. Update update update
    // change fields depending on how the player interact(key presses)
    canvas.addEventListener('click', findPressedField);

    // calls function update()
    // checkForWinner etc... this all should go here

    // draws every time and if we have changes its ok
    draw();
    requestAnimationFrame(play);
}
play();

function findPressedField(event) {
    var x = event.offsetX;
    var y = event.offsetY;
    if (((x < canvas.width - boardFrameOffset && x > boardFrameOffset + boardSide + boardBar) ||
        (x < boardFrameOffset + boardSide && x > boardFrameOffset)) &&
        (y < canvas.height - boardFrameOffset && y > boardFrameOffset)) { // ignoring every piece of the frame
        var i = 0,
            len,
            foundField,
            upperSide = false;
        // determining in which quadrant are we exactly
        if (y < canvas.height / 2 && y > boardFrameOffset) {
            i = 12; // last field of the upper side, last because if we take the first our condition in the for-loop will be achieved every time
            upperSide = true;
        } else {
            i = 0; // first field of the lower side
        }
        if (x < boardSide + boardFrameOffset && x > boardFrameOffset) { // left side of the board
            if (i === 0) { // bottom left side
                i = 6;
            } else { // upper left side
                i = 12;
            }
        } else { // right side of the board
            if (i === 0) { // bottom right side
                i = 0;
            } else { // upper left side
                i = 18; // again last of the array
            }
        }
        for (len = i + 6; i < len; i += 1) {
            var currentField = board.fields[i];
            if (upperSide) { /* its all because of the fields..
             the start position of the lower side is most-right and it starts from 0,
             while the start position of the upper side is the most-left which confuses the algorithm */
                var previousField = board.fields[i - 1];
                if (currentField.x > x) {
                    foundField = previousField;
                    break;
                }
            } else {
                if (x > currentField.x) {
                    foundField = currentField;
                    break;
                }
            }
        }
        if (typeof foundField === 'undefined') { // when we are going out of the for-in and the condition for the upperSide is not met,
            // because we take the previous field element we could not get to check the last one
            foundField = board.fields[len - 1];
        }
        clickedField = foundField;
        console.log(clickedField); // testing purposes
    }
}

function draw() {
    board.fields.forEach(function (field) {
        field.draw(); // we redraw every field again
    });
}<commit_msg>small rendering changes<commit_after>// CONSTANTS
var fieldWidth = 54, // the width of every of the 24 fields
    offsetBetweenFields = 5, // between small field(white) and big field(dark)
    boardSide = 324, // the width of one side of the board
    boardBar = 34, // the thing between the board sides in pixels
    boardFrameOffset = 18, // the offset in the corners between the board itself and the frame in pixels
    checkerHeight = 42, // the height of the checker after being fully drawn in pixels
    checkerRadius = 21, // full width = 44; outer stroke 1px
    checkerInnerCircleRadius = 10;

// GAME ELEMENTS - canvas, ctx, keystate
var canvas,
    ctx,
    clickedField;

// DEFINING GAME OBJECTS - player, checker, dice, board(not if it is just a background) ...
var player = (function () {
    var player = {
        init: function (name, color) {
            this.name = name;
            this.color = color;
            this.currentCheckersCount = 15;
            this.hasHitChecker = false;
            this.hitCheckersCount = 0;
            this.canTakeChecker = false;
            return this;
        },
        tookChecker: function () {
            // TODO: Validate depending on call.
            this.currentCheckersCount -= 1;
        }
    };

    Object.defineProperty(player, 'name', {
        get: function () {
            return this._name;
        },
        set: function (value) {
            //TODO: Validate
            this._name = value;
        }
    });

    Object.defineProperty(player, 'currentCheckersCount', {
        get: function () {
            return this._currentCheckersCount;
        },
        set: function (value) {
            //TODO: Validate
            this._currentCheckersCount = value;
        }
    });

    Object.defineProperty(player, 'color', {
        get: function () {
            return this._color;
        },
        set: function (value) {
            // TODO: Validate
            this._color = value;
        }
    });

    Object.defineProperty(player, 'hasHitChecker', {
        get: function () {
            return this._hasHitChecker;
        },
        set: function (value) {
            // TODO: Validate
            this._hasHitChecker = value;
        }
    });

    Object.defineProperty(player, 'hitCheckersCount', {
        get: function () {
            return this._hitCheckersCount;
        },
        set: function (value) {
            // TODO: Validate
            this._hitCheckersCount = value;
        }
    });

    Object.defineProperty(player, 'canTakeChecker', {
        get: function () {
            return this._canTakeChecker;
        },
        set: function (value) {
            // TODO: Validate
            this._canTakeChecker = value;
        }
    });

    return player;
}());

var field = function () { /* the board is filled with 24 fields,
 IT IS A RESPONSIBILITY OF THE FIELDS TO GIVE COORDINATES TO THE CHECKERS, WITH RESPECT TO THEIR OWN X,Y COORDINATES
 IN THIS WAY WE DON'T CARE HOW TO STACK CHECKERS AND TO TAKE INTO ACCOUNT THEIR Xs AND Ys,
 THE FIELD JUST STACKS THEM DEPENDING ON THE COUNT IT IS HOLDING, GIVING HIS OWN X, Y */
    var field = {
        init: function (x, y) {
            this.checkers = [];
            this.x = x;
            this.y = y;
            /* if field is 0~11 y = canvas.height - boardFrameOffset; else y = boardFrameOffset;
             this is needed only for knowing at which side are we, so we can draw the checkers properly */
            return this;
        },
        addChecker: function (checker) {
            this.checkers.push(checker);
        },
        removeChecker: function () {
            this.checkers.pop();
        },
        draw: function () {
            var count = this.checkers.length, // count checkers, if more than 5 draw stacked
                i,
                fieldCenter;
            for (i = 0; i < count; i += 1) {
                if (i > 4) {
                    // draw stacked
                    ctx.save();
                    ctx.fillStyle = 'red';
                    ctx.font = "12px Verdana";
                    ctx.fillText('x' + (i - 3), (this.x - 6) + fieldWidth / 2, this.y + checkerRadius + 4);
                    // x2 for the 6th checker because it represents how many checkers are stacked
                    // 6 and 4 are just a magic numbers to position the text in the center of the checker, because the text has also its own offset
                    ctx.restore();
                } else {
                    if (this.y < canvas.height / 2) { // if field is in the upper half of the board, should stack them up to bottom
                        fieldCenter = this.x + (fieldWidth / 2); // x coordinate for the checker center
                        this.checkers[i].draw(fieldCenter, this.y + i * checkerHeight + checkerRadius + 2); /* +2 just to look better,
                                                                                the checker from the board frame */
                    } else { // when the field is in the lower part, should stack them from bottom to up
                        fieldCenter = this.x + (fieldWidth / 2); // x coordinate for the checker center
                        this.checkers[i].draw(fieldCenter, this.y - i * checkerHeight + checkerRadius - 2); // -2 just to look better
                    }
                }
            }
        }
    };

    Object.defineProperty(field, 'checkers', {
        get: function () {
            return this._checkers;
        },
        set: function (value) {
            this._checkers = value;
        }
    });

    Object.defineProperty(field, 'x', {
        get: function () {
            return this._x;
        },
        set: function (value) {
            this._x = value;
        }
    });

    Object.defineProperty(field, 'y', {
        get: function () {
            return this._y;
        },
        set: function (value) {
            this._y = value;
        }
    });

    return field;
}();

var checker = function () {
    var checker = {
        init: function (color) {
            this.color = color; // can be only 'black' or 'white'
            return this;
        },
        draw: function (x, y) {
            ctx.save();
            if (this.color === 'black') {
                ctx.strokeStyle = 'white';
            }
            // outer circle
            ctx.beginPath();
            ctx.fillStyle = this.color;
            ctx.lineWidth = 1;
            ctx.arc(x, y, checkerRadius, 0, 2 * Math.PI);
            ctx.fill();
            ctx.stroke();

            // inner circle
            ctx.beginPath();
            ctx.strokeStyle = '#808080';
            ctx.lineWidth = 2;
            ctx.arc(x, y, checkerInnerCircleRadius, 0, 2 * Math.PI);
            ctx.stroke();

            ctx.restore();
        }
    };

    Object.defineProperty(checker, 'color', {
        get: function () {
            return this._color;
        },
        set: function (value) {
            this._color = value;
        }
    });

    return checker;
}();

var board = function () {
    var bottomSideAllSet, // flag to indicate that we must start with a field having 5 checkers in it(the 12th field)
    // at bottom we start with  12. 5 3 5 2 .23
    // at top we start with     11. 5 3 5 2 .0
        patternToPutColors = []; // colors are inverted in the opposite sides of the field, sorry it truly looks ugly..

    var gameboard = {
        init: function () {
            this.fields = [];
            bottomSideAllSet = false;
            patternToPutColors[0] = 1;
            patternToPutColors[5] = 0;
            patternToPutColors[7] = 0;
            patternToPutColors[11] = 1;
            var self = this;
            setupHalfGameBoard(self, bottomSideAllSet);
            bottomSideAllSet = true;
            setupHalfGameBoard(self, bottomSideAllSet);
            return this;
        },
        addField: function (value) {
            this.fields.push(value);
        }
    };

    function setupHalfGameBoard(self, bottomSideAllSet) {
        var i,
            len,
            x,
            y,
            currentField,
            patternOfTheNumberOfCheckersToPut = [2, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 5],// on 0 field put 2, on field 1 put 0, etc.. the setup rules..
            j,  // needed to go trough the patternToPutCheckers
            colorToPut,
            countPassedFields = 0; // needed to add a boardBar offset to the X coordinate

        if (bottomSideAllSet) {
            i = 12;
            j = 11;
        } else {
            i = 0;
            j = 0;
        }
        for (len = i + 12; i < len; i += 1, (i >= 12) ? j -= 1 : j += 1, countPassedFields += 1) {
            if (countPassedFields >= 6) {  // after every 6 fields we must consider the boardBar also, in both the upper and the lower side of the board
                x = (!bottomSideAllSet) * canvas.width - boardFrameOffset - ((countPassedFields + !bottomSideAllSet) * fieldWidth) - boardBar;
            } else {
                x = (!bottomSideAllSet) * canvas.width - boardFrameOffset - ((countPassedFields + !bottomSideAllSet) * fieldWidth);
                // if we are at the bottom we must consider the canvas width and to subtract from it
            }
            y = (!bottomSideAllSet) * (canvas.height - checkerHeight) - boardFrameOffset;

            if (x < 0) { // because we are extracting everything from the canvas width or height
                x *= -1; // when we are at the bottom side, we start from the MOST-RIGHT field(our 0 field) so canvas height, and width must be considered,
            }            // while when we are at the upper side of the board, the MOST-LEFT field(the 12th field) must be filled first,
            if (y < 0) { // so canvas width and height in the upper side are 0, and we get a negative number but we actually need that number to be positive,
                y *= -1; // to go to the right.. to fill the rest of the fields.. it's all because of how the fields position in backgammon
            }
            if (patternToPutColors[j] === 1) {
                colorToPut = 'white';
                patternToPutColors[j] = 0; // swapping the pattern for the next half of the board
            } else if (patternToPutColors[j] === 0) {
                colorToPut = 'black';
                patternToPutColors[j] = 1;
            }
            currentField = Object.create(field).init(x, y); // creating one of the 24 fields in the board
            putCheckersOnField(patternOfTheNumberOfCheckersToPut[j], currentField, colorToPut); // assigning checkers to it with respect to the Backgammon setup rules
            self.addField(currentField); // adding the field to the board, used 'self' because the scope of this has changed
        }
    }

    function putCheckersOnField(checkersToPut, givenField, color) {
        var i;
        for (i = 0; i < checkersToPut; i += 1) {
            givenField.addChecker(Object.create(checker).init(color));
        }
    }

    return gameboard;
}();

// function newGame() or main()
// create initiate and append game canvas
// keep track of keyboard presses
// INITIATE GAME OBJECTS;
// GAME LOOP, STARTS THE ANIMATION;
function newGame() {
    canvas = document.getElementById('board-canvas');
    ctx = canvas.getContext('2d');

    // INIT GAME OBJECTS, SETS UP GAME
    board.init();
}
newGame();
// canvas.addEventListener('click', findPressedField);

function play() {
    // GAME LOOP - game logic, updating the fields, players turns, dices etc. Update update update
    // change fields depending on how the player interact(key presses)
    canvas.addEventListener('click', findPressedField);

    // calls function update()
    // checkForWinner etc... this all should go here

    // draws every time and if we have changes its ok
    draw();
    requestAnimationFrame(play);
}
play();

function findPressedField(event) {
    var x = event.offsetX;
    var y = event.offsetY;
    if (((x < canvas.width - boardFrameOffset && x > boardFrameOffset + boardSide + boardBar) ||
        (x < boardFrameOffset + boardSide && x > boardFrameOffset)) &&
        (y < canvas.height - boardFrameOffset && y > boardFrameOffset)) { // ignoring every piece of the frame
        var i = 0,
            len,
            foundField,
            upperSide = false;
        // determining in which quadrant are we exactly
        if (y < canvas.height / 2 && y > boardFrameOffset) {
            i = 12; // last field of the upper side, last because if we take the first our condition in the for-loop will be achieved every time
            upperSide = true;
        } else {
            i = 0; // first field of the lower side
        }
        if (x < boardSide + boardFrameOffset && x > boardFrameOffset) { // left side of the board
            if (i === 0) { // bottom left side
                i = 6;
            } else { // upper left side
                i = 12;
            }
        } else { // right side of the board
            if (i === 0) { // bottom right side
                i = 0;
            } else { // upper left side
                i = 18; // again last of the array
            }
        }
        for (len = i + 6; i < len; i += 1) {
            var currentField = board.fields[i];
            if (upperSide) { /* its all because of the fields..
             the start position of the lower side is most-right and it starts from 0,
             while the start position of the upper side is the most-left which confuses the algorithm */
                var previousField = board.fields[i - 1];
                if (currentField.x > x) {
                    foundField = previousField;
                    break;
                }
            } else {
                if (x > currentField.x) {
                    foundField = currentField;
                    break;
                }
            }
        }
        if (typeof foundField === 'undefined') { // when we are going out of the for-in and the condition for the upperSide is not met,
            // because we take the previous field element we could not get to check the last one
            foundField = board.fields[len - 1];
        }
        clickedField = foundField;
        console.log(clickedField); // testing purposes
    }
}

function draw() {
    ctx.clearRect(0,0,canvas.width,canvas.height); // refresh canvas

    board.fields.forEach(function (field) {
        field.draw(); // redraw every field again
    });
}<|endoftext|>