wip
This commit is contained in:
		
							parent
							
								
									dd60907abe
								
							
						
					
					
						commit
						4f1795b97b
					
				
					 16 changed files with 576 additions and 551 deletions
				
			
		| 
						 | 
					@ -173,6 +173,7 @@
 | 
				
			||||||
		"uuid": "3.2.1",
 | 
							"uuid": "3.2.1",
 | 
				
			||||||
		"vhost": "3.0.2",
 | 
							"vhost": "3.0.2",
 | 
				
			||||||
		"vue": "^2.5.13",
 | 
							"vue": "^2.5.13",
 | 
				
			||||||
 | 
							"vue-js-modal": "^1.3.9",
 | 
				
			||||||
		"vue-loader": "^14.1.1",
 | 
							"vue-loader": "^14.1.1",
 | 
				
			||||||
		"vue-router": "^3.0.1",
 | 
							"vue-router": "^3.0.1",
 | 
				
			||||||
		"vue-template-compiler": "^2.5.13",
 | 
							"vue-template-compiler": "^2.5.13",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,155 +0,0 @@
 | 
				
			||||||
<mk-signin>
 | 
					 | 
				
			||||||
	<form :class="{ signing: signing }" onsubmit={ onsubmit }>
 | 
					 | 
				
			||||||
		<label class="user-name">
 | 
					 | 
				
			||||||
			<input ref="username" type="text" pattern="^[a-zA-Z0-9-]+$" placeholder="%i18n:common.tags.mk-signin.username%" autofocus="autofocus" required="required" oninput={ oninput }/>%fa:at%
 | 
					 | 
				
			||||||
		</label>
 | 
					 | 
				
			||||||
		<label class="password">
 | 
					 | 
				
			||||||
			<input ref="password" type="password" placeholder="%i18n:common.tags.mk-signin.password%" required="required"/>%fa:lock%
 | 
					 | 
				
			||||||
		</label>
 | 
					 | 
				
			||||||
		<label class="token" v-if="user && user.two_factor_enabled">
 | 
					 | 
				
			||||||
			<input ref="token" type="number" placeholder="%i18n:common.tags.mk-signin.token%" required="required"/>%fa:lock%
 | 
					 | 
				
			||||||
		</label>
 | 
					 | 
				
			||||||
		<button type="submit" disabled={ signing }>{ signing ? '%i18n:common.tags.mk-signin.signing-in%' : '%i18n:common.tags.mk-signin.signin%' }</button>
 | 
					 | 
				
			||||||
	</form>
 | 
					 | 
				
			||||||
	<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
		:scope
 | 
					 | 
				
			||||||
			display block
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> form
 | 
					 | 
				
			||||||
				display block
 | 
					 | 
				
			||||||
				z-index 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				&.signing
 | 
					 | 
				
			||||||
					&, *
 | 
					 | 
				
			||||||
						cursor wait !important
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				label
 | 
					 | 
				
			||||||
					display block
 | 
					 | 
				
			||||||
					margin 12px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					[data-fa]
 | 
					 | 
				
			||||||
						display block
 | 
					 | 
				
			||||||
						pointer-events none
 | 
					 | 
				
			||||||
						position absolute
 | 
					 | 
				
			||||||
						bottom 0
 | 
					 | 
				
			||||||
						top 0
 | 
					 | 
				
			||||||
						left 0
 | 
					 | 
				
			||||||
						z-index 1
 | 
					 | 
				
			||||||
						margin auto
 | 
					 | 
				
			||||||
						padding 0 16px
 | 
					 | 
				
			||||||
						height 1em
 | 
					 | 
				
			||||||
						color #898786
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					input[type=text]
 | 
					 | 
				
			||||||
					input[type=password]
 | 
					 | 
				
			||||||
					input[type=number]
 | 
					 | 
				
			||||||
						user-select text
 | 
					 | 
				
			||||||
						display inline-block
 | 
					 | 
				
			||||||
						cursor auto
 | 
					 | 
				
			||||||
						padding 0 0 0 38px
 | 
					 | 
				
			||||||
						margin 0
 | 
					 | 
				
			||||||
						width 100%
 | 
					 | 
				
			||||||
						line-height 44px
 | 
					 | 
				
			||||||
						font-size 1em
 | 
					 | 
				
			||||||
						color rgba(0, 0, 0, 0.7)
 | 
					 | 
				
			||||||
						background #fff
 | 
					 | 
				
			||||||
						outline none
 | 
					 | 
				
			||||||
						border solid 1px #eee
 | 
					 | 
				
			||||||
						border-radius 4px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						&:hover
 | 
					 | 
				
			||||||
							background rgba(255, 255, 255, 0.7)
 | 
					 | 
				
			||||||
							border-color #ddd
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							& + i
 | 
					 | 
				
			||||||
								color #797776
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						&:focus
 | 
					 | 
				
			||||||
							background #fff
 | 
					 | 
				
			||||||
							border-color #ccc
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							& + i
 | 
					 | 
				
			||||||
								color #797776
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				[type=submit]
 | 
					 | 
				
			||||||
					cursor pointer
 | 
					 | 
				
			||||||
					padding 16px
 | 
					 | 
				
			||||||
					margin -6px 0 0 0
 | 
					 | 
				
			||||||
					width 100%
 | 
					 | 
				
			||||||
					font-size 1.2em
 | 
					 | 
				
			||||||
					color rgba(0, 0, 0, 0.5)
 | 
					 | 
				
			||||||
					outline none
 | 
					 | 
				
			||||||
					border none
 | 
					 | 
				
			||||||
					border-radius 0
 | 
					 | 
				
			||||||
					background transparent
 | 
					 | 
				
			||||||
					transition all .5s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:hover
 | 
					 | 
				
			||||||
						color $theme-color
 | 
					 | 
				
			||||||
						transition all .2s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:focus
 | 
					 | 
				
			||||||
						color $theme-color
 | 
					 | 
				
			||||||
						transition all .2s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:active
 | 
					 | 
				
			||||||
						color darken($theme-color, 30%)
 | 
					 | 
				
			||||||
						transition all .2s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:disabled
 | 
					 | 
				
			||||||
						opacity 0.7
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	</style>
 | 
					 | 
				
			||||||
	<script lang="typescript">
 | 
					 | 
				
			||||||
		this.mixin('api');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.user = null;
 | 
					 | 
				
			||||||
		this.signing = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.oninput = () => {
 | 
					 | 
				
			||||||
			this.api('users/show', {
 | 
					 | 
				
			||||||
				username: this.$refs.username.value
 | 
					 | 
				
			||||||
			}).then(user => {
 | 
					 | 
				
			||||||
				this.user = user;
 | 
					 | 
				
			||||||
				this.$emit('user', user);
 | 
					 | 
				
			||||||
				this.update();
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.onsubmit = e => {
 | 
					 | 
				
			||||||
			e.preventDefault();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (this.$refs.username.value == '') {
 | 
					 | 
				
			||||||
				this.$refs.username.focus();
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (this.$refs.password.value == '') {
 | 
					 | 
				
			||||||
				this.$refs.password.focus();
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (this.user && this.user.two_factor_enabled && this.$refs.token.value == '') {
 | 
					 | 
				
			||||||
				this.$refs.token.focus();
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.update({
 | 
					 | 
				
			||||||
				signing: true
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.api('signin', {
 | 
					 | 
				
			||||||
				username: this.$refs.username.value,
 | 
					 | 
				
			||||||
				password: this.$refs.password.value,
 | 
					 | 
				
			||||||
				token: this.user && this.user.two_factor_enabled ? this.$refs.token.value : undefined
 | 
					 | 
				
			||||||
			}).then(() => {
 | 
					 | 
				
			||||||
				location.reload();
 | 
					 | 
				
			||||||
			}).catch(() => {
 | 
					 | 
				
			||||||
				alert('something happened');
 | 
					 | 
				
			||||||
				this.update({
 | 
					 | 
				
			||||||
					signing: false
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	</script>
 | 
					 | 
				
			||||||
</mk-signin>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,307 +0,0 @@
 | 
				
			||||||
<mk-signup>
 | 
					 | 
				
			||||||
	<form onsubmit={ onsubmit } autocomplete="off">
 | 
					 | 
				
			||||||
		<label class="username">
 | 
					 | 
				
			||||||
			<p class="caption">%fa:at%%i18n:common.tags.mk-signup.username%</p>
 | 
					 | 
				
			||||||
			<input ref="username" type="text" pattern="^[a-zA-Z0-9-]{3,20}$" placeholder="a~z、A~Z、0~9、-" autocomplete="off" required="required" onkeyup={ onChangeUsername }/>
 | 
					 | 
				
			||||||
			<p class="profile-page-url-preview" v-if="refs.username.value != '' && username-state != 'invalidFormat' && username-state != 'minRange' && username-state != 'maxRange'">{ _URL_ + '/' + refs.username.value }</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%%i18n:common.tags.mk-signup.checking%</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.available%</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.unavailable%</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.error%</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.invalid-format%</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.too-short%</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.too-long%</p>
 | 
					 | 
				
			||||||
		</label>
 | 
					 | 
				
			||||||
		<label class="password">
 | 
					 | 
				
			||||||
			<p class="caption">%fa:lock%%i18n:common.tags.mk-signup.password%</p>
 | 
					 | 
				
			||||||
			<input ref="password" type="password" placeholder="%i18n:common.tags.mk-signup.password-placeholder%" autocomplete="off" required="required" onkeyup={ onChangePassword }/>
 | 
					 | 
				
			||||||
			<div class="meter" v-if="passwordStrength != ''" data-strength={ passwordStrength }>
 | 
					 | 
				
			||||||
				<div class="value" ref="passwordMetar"></div>
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
			<p class="info" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.weak-password%</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.normal-password%</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.strong-password%</p>
 | 
					 | 
				
			||||||
		</label>
 | 
					 | 
				
			||||||
		<label class="retype-password">
 | 
					 | 
				
			||||||
			<p class="caption">%fa:lock%%i18n:common.tags.mk-signup.password%(%i18n:common.tags.mk-signup.retype%)</p>
 | 
					 | 
				
			||||||
			<input ref="passwordRetype" type="password" placeholder="%i18n:common.tags.mk-signup.retype-placeholder%" autocomplete="off" required="required" onkeyup={ onChangePasswordRetype }/>
 | 
					 | 
				
			||||||
			<p class="info" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.password-matched%</p>
 | 
					 | 
				
			||||||
			<p class="info" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.password-not-matched%</p>
 | 
					 | 
				
			||||||
		</label>
 | 
					 | 
				
			||||||
		<label class="recaptcha">
 | 
					 | 
				
			||||||
			<p class="caption"><template v-if="recaptchaed">%fa:toggle-on%</template><template v-if="!recaptchaed">%fa:toggle-off%</template>%i18n:common.tags.mk-signup.recaptcha%</p>
 | 
					 | 
				
			||||||
			<div v-if="recaptcha" class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" data-sitekey={ recaptcha.site_key }></div>
 | 
					 | 
				
			||||||
		</label>
 | 
					 | 
				
			||||||
		<label class="agree-tou">
 | 
					 | 
				
			||||||
			<input name="agree-tou" type="checkbox" autocomplete="off" required="required"/>
 | 
					 | 
				
			||||||
			<p><a href={ touUrl } target="_blank">利用規約</a>に同意する</p>
 | 
					 | 
				
			||||||
		</label>
 | 
					 | 
				
			||||||
		<button @click="onsubmit">%i18n:common.tags.mk-signup.create%</button>
 | 
					 | 
				
			||||||
	</form>
 | 
					 | 
				
			||||||
	<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
		:scope
 | 
					 | 
				
			||||||
			display block
 | 
					 | 
				
			||||||
			min-width 302px
 | 
					 | 
				
			||||||
			overflow hidden
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> form
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				label
 | 
					 | 
				
			||||||
					display block
 | 
					 | 
				
			||||||
					margin 16px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					> .caption
 | 
					 | 
				
			||||||
						margin 0 0 4px 0
 | 
					 | 
				
			||||||
						color #828888
 | 
					 | 
				
			||||||
						font-size 0.95em
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						> [data-fa]
 | 
					 | 
				
			||||||
							margin-right 0.25em
 | 
					 | 
				
			||||||
							color #96adac
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					> .info
 | 
					 | 
				
			||||||
						display block
 | 
					 | 
				
			||||||
						margin 4px 0
 | 
					 | 
				
			||||||
						font-size 0.8em
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						> [data-fa]
 | 
					 | 
				
			||||||
							margin-right 0.3em
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&.username
 | 
					 | 
				
			||||||
						.profile-page-url-preview
 | 
					 | 
				
			||||||
							display block
 | 
					 | 
				
			||||||
							margin 4px 8px 0 4px
 | 
					 | 
				
			||||||
							font-size 0.8em
 | 
					 | 
				
			||||||
							color #888
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							&:empty
 | 
					 | 
				
			||||||
								display none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							&:not(:empty) + .info
 | 
					 | 
				
			||||||
								margin-top 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&.password
 | 
					 | 
				
			||||||
						.meter
 | 
					 | 
				
			||||||
							display block
 | 
					 | 
				
			||||||
							margin-top 8px
 | 
					 | 
				
			||||||
							width 100%
 | 
					 | 
				
			||||||
							height 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							&[data-strength='']
 | 
					 | 
				
			||||||
								display none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							&[data-strength='low']
 | 
					 | 
				
			||||||
								> .value
 | 
					 | 
				
			||||||
									background #d73612
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							&[data-strength='medium']
 | 
					 | 
				
			||||||
								> .value
 | 
					 | 
				
			||||||
									background #d7ca12
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							&[data-strength='high']
 | 
					 | 
				
			||||||
								> .value
 | 
					 | 
				
			||||||
									background #61bb22
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							> .value
 | 
					 | 
				
			||||||
								display block
 | 
					 | 
				
			||||||
								width 0%
 | 
					 | 
				
			||||||
								height 100%
 | 
					 | 
				
			||||||
								background transparent
 | 
					 | 
				
			||||||
								border-radius 4px
 | 
					 | 
				
			||||||
								transition all 0.1s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				[type=text], [type=password]
 | 
					 | 
				
			||||||
					user-select text
 | 
					 | 
				
			||||||
					display inline-block
 | 
					 | 
				
			||||||
					cursor auto
 | 
					 | 
				
			||||||
					padding 0 12px
 | 
					 | 
				
			||||||
					margin 0
 | 
					 | 
				
			||||||
					width 100%
 | 
					 | 
				
			||||||
					line-height 44px
 | 
					 | 
				
			||||||
					font-size 1em
 | 
					 | 
				
			||||||
					color #333 !important
 | 
					 | 
				
			||||||
					background #fff !important
 | 
					 | 
				
			||||||
					outline none
 | 
					 | 
				
			||||||
					border solid 1px rgba(0, 0, 0, 0.1)
 | 
					 | 
				
			||||||
					border-radius 4px
 | 
					 | 
				
			||||||
					box-shadow 0 0 0 114514px #fff inset
 | 
					 | 
				
			||||||
					transition all .3s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:hover
 | 
					 | 
				
			||||||
						border-color rgba(0, 0, 0, 0.2)
 | 
					 | 
				
			||||||
						transition all .1s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:focus
 | 
					 | 
				
			||||||
						color $theme-color !important
 | 
					 | 
				
			||||||
						border-color $theme-color
 | 
					 | 
				
			||||||
						box-shadow 0 0 0 1024px #fff inset, 0 0 0 4px rgba($theme-color, 10%)
 | 
					 | 
				
			||||||
						transition all 0s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:disabled
 | 
					 | 
				
			||||||
						opacity 0.5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				.agree-tou
 | 
					 | 
				
			||||||
					padding 4px
 | 
					 | 
				
			||||||
					border-radius 4px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:hover
 | 
					 | 
				
			||||||
						background #f4f4f4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:active
 | 
					 | 
				
			||||||
						background #eee
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&, *
 | 
					 | 
				
			||||||
						cursor pointer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					p
 | 
					 | 
				
			||||||
						display inline
 | 
					 | 
				
			||||||
						color #555
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				button
 | 
					 | 
				
			||||||
					margin 0 0 32px 0
 | 
					 | 
				
			||||||
					padding 16px
 | 
					 | 
				
			||||||
					width 100%
 | 
					 | 
				
			||||||
					font-size 1em
 | 
					 | 
				
			||||||
					color #fff
 | 
					 | 
				
			||||||
					background $theme-color
 | 
					 | 
				
			||||||
					border-radius 3px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:hover
 | 
					 | 
				
			||||||
						background lighten($theme-color, 5%)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:active
 | 
					 | 
				
			||||||
						background darken($theme-color, 5%)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	</style>
 | 
					 | 
				
			||||||
	<script lang="typescript">
 | 
					 | 
				
			||||||
		this.mixin('api');
 | 
					 | 
				
			||||||
		const getPasswordStrength = require('syuilo-password-strength');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.usernameState = null;
 | 
					 | 
				
			||||||
		this.passwordStrength = '';
 | 
					 | 
				
			||||||
		this.passwordRetypeState = null;
 | 
					 | 
				
			||||||
		this.recaptchaed = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.aboutUrl = `${_DOCS_URL_}/${_LANG_}/tou`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		window.onRecaptchaed = () => {
 | 
					 | 
				
			||||||
			this.recaptchaed = true;
 | 
					 | 
				
			||||||
			this.update();
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		window.onRecaptchaExpired = () => {
 | 
					 | 
				
			||||||
			this.recaptchaed = false;
 | 
					 | 
				
			||||||
			this.update();
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.on('mount', () => {
 | 
					 | 
				
			||||||
			this.update({
 | 
					 | 
				
			||||||
				recaptcha: {
 | 
					 | 
				
			||||||
					site_key: _RECAPTCHA_SITEKEY_
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const head = document.getElementsByTagName('head')[0];
 | 
					 | 
				
			||||||
			const script = document.createElement('script');
 | 
					 | 
				
			||||||
			script.setAttribute('src', 'https://www.google.com/recaptcha/api.js');
 | 
					 | 
				
			||||||
			head.appendChild(script);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.onChangeUsername = () => {
 | 
					 | 
				
			||||||
			const username = this.$refs.username.value;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (username == '') {
 | 
					 | 
				
			||||||
				this.update({
 | 
					 | 
				
			||||||
					usernameState: null
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const err =
 | 
					 | 
				
			||||||
				!username.match(/^[a-zA-Z0-9\-]+$/) ? 'invalid-format' :
 | 
					 | 
				
			||||||
				username.length < 3 ? 'min-range' :
 | 
					 | 
				
			||||||
				username.length > 20 ? 'max-range' :
 | 
					 | 
				
			||||||
				null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (err) {
 | 
					 | 
				
			||||||
				this.update({
 | 
					 | 
				
			||||||
					usernameState: err
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.update({
 | 
					 | 
				
			||||||
				usernameState: 'wait'
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.api('username/available', {
 | 
					 | 
				
			||||||
				username: username
 | 
					 | 
				
			||||||
			}).then(result => {
 | 
					 | 
				
			||||||
				this.update({
 | 
					 | 
				
			||||||
					usernameState: result.available ? 'ok' : 'unavailable'
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			}).catch(err => {
 | 
					 | 
				
			||||||
				this.update({
 | 
					 | 
				
			||||||
					usernameState: 'error'
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.onChangePassword = () => {
 | 
					 | 
				
			||||||
			const password = this.$refs.password.value;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (password == '') {
 | 
					 | 
				
			||||||
				this.passwordStrength = '';
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const strength = getPasswordStrength(password);
 | 
					 | 
				
			||||||
			this.passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
 | 
					 | 
				
			||||||
			this.update();
 | 
					 | 
				
			||||||
			this.$refs.passwordMetar.style.width = `${strength * 100}%`;
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.onChangePasswordRetype = () => {
 | 
					 | 
				
			||||||
			const password = this.$refs.password.value;
 | 
					 | 
				
			||||||
			const retypedPassword = this.$refs.passwordRetype.value;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (retypedPassword == '') {
 | 
					 | 
				
			||||||
				this.passwordRetypeState = null;
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.passwordRetypeState = password == retypedPassword ? 'match' : 'not-match';
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.onsubmit = e => {
 | 
					 | 
				
			||||||
			e.preventDefault();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const username = this.$refs.username.value;
 | 
					 | 
				
			||||||
			const password = this.$refs.password.value;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const locker = document.body.appendChild(document.createElement('mk-locker'));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.api('signup', {
 | 
					 | 
				
			||||||
				username: username,
 | 
					 | 
				
			||||||
				password: password,
 | 
					 | 
				
			||||||
				'g-recaptcha-response': grecaptcha.getResponse()
 | 
					 | 
				
			||||||
			}).then(() => {
 | 
					 | 
				
			||||||
				this.api('signin', {
 | 
					 | 
				
			||||||
					username: username,
 | 
					 | 
				
			||||||
					password: password
 | 
					 | 
				
			||||||
				}).then(() => {
 | 
					 | 
				
			||||||
					location.href = '/';
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			}).catch(() => {
 | 
					 | 
				
			||||||
				alert('%i18n:common.tags.mk-signup.some-error%');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				grecaptcha.reset();
 | 
					 | 
				
			||||||
				this.recaptchaed = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				locker.parentNode.removeChild(locker);
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	</script>
 | 
					 | 
				
			||||||
</mk-signup>
 | 
					 | 
				
			||||||
							
								
								
									
										7
									
								
								src/web/app/common/views/components/index.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/web/app/common/views/components/index.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import signin from './signin.vue';
 | 
				
			||||||
 | 
					import signup from './signup.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Vue.component('mk-signin', signin);
 | 
				
			||||||
 | 
					Vue.component('mk-signup', signup);
 | 
				
			||||||
							
								
								
									
										138
									
								
								src/web/app/common/views/components/signin.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/web/app/common/views/components/signin.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,138 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<form class="form" :class="{ signing: signing }" @submit.prevent="onSubmit">
 | 
				
			||||||
 | 
						<label class="user-name">
 | 
				
			||||||
 | 
							<input v-model="username" type="text" pattern="^[a-zA-Z0-9-]+$" placeholder="%i18n:common.tags.mk-signin.username%" autofocus required @change="onUsernameChange"/>%fa:at%
 | 
				
			||||||
 | 
						</label>
 | 
				
			||||||
 | 
						<label class="password">
 | 
				
			||||||
 | 
							<input v-model="password" type="password" placeholder="%i18n:common.tags.mk-signin.password%" required/>%fa:lock%
 | 
				
			||||||
 | 
						</label>
 | 
				
			||||||
 | 
						<label class="token" v-if="user && user.two_factor_enabled">
 | 
				
			||||||
 | 
							<input v-model="token" type="number" placeholder="%i18n:common.tags.mk-signin.token%" required/>%fa:lock%
 | 
				
			||||||
 | 
						</label>
 | 
				
			||||||
 | 
						<button type="submit" disabled={ signing }>{ signing ? '%i18n:common.tags.mk-signin.signing-in%' : '%i18n:common.tags.mk-signin.signin%' }</button>
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						props: ['os'],
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								signing: false,
 | 
				
			||||||
 | 
								user: null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							onUsernameChange() {
 | 
				
			||||||
 | 
								this.os.api('users/show', {
 | 
				
			||||||
 | 
									username: this.username
 | 
				
			||||||
 | 
								}).then(user => {
 | 
				
			||||||
 | 
									this.user = user;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							onSubmit() {
 | 
				
			||||||
 | 
								this.signing = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.os.api('signin', {
 | 
				
			||||||
 | 
									username: this.username,
 | 
				
			||||||
 | 
									password: this.password,
 | 
				
			||||||
 | 
									token: this.user && this.user.two_factor_enabled ? this.token : undefined
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									location.reload();
 | 
				
			||||||
 | 
								}).catch(() => {
 | 
				
			||||||
 | 
									alert('something happened');
 | 
				
			||||||
 | 
									this.signing = false;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.form
 | 
				
			||||||
 | 
						display block
 | 
				
			||||||
 | 
						z-index 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&.signing
 | 
				
			||||||
 | 
							&, *
 | 
				
			||||||
 | 
								cursor wait !important
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						label
 | 
				
			||||||
 | 
							display block
 | 
				
			||||||
 | 
							margin 12px 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[data-fa]
 | 
				
			||||||
 | 
								display block
 | 
				
			||||||
 | 
								pointer-events none
 | 
				
			||||||
 | 
								position absolute
 | 
				
			||||||
 | 
								bottom 0
 | 
				
			||||||
 | 
								top 0
 | 
				
			||||||
 | 
								left 0
 | 
				
			||||||
 | 
								z-index 1
 | 
				
			||||||
 | 
								margin auto
 | 
				
			||||||
 | 
								padding 0 16px
 | 
				
			||||||
 | 
								height 1em
 | 
				
			||||||
 | 
								color #898786
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							input[type=text]
 | 
				
			||||||
 | 
							input[type=password]
 | 
				
			||||||
 | 
							input[type=number]
 | 
				
			||||||
 | 
								user-select text
 | 
				
			||||||
 | 
								display inline-block
 | 
				
			||||||
 | 
								cursor auto
 | 
				
			||||||
 | 
								padding 0 0 0 38px
 | 
				
			||||||
 | 
								margin 0
 | 
				
			||||||
 | 
								width 100%
 | 
				
			||||||
 | 
								line-height 44px
 | 
				
			||||||
 | 
								font-size 1em
 | 
				
			||||||
 | 
								color rgba(0, 0, 0, 0.7)
 | 
				
			||||||
 | 
								background #fff
 | 
				
			||||||
 | 
								outline none
 | 
				
			||||||
 | 
								border solid 1px #eee
 | 
				
			||||||
 | 
								border-radius 4px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								&:hover
 | 
				
			||||||
 | 
									background rgba(255, 255, 255, 0.7)
 | 
				
			||||||
 | 
									border-color #ddd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									& + i
 | 
				
			||||||
 | 
										color #797776
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								&:focus
 | 
				
			||||||
 | 
									background #fff
 | 
				
			||||||
 | 
									border-color #ccc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									& + i
 | 
				
			||||||
 | 
										color #797776
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[type=submit]
 | 
				
			||||||
 | 
							cursor pointer
 | 
				
			||||||
 | 
							padding 16px
 | 
				
			||||||
 | 
							margin -6px 0 0 0
 | 
				
			||||||
 | 
							width 100%
 | 
				
			||||||
 | 
							font-size 1.2em
 | 
				
			||||||
 | 
							color rgba(0, 0, 0, 0.5)
 | 
				
			||||||
 | 
							outline none
 | 
				
			||||||
 | 
							border none
 | 
				
			||||||
 | 
							border-radius 0
 | 
				
			||||||
 | 
							background transparent
 | 
				
			||||||
 | 
							transition all .5s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&:hover
 | 
				
			||||||
 | 
								color $theme-color
 | 
				
			||||||
 | 
								transition all .2s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&:focus
 | 
				
			||||||
 | 
								color $theme-color
 | 
				
			||||||
 | 
								transition all .2s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&:active
 | 
				
			||||||
 | 
								color darken($theme-color, 30%)
 | 
				
			||||||
 | 
								transition all .2s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&:disabled
 | 
				
			||||||
 | 
								opacity 0.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										331
									
								
								src/web/app/common/views/components/signup.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								src/web/app/common/views/components/signup.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,331 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<form @submit.prevent="onSubmit" autocomplete="off">
 | 
				
			||||||
 | 
						<label class="username">
 | 
				
			||||||
 | 
							<p class="caption">%fa:at%%i18n:common.tags.mk-signup.username%</p>
 | 
				
			||||||
 | 
							<input v-model="username" type="text" pattern="^[a-zA-Z0-9-]{3,20}$" placeholder="a~z、A~Z、0~9、-" autocomplete="off" required @keyup="onChangeUsername"/>
 | 
				
			||||||
 | 
							<p class="profile-page-url-preview" v-if="refs.username.value != '' && username-state != 'invalidFormat' && username-state != 'minRange' && username-state != 'maxRange'">{ _URL_ + '/' + refs.username.value }</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%%i18n:common.tags.mk-signup.checking%</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.available%</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.unavailable%</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.error%</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.invalid-format%</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.too-short%</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.too-long%</p>
 | 
				
			||||||
 | 
						</label>
 | 
				
			||||||
 | 
						<label class="password">
 | 
				
			||||||
 | 
							<p class="caption">%fa:lock%%i18n:common.tags.mk-signup.password%</p>
 | 
				
			||||||
 | 
							<input v-model="password" type="password" placeholder="%i18n:common.tags.mk-signup.password-placeholder%" autocomplete="off" required @keyup="onChangePassword"/>
 | 
				
			||||||
 | 
							<div class="meter" v-if="passwordStrength != ''" :data-strength="passwordStrength">
 | 
				
			||||||
 | 
								<div class="value" ref="passwordMetar"></div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<p class="info" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.weak-password%</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.normal-password%</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.strong-password%</p>
 | 
				
			||||||
 | 
						</label>
 | 
				
			||||||
 | 
						<label class="retype-password">
 | 
				
			||||||
 | 
							<p class="caption">%fa:lock%%i18n:common.tags.mk-signup.password%(%i18n:common.tags.mk-signup.retype%)</p>
 | 
				
			||||||
 | 
							<input v-model="passwordRetype" type="password" placeholder="%i18n:common.tags.mk-signup.retype-placeholder%" autocomplete="off" required @keyup="onChangePasswordRetype"/>
 | 
				
			||||||
 | 
							<p class="info" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw%%i18n:common.tags.mk-signup.password-matched%</p>
 | 
				
			||||||
 | 
							<p class="info" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw%%i18n:common.tags.mk-signup.password-not-matched%</p>
 | 
				
			||||||
 | 
						</label>
 | 
				
			||||||
 | 
						<label class="recaptcha">
 | 
				
			||||||
 | 
							<p class="caption"><template v-if="recaptchaed">%fa:toggle-on%</template><template v-if="!recaptchaed">%fa:toggle-off%</template>%i18n:common.tags.mk-signup.recaptcha%</p>
 | 
				
			||||||
 | 
							<div v-if="recaptcha" class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" data-sitekey="recaptcha.site_key"></div>
 | 
				
			||||||
 | 
						</label>
 | 
				
			||||||
 | 
						<label class="agree-tou">
 | 
				
			||||||
 | 
							<input name="agree-tou" type="checkbox" autocomplete="off" required/>
 | 
				
			||||||
 | 
							<p><a :href="touUrl" target="_blank">利用規約</a>に同意する</p>
 | 
				
			||||||
 | 
						</label>
 | 
				
			||||||
 | 
						<button type="submit">%i18n:common.tags.mk-signup.create%</button>
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					const getPasswordStrength = require('syuilo-password-strength');
 | 
				
			||||||
 | 
					import
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const aboutUrl = `${_DOCS_URL_}/${_LANG_}/tou`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							onSubmit() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						mounted() {
 | 
				
			||||||
 | 
							const head = document.getElementsByTagName('head')[0];
 | 
				
			||||||
 | 
							const script = document.createElement('script');
 | 
				
			||||||
 | 
							script.setAttribute('src', 'https://www.google.com/recaptcha/api.js');
 | 
				
			||||||
 | 
							head.appendChild(script);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
						:scope
 | 
				
			||||||
 | 
							display block
 | 
				
			||||||
 | 
							min-width 302px
 | 
				
			||||||
 | 
							overflow hidden
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								label
 | 
				
			||||||
 | 
									display block
 | 
				
			||||||
 | 
									margin 16px 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> .caption
 | 
				
			||||||
 | 
										margin 0 0 4px 0
 | 
				
			||||||
 | 
										color #828888
 | 
				
			||||||
 | 
										font-size 0.95em
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										> [data-fa]
 | 
				
			||||||
 | 
											margin-right 0.25em
 | 
				
			||||||
 | 
											color #96adac
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> .info
 | 
				
			||||||
 | 
										display block
 | 
				
			||||||
 | 
										margin 4px 0
 | 
				
			||||||
 | 
										font-size 0.8em
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										> [data-fa]
 | 
				
			||||||
 | 
											margin-right 0.3em
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&.username
 | 
				
			||||||
 | 
										.profile-page-url-preview
 | 
				
			||||||
 | 
											display block
 | 
				
			||||||
 | 
											margin 4px 8px 0 4px
 | 
				
			||||||
 | 
											font-size 0.8em
 | 
				
			||||||
 | 
											color #888
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											&:empty
 | 
				
			||||||
 | 
												display none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											&:not(:empty) + .info
 | 
				
			||||||
 | 
												margin-top 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&.password
 | 
				
			||||||
 | 
										.meter
 | 
				
			||||||
 | 
											display block
 | 
				
			||||||
 | 
											margin-top 8px
 | 
				
			||||||
 | 
											width 100%
 | 
				
			||||||
 | 
											height 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											&[data-strength='']
 | 
				
			||||||
 | 
												display none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											&[data-strength='low']
 | 
				
			||||||
 | 
												> .value
 | 
				
			||||||
 | 
													background #d73612
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											&[data-strength='medium']
 | 
				
			||||||
 | 
												> .value
 | 
				
			||||||
 | 
													background #d7ca12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											&[data-strength='high']
 | 
				
			||||||
 | 
												> .value
 | 
				
			||||||
 | 
													background #61bb22
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											> .value
 | 
				
			||||||
 | 
												display block
 | 
				
			||||||
 | 
												width 0%
 | 
				
			||||||
 | 
												height 100%
 | 
				
			||||||
 | 
												background transparent
 | 
				
			||||||
 | 
												border-radius 4px
 | 
				
			||||||
 | 
												transition all 0.1s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								[type=text], [type=password]
 | 
				
			||||||
 | 
									user-select text
 | 
				
			||||||
 | 
									display inline-block
 | 
				
			||||||
 | 
									cursor auto
 | 
				
			||||||
 | 
									padding 0 12px
 | 
				
			||||||
 | 
									margin 0
 | 
				
			||||||
 | 
									width 100%
 | 
				
			||||||
 | 
									line-height 44px
 | 
				
			||||||
 | 
									font-size 1em
 | 
				
			||||||
 | 
									color #333 !important
 | 
				
			||||||
 | 
									background #fff !important
 | 
				
			||||||
 | 
									outline none
 | 
				
			||||||
 | 
									border solid 1px rgba(0, 0, 0, 0.1)
 | 
				
			||||||
 | 
									border-radius 4px
 | 
				
			||||||
 | 
									box-shadow 0 0 0 114514px #fff inset
 | 
				
			||||||
 | 
									transition all .3s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:hover
 | 
				
			||||||
 | 
										border-color rgba(0, 0, 0, 0.2)
 | 
				
			||||||
 | 
										transition all .1s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:focus
 | 
				
			||||||
 | 
										color $theme-color !important
 | 
				
			||||||
 | 
										border-color $theme-color
 | 
				
			||||||
 | 
										box-shadow 0 0 0 1024px #fff inset, 0 0 0 4px rgba($theme-color, 10%)
 | 
				
			||||||
 | 
										transition all 0s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:disabled
 | 
				
			||||||
 | 
										opacity 0.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								.agree-tou
 | 
				
			||||||
 | 
									padding 4px
 | 
				
			||||||
 | 
									border-radius 4px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:hover
 | 
				
			||||||
 | 
										background #f4f4f4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:active
 | 
				
			||||||
 | 
										background #eee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&, *
 | 
				
			||||||
 | 
										cursor pointer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									p
 | 
				
			||||||
 | 
										display inline
 | 
				
			||||||
 | 
										color #555
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								button
 | 
				
			||||||
 | 
									margin 0 0 32px 0
 | 
				
			||||||
 | 
									padding 16px
 | 
				
			||||||
 | 
									width 100%
 | 
				
			||||||
 | 
									font-size 1em
 | 
				
			||||||
 | 
									color #fff
 | 
				
			||||||
 | 
									background $theme-color
 | 
				
			||||||
 | 
									border-radius 3px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:hover
 | 
				
			||||||
 | 
										background lighten($theme-color, 5%)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:active
 | 
				
			||||||
 | 
										background darken($theme-color, 5%)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="typescript">
 | 
				
			||||||
 | 
						this.mixin('api');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.usernameState = null;
 | 
				
			||||||
 | 
						this.passwordStrength = '';
 | 
				
			||||||
 | 
						this.passwordRetypeState = null;
 | 
				
			||||||
 | 
						this.recaptchaed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.aboutUrl = `${_DOCS_URL_}/${_LANG_}/tou`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						window.onRecaptchaed = () => {
 | 
				
			||||||
 | 
							this.recaptchaed = true;
 | 
				
			||||||
 | 
							this.update();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						window.onRecaptchaExpired = () => {
 | 
				
			||||||
 | 
							this.recaptchaed = false;
 | 
				
			||||||
 | 
							this.update();
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.on('mount', () => {
 | 
				
			||||||
 | 
							this.update({
 | 
				
			||||||
 | 
								recaptcha: {
 | 
				
			||||||
 | 
									site_key: _RECAPTCHA_SITEKEY_
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const head = document.getElementsByTagName('head')[0];
 | 
				
			||||||
 | 
							const script = document.createElement('script');
 | 
				
			||||||
 | 
							script.setAttribute('src', 'https://www.google.com/recaptcha/api.js');
 | 
				
			||||||
 | 
							head.appendChild(script);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.onChangeUsername = () => {
 | 
				
			||||||
 | 
							const username = this.$refs.username.value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (username == '') {
 | 
				
			||||||
 | 
								this.update({
 | 
				
			||||||
 | 
									usernameState: null
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const err =
 | 
				
			||||||
 | 
								!username.match(/^[a-zA-Z0-9\-]+$/) ? 'invalid-format' :
 | 
				
			||||||
 | 
								username.length < 3 ? 'min-range' :
 | 
				
			||||||
 | 
								username.length > 20 ? 'max-range' :
 | 
				
			||||||
 | 
								null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (err) {
 | 
				
			||||||
 | 
								this.update({
 | 
				
			||||||
 | 
									usernameState: err
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.update({
 | 
				
			||||||
 | 
								usernameState: 'wait'
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.api('username/available', {
 | 
				
			||||||
 | 
								username: username
 | 
				
			||||||
 | 
							}).then(result => {
 | 
				
			||||||
 | 
								this.update({
 | 
				
			||||||
 | 
									usernameState: result.available ? 'ok' : 'unavailable'
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}).catch(err => {
 | 
				
			||||||
 | 
								this.update({
 | 
				
			||||||
 | 
									usernameState: 'error'
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.onChangePassword = () => {
 | 
				
			||||||
 | 
							const password = this.$refs.password.value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (password == '') {
 | 
				
			||||||
 | 
								this.passwordStrength = '';
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const strength = getPasswordStrength(password);
 | 
				
			||||||
 | 
							this.passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
 | 
				
			||||||
 | 
							this.update();
 | 
				
			||||||
 | 
							this.$refs.passwordMetar.style.width = `${strength * 100}%`;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.onChangePasswordRetype = () => {
 | 
				
			||||||
 | 
							const password = this.$refs.password.value;
 | 
				
			||||||
 | 
							const retypedPassword = this.$refs.passwordRetype.value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (retypedPassword == '') {
 | 
				
			||||||
 | 
								this.passwordRetypeState = null;
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.passwordRetypeState = password == retypedPassword ? 'match' : 'not-match';
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.onsubmit = e => {
 | 
				
			||||||
 | 
							e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const username = this.$refs.username.value;
 | 
				
			||||||
 | 
							const password = this.$refs.password.value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const locker = document.body.appendChild(document.createElement('mk-locker'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.api('signup', {
 | 
				
			||||||
 | 
								username: username,
 | 
				
			||||||
 | 
								password: password,
 | 
				
			||||||
 | 
								'g-recaptcha-response': grecaptcha.getResponse()
 | 
				
			||||||
 | 
							}).then(() => {
 | 
				
			||||||
 | 
								this.api('signin', {
 | 
				
			||||||
 | 
									username: username,
 | 
				
			||||||
 | 
									password: password
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									location.href = '/';
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}).catch(() => {
 | 
				
			||||||
 | 
								alert('%i18n:common.tags.mk-signup.some-error%');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								grecaptcha.reset();
 | 
				
			||||||
 | 
								this.recaptchaed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								locker.parentNode.removeChild(locker);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					@ -17,9 +17,24 @@
 | 
				
			||||||
			<p class="c">{ _COPYRIGHT_ }</p>
 | 
								<p class="c">{ _COPYRIGHT_ }</p>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</footer>
 | 
						</footer>
 | 
				
			||||||
 | 
						<modal name="signup">
 | 
				
			||||||
 | 
							<mk-signup/>
 | 
				
			||||||
 | 
						</modal>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							signup() {
 | 
				
			||||||
 | 
								this.$modal.show('signup');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
#wait {
 | 
					#wait {
 | 
				
			||||||
	right: auto;
 | 
						right: auto;
 | 
				
			||||||
| 
						 | 
					@ -112,10 +127,3 @@
 | 
				
			||||||
				font-size 10px
 | 
									font-size 10px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from 'vue'
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,8 +9,10 @@ declare const _HOST_: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import VueRouter from 'vue-router';
 | 
					import VueRouter from 'vue-router';
 | 
				
			||||||
 | 
					import VModal from 'vue-js-modal';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vue.use(VueRouter);
 | 
					Vue.use(VueRouter);
 | 
				
			||||||
 | 
					Vue.use(VModal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import App from './app.vue';
 | 
					import App from './app.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue