<commit_before>module.exports = [
  {
    name: '首頁',
    link: '/',
    icon: 'fa-home'
  },
  {
    name: '系統設定',
    icon: 'fa-bars',
    child: [
      {
        name: '角色設定',
        link: '/base/role',
        icon: 'fa-circle-o'
      },
      {
        name: '權限設定',
        link: '/base/permission',
        icon: 'fa-circle-o'
      },
      {
        name: '金流設定',
        link: '/base/currency',
        icon: 'fa-circle-o'
      },
      {
        name: '指定收款',
        link: '/base/payment',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '人員管理',
    icon: 'fa-bars',
    child: [
      {
        name: '站長',
        link: '/member/0',
        icon: 'fa-circle-o'
      },
      {
        name: '大總監',
        link: '/member/1',
        icon: 'fa-circle-o'
      },
      {
        name: '總監',
        link: '/member/2',
        icon: 'fa-circle-o'
      },
      {
        name: '大股東',
        link: '/member/3',
        icon: 'fa-circle-o'
      },
      {
        name: '股東',
        link: '/member/4',
        icon: 'fa-circle-o'
      },
      {
        name: '總代理',
        link: '/member/5',
        icon: 'fa-circle-o'
      }
      ,
      {
        name: '代理商',
        link: '/member/6',
        icon: 'fa-circle-o'
      }
      ,
      {
        name: '會員',
        link: '/member/7',
        icon: 'fa-circle-o'
      }
      ,
      {
        name: '客服',
        link: '/member/service',
        icon: 'fa-circle-o'
      }
      ,
      {
        name: '登入歷程',
        link: '/member/login-log',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '遊戲管理',
    icon: 'fa-bars',
    child: [
      {
        name: '平台設定',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '遊戲管理',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '訂單操作',
    icon: 'fa-bars',
    child: [
      {
        name: '儲值',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '轉移',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '提領',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '紅利贈點',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '訊息管理',
    icon: 'fa-bars',
    child: [
      {
        name: '站內訊息',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '簡訊發送',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '黑名單',
    icon: 'fa-bars',
    child: [
      {
        name: '銀行',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '電話',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: 'IP',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '交易查詢',
    icon: 'fa-bars',
    child: [
      {
        name: '營運報表',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '遊戲報表',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '前台設定',
    icon: 'fa-bars',
    child: [
      {
        name: '公用版面',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '代理商版面',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '系統公告',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: 'UI Elements',
    icon: 'fa-bars',
    child: [
      {
        name: 'General',
        link: '/ui/general',
        icon: 'fa-circle-o'
      },
      {
        name: 'Icons',
        link: '/ui/icons',
        icon: 'fa-circle-o'
      },
      {
        name: 'Buttons',
        link: '/ui/buttons',
        icon: 'fa-circle-o'
      }
    ]
  }
]
<commit_msg>menu updata<commit_after>module.exports = [
  {
    name: '首頁',
    link: '/',
    icon: 'fa-home'
  },
  {
    name: '系統設置',
    icon: 'fa-sliders',
    child: [
      {
        name: '角色設定',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '權限設定',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '站別設定',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '黑名單設定',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '帳號管理',
    icon: 'fa-users',
    child: [
      {
        name: '站長',
        link: '/member/0',
        icon: 'fa-circle-o'
      },
      {
        name: '大總監',
        link: '/member/1',
        icon: 'fa-circle-o'
      },
      {
        name: '總監',
        link: '/member/2',
        icon: 'fa-circle-o'
      },
      {
        name: '大股東',
        link: '/member/3',
        icon: 'fa-circle-o'
      },
      {
        name: '股東',
        link: '/member/4',
        icon: 'fa-circle-o'
      },
      {
        name: '總代理',
        link: '/member/5',
        icon: 'fa-circle-o'
      }
      ,
      {
        name: '代理商',
        link: '/member/6',
        icon: 'fa-circle-o'
      }
      ,
      {
        name: '會員',
        link: '/member/7',
        icon: 'fa-circle-o'
      }
      ,
      {
        name: '客服',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '遊戲管理',
    icon: 'fa-gamepad',
    child: [
      {
        name: '平台設定',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '遊戲管理',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '帳務管理',
    icon: 'fa-server',
    child: [
      {
        name: '儲值',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '轉移',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '提領',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '紅利贈點',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '訊息管理',
    icon: 'fa-comments',
    child: [
      {
        name: '站內訊息',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '簡訊發送',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '報表查詢',
    icon: 'fa-bar-chart',
    child: [
      {
        name: '營運報表',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '遊戲報表',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '網站設定',
    icon: 'fa-cogs',
    child: [
      {
        name: '版面設定',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '系統公告',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '活動訊息',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: '歷程紀錄',
    icon: 'fa-history',
    child: [
      {
        name: '登入紀錄',
        link: '/',
        icon: 'fa-circle-o'
      },
      {
        name: '帳務歷程',
        link: '/',
        icon: 'fa-circle-o'
      }
    ]
  },
  {
    name: 'UI Elements',
    icon: 'fa-circle-o',
    child: [
      {
        name: 'General',
        link: '/ui/general',
        icon: 'fa-circle-o'
      },
      {
        name: 'Icons',
        link: '/ui/icons',
        icon: 'fa-circle-o'
      },
      {
        name: 'Buttons',
        link: '/ui/buttons',
        icon: 'fa-circle-o'
      }
    ]
  }
]
<|endoftext|>


<commit_before>var decode = require('punycode').ucs2.decode,
	formats = {
		'date-time': /^\d{4}-(0[0-9]{1}|1[0-2]{1})-[0-9]{2}[t ]\d{2}:\d{2}:\d{2}(\.\d+)?([zZ]|[+-]\d{2}:\d{2})$/i,
		'date': /^\d{4}-(0[0-9]{1}|1[0-2]{1})-[0-9]{2}$/,
		'time': /^\d{2}:\d{2}:\d{2}$/,
		'color': /^(#[0-9a-f]{3}|#[0-9a-f]{6}|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)$/i,
		'style': /^(?:\s*-?[_a-zA-Z]+[_a-zA-Z0-9-]*:[^\n\r\f;]+;)*\s*-?[_a-zA-Z]+[_a-zA-Z0-9-]*:[^\n\r\f;]+;?\s*$/,
		'phone': /^(?:(?:\(?(?:00|\+)(?:[1-4]\d\d|[1-9]\d?)\)?)?[\-\.\ \\\/]?)?(?:(?:\(?\d{1,}\)?[\-\.\ \\\/]?){0,})(?:[\-\.\ \\\/]?(?:#|ext\.?|extension|x)[\-\.\ \\\/]?(?:\d+))?$/i,
		'uri': /^(?:([a-z0-9+.-]+:\/\/)((?:(?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(:(?:\d*))?(\/(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?|([a-z0-9+.-]+:)(\/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?)(\?(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?(#(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?$/i,
		'email': /^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/,
		'ipv4': /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/,
		'ipv6': /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,

		// hostname regex from: http://stackoverflow.com/a/1420225/5628
		'hostname': /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/,

		'utc-millisec': function(subject) {
			var parsed = parseInt(subject, 10);

			return !isNaN(parsed) && parsed.toString() === subject.toString();
		},
		'regex': function (subject) {
			try { new RegExp(subject); }
			catch(e) { return false; }
			return true;
		}
	};

// aliases
formats['host-name'] = formats.hostname;
formats['ip-address'] = formats.ipv4;

function minLength(subject, schema, context) {
	if(decode(subject).length < schema.minLength) {
		context.addError('Failed "minLength" criteria', subject, schema);
		return false;
	}

	return true;
}

function maxLength(subject, schema, context) {
	if(decode(subject).length > schema.maxLength) {
		context.addError('Failed "maxLength" criteria', subject, schema);
		return false;
	}

	return true;
}

function pattern(subject, pattern, context) {
	if(!subject.match(pattern)) {
		context.addError('Failed "pattern" criteria', subject, pattern);
		return false;
	}

	return true;
}

function format(subject, format, context) {
	var validator = formats[format];

	if(!validator)
		throw new Error('Invalid schema: unknown format (' + format + ')');

	var valid = validator.test ? validator.test(subject) : validator(subject);
	if(!valid) {
		context.addError('Failed "format" criteria', subject, pattern);
	}

	return valid;
}

function validateString(subject, schema, context) {
	if(typeof subject !== 'string') {
		context.addError('Failed type:string criteria', schema);
		return false;
	}

	var valid = true;

	if(schema.minLength) valid = minLength(subject, schema, context) && valid;
	if(schema.maxLength) valid = maxLength(subject, schema, context) && valid;
	if(schema.pattern) valid = pattern(subject, schema.pattern, context) && valid;
	if(schema.format) valid = format(subject, schema.format, context) && valid;

	return valid;
}

module.exports = validateString;
<commit_msg>standardize string<commit_after>var decode = require('punycode').ucs2.decode,
	formats = {
		'date-time': /^\d{4}-(0[0-9]{1}|1[0-2]{1})-[0-9]{2}[t ]\d{2}:\d{2}:\d{2}(\.\d+)?([zZ]|[+-]\d{2}:\d{2})$/i,
		'date': /^\d{4}-(0[0-9]{1}|1[0-2]{1})-[0-9]{2}$/,
		'time': /^\d{2}:\d{2}:\d{2}$/,
		'color': /^(#[0-9a-f]{3}|#[0-9a-f]{6}|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)$/i,
		'style': /^(?:\s*-?[_a-zA-Z]+[_a-zA-Z0-9-]*:[^\n\r\f;]+;)*\s*-?[_a-zA-Z]+[_a-zA-Z0-9-]*:[^\n\r\f;]+;?\s*$/,
		'phone': /^(?:(?:\(?(?:00|\+)(?:[1-4]\d\d|[1-9]\d?)\)?)?[\-\.\ \\\/]?)?(?:(?:\(?\d{1,}\)?[\-\.\ \\\/]?){0,})(?:[\-\.\ \\\/]?(?:#|ext\.?|extension|x)[\-\.\ \\\/]?(?:\d+))?$/i,
		'uri': /^(?:([a-z0-9+.-]+:\/\/)((?:(?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(:(?:\d*))?(\/(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?|([a-z0-9+.-]+:)(\/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?)(\?(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?(#(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?$/i,
		'email': /^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/,
		'ipv4': /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/,
		'ipv6': /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,

		// hostname regex from: http://stackoverflow.com/a/1420225/5628
		'hostname': /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/,

		'utc-millisec': function(subject) {
			var parsed = parseInt(subject, 10);

			return !isNaN(parsed) && parsed.toString() === subject.toString();
		},
		'regex': function (subject) {
			try { new RegExp(subject); }
			catch(e) { return false; }
			return true;
		}
	};

// aliases
formats['host-name'] = formats.hostname;
formats['ip-address'] = formats.ipv4;

function minLength(subject, schema, context) {
	if(decode(subject).length < schema.minLength) {
		context.addError('Failed "minLength" criteria', subject, schema);
		return false;
	}

	return true;
}

function maxLength(subject, schema, context) {
	if(decode(subject).length > schema.maxLength) {
		context.addError('Failed "maxLength" criteria', subject, schema);
		return false;
	}

	return true;
}

function pattern(subject, schema, context) {
	var pattern = schema.pattern;

	if(!subject.match(pattern)) {
		context.addError('Failed "pattern" criteria (' + pattern + ')', subject, pattern);
		return false;
	}

	return true;
}

function format(subject, schema, context) {
	var format = schema.format,
		validator = formats[format];

	if(!validator)
		throw new Error('Invalid schema: unknown format (' + format + ')');

	var valid = validator.test ? validator.test(subject) : validator(subject);
	if(!valid) {
		context.addError('Failed "format" criteria (' + format + ')', subject, pattern);
	}

	return valid;
}

function validateString(subject, schema, context) {
	if(typeof subject !== 'string') {
		context.addError('Failed type:string criteria', schema);
		return false;
	}

	var valid = true;

	if('minLength' in schema) valid = minLength(subject, schema, context) && valid;
	if('maxLength' in schema) valid = maxLength(subject, schema, context) && valid;
	if('pattern' in schema) valid = pattern(subject, schema, context) && valid;
	if('format' in schema) valid = format(subject, schema, context) && valid;

	return valid;
}

module.exports = validateString;
<|endoftext|>


<commit_before>"use strict";

class gsuiDAW extends HTMLElement {
	#cmps = new Map()
	#currentActionInd = -1
	#actions = null
	#cookiesText = "..."
	#dispatch = GSUI.dispatchEvent.bind( null, this, "gsuiDAW" )
	#children = GSUI.getTemplate( "gsui-daw" )
	#elements = GSUI.findElements( this.#children, {
		bpm: ".gsuiDAW-tempo-bpm",
		bPM: ".gsuiDAW-tempo-beatsPerMeasure",
		sPB: ".gsuiDAW-tempo-stepsPerBeat",
		cmpName: ".gsuiDAW-currCmp-name",
		cmpSave: ".gsuiDAW-currCmp-saveBtn",
		cmpIcon: ".gsuiDAW-currCmp-localIcon",
		vers: ".gsuiDAW-version-num",
		clock: "gsui-clock",
		spectrum: "gsui-spectrum",
		volume: ".gsuiDAW-volume gsui-slider",
		currentTime: ".gsuiDAW-areaTime gsui-slider",
		userAvatar: ".gsuiDAW-btn[data-action='profile']",
		cmpsLocalList: ".gsuiDAW-dropdown-list[data-list='local']",
		cmpsCloudList: ".gsuiDAW-dropdown-list[data-list='cloud']",
		historyList: ".gsuiDAW-history .gsuiDAW-dropdown-list",
	} )
	#popups = {
		about: GSUI.getTemplate( "gsui-daw-popup-about" ),
		export: GSUI.getTemplate( "gsui-daw-popup-export" ),
		shortcuts: GSUI.getTemplate( "gsui-daw-popup-shortcuts" ),
		cookies: GSUI.getTemplate( "gsui-daw-popup-cookies" ),
		settings: GSUI.findElements( GSUI.getTemplate( "gsui-daw-popup-settings" ), {
			root: ".gsuiDAW-popup-settings",
			sampleRate: "[name='sampleRate']",
			uiRateRadio: {
				auto: "[name='uiRate'][value='auto']",
				manual: "[name='uiRate'][value='manual']",
			},
			uiRateManualFPS: "[name='uiRate'][value='manual'] + .gsuiDAW-uiRateFps",
			uiRateManualRange: "[name='uiRateFPS']",
			windowsLowGraphics: "[name='windowsLowGraphics']",
			timelineNumbering: "[name='timelineNumbering']",
		} ),
	}

	constructor() {
		super();
		Object.seal( this );

		this.#actions = this.#elements.historyList.getElementsByClassName( "gsuiDAW-history-action" );
		this.onclick = this.#onclick.bind( this );
		this.#popups.settings.uiRateManualRange.onmousedown = () => this.#popups.settings.uiRateRadio.manual.checked = true;
		this.#popups.settings.uiRateManualRange.oninput = e => {
			this.#popups.settings.uiRateManualFPS.textContent =
				e.target.value.padStart( 2, "0" );
		};
		this.#elements.spectrum.setResolution( 140 );
		GSUI.listenEvents( this, {
			gsuiSlider: {
				input: ( d, tar ) => this.#dispatch( tar.dataset.action, d.args[ 0 ] ),
				inputStart: GSUI.noop,
				inputEnd: GSUI.noop,
				change: GSUI.noop,
			},
		} );
	}

	// .........................................................................
	connectedCallback() {
		if ( this.#children ) {
			this.append( ...this.#children );
			this.#children = null;
			GSUI.recallAttributes( this, {
				saved: true,
				uirate: "auto",
				windowslowgraphics: false,
				timelinenumbering: 0,
				samplerate: 24000,
				bpm: 60,
				name: "",
				location: "local",
				timedivision: "1/1",
				currenttime: 0,
				maxtime: 1,
				volume: 1,
				version: "0.0.0",
			} );
		}
	}
	disconnectedCallback() {
	}
	static get observedAttributes() {
		return [ "samplerate", "uirate", "windowslowgraphics", "timelinenumbering", "bpm", "timedivision", "name", "volume", "currenttime", "maxtime", "location", "useravatar", "version" ];
	}
	attributeChangedCallback( prop, prev, val ) {
		if ( !this.#children && prev !== val ) {
			switch ( prop ) {
				case "name":
					this.#elements.cmpName.textContent = val;
					break;
				case "samplerate":
					this.#popups.settings.sampleRate.value = val;
					break;
				case "uirate":
					this.#popups.settings.uiRateRadio[ val === "auto" ? val : "manual" ].checked = true;
					if ( val !== "auto" ) {
						this.#popups.settings.uiRateManualFPS.textContent = val;
						this.#popups.settings.uiRateManualRange.value = val;
					}
					break;
				case "windowslowgraphics":
					this.#popups.settings.windowsLowGraphics.checked = val !== null;
					break;
				case "timelinenumbering":
					this.#popups.settings.timelineNumbering.value = val;
					break;
				case "bpm":
					this.#elements.bpm.textContent = val;
					break;
				case "timedivision":
					this.#elements.bPM.textContent = val.split( "/" )[ 0 ];
					this.#elements.sPB.textContent = val.split( "/" )[ 1 ];
					break;
				case "volume":
					this.#elements.volume.setValue( val );
					break;
				case "currenttime":
					this.#elements.clock.setTime( val );
					this.#elements.currentTime.setValue( val );
					break;
				case "maxtime":
					GSUI.setAttribute( this.#elements.currentTime, "max", val );
					break;
				case "location":
					GSUI.setAttribute( this.#elements.cmpIcon, "data-icon", val === "local" ? "local" : "cloud" );
					GSUI.setAttribute( this.#elements.cmpSave, "data-icon", val === "local" ? "save" : "upload" );
					break;
				case "useravatar":
					this.#elements.userAvatar.style.backgroundImage = `url("${ val }")`;
					break;
				case "version":
					this.#elements.vers.textContent = val;
					this.#popups.about.querySelector( ".gsuiDAW-popup-about-versionNum" ).textContent = val;
					break;
			}
		}
	}

	// .........................................................................
	setCookiesText( text ) {
		this.#cookiesText = text;
	}
	updateSpectrum( data ) {
		this.#elements.spectrum.draw( data );
	}

	// .........................................................................
	clearCompositions() {
		this.#cmps.forEach( html => html.root.remove() );
	}
	addComposition( cmp ) {
		if ( this.#cmps.has( cmp.id ) ) {
			this.updateComposition( cmp );
		} else {
			const root = GSUI.getTemplate( "gsui-daw-cmp", { id: cmp.id, saveMode: cmp.options.saveMode } ),
				html = GSUI.findElements( root, {
					// root: ".cmp",
					bpm: ".gsuiDAW-cmp-bpm",
					name: ".gsuiDAW-cmp-name",
					save: ".gsuiDAW-cmp-save",
					duration: ".gsuiDAW-cmp-duration",
				} );

			this.#cmps.set( cmp.id, html );
			this.updateComposition( cmp );
			( cmp.options.saveMode === "local"
				? this.#elements.cmpsLocalList
				: this.#elements.cmpsCloudList ).append( root );
		}
	}
	updateComposition( cmp ) {
		const html = this.#cmps.get( cmp.id ),
			[ min, sec ] = gsuiClock.parseBeatsToSeconds( cmp.duration, cmp.bpm );

		html.bpm.textContent = cmp.bpm;
		html.name.textContent = cmp.name;
		html.duration.textContent = `${ min }:${ sec }`;
	}

	// .........................................................................
	clearHistory() {
		Array.from( this.#actions ).forEach( a => a.remove() );
		this.#currentActionInd = -1;
	}
	stackAction( icon, desc ) {
		Array.from( this.#actions ).forEach( a => "undone" in a.dataset && a.remove() );
		this.#elements.historyList.append( GSUI.getTemplate( "gsui-daw-history-action", { icon, desc } ) );
		this.#elements.historyList.scroll( 0, Number.MAX_SAFE_INTEGER );
		this.#currentActionInd = this.#actions.length - 1;
	}
	undo() {
		if ( this.#currentActionInd >= 0 ) {
			GSUI.setAttribute( this.#actions[ this.#currentActionInd-- ], "data-undone", true );
		}
	}
	redo() {
		if ( this.#currentActionInd < this.#actions.length - 1 ) {
			GSUI.setAttribute( this.#actions[ ++this.#currentActionInd ], "data-undone", false );
		}
	}

	// .........................................................................
	#onclick( e ) {
		const act = e.target.dataset.action;

		switch ( act ) {
			case "saveCurrent":
			case "focusSwitch":
			case "play":
			case "stop":
			case "reset":
			case "undo":
			case "redo":
				this.#dispatch( act );
				break;
			case "rename":
				GSUI.popup.prompt( "Composition's title", "", this.getAttribute( "name" ), "Rename" )
					.then( n => n !== this.getAttribute( "name" ) && this.#dispatch( "rename", n ) );
				break;
			case "cookies":
				GSUI.popup.custom( { title: "Cookies consent", element: this.#popups.cookies } )
					.then( arg => arg !== undefined && this.#dispatch( "oki-cookies" ) );
				break;
			case "about":
				GSUI.popup.custom( { title: "About", element: this.#popups.about } );
				break;
			case "export":
				GSUI.popup.custom( { title: "Export", element: this.#popups.export } );
				break;
			case "shortcuts":
				GSUI.popup.custom( { title: "Keyboard / mouse shortcuts", element: this.#popups.shortcuts } );
				break;
			case "settings":
				GSUI.popup.custom( { title: "Settings", element: this.#popups.settings.root } )
					.then( data => {
						if ( data ) {
							if ( data.uiRate === "manual" ) {
								data.uiRate = data.uiRateFPS;
							}
							delete data.uiRateFPS;
							if (
								data.uiRate !== +this.getAttribute( "uirate" ) ||
								data.sampleRate !== +this.getAttribute( "samplerate" ) ||
								data.timelineNumbering !== +this.getAttribute( "timelinenumbering" ) ||
								data.windowsLowGraphics !== ( this.getAttribute( "windowslowgraphics" ) === "" )
							) {
								lg( data, +this.getAttribute( "samplerate" ) )
							}
						}
					} );
				break;
			case "login":
			case "tempo":
				lg( "popup", act );
				break;
			case "cmps":
			case "help":
			case "undoMore":
			case "changelog":
				break;
			default:
				act && lg( "untracked action:", act );
				break;
		}
	}
}

Object.seal( gsuiDAW );
customElements.define( "gsui-daw", gsuiDAW );
<commit_msg>gsuiDAW: rename cmp popup fix<commit_after>"use strict";

class gsuiDAW extends HTMLElement {
	#cmps = new Map()
	#currentActionInd = -1
	#actions = null
	#cookiesText = "..."
	#dispatch = GSUI.dispatchEvent.bind( null, this, "gsuiDAW" )
	#children = GSUI.getTemplate( "gsui-daw" )
	#elements = GSUI.findElements( this.#children, {
		bpm: ".gsuiDAW-tempo-bpm",
		bPM: ".gsuiDAW-tempo-beatsPerMeasure",
		sPB: ".gsuiDAW-tempo-stepsPerBeat",
		cmpName: ".gsuiDAW-currCmp-name",
		cmpSave: ".gsuiDAW-currCmp-saveBtn",
		cmpIcon: ".gsuiDAW-currCmp-localIcon",
		vers: ".gsuiDAW-version-num",
		clock: "gsui-clock",
		spectrum: "gsui-spectrum",
		volume: ".gsuiDAW-volume gsui-slider",
		currentTime: ".gsuiDAW-areaTime gsui-slider",
		userAvatar: ".gsuiDAW-btn[data-action='profile']",
		cmpsLocalList: ".gsuiDAW-dropdown-list[data-list='local']",
		cmpsCloudList: ".gsuiDAW-dropdown-list[data-list='cloud']",
		historyList: ".gsuiDAW-history .gsuiDAW-dropdown-list",
	} )
	#popups = {
		about: GSUI.getTemplate( "gsui-daw-popup-about" ),
		export: GSUI.getTemplate( "gsui-daw-popup-export" ),
		shortcuts: GSUI.getTemplate( "gsui-daw-popup-shortcuts" ),
		cookies: GSUI.getTemplate( "gsui-daw-popup-cookies" ),
		settings: GSUI.findElements( GSUI.getTemplate( "gsui-daw-popup-settings" ), {
			root: ".gsuiDAW-popup-settings",
			sampleRate: "[name='sampleRate']",
			uiRateRadio: {
				auto: "[name='uiRate'][value='auto']",
				manual: "[name='uiRate'][value='manual']",
			},
			uiRateManualFPS: "[name='uiRate'][value='manual'] + .gsuiDAW-uiRateFps",
			uiRateManualRange: "[name='uiRateFPS']",
			windowsLowGraphics: "[name='windowsLowGraphics']",
			timelineNumbering: "[name='timelineNumbering']",
		} ),
	}

	constructor() {
		super();
		Object.seal( this );

		this.#actions = this.#elements.historyList.getElementsByClassName( "gsuiDAW-history-action" );
		this.onclick = this.#onclick.bind( this );
		this.#popups.settings.uiRateManualRange.onmousedown = () => this.#popups.settings.uiRateRadio.manual.checked = true;
		this.#popups.settings.uiRateManualRange.oninput = e => {
			this.#popups.settings.uiRateManualFPS.textContent =
				e.target.value.padStart( 2, "0" );
		};
		this.#elements.spectrum.setResolution( 140 );
		GSUI.listenEvents( this, {
			gsuiSlider: {
				input: ( d, tar ) => this.#dispatch( tar.dataset.action, d.args[ 0 ] ),
				inputStart: GSUI.noop,
				inputEnd: GSUI.noop,
				change: GSUI.noop,
			},
		} );
	}

	// .........................................................................
	connectedCallback() {
		if ( this.#children ) {
			this.append( ...this.#children );
			this.#children = null;
			GSUI.recallAttributes( this, {
				saved: true,
				uirate: "auto",
				windowslowgraphics: false,
				timelinenumbering: 0,
				samplerate: 24000,
				bpm: 60,
				name: "",
				location: "local",
				timedivision: "1/1",
				currenttime: 0,
				maxtime: 1,
				volume: 1,
				version: "0.0.0",
			} );
		}
	}
	disconnectedCallback() {
	}
	static get observedAttributes() {
		return [ "samplerate", "uirate", "windowslowgraphics", "timelinenumbering", "bpm", "timedivision", "name", "volume", "currenttime", "maxtime", "location", "useravatar", "version" ];
	}
	attributeChangedCallback( prop, prev, val ) {
		if ( !this.#children && prev !== val ) {
			switch ( prop ) {
				case "name":
					this.#elements.cmpName.textContent = val;
					break;
				case "samplerate":
					this.#popups.settings.sampleRate.value = val;
					break;
				case "uirate":
					this.#popups.settings.uiRateRadio[ val === "auto" ? val : "manual" ].checked = true;
					if ( val !== "auto" ) {
						this.#popups.settings.uiRateManualFPS.textContent = val;
						this.#popups.settings.uiRateManualRange.value = val;
					}
					break;
				case "windowslowgraphics":
					this.#popups.settings.windowsLowGraphics.checked = val !== null;
					break;
				case "timelinenumbering":
					this.#popups.settings.timelineNumbering.value = val;
					break;
				case "bpm":
					this.#elements.bpm.textContent = val;
					break;
				case "timedivision":
					this.#elements.bPM.textContent = val.split( "/" )[ 0 ];
					this.#elements.sPB.textContent = val.split( "/" )[ 1 ];
					break;
				case "volume":
					this.#elements.volume.setValue( val );
					break;
				case "currenttime":
					this.#elements.clock.setTime( val );
					this.#elements.currentTime.setValue( val );
					break;
				case "maxtime":
					GSUI.setAttribute( this.#elements.currentTime, "max", val );
					break;
				case "location":
					GSUI.setAttribute( this.#elements.cmpIcon, "data-icon", val === "local" ? "local" : "cloud" );
					GSUI.setAttribute( this.#elements.cmpSave, "data-icon", val === "local" ? "save" : "upload" );
					break;
				case "useravatar":
					this.#elements.userAvatar.style.backgroundImage = `url("${ val }")`;
					break;
				case "version":
					this.#elements.vers.textContent = val;
					this.#popups.about.querySelector( ".gsuiDAW-popup-about-versionNum" ).textContent = val;
					break;
			}
		}
	}

	// .........................................................................
	setCookiesText( text ) {
		this.#cookiesText = text;
	}
	updateSpectrum( data ) {
		this.#elements.spectrum.draw( data );
	}

	// .........................................................................
	clearCompositions() {
		this.#cmps.forEach( html => html.root.remove() );
	}
	addComposition( cmp ) {
		if ( this.#cmps.has( cmp.id ) ) {
			this.updateComposition( cmp );
		} else {
			const root = GSUI.getTemplate( "gsui-daw-cmp", { id: cmp.id, saveMode: cmp.options.saveMode } ),
				html = GSUI.findElements( root, {
					// root: ".cmp",
					bpm: ".gsuiDAW-cmp-bpm",
					name: ".gsuiDAW-cmp-name",
					save: ".gsuiDAW-cmp-save",
					duration: ".gsuiDAW-cmp-duration",
				} );

			this.#cmps.set( cmp.id, html );
			this.updateComposition( cmp );
			( cmp.options.saveMode === "local"
				? this.#elements.cmpsLocalList
				: this.#elements.cmpsCloudList ).append( root );
		}
	}
	updateComposition( cmp ) {
		const html = this.#cmps.get( cmp.id ),
			[ min, sec ] = gsuiClock.parseBeatsToSeconds( cmp.duration, cmp.bpm );

		html.bpm.textContent = cmp.bpm;
		html.name.textContent = cmp.name;
		html.duration.textContent = `${ min }:${ sec }`;
	}

	// .........................................................................
	clearHistory() {
		Array.from( this.#actions ).forEach( a => a.remove() );
		this.#currentActionInd = -1;
	}
	stackAction( icon, desc ) {
		Array.from( this.#actions ).forEach( a => "undone" in a.dataset && a.remove() );
		this.#elements.historyList.append( GSUI.getTemplate( "gsui-daw-history-action", { icon, desc } ) );
		this.#elements.historyList.scroll( 0, Number.MAX_SAFE_INTEGER );
		this.#currentActionInd = this.#actions.length - 1;
	}
	undo() {
		if ( this.#currentActionInd >= 0 ) {
			GSUI.setAttribute( this.#actions[ this.#currentActionInd-- ], "data-undone", true );
		}
	}
	redo() {
		if ( this.#currentActionInd < this.#actions.length - 1 ) {
			GSUI.setAttribute( this.#actions[ ++this.#currentActionInd ], "data-undone", false );
		}
	}

	// .........................................................................
	#onclick( e ) {
		const act = e.target.dataset.action;

		switch ( act ) {
			case "saveCurrent":
			case "focusSwitch":
			case "play":
			case "stop":
			case "reset":
			case "undo":
			case "redo":
				this.#dispatch( act );
				break;
			case "rename":
				GSUI.popup.prompt( "Composition's title", "", this.getAttribute( "name" ), "Rename" )
					.then( n => n && n !== this.getAttribute( "name" ) && this.#dispatch( "rename", n ) );
				break;
			case "cookies":
				GSUI.popup.custom( { title: "Cookies consent", element: this.#popups.cookies } )
					.then( arg => arg !== undefined && this.#dispatch( "oki-cookies" ) );
				break;
			case "about":
				GSUI.popup.custom( { title: "About", element: this.#popups.about } );
				break;
			case "export":
				GSUI.popup.custom( { title: "Export", element: this.#popups.export } );
				break;
			case "shortcuts":
				GSUI.popup.custom( { title: "Keyboard / mouse shortcuts", element: this.#popups.shortcuts } );
				break;
			case "settings":
				GSUI.popup.custom( { title: "Settings", element: this.#popups.settings.root } )
					.then( data => {
						if ( data ) {
							if ( data.uiRate === "manual" ) {
								data.uiRate = data.uiRateFPS;
							}
							delete data.uiRateFPS;
							if (
								data.uiRate !== +this.getAttribute( "uirate" ) ||
								data.sampleRate !== +this.getAttribute( "samplerate" ) ||
								data.timelineNumbering !== +this.getAttribute( "timelinenumbering" ) ||
								data.windowsLowGraphics !== ( this.getAttribute( "windowslowgraphics" ) === "" )
							) {
								lg( data, +this.getAttribute( "samplerate" ) )
							}
						}
					} );
				break;
			case "login":
			case "tempo":
				lg( "popup", act );
				break;
			case "cmps":
			case "help":
			case "undoMore":
			case "changelog":
				break;
			default:
				act && lg( "untracked action:", act );
				break;
		}
	}
}

Object.seal( gsuiDAW );
customElements.define( "gsui-daw", gsuiDAW );
<|endoftext|>


<commit_before>(function(){"use strict";function t(e,t){var n;i(e),i(t);for(n in t)if(t.hasOwnProperty(n)){if(e.hasOwnProperty(n)===!1)return!1;if(typeof e[n]!=typeof t[n])return!1}return!0}function n(e,t,n){if(r(e,t)===!1)throw new Error(n||"Invalid type")}function r(e,t){return typeof e=="undefined"||e===null?!1:f(t)&&e instanceof t?!0:!1}function i(e,t){if(s(e)===!1)throw new Error(t||"Invalid object")}function s(e){return typeof e=="object"&&e!==null&&u(e)===!1?!0:!1}function o(e,t){if(u(e)===!1)throw new Error(t||"Invalid array")}function u(e){return Object.prototype.toString.call(e)==="[object Array]"?!0:!1}function a(e,t){if(f(e)===!1)throw new Error(t||"Invalid function")}function f(e){return typeof e=="function"?!0:!1}function l(e,t){if(c(e)===!1)throw new Error(t||"Invalid string")}function c(e){return p(e)&&e!==""?!0:!1}function h(e,t){if(p(e)===!1)throw new Error(t||"Invalid string")}function p(e){return typeof e=="string"?!0:!1}function d(e,t){if(v(e)===!1)throw new Error(t||"Invalid number")}function v(e){return isNaN(e)===!0?!1:typeof e=="number"?!0:!1}var e={quacksLike:t,verifyInstance:n,isInstance:r,verifyObject:i,isObject:s,verifyArray:o,isArray:u,verifyFunction:a,isFunction:f,verifyUnemptyString:l,isUnemptyString:c,verifyString:h,isString:p,verifyNumber:d,isNumber:v};typeof window=="undefined"?module.exports=e:window.check=e})();<commit_msg>Update minified script.<commit_after>(function(){"use strict";function t(e,t){var n;i(e),i(t);for(n in t)if(t.hasOwnProperty(n)){if(e.hasOwnProperty(n)===!1)return!1;if(typeof e[n]!=typeof t[n])return!1}return!0}function n(e,t,n){if(r(e,t)===!1)throw new Error(n||"Invalid type")}function r(e,t){return typeof e=="undefined"||e===null?!1:f(t)&&e instanceof t?!0:!1}function i(e,t){if(s(e)===!1)throw new Error(t||"Invalid object")}function s(e){return typeof e=="object"&&e!==null&&u(e)===!1?!0:!1}function o(e,t){if(u(e)===!1)throw new Error(t||"Invalid array")}function u(e){return Object.prototype.toString.call(e)==="[object Array]"?!0:!1}function a(e,t){if(f(e)===!1)throw new Error(t||"Invalid function")}function f(e){return typeof e=="function"?!0:!1}function l(e,t){if(c(e)===!1)throw new Error(t||"Invalid string")}function c(e){return p(e)&&e!==""?!0:!1}function h(e,t){if(p(e)===!1)throw new Error(t||"Invalid string")}function p(e){return typeof e=="string"?!0:!1}function d(e,t){if(v(e)===!1)throw new Error(t||"Invalid number")}function v(e){return isNaN(e)===!0?!1:typeof e=="number"?!0:!1}var e={quacksLike:t,verifyInstance:n,isInstance:r,verifyObject:i,isObject:s,verifyArray:o,isArray:u,verifyFunction:a,isFunction:f,verifyUnemptyString:l,isUnemptyString:c,verifyString:h,isString:p,verifyNumber:d,isNumber:v};module&&module.exports?module.exports=e:window.check=e})();<|endoftext|>


<commit_before>// Copyright 2013 Google Inc.
// Copyright 2014 Volker Sorge
//
// 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.

/**
 * @fileoverview A collection of JavaScript utilities used to simplify working
 * with DOM nodes.
 * Currently minimized for the standalone speech rule engine.
 * @author clchen@google.com (Charles L. Chen)
 * @author volker.sorge@gmail.com (Volker Sorge)
 */

goog.provide('sre.DomUtil');

goog.require('sre.Engine');
goog.require('sre.XpathUtil');

/**
 * Converts a NodeList into an array
 * @param {!(NodeList|NamedNodeMap)} nodeList The nodeList.
 * @return {!Array} The array of nodes in the nodeList.
 */
sre.DomUtil.toArray = function(nodeList) {
  var nodeArray = [];
  for (var i = 0; i < nodeList.length; i++) {
    nodeArray.push(nodeList[i]);
  }
  return nodeArray;
};

/**
 * Trims the whitespace in an XML input string.
 * @param {string} input The XML input string.
 * @return {string} The string with whitespace removed between tags.
 * @private
 */
sre.DomUtil.trimInput_ = function(input) {
  input = input.replace(/ /g, ' ');
  return input.replace(/>\s+</g, '><').trim();
};

/**
 * Parses the XML input string into an XML structure.
 * @param {string} input The XML input string.
 * @param {function (new:Error, string)=} opt_error Optional error function.
 * @return {!Element} The XML document structure corresponding to the node.
 */
sre.DomUtil.parseInput = function(input, opt_error) {
  var error = opt_error || Error;
  var dp = new sre.SystemExternal.xmldom.DOMParser();
  var clean_input = sre.DomUtil.trimInput_(input);
  if (!clean_input) {
    var newError = new error('Empty input!');
    throw newError;
  }
  try {
    var doc = dp.parseFromString(clean_input, 'text/xml');
    if (sre.Engine.getInstance().mode === sre.Engine.Mode.HTTP) {
      sre.XpathUtil.currentDocument = doc;
    }
    return doc.documentElement;
  } catch (err) {
    throw new error('Illegal input: ' + err.message);
  }
};

/**
 * Missing Node interface.
 * @enum {number}
 */
sre.DomUtil.NodeType = {
  ELEMENT_NODE: 1,
  ATTRIBUTE_NODE: 2,
  TEXT_NODE: 3,
  CDATA_SECTION_NODE: 4,
  ENTITY_REFERENCE_NODE: 5,
  ENTITY_NODE: 6,
  PROCESSING_INSTRUCTION_NODE: 7,
  COMMENT_NODE: 8,
  DOCUMENT_NODE: 9,
  DOCUMENT_TYPE_NODE: 10,
  DOCUMENT_FRAGMENT_NODE: 11,
  NOTATION_NODE: 12
};

/**
 * Cleanly replaces child nodes in a parent.
 * @param {!Node} oldNode The node to be replaced.
 * @param {!Node} newNode The replacement node.
 */
sre.DomUtil.replaceNode = function(oldNode, newNode) {
  if (!oldNode.parentNode) {
    return;
  }
  oldNode.parentNode.insertBefore(newNode, oldNode);
  oldNode.parentNode.removeChild(oldNode);
};

/**
 * Pretty prints an XML representation.
 * @param {string} xml The serialised XML string.
 * @return {string} The formatted string.
 */
sre.DomUtil.formatXml = function(xml) {
  var reg = /(>)(<)(\/*)/g;
  xml = xml.replace(reg, '$1\r\n$2$3');
  reg = /(>)(.+)(<c)/g;
  xml = xml.replace(reg, '$1\r\n$2\r\n$3');
  var formatted = '';
  var padding = '';
  xml.split('\r\n')
      .forEach(function(node) {
        if (node.match(/.+<\/\w[^>]*>$/)) {
          // Node with content.
          formatted += padding + node + '\r\n';
        } else if (node.match(/^<\/\w/)) {
          if (padding) {
            // Closing tag
            padding = padding.slice(2);
            formatted += padding + node + '\r\n';
          }
        } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
          // Opening tag
          formatted += padding + node + '\r\n';
          padding += '  ';
        } else {
          // Empty tag
          formatted += padding + node + '\r\n';
        }
      });
  return formatted;
};

/**
 * Transforms a data attribute name into its camel cased version.
 * @param {!string} attr Micro data attributes.
 * @return {!string} The camel cased attribute.
 */
sre.DomUtil.dataAttribute = function(attr) {
  if (attr.match(/^data-/)) {
    attr = attr.substr(5);
  }
  return attr.replace(/-([a-z])/g, function(letter, index) {
    return index.toUpperCase();});
};

/**
 * Retrieves a data attribute from a given node. Tries using microdata access if
 * possible.
 * @param {!Node} node A DOM node.
 * @param {!string} attr The data attribute.
 * @return {!string} The value for that attribute.
 */
sre.DomUtil.getDataAttribute = function(node, attr) {
  if (node.dataset) {
    return node.dataset[sre.DomUtil.dataAttribute(attr)];
  }
  return node.getAttribute(attr);
};

/**
 * A wrapper function for query selector on a node wrt. to an attribute. If
 * query selectors are not implemented on that node it reverts to Xpath.
 * @param {!Node} node A DOM node.
 * @param {!string} attr The data attribute.
 * @return {!Array.<Node>} The list of result nodes.
 */
sre.DomUtil.querySelectorAllByAttr = function(node, attr) {
  return (node.querySelectorAll ?
          sre.DomUtil.toArray(node.querySelectorAll('[' + attr + ']')) :
          sre.XpathUtil.evalXPath('.//*[@' + attr + ']', node));
};
<commit_msg>Adds some more wrapper methods for queryselectors vs xpath.<commit_after>// Copyright 2013 Google Inc.
// Copyright 2014 Volker Sorge
//
// 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.

/**
 * @fileoverview A collection of JavaScript utilities used to simplify working
 * with DOM nodes.
 * Currently minimized for the standalone speech rule engine.
 * @author clchen@google.com (Charles L. Chen)
 * @author volker.sorge@gmail.com (Volker Sorge)
 */

goog.provide('sre.DomUtil');

goog.require('sre.Engine');
goog.require('sre.XpathUtil');

/**
 * Converts a NodeList into an array
 * @param {!(NodeList|NamedNodeMap)} nodeList The nodeList.
 * @return {!Array} The array of nodes in the nodeList.
 */
sre.DomUtil.toArray = function(nodeList) {
  var nodeArray = [];
  for (var i = 0; i < nodeList.length; i++) {
    nodeArray.push(nodeList[i]);
  }
  return nodeArray;
};

/**
 * Trims the whitespace in an XML input string.
 * @param {string} input The XML input string.
 * @return {string} The string with whitespace removed between tags.
 * @private
 */
sre.DomUtil.trimInput_ = function(input) {
  input = input.replace(/ /g, ' ');
  return input.replace(/>\s+</g, '><').trim();
};

/**
 * Parses the XML input string into an XML structure.
 * @param {string} input The XML input string.
 * @param {function (new:Error, string)=} opt_error Optional error function.
 * @return {!Element} The XML document structure corresponding to the node.
 */
sre.DomUtil.parseInput = function(input, opt_error) {
  var error = opt_error || Error;
  var dp = new sre.SystemExternal.xmldom.DOMParser();
  var clean_input = sre.DomUtil.trimInput_(input);
  if (!clean_input) {
    var newError = new error('Empty input!');
    throw newError;
  }
  try {
    var doc = dp.parseFromString(clean_input, 'text/xml');
    if (sre.Engine.getInstance().mode === sre.Engine.Mode.HTTP) {
      sre.XpathUtil.currentDocument = doc;
    }
    return doc.documentElement;
  } catch (err) {
    throw new error('Illegal input: ' + err.message);
  }
};

/**
 * Missing Node interface.
 * @enum {number}
 */
sre.DomUtil.NodeType = {
  ELEMENT_NODE: 1,
  ATTRIBUTE_NODE: 2,
  TEXT_NODE: 3,
  CDATA_SECTION_NODE: 4,
  ENTITY_REFERENCE_NODE: 5,
  ENTITY_NODE: 6,
  PROCESSING_INSTRUCTION_NODE: 7,
  COMMENT_NODE: 8,
  DOCUMENT_NODE: 9,
  DOCUMENT_TYPE_NODE: 10,
  DOCUMENT_FRAGMENT_NODE: 11,
  NOTATION_NODE: 12
};

/**
 * Cleanly replaces child nodes in a parent.
 * @param {!Node} oldNode The node to be replaced.
 * @param {!Node} newNode The replacement node.
 */
sre.DomUtil.replaceNode = function(oldNode, newNode) {
  if (!oldNode.parentNode) {
    return;
  }
  oldNode.parentNode.insertBefore(newNode, oldNode);
  oldNode.parentNode.removeChild(oldNode);
};

/**
 * Pretty prints an XML representation.
 * @param {string} xml The serialised XML string.
 * @return {string} The formatted string.
 */
sre.DomUtil.formatXml = function(xml) {
  var reg = /(>)(<)(\/*)/g;
  xml = xml.replace(reg, '$1\r\n$2$3');
  reg = /(>)(.+)(<c)/g;
  xml = xml.replace(reg, '$1\r\n$2\r\n$3');
  var formatted = '';
  var padding = '';
  xml.split('\r\n')
      .forEach(function(node) {
        if (node.match(/.+<\/\w[^>]*>$/)) {
          // Node with content.
          formatted += padding + node + '\r\n';
        } else if (node.match(/^<\/\w/)) {
          if (padding) {
            // Closing tag
            padding = padding.slice(2);
            formatted += padding + node + '\r\n';
          }
        } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
          // Opening tag
          formatted += padding + node + '\r\n';
          padding += '  ';
        } else {
          // Empty tag
          formatted += padding + node + '\r\n';
        }
      });
  return formatted;
};

/**
 * Transforms a data attribute name into its camel cased version.
 * @param {!string} attr Micro data attributes.
 * @return {!string} The camel cased attribute.
 */
sre.DomUtil.dataAttribute = function(attr) {
  if (attr.match(/^data-/)) {
    attr = attr.substr(5);
  }
  return attr.replace(/-([a-z])/g, function(letter, index) {
    return index.toUpperCase();});
};

/**
 * Retrieves a data attribute from a given node. Tries using microdata access if
 * possible.
 * @param {!Node} node A DOM node.
 * @param {!string} attr The data attribute.
 * @return {!string} The value for that attribute.
 */
sre.DomUtil.getDataAttribute = function(node, attr) {
  if (node.dataset) {
    return node.dataset[sre.DomUtil.dataAttribute(attr)];
  }
  return node.getAttribute(attr);
};

/**
 * A wrapper function for query selector on a node wrt. to an attribute. If
 * query selectors are not implemented on that node it reverts to Xpath.
 * @param {!Node} node A DOM node.
 * @param {!string} attr The data attribute.
 * @return {!Array.<Node>} The list of result nodes.
 */
sre.DomUtil.querySelectorAllByAttr = function(node, attr) {
  return (node.querySelectorAll ?
          sre.DomUtil.toArray(node.querySelectorAll('[' + attr + ']')) :
          sre.XpathUtil.evalXPath('.//*[@' + attr + ']', node));
};

/**
 * A wrapper function for query selector on a node wrt. to an attribute. If
 * query selectors are not implemented on that node it reverts to Xpath.
 * @param {!Node} node A DOM node.
 * @param {!string} attr The data attribute.
 * @param {!string} value The value of the data attribute.
 * @return {!Array.<Node>} The list of result nodes.
 */
sre.DomUtil.querySelectorAllByAttrValue = function(node, attr, value) {
  return (node.querySelectorAll ?
          sre.DomUtil.toArray(node.querySelectorAll('[' + attr + '="'+ value +'"]')) :
          sre.XpathUtil.evalXPath('.//*[@' + attr + '="'+ value +'"]', node));
};

/**
 * A wrapper function for query selector on a node wrt. to a tag name. If
 * query selectors are not implemented on that node it reverts to Xpath.
 * @param {!Node} node A DOM node.
 * @param {!string} tag The tag name.
 * @return {!Array.<Node>} The list of result nodes.
 */
sre.DomUtil.querySelectorAll = function(node, tag) {
  return (node.querySelectorAll ?
          sre.DomUtil.toArray(node.querySelectorAll(tag)) :
          sre.XpathUtil.evalXPath('.//' + tag, node));
};
<|endoftext|>


<commit_before>var idToken = "";
var email = "";
var prefUserName = "";
var identityID = "";
var sub = "";

// Load the SDK for JavaScript

$(function() {
   checkSession();
});

function checkSession(){
  var poolData = {
      UserPoolId : 'us-west-2_dEcrjTcVl',
      ClientId : '2kkhe3k563aocuioe4sklhokg4'
  };
  var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
  var cognitoUser = userPool.getCurrentUser();
  if (cognitoUser != null) {
    console.log("got here");
    cognitoUser.getSession(function(err, session) {
      if (err) {
          alert(err);
          return;
      }
      console.log(cognitoUser);
      idToken = session.idToken.jwtToken;
      email = cognitoUser.usernmae;
      cognitoUser.getUserAttributes(function(err, result) {
        if (err) {
            alert(err);
            return;
        }
        console.log(result);
        prefUserName = result[4].getValue();
        sub = result[0].getValue();
        console.log(sub);
        $("#userProfileLink").html(prefUserName + "'s profile");
AWS.config.update({
  credentials: new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'us-west-2:88b13c2b-9ce8-4370-8071-13f8cd379e01'
  }),
  region: 'us-west-2'
});
			AWS.config.credentials = new AWS.CognitoIdentityCredentials({
				IdentityPoolId: 'us-west-2:88b13c2b-9ce8-4370-8071-13f8cd379e01',
				Logins: {
					'cognito-idp.us-west-2.amazonaws.com/us-west-2_dEcrjTcVl': session.getIdToken().getJwtToken()
				}
			});
    AWS.config.credentials.refresh((error) => {
        if (error) {
            console.error(error);
        } else {
            console.log('Successfully logged!');
            idenittyID = AWS.config.credentials.identityId;
        }
        });
      });
      $('#signout').click(function(){
        cognitoUser.signOut();
        window.location.replace("login.html");
      });
      $.ajax({
         url: "https://sejeqwt9og.execute-api.us-west-2.amazonaws.com/Dev/driver-payments?type=total",
         type: "GET",
         headers: {"Authorization": idToken, "Content-Type": "application/json"},
         success: function(result) { $('#totalAmount').html("$" + result.totalAmount); }
      });

      $.ajax({
         url: "https://sejeqwt9og.execute-api.us-west-2.amazonaws.com/Dev/videos?request=totalHours",
         type: "GET",
         headers: {"Authorization": idToken, "Content-Type": "application/json"},
         success: function(result) { $('#totalHours').html(result.totalHours + " hours"); }
      });

      $.ajax({
         url: "https://sejeqwt9og.execute-api.us-west-2.amazonaws.com/Dev/videos?request=streak",
         type: "GET",
         headers: {"Authorization": idToken, "Content-Type": "application/json"},
         success: function(result) { $('#longestStreak').html(result.longestStreak + " days"); }
      });

      $.ajax({
         url: "https://sejeqwt9og.execute-api.us-west-2.amazonaws.com/Dev/driver-payments?year=2017",
         type: "GET",
         headers: {"Authorization": idToken, "Content-Type": "application/json"},
         success: function(result) { populateChart(result.payments); }
      });

 //   cognitoUser.signOut();

    });
  }
  else {
    window.location.replace("login.html");

  }
}

function populateChart(payments) {

var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

Morris.Line({
  element: 'morris-area-chart',
  data: [{
    m: '2017-01', // <-- valid timestamp strings
    a: payments["1"],
  }, {
    m: '2017-02',
    a: payments["2"],
  }, {
    m: '2017-03',
    a: payments["3"],
  }, {
    m: '2017-04',
    a: payments["4"],
  }, {
    m: '2017-05',
    a: payments["5"],
  }, {
    m: '2017-06',
    a: payments["6"],
  }, {
    m: '2017-07',
    a: payments["7"],
  }, {
    m: '2017-08',
    a: payments["8"],
  }, {
    m: '2017-09',
    a: payments["9"],
  }, {
    m: '2017-10',
    a: payments["10"],
  }, {
    m: '2017-11',
    a: payments["11"],
  }, {
    m: '2017-12',
    a: payments["12"],
  }, ],
  xkey: 'm',
  ykeys: ['a'],
  labels: ['2017'],
  xLabelFormat: function(x) {
    var month = months[x.getMonth()];
    return month;
  },
  yLabelFormat: function(y) {
    return "$" + y;
  },
  dateFormat: function(x) {
    var month = months[new Date(x).getMonth()];
    return month;
  },
});
    Morris.Donut({
        element: 'morris-donut-chart',
        data: [{
            label: "Download Sales",
            value: 12
        }, {
            label: "In-Store Sales",
            value: 30
        }, {
            label: "Mail-Order Sales",
            value: 20
        }],
        resize: true
    });

    Morris.Bar({
        element: 'morris-bar-chart',
        data: [{
            y: '2006',
            a: 100,
            b: 90
        }, {
            y: '2007',
            a: 75,
            b: 65
        }, {
            y: '2008',
            a: 50,
            b: 40
        }, {
            y: '2009',
            a: 75,
            b: 65
        }, {
            y: '2010',
            a: 50,
            b: 40
        }, {
            y: '2011',
            a: 75,
            b: 65
        }, {
            y: '2012',
            a: 100,
            b: 90
        }],
        xkey: 'y',
        ykeys: ['a', 'b'],
        labels: ['Series A', 'Series B'],
        hideHover: 'auto',
        resize: true
    });

}

<commit_msg>fix decimal issue<commit_after>var idToken = "";
var email = "";
var prefUserName = "";
var identityID = "";
var sub = "";

// Load the SDK for JavaScript

$(function() {
   checkSession();
});

function checkSession(){
  var poolData = {
      UserPoolId : 'us-west-2_dEcrjTcVl',
      ClientId : '2kkhe3k563aocuioe4sklhokg4'
  };
  var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
  var cognitoUser = userPool.getCurrentUser();
  if (cognitoUser != null) {
    console.log("got here");
    cognitoUser.getSession(function(err, session) {
      if (err) {
          alert(err);
          return;
      }
      console.log(cognitoUser);
      idToken = session.idToken.jwtToken;
      email = cognitoUser.usernmae;
      cognitoUser.getUserAttributes(function(err, result) {
        if (err) {
            alert(err);
            return;
        }
        console.log(result);
        prefUserName = result[4].getValue();
        sub = result[0].getValue();
        console.log(sub);
        $("#userProfileLink").html(prefUserName + "'s profile");
AWS.config.update({
  credentials: new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'us-west-2:88b13c2b-9ce8-4370-8071-13f8cd379e01'
  }),
  region: 'us-west-2'
});
			AWS.config.credentials = new AWS.CognitoIdentityCredentials({
				IdentityPoolId: 'us-west-2:88b13c2b-9ce8-4370-8071-13f8cd379e01',
				Logins: {
					'cognito-idp.us-west-2.amazonaws.com/us-west-2_dEcrjTcVl': session.getIdToken().getJwtToken()
				}
			});
    AWS.config.credentials.refresh((error) => {
        if (error) {
            console.error(error);
        } else {
            console.log('Successfully logged!');
            idenittyID = AWS.config.credentials.identityId;
        }
        });
      });
      $('#signout').click(function(){
        cognitoUser.signOut();
        window.location.replace("login.html");
      });
      $.ajax({
         url: "https://sejeqwt9og.execute-api.us-west-2.amazonaws.com/Dev/driver-payments?type=total",
         type: "GET",
         headers: {"Authorization": idToken, "Content-Type": "application/json"},
         success: function(result) { $('#totalAmount').html("$" + result.totalAmount.toFixed(2)); }
      });

      $.ajax({
         url: "https://sejeqwt9og.execute-api.us-west-2.amazonaws.com/Dev/videos?request=totalHours",
         type: "GET",
         headers: {"Authorization": idToken, "Content-Type": "application/json"},
         success: function(result) { $('#totalHours').html(result.totalHours + " hours"); }
      });

      $.ajax({
         url: "https://sejeqwt9og.execute-api.us-west-2.amazonaws.com/Dev/videos?request=streak",
         type: "GET",
         headers: {"Authorization": idToken, "Content-Type": "application/json"},
         success: function(result) { $('#longestStreak').html(result.longestStreak + " days"); }
      });

      $.ajax({
         url: "https://sejeqwt9og.execute-api.us-west-2.amazonaws.com/Dev/driver-payments?year=2017",
         type: "GET",
         headers: {"Authorization": idToken, "Content-Type": "application/json"},
         success: function(result) { populateChart(result.payments); }
      });

 //   cognitoUser.signOut();

    });
  }
  else {
    window.location.replace("login.html");

  }
}

function populateChart(payments) {

var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

Morris.Line({
  element: 'morris-area-chart',
  data: [{
    m: '2017-01', // <-- valid timestamp strings
    a: payments["1"],
  }, {
    m: '2017-02',
    a: payments["2"],
  }, {
    m: '2017-03',
    a: payments["3"],
  }, {
    m: '2017-04',
    a: payments["4"],
  }, {
    m: '2017-05',
    a: payments["5"],
  }, {
    m: '2017-06',
    a: payments["6"],
  }, {
    m: '2017-07',
    a: payments["7"],
  }, {
    m: '2017-08',
    a: payments["8"],
  }, {
    m: '2017-09',
    a: payments["9"],
  }, {
    m: '2017-10',
    a: payments["10"],
  }, {
    m: '2017-11',
    a: payments["11"],
  }, {
    m: '2017-12',
    a: payments["12"],
  }, ],
  xkey: 'm',
  ykeys: ['a'],
  labels: ['2017'],
  xLabelFormat: function(x) {
    var month = months[x.getMonth()];
    return month;
  },
  yLabelFormat: function(y) {
    return "$" + y;
  },
  dateFormat: function(x) {
    var month = months[new Date(x).getMonth()];
    return month;
  },
});
    Morris.Donut({
        element: 'morris-donut-chart',
        data: [{
            label: "Download Sales",
            value: 12
        }, {
            label: "In-Store Sales",
            value: 30
        }, {
            label: "Mail-Order Sales",
            value: 20
        }],
        resize: true
    });

    Morris.Bar({
        element: 'morris-bar-chart',
        data: [{
            y: '2006',
            a: 100,
            b: 90
        }, {
            y: '2007',
            a: 75,
            b: 65
        }, {
            y: '2008',
            a: 50,
            b: 40
        }, {
            y: '2009',
            a: 75,
            b: 65
        }, {
            y: '2010',
            a: 50,
            b: 40
        }, {
            y: '2011',
            a: 75,
            b: 65
        }, {
            y: '2012',
            a: 100,
            b: 90
        }],
        xkey: 'y',
        ykeys: ['a', 'b'],
        labels: ['Series A', 'Series B'],
        hideHover: 'auto',
        resize: true
    });

}

<|endoftext|>


<commit_before><commit_msg>fix bug: overlay url detection fails on param-less url<commit_after><|endoftext|>


<commit_before>import React from 'react'
import { difference, first, includes, map, some, sortBy } from 'lodash'
import { find, get } from 'lodash/fp'
const { array, bool, func, object, string } = React.PropTypes
import cx from 'classnames'
import cheerio from 'cheerio'
import {
  humanDate, nonbreaking, present, textLength, truncate, appendInP
} from '../util/text'
import { sanitize } from 'hylo-utils/text'
import { linkifyHashtags } from '../util/linkify'
import { tagUrl } from '../routes'
import A from './A'
import Avatar from './Avatar'
import Dropdown from './Dropdown'
import Attachments from './Attachments'
import { ClickCatchingSpan } from './ClickCatcher'
import { handleMouseOver } from './TagPopover'
import LazyLoader from './LazyLoader'
import Icon from './Icon'
import LinkedPersonSentence from './LinkedPersonSentence'
import LinkPreview from './LinkPreview'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { navigate } from '../actions'
import {
  completePost, followPost, removePost, startPostEdit, voteOnPost, pinPost
} from '../actions/posts'
import { same } from '../models'
import { denormalizedPost, getComments, isPinned } from '../models/post'
import { getCurrentCommunity } from '../models/community'
import { canEditPost, canModerate } from '../models/currentUser'
import { isMobile } from '../client/util'
import decode from 'ent/decode'
import CommentSection from './CommentSection'

const spacer =   •

export const presentDescription = (post, community, opts = {}) =>
  present(sanitize(post.description), {slug: get('slug', community), ...opts})

class Post extends React.Component {
  static propTypes = {
    post: object,
    community: object,
    comments: array,
    dispatch: func,
    expanded: bool,
    onExpand: func,
    commentId: string
  }

  static childContextTypes = {post: object}

  getChildContext () {
    return {post: this.props.post}
  }

  render () {
    const { post, comments, expanded, onExpand, community } = this.props
    const { communities, tag, media, linkPreview } = post
    const image = find(m => m.type === 'image', media)
    const classes = cx('post', tag, {image, expanded})
    const title = linkifyHashtags(decode(sanitize(post.name || '')), get('slug', community))

    return

      <a name={`post-${post.id}`}>

      {image && <LazyLoader>

      </LazyLoader>}

      {linkPreview && <LinkPreview {...{linkPreview}}/>}

<VoteButton/><Voters/>

      <Attachments/>
      <CommentSection {...{post, expanded, onExpand, comments}}/>

  }
}

export default compose(
  connect((state, { post }) => {
    return {
      comments: getComments(post, state),
      community: getCurrentCommunity(state),
      post: denormalizedPost(post, state)
    }
  })
)(Post)

export const UndecoratedPost = Post // for testing

export const Header = ({ communities }, { post, currentUser, dispatch }) => {
  const { tag, fulfilled_at } = post
  const person = tag === 'welcome' ? post.relatedUsers[0] : post.user
  const createdAt = new Date(post.created_at)
  const canEdit = canEditPost(currentUser, post)
  const showCheckbox = post.tag === 'request' && (canEdit || fulfilled_at)

  return

    <Menu/>
    <Avatar person={person}/>
    {showCheckbox && <input type='checkbox'
      className='completion-toggle'
      checked={!!post.fulfilled_at}
      onChange={() => canEdit && dispatch(completePost(post.id))}
      readOnly={!canEdit}/>}
    {tag === 'welcome'
      ? <WelcomePostHeader communities={communities}/>
      :

          <A className='name' to={`/u/${person.id}`}>{person.name}

            <A to={`/p/${post.id}`} title={createdAt}>
              {nonbreaking(humanDate(createdAt))}

            {communities && <Communities communities={communities}/>}
            {post.public && {spacer}Public}

}

}
Header.contextTypes = {post: object, currentUser: object, dispatch: func}

const Communities = ({ communities }, { community }) => {
  if (community) communities = sortBy(communities, c => c.id !== community.id)
  const { length } = communities
  if (communities.length === 0) return null

  const communityLink = community => <A to={`/c/${community.slug}`}>{community.name}
  return
     in {communityLink(communities[0])}
    {length > 1 &&  + }
    {length > 1 && <Dropdown className='post-communities-dropdown'
      toggleChildren={{length - 1} other{length > 2 ? 's' : ''}}>
      {communities.slice(1).map(c =>
{communityLink(c)}
)}
    </Dropdown>}

}
Communities.contextTypes = {community: object}

const getTags = text =>
  cheerio.load(text)('.hashtag').toArray().map(tag =>
    tag.children[0].data.replace(/^#/, ''))

const extractTags = (shortDesc, fullDesc) => {
  const tags = getTags(fullDesc)
  return tags.length === 0 ? [] : difference(tags, getTags(shortDesc))
}

const HashtagLink = ({ tag, slug }, { dispatch }) => {
  const onMouseOver = handleMouseOver(dispatch)
  return
    {`#${tag}`}

}
HashtagLink.contextTypes = {dispatch: func}

export const Details = ({ expanded, onExpand }, { post, community, dispatch }) => {
  const truncatedSize = 300
  const { tag } = post
  if (!community) community = post.communities[0]
  const slug = get('slug', community)
  let description = presentDescription(post, community)
  let extractedTags = []
  const truncated = !expanded && textLength(description) > truncatedSize
  if (truncated) {
    const orig = description
    description = truncate(description, truncatedSize)
    extractedTags = extractTags(description, orig)
  }
  if (description) description = appendInP(description, ' ')

  return

    <ClickCatchingSpan dangerouslySetInnerHTML={{__html: description}}/>
    {truncated &&
      <wbr/>
       onExpand(null)} className='show-more'>Show more

    }
    {extractedTags.map(tag =>
      <wbr/>
      <HashtagLink tag={tag} slug={slug}/>

    )}
    {tag && <HashtagLink tag={tag} slug={slug}/>}

}
Details.contextTypes = {post: object, community: object, dispatch: func}

const WelcomePostHeader = ({ communities }, { post }) => {
  let person = post.relatedUsers[0]
  let community = communities[0]
  return

    <A to={`/u/${person.id}`}>{person.name} joined

    {community
      ?
          <A to={`/c/${community.slug}`}>{community.name}.

            Welcome them!

      :
          a community that is no longer active.
        }

}
WelcomePostHeader.contextTypes = {post: object}

export const Menu = (props, { dispatch, post, currentUser, community }) => {
  const canEdit = canEditPost(currentUser, post)
  const following = some(post.follower_ids, id => id === get('id', currentUser))
  const pinned = isPinned(post, community)
  const edit = () => isMobile()
    ? dispatch(navigate(`/p/${post.id}/edit`))
    : dispatch(startPostEdit(post))
  const remove = () => window.confirm('Are you sure? This cannot be undone.') &&
    dispatch(removePost(post.id))
  const pin = () => dispatch(pinPost(get('slug', community), post.id))

  const toggleChildren = pinned
    ? Pinned
    :

  return <Dropdown className='post-menu' alignRight {...{toggleChildren}}>
    {canModerate(currentUser, community) &&

      {pinned ? 'Unpin post' : 'Pin post'}

}
    {canEdit &&
Edit
}
    {canEdit &&
Remove
}

       dispatch(followPost(post.id, currentUser))}>
        Turn {following ? 'off' : 'on'} notifications for this post

       window.alert('TODO')}>Report objectionable content

  </Dropdown>
}
Menu.contextTypes = {post: object, currentUser: object, dispatch: func, community: object}

export const VoteButton = (props, { post, currentUser, dispatch }) => {
  let vote = () => dispatch(voteOnPost(post, currentUser))
  let myVote = includes(map(post.voters, 'id'), (currentUser || {}).id)
  return
    {myVote ? <Icon name='Heart2' /> : <Icon name='Heart' />}
    {myVote ? 'Liked' : 'Like'}

}
VoteButton.contextTypes = {post: object, currentUser: object, dispatch: func}

export const Voters = (props, { post, currentUser }) => {
  const voters = post.voters || []

  let onlyAuthorIsVoting = voters.length === 1 && same('id', first(voters), post.user)
  return voters.length > 0 && !onlyAuthorIsVoting
    ? <LinkedPersonSentence people={voters} className='voters meta'>
        liked this.
      </LinkedPersonSentence>
    :
}
Voters.contextTypes = {post: object, currentUser: object}
<commit_msg>when showing tags extracted from description, don't duplicate selected tag<commit_after>import React from 'react'
import { difference, first, includes, map, some, sortBy } from 'lodash'
import { find, filter, get } from 'lodash/fp'
const { array, bool, func, object, string } = React.PropTypes
import cx from 'classnames'
import cheerio from 'cheerio'
import {
  humanDate, nonbreaking, present, textLength, truncate, appendInP
} from '../util/text'
import { sanitize } from 'hylo-utils/text'
import { linkifyHashtags } from '../util/linkify'
import { tagUrl } from '../routes'
import A from './A'
import Avatar from './Avatar'
import Dropdown from './Dropdown'
import Attachments from './Attachments'
import { ClickCatchingSpan } from './ClickCatcher'
import { handleMouseOver } from './TagPopover'
import LazyLoader from './LazyLoader'
import Icon from './Icon'
import LinkedPersonSentence from './LinkedPersonSentence'
import LinkPreview from './LinkPreview'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { navigate } from '../actions'
import {
  completePost, followPost, removePost, startPostEdit, voteOnPost, pinPost
} from '../actions/posts'
import { same } from '../models'
import { denormalizedPost, getComments, isPinned } from '../models/post'
import { getCurrentCommunity } from '../models/community'
import { canEditPost, canModerate } from '../models/currentUser'
import { isMobile } from '../client/util'
import decode from 'ent/decode'
import CommentSection from './CommentSection'

const spacer =   •

export const presentDescription = (post, community, opts = {}) =>
  present(sanitize(post.description), {slug: get('slug', community), ...opts})

class Post extends React.Component {
  static propTypes = {
    post: object,
    community: object,
    comments: array,
    dispatch: func,
    expanded: bool,
    onExpand: func,
    commentId: string
  }

  static childContextTypes = {post: object}

  getChildContext () {
    return {post: this.props.post}
  }

  render () {
    const { post, comments, expanded, onExpand, community } = this.props
    const { communities, tag, media, linkPreview } = post
    const image = find(m => m.type === 'image', media)
    const classes = cx('post', tag, {image, expanded})
    const title = linkifyHashtags(decode(sanitize(post.name || '')), get('slug', community))

    return

      <a name={`post-${post.id}`}>

      {image && <LazyLoader>

      </LazyLoader>}

      {linkPreview && <LinkPreview {...{linkPreview}}/>}

<VoteButton/><Voters/>

      <Attachments/>
      <CommentSection {...{post, expanded, onExpand, comments}}/>

  }
}

export default compose(
  connect((state, { post }) => {
    return {
      comments: getComments(post, state),
      community: getCurrentCommunity(state),
      post: denormalizedPost(post, state)
    }
  })
)(Post)

export const UndecoratedPost = Post // for testing

export const Header = ({ communities }, { post, currentUser, dispatch }) => {
  const { tag, fulfilled_at } = post
  const person = tag === 'welcome' ? post.relatedUsers[0] : post.user
  const createdAt = new Date(post.created_at)
  const canEdit = canEditPost(currentUser, post)
  const showCheckbox = post.tag === 'request' && (canEdit || fulfilled_at)

  return

    <Menu/>
    <Avatar person={person}/>
    {showCheckbox && <input type='checkbox'
      className='completion-toggle'
      checked={!!post.fulfilled_at}
      onChange={() => canEdit && dispatch(completePost(post.id))}
      readOnly={!canEdit}/>}
    {tag === 'welcome'
      ? <WelcomePostHeader communities={communities}/>
      :

          <A className='name' to={`/u/${person.id}`}>{person.name}

            <A to={`/p/${post.id}`} title={createdAt}>
              {nonbreaking(humanDate(createdAt))}

            {communities && <Communities communities={communities}/>}
            {post.public && {spacer}Public}

}

}
Header.contextTypes = {post: object, currentUser: object, dispatch: func}

const Communities = ({ communities }, { community }) => {
  if (community) communities = sortBy(communities, c => c.id !== community.id)
  const { length } = communities
  if (communities.length === 0) return null

  const communityLink = community => <A to={`/c/${community.slug}`}>{community.name}
  return
     in {communityLink(communities[0])}
    {length > 1 &&  + }
    {length > 1 && <Dropdown className='post-communities-dropdown'
      toggleChildren={{length - 1} other{length > 2 ? 's' : ''}}>
      {communities.slice(1).map(c =>
{communityLink(c)}
)}
    </Dropdown>}

}
Communities.contextTypes = {community: object}

const getTags = text =>
  cheerio.load(text)('.hashtag').toArray().map(tag =>
    tag.children[0].data.replace(/^#/, ''))

const extractTags = (shortDesc, fullDesc, omitTag) => {
  const tags = filter(t => t !== omitTag, getTags(fullDesc))
  return tags.length === 0 ? [] : difference(tags, getTags(shortDesc))
}

const HashtagLink = ({ tag, slug }, { dispatch }) => {
  const onMouseOver = handleMouseOver(dispatch)
  return
    {`#${tag}`}

}
HashtagLink.contextTypes = {dispatch: func}

export const Details = ({ expanded, onExpand }, { post, community, dispatch }) => {
  const truncatedSize = 300
  const { tag } = post
  if (!community) community = post.communities[0]
  const slug = get('slug', community)
  let description = presentDescription(post, community)
  let extractedTags = []
  const truncated = !expanded && textLength(description) > truncatedSize
  if (truncated) {
    const orig = description
    description = truncate(description, truncatedSize)
    extractedTags = extractTags(description, orig, tag)
  }
  if (description) description = appendInP(description, ' ')

  return

    <ClickCatchingSpan dangerouslySetInnerHTML={{__html: description}}/>
    {truncated &&
      <wbr/>
       onExpand(null)} className='show-more'>Show more

    }
    {extractedTags.map(tag =>
      <wbr/>
      <HashtagLink tag={tag} slug={slug}/>

    )}
    {tag && <HashtagLink tag={tag} slug={slug}/>}

}
Details.contextTypes = {post: object, community: object, dispatch: func}

const WelcomePostHeader = ({ communities }, { post }) => {
  let person = post.relatedUsers[0]
  let community = communities[0]
  return

    <A to={`/u/${person.id}`}>{person.name} joined

    {community
      ?
          <A to={`/c/${community.slug}`}>{community.name}.

            Welcome them!

      :
          a community that is no longer active.
        }

}
WelcomePostHeader.contextTypes = {post: object}

export const Menu = (props, { dispatch, post, currentUser, community }) => {
  const canEdit = canEditPost(currentUser, post)
  const following = some(post.follower_ids, id => id === get('id', currentUser))
  const pinned = isPinned(post, community)
  const edit = () => isMobile()
    ? dispatch(navigate(`/p/${post.id}/edit`))
    : dispatch(startPostEdit(post))
  const remove = () => window.confirm('Are you sure? This cannot be undone.') &&
    dispatch(removePost(post.id))
  const pin = () => dispatch(pinPost(get('slug', community), post.id))

  const toggleChildren = pinned
    ? Pinned
    :

  return <Dropdown className='post-menu' alignRight {...{toggleChildren}}>
    {canModerate(currentUser, community) &&

      {pinned ? 'Unpin post' : 'Pin post'}

}
    {canEdit &&
Edit
}
    {canEdit &&
Remove
}

       dispatch(followPost(post.id, currentUser))}>
        Turn {following ? 'off' : 'on'} notifications for this post

       window.alert('TODO')}>Report objectionable content

  </Dropdown>
}
Menu.contextTypes = {post: object, currentUser: object, dispatch: func, community: object}

export const VoteButton = (props, { post, currentUser, dispatch }) => {
  let vote = () => dispatch(voteOnPost(post, currentUser))
  let myVote = includes(map(post.voters, 'id'), (currentUser || {}).id)
  return
    {myVote ? <Icon name='Heart2' /> : <Icon name='Heart' />}
    {myVote ? 'Liked' : 'Like'}

}
VoteButton.contextTypes = {post: object, currentUser: object, dispatch: func}

export const Voters = (props, { post, currentUser }) => {
  const voters = post.voters || []

  let onlyAuthorIsVoting = voters.length === 1 && same('id', first(voters), post.user)
  return voters.length > 0 && !onlyAuthorIsVoting
    ? <LinkedPersonSentence people={voters} className='voters meta'>
        liked this.
      </LinkedPersonSentence>
    :
}
Voters.contextTypes = {post: object, currentUser: object}
<|endoftext|>