<commit_before>const Collection = require('../util/Collection');
const Snowflake = require('../util/Snowflake');

const Targets = {
  GUILD: 'GUILD',
  CHANNEL: 'CHANNEL',
  USER: 'USER',
  ROLE: 'ROLE',
  INVITE: 'INVITE',
  WEBHOOK: 'WEBHOOK',
  EMOJI: 'EMOJI',
  MESSAGE: 'MESSAGE',
};

const Actions = {
  GUILD_UPDATE: 1,
  CHANNEL_CREATE: 10,
  CHANNEL_UPDATE: 11,
  CHANNEL_DELETE: 12,
  CHANNEL_OVERWRITE_CREATE: 13,
  CHANNEL_OVERWRITE_UPDATE: 14,
  CHANNEL_OVERWRITE_DELETE: 15,
  MEMBER_KICK: 20,
  MEMBER_PRUNE: 21,
  MEMBER_BAN_ADD: 22,
  MEMBER_BAN_REMOVE: 23,
  MEMBER_UPDATE: 24,
  MEMBER_ROLE_UPDATE: 25,
  ROLE_CREATE: 30,
  ROLE_UPDATE: 31,
  ROLE_DELETE: 32,
  INVITE_CREATE: 40,
  INVITE_UPDATE: 41,
  INVITE_DELETE: 42,
  WEBHOOK_CREATE: 50,
  WEBHOOK_UPDATE: 51,
  WEBHOOK_DELETE: 52,
  EMOJI_CREATE: 60,
  EMOJI_UPDATE: 61,
  EMOJI_DELETE: 62,
  MESSAGE_DELETE: 72,
};

/**
 * Audit logs entries are held in this class.
 */
class GuildAuditLogs {
  constructor(guild, data) {
    if (data.users) for (const user of data.users) guild.client.dataManager.newUser(user);

    /**
     * The entries for this guild's audit logs
     * @type {Collection<Snowflake, GuildAuditLogsEntry>}
     */
    this.entries = new Collection();
    for (const item of data.audit_log_entries) {
      const entry = new GuildAuditLogsEntry(guild, item);
      this.entries.set(entry.id, entry);
    }
  }

  /**
   * Handles possible promises for entry targets.
   * @returns {GuildAuditLogs}
   */
  static build(...args) {
    return new Promise(resolve => {
      const logs = new GuildAuditLogs(...args);
      Promise.all(logs.entries.map(e => e.target)).then(() => resolve(logs));
    });
  }

  /**
   * Find target type from entry action.
   * @param {number} target The action target
   * @returns {?string}
   */
  static targetType(target) {
    if (target < 10) return Targets.GUILD;
    if (target < 20) return Targets.CHANNEL;
    if (target < 30) return Targets.USER;
    if (target < 40) return Targets.ROLE;
    if (target < 50) return Targets.INVITE;
    if (target < 60) return Targets.WEBHOOK;
    if (target < 70) return Targets.EMOJI;
    if (target < 80) return Targets.MESSAGE;
    return null;
  }

  /**
   * Find action type from entry action.
   * @param {string} action The action target
   * @returns {string}
   */
  static actionType(action) {
    if ([
      Actions.CHANNEL_CREATE,
      Actions.CHANNEL_OVERWRITE_CREATE,
      Actions.MEMBER_BAN_REMOVE,
      Actions.ROLE_CREATE,
      Actions.INVITE_CREATE,
      Actions.WEBHOOK_CREATE,
      Actions.EMOJI_CREATE,
    ].includes(action)) return 'CREATE';

    if ([
      Actions.CHANNEL_DELETE,
      Actions.CHANNEL_OVERWRITE_DELETE,
      Actions.MEMBER_KICK,
      Actions.MEMBER_PRUNE,
      Actions.MEMBER_BAN_ADD,
      Actions.ROLE_DELETE,
      Actions.INVITE_DELETE,
      Actions.WEBHOOK_DELETE,
      Actions.EMOJI_DELETE,
      Actions.MESSAGE_DELETE,
    ].includes(action)) return 'DELETE';

    if ([
      Actions.GUILD_UPDATE,
      Actions.CHANNEL_UPDATE,
      Actions.CHANNEL_OVERWRITE_UPDATE,
      Actions.MEMBER_UPDATE,
      Actions.ROLE_UPDATE,
      Actions.INVITE_UPDATE,
      Actions.WEBHOOK_UPDATE,
      Actions.EMOJI_UPDATE,
    ].includes(action)) return 'UPDATE';

    return 'ALL';
  }
}

/**
 * Audit logs entry.
 */
class GuildAuditLogsEntry {
  constructor(guild, data) {
    const targetType = GuildAuditLogs.targetType(data.action_type);
    /**
     * The target type of this entry
     * @type {string}
     */
    this.targetType = targetType;

    /**
     * The action type of this entry
     * @type {string}
     */
    this.actionType = GuildAuditLogs.actionType(data.action_type);

    /**
     * Specific action type of this entry
     * @type {string}
     */
    this.action = Object.keys(Actions).find(k => Actions[k] === data.action_type);

    /**
     * The reason of this entry
     * @type {?string}
     */
    this.reason = data.reason || null;

    /**
     * The user that executed this entry
     * @type {User}
     */
    this.executor = guild.client.users.get(data.user_id);

    /**
     * An entry in the audit log representing a specific change
     * @typedef {object} AuditLogChange
     * @property {string} key The property that was changed, e.g. `nick` for nickname changes
     * @property {string|boolean|number} [old] The old value of the change, e.g. for nicknames, the old nickname
     * @property {string|boolean|number} [new] The new value of the change, e.g. for nicknames, the new nickname
     */

    /**
     * Specific property changes
     * @type {AuditLogChange[]}
     */
    this.changes = data.changes ? data.changes.map(c => ({ key: c.key, old: c.old_value, new: c.new_value })) : null;

    /**
     * The ID of this entry
     * @type {Snowflake}
     */
    this.id = data.id;

    /**
     * Any extra data from the entry
     * @type {?Object|Role|GuildMember}
     */
    this.extra = null;
    if (data.options) {
      if (data.action_type === Actions.MEMBER_PRUNE) {
        this.extra = {
          removed: data.options.members_removed,
          days: data.options.delete_member_days,
        };
      } else if (data.action_type === Actions.MESSAGE_DELETE) {
        this.extra = {
          count: data.options.count,
          channel: guild.channels.get(data.options.channel_id),
        };
      } else {
        switch (data.options.type) {
          case 'member':
            this.extra = guild.members.get(data.options.id);
            if (!this.extra) this.extra = { id: data.options.id };
            break;
          case 'role':
            this.extra = guild.roles.get(data.options.id);
            if (!this.extra) this.extra = { id: data.options.id, name: data.options.role_name };
            break;
          default:
            break;
        }
      }
    }

    if ([Targets.USER, Targets.GUILD].includes(targetType)) {
      /**
       * The target of this entry
       * @type {?Guild|User|Role|Emoji|Invite|Webhook}
       */
      this.target = guild.client[`${targetType.toLowerCase()}s`].get(data.target_id);
    } else if (targetType === Targets.WEBHOOK) {
      this.target = guild.fetchWebhooks()
        .then(hooks => {
          this.target = hooks.find(h => h.id === data.target_id);
          return this.target;
        });
    } else if (targetType === Targets.INVITE) {
      const change = this.changes.find(c => c.name === 'code');
      this.target = guild.fetchInvites()
        .then(invites => {
          this.target = invites.find(i => i.code === (change.new || change.old));
          return this.target;
        });
    } else if (targetType === Targets.MESSAGE) {
      this.target = guild.client.users.get(data.target_id);
    } else {
      this.target = guild[`${targetType.toLowerCase()}s`].get(data.target_id);
    }
  }

  /**
   * The timestamp this entry was created at
   * @type {number}
   * @readonly
   */
  get createdTimestamp() {
    return Snowflake.deconstruct(this.id).timestamp;
  }

  /**
   * The time this entry was created
   * @type {Date}
   * @readonly
   */
  get createdAt() {
    return new Date(this.createdTimestamp);
  }
}

GuildAuditLogs.Actions = Actions;
GuildAuditLogs.Targets = Targets;
GuildAuditLogs.Entry = GuildAuditLogsEntry;

module.exports = GuildAuditLogs;
<commit_msg>using correct properties for invites (#1467)<commit_after>const Collection = require('../util/Collection');
const Snowflake = require('../util/Snowflake');

const Targets = {
  GUILD: 'GUILD',
  CHANNEL: 'CHANNEL',
  USER: 'USER',
  ROLE: 'ROLE',
  INVITE: 'INVITE',
  WEBHOOK: 'WEBHOOK',
  EMOJI: 'EMOJI',
  MESSAGE: 'MESSAGE',
};

const Actions = {
  GUILD_UPDATE: 1,
  CHANNEL_CREATE: 10,
  CHANNEL_UPDATE: 11,
  CHANNEL_DELETE: 12,
  CHANNEL_OVERWRITE_CREATE: 13,
  CHANNEL_OVERWRITE_UPDATE: 14,
  CHANNEL_OVERWRITE_DELETE: 15,
  MEMBER_KICK: 20,
  MEMBER_PRUNE: 21,
  MEMBER_BAN_ADD: 22,
  MEMBER_BAN_REMOVE: 23,
  MEMBER_UPDATE: 24,
  MEMBER_ROLE_UPDATE: 25,
  ROLE_CREATE: 30,
  ROLE_UPDATE: 31,
  ROLE_DELETE: 32,
  INVITE_CREATE: 40,
  INVITE_UPDATE: 41,
  INVITE_DELETE: 42,
  WEBHOOK_CREATE: 50,
  WEBHOOK_UPDATE: 51,
  WEBHOOK_DELETE: 52,
  EMOJI_CREATE: 60,
  EMOJI_UPDATE: 61,
  EMOJI_DELETE: 62,
  MESSAGE_DELETE: 72,
};

/**
 * Audit logs entries are held in this class.
 */
class GuildAuditLogs {
  constructor(guild, data) {
    if (data.users) for (const user of data.users) guild.client.dataManager.newUser(user);

    /**
     * The entries for this guild's audit logs
     * @type {Collection<Snowflake, GuildAuditLogsEntry>}
     */
    this.entries = new Collection();
    for (const item of data.audit_log_entries) {
      const entry = new GuildAuditLogsEntry(guild, item);
      this.entries.set(entry.id, entry);
    }
  }

  /**
   * Handles possible promises for entry targets.
   * @returns {GuildAuditLogs}
   */
  static build(...args) {
    return new Promise(resolve => {
      const logs = new GuildAuditLogs(...args);
      Promise.all(logs.entries.map(e => e.target)).then(() => resolve(logs));
    });
  }

  /**
   * Find target type from entry action.
   * @param {number} target The action target
   * @returns {?string}
   */
  static targetType(target) {
    if (target < 10) return Targets.GUILD;
    if (target < 20) return Targets.CHANNEL;
    if (target < 30) return Targets.USER;
    if (target < 40) return Targets.ROLE;
    if (target < 50) return Targets.INVITE;
    if (target < 60) return Targets.WEBHOOK;
    if (target < 70) return Targets.EMOJI;
    if (target < 80) return Targets.MESSAGE;
    return null;
  }

  /**
   * Find action type from entry action.
   * @param {string} action The action target
   * @returns {string}
   */
  static actionType(action) {
    if ([
      Actions.CHANNEL_CREATE,
      Actions.CHANNEL_OVERWRITE_CREATE,
      Actions.MEMBER_BAN_REMOVE,
      Actions.ROLE_CREATE,
      Actions.INVITE_CREATE,
      Actions.WEBHOOK_CREATE,
      Actions.EMOJI_CREATE,
    ].includes(action)) return 'CREATE';

    if ([
      Actions.CHANNEL_DELETE,
      Actions.CHANNEL_OVERWRITE_DELETE,
      Actions.MEMBER_KICK,
      Actions.MEMBER_PRUNE,
      Actions.MEMBER_BAN_ADD,
      Actions.ROLE_DELETE,
      Actions.INVITE_DELETE,
      Actions.WEBHOOK_DELETE,
      Actions.EMOJI_DELETE,
      Actions.MESSAGE_DELETE,
    ].includes(action)) return 'DELETE';

    if ([
      Actions.GUILD_UPDATE,
      Actions.CHANNEL_UPDATE,
      Actions.CHANNEL_OVERWRITE_UPDATE,
      Actions.MEMBER_UPDATE,
      Actions.ROLE_UPDATE,
      Actions.INVITE_UPDATE,
      Actions.WEBHOOK_UPDATE,
      Actions.EMOJI_UPDATE,
    ].includes(action)) return 'UPDATE';

    return 'ALL';
  }
}

/**
 * Audit logs entry.
 */
class GuildAuditLogsEntry {
  constructor(guild, data) {
    const targetType = GuildAuditLogs.targetType(data.action_type);
    /**
     * The target type of this entry
     * @type {string}
     */
    this.targetType = targetType;

    /**
     * The action type of this entry
     * @type {string}
     */
    this.actionType = GuildAuditLogs.actionType(data.action_type);

    /**
     * Specific action type of this entry
     * @type {string}
     */
    this.action = Object.keys(Actions).find(k => Actions[k] === data.action_type);

    /**
     * The reason of this entry
     * @type {?string}
     */
    this.reason = data.reason || null;

    /**
     * The user that executed this entry
     * @type {User}
     */
    this.executor = guild.client.users.get(data.user_id);

    /**
     * An entry in the audit log representing a specific change
     * @typedef {object} AuditLogChange
     * @property {string} key The property that was changed, e.g. `nick` for nickname changes
     * @property {string|boolean|number} [old] The old value of the change, e.g. for nicknames, the old nickname
     * @property {string|boolean|number} [new] The new value of the change, e.g. for nicknames, the new nickname
     */

    /**
     * Specific property changes
     * @type {AuditLogChange[]}
     */
    this.changes = data.changes ? data.changes.map(c => ({ key: c.key, old: c.old_value, new: c.new_value })) : null;

    /**
     * The ID of this entry
     * @type {Snowflake}
     */
    this.id = data.id;

    /**
     * Any extra data from the entry
     * @type {?Object|Role|GuildMember}
     */
    this.extra = null;
    if (data.options) {
      if (data.action_type === Actions.MEMBER_PRUNE) {
        this.extra = {
          removed: data.options.members_removed,
          days: data.options.delete_member_days,
        };
      } else if (data.action_type === Actions.MESSAGE_DELETE) {
        this.extra = {
          count: data.options.count,
          channel: guild.channels.get(data.options.channel_id),
        };
      } else {
        switch (data.options.type) {
          case 'member':
            this.extra = guild.members.get(data.options.id);
            if (!this.extra) this.extra = { id: data.options.id };
            break;
          case 'role':
            this.extra = guild.roles.get(data.options.id);
            if (!this.extra) this.extra = { id: data.options.id, name: data.options.role_name };
            break;
          default:
            break;
        }
      }
    }

    if ([Targets.USER, Targets.GUILD].includes(targetType)) {
      /**
       * The target of this entry
       * @type {?Guild|User|Role|Emoji|Invite|Webhook}
       */
      this.target = guild.client[`${targetType.toLowerCase()}s`].get(data.target_id);
    } else if (targetType === Targets.WEBHOOK) {
      this.target = guild.fetchWebhooks()
        .then(hooks => {
          this.target = hooks.find(h => h.id === data.target_id);
          return this.target;
        });
    } else if (targetType === Targets.INVITE) {
      const change = this.changes.find(c => c.key === 'code');
      this.target = guild.fetchInvites()
        .then(invites => {
          this.target = invites.find(i => i.code === (change.new_value || change.old_value));
          return this.target;
        });
    } else if (targetType === Targets.MESSAGE) {
      this.target = guild.client.users.get(data.target_id);
    } else {
      this.target = guild[`${targetType.toLowerCase()}s`].get(data.target_id);
    }
  }

  /**
   * The timestamp this entry was created at
   * @type {number}
   * @readonly
   */
  get createdTimestamp() {
    return Snowflake.deconstruct(this.id).timestamp;
  }

  /**
   * The time this entry was created
   * @type {Date}
   * @readonly
   */
  get createdAt() {
    return new Date(this.createdTimestamp);
  }
}

GuildAuditLogs.Actions = Actions;
GuildAuditLogs.Targets = Targets;
GuildAuditLogs.Entry = GuildAuditLogsEntry;

module.exports = GuildAuditLogs;
<|endoftext|>


<commit_before>$(document).ready(function() {
	objCommon = new Common();
	objCommon.init();
});

function Common() {
	this.page = 2;
	this.before_page = 1;
	this.init = function() {
		if($('#book_list_div').length){
			$( window ).scroll(function() {
				var window_bottom = $(window).scrollTop() + $(window).height();
				var show_list_bottom = $('#book_list_div').position().top+$('#book_list_div').outerHeight(true);
				if(window_bottom > show_list_bottom){
					if(objCommon.page > objCommon.before_page){
						$('#loading').css('display','block');
						objCommon.before_page++;
						$.ajax({
							url: '/smart_omr/_common/elements/book_list_body.php',
							data:{'page':objCommon.page,search_key:$('#frmSearch #before_search_key').val()},
							type: 'POST',
							dataType: 'html',
							success: function(htmlResult){
								if($.trim(htmlResult)==''){
									$('#loading').css('display','none');
								}else{
									$('#book_list_div').append(htmlResult);
									$('#loading').css('display','none');
									objCommon.page++;
									objCommon.fitImages();
								}
						  	}
						});
					}
				}
			});
		}
		if($('#frmSearch').length){
			$('#frmSearch .search-bt').on('click',function(){objCommon.frmSearch();});
			$('#frmSearch #search_key').on('keyup', function(e) {if(e.which == 13) {objCommon.frmSearch();}});
		}
		if($('._d_logout').length){
			$('._d_logout').on('click',function(){objCommon.logOut();});
		}
		if($('.fit_image').length){
			objCommon.fitImages();
		}
		if($('.pure-menu-item').length){
			$('.pure-menu-item').on('click',function(){
				$('.pure-menu-item').removeClass('active');
				$(this).addClass('active');
			});
		}
		//set comment div
		if($('#comment_div[comment_seq]').length>0){
			objCommon.getComment($('#comment_div').attr('comment_seq'),$('#comment_div').attr('bbs_seq'));
		}

		// OMR upload
		if($('[data-test-key]').length>0){
			objCommon.initUploadOMR();
		}
	};
	this.displayTab = function(blockID){
		$('.sub_tabs').css('display','none');
		$('#'+blockID).css('display','block');
	};
	this.fitImages = function(){
		function findTarget(ele){
			eleFind = ele.parent('.fit_target');
			if(eleFind.length>0){
				return(eleFind);
			}else{
				return(findTarget(ele.parent()));
			};
		};
		$('.fit_image[fit_size!=1]').each(function(){
//			var objImage = new Image();
			var objTarget = findTarget($(this));
//			objImage.src = $(this).attr('src');

//			var intWidth = objImage.width;
//			var intHeight =  objImage.height;
			var intWidth = $(this).width();
			var intHeight =  $(this).height();
			var intNewWidth,intNewHeight;
			/*
			intNewWidth = $('.workbook_cover_box').eq(0).width();
			if(!intNewWidth){
				objTarget.each(function(){
					intNewWidth = $(this).width();
				});
			}
			*/
			objTarget.each(function(){
				intNewWidth = $(this).width();
				intNewHeight = $(this).height();
			});
			console.log(intNewHeight);
			/*
			if($(this).attr('src')=='http://files.itworld.co.kr/image/avatar/article/2013/June/just2kill@gmail.com/DT_Haswell_i7_FB_500_0.jpg'){
				alert(intNewWidth);
			}
			*/
			var targetRate = intNewWidth/intNewHeight;
			var imageRate = intWidth/intHeight;

			if(targetRate < imageRate){
				var widthRate = intNewHeight/intHeight;
				var margin = 0 - (intWidth*widthRate) / 2;
				//$(this).css({'padding-left':'50%'});
				//$(this).css({'margin-left':margin+'px'});
				$(this).css({'height':'100%'});
			}else{
				var heightRate = intNewWidth/intWidth;
				var margin = 0 - (((intHeight*heightRate) - intNewHeight)/2);
				$(this).css({'margin-top':margin});
				$(this).css({'width':'100%'});
			}
			$(this).css({ 'display':'' });
			$(this).attr({'fit_size':1});
		});
	};
	this.frmSearch = function(){
		var eleForm = $('#frmSearch');
		var options = {
				url: '/smart_omr/_common/elements/book_list_body.php',
				data:{},
				dataType: 'html',
				resetForm:false,
				type:	'post',       // 'get' or 'post', override for form's
				// 'method' attribute
				beforeSubmit:function(){
				},
				success:function(htmlResult){
					if($.trim(htmlResult)!=''){
						$('#book_list_div').html(htmlResult);
						objCommon.fitImages();
					}else{
						$('#book_list_div').html('
조회 내역이 없습니다.
');
					}
					$('#loading').css('display','none');
					$('#frmSearch #before_search_key').val($('#frmSearch #search_key').val());
					objCommon.page = 2;
					objCommon.before_page = 1;
				}
		};
		eleForm.ajaxSubmit(options);
	};
	this.frmReset = function(){
		$("#frmSearch input").each(function(){
			$('#frmSearch #'+this.id).val('');
		});
	};
	this.logOut = function(){
		$.ajax({
			url: '/_connector/yellow.501.php',
			data:{'viewID':'SOMR_LOG_OUT'},
			type: 'POST',
			dataType: 'json',
			beforeSend:function(){
			},
			success: function(jsonResult){
				location.href='/smart_omr';
			}
		});
	};
	this.sendMail = function(strType){
		var eleForm = $('#frmMail');
		var options = {
				url: '/_connector/yellow.501.php',
				data:{'viewID':'SEND_MAIL','mail_type':strType},
				dataType: 'json',
				resetForm:false,
				type:	'POST',       // 'get' or 'post', override for form's
				beforeSubmit:function(){
					if( $('#frmMail #sender_email').length>0 && !$('#frmMail #sender_email').val().match(/([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/)){
						alert("이메일 주소가 유효하지 않습니다");
						$('#frmMail #sender_email').focus();
						return false;
					}
					if( $('#frmMail #receiver_email').length>0 && !$('#frmMail #receiver_email').val().match(/([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/)){
						alert("이메일 주소가 유효하지 않습니다");
						$('#frmMail #receiver_email').focus();
						return false;
					}
					if(!objRegistration.userFrmChk('frmMail')){
						return false;
					}
				},
				success:function(jsonResult){
					if(jsonResult.result){
						halert(jsonResult.message);
						location.href='/smart_omr/';
					}else{
						halert('전송오류! 잠시후 다시 시도해 주세요');
					}
				}
		};
		eleForm.ajaxSubmit(options);
	};
	/********************
	 *  comment
	 * ******************/
	this.saveComment = function(){
		var options = {
			url : '/_connector/yellow.501.php',
			data : {
				'viewID' : "SAVE_SOMR_COMMENT"
			},
			dataType : 'json',
			resetForm : true,
			type : 'post', // 'get' or 'post', override for form's 'method'
							// attribute
			beforeSubmit : function() {
				if($.trim($('#frmComment #comment').val())==''){
					$('#frmComment #comment').focus();
					return false;
				}
				return (true);
			},
			success : function(jsonResult) {
				if (jsonResult.boolResult) {
					objCommon.getComment(jsonResult.post_seq,jsonResult.bbs_seq);
				}else{
				}
			}
		};
		$('#frmComment').ajaxSubmit(options);
	};
	this.getComment = function(post_seq,bbs_seq){
		$.ajax({
			url : '/smart_omr/_common/elements/comment.php',
			data : {
				'post_seq' : post_seq,
				'bbs_seq' : bbs_seq
			},
			type : 'POST',
			dataType : 'text',
			beforeSend: function() {
			},
			success : function(resultHtml) {
				$('#comment_div').html(resultHtml);
				$("#frmComment #comment").keypress(function(e){
				    if(e.which==13){
					     $('#btn_comment').click();
					     e.preventDefault()
				    }
				});
			}
		});
	};
	this.deleteComment = function(post_seq,bbs_seq,comment_seq){
		$.ajax({
			url : '/_connector/yellow.501.php',
			data : {
				'viewID' : 'DELETE_SOMR_COMMENT',
				'comment_seq' : comment_seq,
				'post_seq' : post_seq,
				'bbs_seq' : bbs_seq
			},
			type : 'POST',
			dataType : 'json',
			beforeSend: function() {
			},
			success : function(jsonResult) {
				if(jsonResult.result){
					objCommon.getComment(post_seq,bbs_seq);
				}
			}
		});
	};
	this.initUploadOMR = function(){
		var upload_button = $('[data-test-key]');
		$('[data-test-key]').each(function(){
			var eleBtn = $(this);
			new AjaxUpload(eleBtn, {
				action: '/_connector/yellow.501',
			    data: {
			        'viewID': 'SOMR_UPLOAD_OMR',
			        'test_key': eleBtn.data('test-key')
			    },
			   responseType: 'json',
				name: 'OMR',
				onSubmit : function(file, ext){
					if(ext.toLowerCase()!="jpg" && ext.toLowerCase()!="png"){
						alert("jpg 또는 png 파일만 업로드 가능합니다.");
						return(false);
					}
					this.disable();
				},
				onComplete: function(file,response){
					this.enable();
					if(response.boolResult){
						halert('답안이 전송되었습니다.');
						location.href="/smart_omr/exercise_book/test_result.php?t="+response.str_test_seq;
					}else{
						halert("테스트 저장 오류!");
					}
				}
			});
		});
	}
}

function hconfirm(strMessage,fncTrueCallBack,fncFalseCallack){
	if(confirm(strMessage)){
		console.log(fncTrueCallBack);
		//fncTrueCallBack.call();
	}else{
		console.log(fncFalseCallack);
		//fncFalseCallack.call();
	}
}
function halert(strMessage,fncOkCallBack){
	alert(strMessage);
	if(fncOkCallBack!="undefined"){
		//fncOkCallBack.call();
	}else{
		// modal close
	}
}<commit_msg>OMR 이미지 업로드 확장자 jpeg 추가<commit_after>$(document).ready(function() {
	objCommon = new Common();
	objCommon.init();
});

function Common() {
	this.page = 2;
	this.before_page = 1;
	this.init = function() {
		if($('#book_list_div').length){
			$( window ).scroll(function() {
				var window_bottom = $(window).scrollTop() + $(window).height();
				var show_list_bottom = $('#book_list_div').position().top+$('#book_list_div').outerHeight(true);
				if(window_bottom > show_list_bottom){
					if(objCommon.page > objCommon.before_page){
						$('#loading').css('display','block');
						objCommon.before_page++;
						$.ajax({
							url: '/smart_omr/_common/elements/book_list_body.php',
							data:{'page':objCommon.page,search_key:$('#frmSearch #before_search_key').val()},
							type: 'POST',
							dataType: 'html',
							success: function(htmlResult){
								if($.trim(htmlResult)==''){
									$('#loading').css('display','none');
								}else{
									$('#book_list_div').append(htmlResult);
									$('#loading').css('display','none');
									objCommon.page++;
									objCommon.fitImages();
								}
						  	}
						});
					}
				}
			});
		}
		if($('#frmSearch').length){
			$('#frmSearch .search-bt').on('click',function(){objCommon.frmSearch();});
			$('#frmSearch #search_key').on('keyup', function(e) {if(e.which == 13) {objCommon.frmSearch();}});
		}
		if($('._d_logout').length){
			$('._d_logout').on('click',function(){objCommon.logOut();});
		}
		if($('.fit_image').length){
			objCommon.fitImages();
		}
		if($('.pure-menu-item').length){
			$('.pure-menu-item').on('click',function(){
				$('.pure-menu-item').removeClass('active');
				$(this).addClass('active');
			});
		}
		//set comment div
		if($('#comment_div[comment_seq]').length>0){
			objCommon.getComment($('#comment_div').attr('comment_seq'),$('#comment_div').attr('bbs_seq'));
		}

		// OMR upload
		if($('[data-test-key]').length>0){
			objCommon.initUploadOMR();
		}
	};
	this.displayTab = function(blockID){
		$('.sub_tabs').css('display','none');
		$('#'+blockID).css('display','block');
	};
	this.fitImages = function(){
		function findTarget(ele){
			eleFind = ele.parent('.fit_target');
			if(eleFind.length>0){
				return(eleFind);
			}else{
				return(findTarget(ele.parent()));
			};
		};
		$('.fit_image[fit_size!=1]').each(function(){
//			var objImage = new Image();
			var objTarget = findTarget($(this));
//			objImage.src = $(this).attr('src');

//			var intWidth = objImage.width;
//			var intHeight =  objImage.height;
			var intWidth = $(this).width();
			var intHeight =  $(this).height();
			var intNewWidth,intNewHeight;
			/*
			intNewWidth = $('.workbook_cover_box').eq(0).width();
			if(!intNewWidth){
				objTarget.each(function(){
					intNewWidth = $(this).width();
				});
			}
			*/
			objTarget.each(function(){
				intNewWidth = $(this).width();
				intNewHeight = $(this).height();
			});
			console.log(intNewHeight);
			/*
			if($(this).attr('src')=='http://files.itworld.co.kr/image/avatar/article/2013/June/just2kill@gmail.com/DT_Haswell_i7_FB_500_0.jpg'){
				alert(intNewWidth);
			}
			*/
			var targetRate = intNewWidth/intNewHeight;
			var imageRate = intWidth/intHeight;

			if(targetRate < imageRate){
				var widthRate = intNewHeight/intHeight;
				var margin = 0 - (intWidth*widthRate) / 2;
				//$(this).css({'padding-left':'50%'});
				//$(this).css({'margin-left':margin+'px'});
				$(this).css({'height':'100%'});
			}else{
				var heightRate = intNewWidth/intWidth;
				var margin = 0 - (((intHeight*heightRate) - intNewHeight)/2);
				$(this).css({'margin-top':margin});
				$(this).css({'width':'100%'});
			}
			$(this).css({ 'display':'' });
			$(this).attr({'fit_size':1});
		});
	};
	this.frmSearch = function(){
		var eleForm = $('#frmSearch');
		var options = {
				url: '/smart_omr/_common/elements/book_list_body.php',
				data:{},
				dataType: 'html',
				resetForm:false,
				type:	'post',       // 'get' or 'post', override for form's
				// 'method' attribute
				beforeSubmit:function(){
				},
				success:function(htmlResult){
					if($.trim(htmlResult)!=''){
						$('#book_list_div').html(htmlResult);
						objCommon.fitImages();
					}else{
						$('#book_list_div').html('
조회 내역이 없습니다.
');
					}
					$('#loading').css('display','none');
					$('#frmSearch #before_search_key').val($('#frmSearch #search_key').val());
					objCommon.page = 2;
					objCommon.before_page = 1;
				}
		};
		eleForm.ajaxSubmit(options);
	};
	this.frmReset = function(){
		$("#frmSearch input").each(function(){
			$('#frmSearch #'+this.id).val('');
		});
	};
	this.logOut = function(){
		$.ajax({
			url: '/_connector/yellow.501.php',
			data:{'viewID':'SOMR_LOG_OUT'},
			type: 'POST',
			dataType: 'json',
			beforeSend:function(){
			},
			success: function(jsonResult){
				location.href='/smart_omr';
			}
		});
	};
	this.sendMail = function(strType){
		var eleForm = $('#frmMail');
		var options = {
				url: '/_connector/yellow.501.php',
				data:{'viewID':'SEND_MAIL','mail_type':strType},
				dataType: 'json',
				resetForm:false,
				type:	'POST',       // 'get' or 'post', override for form's
				beforeSubmit:function(){
					if( $('#frmMail #sender_email').length>0 && !$('#frmMail #sender_email').val().match(/([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/)){
						alert("이메일 주소가 유효하지 않습니다");
						$('#frmMail #sender_email').focus();
						return false;
					}
					if( $('#frmMail #receiver_email').length>0 && !$('#frmMail #receiver_email').val().match(/([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/)){
						alert("이메일 주소가 유효하지 않습니다");
						$('#frmMail #receiver_email').focus();
						return false;
					}
					if(!objRegistration.userFrmChk('frmMail')){
						return false;
					}
				},
				success:function(jsonResult){
					if(jsonResult.result){
						halert(jsonResult.message);
						location.href='/smart_omr/';
					}else{
						halert('전송오류! 잠시후 다시 시도해 주세요');
					}
				}
		};
		eleForm.ajaxSubmit(options);
	};
	/********************
	 *  comment
	 * ******************/
	this.saveComment = function(){
		var options = {
			url : '/_connector/yellow.501.php',
			data : {
				'viewID' : "SAVE_SOMR_COMMENT"
			},
			dataType : 'json',
			resetForm : true,
			type : 'post', // 'get' or 'post', override for form's 'method'
							// attribute
			beforeSubmit : function() {
				if($.trim($('#frmComment #comment').val())==''){
					$('#frmComment #comment').focus();
					return false;
				}
				return (true);
			},
			success : function(jsonResult) {
				if (jsonResult.boolResult) {
					objCommon.getComment(jsonResult.post_seq,jsonResult.bbs_seq);
				}else{
				}
			}
		};
		$('#frmComment').ajaxSubmit(options);
	};
	this.getComment = function(post_seq,bbs_seq){
		$.ajax({
			url : '/smart_omr/_common/elements/comment.php',
			data : {
				'post_seq' : post_seq,
				'bbs_seq' : bbs_seq
			},
			type : 'POST',
			dataType : 'text',
			beforeSend: function() {
			},
			success : function(resultHtml) {
				$('#comment_div').html(resultHtml);
				$("#frmComment #comment").keypress(function(e){
				    if(e.which==13){
					     $('#btn_comment').click();
					     e.preventDefault()
				    }
				});
			}
		});
	};
	this.deleteComment = function(post_seq,bbs_seq,comment_seq){
		$.ajax({
			url : '/_connector/yellow.501.php',
			data : {
				'viewID' : 'DELETE_SOMR_COMMENT',
				'comment_seq' : comment_seq,
				'post_seq' : post_seq,
				'bbs_seq' : bbs_seq
			},
			type : 'POST',
			dataType : 'json',
			beforeSend: function() {
			},
			success : function(jsonResult) {
				if(jsonResult.result){
					objCommon.getComment(post_seq,bbs_seq);
				}
			}
		});
	};
	this.initUploadOMR = function(){
		var upload_button = $('[data-test-key]');
		$('[data-test-key]').each(function(){
			var eleBtn = $(this);
			new AjaxUpload(eleBtn, {
				action: '/_connector/yellow.501',
			    data: {
			        'viewID': 'SOMR_UPLOAD_OMR',
			        'test_key': eleBtn.data('test-key')
			    },
			   responseType: 'json',
				name: 'OMR',
				onSubmit : function(file, ext){
					if(ext.toLowerCase()!="jpeg" && ext.toLowerCase()!="jpg" && ext.toLowerCase()!="png"){
						alert("jpg 또는 png 파일만 업로드 가능합니다.");
						return(false);
					}
					this.disable();
				},
				onComplete: function(file,response){
					this.enable();
					if(response.boolResult){
						halert('답안이 전송되었습니다.');
						location.href="/smart_omr/exercise_book/test_result.php?t="+response.str_test_seq;
					}else{
						halert("테스트 저장 오류!");
					}
				}
			});
		});
	}
}

function hconfirm(strMessage,fncTrueCallBack,fncFalseCallack){
	if(confirm(strMessage)){
		console.log(fncTrueCallBack);
		//fncTrueCallBack.call();
	}else{
		console.log(fncFalseCallack);
		//fncFalseCallack.call();
	}
}
function halert(strMessage,fncOkCallBack){
	alert(strMessage);
	if(fncOkCallBack!="undefined"){
		//fncOkCallBack.call();
	}else{
		// modal close
	}
}<|endoftext|>


<commit_before>/* Copyright 2013-2014 Extended Mind Technologies Oy
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 'use strict';

 function EditorController($rootScope, $scope,
                           ConvertService, ItemsService, SwiperService, UISessionService,
                           UserSessionService) {

  // OPENING, INITIALIZING, CLOSING

  var itemInEdit;
  $scope.initializeEditor = function(editorType, item, mode){
    // Reset $scope variables. These may exist from previous editor.
    $scope.task = undefined;
    $scope.note = undefined;
    $scope.list = undefined;
    $scope.item = undefined;
    $scope.tag = undefined;
    itemInEdit = item;

    $scope.editorType = editorType;
    $scope.editorVisible = true;
    $scope.mode = mode;

    if (editorType === 'task'){
      $scope.task = itemInEdit;
    }else if (editorType === 'note'){
      $scope.note = itemInEdit;
    }else if (editorType === 'list'){
      $scope.list = itemInEdit;
    }else if (editorType === 'item'){
      $scope.item = itemInEdit;
    }else if (editorType === 'tag'){
      $scope.tag = itemInEdit;
    }else if (editorType === 'user'){
      $scope.user = itemInEdit ? itemInEdit : UserSessionService.getUser();
    }
  };

  function editorAboutToClose() {
    if (typeof featureEditorAboutToCloseCallback === 'function') featureEditorAboutToCloseCallback();
  }

  // Callback from Snap.js, outside of AngularJS event loop
  function editorOpened() {
    if ((!itemInEdit || !itemInEdit.deleted) && typeof titleBarInputFocusCallbackFunction === 'function' &&
        $scope.editorType !== 'task')
    {
      // Focus on found and not deleted item
      setFocusOnTitlebarInput();
    }
  }

  // Callback from Snap.js, outside of AngularJS event loop
  function editorClosed() {
    // Don't manipulate list items in list before editor has been closed.
    UISessionService.resolveDeferredActions('edit');

    // NOTE: Call $apply here if these don't seem to be reseted
    $scope.editorType = undefined;
    $scope.mode = undefined;
    $scope.editorVisible = false;
    titleBarInputFocusCallbackFunction = titleBarInputBlurCallbackFunction = undefined;
    featureEditorAboutToCloseCallback = undefined;
  }

  if (angular.isFunction($scope.registerEditorAboutToOpenCallback))
    $scope.registerEditorAboutToOpenCallback($scope.initializeEditor, 'EditorController');

  if (angular.isFunction($scope.registerEditorAboutToCloseCallback))
    $scope.registerEditorAboutToCloseCallback(editorAboutToClose, 'EditorController');

  if (angular.isFunction($scope.registerEditorClosedCallback))
    $scope.registerEditorClosedCallback(editorClosed, 'EditorController');

  if (angular.isFunction($scope.registerEditorOpenedCallback))
    $scope.registerEditorOpenedCallback(editorOpened, 'EditorController');

  $scope.$on('$destroy', function() {
    if (angular.isFunction($scope.unregisterEditorAboutToOpenCallback))
      $scope.unregisterEditorAboutToOpenCallback('EditorController');

    if (angular.isFunction($scope.unregisterEditorAboutToCloseCallback))
      $scope.unregisterEditorAboutToCloseCallback('EditorController');

    if (angular.isFunction($scope.unregisterEditorOpenedCallback))
      $scope.unregisterEditorOpenedCallback('EditorController');

    if (angular.isFunction($scope.unregisterEditorClosedCallback))
      $scope.unregisterEditorClosedCallback('EditorController');
  });

  // HELPER METHODS

  var featureEditorAboutToCloseCallback;
  $scope.registerFeatureEditorAboutToCloseCallback = function(callback) {
    featureEditorAboutToCloseCallback = callback;
  };
  $scope.unregisterEditorAboutToCloseCallback = function() {
    featureEditorAboutToCloseCallback = undefined;
  };

  $scope.deferEdit = function(){
    return UISessionService.deferAction('edit', $rootScope.EDITOR_CLOSED_FAILSAFE_TIME);
  };

  // CONVERTING

  $scope.convertToNote = function(itemInEdit){
    var convertToNotePromise;
    if (itemInEdit.trans.itemType === 'item') {
      convertToNotePromise = ItemsService.itemToNote(itemInEdit,
                                                     UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'task') {
      convertToNotePromise = ConvertService.finishTaskToNoteConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'list') {
      convertToNotePromise = ConvertService.finishListToNoteConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    if (convertToNotePromise){
      convertToNotePromise.then(function(note){
        $scope.initializeEditor('note', note);
      });
    }
  };

  $scope.convertToTask = function(itemInEdit){
    var convertToTaskPromise;
    if (itemInEdit.trans.itemType === 'item') {
      convertToTaskPromise = ItemsService.itemToTask(itemInEdit,
                                                     UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'note') {
      convertToTaskPromise = ConvertService.finishNoteToTaskConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    } else if (itemInEdit.trans.itemType === 'list') {
      convertToTaskPromise = ConvertService.finishListToTaskConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    if (convertToTaskPromise){
      convertToTaskPromise.then(function(task){
        $scope.initializeEditor('task', task);
      });
    }
  };

  $scope.convertToList = function(itemInEdit){
    var convertToListPromise;
    if (itemInEdit.trans.itemType === 'item'){
      convertToListPromise = ItemsService.itemToList(itemInEdit,
                                                     UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'task') {
      convertToListPromise = ConvertService.finishTaskToListConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'note') {
      convertToListPromise = ConvertService.finishNoteToListConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    if (convertToListPromise){
      convertToListPromise.then(function(list){
        $scope.initializeEditor('list', list);
      });
    }
  };

  // TITLEBAR

  var titleBarInputFocusCallbackFunction;
  var titleBarInputBlurCallbackFunction;

  $scope.registerTitleBarInputCallbacks = function (focusCallback, blurCallback) {
    titleBarInputFocusCallbackFunction = focusCallback;
    titleBarInputBlurCallbackFunction = blurCallback;
  };

  function setFocusOnTitlebarInput() {
    if (typeof titleBarInputFocusCallbackFunction === 'function') titleBarInputFocusCallbackFunction();
  }
  function blurTitlebarInput() {
    if (typeof titleBarInputBlurCallbackFunction === 'function') titleBarInputBlurCallbackFunction();
  }

  $scope.handleBasicTitlebarKeydown = function(keydownEvent/*, item*/){
    // Escape
    if (keydownEvent.keyCode === 27){
      blurTitlebarInput();
    }
  };

  $scope.isTitleClamped = function() {
    return $scope.descriptionFocused || $scope.listPickerOpen;
  };

  // NAVIGATION

  $scope.isFirstSlide = function(){
    return SwiperService.isSlideActive($scope.editorType + 'Editor/basic');
  };

  $scope.swipeToAdvanced = function() {
    SwiperService.swipeTo($scope.editorType + 'Editor/advanced');
  };

  $scope.swipeToBasic = function() {
    SwiperService.swipeTo($scope.editorType + 'Editor/basic');
  };

  $scope.editorSwiperSlideChanged = function(){
    // Need to digest on slide change to get changes to header to bite
    if (!$rootScope.$$phase && !$scope.$$phase)
      $scope.$digest();
  };

  // LIST PICKER WIDGET
  $scope.openListPicker = function() {
    $scope.listPickerOpen = true;
  };
  $scope.closeListPicker = function() {
    $scope.listPickerOpen = false;
  };

  $scope.closeListPickerAndSetListToItem = function(item, list) {

    function doCloseAndSave() {
      $scope.closeListPicker();
      item.trans.list = list;
    }

    if (!list.trans.uuid) {// List is new, save it first. Close list picker on error saving new list.
      $scope.saveList(list).then(doCloseAndSave, $scope.closeListPicker);
    }
    else
      doCloseAndSave();
  };

  $scope.closeListPickerAndClearListFromItem = function(item, list) {
    $scope.closeListPicker();
    if (item.trans.list === list){
      item.trans.list = undefined;
    }
  };

  var editorFooterCloseCallback;
  $scope.registerEditorFooterCloseCallback = function(callback) {
    editorFooterCloseCallback = callback;
  };

  $scope.setTitleBarFocus = function() {
    if (typeof editorFooterCloseCallback === 'function') editorFooterCloseCallback();
  };

  // DESCRIPTION
  $scope.setDescriptionFocus = function(value){
    $scope.descriptionFocused = value;
  };

  // URL
  $scope.setUrlFocus = function(value) {
    $scope.urlFocused = value;
  };

}

EditorController['$inject'] = ['$rootScope', '$scope',
'ConvertService', 'ItemsService', 'SwiperService', 'UISessionService', 'UserSessionService'];
angular.module('em.main').controller('EditorController', EditorController);
<commit_msg>Disable auto focusing title in note editor.<commit_after>/* Copyright 2013-2014 Extended Mind Technologies Oy
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 'use strict';

 function EditorController($rootScope, $scope,
                           ConvertService, ItemsService, SwiperService, UISessionService,
                           UserSessionService) {

  // OPENING, INITIALIZING, CLOSING

  var itemInEdit;
  $scope.initializeEditor = function(editorType, item, mode){
    // Reset $scope variables. These may exist from previous editor.
    $scope.task = undefined;
    $scope.note = undefined;
    $scope.list = undefined;
    $scope.item = undefined;
    $scope.tag = undefined;
    itemInEdit = item;

    $scope.editorType = editorType;
    $scope.editorVisible = true;
    $scope.mode = mode;

    if (editorType === 'task'){
      $scope.task = itemInEdit;
    }else if (editorType === 'note'){
      $scope.note = itemInEdit;
    }else if (editorType === 'list'){
      $scope.list = itemInEdit;
    }else if (editorType === 'item'){
      $scope.item = itemInEdit;
    }else if (editorType === 'tag'){
      $scope.tag = itemInEdit;
    }else if (editorType === 'user'){
      $scope.user = itemInEdit ? itemInEdit : UserSessionService.getUser();
    }
  };

  function editorAboutToClose() {
    if (typeof featureEditorAboutToCloseCallback === 'function') featureEditorAboutToCloseCallback();
  }

  // Callback from Snap.js, outside of AngularJS event loop
  function editorOpened() {
    if ((!itemInEdit || !itemInEdit.deleted) && typeof titleBarInputFocusCallbackFunction === 'function' &&
        $scope.editorType !== 'task' && $scope.editorType !== 'note')
    {
      // Focus on found and not deleted item
      setFocusOnTitlebarInput();
    }
  }

  // Callback from Snap.js, outside of AngularJS event loop
  function editorClosed() {
    // Don't manipulate list items in list before editor has been closed.
    UISessionService.resolveDeferredActions('edit');

    // NOTE: Call $apply here if these don't seem to be reseted
    $scope.editorType = undefined;
    $scope.mode = undefined;
    $scope.editorVisible = false;
    titleBarInputFocusCallbackFunction = titleBarInputBlurCallbackFunction = undefined;
    featureEditorAboutToCloseCallback = undefined;
  }

  if (angular.isFunction($scope.registerEditorAboutToOpenCallback))
    $scope.registerEditorAboutToOpenCallback($scope.initializeEditor, 'EditorController');

  if (angular.isFunction($scope.registerEditorAboutToCloseCallback))
    $scope.registerEditorAboutToCloseCallback(editorAboutToClose, 'EditorController');

  if (angular.isFunction($scope.registerEditorClosedCallback))
    $scope.registerEditorClosedCallback(editorClosed, 'EditorController');

  if (angular.isFunction($scope.registerEditorOpenedCallback))
    $scope.registerEditorOpenedCallback(editorOpened, 'EditorController');

  $scope.$on('$destroy', function() {
    if (angular.isFunction($scope.unregisterEditorAboutToOpenCallback))
      $scope.unregisterEditorAboutToOpenCallback('EditorController');

    if (angular.isFunction($scope.unregisterEditorAboutToCloseCallback))
      $scope.unregisterEditorAboutToCloseCallback('EditorController');

    if (angular.isFunction($scope.unregisterEditorOpenedCallback))
      $scope.unregisterEditorOpenedCallback('EditorController');

    if (angular.isFunction($scope.unregisterEditorClosedCallback))
      $scope.unregisterEditorClosedCallback('EditorController');
  });

  // HELPER METHODS

  var featureEditorAboutToCloseCallback;
  $scope.registerFeatureEditorAboutToCloseCallback = function(callback) {
    featureEditorAboutToCloseCallback = callback;
  };
  $scope.unregisterEditorAboutToCloseCallback = function() {
    featureEditorAboutToCloseCallback = undefined;
  };

  $scope.deferEdit = function(){
    return UISessionService.deferAction('edit', $rootScope.EDITOR_CLOSED_FAILSAFE_TIME);
  };

  // CONVERTING

  $scope.convertToNote = function(itemInEdit){
    var convertToNotePromise;
    if (itemInEdit.trans.itemType === 'item') {
      convertToNotePromise = ItemsService.itemToNote(itemInEdit,
                                                     UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'task') {
      convertToNotePromise = ConvertService.finishTaskToNoteConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'list') {
      convertToNotePromise = ConvertService.finishListToNoteConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    if (convertToNotePromise){
      convertToNotePromise.then(function(note){
        $scope.initializeEditor('note', note);
      });
    }
  };

  $scope.convertToTask = function(itemInEdit){
    var convertToTaskPromise;
    if (itemInEdit.trans.itemType === 'item') {
      convertToTaskPromise = ItemsService.itemToTask(itemInEdit,
                                                     UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'note') {
      convertToTaskPromise = ConvertService.finishNoteToTaskConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    } else if (itemInEdit.trans.itemType === 'list') {
      convertToTaskPromise = ConvertService.finishListToTaskConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    if (convertToTaskPromise){
      convertToTaskPromise.then(function(task){
        $scope.initializeEditor('task', task);
      });
    }
  };

  $scope.convertToList = function(itemInEdit){
    var convertToListPromise;
    if (itemInEdit.trans.itemType === 'item'){
      convertToListPromise = ItemsService.itemToList(itemInEdit,
                                                     UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'task') {
      convertToListPromise = ConvertService.finishTaskToListConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    else if (itemInEdit.trans.itemType === 'note') {
      convertToListPromise = ConvertService.finishNoteToListConvert(itemInEdit,
                                                                    UISessionService.getActiveUUID());
    }
    if (convertToListPromise){
      convertToListPromise.then(function(list){
        $scope.initializeEditor('list', list);
      });
    }
  };

  // TITLEBAR

  var titleBarInputFocusCallbackFunction;
  var titleBarInputBlurCallbackFunction;

  $scope.registerTitleBarInputCallbacks = function (focusCallback, blurCallback) {
    titleBarInputFocusCallbackFunction = focusCallback;
    titleBarInputBlurCallbackFunction = blurCallback;
  };

  function setFocusOnTitlebarInput() {
    if (typeof titleBarInputFocusCallbackFunction === 'function') titleBarInputFocusCallbackFunction();
  }
  function blurTitlebarInput() {
    if (typeof titleBarInputBlurCallbackFunction === 'function') titleBarInputBlurCallbackFunction();
  }

  $scope.handleBasicTitlebarKeydown = function(keydownEvent/*, item*/){
    // Escape
    if (keydownEvent.keyCode === 27){
      blurTitlebarInput();
    }
  };

  $scope.isTitleClamped = function() {
    return $scope.descriptionFocused || $scope.listPickerOpen;
  };

  // NAVIGATION

  $scope.isFirstSlide = function(){
    return SwiperService.isSlideActive($scope.editorType + 'Editor/basic');
  };

  $scope.swipeToAdvanced = function() {
    SwiperService.swipeTo($scope.editorType + 'Editor/advanced');
  };

  $scope.swipeToBasic = function() {
    SwiperService.swipeTo($scope.editorType + 'Editor/basic');
  };

  $scope.editorSwiperSlideChanged = function(){
    // Need to digest on slide change to get changes to header to bite
    if (!$rootScope.$$phase && !$scope.$$phase)
      $scope.$digest();
  };

  // LIST PICKER WIDGET
  $scope.openListPicker = function() {
    $scope.listPickerOpen = true;
  };
  $scope.closeListPicker = function() {
    $scope.listPickerOpen = false;
  };

  $scope.closeListPickerAndSetListToItem = function(item, list) {

    function doCloseAndSave() {
      $scope.closeListPicker();
      item.trans.list = list;
    }

    if (!list.trans.uuid) {// List is new, save it first. Close list picker on error saving new list.
      $scope.saveList(list).then(doCloseAndSave, $scope.closeListPicker);
    }
    else
      doCloseAndSave();
  };

  $scope.closeListPickerAndClearListFromItem = function(item, list) {
    $scope.closeListPicker();
    if (item.trans.list === list){
      item.trans.list = undefined;
    }
  };

  var editorFooterCloseCallback;
  $scope.registerEditorFooterCloseCallback = function(callback) {
    editorFooterCloseCallback = callback;
  };

  $scope.setTitleBarFocus = function() {
    if (typeof editorFooterCloseCallback === 'function') editorFooterCloseCallback();
  };

  // DESCRIPTION
  $scope.setDescriptionFocus = function(value){
    $scope.descriptionFocused = value;
  };

  // URL
  $scope.setUrlFocus = function(value) {
    $scope.urlFocused = value;
  };

}

EditorController['$inject'] = ['$rootScope', '$scope',
'ConvertService', 'ItemsService', 'SwiperService', 'UISessionService', 'UserSessionService'];
angular.module('em.main').controller('EditorController', EditorController);
<|endoftext|>


<commit_before>import {
	module,
	test
} from "qunit";
import {
	runAppend,
	runDestroy,
	getElem,
	cleanOutput,
	buildOwner,
	fixtureElement
} from "test-utils";
import {
	get,
	set,
	setOwner,
	$,
	HTMLBars,
	inject,
	run,
	Component,
	EventDispatcher
} from "ember";
import HotkeyService from "services/HotkeyService";
import CheckBoxComponent from "components/form/CheckBoxComponent";

const { service } = inject;
const { compile } = HTMLBars;

let eventDispatcher, owner, context;

module( "components/form/CheckBoxComponent", {
	beforeEach() {
		eventDispatcher = EventDispatcher.create();
		eventDispatcher.setup( {}, fixtureElement );
		owner = buildOwner();
		owner.register( "event_dispatcher:main", eventDispatcher );
		owner.register( "service:hotkey", HotkeyService );
		owner.register( "component:check-box", CheckBoxComponent );
	},

	afterEach() {
		//noinspection JSUnusedAssignment
		runDestroy( context );
		runDestroy( eventDispatcher );
		runDestroy( owner );
		owner = context = null;
	}
});

test( "CheckBoxComponent", function( assert ) {

	context = Component.extend({
		checked: true,
		disabled: false,
		layout: compile(
			"{{#check-box checked=checked disabled=disabled}}foo{{/check-box}}"
		)
	}).create();
	setOwner( context, owner );

	// initial
	runAppend( context );
	assert.equal(
		cleanOutput( context, ".check-box-component" ),
		"foo",
		"The CheckBoxComponent has a label"
	);
	assert.equal(
		getElem( context, ".check-box-component" ).hasClass( "checked" ),
		true,
		"The CheckBoxComponent's checked state is set on initialization"
	);

	// set to false in context
	run(function() {
		set( context, "checked", false );
	});
	assert.equal(
		getElem( context, ".check-box-component" ).hasClass( "checked" ),
		false,
		"The CheckBoxComponent reacts to changes of the checked attribute"
	);

	// toggle by clicking the CheckBoxComponent
	run(function() {
		getElem( context, ".check-box-component" ).click();
	});
	assert.equal(
		getElem( context, ".check-box-component" ).hasClass( "checked" ),
		true,
		"The CheckBoxComponent was clicked and checked is now true"
	);
	assert.equal(
		get( context, "checked" ),
		true,
		"The context's checked variable is true as well"
	);

	// disable CheckBoxComponent
	run(function() {
		set( context, "disabled", true );
	});
	assert.equal(
		getElem( context, ".check-box-component" ).hasClass( "disabled" ),
		true,
		"The CheckBoxComponent is now disabled"
	);

	// try to click the disabled CheckBoxComponent
	run(function() {
		getElem( context, ".check-box-component" ).click();
	});
	assert.equal(
		getElem( context, ".check-box-component" ).hasClass( "checked" ),
		true,
		"The disabled CheckBoxComponent can't be clicked"
	);
	assert.equal(
		get( context, "checked" ),
		true,
		"The context's checked variable is still true"
	);

});

test( "CheckBoxComponent - without block", function( assert ) {

	context = Component.extend({
		label: "foo",
		layout: compile(
			"{{check-box label}}"
		)
	}).create();
	setOwner( context, owner );

	runAppend( context );
	assert.equal(
		cleanOutput( context, ".check-box-component" ),
		"foo",
		"The CheckBoxComponent has a label"
	);

	run(function() {
		set( context, "label", "bar" );
	});
	assert.equal(
		cleanOutput( context, ".check-box-component" ),
		"bar",
		"The label updates"
	);

});

test( "Hotkeys", assert => {

	let e;

	context = Component.extend({
		checked: false,
		disabled: false,
		layout: compile( "{{check-box 'foo' checked=checked disabled=disabled}}" ),

		hotkey: service(),
		keyUp( e ) {
			return get( this, "hotkey" ).trigger( e );
		}
	}).create();
	setOwner( context, owner );
	runAppend( context );

	const $context = context.$();
	const $elem = $context.find( ".check-box-component" );

	const trigger = code => run( () => {
		e = $.Event( "keyup" );
		e.code = code;
		$context.trigger( e );
	});

	assert.notStrictEqual( document.activeElement, $elem[0], "Is not focused initially" );
	assert.strictEqual( get( context, "checked" ), false, "Is not checked initially" );
	assert.strictEqual( $elem.attr( "tabindex" ), "0", "Has a tabindex attribute with value 0" );

	trigger( "Space" );
	assert.strictEqual( get( context, "checked" ), false, "Is still not checked on Space" );
	assert.notOk( e.isDefaultPrevented(), "Doesn't prevent event's default action" );
	assert.notOk( e.isImmediatePropagationStopped(), "Doesn't stop event's propagation" );

	$elem.focus();
	assert.strictEqual( document.activeElement, $elem[0], "Is focused now" );

	trigger( "Space" );
	assert.strictEqual( get( context, "checked" ), true, "Is checked on Space" );
	assert.ok( e.isDefaultPrevented(), "Prevents event's default action" );
	assert.ok( e.isImmediatePropagationStopped(), "Stops event's propagation" );
	trigger( "Space" );
	assert.strictEqual( get( context, "checked" ), false, "Is not checked anymore on Space" );
	assert.ok( e.isDefaultPrevented(), "Prevents event's default action" );
	assert.ok( e.isImmediatePropagationStopped(), "Stops event's propagation" );

	trigger( "Escape" );
	assert.notStrictEqual( document.activeElement, $elem[0], "Removes focus on Escape" );

	run( () => set( context, "disabled", true ) );
	$elem.focus();
	assert.strictEqual( document.activeElement, $elem[0], "Is focused now" );

	trigger( "Space" );
	assert.strictEqual( get( context, "checked" ), false, "Is not checked on Space when disabled" );
	assert.notOk( e.isDefaultPrevented(), "Doesn't prevent event's default action" );
	assert.notOk( e.isImmediatePropagationStopped(), "Doesn't stop event's propagation" );

	trigger( "Escape" );
	assert.notStrictEqual( document.activeElement, $elem[0], "Removes focus on Escape" );

});
<commit_msg>Fix CheckBoxComponent tests<commit_after>import {
	moduleForComponent,
	test
} from "ember-qunit";
import {
	buildResolver,
	hbs
} from "test-utils";
import $ from "jquery";
import HotkeyService from "services/HotkeyService";
import CheckBoxComponent from "components/form/CheckBoxComponent";

moduleForComponent( "components/form/CheckBoxComponent", {
	integration: true,
	resolver: buildResolver({
		CheckBoxComponent,
		HotkeyService
	}),
	beforeEach() {
		this.inject.service( "hotkey" );
	}
});

test( "CheckBoxComponent", function( assert ) {

	this.set( "checked", true );
	this.set( "disabled", false );
	this.render( hbs`{{#check-box checked=checked disabled=disabled}}foo{{/check-box}}` );
	const $elem = this.$( ".check-box-component" );

	assert.ok( $elem.get( 0 ) instanceof HTMLElement, "Component renders" );
	assert.strictEqual( $elem.text(), "foo", "Has a label" );
	assert.ok( $elem.hasClass( "checked" ), "Is checked initially" );

	// set to false in context
	this.set( "checked", false );
	assert.notOk( $elem.hasClass( "checked" ), "Is not checked anymore after binding has changed" );

	// toggle by clicking the CheckBoxComponent
	$elem.click();
	assert.ok( $elem.hasClass( "checked" ), "Clicking toggles checked state" );
	assert.ok( this.get( "checked" ), "Clicking also updated binding" );

	// disable CheckBoxComponent
	this.set( "disabled", true );
	assert.ok( $elem.hasClass( "disabled" ), "Is now disabled" );

	// try to click the disabled CheckBoxComponent
	$elem.click();
	assert.ok( $elem.hasClass( "checked" ), "Clicking while being disabled doesn't do anything" );
	assert.ok( this.get( "checked" ), "The binding also doesn't change" );

});

test( "CheckBoxComponent - without block", function( assert ) {

	this.set( "label", "foo" );
	this.render( hbs`{{check-box label}}` );
	const $elem = this.$( ".check-box-component" );

	assert.strictEqual( $elem.text(), "foo", "Has a label" );

	this.set( "label", "bar" );
	assert.strictEqual( $elem.text(), "bar", "Label gets updated" );

});

test( "Hotkeys", function( assert ) {

	let event;
	const hotkeyService = this.container.lookup( "service:hotkey" );

	function trigger( code ) {
		const event = $.Event( "keyup" );
		event.code = code;
		hotkeyService.trigger( event );
		return event;
	}

	this.set( "checked", false );
	this.set( "disabled", false );
	this.render( hbs`{{check-box "foo" checked=checked disabled=disabled}}` );

	const $elem = this.$( ".check-box-component" );
	const document = $elem.get( 0 ).ownerDocument;

	assert.notStrictEqual( document.activeElement, $elem[0], "Is not focused initially" );
	assert.notOk( this.get( "checked" ), "Is not checked initially" );
	assert.strictEqual( $elem.attr( "tabindex" ), "0", "Has a tabindex attribute with value 0" );

	event = trigger( "Space" );
	assert.notOk( this.get( "checked" ), "Is still not checked on Space" );
	assert.notOk( event.isDefaultPrevented(), "Doesn't prevent event's default action" );
	assert.notOk( event.isImmediatePropagationStopped(), "Doesn't stop event's propagation" );

	$elem.focus();
	assert.strictEqual( document.activeElement, $elem[0], "Is focused now" );

	event = trigger( "Space" );
	assert.ok( this.get( "checked" ), "Is checked on Space" );
	assert.ok( event.isDefaultPrevented(), "Prevents event's default action" );
	assert.ok( event.isImmediatePropagationStopped(), "Stops event's propagation" );
	event = trigger( "Space" );
	assert.notOk( this.get( "checked" ), "Is not checked anymore on Space" );
	assert.ok( event.isDefaultPrevented(), "Prevents event's default action" );
	assert.ok( event.isImmediatePropagationStopped(), "Stops event's propagation" );

	trigger( "Escape" );
	assert.notStrictEqual( document.activeElement, $elem[0], "Removes focus on Escape" );

	this.set( "disabled", true );
	$elem.focus();
	assert.strictEqual( document.activeElement, $elem[0], "Is focused now" );

	event = trigger( "Space" );
	assert.notOk( this.get( "checked" ), "Is not checked on Space while being disabled" );
	assert.notOk( event.isDefaultPrevented(), "Doesn't prevent event's default action" );
	assert.notOk( event.isImmediatePropagationStopped(), "Doesn't stop event's propagation" );

	trigger( "Escape" );
	assert.notStrictEqual( document.activeElement, $elem[0], "Removes focus on Escape" );

});
<|endoftext|>


<commit_before>'use strict';

// modules dependencies
var mongoat = require('../../index');

var _this;

// test update method
describe('Update', function () {
  // connect to db before all tests
  beforeAll(function (done) {
    _this = this;

    mongoat.MongoClient.connect('mongodb://localhost:27017/mongoatTest')
    .then(function (db) {
      db.dropDatabase();
      _this.testDb = db;
      _this.testCol = db.collection('Person.update');
      _this.testCol.datetime(true);

      done();
    });
  });

  // close db after all tests
  afterAll(function (done) {
    _this.testCol.find()
    .nextObject()
    .then(function (mongObject) {
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAl');
      expect(mongObject.age).toBe(25);
      expect(mongObject.company).toBe('Dial Once');
      expect(mongObject.job).toBe('software engineer');
      expect(mongObject.createdAt).toBeDefined();

      _this.testDb.dropDatabase();
      _this.testDb.close();

      done();
    });
  });

  // test update without hooks
  it('should upsert new document to collection',
  function (done) {
    _this.testCol.update(
      { firstName: 'Yacine' },
      { $setOnInsert: { lastName: 'KHATAl', age: 25 } },
      { upsert: true }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(0);

      done();
    });
  });

  // test with multiple before and after update hooks
  it('should update document from collection and handle before and after update hooks',
  function (done) {
    // add before update hooks
    _this.testCol.before('update', function (object) {
      expect(object.$set.job).toBe('software engineer');
      return object;
    });

    _this.testCol.before('update', function (object) {
      expect(object.$set.job).toBe('software engineer');
      object.$set.company = 'Dial Once';
      return object;
    });

    // add after update hooks
    _this.testCol.after('update', function (object) {
      expect(object.$set.job).toBe('software engineer');
      expect(object.$set.company).toBe('Dial Once');
      return object;
    });

    _this.testCol.after('update', function (object) {
      expect(object.$set.job).toBe('software engineer');
      expect(object.$set.company).toBe('Dial Once');
      return object;
    });

    _this.testCol.update(
      { firstName: 'Yacine' },
      { $set: { job: 'software engineer' } }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(1);

      done();
    });
  });
});
<commit_msg>Enable versioning to improve coverage<commit_after>'use strict';

// modules dependencies
var mongoat = require('../../index');

var _this;

// test update method
describe('Update', function () {
  // connect to db before all tests
  beforeAll(function (done) {
    _this = this;

    mongoat.MongoClient.connect('mongodb://localhost:27017/mongoatTest')
    .then(function (db) {
      db.dropDatabase();
      _this.testDb = db;
      _this.testCol = db.collection('Person.update');
      _this.testCol.datetime(true);
      _this.testCol.version(true);

      done();
    });
  });

  // close db after all tests
  afterAll(function (done) {
    _this.testCol.find()
    .nextObject()
    .then(function (mongObject) {
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAl');
      expect(mongObject.age).toBe(25);
      expect(mongObject.company).toBe('Dial Once');
      expect(mongObject.job).toBe('software engineer');
      expect(mongObject.createdAt).toBeDefined();

      _this.testDb.dropDatabase();
      _this.testDb.close();

      done();
    });
  });

  // test update without hooks
  it('should upsert new document to collection',
  function (done) {
    _this.testCol.update(
      { firstName: 'Yacine' },
      { firstName: 'Yacine', lastName: 'KHATAl', age: 25 },
      { upsert: true }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(0);

      done();
    });
  });

  // test with multiple before and after update hooks
  it('should update document from collection and handle before and after update hooks',
  function (done) {
    // add before update hooks
    _this.testCol.before('update', function (object) {
      expect(object.$set.job).toBe('software engineer');
      return object;
    });

    _this.testCol.before('update', function (object) {
      expect(object.$set.job).toBe('software engineer');
      object.$set.company = 'Dial Once';
      return object;
    });

    // add after update hooks
    _this.testCol.after('update', function (object) {
      expect(object.$set.job).toBe('software engineer');
      expect(object.$set.company).toBe('Dial Once');
      return object;
    });

    _this.testCol.after('update', function (object) {
      expect(object.$set.job).toBe('software engineer');
      expect(object.$set.company).toBe('Dial Once');
      return object;
    });

    _this.testCol.update(
      { firstName: 'Yacine' },
      { $set: { job: 'software engineer' } }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(1);

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


<commit_before>import React from "react"
import { Link, graphql } from "gatsby"

import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm, scale } from "../utils/typography"

class AdventTemplate extends React.Component {
  render() {
    const post = this.props.data.markdownRemark
    const siteTitle = this.props.data.site.siteMetadata.title
    const { previous, next } = this.props.pageContext

    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO
          title={post.frontmatter.title}
          description={post.frontmatter.description || post.excerpt}
        />

              {post.frontmatter.date}

Preface

            I'm doing writeups for the Advent of Code 2020 in Clojure.  I'm trying to cultivate a Digital Garden, so
            this is mostly for me, but if you like it, that's cool too.

          <ul
            style={{
              display: `flex`,
              flexWrap: `wrap`,
              justifyContent: `space-between`,
              listStyle: `none`,
              padding: 0,
            }}
          >

              {previous && (
                <Link to={previous.fields.slug} rel="prev">
                  ← {previous.frontmatter.title}
                </Link>
              )}

              {next && (
                <Link to={next.fields.slug} rel="next">
                  {next.frontmatter.title} →
                </Link>
              )}

      </Layout>
    )
  }
}

export default AdventTemplate

export const pageQuery = graphql`
  query AdventBySlug($slug: String!) {
    site {
      siteMetadata {
        title
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt(pruneLength: 160)
      html
      frontmatter {
        title
        date(formatString: "MMMM DD, YYYY")
        description
      }
    }
  }
`
<commit_msg>Update advent template<commit_after>import React from "react"
import { Link, graphql } from "gatsby"

import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm, scale } from "../utils/typography"

class AdventTemplate extends React.Component {
  render() {
    const post = this.props.data.markdownRemark
    const siteTitle = this.props.data.site.siteMetadata.title
    const { previous, next } = this.props.pageContext

    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO
          title={post.frontmatter.title}
          description={post.frontmatter.description || post.excerpt}
        />

              {post.frontmatter.date}

Preface

            I'm doing writeups for the Advent of Code 2020 in Clojure. This is mostly
            to motivate me, but if you like it, that's cool too.

            All the code is on <Link href="https://github.com/bolivier/advent-of-code-2020">my Github</Link>

          <ul
            style={{
              display: `flex`,
              flexWrap: `wrap`,
              justifyContent: `space-between`,
              listStyle: `none`,
              padding: 0,
            }}
          >

              {previous && (
                <Link to={previous.fields.slug} rel="prev">
                  ← {previous.frontmatter.title}
                </Link>
              )}

              {next && (
                <Link to={next.fields.slug} rel="next">
                  {next.frontmatter.title} →
                </Link>
              )}

      </Layout>
    )
  }
}

export default AdventTemplate

export const pageQuery = graphql`
  query AdventBySlug($slug: String!) {
    site {
      siteMetadata {
        title
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt(pruneLength: 160)
      html
      frontmatter {
        title
        date(formatString: "MMMM DD, YYYY")
        description
      }
    }
  }
`
<|endoftext|>


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

  // var t = new Date();

  if (!d3.custom) d3.custom = {};

  d3.custom.lineChart = function module() {

    // default values. may be overwritten by exported functions.

    var margin = {top: 8, right: 8, bottom: 40, left: 40},
        width = d3.select(this)[0].parentNode.offsetWidth - ((margin.left + margin.right)*3) - 1,
        height = 120,
        xScale = d3.time.scale().range([0, width]),
        yScale = d3.scale.linear().range([height, 0]),
        xAxis = d3.svg.axis().scale(xScale).orient("bottom").ticks(Math.max(width/75, 2)),
        yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(Math.max(height/25, 2)),
        brush = d3.svg.brush(),
        extent = [];

    var svg;

    var dispatch = d3.dispatch('brushended', 'ready');

    function exports(selection) {
      selection.each(function (d, i) {
        if (!d) return;

        var data = d.data;

        if (extent.length) {
          xScale.domain(extent);
        } else {
          xScale.domain(d3.extent([].concat.apply([], data.map(function(d) {return [d.starttime, d.endtime]; }))));
        }

        var line = d3.svg.line().interpolate("linear")
          .x(function(d) { return xScale(d[0]); })
          .y(function(d) { return yScale(d[1]); });

        // FIXME this is probably wrong and causes all the trouble
        if (!d3.select(this).select('svg')[0][0]) { // why not (!svg)?
          svg = d3.select(this)
                .append('svg')
                  .classed('chart', true)
                  .attr("width", width + margin.left + margin.right)
                  .attr("height", height + margin.top + margin.bottom)
                .append("defs")
                .append("clipPath")
                  .attr("id", "clip")
                .append("rect")
                  .attr("width", width)
                  .attr("height", height);
        }

        d3.select(this).selectAll('svg').selectAll("*").remove();
        // svg.selectAll("*").remove(); // why not?

        svg = d3.select(this).selectAll('svg').data(data);

        brush.x(xScale)
          .on("brushend", brushended);

        var chart = svg.append("g")
          .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
          .attr("class", "chart");

        // x-graph
        chart.append("path")
          .attr("class", "line line0")
          .attr("clip-path", "url(#clip)")
          .attr("d", line(data.map(function(d) { return [d.ts, d.avgx]; })));

        // y-graph
        chart.append("path")
          .attr("class", "line line1")
          .attr("clip-path", "url(#clip)")
          .attr("d", line(data.map(function(d) { return [d.ts, d.avgy]; })));

        // z-graph
        chart.append("path")
          .attr("class", "line line2")
          .attr("clip-path", "url(#clip)")
          .attr("d", line(data.map(function(d) { return [d.ts, d.avgz]; })));

        // x-axis
        chart.append("g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + (height + 3) + ")")
          .call(xAxis);

        // y-axis
        chart.append("g")
          .attr("class", "y axis")
          .attr("transform", "translate(-3,0)")
          .call(yAxis);

        chart.append("g")
          .attr("class", "brush")
          .call(brush)
        .selectAll("rect")
          .attr("height", height);

        var chartName = this.getAttribute('data').split('.')[1].toUpperCase();

        // console.log(chartName, 'CHART DOMAIN XSCALE', xScale.domain().map(function (d) { return +d; }));
        // console.log(chartName, 'CHART DOMAIN YSCALE', yScale.domain());
        // console.log(chartName, 'CHART RANGE  XSCALE', xScale.range());
        // console.log(chartName, 'CHART RANGE  YSCALE', yScale.range());
        // console.log('--------------------------------------------------');

        function brushended() {
          // if (brush.empty()) return;

          // console.log('CHART EXTENT 1:', brush.extent().map(function (d) { return +d; }), extent);
          dispatch.brushended(brush.empty() ? [] : brush.extent().map(function (d) { return +d; }));

          // this first clears the brush, and then tells it to redraw
          // https://groups.google.com/d/msg/d3-js/SN4-kJD6_2Q/SmQNwLm-5bwJ
          chart.select(".brush").call(brush.clear());

          console.log(chartName, brush.extent());
          xScale.domain(brush.extent()); // scale the x-domain to the extent (zoom in)

          // transition is for debugging purposes (to see what's happening)

          // FIXME only the axis of the last drawn chart is selected, not 'this' one
          svg.transition().duration(2000).select(".x.axis").call(xAxis);

          svg.transition().duration(2000).selectAll(".line0").attr("d", line(data.map(function (d) { return [d.ts, d.avgx]; })));
          svg.transition().duration(2000).selectAll(".line1").attr("d", line(data.map(function (d) { return [d.ts, d.avgy]; })));
          svg.transition().duration(2000).selectAll(".line2").attr("d", line(data.map(function (d) { return [d.ts, d.avgz]; })));

          // chart.select("brush").call(brush.clear()); // clear brush
          // chart.select("brush").call(brush); // call new brush
          // svg.select(".x.axis").call(xAxis); // rescale x axis

          // scale x domain to brush extent (zoom in)
          // brush.clear();

          // transition_data();
          // reset_axis();
          // brush.clear();
          // chart.select("brush").call(brush.clear());
          // chart.select("brush").call(brush);

          // redraw the graph lines

          // brush.clear();
          // reset_axis();
          // clear the brush and call a new one
          // chart.select("brush").call(brush.clear());
          // chart.select("brush").call(brush);
          // clear_brush();
        }

        // function transition_data() {
        //   this.svg.selectAll(".line0").attr("d", line(data.map(function (d) { return [d.ts, d.avgx]; })));
        //   this.svg.selectAll(".line1").attr("d", line(data.map(function (d) { return [d.ts, d.avgy]; })));
        //   this.svg.selectAll(".line2").attr("d", line(data.map(function (d) { return [d.ts, d.avgz]; })));
        // }

        // function reset_axis() {
        //   svg.transition().duration(500)
        //     .select(".x.axis")
        //     .call(xAxis);
        // }

        // function clear_brush() {
        //   if (!data) { console.error('clear_brush has no data'); return; }
        //   xScale.domain(d3.extent([].concat.apply([], data.map(function (d) { return [d.starttime, d.endtime]; }))));
        //   transition_data();
        //   reset_axis();
        // }
      });
    }

    // clear brush means:
    // 1. scale x domain to extent
    // 2. draw the three lines
    // 3. call x axis

    exports.width = function(_) {
      if (!arguments.length) return width;
      width = parseInt(_);
      return this;
    };

    exports.height = function(_) {
      if (!arguments.length) return height;
      height = parseInt(_);
      duration = 0;
      return this;
    };

    // to update the x-domain with the brush'es extent
    exports.xScale = function(_) {
      if (!arguments.length) return xScale;
      xScale = xScale.domain(_);
      return this;
    };

    // to update the y-domain when it changes FIXME: release watch after final
    // y-domain has been calculated (or better calculate all domains first and
    // then draw stuff)
    exports.yScale = function(_) {
      if (!arguments.length) return yScale;
      yScale = yScale.range([height, 0]).domain(_);
      return this;
    };

    exports.extent = function(_) {
      if (!arguments.length) return extent; // currently set value
      // console.log('CHART EXTENT 5:', _, extent);
      extent = _;
      return this;
    };

    d3.rebind(exports, dispatch, 'on');
    return exports;
  };

  // document.addEventListener("DOMContentLoaded", function (event) {
  //   console.log("DOM fully loaded and parsed after " + (event.timeStamp - t) + " ms");
  // });

}());
<commit_msg>Minor changes.<commit_after>(function () {
  'use strict';

  // var t = new Date();

  if (!d3.custom) d3.custom = {};

  d3.custom.lineChart = function module() {

    // default values. may be overwritten by exported functions.

    var margin = {top: 8, right: 8, bottom: 40, left: 40},
        width = d3.select(this)[0].parentNode.offsetWidth - ((margin.left + margin.right)*3) - 1,
        height = 120,
        xScale = d3.time.scale().range([0, width]),
        yScale = d3.scale.linear().range([height, 0]),
        xAxis = d3.svg.axis().scale(xScale).orient("bottom").ticks(Math.max(width/75, 2)),
        yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(Math.max(height/25, 2)),
        brush = d3.svg.brush(),
        extent = [];

    var svg;

    var dispatch = d3.dispatch('brushended', 'ready');

    function exports(selection) {
      selection.each(function (d, i) {
        if (!d) return;

        var data = d.data;

        if (extent.length) {
          xScale.domain(extent);
        } else {
          xScale.domain(d3.extent([].concat.apply([], data.map(function(d) {return [d.starttime, d.endtime]; }))));
        }

        var line = d3.svg.line().interpolate("linear")
          .x(function(d) { return xScale(d[0]); })
          .y(function(d) { return yScale(d[1]); });

        // FIXME this is probably wrong and causes all the trouble
        if (!d3.select(this).select('svg')[0][0]) { // why not (!svg)?
          svg = d3.select(this)
                .append('svg')
                  .classed('chart', true)
                  .attr("width", width + margin.left + margin.right)
                  .attr("height", height + margin.top + margin.bottom)
                .append("defs")
                .append("clipPath")
                  .attr("id", "clip")
                .append("rect")
                  .attr("width", width)
                  .attr("height", height);
        }

        d3.select(this).selectAll('svg').selectAll("*").remove();
        // svg.selectAll("*").remove(); // why not?

        svg = d3.select(this).selectAll('svg').data(data);

        brush.x(xScale)
          .on("brushend", brushended);

        var chart = svg.append("g")
          .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
          .attr("class", "chart");

        // x-graph
        chart.append("path")
          .attr("class", "line line0")
          .attr("clip-path", "url(#clip)")
          .attr("d", line(data.map(function(d) { return [d.ts, d.avgx]; })));

        // y-graph
        chart.append("path")
          .attr("class", "line line1")
          .attr("clip-path", "url(#clip)")
          .attr("d", line(data.map(function(d) { return [d.ts, d.avgy]; })));

        // z-graph
        chart.append("path")
          .attr("class", "line line2")
          .attr("clip-path", "url(#clip)")
          .attr("d", line(data.map(function(d) { return [d.ts, d.avgz]; })));

        // x-axis
        chart.append("g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + (height + 3) + ")")
          .call(xAxis);

        // y-axis
        chart.append("g")
          .attr("class", "y axis")
          .attr("transform", "translate(-3,0)")
          .call(yAxis);

        chart.append("g")
          .attr("class", "brush")
          .call(brush)
        .selectAll("rect")
          .attr("height", height);

        var chartName = this.getAttribute('data').split('.')[1].toUpperCase();

        // console.log(chartName, 'CHART DOMAIN XSCALE', xScale.domain().map(function (d) { return +d; }));
        // console.log(chartName, 'CHART DOMAIN YSCALE', yScale.domain());
        // console.log(chartName, 'CHART RANGE  XSCALE', xScale.range());
        // console.log(chartName, 'CHART RANGE  YSCALE', yScale.range());
        // console.log('--------------------------------------------------');

        function brushended() {
          // if (brush.empty()) return;

          // console.log('CHART EXTENT 1:', brush.extent().map(function (d) { return +d; }), extent);
          dispatch.brushended(brush.empty() ? [] : brush.extent().map(function (d) { return +d; }));

          // this first clears the brush, and then tells it to redraw
          // https://groups.google.com/d/msg/d3-js/SN4-kJD6_2Q/SmQNwLm-5bwJ
          chart.select(".brush").call(brush.clear());

          console.log(chartName, brush.extent());
          xScale.domain(brush.extent()); // scale the x-domain to the extent (zoom in)

          // transition is for debugging purposes (to see what's happening)

          // FIXME only the axis of the last drawn chart is selected, not 'this' one
          svg.transition().duration(2000).select(".x.axis").call(xAxis);

          svg.transition().duration(2000).selectAll(".line0").attr("d", line(data.map(function (d) { return [d.ts, d.avgx]; })));
          svg.transition().duration(2000).selectAll(".line1").attr("d", line(data.map(function (d) { return [d.ts, d.avgy]; })));
          svg.transition().duration(2000).selectAll(".line2").attr("d", line(data.map(function (d) { return [d.ts, d.avgz]; })));

          // chart.select("brush").call(brush.clear()); // clear brush
          // chart.select("brush").call(brush); // call new brush
          // svg.select(".x.axis").call(xAxis); // rescale x axis

          // scale x domain to brush extent (zoom in)
          // brush.clear();

          // transition_data();
          // reset_axis();
          // brush.clear();
          // chart.select("brush").call(brush.clear());
          // chart.select("brush").call(brush);

          // redraw the graph lines

          // brush.clear();
          // reset_axis();
          // clear the brush and call a new one
          // chart.select("brush").call(brush.clear());
          // chart.select("brush").call(brush);
          // clear_brush();
        }

        // function transition_data() {
        //   this.svg.selectAll(".line0").attr("d", line(data.map(function (d) { return [d.ts, d.avgx]; })));
        //   this.svg.selectAll(".line1").attr("d", line(data.map(function (d) { return [d.ts, d.avgy]; })));
        //   this.svg.selectAll(".line2").attr("d", line(data.map(function (d) { return [d.ts, d.avgz]; })));
        // }

        // function reset_axis() {
        //   svg.transition().duration(500)
        //     .select(".x.axis")
        //     .call(xAxis);
        // }

        // function clear_brush() {
        //   if (!data) { console.error('clear_brush has no data'); return; }
        //   xScale.domain(d3.extent([].concat.apply([], data.map(function (d) { return [d.starttime, d.endtime]; }))));
        //   transition_data();
        //   reset_axis();
        // }
      });
    }

    // clear brush means:
    // 1. scale x domain to extent
    // 2. draw the three lines
    // 3. call x axis

    exports.width = function(_) {
      if (!arguments.length) return width;
      width = parseInt(_);
      return this;
    };

    exports.height = function(_) {
      if (!arguments.length) return height;
      height = parseInt(_);
      duration = 0;
      return this;
    };

    // to update the x-domain with the brush'es extent
    exports.xScale = function(_) {
      if (!arguments.length) return xScale;
      xScale = xScale.domain(_);
      return this;
    };

    // to update the y-domain when it changes
    exports.yScale = function(_) {
      if (!arguments.length) return yScale;
      yScale = yScale.domain(_);
      return this;
    };

    exports.extent = function(_) {
      if (!arguments.length) return extent; // currently set value
      // console.log('CHART EXTENT 5:', _, extent);
      extent = _;
      return this;
    };

    d3.rebind(exports, dispatch, 'on');
    return exports;
  };

  // document.addEventListener("DOMContentLoaded", function (event) {
  //   console.log("DOM fully loaded and parsed after " + (event.timeStamp - t) + " ms");
  // });

}());
<|endoftext|>


<commit_before>'use strict';
const Base = require('yeoman-generator').Base;

const plugins = require('../../utils/types').plugin;
const stringifyAble = require('../../utils').stringifyAble;
const path = require('path');

function toJSONFromText(text) {
  const r = {};
  text.split('\n').forEach((line) => {
    const trimmedLine = line.trim();
    if (trimmedLine.length === 0) { // ignore empty lines (e.g. new line added by editor)
      return;
    }

    const splitPoint = trimmedLine.indexOf('=');
    const key = trimmedLine.slice(0, splitPoint);
    let value = trimmedLine.slice(splitPoint + 1);
    value = value.trim();
    if (!isNaN(parseFloat(value))) {
      value = parseFloat(value);
    }
    let obj = r;
    const keys = key.trim().split('.');
    keys.slice(0, keys.length - 1).forEach((k) => {
      if (!(k in obj)) {
        obj[k] = {};
      }
      obj = obj[k];
    });
    obj[keys[keys.length - 1]] = value;
  });
  return r;
}

class Generator extends Base {
  constructor(args, options) {
    super(args, options);
    this.basetype = 'web';
    this.new_ = null;
  }

  prompting() {
    const type = this.config.get('type');
    const isHybridType = plugins.isTypeHybrid({type});

    return this.prompt([{
      type: 'list',
      name: 'basetype',
      choices: ['web', 'server'],
      message: 'Base Type',
      default: 'web',
      when: isHybridType
    }, {
      name: 'type',
      message: 'Type',
      required: true
    }, {
      name: 'id',
      message: 'ID',
      required: true
    }, {
      name: 'module',
      message: 'File Name',
      required: true
    }, {
      type: 'editor',
      name: 'extras',
      message: 'Extras (key=value)'
    }]).then((props) => {
      this.basetype = 'web';
      if ((isHybridType && props.basetype === 'server') || (!isHybridType && plugins.isTypeServer({type}))) {
        this.basetype = 'server';
      }
      this.new_ = {
        type: props.type,
        id: props.id,
        module: props.module,
        extras: toJSONFromText(props.extras)
      };
    });
  }

  writing() {
    const basekey = this.basetype === 'web' ? 'extensions' : 'sextensions';
    const arr = this.config.get(basekey);
    arr.push(this.new_);
    this.config.set(basekey, arr);

    // inject new extension

    const d = stringifyAble(this.new_);

    if (this.basetype === 'web') {
      this._injectWebExtension(d);
    } else {
      this._injectServerExtension(d);
    }
  }

  _injectWebExtension(d) {
    const file = this.destinationPath('phovea.js');
    const old = this.fs.read(file);
    const absFile = d.module.startsWith('~') ? d.module.slice(1) : `./src/${d.module.includes('.') ? d.module.slice(0, d.module.lastIndexOf('.')) : d.module}`;
    const text = `\n\n  registry.push('${d.type}', '${d.id}', function() { return import('${absFile}'); }, ${d.stringify(d.extras, ' ')});\n  // generator-phovea:end`;
    const new_ = old.replace('  // generator-phovea:end', text);
    this.fs.write(file, new_);

    const target = this.destinationPath(`src/${d.module}${d.module.includes('.') ? '' : '.ts'}`);
    if (absFile.startsWith('.') && !this.fs.exists(target)) {
      let source = this.templatePath(`${d.type}.tmpl.ts`);
      if (!this.fs.exists(source)) {
        source = this.templatePath('template.tmpl.ts');
      }
      this.fs.copyTpl(source, target, Object.assign({
        moduleName: path.basename(target, path.extname(target))
      }, d));
    }
  }

  _injectServerExtension(d) {
    const name = this.config.get('name');
    const file = this.destinationPath(`${name}/__init__.py`);
    const old = this.fs.read(file);
    const text = `\n\n  registry.append('${d.type}', '${d.id}', '${name}.${d.module}', ${d.stringifyPython(d.extras, '  ')})\n  # generator-phovea:end`;
    const new_ = old.replace('  # generator-phovea:end', text);
    this.fs.write(file, new_);

    const target = this.destinationPath(`${name}/${d.module}.py`);
    if (!this.fs.exists(target)) {
      let source = this.templatePath(`${d.type}.tmpl.py`);
      if (!this.fs.exists(source)) {
        source = this.templatePath('template.tmpl.py');
      }
      this.fs.copyTpl(source, target, Object.assign({
        moduleName: path.basename(target, path.extname(target))
      }, d));
    }
  }
}

module.exports = Generator;
<commit_msg>implemented comment<commit_after>'use strict';
const Base = require('yeoman-generator').Base;

const plugins = require('../../utils/types').plugin;
const stringifyAble = require('../../utils').stringifyAble;
const path = require('path');

function toJSONFromText(text) {
  const r = {};
  text.split('\n').forEach((line) => {
    const trimmedLine = line.trim();
    if (trimmedLine.length === 0) { // ignore empty lines (e.g. new line added by editor)
      return;
    }

    const splitPoint = trimmedLine.indexOf('=');
    const key = trimmedLine.slice(0, splitPoint);
    let value = trimmedLine.slice(splitPoint + 1);
    value = value.trim();
    if (!isNaN(parseFloat(value))) {
      value = parseFloat(value);
    }
    let obj = r;
    const keys = key.trim().split('.');
    keys.slice(0, keys.length - 1).forEach((k) => {
      if (!(k in obj)) {
        obj[k] = {};
      }
      obj = obj[k];
    });
    obj[keys[keys.length - 1]] = value;
  });
  return r;
}

class Generator extends Base {
  constructor(args, options) {
    super(args, options);
    this.basetype = 'web';
    this.new_ = null;
  }

  prompting() {
    const type = this.config.get('type');
    const isHybridType = plugins.isTypeHybrid({type});

    return this.prompt([{
      type: 'list',
      name: 'basetype',
      choices: ['web', 'server'],
      message: 'Base Type',
      default: 'web',
      when: isHybridType
    }, {
      name: 'type',
      message: 'Type',
      required: true
    }, {
      name: 'id',
      message: 'ID',
      required: true
    }, {
      name: 'module',
      message: 'File Name',
      required: true
    }, {
      type: 'editor',
      name: 'extras',
      message: 'Extras (key=value)'
    }]).then((props) => {
      this.basetype = 'web';
      if ((isHybridType && props.basetype === 'server') || (!isHybridType && plugins.isTypeServer({type}))) {
        this.basetype = 'server';
      }
      this.new_ = {
        type: props.type,
        id: props.id,
        module: props.module,
        extras: toJSONFromText(props.extras)
      };
    });
  }

  writing() {
    const basekey = this.basetype === 'web' ? 'extensions' : 'sextensions';
    const arr = this.config.get(basekey);
    arr.push(this.new_);
    this.config.set(basekey, arr);

    // inject new extension

    const d = stringifyAble(this.new_);

    if (this.basetype === 'web') {
      this._injectWebExtension(d);
    } else {
      this._injectServerExtension(d);
    }
  }

  _injectWebExtension(d) {
    const file = this.destinationPath('phovea.js');
    const old = this.fs.read(file);
    const absFile = d.module.startsWith('~') ? d.module.slice(1) : `./src/${d.module.includes('.') ? d.module.slice(0, d.module.lastIndexOf('.')) : d.module}`;
    const text = `\n\n  registry.push('${d.type}', '${d.id}', function() { return import('${absFile}'); }, ${d.stringify(d.extras, ' ')});\n  // generator-phovea:end`;
    const new_ = old.replace('  // generator-phovea:end', text);
    this.fs.write(file, new_);

    const target = this.destinationPath(`src/${d.module}${d.module.includes('.') ? '' : '.ts'}`);
    if (absFile.startsWith('.') && !this.fs.exists(target)) {
      let source = this.templatePath(`${d.type}.tmpl.ts`);
      if (!this.fs.exists(source)) {
        source = this.templatePath('template.tmpl.ts');
      }
      const config = this._getModuleConfig(target);
      this.fs.copyTpl(source, target, Object.assign(config, d));
    }
  }

  _injectServerExtension(d) {
    const name = this.config.get('name');
    const file = this.destinationPath(`${name}/__init__.py`);
    const old = this.fs.read(file);
    const text = `\n\n  registry.append('${d.type}', '${d.id}', '${name}.${d.module}', ${d.stringifyPython(d.extras, '  ')})\n  # generator-phovea:end`;
    const new_ = old.replace('  # generator-phovea:end', text);
    this.fs.write(file, new_);

    const target = this.destinationPath(`${name}/${d.module}.py`);
    if (!this.fs.exists(target)) {
      let source = this.templatePath(`${d.type}.tmpl.py`);
      if (!this.fs.exists(source)) {
        source = this.templatePath('template.tmpl.py');
      }
      const config = this._getModuleConfig(target);
      this.fs.copyTpl(source, target, Object.assign(config, d));
    }
  }

  _getModuleConfig(target) {
    return {
      moduleName: path.basename(target, path.extname(target))
    };
  }
}

module.exports = Generator;
<|endoftext|>