@ -1,4 +1,47 @@ |
|||
package com.dreamchaser.depository_manage.entity; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* 出库子订单 |
|||
*/ |
|||
@Data |
|||
public class ApplicationOutRecordMin { |
|||
/** |
|||
* id |
|||
*/ |
|||
private Integer id; |
|||
|
|||
/** |
|||
* 物料id |
|||
*/ |
|||
private Integer mid; |
|||
|
|||
/** |
|||
* 仓库id |
|||
*/ |
|||
private Integer depositoryId; |
|||
/** |
|||
* 对应库位id |
|||
*/ |
|||
private Integer placeId; |
|||
/** |
|||
* 数量 |
|||
*/ |
|||
private Integer quantity; |
|||
/** |
|||
* 出库单号 |
|||
*/ |
|||
private String code; |
|||
|
|||
/** |
|||
* 审核人编号 |
|||
*/ |
|||
private Integer checkId; |
|||
|
|||
/** |
|||
* 主订单编号 |
|||
*/ |
|||
private Integer parentId; |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,5 @@ |
|||
/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl |
|||
* Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
|
|||
* */ |
|||
|
|||
!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b<s.length;b++){var c=s[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!o[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(v(c.styleSheet.rawCssText,e,f),o[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!r||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}w()};x(),c.update=x,c.getEmValue=t,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this); |
|||
@ -0,0 +1,25 @@ |
|||
const Home = httpVueLoader('static/vuePage/index.vue'); |
|||
var Header = httpVueLoader('static/vuePage/scanCode.vue'); |
|||
Vue.component('my-header', Header); |
|||
const Foo = { |
|||
template: '<div>foo</div>' |
|||
}; |
|||
const Bar = { |
|||
template: '<div>bar</div>' |
|||
}; |
|||
const routes = [{ |
|||
path: '/', |
|||
component: Home, |
|||
children: [{ |
|||
path: '/foo', |
|||
component: Foo |
|||
}, |
|||
{ |
|||
path: '/bar', |
|||
component: Bar |
|||
}, |
|||
] |
|||
}, ]; |
|||
const router = new VueRouter({ |
|||
routes // (缩写) 相当于 routes: routes
|
|||
}) |
|||
@ -0,0 +1,307 @@ |
|||
<template> |
|||
<div class="test"> |
|||
<div class="fixBox"> |
|||
<div class="iconBox" |
|||
@click="() => { |
|||
this.$router.history.go(-1); |
|||
}"> |
|||
<i class="iconfont icon-fanhuitubiao"></i> |
|||
</div> |
|||
<div class="videoBox"> |
|||
<div class="outVideo" v-if="isAnimation"> |
|||
<video |
|||
autoPlay |
|||
ref="outVideo" |
|||
webkit-playsinline |
|||
x5-video-player-type="h5" |
|||
x5-video-player-fullscreen="true" |
|||
x-webkit-airplay="true" |
|||
playsinline |
|||
v-if="!trueFlag" |
|||
id="myVideo" |
|||
></video> |
|||
</div> |
|||
<div class="codebg" v-if="!trueFlag"> |
|||
<div class="line"></div> |
|||
</div> |
|||
<canvas |
|||
id="qr-canvas" |
|||
style="width:6rem;height:6rem;" |
|||
ref="canvas" |
|||
></canvas> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import jsQR from "jsqr"; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
photoBase: "", |
|||
trueFlag: false, |
|||
isAnimation: true |
|||
}; |
|||
}, |
|||
mounted() { |
|||
this.outvideo = this.$refs.outVideo; |
|||
this.cvsele = this.$refs.canvas; |
|||
this.canvas = this.cvsele.getContext("2d"); |
|||
this.video = document.createElement("video"); |
|||
this.$nextTick(() => { |
|||
this.getVideo(); |
|||
}); |
|||
}, |
|||
methods: { |
|||
getVideo() { |
|||
let self = this; |
|||
try { |
|||
navigator.getUserMedia = |
|||
navigator.mediaDevices.getUserMedia || |
|||
navigator.mediaDevices.webkitGetUserMedia || |
|||
navigator.mediaDevices.mozGetUserMedia; |
|||
self.URL = |
|||
window.URL || window.webkitURL || window.mozURL || window.msURL; |
|||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { |
|||
//console.log(navigator.mediaDevices.getUserMedia); |
|||
navigator.mediaDevices |
|||
.getUserMedia({ |
|||
video: {facingMode: {exact: "environment"}} |
|||
// video: { facingMode: "environment" } |
|||
}) |
|||
.then(stream => { |
|||
this.outvideo.srcObject = stream; |
|||
this.video.srcObject = stream; |
|||
this.video.setAttribute("playsinline", true); |
|||
this.video.setAttribute("webkit-playsinline", true); |
|||
this.video.addEventListener("loadedmetadata", () => { |
|||
this.$refs.outVideo.play(); |
|||
this.video.play(); |
|||
this.sweep(); |
|||
}); |
|||
setTimeout(() => { |
|||
this.$refs.outVideo.play(); |
|||
this.video.play(); |
|||
}, 150); |
|||
}) |
|||
.catch(function (err) { |
|||
console.log(err); |
|||
alert("请允许网站使用您的摄像头权限"); |
|||
self.$router.history.go(-1); |
|||
}); |
|||
} else if (navigator.getUserMedia) { |
|||
navigator |
|||
.getUserMedia({ |
|||
video: true |
|||
}) |
|||
.then(stream => { |
|||
this.outvideo.srcObject = stream; |
|||
this.video.srcObject = stream; |
|||
this.video.setAttribute("playsinline", true); |
|||
this.video.setAttribute("webkit-playsinline", true); |
|||
this.video.addEventListener("loadedmetadata", () => { |
|||
this.$refs.outVideo.play(); |
|||
this.video.play(); |
|||
this.sweep(); |
|||
}); |
|||
setTimeout(() => { |
|||
this.$refs.outVideo.play(); |
|||
this.video.play(); |
|||
}, 150); |
|||
}) |
|||
.catch(function (err) { |
|||
alert(err); |
|||
}); |
|||
} |
|||
} catch (err) { |
|||
console.error(err); |
|||
} |
|||
}, |
|||
sweep() { |
|||
if (this.video.readyState === this.video.HAVE_ENOUGH_DATA) { |
|||
const {videoWidth, videoHeight} = this.video; |
|||
this.cvsele.width = videoWidth; |
|||
this.cvsele.height = videoHeight; |
|||
this.canvas.drawImage(this.video, 0, 0, videoWidth, videoHeight); |
|||
try { |
|||
const img = this.canvas.getImageData(0, 0, videoWidth, videoHeight); |
|||
this.imgurl = img; |
|||
const obj = jsQR(img.data, img.width, img.height, { |
|||
inversionAttempts: "dontInvert" |
|||
}); |
|||
console.log(obj); |
|||
if (obj) { |
|||
const loc = obj.location; |
|||
this.draw(loc.topLeftCorner, loc.topRightCorner); |
|||
this.draw(loc.topRightCorner, loc.bottomRightCorner); |
|||
this.draw(loc.bottomRightCorner, loc.bottomLeftCorner); |
|||
this.draw(loc.bottomLeftCorner, loc.topLeftCorner); |
|||
if (obj.data) { |
|||
console.info("识别结果:", obj.data); |
|||
this.isAnimation = false; |
|||
} |
|||
} else { |
|||
// console.error("识别失败,请检查二维码是否正确!"); |
|||
} |
|||
} catch (err) { |
|||
// console.error("识别失败,请检查二维码是否正确!", err); |
|||
} |
|||
} |
|||
if (this.isAnimation) { |
|||
this.timer = requestAnimationFrame(() => { |
|||
this.sweep(); |
|||
}); |
|||
} |
|||
}, |
|||
draw(begin, end) { |
|||
this.canvas.beginPath(); |
|||
this.canvas.moveTo(begin.x, begin.y); |
|||
this.canvas.lineTo(end.x, end.y); |
|||
this.canvas.lineWidth = 3; |
|||
this.canvas.strokeStyle = "red"; |
|||
this.canvas.stroke(); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.test { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.biginSearch { |
|||
outline: none; |
|||
border: none; |
|||
width: 3.15rem; |
|||
height: 0.8rem; |
|||
background: var(--themeColorzc); |
|||
border-radius: 0.1rem; |
|||
color: #fff; |
|||
font-weight: 700; |
|||
margin-top: 0.3rem; |
|||
font-size: 0.3rem; |
|||
} |
|||
|
|||
.fixBox { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
bottom: 0; |
|||
right: 0; |
|||
// background: rgba(47, 47, 47, 0.81); |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.videoBox { |
|||
// border: 2px solid rgba(247, 241, 241, 0.81); |
|||
// width: 6rem; |
|||
// height: 6rem; |
|||
// overflow: hidden; |
|||
width: 100%; |
|||
height: 100%; |
|||
position: relative; |
|||
background-color: black; |
|||
|
|||
.outVideo { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
bottom: 0; |
|||
right: 0; |
|||
z-index: 999; |
|||
background-color: black; |
|||
|
|||
video { |
|||
width: 100%; |
|||
height: 100%; |
|||
object-fit: fill; |
|||
} |
|||
} |
|||
} |
|||
|
|||
canvas { |
|||
position: absolute; |
|||
left: 50%; |
|||
top: 50%; |
|||
transform: translate(-50%, -50%); |
|||
} |
|||
|
|||
.codebg { |
|||
position: absolute; |
|||
width: 6rem; |
|||
height: 6rem; |
|||
margin: 0px auto; |
|||
left: 50%; |
|||
top: 50%; |
|||
transform: translate(-50%, -50%); |
|||
|
|||
z-index: 999; |
|||
/*此处为了居中*/ |
|||
// background: url(img/ewm1.jpg) center top no-repeat; |
|||
/*二维码*/ |
|||
} |
|||
|
|||
.line { |
|||
position: absolute; |
|||
left: 0px; |
|||
z-index: 2; |
|||
height: 3px; |
|||
width: 6rem; |
|||
/* background: url(img/share/dapai.png) no-repeat; */ |
|||
/*上下扫的线*/ |
|||
background-color: var(--themeColorzc); |
|||
opacity: 0.5; |
|||
/*动画效果*/ |
|||
animation: myScan 1s infinite alternate; |
|||
-webkit-animation: myScan 1s infinite alternate; |
|||
} |
|||
|
|||
@keyframes myScan { |
|||
from { |
|||
top: 0px; |
|||
} |
|||
|
|||
to { |
|||
top: 300px; |
|||
} |
|||
} |
|||
|
|||
@font-face { |
|||
font-family: "iconfont"; /* Project id 2570754 */ |
|||
src: url("//at.alicdn.com/t/font_2570754_w4gqnoegfag.woff2?t=1629957776725") format("woff2"), |
|||
url("//at.alicdn.com/t/font_2570754_w4gqnoegfag.woff?t=1629957776725") format("woff"), |
|||
url("//at.alicdn.com/t/font_2570754_w4gqnoegfag.ttf?t=1629957776725") format("truetype"); |
|||
} |
|||
|
|||
.iconfont { |
|||
font-family: "iconfont" !important; |
|||
font-size: 16px; |
|||
font-style: normal; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
} |
|||
|
|||
.icon-fanhuitubiao:before { |
|||
font-size: 0.48rem; |
|||
color: #fff; |
|||
content: "\e65c"; |
|||
} |
|||
|
|||
.iconBox { |
|||
position: absolute; |
|||
top: 0.3rem; |
|||
left: 0.3rem; |
|||
} |
|||
</style> |
|||
|
|||
|
|||
@ -1,144 +1,68 @@ |
|||
<template> |
|||
<view class="content"> |
|||
<view class="text-area"> |
|||
<text>{{code}}</text> |
|||
<text class="title">{{result}}</text> |
|||
</view> |
|||
<view v-if="select_view == 'credible_view'" class="open_view"> |
|||
<button type="primary">受信地址</button> |
|||
</view> |
|||
<div> |
|||
<p class="error">{{ error }}</p><!--错误信息--> |
|||
|
|||
<view v-if="select_view == 'open_view'" class="open_view"> |
|||
<button type="primary">不受信地址</button> |
|||
</view> |
|||
</view> |
|||
<p class="decode-result">扫描结果: <b>{{ result }}</b></p><!--扫描结果--> |
|||
|
|||
<qrcode-stream @decode="onDecode" @init="onInit" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import helper from "@/common/helper.js"; |
|||
|
|||
export default{ |
|||
data() { |
|||
return { |
|||
domain:"www.zunkr.com", |
|||
code: '默认的内容',// 获取的二维码 |
|||
result: '', |
|||
select_view:"no", |
|||
model:"pda", |
|||
callbackurl:'' |
|||
} |
|||
}, |
|||
onLoad:function(option){ |
|||
var that = this; |
|||
if(!option.code){ |
|||
this.result = "扫码内容为空"; |
|||
} |
|||
if(option.model){ |
|||
this.model = option.model; |
|||
} |
|||
this.callbackurl = decodeURIComponent(option.callbackurl);// 回调地址 |
|||
const code = decodeURIComponent(option.code); |
|||
// code = "https://www.baidu.com?51b6d2c0-1964-11ea-a286-00163e0dfa3d"; |
|||
this.code = code; |
|||
// 分解是类型 |
|||
switch (typeof(code)){ |
|||
case 'number': |
|||
this.result = code+"number 类型"; |
|||
break; |
|||
case 'string': |
|||
this.result = code+"string 类型"; |
|||
|
|||
if(helper.IsURL(code)){ |
|||
//下载插件 |
|||
//cnpm install --save vue-qrcode-reader |
|||
|
|||
// 解析内部地址 |
|||
var domain = helper.AnalysisURL(code)[2] |
|||
console.log(domain) |
|||
console.log(that.domain) |
|||
console.log(that.model) |
|||
if(domain === that.domain){ |
|||
// 检测工作模式 |
|||
if (that.model == 'pda'){ |
|||
// 分解参数 |
|||
var param = code.split("?"); |
|||
var uuid = param[1]; |
|||
this.result = domain+"为可控域名\r\nuuid为"+uuid; |
|||
if(that.callbackurl){ |
|||
uni.redirectTo({ |
|||
url:that.callbackurl+'?item='+uuid |
|||
}) |
|||
} |
|||
|
|||
}else if(that.model == 'browser'){ |
|||
this.result = domain+"可信域验证通过"; |
|||
this.select_view = "credible_view" |
|||
} |
|||
}else{ |
|||
// 检测工作模式 |
|||
console.log(that.model) |
|||
if (that.model == 'pda'){ |
|||
uni.showToast({ |
|||
title:"PDA模式权限验证失败" |
|||
}) |
|||
this.result = '模式权限验证失败'; |
|||
this.select_view = "no" |
|||
}else if(that.model == 'browser'){ |
|||
this.result = domain+"不属于内部域名可以第二webview打开"; |
|||
this.select_view = "open_view" |
|||
} |
|||
//引入 |
|||
import { QrcodeStream } from 'static/lib/vue-qrcode-reader'; |
|||
|
|||
} |
|||
export default { |
|||
|
|||
}else{ |
|||
this.result = code+"外部数据禁止使用"; |
|||
} |
|||
//注册 |
|||
name:"scanCode", |
|||
components: { QrcodeStream }, |
|||
|
|||
break; |
|||
case 'boolean': |
|||
this.result = code+"boolean 类型"; |
|||
break; |
|||
case 'array': |
|||
this.result = code+"array 类型"; |
|||
break; |
|||
default: |
|||
this.result = code+"default 类型"; |
|||
break; |
|||
data () { |
|||
return { |
|||
result: '',//扫码结果信息 |
|||
error: '',//错误信息 |
|||
} |
|||
|
|||
console.log('App Load') |
|||
}, |
|||
onShow: function() { |
|||
var that = this; |
|||
|
|||
console.log('App Show') |
|||
}, |
|||
methods: { |
|||
back(){ |
|||
console.log("回去"); |
|||
uni.navigateBack({ |
|||
delta:1 |
|||
}) |
|||
|
|||
onDecode (result) { |
|||
alert(result); |
|||
this.result = result |
|||
}, |
|||
open_view(){ |
|||
console.log(encodeURI(this.code)); |
|||
uni.navigateTo({ |
|||
url: "/pages/webviews/openview?url="+ encodeURI(this.code) , |
|||
animationType: 'pop-in', |
|||
animationDuration: 200, |
|||
success: res => {}, |
|||
fail: () => {}, |
|||
complete: () => {} |
|||
}); |
|||
|
|||
|
|||
async onInit (promise) { |
|||
try { |
|||
await promise |
|||
} catch (error) { |
|||
if (error.name === 'NotAllowedError') { |
|||
this.error = "ERROR: 您需要授予相机访问权限" |
|||
} else if (error.name === 'NotFoundError') { |
|||
this.error = "ERROR: 这个设备上没有摄像头" |
|||
} else if (error.name === 'NotSupportedError') { |
|||
this.error = "ERROR: 所需的安全上下文(HTTPS、本地主机)" |
|||
} else if (error.name === 'NotReadableError') { |
|||
this.error = "ERROR: 相机被占用" |
|||
} else if (error.name === 'OverconstrainedError') { |
|||
this.error = "ERROR: 安装摄像头不合适" |
|||
} else if (error.name === 'StreamApiNotSupportedError') { |
|||
this.error = "ERROR: 此浏览器不支持流API" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.scan-result { |
|||
min-height: 50upx; |
|||
line-height: 50upx; |
|||
<style scoped> |
|||
.error { |
|||
font-weight: bold; |
|||
color: red; |
|||
} |
|||
</style> |
|||
|
|||
@ -0,0 +1,634 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en" xmlns:th="http://www.thymeleaf.org"> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<title>分步表单</title> |
|||
<meta name="renderer" content="webkit"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> |
|||
<link rel="stylesheet" href="/static/lib/layui-v2.6.3/css/layui.css" media="all"> |
|||
<link rel="stylesheet" href="/static/css/public.css" media="all"> |
|||
<link rel="stylesheet" href="/static/js/lay-module/step-lay/step.css" media="all"> |
|||
</head> |
|||
<body> |
|||
<style> |
|||
.inputdiv { |
|||
display: flex; |
|||
background-color: #fff; |
|||
height: 38px; |
|||
line-height: 38px; |
|||
border: 1px solid rgb(238, 238, 238); |
|||
} |
|||
|
|||
.layui-form-label { |
|||
padding: 9px 0px; |
|||
text-align: left; |
|||
} |
|||
|
|||
.layui-input-block { |
|||
margin-left: 80px; |
|||
} |
|||
|
|||
</style> |
|||
|
|||
|
|||
<div class="layuimini-container"> |
|||
<div class="layuimini-main"> |
|||
<div class="layui-fluid"> |
|||
<!-- 入库申请提交--> |
|||
<div class="layui-carousel" id="stepForm" lay-filter="stepForm" style="margin: 0 auto; "> |
|||
<input th:value="${materialList}" style="display:none;" id="scanValue_materialList"> |
|||
<input th:value="${depository}" style="display: none" id="scanValue_depositoryId"> |
|||
<input th:value="${place}" style="display: none" id="scanValue_placeId"> |
|||
<div carousel-item style="overflow: inherit"> |
|||
<div> |
|||
<form class="layui-form" style="margin: 0 auto;max-width: 460px;padding-top: 40px;"> |
|||
<div class="layui-card" id="cardParent"> |
|||
|
|||
|
|||
<!-- 提交按钮--> |
|||
<div class="layui-form-item" id="btn_sub"> |
|||
<div class="layui-input-block" style="bottom: 15px;"> |
|||
<button id="submitForm" class="layui-btn" lay-submit lay-filter="formStep" |
|||
style="margin-left: 15%"> |
|||
 提交  |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
<!-- 下一步--> |
|||
<div> |
|||
<form class="layui-form" style="margin: 0 auto;max-width: 460px;padding-top: 40px;"> |
|||
<div style="text-align: center;margin-top: 90px;"> |
|||
<i class="layui-icon layui-circle" |
|||
style="color: white;font-size:30px;font-weight:bold;background: #52C41A;padding: 20px;line-height: 80px;"></i> |
|||
<div style="font-size: 24px;color: #333;font-weight: 500;margin-top: 30px;"> |
|||
提交成功 |
|||
</div> |
|||
<div style="text-align: center;margin-top: 50px;"> |
|||
<button class="layui-btn next">再填写一次</button> |
|||
</div> |
|||
</div> |
|||
|
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script src="/static/lib/layui-v2.6.3/layui.js" charset="utf-8"></script> |
|||
<script src="/static/js/lay-config.js?v=1.0.4" charset="utf-8"></script> |
|||
<script> |
|||
// 用于添加标签 |
|||
function addItem(obj) { |
|||
} |
|||
|
|||
// 用于删除标签 |
|||
function deleteItem(obj) { |
|||
} |
|||
|
|||
// 用于编码查询 |
|||
function selectCode(obj) { |
|||
} |
|||
|
|||
// 用于点击搜索按钮 |
|||
function selectMaterial(obj) { |
|||
} |
|||
|
|||
// 用于物料名称查询 |
|||
function selectMaterialByName(obj) { |
|||
} |
|||
|
|||
// 用于加载仓库菜单 |
|||
function openDepositoryTree(obj) { |
|||
} |
|||
|
|||
// 用于页面初始化时的卡片构造 |
|||
function initForm(num, obj) { |
|||
} |
|||
|
|||
// 用于暂存卡片个数 |
|||
var params = []; |
|||
// 用于卡片编号 |
|||
var NewIdNumber = 0; |
|||
layui.use(['form', 'step', 'flow'], function () { |
|||
var $ = layui.$, |
|||
form = layui.form, |
|||
flow = layui.flow, |
|||
step = layui.step; |
|||
let scanValue = {}; |
|||
// 页面初始化 |
|||
$(function () { |
|||
let depository = $("#scanValue_depositoryId").val(); |
|||
let place = $("#scanValue_placeId").val(); |
|||
let materialList = $("#scanValue_materialList").val(); |
|||
materialList = JSON.parse(materialList); |
|||
if (depository !== undefined && depository !== null && depository !== "") { |
|||
// 如果扫描到仓库 |
|||
depository = JSON.parse(depository); |
|||
scanValue.depository = depository; |
|||
} |
|||
if (place !== undefined && place !== null && place !== "") { |
|||
// 如果扫描到库位 |
|||
place = JSON.parse(place); |
|||
scanValue.place = place; |
|||
} |
|||
if (materialList.length > 0) { |
|||
for (let i = 0; i < materialList.length; i++) { |
|||
initForm(i, materialList[i]); |
|||
} |
|||
} else { |
|||
var material = {}; |
|||
material.mname = ''; |
|||
material.mid = ''; |
|||
material.code = ''; |
|||
initForm(0, material); |
|||
} |
|||
|
|||
}); |
|||
|
|||
//用于页面初始化时的卡片构造 |
|||
initForm = function (num, obj) { |
|||
var parent = $("#cardParent"); |
|||
// 获取待添加父类 |
|||
NewIdNumber = num; |
|||
if (num === 0) { // 如果是第一个 |
|||
NewIdNumber = ""; |
|||
} |
|||
var depositoryItem = ` |
|||
<div class="layui-form-item"> |
|||
<label class="layui-form-label">仓库:</label> |
|||
<div class="layui-input-block"> |
|||
<input type="text" placeholder="请选择仓库" class="layui-input" |
|||
id="openSonByDepository" readonly onclick="openDepositoryTree(this)" |
|||
lay-verify="required"/> |
|||
<input type="text" name=` + "depositoryId" + NewIdNumber + ` class="layui-input" id="depositoryId" |
|||
style="display: none" lay-verify="required"/> |
|||
<input type="text" name=` + "placeId" + NewIdNumber + ` class="layui-input" id="placeId" value="0" |
|||
style="display: none" lay-verify="required"/> |
|||
|
|||
</div> |
|||
</div> |
|||
`; |
|||
if (scanValue.depository !== undefined && scanValue.depository !== null && scanValue.depository !== "") { |
|||
let depository = scanValue.depository; |
|||
// 如果扫描了仓库 |
|||
depositoryItem = `<div class="layui-form-item"> |
|||
<label class="layui-form-label">仓库:</label> |
|||
<div class="layui-input-block"> |
|||
<input type="text" placeholder="请选择仓库" class="layui-input" value=` + depository.dname + ` |
|||
id="openSonByDepository" readonly onclick="openDepositoryTree(this)" |
|||
lay-verify="required"/> |
|||
<input type="text" name=` + "depositoryId" + NewIdNumber + ` class="layui-input" id="depositoryId" value=` + depository.did + ` |
|||
style="display: none" lay-verify="required"/> |
|||
<input type="text" name=` + "placeId" + NewIdNumber + ` class="layui-input" id="placeId" value="0" |
|||
style="display: none" lay-verify="required"/> |
|||
|
|||
</div> |
|||
</div>`; |
|||
} |
|||
|
|||
if (scanValue.place !== undefined && scanValue.place !== null && scanValue.place !== "") { |
|||
// 如果扫描了库位 |
|||
let place = scanValue.place; |
|||
depositoryItem = `<div class="layui-form-item"> |
|||
<label class="layui-form-label">仓库:</label> |
|||
<div class="layui-input-block"> |
|||
<input type="text" placeholder="请选择仓库" class="layui-input" |
|||
id="openSonByDepository" readonly onclick="openDepositoryTree(this)" value=` + place.dname + "-" + place.code + ` |
|||
lay-verify="required"/> |
|||
<input type="text" name=` + "depositoryId" + NewIdNumber + ` class="layui-input" id="depositoryId" value=` + place.depositoryId + ` |
|||
style="display: none" lay-verify="required"/> |
|||
<input type="text" name=` + "placeId" + NewIdNumber + ` class="layui-input" id="placeId" value=` + place.pid + ` |
|||
style="display: none" lay-verify="required"/> |
|||
|
|||
</div> |
|||
</div>`; |
|||
|
|||
} |
|||
var firstItem = |
|||
// 前半部分 |
|||
` <div class="layui-card-body" style="padding-right: 0px" id=` + "cardItem" + NewIdNumber + `> |
|||
<hr> |
|||
<i class="layui-icon layui-icon-subtraction" style="display: inline" onclick="deleteItem(this)"></i> |
|||
<div class="layui-form-item"> |
|||
<label class="layui-form-label">物料名称</label> |
|||
<div class="layui-input-block"> |
|||
<div class="inputdiv"> |
|||
<input type="text" placeholder="请选择物料" class="layui-input" style="border-style: none" |
|||
id="openSonByMaterial" lay-verify="required" value="${obj.mname}" onblur="selectMaterialByName(this)"/> |
|||
<i class="layui-icon layui-icon-search" style="display: inline" id="selectMaterial" onclick="selectMaterial(this)"></i> |
|||
</div> |
|||
<input type="text" name=` + "mid" + NewIdNumber + ` class="layui-input" id="mid" value="${obj.mid}" |
|||
style="display: none" lay-verify="required" /> |
|||
</div> |
|||
</div> |
|||
<div class="layui-form-item"> |
|||
<label class="layui-form-label">物料编码:</label> |
|||
<div class="layui-input-block"> |
|||
<input id="code" name=` + "code" + NewIdNumber + ` type="text" placeholder="请填写入物料编码" onblur="selectCode(this)" |
|||
class="layui-input" lay-verify="required" value="${obj.code}"> |
|||
</div> |
|||
</div> |
|||
<div class="layui-form-item"> |
|||
<label class="layui-form-label">物料单价:</label> |
|||
<div class="layui-input-block"> |
|||
<input name=` + "price" + NewIdNumber + ` type="number" placeholder="请填写入物料单价" value="" |
|||
class="layui-input" lay-verify="number"> |
|||
</div> |
|||
</div> |
|||
<div class="layui-form-item"> |
|||
<label class="layui-form-label">物料数量:</label> |
|||
<div class="layui-input-block"> |
|||
<input name=` + "quantity" + NewIdNumber + ` type="number" placeholder="请填写入物料数量" value="" |
|||
class="layui-input" lay-verify="number"> |
|||
</div> |
|||
</div>`; |
|||
|
|||
|
|||
var lastItem = `<div class="layui-form-item"> |
|||
<label class="layui-form-label">备注说明:</label> |
|||
<div class="layui-input-block"> |
|||
<textarea name=` + "applyRemark" + NewIdNumber + ` placeholder="请填写相关原因及申请原因" value="" |
|||
class="layui-textarea"></textarea> |
|||
</div> |
|||
</div> |
|||
<i class="layui-icon layui-icon-addition" style="display: inline" onclick="addItem(this)"></i> |
|||
</div>`; |
|||
// 获取当前高度 |
|||
var height = parseInt(($("#stepForm").css('height')).split("px")[0]); |
|||
if (NewIdNumber !== "") { |
|||
params.push(NewIdNumber) |
|||
} |
|||
$("#stepForm").css("height", height + 475 + 'px'); |
|||
var materialItem = firstItem + depositoryItem + lastItem; // 最终 |
|||
$("#btn_sub").prepend(materialItem) |
|||
}; |
|||
|
|||
// 用于分步表单加载 |
|||
step.render({ |
|||
elem: '#stepForm', |
|||
filter: 'stepForm', |
|||
width: '100%', //设置容器宽度 |
|||
height: '600px', |
|||
stepItems: [{ |
|||
title: '填写信息' |
|||
}, { |
|||
title: '提交成功' |
|||
}] |
|||
}); |
|||
|
|||
|
|||
// 用于提交操作 |
|||
form.on('submit(formStep)', function (data) { |
|||
data = data.field; |
|||
data.type = 1; |
|||
data.params = params; |
|||
$.ajax({ |
|||
url: "/depositoryRecord/applicationIn", |
|||
type: 'post', |
|||
dataType: 'json', |
|||
contentType: "application/json;charset=utf-8", |
|||
data: JSON.stringify(data), |
|||
beforeSend: function () { |
|||
this.layerIndex = layer.load(0, {shade: [0.5, '#393D49']}); |
|||
}, |
|||
success: function (data) { |
|||
layer.close(this.layerIndex); |
|||
if (data.status >= 300) { |
|||
console.log(data) |
|||
var d = data.data; |
|||
if (d === "") { |
|||
layer.msg(data.statusInfo.detail); |
|||
} else { |
|||
var err = d["err"]; |
|||
var success = d["success"]; |
|||
var errMsg = ""; |
|||
for (let i = 0; i < err.length; i++) { |
|||
errMsg += "物料编码: " |
|||
errMsg += err[i]["code"]; |
|||
} |
|||
errMsg += "添加失败,请选择新仓库或库位"; |
|||
layer.msg(errMsg, { |
|||
icon: 5, |
|||
time: 1000 |
|||
}, function () { |
|||
step.next('#stepForm'); |
|||
}); |
|||
} |
|||
|
|||
} else { |
|||
layer.msg("申请提交成功", { |
|||
icon: 6,//成功的表情 |
|||
time: 500 //1秒关闭(如果不配置,默认是3秒) |
|||
}, function () { |
|||
step.next('#stepForm'); |
|||
}); |
|||
} |
|||
}, |
|||
complete: function () { |
|||
layer.close(this.layerIndex); |
|||
} |
|||
}); |
|||
return false; |
|||
}); |
|||
|
|||
|
|||
form.on('submit(formStep2)', function (data) { |
|||
step.next('#stepForm'); |
|||
return false; |
|||
}); |
|||
|
|||
$('.pre').click(function () { |
|||
step.pre('#stepForm'); |
|||
}); |
|||
|
|||
$('.next').click(function () { |
|||
step.next('#stepForm'); |
|||
}); |
|||
|
|||
// 实现卡片添加 |
|||
addItem = function (obj) { |
|||
// 获取父元素id |
|||
var parentId = obj.parentNode.id; |
|||
NewIdNumber = NewIdNumber + 1; |
|||
// 物料名称栏目 |
|||
var depositoryItem = ` <div class="layui-form-item"> |
|||
<label class="layui-form-label">仓库:</label> |
|||
<div class="layui-input-block"> |
|||
<input type="text" placeholder="请选择仓库" class="layui-input" |
|||
id="openSonByDepository" readonly onclick="openDepositoryTree(this)" |
|||
lay-verify="required"/> |
|||
<input type="text" name=` + "depositoryId" + NewIdNumber + ` class="layui-input" id="depositoryId" |
|||
style="display: none" lay-verify="required"/> |
|||
<input type="text" name=` + "placeId" + NewIdNumber + ` class="layui-input" id="placeId" |
|||
style="display: none" lay-verify="required"/> |
|||
|
|||
</div> |
|||
</div> |
|||
`; |
|||
if (scanValue.depository !== undefined && scanValue.depository !== null && scanValue.depository !== "") { |
|||
let depository = scanValue.depository; |
|||
// 如果扫描了仓库 |
|||
depositoryItem = `<div class="layui-form-item"> |
|||
<label class="layui-form-label">仓库:</label> |
|||
<div class="layui-input-block"> |
|||
<input type="text" placeholder="请选择仓库" class="layui-input" value=` + depository.dname + ` |
|||
id="openSonByDepository" readonly onclick="openDepositoryTree(this)" |
|||
lay-verify="required"/> |
|||
<input type="text" name=` + "depositoryId" + NewIdNumber + ` class="layui-input" id="depositoryId" value=` + depository.did + ` |
|||
style="display: none" lay-verify="required"/> |
|||
<input type="text" name=` + "placeId" + NewIdNumber + ` class="layui-input" id="placeId" value="0" |
|||
style="display: none" lay-verify="required"/> |
|||
|
|||
</div> |
|||
</div>`; |
|||
} |
|||
|
|||
if (scanValue.place !== undefined && scanValue.place !== null && scanValue.place !== "") { |
|||
// 如果扫描了库位 |
|||
let place = scanValue.place; |
|||
depositoryItem = `<div class="layui-form-item"> |
|||
<label class="layui-form-label">仓库:</label> |
|||
<div class="layui-input-block"> |
|||
<input type="text" placeholder="请选择仓库" class="layui-input" |
|||
id="openSonByDepository" readonly onclick="openDepositoryTree(this)" value=` + place.dname + "-" + place.code + ` |
|||
lay-verify="required"/> |
|||
<input type="text" name=` + "depositoryId" + NewIdNumber + ` class="layui-input" id="depositoryId" value=` + place.depositoryId + ` |
|||
style="display: none" lay-verify="required"/> |
|||
<input type="text" name=` + "placeId" + NewIdNumber + ` class="layui-input" id="placeId" value=` + place.pid + ` |
|||
style="display: none" lay-verify="required"/> |
|||
|
|||
</div> |
|||
</div>`; |
|||
|
|||
} |
|||
var firstItem = |
|||
` |
|||
<div class="layui-card-body" style="padding-right: 0px" id=` + "cardItem" + NewIdNumber + `> |
|||
<hr> |
|||
<i class="layui-icon layui-icon-subtraction" style="display: inline" onclick="deleteItem(this)"></i> |
|||
<div class="layui-form-item"> |
|||
<label class="layui-form-label">物料名称</label> |
|||
<div class="layui-input-block"> |
|||
<div class="inputdiv"> |
|||
<input type="text" placeholder="请选择物料" class="layui-input" style="border-style: none" |
|||
id="openSonByMaterial" lay-verify="required" onblur="selectMaterialByName(this)"/> |
|||
<i class="layui-icon layui-icon-search" style="display: inline" id="selectMaterial" onclick="selectMaterial(this)"></i> |
|||
</div> |
|||
<input type="text" name=` + "mid" + NewIdNumber + ` class="layui-input" id="mid" |
|||
style="display: none" lay-verify="required" /> |
|||
</div> |
|||
</div> |
|||
<div class="layui-form-item"> |
|||
<label class="layui-form-label">物料编码:</label> |
|||
<div class="layui-input-block"> |
|||
<input id="code" name=` + "code" + NewIdNumber + ` type="text" placeholder="请填写入物料编码" value="" onblur="selectCode(this)" |
|||
class="layui-input" lay-verify="required"> |
|||
</div> |
|||
</div> |
|||
<div class="layui-form-item"> |
|||
<label class="layui-form-label">物料单价:</label> |
|||
<div class="layui-input-block"> |
|||
<input name=` + "price" + NewIdNumber + ` type="number" placeholder="请填写入物料单价" value="" |
|||
class="layui-input" lay-verify="number"> |
|||
</div> |
|||
</div> |
|||
<div class="layui-form-item"> |
|||
<label class="layui-form-label">物料数量:</label> |
|||
<div class="layui-input-block"> |
|||
<input name=` + "quantity" + NewIdNumber + ` type="number" placeholder="请填写入物料数量" value="" |
|||
class="layui-input" lay-verify="number"> |
|||
</div> |
|||
</div>`; |
|||
|
|||
|
|||
var lastItem = `<div class="layui-form-item"> |
|||
<label class="layui-form-label">备注说明:</label> |
|||
<div class="layui-input-block"> |
|||
<textarea name=` + "applyRemark" + NewIdNumber + ` placeholder="请填写相关原因及申请原因" value="" |
|||
class="layui-textarea"></textarea> |
|||
</div> |
|||
</div> |
|||
<i class="layui-icon layui-icon-addition" style="display: inline" onclick="addItem(this)"></i> |
|||
</div>`; |
|||
// 获取当前高度 |
|||
var height = parseInt(($("#stepForm").css('height')).split("px")[0]); |
|||
params.push(NewIdNumber) |
|||
$("#stepForm").css("height", height + 475 + 'px'); |
|||
var materialItem = firstItem + depositoryItem + lastItem; |
|||
$("#" + parentId).after(materialItem); |
|||
}; |
|||
|
|||
// 实现卡片删除 |
|||
deleteItem = function (obj) { |
|||
// 获取父节点 |
|||
var parent = obj.parentNode; |
|||
var parentId = parent.id; |
|||
parentId = parseInt(parentId.split("cardItem")[1]); |
|||
// 获取祖父节点 |
|||
var reparent = parent.parentNode; |
|||
var height = parseInt(($("#stepForm").css('height')).split("px")[0]); |
|||
$("#stepForm").css("height", height - 475 + 'px'); |
|||
params = remove(params, parentId); |
|||
reparent.removeChild(parent); |
|||
}; |
|||
|
|||
|
|||
//删除数组中指定元素 |
|||
function remove(arr, item) { |
|||
var result = []; |
|||
for (let i = 0; i < arr.length; i++) { |
|||
if (arr[i] === item) { |
|||
continue; |
|||
} |
|||
result.push(arr[i]); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
|
|||
// 用于实现通过编码查询物料 |
|||
selectCode = function (obj) { |
|||
// 输入code |
|||
var code = obj.value; |
|||
// 获取对应元素 |
|||
var parent = obj.parentNode.parentNode.parentNode; |
|||
var children = parent.childNodes[5]; |
|||
var materialItem = children.childNodes[3].childNodes[1].childNodes; |
|||
var materialName = materialItem[1]; |
|||
var materialId = materialName.parentNode.parentNode.childNodes[3]; |
|||
var req = {}; |
|||
req.code = code; |
|||
req.type = "in"; |
|||
$.ajax({ |
|||
url: "/material/findMatrialByCode", |
|||
type: "get", |
|||
dataType: 'json', |
|||
data: req, |
|||
contentType: "application/json;charset=utf-8", |
|||
success: function (d) { |
|||
var d = d.data; |
|||
if (d == null) { |
|||
layer.msg("没有该编码,请确认是否输入正确"); |
|||
materialName.value = ""; |
|||
materialId.value = ""; |
|||
obj.value = ""; |
|||
} else { |
|||
materialName.value = d.mname; |
|||
materialId.value = d.id; |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// 用于实现点击搜索按钮 |
|||
selectMaterial = function (obj) { |
|||
var parent = obj.parentNode.parentNode.parentNode.parentNode; |
|||
var parentId = parent.id; |
|||
var codeChildren = parent.childNodes[7]; |
|||
var materialChildren = parent.childNodes[5]; |
|||
var codeItem = codeChildren.childNodes[3].childNodes; |
|||
var codeValue = codeItem[1]; |
|||
var materialItem = materialChildren.childNodes[3].childNodes[1].childNodes; |
|||
var materialName = materialItem[1]; |
|||
var materialId = materialName.parentNode.parentNode.childNodes[3]; |
|||
var mname = materialName.value; |
|||
layer.open({ |
|||
type: 2, |
|||
title: '弹窗内容', |
|||
skin: 'layui-layer-rim', |
|||
maxmin: true, |
|||
shadeClose: true, //点击遮罩关闭层 |
|||
area: ['70%', '70%'], |
|||
content: '/selectMaterialByCard?mname=' + mname + '&type=1&clickObj=' + parentId, |
|||
move: '.layui-layer-title', |
|||
fixed: false, |
|||
end: function () { |
|||
var mid = materialId.value; |
|||
$.ajax({ |
|||
url: "/material/findMatrialById?mid=" + mid, |
|||
type: "get", |
|||
dataType: 'json', |
|||
contentType: "application/json;charset=utf-8", |
|||
success: function (d) { |
|||
var material = d.data.materialById; |
|||
var code = material.code; |
|||
if (code === undefined) { |
|||
code = ""; |
|||
} |
|||
codeValue.value = code; |
|||
} |
|||
}); |
|||
|
|||
} |
|||
}); |
|||
}; |
|||
|
|||
//用于实现物料名称搜索 |
|||
selectMaterialByName = function (obj) { |
|||
// 输入code |
|||
var data = obj.value; |
|||
// 获取对应元素 |
|||
var parent = obj.parentNode.parentNode.parentNode.parentNode; |
|||
var materialChildren = parent.childNodes[5]; |
|||
var codeChildren = parent.childNodes[7]; |
|||
var codeItem = codeChildren.childNodes[3].childNodes; |
|||
var codeValue = codeItem[1]; |
|||
var materialItem = materialChildren.childNodes[3].childNodes[1].childNodes; |
|||
var materialName = materialItem[1]; |
|||
var materialId = materialName.parentNode.parentNode.childNodes[3]; |
|||
var req = {}; |
|||
req.mname = data; |
|||
$.ajax({ |
|||
url: "/material/findMaterialByCondition", |
|||
type: "post", |
|||
dataType: 'json', |
|||
data: JSON.stringify(req), |
|||
contentType: "application/json;charset=utf-8", |
|||
success: function (d) { |
|||
if (d.count > 1) { |
|||
layer.msg("请点击右侧搜索确定物品"); |
|||
materialId.value = ""; |
|||
codeValue.value = ""; |
|||
return false; |
|||
} else if (d.count === 0) { |
|||
layer.msg("没有该物品,请确认输入是否正确"); |
|||
materialId.value = ""; |
|||
codeValue.value = ""; |
|||
materialName.value = ""; |
|||
return false; |
|||
} else { |
|||
var material = d.data[0]; |
|||
materialName.value = material.mname; |
|||
materialId.value = material.id; |
|||
codeValue.value = material.code; |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// 用于打开仓库树形菜单 |
|||
openDepositoryTree = function (obj) { |
|||
var parent = obj.parentNode.parentNode.parentNode; |
|||
var parentId = parent.id; |
|||
layer.open({ |
|||
type: 2, |
|||
title: '弹窗内容', |
|||
skin: 'layui-layer-rim', |
|||
maxmin: true, |
|||
shadeClose: true, //点击遮罩关闭层 |
|||
area: ['70%', '70%'], |
|||
move: '.layui-layer-title', |
|||
fixed: false, |
|||
content: '/selectDepositoryByCard?type=1&clickObj=' + parentId, |
|||
}); |
|||
} |
|||
}) |
|||
</script> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,333 @@ |
|||
<!DOCTYPE html> |
|||
<html xmlns:th="http://www.w3.org/1999/xhtml"> |
|||
<html xmlns:th="http://www.thymeleaf.org"> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<title>扫码</title> |
|||
<link rel="stylesheet" href="/static/lib/layui-v2.6.3/css/layui.css" media="all"> |
|||
<link rel="stylesheet" href="/static/css/layuimini.css?v=2.0.4.2" media="all"> |
|||
<link rel="stylesheet" href="/static/css/themes/default.css" media="all"> |
|||
<link rel="stylesheet" href="/static/lib/font-awesome-4.7.0/css/font-awesome.min.css" media="all"> |
|||
<!--[if lt IE 9]> |
|||
<script src="/static/js/html5.min.js"></script> |
|||
<script src="/static/js/respond.min.js"></script> |
|||
<![endif]--> |
|||
<!-- vue相关--> |
|||
<script src="../static/js/vue/vue.js"></script> |
|||
<script src="../static/js/vue/vue-router.js"></script> |
|||
<script src="../static/lib/http-vue-loader/src/httpVueLoader.js"></script> |
|||
<script src="../static/js/VueQrcodeReader.umd.min.js"></script> |
|||
<style> |
|||
.validation-success, |
|||
.validation-failure, |
|||
.validation-pending { |
|||
position: absolute; |
|||
width: 100%; |
|||
height: 100%; |
|||
|
|||
background-color: rgba(255, 255, 255, .8); |
|||
text-align: center; |
|||
font-weight: bold; |
|||
font-size: 1.4rem; |
|||
padding: 10px; |
|||
|
|||
display: flex; |
|||
flex-flow: column nowrap; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.validation-success { |
|||
color: green; |
|||
} |
|||
|
|||
.validation-failure { |
|||
color: red; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="app"> |
|||
<qrcode-stream :camera="camera" @decode="onDecode" @init="onInit" :track="paintBoundingBox"> |
|||
<div v-if="validationPending" class="validation-pending"> |
|||
Long validation in progress... |
|||
</div> |
|||
</qrcode-stream> |
|||
</div> |
|||
<script src="/static/lib/layui-v2.6.3/layui.js" charset="utf-8"></script> |
|||
<script> |
|||
Vue.use(httpVueLoader); |
|||
|
|||
var vue = new Vue({ |
|||
data() { |
|||
return { |
|||
isValid: undefined, |
|||
camera: 'auto', |
|||
result: '', |
|||
error: '', |
|||
materialList: [], |
|||
depository: null, |
|||
place: null |
|||
|
|||
} |
|||
}, |
|||
computed: { |
|||
validationPending() { |
|||
return this.isValid === undefined |
|||
&& this.camera === 'off' |
|||
}, |
|||
}, |
|||
methods: { |
|||
onDecode(result) { |
|||
let params = {}; // 用于暂存扫描结果 |
|||
this.result = result; |
|||
let parse = JSON.parse(result); |
|||
vue.turnCameraOff(); // 暂停扫描 |
|||
if (parse.did !== undefined) { |
|||
// 如果扫描的是仓库二维码 |
|||
this.depository = parse;// 将扫描结果保存到vue中 |
|||
params.depository = this.depository; |
|||
params.place = this.place; |
|||
params.materialList = this.materialList; |
|||
if (this.materialList.length > 0) { |
|||
// 如果有物料 |
|||
this.temporaryScanValue(params); // 将数据暂存至redis中 |
|||
this.chooseInOrOut(); // 弹出选择框 |
|||
} else { |
|||
// 如果没有 |
|||
layer.confirm("暂未选择物料,是否继续扫描", { |
|||
btn:["继续","取消"] |
|||
},function () { // 继续 |
|||
vue.turnCameraOn(); // 继续扫描 |
|||
layer.close(layer.index); // 关闭弹窗 |
|||
},function () { // 取消 |
|||
vue.temporaryScanValue(params); // 将数据暂存 |
|||
vue.chooseInOrOut(); // 弹出选择框 |
|||
}) |
|||
} |
|||
} |
|||
else if (parse.pid !== undefined) { |
|||
// 如果扫描的是库位二维码 |
|||
this.place = parse; // 将扫描结果保存到vue中 |
|||
params.depository = this.depository; |
|||
params.place = this.place; |
|||
params.materialList = this.materialList; |
|||
if (this.materialList.length > 0) { |
|||
// 如果有物料 |
|||
this.temporaryScanValue(params); // 将数据暂存至redis中 |
|||
this.chooseInOrOut(); // 弹出选择框 |
|||
} else { |
|||
// 如果没有 |
|||
layer.confirm("当前并未扫描物料,是否继续扫描", |
|||
{btn:["继续","取消"]}, |
|||
function () { // 继续扫描 |
|||
vue.turnCameraOn(); // 继续扫描 |
|||
layer.close(layer.index); // 关闭弹窗 |
|||
}, |
|||
function () { |
|||
vue.temporaryScanValue(params); // 将数据暂存 |
|||
vue.chooseInOrOut(); // 弹出选择框 |
|||
} |
|||
) |
|||
} |
|||
} |
|||
else if (parse.mid !== undefined) { |
|||
// 如果扫描的是物料二维码 |
|||
this.materialList.push(parse); |
|||
layer.confirm("是否继续扫描", |
|||
{ |
|||
btn: ["继续", "取消"] |
|||
}, |
|||
function () { // 继续扫描物料 |
|||
vue.turnCameraOn(); // 继续扫描 |
|||
layer.close(layer.index); // 关闭弹窗 |
|||
}, |
|||
function () { |
|||
// 不扫描物料 |
|||
params.materialList = vue.materialList; |
|||
params.depository = vue.depository; |
|||
params.place = vue.place; |
|||
vue.temporaryScanValue(params); // 将物料暂存 |
|||
if (vue.depository !== '' || vue.place !== '') { |
|||
// 如果已经扫描了仓库或库位 |
|||
// 弹出选择框 |
|||
vue.chooseInOrOut(); |
|||
} |
|||
else { |
|||
// 如果没有扫描仓库或库位 |
|||
layer.confirm("暂未扫描仓库,是否继续该操作", |
|||
{ |
|||
btn: ["继续", "取消"] |
|||
}, |
|||
function () {// 继续 |
|||
// 弹出选择框 |
|||
vue.chooseInOrOut(); |
|||
}, |
|||
function () { // 取消当前操作 |
|||
vue.turnCameraOn(); // 继续扫描 |
|||
layer.close(layer.index); // 关闭弹窗 |
|||
} |
|||
) |
|||
} |
|||
} |
|||
) |
|||
} |
|||
}, |
|||
/*layer.confirm("请选择入库|出库",{ |
|||
btn:["入库","出库"]}, |
|||
function () { |
|||
layer.open({ |
|||
type: 2, |
|||
title: '入库', |
|||
skin: 'layui-layer-rim', |
|||
maxmin: true, |
|||
shadeClose: true, //点击遮罩关闭层 |
|||
area: ['100%', '100%'], |
|||
move : '.layui-layer-title', |
|||
fixed:false, |
|||
content: '/application_in_back?mid='+material.mid, |
|||
}) |
|||
},function () { |
|||
let req = {}; |
|||
req.code = material.code; |
|||
req.type = "out"; |
|||
layui.$.ajax({ |
|||
url: "/material/findMatrialByCode", |
|||
type: "get", |
|||
dataType: 'json', |
|||
data:(req), |
|||
contentType: "application/json;charset=utf-8", |
|||
success: function (d) { |
|||
var d = d.data; |
|||
if(d !== null){ |
|||
if(d == null) { |
|||
layer.msg("没有该编码,请确认是否输入正确"); |
|||
}else{ |
|||
layer.open({ |
|||
type: 2, |
|||
title: '出库', |
|||
skin: 'layui-layer-rim', |
|||
maxmin: true, |
|||
shadeClose: true, //点击遮罩关闭层 |
|||
area: ['100%', '100%'], |
|||
move : '.layui-layer-title', |
|||
fixed:false, |
|||
content: '/application_out_back?code='+d.code+"&depositoryCode="+d.depositoryCode, |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
)*/ |
|||
async onInit(promise) { |
|||
try { |
|||
await promise.then(this.resetValidationState) |
|||
} catch (error) { |
|||
if (error.name === 'NotAllowedError') { |
|||
this.error = "ERROR: you need to grant camera access permission" |
|||
} else if (error.name === 'NotFoundError') { |
|||
this.error = "ERROR: no camera on this device" |
|||
} else if (error.name === 'NotSupportedError') { |
|||
this.error = "ERROR: secure context required (HTTPS, localhost)" |
|||
} else if (error.name === 'NotReadableError') { |
|||
this.error = "ERROR: is the camera already in use?" |
|||
} else if (error.name === 'OverconstrainedError') { |
|||
this.error = "ERROR: installed cameras are not suitable" |
|||
} else if (error.name === 'StreamApiNotSupportedError') { |
|||
this.error = "ERROR: Stream API is not supported in this browser" |
|||
} else if (error.name === 'InsecureContextError') { |
|||
this.error = 'ERROR: Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.'; |
|||
} else { |
|||
this.error = `ERROR: Camera error (${error.name})` |
|||
} |
|||
console.log(this.error) |
|||
} |
|||
} |
|||
, |
|||
resetValidationState() { |
|||
this.isValid = undefined |
|||
} |
|||
, |
|||
// 绘制二维码跟踪框 |
|||
paintBoundingBox(detectedCodes, ctx) { |
|||
for (const detectedCode of detectedCodes) { |
|||
const {boundingBox: {x, y, width, height}} = detectedCode; |
|||
ctx.lineWidth = 2; |
|||
ctx.strokeStyle = '#007bff'; |
|||
ctx.strokeRect(x, y, width, height) |
|||
} |
|||
} |
|||
, |
|||
// 打开相机 |
|||
turnCameraOn() { |
|||
this.camera = 'auto' |
|||
} |
|||
, |
|||
// 关闭相机 |
|||
turnCameraOff() { |
|||
this.camera = 'off' |
|||
}, |
|||
// 将扫描到的数据暂存到redis |
|||
temporaryScanValue(params) { |
|||
layui.$.ajax({ |
|||
url: "/material/temporaryValue", |
|||
type: 'post', |
|||
dataType: 'json', |
|||
contentType: "application/json;charset=utf-8", |
|||
data: JSON.stringify(params) |
|||
}); |
|||
}, |
|||
// 弹出入库|出库选择框 |
|||
chooseInOrOut(){ |
|||
layer.confirm("请选择入库|出库", { |
|||
btn: ["入库"] |
|||
}, |
|||
function () { // 选择入库 |
|||
layer.open({ |
|||
type: 2, |
|||
title: '入库', |
|||
skin: 'layui-layer-rim', |
|||
maxmin: true, |
|||
shadeClose: true, //点击遮罩关闭层 |
|||
area: ['100%', '100%'], |
|||
move: '.layui-layer-title', |
|||
fixed: false, |
|||
content: '/application_in_scanQrCode', |
|||
end:function () { |
|||
var index = parent.layer.getFrameIndex(window.name); |
|||
parent.layer.close(index); |
|||
} |
|||
}) |
|||
}, |
|||
function () { // 选择出库 |
|||
layui.$.ajax({ // 判断当前物料是否存在库存 |
|||
url:"/material/IsMaterialExist", |
|||
type:"get", |
|||
dataType: 'json', |
|||
contentType: "application/json;charset=utf-8", |
|||
success:function (d) { |
|||
layer.open({ |
|||
type: 2, |
|||
title: '出库', |
|||
skin: 'layui-layer-rim', |
|||
maxmin: true, |
|||
shadeClose: true, //点击遮罩关闭层 |
|||
area: ['100%', '100%'], |
|||
move: '.layui-layer-title', |
|||
fixed: false, |
|||
content: '/application_Out_scanQrCode', |
|||
end:function () { |
|||
var index = parent.layer.getFrameIndex(window.name); |
|||
parent.layer.close(index); |
|||
} |
|||
}) |
|||
} |
|||
}); |
|||
|
|||
}) |
|||
} |
|||
} |
|||
}).$mount('#app') |
|||
</script> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,150 @@ |
|||
|
|||
<!doctype html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
<meta name="author" content="ZXing for JS"> |
|||
|
|||
<title>ZXing TypeScript | Decoding from camera stream</title> |
|||
|
|||
<!-- <link rel="stylesheet" rel="preload" as="style" onload="this.rel='stylesheet';this.onload=null" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">--> |
|||
<!-- <link rel="stylesheet" rel="preload" as="style" onload="this.rel='stylesheet';this.onload=null" href="https://unpkg.com/normalize.css@8.0.0/normalize.css">--> |
|||
<!-- <link rel="stylesheet" rel="preload" as="style" onload="this.rel='stylesheet';this.onload=null" href="https://unpkg.com/milligram@1.3.0/dist/milligram.min.css">--> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<main class="wrapper" style="padding-top:2em"> |
|||
|
|||
<section class="container" id="demo-content"> |
|||
<div> |
|||
<a class="button" id="startButton">扫描</a> |
|||
<a class="button" id="resetButton">重置</a> |
|||
</div> |
|||
|
|||
<div> |
|||
<video id="video" width="300" height="200" style="border: 1px solid gray"></video> |
|||
</div> |
|||
|
|||
<div id="sourceSelectPanel" style="display:none"> |
|||
<label for="sourceSelect">选择摄像头: </label> |
|||
<select id="sourceSelect" style="max-width:400px"> |
|||
</select> |
|||
</div> |
|||
|
|||
<div style="display: table"> |
|||
<label for="decoding-style"> 扫描次数:</label> |
|||
<select id="decoding-style" size="1"> |
|||
<option value="once">扫描一次</option> |
|||
<option value="continuously">持续扫描</option> |
|||
</select> |
|||
</div> |
|||
|
|||
<label>结果:</label> |
|||
<pre><code id="result"></code></pre> |
|||
</section> |
|||
|
|||
</main> |
|||
|
|||
<script type="text/javascript" src="https://unpkg.com/@zxing/library@latest"></script> |
|||
<script type="text/javascript"> |
|||
function decodeOnce(codeReader, selectedDeviceId) { |
|||
codeReader.decodeFromInputVideoDevice(selectedDeviceId, 'video').then((result) => { |
|||
console.log(result); |
|||
document.getElementById('result').textContent = result.text |
|||
}).catch((err) => { |
|||
console.error(err); |
|||
document.getElementById('result').textContent = err |
|||
}) |
|||
} |
|||
|
|||
function decodeContinuously(codeReader, selectedDeviceId) { |
|||
codeReader.decodeFromInputVideoDeviceContinuously(selectedDeviceId, 'video', (result, err) => { |
|||
if (result) { |
|||
// properly decoded qr code |
|||
console.log('Found QR code!', result); |
|||
document.getElementById('result').textContent = result.text |
|||
} |
|||
|
|||
if (err) { |
|||
if (err instanceof ZXing.NotFoundException) { |
|||
console.log('No QR code found.') |
|||
} |
|||
|
|||
if (err instanceof ZXing.ChecksumException) { |
|||
console.log('A code was found, but it\'s read value was not valid.') |
|||
} |
|||
|
|||
if (err instanceof ZXing.FormatException) { |
|||
console.log('A code was found, but it was in a invalid format.') |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
|
|||
window.addEventListener('load', function () { |
|||
let selectedDeviceId; |
|||
const codeReader = new ZXing.BrowserQRCodeReader(); |
|||
console.log('ZXing 初始化'); |
|||
|
|||
codeReader.getVideoInputDevices() |
|||
.then((videoInputDevices) => { |
|||
const sourceSelect = document.getElementById('sourceSelect') |
|||
// selectedDeviceId = videoInputDevices[0].deviceId; |
|||
if (videoInputDevices.length >= 1) { |
|||
videoInputDevices.forEach((element) => { |
|||
const sourceOption = document.createElement('option'); |
|||
console.log(element.label); |
|||
if(element.label.includes("front")){ |
|||
sourceOption.text = "前置摄像头"; |
|||
sourceOption.value = element.deviceId; |
|||
}else if (element.label.includes("back")){ |
|||
sourceOption.text = "后置摄像头"; |
|||
sourceOption.selected=true; |
|||
sourceOption.value = element.deviceId; |
|||
selectedDeviceId = element.deviceId; |
|||
} |
|||
sourceSelect.appendChild(sourceOption) |
|||
}); |
|||
|
|||
sourceSelect.onchange = () => { |
|||
selectedDeviceId = sourceSelect.value; |
|||
}; |
|||
|
|||
const sourceSelectPanel = document.getElementById('sourceSelectPanel') |
|||
sourceSelectPanel.style.display = 'block' |
|||
} |
|||
|
|||
document.getElementById('startButton').addEventListener('click', () => { |
|||
|
|||
const decodingStyle = document.getElementById('decoding-style').value; |
|||
|
|||
if (decodingStyle === "once") { |
|||
// 如果只扫描一次 |
|||
decodeOnce(codeReader, selectedDeviceId); |
|||
} else { |
|||
// 扫描多次 |
|||
decodeContinuously(codeReader, selectedDeviceId); |
|||
} |
|||
|
|||
console.log(`Started decode from camera with id ${selectedDeviceId}`) |
|||
}); |
|||
|
|||
document.getElementById('resetButton').addEventListener('click', () => { |
|||
codeReader.reset(); |
|||
document.getElementById('result').textContent = ''; |
|||
console.log('Reset.') |
|||
}) |
|||
|
|||
}) |
|||
.catch((err) => { |
|||
console.error(err) |
|||
}) |
|||
}) |
|||
</script> |
|||
|
|||
</body> |
|||
|
|||
</html> |
|||
|
After Width: | Height: | Size: 625 B |
|
After Width: | Height: | Size: 3.7 KiB |
@ -0,0 +1 @@ |
|||
(function(e,t){function n(e,t){var n=e.createElement("p"),i=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x<style>"+t+"</style>",i.insertBefore(n.lastChild,i.firstChild)}function i(){var e=m.elements;return"string"==typeof e?e.split(" "):e}function r(e){var t={},n=e.createElement,r=e.createDocumentFragment,o=r();e.createElement=function(e){m.shivMethods||n(e);var i;return i=t[e]?t[e].cloneNode():g.test(e)?(t[e]=n(e)).cloneNode():n(e),i.canHaveChildren&&!f.test(e)?o.appendChild(i):i},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+i().join().replace(/\w+/g,function(e){return t[e]=n(e),o.createElement(e),'c("'+e+'")'})+");return n}")(m,o)}function o(e){var t;return e.documentShived?e:(m.shivCSS&&!d&&(t=!!n(e,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")),h||(t=!r(e)),t&&(e.documentShived=t),e)}function a(e){for(var t,n=e.getElementsByTagName("*"),r=n.length,o=RegExp("^(?:"+i().join("|")+")$","i"),a=[];r--;)t=n[r],o.test(t.nodeName)&&a.push(t.applyElement(s(t)));return a}function s(e){for(var t,n=e.attributes,i=n.length,r=e.ownerDocument.createElement(b+":"+e.nodeName);i--;)t=n[i],t.specified&&r.setAttribute(t.nodeName,t.nodeValue);return r.style.cssText=e.style.cssText,r}function l(e){for(var t,n=e.split("{"),r=n.length,o=RegExp("(^|[\\s,>+~])("+i().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),a="$1"+b+"\\:$2";r--;)t=n[r]=n[r].split("}"),t[t.length-1]=t[t.length-1].replace(o,a),n[r]=t.join("}");return n.join("{")}function c(e){for(var t=e.length;t--;)e[t].removeNode()}function u(e){var t,i,r=e.namespaces,o=e.parentWindow;return!y||e.printShived?e:(r[b]===void 0&&r.add(b),o.attachEvent("onbeforeprint",function(){for(var r,o,s,c=e.styleSheets,u=[],d=c.length,h=Array(d);d--;)h[d]=c[d];for(;s=h.pop();)if(!s.disabled&&v.test(s.media)){for(r=s.imports,d=0,o=r.length;o>d;d++)h.push(r[d]);try{u.push(s.cssText)}catch(p){}}u=l(u.reverse().join("")),i=a(e),t=n(e,u)}),o.attachEvent("onafterprint",function(){c(i),t.removeNode(!0)}),e.printShived=!0,e)}var d,h,p=e.html5||{},f=/^<|^(?:button|form|map|select|textarea|object|iframe)$/i,g=/^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i;(function(){var n=t.createElement("a");n.innerHTML="<xyz></xyz>",d="hidden"in n,d&&"function"==typeof injectElementWithStyles&&injectElementWithStyles("#modernizr{}",function(t){t.hidden=!0,d="none"==(e.getComputedStyle?getComputedStyle(t,null):t.currentStyle).display}),h=1==n.childNodes.length||function(){try{t.createElement("a")}catch(e){return!0}var n=t.createDocumentFragment();return n.cloneNode===void 0||n.createDocumentFragment===void 0||n.createElement===void 0}()})();var m={elements:p.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:p.shivCSS!==!1,shivMethods:p.shivMethods!==!1,type:"default",shivDocument:o};e.html5=m,o(t);var v=/^$|\b(?:all|print)\b/,b="html5shiv",y=!h&&function(){var n=t.documentElement;return t.namespaces!==void 0&&t.parentWindow!==void 0&&n.applyElement!==void 0&&n.removeNode!==void 0&&e.attachEvent!==void 0}();m.type+=" print",m.shivPrint=u,u(t)})(this,document); |
|||
@ -0,0 +1,5 @@ |
|||
/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl |
|||
* Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
|
|||
* */ |
|||
|
|||
!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b<s.length;b++){var c=s[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!o[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(v(c.styleSheet.rawCssText,e,f),o[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!r||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}w()};x(),c.update=x,c.getEmValue=t,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this); |
|||
@ -0,0 +1,84 @@ |
|||
(function(){var E;var g=window,n=document,p=function(a){var b=g._gaUserPrefs;if(b&&b.ioo&&b.ioo()||a&&!0===g["ga-disable-"+a])return!0;try{var c=g.external;if(c&&c._gaUserPrefs&&"oo"==c._gaUserPrefs)return!0}catch(f){}a=[];b=n.cookie.split(";");c=/^\s*AMP_TOKEN=\s*(.*?)\s*$/;for(var d=0;d<b.length;d++){var e=b[d].match(c);e&&a.push(e[1])}for(b=0;b<a.length;b++)if("$OPT_OUT"==decodeURIComponent(a[b]))return!0;return!1};var q=function(a){return encodeURIComponent?encodeURIComponent(a).replace(/\(/g,"%28").replace(/\)/g,"%29"):a},r=/^(www\.)?google(\.com?)?(\.[a-z]{2})?$/,u=/(^|\.)doubleclick\.net$/i;function Aa(a,b){switch(b){case 0:return""+a;case 1:return 1*a;case 2:return!!a;case 3:return 1E3*a}return a}function Ba(a){return"function"==typeof a}function Ca(a){return void 0!=a&&-1<(a.constructor+"").indexOf("String")}function F(a,b){return void 0==a||"-"==a&&!b||""==a}function Da(a){if(!a||""==a)return"";for(;a&&-1<" \n\r\t".indexOf(a.charAt(0));)a=a.substring(1);for(;a&&-1<" \n\r\t".indexOf(a.charAt(a.length-1));)a=a.substring(0,a.length-1);return a} |
|||
function Ea(){return Math.round(2147483647*Math.random())}function Fa(){}function G(a,b){if(encodeURIComponent instanceof Function)return b?encodeURI(a):encodeURIComponent(a);H(68);return escape(a)}function I(a){a=a.split("+").join(" ");if(decodeURIComponent instanceof Function)try{return decodeURIComponent(a)}catch(b){H(17)}else H(68);return unescape(a)}var Ga=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,!!d):a.attachEvent&&a.attachEvent("on"+b,c)}; |
|||
function Ia(a,b){if(a){var c=J.createElement("script");c.type="text/javascript";c.async=!0;c.src=a;c.id=b;a=J.getElementsByTagName("script")[0];a.parentNode.insertBefore(c,a);return c}}function K(a){return a&&0<a.length?a[0]:""}function L(a){var b=a?a.length:0;return 0<b?a[b-1]:""}var nf=function(){this.prefix="ga.";this.values={}};nf.prototype.set=function(a,b){this.values[this.prefix+a]=b};nf.prototype.get=function(a){return this.values[this.prefix+a]}; |
|||
nf.prototype.contains=function(a){return void 0!==this.get(a)};function Ka(a){0==a.indexOf("www.")&&(a=a.substring(4));return a.toLowerCase()} |
|||
function La(a,b){var c={url:a,protocol:"http",host:"",path:"",R:new nf,anchor:""};if(!a)return c;var d=a.indexOf("://");0<=d&&(c.protocol=a.substring(0,d),a=a.substring(d+3));d=a.search("/|\\?|#");if(0<=d)c.host=a.substring(0,d).toLowerCase(),a=a.substring(d);else return c.host=a.toLowerCase(),c;d=a.indexOf("#");0<=d&&(c.anchor=a.substring(d+1),a=a.substring(0,d));d=a.indexOf("?");0<=d&&(Na(c.R,a.substring(d+1)),a=a.substring(0,d));c.anchor&&b&&Na(c.R,c.anchor);a&&"/"==a.charAt(0)&&(a=a.substring(1)); |
|||
c.path=a;return c} |
|||
function Oa(a,b){function c(a){var b=(a.hostname||"").split(":")[0].toLowerCase(),c=(a.protocol||"").toLowerCase();c=1*a.port||("http:"==c?80:"https:"==c?443:"");a=a.pathname||"";0==a.indexOf("/")||(a="/"+a);return[b,""+c,a]}b=b||J.createElement("a");b.href=J.location.href;var d=(b.protocol||"").toLowerCase(),e=c(b),f=b.search||"",Be=d+"//"+e[0]+(e[1]?":"+e[1]:"");0==a.indexOf("//")?a=d+a:0==a.indexOf("/")?a=Be+a:a&&0!=a.indexOf("?")?0>a.split("/")[0].indexOf(":")&&(a=Be+e[2].substring(0,e[2].lastIndexOf("/"))+ |
|||
"/"+a):a=Be+e[2]+(a||f);b.href=a;d=c(b);return{protocol:(b.protocol||"").toLowerCase(),host:d[0],port:d[1],path:d[2],query:b.search||"",url:a||""}}function Na(a,b){function c(b,c){a.contains(b)||a.set(b,[]);a.get(b).push(c)}b=Da(b).split("&");for(var d=0;d<b.length;d++)if(b[d]){var e=b[d].indexOf("=");0>e?c(b[d],"1"):c(b[d].substring(0,e),b[d].substring(e+1))}} |
|||
function Pa(a,b){return F(a)||"["==a.charAt(0)&&"]"==a.charAt(a.length-1)?"-":a.indexOf(J.domain+(b&&"/"!=b?b:""))==(0==a.indexOf("http://")?7:0==a.indexOf("https://")?8:0)?"0":a};var Qa=0;function Ra(a,b,c){1<=Qa||1<=100*Math.random()||ld()||(a=["utmt=error","utmerr="+a,"utmwv=5.7.2","utmn="+Ea(),"utmsp=1"],b&&a.push("api="+b),c&&a.push("msg="+G(c.substring(0,100))),M.w&&a.push("aip=1"),Sa(a.join("&")),Qa++)};var Ta=0,Ua={};function N(a){return Va("x"+Ta++,a)}function Va(a,b){Ua[a]=!!b;return a} |
|||
var Wa=N(),Xa=Va("anonymizeIp"),Ya=N(),$a=N(),ab=N(),bb=N(),O=N(),P=N(),cb=N(),db=N(),eb=N(),fb=N(),gb=N(),hb=N(),ib=N(),jb=N(),kb=N(),lb=N(),nb=N(),ob=N(),pb=N(),qb=N(),rb=N(),sb=N(),tb=N(),ub=N(),vb=N(),wb=N(),xb=N(),yb=N(),zb=N(),Ab=N(),Bb=N(),Cb=N(),Db=N(),Eb=N(),Fb=N(!0),Gb=Va("currencyCode"),v=Va("storeGac"),Hb=Va("page"),Ib=Va("title"),Jb=N(),Kb=N(),Lb=N(),Mb=N(),Nb=N(),Ob=N(),Pb=N(),Qb=N(),Rb=N(),Q=N(!0),Sb=N(!0),Tb=N(!0),Ub=N(!0),Vb=N(!0),Wb=N(!0),Zb=N(!0),$b=N(!0),ac=N(!0),bc=N(!0),cc=N(!0), |
|||
R=N(!0),dc=N(!0),ec=N(!0),fc=N(!0),gc=N(!0),hc=N(!0),ic=N(!0),jc=N(!0),S=N(!0),kc=N(!0),lc=N(!0),mc=N(!0),nc=N(!0),oc=N(!0),pc=N(!0),qc=N(!0),rc=Va("campaignParams"),sc=N(),tc=Va("hitCallback"),uc=N();N();var vc=N(),wc=N(),xc=N(),yc=N(),zc=N(),Ac=N(),Bc=N(),Cc=N(),Dc=N(),Ec=N(),Fc=N(),Gc=N(),Hc=N(),Ic=N();N(); |
|||
var Mc=N(),Nc=N(),Yb=N(),Jc=N(),Kc=N(),Lc=Va("utmtCookieName"),Cd=Va("displayFeatures"),Oc=N(),of=Va("gtmid"),Oe=Va("uaName"),Pe=Va("uaDomain"),Qe=Va("uaPath"),pf=Va("linkid"),w=N(),x=N(),y=N(),z=N();var Re=function(){function a(a,c,d){T(qf.prototype,a,c,d)}a("_createTracker",qf.prototype.hb,55);a("_getTracker",qf.prototype.oa,0);a("_getTrackerByName",qf.prototype.u,51);a("_getTrackers",qf.prototype.pa,130);a("_anonymizeIp",qf.prototype.aa,16);a("_forceSSL",qf.prototype.la,125);a("_getPlugin",Pc,120)},Se=function(){function a(a,c,d){T(U.prototype,a,c,d)}Qc("_getName",$a,58);Qc("_getAccount",Wa,64);Qc("_visitCode",Q,54);Qc("_getClientInfo",ib,53,1);Qc("_getDetectTitle",lb,56,1);Qc("_getDetectFlash", |
|||
jb,65,1);Qc("_getLocalGifPath",wb,57);Qc("_getServiceMode",xb,59);V("_setClientInfo",ib,66,2);V("_setAccount",Wa,3);V("_setNamespace",Ya,48);V("_setAllowLinker",fb,11,2);V("_setDetectFlash",jb,61,2);V("_setDetectTitle",lb,62,2);V("_setLocalGifPath",wb,46,0);V("_setLocalServerMode",xb,92,void 0,0);V("_setRemoteServerMode",xb,63,void 0,1);V("_setLocalRemoteServerMode",xb,47,void 0,2);V("_setSampleRate",vb,45,1);V("_setCampaignTrack",kb,36,2);V("_setAllowAnchor",gb,7,2);V("_setCampNameKey",ob,41);V("_setCampContentKey", |
|||
tb,38);V("_setCampIdKey",nb,39);V("_setCampMediumKey",rb,40);V("_setCampNOKey",ub,42);V("_setCampSourceKey",qb,43);V("_setCampTermKey",sb,44);V("_setCampCIdKey",pb,37);V("_setCookiePath",P,9,0);V("_setMaxCustomVariables",yb,0,1);V("_setVisitorCookieTimeout",cb,28,1);V("_setSessionCookieTimeout",db,26,1);V("_setCampaignCookieTimeout",eb,29,1);V("_setReferrerOverride",Jb,49);V("_setSiteSpeedSampleRate",Dc,132);V("_storeGac",v,143);a("_trackPageview",U.prototype.Fa,1);a("_trackEvent",U.prototype.F,4); |
|||
a("_trackPageLoadTime",U.prototype.Ea,100);a("_trackSocial",U.prototype.Ga,104);a("_trackTrans",U.prototype.Ia,18);a("_sendXEvent",U.prototype.ib,78);a("_createEventTracker",U.prototype.ia,74);a("_getVersion",U.prototype.qa,60);a("_setDomainName",U.prototype.B,6);a("_setAllowHash",U.prototype.va,8);a("_getLinkerUrl",U.prototype.na,52);a("_link",U.prototype.link,101);a("_linkByPost",U.prototype.ua,102);a("_setTrans",U.prototype.za,20);a("_addTrans",U.prototype.$,21);a("_addItem",U.prototype.Y,19); |
|||
a("_clearTrans",U.prototype.ea,105);a("_setTransactionDelim",U.prototype.Aa,82);a("_setCustomVar",U.prototype.wa,10);a("_deleteCustomVar",U.prototype.ka,35);a("_getVisitorCustomVar",U.prototype.ra,50);a("_setXKey",U.prototype.Ca,83);a("_setXValue",U.prototype.Da,84);a("_getXKey",U.prototype.sa,76);a("_getXValue",U.prototype.ta,77);a("_clearXKey",U.prototype.fa,72);a("_clearXValue",U.prototype.ga,73);a("_createXObj",U.prototype.ja,75);a("_addIgnoredOrganic",U.prototype.W,15);a("_clearIgnoredOrganic", |
|||
U.prototype.ba,97);a("_addIgnoredRef",U.prototype.X,31);a("_clearIgnoredRef",U.prototype.ca,32);a("_addOrganic",U.prototype.Z,14);a("_clearOrganic",U.prototype.da,70);a("_cookiePathCopy",U.prototype.ha,30);a("_get",U.prototype.ma,106);a("_set",U.prototype.xa,107);a("_addEventListener",U.prototype.addEventListener,108);a("_removeEventListener",U.prototype.removeEventListener,109);a("_addDevId",U.prototype.V);a("_getPlugin",Pc,122);a("_setPageGroup",U.prototype.ya,126);a("_trackTiming",U.prototype.Ha, |
|||
124);a("_initData",U.prototype.initData,2);a("_setVar",U.prototype.Ba,22);V("_setSessionTimeout",db,27,3);V("_setCookieTimeout",eb,25,3);V("_setCookiePersistence",cb,24,1);a("_setAutoTrackOutbound",Fa,79);a("_setTrackOutboundSubdomains",Fa,81);a("_setHrefExamineLimit",Fa,80)};function Pc(a){var b=this.plugins_;if(b)return b.get(a)} |
|||
var T=function(a,b,c,d){a[b]=function(){try{return void 0!=d&&H(d),c.apply(this,arguments)}catch(e){throw Ra("exc",b,e&&e.name),e;}}},Qc=function(a,b,c,d){U.prototype[a]=function(){try{return H(c),Aa(this.a.get(b),d)}catch(e){throw Ra("exc",a,e&&e.name),e;}}},V=function(a,b,c,d,e){U.prototype[a]=function(f){try{H(c),void 0==e?this.a.set(b,Aa(f,d)):this.a.set(b,e)}catch(Be){throw Ra("exc",a,Be&&Be.name),Be;}}},Te=function(a,b){return{type:b,target:a,stopPropagation:function(){throw"aborted";}}};var Rc=new RegExp(/(^|\.)doubleclick\.net$/i),Sc=function(a,b){return Rc.test(J.location.hostname)?!0:"/"!==b?!1:0!=a.indexOf("www.google.")&&0!=a.indexOf(".google.")&&0!=a.indexOf("google.")||-1<a.indexOf("google.org")?!1:!0},Tc=function(a){var b=a.get(bb),c=a.c(P,"/");Sc(b,c)&&a.stopPropagation()};var Zc=function(){var a={},b={},c=new Uc;this.g=function(a,b){c.add(a,b)};var d=new Uc;this.v=function(a,b){d.add(a,b)};var e=!1,f=!1,Be=!0;this.T=function(){e=!0};this.j=function(a){this.load();this.set(sc,a,!0);a=new Vc(this);e=!1;d.cb(this);e=!0;b={};this.store();a.Ja()};this.load=function(){e&&(e=!1,this.Ka(),Wc(this),f||(f=!0,c.cb(this),Xc(this),Wc(this)),e=!0)};this.store=function(){e&&(f?(e=!1,Xc(this),e=!0):this.load())};this.get=function(c){Ua[c]&&this.load();return void 0!==b[c]?b[c]:a[c]}; |
|||
this.set=function(c,d,e){Ua[c]&&this.load();e?b[c]=d:a[c]=d;Ua[c]&&this.store()};this.Za=function(b){a[b]=this.b(b,0)+1};this.b=function(a,b){a=this.get(a);return void 0==a||""===a?b:1*a};this.c=function(a,b){a=this.get(a);return void 0==a?b:a+""};this.Ka=function(){if(Be){var b=this.c(bb,""),c=this.c(P,"/");Sc(b,c)||(a[O]=a[hb]&&""!=b?Yc(b):1,Be=!1)}}};Zc.prototype.stopPropagation=function(){throw"aborted";}; |
|||
var Vc=function(a){var b=this;this.fb=0;var c=a.get(tc);this.Ua=function(){0<b.fb&&c&&(b.fb--,b.fb||c())};this.Ja=function(){!b.fb&&c&&setTimeout(c,10)};a.set(uc,b,!0)};function $c(a,b){b=b||[];for(var c=0;c<b.length;c++){var d=b[c];if(""+a==d||0==d.indexOf(a+"."))return d}return"-"} |
|||
var bd=function(a,b,c){c=c?"":a.c(O,"1");b=b.split(".");if(6!==b.length||ad(b[0],c))return!1;c=1*b[1];var d=1*b[2],e=1*b[3],f=1*b[4];b=1*b[5];if(!(0<=c&&0<d&&0<e&&0<f&&0<=b))return!1;a.set(Q,c);a.set(Vb,d);a.set(Wb,e);a.set(Zb,f);a.set($b,b);return!0},cd=function(a){var b=a.get(Q),c=a.get(Vb),d=a.get(Wb),e=a.get(Zb),f=a.b($b,1);return[a.b(O,1),void 0!=b?b:"-",c||"-",d||"-",e||"-",f].join(".")},dd=function(a){return[a.b(O,1),a.b(cc,0),a.b(R,1),a.b(dc,0)].join(".")},ed=function(a,b,c){c=c?"":a.c(O, |
|||
"1");var d=b.split(".");if(4!==d.length||ad(d[0],c))d=null;a.set(cc,d?1*d[1]:0);a.set(R,d?1*d[2]:10);a.set(dc,d?1*d[3]:a.get(ab));return null!=d||!ad(b,c)},fd=function(a,b){var c=G(a.c(Tb,"")),d=[],e=a.get(Fb);if(!b&&e){for(b=0;b<e.length;b++){var f=e[b];f&&1==f.scope&&d.push(b+"="+G(f.name)+"="+G(f.value)+"=1")}0<d.length&&(c+="|"+d.join("^"))}return c?a.b(O,1)+"."+c:null},gd=function(a,b,c){c=c?"":a.c(O,"1");b=b.split(".");if(2>b.length||ad(b[0],c))return!1;b=b.slice(1).join(".").split("|");0<b.length&& |
|||
a.set(Tb,I(b[0]));if(1>=b.length)return!0;b=b[1].split(-1==b[1].indexOf(",")?"^":",");for(c=0;c<b.length;c++){var d=b[c].split("=");if(4==d.length){var e={};e.name=I(d[1]);e.value=I(d[2]);e.scope=1;a.get(Fb)[d[0]]=e}}return!0},hd=function(a,b){return(b=Ue(a,b))?[a.b(O,1),a.b(ec,0),a.b(fc,1),a.b(gc,1),b].join("."):""},Ue=function(a){function b(b,e){F(a.get(b))||(b=a.c(b,""),b=b.split(" ").join("%20"),b=b.split("+").join("%20"),c.push(e+"="+b))}var c=[];b(ic,"utmcid");b(nc,"utmcsr");b(S,"utmgclid"); |
|||
b(kc,"utmgclsrc");b(lc,"utmdclid");b(mc,"utmdsid");b(jc,"utmccn");b(oc,"utmcmd");b(pc,"utmctr");b(qc,"utmcct");return c.join("|")},id=function(a,b,c){c=c?"":a.c(O,"1");b=b.split(".");if(5>b.length||ad(b[0],c))return a.set(ec,void 0),a.set(fc,void 0),a.set(gc,void 0),a.set(ic,void 0),a.set(jc,void 0),a.set(nc,void 0),a.set(oc,void 0),a.set(pc,void 0),a.set(qc,void 0),a.set(S,void 0),a.set(kc,void 0),a.set(lc,void 0),a.set(mc,void 0),!1;a.set(ec,1*b[1]);a.set(fc,1*b[2]);a.set(gc,1*b[3]);Ve(a,b.slice(4).join(".")); |
|||
return!0},Ve=function(a,b){function c(a){return(a=b.match(a+"=(.*?)(?:\\|utm|$)"))&&2==a.length?a[1]:void 0}function d(b,c){c?(c=e?I(c):c.split("%20").join(" "),a.set(b,c)):a.set(b,void 0)}-1==b.indexOf("=")&&(b=I(b));var e="2"==c("utmcvr");d(ic,c("utmcid"));d(jc,c("utmccn"));d(nc,c("utmcsr"));d(oc,c("utmcmd"));d(pc,c("utmctr"));d(qc,c("utmcct"));d(S,c("utmgclid"));d(kc,c("utmgclsrc"));d(lc,c("utmdclid"));d(mc,c("utmdsid"))},ad=function(a,b){return b?a!=b:!/^\d+$/.test(a)};var Uc=function(){this.filters=[]};Uc.prototype.add=function(a,b){this.filters.push({name:a,s:b})};Uc.prototype.cb=function(a){try{for(var b=0;b<this.filters.length;b++)this.filters[b].s.call(W,a)}catch(c){}};function jd(a){100!=a.get(vb)&&a.get(Q)%1E4>=100*a.get(vb)&&a.stopPropagation()}function kd(a){ld(a.get(Wa))&&a.stopPropagation()}function md(a){"file:"==J.location.protocol&&a.stopPropagation()}function Ge(a){He()&&a.stopPropagation()} |
|||
function nd(a){a.get(Ib)||a.set(Ib,J.title,!0);a.get(Hb)||a.set(Hb,J.location.pathname+J.location.search,!0)}function lf(a){a.get(Wa)&&"UA-XXXXX-X"!=a.get(Wa)||a.stopPropagation()};var od=new function(){var a=[];this.set=function(b){a[b]=!0};this.encode=function(){for(var b=[],c=0;c<a.length;c++)a[c]&&(b[Math.floor(c/6)]^=1<<c%6);for(c=0;c<b.length;c++)b[c]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".charAt(b[c]||0);return b.join("")+"~"}};function H(a){od.set(a)};var W=window,J=document,ld=function(a){var b=W._gaUserPrefs;if(b&&b.ioo&&b.ioo()||a&&!0===W["ga-disable-"+a])return!0;try{var c=W.external;if(c&&c._gaUserPrefs&&"oo"==c._gaUserPrefs)return!0}catch(d){}return!1},He=function(){return W.navigator&&"preview"==W.navigator.loadPurpose},We=function(a,b){setTimeout(a,b)},pd=function(a){var b=[],c=J.cookie.split(";");a=new RegExp("^\\s*"+a+"=\\s*(.*?)\\s*$");for(var d=0;d<c.length;d++){var e=c[d].match(a);e&&b.push(e[1])}return b},X=function(a,b,c,d,e,f){e= |
|||
ld(e)?!1:Sc(d,c)?!1:He()?!1:!0;e&&((b=mf(b))&&2E3<b.length&&(b=b.substring(0,2E3),H(69)),a=a+"="+b+"; path="+c+"; ",f&&(a+="expires="+(new Date((new Date).getTime()+f)).toGMTString()+"; "),d&&(a+="domain="+d+";"),J.cookie=a)},mf=function(a){if(!a)return a;var b=a.indexOf(";");-1!=b&&(a=a.substring(0,b),H(141));if(!(0<=W.navigator.userAgent.indexOf("Firefox")))return a;a=a.replace(/\n|\r/g," ");b=0;for(var c=a.length;b<c;++b){var d=a.charCodeAt(b)&255;if(10==d||13==d)a=a.substring(0,b)+"?"+a.substring(b+ |
|||
1)}return a};var A,B=/^.*Version\/?(\d+)[^\d].*$/i;var qd,rd,sd=function(){if(!qd){var a={},b=W.navigator,c=W.screen;a.jb=c?c.width+"x"+c.height:"-";a.P=c?c.colorDepth+"-bit":"-";a.language=(b&&(b.language||b.browserLanguage)||"-").toLowerCase();a.javaEnabled=b&&b.javaEnabled()?1:0;a.characterSet=J.characterSet||J.charset||"-";try{var d=J.documentElement,e=J.body,f=e&&e.clientWidth&&e.clientHeight;b=[];d&&d.clientWidth&&d.clientHeight&&("CSS1Compat"===J.compatMode||!f)?b=[d.clientWidth,d.clientHeight]:f&&(b=[e.clientWidth,e.clientHeight]);var Be= |
|||
0>=b[0]||0>=b[1]?"":b.join("x");a.Wa=Be}catch(k){H(135)}qd=a}},td=function(){sd();var a=qd,b=W.navigator;a=b.appName+b.version+a.language+b.platform+b.userAgent+a.javaEnabled+a.jb+a.P+(J.cookie?J.cookie:"")+(J.referrer?J.referrer:"");b=a.length;for(var c=W.history.length;0<c;)a+=c--^b++;return Yc(a)},ud=function(a){sd();var b=qd;a.set(Lb,b.jb);a.set(Mb,b.P);a.set(Pb,b.language);a.set(Qb,b.characterSet);a.set(Nb,b.javaEnabled);a.set(Rb,b.Wa);if(a.get(ib)&&a.get(jb)){if(!(b=rd)){var c,d;var e="ShockwaveFlash"; |
|||
if((b=(b=W.navigator)?b.plugins:void 0)&&0<b.length)for(c=0;c<b.length&&!d;c++)e=b[c],-1<e.name.indexOf("Shockwave Flash")&&(d=e.description.split("Shockwave Flash ")[1]);else{e=e+"."+e;try{c=new ActiveXObject(e+".7"),d=c.GetVariable("$version")}catch(f){}if(!d)try{c=new ActiveXObject(e+".6"),d="WIN 6,0,21,0",c.AllowScriptAccess="always",d=c.GetVariable("$version")}catch(f){}if(!d)try{c=new ActiveXObject(e),d=c.GetVariable("$version")}catch(f){}d&&(d=d.split(" ")[1].split(","),d=d[0]+"."+d[1]+" r"+ |
|||
d[2])}b=d?d:"-"}rd=b;a.set(Ob,rd)}else a.set(Ob,"-")};var vd=function(a){if(Ba(a))this.s=a;else{var b=a[0],c=b.lastIndexOf(":"),d=b.lastIndexOf(".");this.h=this.i=this.l="";-1==c&&-1==d?this.h=b:-1==c&&-1!=d?(this.i=b.substring(0,d),this.h=b.substring(d+1)):-1!=c&&-1==d?(this.l=b.substring(0,c),this.h=b.substring(c+1)):c>d?(this.i=b.substring(0,d),this.l=b.substring(d+1,c),this.h=b.substring(c+1)):(this.i=b.substring(0,d),this.h=b.substring(d+1));this.Xa=a.slice(1);this.Ma=!this.l&&"_require"==this.h;this.J=!this.i&&!this.l&&"_provide"==this.h}},Y=function(){T(Y.prototype, |
|||
"push",Y.prototype.push,5);T(Y.prototype,"_getPlugin",Pc,121);T(Y.prototype,"_createAsyncTracker",Y.prototype.Sa,33);T(Y.prototype,"_getAsyncTracker",Y.prototype.Ta,34);this.I=new nf;this.eb=[]};E=Y.prototype;E.Na=function(a,b,c){var d=this.I.get(a);if(!Ba(d))return!1;b.plugins_=b.plugins_||new nf;b.plugins_.set(a,new d(b,c||{}));return!0};E.push=function(a){var b=Z.Va.apply(this,arguments);b=Z.eb.concat(b);for(Z.eb=[];0<b.length&&!Z.O(b[0])&&!(b.shift(),0<Z.eb.length););Z.eb=Z.eb.concat(b);return 0}; |
|||
E.Va=function(a){for(var b=[],c=0;c<arguments.length;c++)try{var d=new vd(arguments[c]);d.J?this.O(d):b.push(d)}catch(e){}return b}; |
|||
E.O=function(a){try{if(a.s)a.s.apply(W);else if(a.J)this.I.set(a.Xa[0],a.Xa[1]);else{var b="_gat"==a.i?M:"_gaq"==a.i?Z:M.u(a.i);if(a.Ma){if(!this.Na(a.Xa[0],b,a.Xa[2])){if(!a.Pa){var c=Oa(""+a.Xa[1]);var d=c.protocol,e=J.location.protocol;var f;if(f="https:"==d||d==e?!0:"http:"!=d?!1:"http:"==e)a:{var Be=Oa(J.location.href);if(!(c.query||0<=c.url.indexOf("?")||0<=c.path.indexOf("://")||c.host==Be.host&&c.port==Be.port)){var k="http:"==c.protocol?80:443,Ja=M.S;for(b=0;b<Ja.length;b++)if(c.host==Ja[b][0]&& |
|||
(c.port||k)==(Ja[b][1]||k)&&0==c.path.indexOf(Ja[b][2])){f=!0;break a}}f=!1}f&&!ld()&&(a.Pa=Ia(c.url))}return!0}}else a.l&&(b=b.plugins_.get(a.l)),b[a.h].apply(b,a.Xa)}}catch(t){}};E.Sa=function(a,b){return M.hb(a,b||"")};E.Ta=function(a){return M.u(a)};var yd=function(){function a(a,b,c,d){void 0==f[a]&&(f[a]={});void 0==f[a][b]&&(f[a][b]=[]);f[a][b][c]=d}function b(a,b,c){if(void 0!=f[a]&&void 0!=f[a][b])return f[a][b][c]}function c(a,b){if(void 0!=f[a]&&void 0!=f[a][b]){f[a][b]=void 0;b=!0;var c;for(c=0;c<Be.length;c++)if(void 0!=f[a][Be[c]]){b=!1;break}b&&(f[a]=void 0)}}function d(a){var b="",c=!1,d;for(d=0;d<Be.length;d++){var e=a[Be[d]];if(void 0!=e){c&&(b+=Be[d]);var f=e,Ja=[];for(e=0;e<f.length;e++)if(void 0!=f[e]){c="";1!=e&&void 0==f[e- |
|||
1]&&(c+=e.toString()+"!");var fa,Ke=f[e],Le="";for(fa=0;fa<Ke.length;fa++){var Me=Ke.charAt(fa);var m=k[Me];Le+=void 0!=m?m:Me}c+=Le;Ja.push(c)}b+="("+Ja.join("*")+")";c=!1}else c=!0}return b}var e=this,f=[],Be=["k","v"],k={"'":"'0",")":"'1","*":"'2","!":"'3"};e.Ra=function(a){return void 0!=f[a]};e.A=function(){for(var a="",b=0;b<f.length;b++)void 0!=f[b]&&(a+=b.toString()+d(f[b]));return a};e.Qa=function(a){if(void 0==a)return e.A();for(var b=a.A(),c=0;c<f.length;c++)void 0==f[c]||a.Ra(c)||(b+= |
|||
c.toString()+d(f[c]));return b};e.f=function(b,c,d){if(!wd(d))return!1;a(b,"k",c,d);return!0};e.o=function(b,c,d){if(!xd(d))return!1;a(b,"v",c,d.toString());return!0};e.getKey=function(a,c){return b(a,"k",c)};e.N=function(a,c){return b(a,"v",c)};e.L=function(a){c(a,"k")};e.M=function(a){c(a,"v")};T(e,"_setKey",e.f,89);T(e,"_setValue",e.o,90);T(e,"_getKey",e.getKey,87);T(e,"_getValue",e.N,88);T(e,"_clearKey",e.L,85);T(e,"_clearValue",e.M,86)};function wd(a){return"string"==typeof a} |
|||
function xd(a){return!("number"==typeof a||void 0!=Number&&a instanceof Number)||Math.round(a)!=a||isNaN(a)||Infinity==a?!1:!0};var zd=function(a){var b=W.gaGlobal;a&&!b&&(W.gaGlobal=b={});return b},Ad=function(){var a=zd(!0).hid;null==a&&(a=Ea(),zd(!0).hid=a);return a},Dd=function(a){a.set(Kb,Ad());var b=zd();if(b&&b.dh==a.get(O)){var c=b.sid;c&&(a.get(ac)?H(112):H(132),a.set(Zb,c),a.get(Sb)&&a.set(Wb,c));b=b.vid;a.get(Sb)&&b&&(b=b.split("."),a.set(Q,1*b[0]),a.set(Vb,1*b[1]))}};var Ed,Fd=function(a,b,c,d){var e=a.c(bb,""),f=a.c(P,"/");d=void 0!=d?d:a.b(cb,0);a=a.c(Wa,"");X(b,c,f,e,a,d)},Xc=function(a){var b=a.c(bb,""),c=a.c(P,"/"),d=a.c(Wa,"");X("__utma",cd(a),c,b,d,a.get(cb));X("__utmb",dd(a),c,b,d,a.get(db));X("__utmc",""+a.b(O,1),c,b,d);var e=hd(a,!0);e?X("__utmz",e,c,b,d,a.get(eb)):X("__utmz","",c,b,"",-1);(e=fd(a,!1))?X("__utmv",e,c,b,d,a.get(cb)):X("__utmv","",c,b,"",-1);if(1==a.get(v)&&(e=a.get(w))){var f=a.get(x);b=a.c(bb,"");c=a.c(P,"/");d=a.c(Wa,"");var Be=a.b(y, |
|||
0);a=Math.min(a.b(cb,7776E6),a.b(eb,7776E6),7776E6);a=Math.min(a,1E3*Be+a-(new Date).getTime());if(!f||"aw.ds"==f)if(f=["1",Be+"",q(e)].join("."),0<a&&(e="_gac_"+q(d),!(p(d)||u.test(J.location.hostname)||"/"==c&&r.test(b))&&((d=f)&&1200<d.length&&(d=d.substring(0,1200)),c=e+"="+d+"; path="+c+"; ",a&&(c+="expires="+(new Date((new Date).getTime()+a)).toGMTString()+"; "),b&&"none"!==b&&(c+="domain="+b+";"),b=J.cookie,J.cookie=c,b==J.cookie)))for(b=[],c=J.cookie.split(";"),a=new RegExp("^\\s*"+e+"=\\s*(.*?)\\s*$"), |
|||
d=0;d<c.length;d++)(e=c[d].match(a))&&b.push(e[1])}},Wc=function(a){var b=a.b(O,1);if(!bd(a,$c(b,pd("__utma"))))return a.set(Ub,!0),!1;var c=!ed(a,$c(b,pd("__utmb")));a.set(bc,c);id(a,$c(b,pd("__utmz")));gd(a,$c(b,pd("__utmv")));if(1==a.get(v)){b=a.get(w);var d=a.get(x);if(!b||d&&"aw.ds"!=d){if(J){b=[];d=J.cookie.split(";");for(var e=/^\s*_gac_(UA-\d+-\d+)=\s*(.+?)\s*$/,f=0;f<d.length;f++){var Be=d[f].match(e);Be&&b.push({Oa:Be[1],value:Be[2]})}d={};if(b&&b.length)for(e=0;e<b.length;e++)f=b[e].value.split("."), |
|||
"1"==f[0]&&3==f.length&&f[1]&&(d[b[e].Oa]||(d[b[e].Oa]=[]),d[b[e].Oa].push({timestamp:f[1],kb:f[2]}));b=d}else b={};(b=b[a.get(Wa)])&&0<b.length&&(b=b[0],a.set(y,b.timestamp),a.set(w,b.kb),a.set(x,void 0))}}Ed=!c;return!0},Gd=function(a){Ed||0<pd("__utmb").length||(X("__utmd","1",a.c(P,"/"),a.c(bb,""),a.c(Wa,""),1E4),0==pd("__utmd").length&&a.stopPropagation())};var h=0,Jd=function(a){void 0==a.get(Q)?Hd(a):a.get(Ub)&&!a.get(Mc)?Hd(a):a.get(bc)&&Id(a)},Kd=function(a){a.get(hc)&&!a.get(ac)&&(Id(a),a.set(fc,a.get($b)))},Hd=function(a){h++;1<h&&H(137);var b=a.get(ab);a.set(Sb,!0);a.set(Q,Ea()^td(a)&2147483647);a.set(Tb,"");a.set(Vb,b);a.set(Wb,b);a.set(Zb,b);a.set($b,1);a.set(ac,!0);a.set(cc,0);a.set(R,10);a.set(dc,b);a.set(Fb,[]);a.set(Ub,!1);a.set(bc,!1)},Id=function(a){h++;1<h&&H(137);a.set(Wb,a.get(Zb));a.set(Zb,a.get(ab));a.Za($b);a.set(ac,!0);a.set(cc, |
|||
0);a.set(R,10);a.set(dc,a.get(ab));a.set(bc,!1)};var Ld="daum:q eniro:search_word naver:query pchome:q images.google:q google:q yahoo:p yahoo:q msn:q bing:q aol:query aol:q lycos:q lycos:query ask:q cnn:query virgilio:qs baidu:wd baidu:word alice:qs yandex:text najdi:q seznam:q rakuten:qt biglobe:q goo.ne:MT search.smt.docomo:MT onet:qt onet:q kvasir:q terra:query rambler:query conduit:q babylon:q search-results:q avg:q comcast:q incredimail:q startsiden:q go.mail.ru:q centrum.cz:q 360.cn:q sogou:query tut.by:query globo:q ukr:q so.com:q haosou.com:q auone:q".split(" "), |
|||
Sd=function(a){if(a.get(kb)&&!a.get(Mc)){var b=!F(a.get(ic))||!F(a.get(nc))||!F(a.get(S))||!F(a.get(lc));for(var c={},d=0;d<Md.length;d++){var e=Md[d];c[e]=a.get(e)}(d=a.get(rc))?(H(149),e=new nf,Na(e,d),d=e):d=La(J.location.href,a.get(gb)).R;if("1"!=L(d.get(a.get(ub)))||!b)if(d=Xe(a,d)||Qd(a),d||b||!a.get(ac)||(Pd(a,void 0,"(direct)",void 0,void 0,void 0,"(direct)","(none)",void 0,void 0),d=!0),d&&(a.set(hc,Rd(a,c)),b="(direct)"==a.get(nc)&&"(direct)"==a.get(jc)&&"(none)"==a.get(oc),a.get(hc)||a.get(ac)&& |
|||
!b))a.set(ec,a.get(ab)),a.set(fc,a.get($b)),a.Za(gc)}},Xe=function(a,b){function c(c,d){d=d||"-";return(c=L(b.get(a.get(c))))&&"-"!=c?I(c):d}var d=L(b.get(a.get(nb)))||"-",e=L(b.get(a.get(qb)))||"-",f=L(b.get(a.get(pb)))||"-",Be=L(b.get("gclsrc"))||"-",k=L(b.get("dclid"))||"-";"-"!=f&&a.set(w,f);"-"!=Be&&a.set(x,Be);var Ja=c(ob,"(not set)"),t=c(rb,"(not set)"),Za=c(sb),Ma=c(tb);if(F(d)&&F(f)&&F(k)&&F(e))return!1;var mb=!F(f)&&!F(Be);mb=F(e)&&(!F(k)||mb);var Xb=F(Za);if(mb||Xb){var Bd=Nd(a);Bd=La(Bd, |
|||
!0);(Bd=Od(a,Bd))&&!F(Bd[1]&&!Bd[2])&&(mb&&(e=Bd[0]),Xb&&(Za=Bd[1]))}Pd(a,d,e,f,Be,k,Ja,t,Za,Ma);return!0},Qd=function(a){var b=Nd(a),c=La(b,!0);(b=!(void 0!=b&&null!=b&&""!=b&&"0"!=b&&"-"!=b&&0<=b.indexOf("://")))||(b=c&&-1<c.host.indexOf("google")&&c.R.contains("q")&&"cse"==c.path);if(b)return!1;if((b=Od(a,c))&&!b[2])return Pd(a,void 0,b[0],void 0,void 0,void 0,"(organic)","organic",b[1],void 0),!0;if(b||!a.get(ac))return!1;a:{b=a.get(Bb);for(var d=Ka(c.host),e=0;e<b.length;++e)if(-1<d.indexOf(b[e])){a= |
|||
!1;break a}Pd(a,void 0,d,void 0,void 0,void 0,"(referral)","referral",void 0,"/"+c.path);a=!0}return a},Od=function(a,b){for(var c=a.get(zb),d=0;d<c.length;++d){var e=c[d].split(":");if(-1<b.host.indexOf(e[0].toLowerCase())){var f=b.R.get(e[1]);if(f&&(f=K(f),!f&&-1<b.host.indexOf("google.")&&(f="(not provided)"),!e[3]||-1<b.url.indexOf(e[3]))){f||H(151);a:{b=f;a=a.get(Ab);b=I(b).toLowerCase();for(c=0;c<a.length;++c)if(b==a[c]){a=!0;break a}a=!1}return[e[2]||e[0],f,a]}}}return null},Pd=function(a, |
|||
b,c,d,e,f,Be,k,Ja,t){a.set(ic,b);a.set(nc,c);a.set(S,d);a.set(kc,e);a.set(lc,f);a.set(jc,Be);a.set(oc,k);a.set(pc,Ja);a.set(qc,t)},Md=[jc,ic,S,lc,nc,oc,pc,qc],Rd=function(a,b){function c(a){a=(""+a).split("+").join("%20");return a=a.split(" ").join("%20")}function d(c){var d=""+(a.get(c)||"");c=""+(b[c]||"");return 0<d.length&&d==c}if(d(S)||d(lc))return H(131),!1;for(var e=0;e<Md.length;e++){var f=Md[e],Be=b[f]||"-";f=a.get(f)||"-";if(c(Be)!=c(f))return!0}return!1},Td=new RegExp(/^https?:\/\/(www\.)?google(\.com?)?(\.[a-z]{2}t?)?\/?$/i), |
|||
jf=/^https?:\/\/(r\.)?search\.yahoo\.com?(\.jp)?\/?[^?]*$/i,rf=/^https?:\/\/(www\.)?bing\.com\/?$/i,Nd=function(a){a=Pa(a.get(Jb),a.get(P));try{if(Td.test(a))return H(136),a+"?q=";if(jf.test(a))return H(150),a+"?p=(not provided)";if(rf.test(a))return a+"?q=(not provided)"}catch(b){H(145)}return a};var Ud,Vd,Wd=function(a){Ud=a.c(S,"");Vd=a.c(kc,"")},Xd=function(a){var b=a.c(S,""),c=a.c(kc,"");b!=Ud&&(-1<c.indexOf("ds")?a.set(mc,void 0):!F(Ud)&&-1<Vd.indexOf("ds")&&a.set(mc,Ud))};var Zd=function(a){Yd(a,J.location.href)?(a.set(Mc,!0),H(12)):a.set(Mc,!1)},Yd=function(a,b){if(!a.get(fb))return!1;var c=La(b,a.get(gb));b=K(c.R.get("__utma"));var d=K(c.R.get("__utmb")),e=K(c.R.get("__utmc")),f=K(c.R.get("__utmx")),Be=K(c.R.get("__utmz")),k=K(c.R.get("__utmv"));c=K(c.R.get("__utmk"));if(Yc(""+b+d+e+f+Be+k)!=c){b=I(b);d=I(d);e=I(e);f=I(f);e=$d(b+d+e+f,Be,k,c);if(!e)return!1;Be=e[0];k=e[1]}if(!bd(a,b,!0))return!1;ed(a,d,!0);id(a,Be,!0);gd(a,k,!0);ae(a,f,!0);return!0},ce=function(a, |
|||
b,c){var d=cd(a)||"-";var e=dd(a)||"-",f=""+a.b(O,1)||"-",Be=be(a)||"-",k=hd(a,!1)||"-";a=fd(a,!1)||"-";var Ja=Yc(""+d+e+f+Be+k+a),t=[];t.push("__utma="+d);t.push("__utmb="+e);t.push("__utmc="+f);t.push("__utmx="+Be);t.push("__utmz="+k);t.push("__utmv="+a);t.push("__utmk="+Ja);d=t.join("&");if(!d)return b;e=b.indexOf("#");if(c)return 0>e?b+"#"+d:b+"&"+d;c="";0<e&&(c=b.substring(e),b=b.substring(0,e));return 0>b.indexOf("?")?b+"?"+d+c:b+"&"+d+c},$d=function(a,b,c,d){for(var e=0;3>e;e++){for(var f= |
|||
0;3>f;f++){if(d==Yc(a+b+c))return H(127),[b,c];var Be=b.replace(/ /g,"%20"),k=c.replace(/ /g,"%20");if(d==Yc(a+Be+k))return H(128),[Be,k];Be=Be.replace(/\+/g,"%20");k=k.replace(/\+/g,"%20");if(d==Yc(a+Be+k))return H(129),[Be,k];try{var Ja=b.match("utmctr=(.*?)(?:\\|utm|$)");if(Ja&&2==Ja.length&&(Be=b.replace(Ja[1],G(I(Ja[1]))),d==Yc(a+Be+c)))return H(139),[Be,c]}catch(t){}b=I(b)}c=I(c)}};var de="|",fe=function(a,b,c,d,e,f,Be,k,Ja){var t=ee(a,b);t||(t={},a.get(Cb).push(t));t.id_=b;t.affiliation_=c;t.total_=d;t.tax_=e;t.shipping_=f;t.city_=Be;t.state_=k;t.country_=Ja;t.items_=t.items_||[];return t},ge=function(a,b,c,d,e,f,Be){a=ee(a,b)||fe(a,b,"",0,0,0,"","","");a:{if(a&&a.items_){var k=a.items_;for(var Ja=0;Ja<k.length;Ja++)if(k[Ja].sku_==c){k=k[Ja];break a}}k=null}Ja=k||{};Ja.transId_=b;Ja.sku_=c;Ja.name_=d;Ja.category_=e;Ja.price_=f;Ja.quantity_=Be;k||a.items_.push(Ja);return Ja}, |
|||
ee=function(a,b){a=a.get(Cb);for(var c=0;c<a.length;c++)if(a[c].id_==b)return a[c];return null};var he,ie=function(a){if(!he){var b=J.location.hash;var c=W.name,d=/^#?gaso=([^&]*)/;if(c=(b=(b=b&&b.match(d)||c&&c.match(d))?b[1]:K(pd("GASO")))&&b.match(/^(?:!([-0-9a-z.]{1,40})!)?([-.\w]{10,1200})$/i))Fd(a,"GASO",""+b,0),M._gasoDomain=a.get(bb),M._gasoCPath=a.get(P),a=c[1],Ia("https://www.google.com/analytics/web/inpage/pub/inpage.js?"+(a?"prefix="+a+"&":"")+Ea(),"_gasojs");he=!0}};var ae=function(a,b,c){c&&(b=I(b));c=a.b(O,1);b=b.split(".");2>b.length||!/^\d+$/.test(b[0])||(b[0]=""+c,Fd(a,"__utmx",b.join("."),void 0))},be=function(a,b){a=$c(a.get(O),pd("__utmx"));"-"==a&&(a="");return b?G(a):a},Ye=function(a){try{var b=La(J.location.href,!1),c=decodeURIComponent(L(b.R.get("utm_referrer")))||"";c&&a.set(Jb,c);var d=decodeURIComponent(K(b.R.get("utm_expid")))||"";d&&(d=d.split(".")[0],a.set(Oc,""+d))}catch(e){H(146)}},l=function(a){var b=W.gaData&&W.gaData.expId;b&&a.set(Oc, |
|||
""+b)};var ke=function(a,b){var c=Math.min(a.b(Dc,0),100);if(a.b(Q,0)%100>=c)return!1;c=Ze()||$e();if(void 0==c)return!1;var d=c[0];if(void 0==d||Infinity==d||isNaN(d))return!1;0<d?af(c)?b(je(c)):b(je(c.slice(0,1))):Ga(W,"load",function(){ke(a,b)},!1);return!0},me=function(a,b,c,d){var e=new yd;e.f(14,90,b.substring(0,500));e.f(14,91,a.substring(0,150));e.f(14,92,""+le(c));void 0!=d&&e.f(14,93,d.substring(0,500));e.o(14,90,c);return e},af=function(a){for(var b=1;b<a.length;b++)if(isNaN(a[b])||Infinity== |
|||
a[b]||0>a[b])return!1;return!0},le=function(a){return isNaN(a)||0>a?0:5E3>a?10*Math.floor(a/10):5E4>a?100*Math.floor(a/100):41E5>a?1E3*Math.floor(a/1E3):41E5},je=function(a){for(var b=new yd,c=0;c<a.length;c++)b.f(14,c+1,""+le(a[c])),b.o(14,c+1,a[c]);return b},Ze=function(){var a=W.performance||W.webkitPerformance;if(a=a&&a.timing){var b=a.navigationStart;if(0==b)H(133);else return[a.loadEventStart-b,a.domainLookupEnd-a.domainLookupStart,a.connectEnd-a.connectStart,a.responseStart-a.requestStart, |
|||
a.responseEnd-a.responseStart,a.fetchStart-b,a.domInteractive-b,a.domContentLoadedEventStart-b]}},$e=function(){if(W.top==W){var a=W.external,b=a&&a.onloadT;a&&!a.isValidLoadTime&&(b=void 0);2147483648<b&&(b=void 0);0<b&&a.setPageReadyTime();if(void 0!=b)return[b]}};var cf=function(a){if(a.get(Sb))try{a:{var b=pd(a.get(Oe)||"_ga");if(b&&!(1>b.length)){for(var c=[],d=0;d<b.length;d++){var e=b[d].split("."),f=e.shift();if(("GA1"==f||"1"==f)&&1<e.length){var Be=e.shift().split("-");1==Be.length&&(Be[1]="1");Be[0]*=1;Be[1]*=1;var k={Ya:Be,$a:e.join(".")}}else k=void 0;k&&c.push(k)}if(1==c.length){var Ja=c[0].$a;break a}if(0!=c.length){var t=a.get(Pe)||a.get(bb);c=bf(c,(0==t.indexOf(".")?t.substr(1):t).split(".").length,0);if(1==c.length){Ja=c[0].$a;break a}var Za= |
|||
a.get(Qe)||a.get(P);(b=Za)?(1<b.length&&"/"==b.charAt(b.length-1)&&(b=b.substr(0,b.length-1)),0!=b.indexOf("/")&&(b="/"+b),Za=b):Za="/";c=bf(c,"/"==Za?1:Za.split("/").length,1);Ja=c[0].$a;break a}}Ja=void 0}if(Ja){var Ma=(""+Ja).split(".");2==Ma.length&&/[0-9.]/.test(Ma)&&(H(114),a.set(Q,Ma[0]),a.set(Vb,Ma[1]),a.set(Sb,!1))}}catch(mb){H(115)}},bf=function(a,b,c){for(var d=[],e=[],f=128,Be=0;Be<a.length;Be++){var k=a[Be];k.Ya[c]==b?d.push(k):k.Ya[c]==f?e.push(k):k.Ya[c]<f&&(e=[k],f=k.Ya[c])}return 0< |
|||
d.length?d:e};var kf=/^gtm\d+$/,hf=function(a){var b=!!a.b(Cd,1);if(b)if(H(140),"page"!=a.get(sc))a.set(Kc,"",!0);else if(b=a.c(Lc,""),b||(b=(b=a.c($a,""))&&"~0"!=b?kf.test(b)?"__utmt_"+G(a.c(Wa,"")):"__utmt_"+G(b):"__utmt"),0<pd(b).length)a.set(Kc,"",!0);else if(X(b,"1",a.c(P,"/"),a.c(bb,""),a.c(Wa,""),6E5),0<pd(b).length){a.set(Kc,Ea(),!0);a.set(Yb,1,!0);if(void 0!==W.__ga4__)b=W.__ga4__;else{if(void 0===A){var c=W.navigator.userAgent;if(c){b=c;try{b=decodeURIComponent(c)}catch(d){}if(c=!(0<=b.indexOf("Chrome"))&& |
|||
!(0<=b.indexOf("CriOS"))&&(0<=b.indexOf("Safari/")||0<=b.indexOf("Safari,")))b=B.exec(b),c=11<=(b?Number(b[1]):-1);A=c}else A=!1}b=A}b?(a.set(z,C(a),!0),a.set(Jc,"https://ssl.google-analytics.com/j/__utm.gif",!0)):a.set(Jc,Ne()+"/r/__utm.gif?",!0)}},C=function(a){a=aa(a);return{gb:"t=dc&_r=3&"+a,google:"t=sr&slf_rd=1&_r=4&"+a,count:0}},aa=function(a){function b(a,b){c.push(a+"="+G(b))}var c=[];b("v","1");b("_v","5.7.2");b("tid",a.get(Wa));b("cid",a.get(Q)+"."+a.get(Vb));b("jid",a.get(Kc));b("aip", |
|||
"1");return c.join("&")+"&z="+Ea()};var U=function(a,b,c){function d(a){return function(b){if((b=b.get(Nc)[a])&&b.length)for(var c=Te(e,a),d=0;d<b.length;d++)b[d].call(e,c)}}var e=this;this.a=new Zc;this.get=function(a){return this.a.get(a)};this.set=function(a,b,c){this.a.set(a,b,c)};this.set(Wa,b||"UA-XXXXX-X");this.set($a,a||"");this.set(Ya,c||"");this.set(ab,Math.round((new Date).getTime()/1E3));this.set(P,"/");this.set(cb,63072E6);this.set(eb,15768E6);this.set(db,18E5);this.set(fb,!1);this.set(yb,50);this.set(gb,!1);this.set(hb, |
|||
!0);this.set(ib,!0);this.set(jb,!0);this.set(kb,!0);this.set(lb,!0);this.set(ob,"utm_campaign");this.set(nb,"utm_id");this.set(pb,"gclid");this.set(qb,"utm_source");this.set(rb,"utm_medium");this.set(sb,"utm_term");this.set(tb,"utm_content");this.set(ub,"utm_nooverride");this.set(vb,100);this.set(Dc,1);this.set(Ec,!1);this.set(wb,"/__utm.gif");this.set(xb,1);this.set(Cb,[]);this.set(Fb,[]);this.set(zb,Ld.slice(0));this.set(Ab,[]);this.set(Bb,[]);this.B("auto");this.set(Jb,J.referrer);this.set(v,!0); |
|||
this.set(y,Math.round((new Date).getTime()/1E3));Ye(this.a);this.set(Nc,{hit:[],load:[]});this.a.g("0",Zd);this.a.g("1",Wd);this.a.g("2",Jd);this.a.g("3",cf);this.a.g("4",Sd);this.a.g("5",Xd);this.a.g("6",Kd);this.a.g("7",d("load"));this.a.g("8",ie);this.a.v("A",kd);this.a.v("B",md);this.a.v("C",Ge);this.a.v("D",Jd);this.a.v("E",jd);this.a.v("F",Tc);this.a.v("G",ne);this.a.v("H",lf);this.a.v("I",Gd);this.a.v("J",nd);this.a.v("K",ud);this.a.v("L",Dd);this.a.v("M",l);this.a.v("N",hf);this.a.v("O",d("hit")); |
|||
this.a.v("P",oe);this.a.v("Q",pe);0===this.get(ab)&&H(111);this.a.T();this.H=void 0};E=U.prototype;E.m=function(){var a=this.get(Db);a||(a=new yd,this.set(Db,a));return a};E.La=function(a){for(var b in a){var c=a[b];a.hasOwnProperty(b)&&this.set(b,c,!0)}};E.K=function(a){if(this.get(Ec))return!1;var b=this,c=ke(this.a,function(c){b.set(Hb,a,!0);b.ib(c)});this.set(Ec,c);return c}; |
|||
E.Fa=function(a){a&&Ca(a)?(H(13),this.set(Hb,a,!0)):"object"===typeof a&&null!==a&&this.La(a);this.H=a=this.get(Hb);this.a.j("page");this.K(a)};E.F=function(a,b,c,d,e){if(""==a||!wd(a)||""==b||!wd(b)||void 0!=c&&!wd(c)||void 0!=d&&!xd(d))return!1;this.set(wc,a,!0);this.set(xc,b,!0);this.set(yc,c,!0);this.set(zc,d,!0);this.set(vc,!!e,!0);this.a.j("event");return!0}; |
|||
E.Ha=function(a,b,c,d,e){var f=this.a.b(Dc,0);1*e===e&&(f=e);if(this.a.b(Q,0)%100>=f)return!1;c=1*(""+c);if(""==a||!wd(a)||""==b||!wd(b)||!xd(c)||isNaN(c)||0>c||0>f||100<f||void 0!=d&&(""==d||!wd(d)))return!1;this.ib(me(a,b,c,d));return!0};E.Ga=function(a,b,c,d){if(!a||!b)return!1;this.set(Ac,a,!0);this.set(Bc,b,!0);this.set(Cc,c||J.location.href,!0);d&&this.set(Hb,d,!0);this.a.j("social");return!0};E.Ea=function(){this.set(Dc,10);this.K(this.H)};E.Ia=function(){this.a.j("trans")}; |
|||
E.ib=function(a){this.set(Eb,a,!0);this.a.j("event")};E.ia=function(a){this.initData();var b=this;return{_trackEvent:function(c,d,e){H(91);b.F(a,c,d,e)}}};E.ma=function(a){return this.get(a)};E.xa=function(a,b){if(a)if(Ca(a))this.set(a,b);else if("object"==typeof a)for(var c in a)a.hasOwnProperty(c)&&this.set(c,a[c])};E.addEventListener=function(a,b){(a=this.get(Nc)[a])&&a.push(b)};E.removeEventListener=function(a,b){a=this.get(Nc)[a];for(var c=0;a&&c<a.length;c++)if(a[c]==b){a.splice(c,1);break}}; |
|||
E.qa=function(){return"5.7.2"};E.B=function(a){this.get(hb);a="auto"==a?Ka(J.domain):a&&"-"!=a&&"none"!=a?a.toLowerCase():"";this.set(bb,a)};E.va=function(a){this.set(hb,!!a)};E.na=function(a,b){return ce(this.a,a,b)};E.link=function(a,b){this.a.get(fb)&&a&&(J.location.href=ce(this.a,a,b))};E.ua=function(a,b){this.a.get(fb)&&a&&a.action&&(a.action=ce(this.a,a.action,b))}; |
|||
E.za=function(){this.initData();var a=this.a,b=J.getElementById?J.getElementById("utmtrans"):J.utmform&&J.utmform.utmtrans?J.utmform.utmtrans:null;if(b&&b.value){a.set(Cb,[]);b=b.value.split("UTM:");for(var c=0;c<b.length;c++){b[c]=Da(b[c]);for(var d=b[c].split(de),e=0;e<d.length;e++)d[e]=Da(d[e]);"T"==d[0]?fe(a,d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8]):"I"==d[0]&&ge(a,d[1],d[2],d[3],d[4],d[5],d[6])}}};E.$=function(a,b,c,d,e,f,Be,k){return fe(this.a,a,b,c,d,e,f,Be,k)}; |
|||
E.Y=function(a,b,c,d,e,f){return ge(this.a,a,b,c,d,e,f)};E.Aa=function(a){de=a||"|"};E.ea=function(){this.set(Cb,[])};E.wa=function(a,b,c,d){var e=this.a;if(0>=a||a>e.get(yb))a=!1;else if(!b||!c||128<b.length+c.length)a=!1;else{1!=d&&2!=d&&(d=3);var f={};f.name=b;f.value=c;f.scope=d;e.get(Fb)[a]=f;a=!0}a&&this.a.store();return a};E.ka=function(a){this.a.get(Fb)[a]=void 0;this.a.store()};E.ra=function(a){return(a=this.a.get(Fb)[a])&&1==a.scope?a.value:void 0}; |
|||
E.Ca=function(a,b,c){12==a&&1==b?this.set(pf,c):this.m().f(a,b,c)};E.Da=function(a,b,c){this.m().o(a,b,c)};E.sa=function(a,b){return this.m().getKey(a,b)};E.ta=function(a,b){return this.m().N(a,b)};E.fa=function(a){this.m().L(a)};E.ga=function(a){this.m().M(a)};E.ja=function(){return new yd};E.W=function(a){a&&this.get(Ab).push(a.toLowerCase())};E.ba=function(){this.set(Ab,[])};E.X=function(a){a&&this.get(Bb).push(a.toLowerCase())};E.ca=function(){this.set(Bb,[])}; |
|||
E.Z=function(a,b,c,d,e){if(a&&b){a=[a,b.toLowerCase()].join(":");if(d||e)a=[a,d,e].join(":");d=this.get(zb);d.splice(c?0:d.length,0,a)}};E.da=function(){this.set(zb,[])};E.ha=function(a){this.a.load();var b=this.get(P),c=be(this.a);this.set(P,a);this.a.store();ae(this.a,c);this.set(P,b)};E.ya=function(a,b){if(0<a&&5>=a&&Ca(b)&&""!=b){var c=this.get(Fc)||[];c[a]=b;this.set(Fc,c)}};E.V=function(a){a=""+a;if(a.match(/^[A-Za-z0-9]{1,5}$/)){var b=this.get(Ic)||[];b.push(a);this.set(Ic,b)}}; |
|||
E.initData=function(){this.a.load()};E.Ba=function(a){a&&""!=a&&(this.set(Tb,a),this.a.j("var"))};var ne=function(a){"trans"!==a.get(sc)&&500<=a.b(cc,0)&&a.stopPropagation();if("event"===a.get(sc)){var b=(new Date).getTime(),c=a.b(dc,0),d=a.b(Zb,0);c=Math.floor((b-(c!=d?c:1E3*c))/1E3);0<c&&(a.set(dc,b),a.set(R,Math.min(10,a.b(R,0)+c)));0>=a.b(R,0)&&a.stopPropagation()}},pe=function(a){"event"===a.get(sc)&&a.set(R,Math.max(0,a.b(R,10)-1))};var qe=function(){var a=[];this.add=function(b,c,d){d&&(c=G(""+c));a.push(b+"="+c)};this.toString=function(){return a.join("&")}},re=function(a,b){(b||2!=a.get(xb))&&a.Za(cc)},se=function(a,b){b.add("utmwv","5.7.2");b.add("utms",a.get(cc));b.add("utmn",Ea());var c=J.location.hostname;F(c)||b.add("utmhn",c,!0);a=a.get(vb);100!=a&&b.add("utmsp",a,!0)},te=function(a,b){b.add("utmht",(new Date).getTime());b.add("utmac",Da(a.get(Wa)));a.get(Oc)&&b.add("utmxkey",a.get(Oc),!0);a.get(vc)&&b.add("utmni",1); |
|||
a.get(of)&&b.add("utmgtm",a.get(of),!0);var c=a.get(Ic);c&&0<c.length&&b.add("utmdid",c.join("."));ff(a,b);!1!==a.get(Xa)&&(a.get(Xa)||M.w)&&b.add("aip",1);void 0!==a.get(Kc)&&b.add("utmjid",a.c(Kc,""),!0);a.b(Yb,0)&&b.add("utmredir",a.b(Yb,0),!0);M.bb||(M.bb=a.get(Wa));(1<M.ab()||M.bb!=a.get(Wa))&&b.add("utmmt",1);b.add("utmu",od.encode())},ue=function(a,b){a=a.get(Fc)||[];for(var c=[],d=1;d<a.length;d++)a[d]&&c.push(d+":"+G(a[d].replace(/%/g,"%25").replace(/:/g,"%3A").replace(/,/g,"%2C")));c.length&& |
|||
b.add("utmpg",c.join(","))},ff=function(a,b){function c(a,b){b&&d.push(a+"="+b+";")}var d=[];c("__utma",cd(a));c("__utmz",hd(a,!1));c("__utmv",fd(a,!0));c("__utmx",be(a));b.add("utmcc",d.join("+"),!0)},ve=function(a,b){a.get(ib)&&(b.add("utmcs",a.get(Qb),!0),b.add("utmsr",a.get(Lb)),a.get(Rb)&&b.add("utmvp",a.get(Rb)),b.add("utmsc",a.get(Mb)),b.add("utmul",a.get(Pb)),b.add("utmje",a.get(Nb)),b.add("utmfl",a.get(Ob),!0))},we=function(a,b){a.get(lb)&&a.get(Ib)&&b.add("utmdt",a.get(Ib),!0);b.add("utmhid", |
|||
a.get(Kb));b.add("utmr",Pa(a.get(Jb),a.get(P)),!0);b.add("utmp",G(a.get(Hb),!0),!0)},xe=function(a,b){for(var c=a.get(Db),d=a.get(Eb),e=a.get(Fb)||[],f=0;f<e.length;f++){var Be=e[f];Be&&(c||(c=new yd),c.f(8,f,Be.name),c.f(9,f,Be.value),3!=Be.scope&&c.f(11,f,""+Be.scope))}F(a.get(wc))||F(a.get(xc),!0)||(c||(c=new yd),c.f(5,1,a.get(wc)),c.f(5,2,a.get(xc)),e=a.get(yc),void 0!=e&&c.f(5,3,e),e=a.get(zc),void 0!=e&&c.o(5,1,e));F(a.get(pf))||(c||(c=new yd),c.f(12,1,a.get(pf)));c?b.add("utme",c.Qa(d),!0): |
|||
d&&b.add("utme",d.A(),!0)},ye=function(a,b,c){var d=new qe;re(a,c);se(a,d);d.add("utmt","tran");d.add("utmtid",b.id_,!0);d.add("utmtst",b.affiliation_,!0);d.add("utmtto",b.total_,!0);d.add("utmttx",b.tax_,!0);d.add("utmtsp",b.shipping_,!0);d.add("utmtci",b.city_,!0);d.add("utmtrg",b.state_,!0);d.add("utmtco",b.country_,!0);xe(a,d);ve(a,d);we(a,d);(b=a.get(Gb))&&d.add("utmcu",b,!0);c||(ue(a,d),te(a,d));return d.toString()},ze=function(a,b,c){var d=new qe;re(a,c);se(a,d);d.add("utmt","item");d.add("utmtid", |
|||
b.transId_,!0);d.add("utmipc",b.sku_,!0);d.add("utmipn",b.name_,!0);d.add("utmiva",b.category_,!0);d.add("utmipr",b.price_,!0);d.add("utmiqt",b.quantity_,!0);xe(a,d);ve(a,d);we(a,d);(b=a.get(Gb))&&d.add("utmcu",b,!0);c||(ue(a,d),te(a,d));return d.toString()},Ae=function(a,b){var c=a.get(sc);if("page"==c)c=new qe,re(a,b),se(a,c),xe(a,c),ve(a,c),we(a,c),b||(ue(a,c),te(a,c)),a=[c.toString()];else if("event"==c)c=new qe,re(a,b),se(a,c),c.add("utmt","event"),xe(a,c),ve(a,c),we(a,c),b||(ue(a,c),te(a,c)), |
|||
a=[c.toString()];else if("var"==c)c=new qe,re(a,b),se(a,c),c.add("utmt","var"),!b&&te(a,c),a=[c.toString()];else if("trans"==c){c=[];for(var d=a.get(Cb),e=0;e<d.length;++e){c.push(ye(a,d[e],b));for(var f=d[e].items_,Be=0;Be<f.length;++Be)c.push(ze(a,f[Be],b))}a=c}else"social"==c?b?a=[]:(c=new qe,re(a,b),se(a,c),c.add("utmt","social"),c.add("utmsn",a.get(Ac),!0),c.add("utmsa",a.get(Bc),!0),c.add("utmsid",a.get(Cc),!0),xe(a,c),ve(a,c),we(a,c),ue(a,c),te(a,c),a=[c.toString()]):"feedback"==c?b?a=[]:(c= |
|||
new qe,re(a,b),se(a,c),c.add("utmt","feedback"),c.add("utmfbid",a.get(Gc),!0),c.add("utmfbpr",a.get(Hc),!0),xe(a,c),ve(a,c),we(a,c),ue(a,c),te(a,c),a=[c.toString()]):a=[];return a},oe=function(a){var b=a.get(xb),c=a.get(uc),d=c&&c.Ua,e=0,f=a.get(z);if(0==b||2==b){var Be=a.get(wb)+"?";var k=Ae(a,!0);for(var Ja=0,t=k.length;Ja<t;Ja++)Sa(k[Ja],d,Be,!0),e++}if(1==b||2==b)for(k=Ae(a),a=a.c(Jc,""),Ja=0,t=k.length;Ja<t;Ja++)try{if(f){var Za=k[Ja];b=(b=d)||Fa;df("",b,a+"?"+Za,f)}else Sa(k[Ja],d,a);e++}catch(Ma){Ma&& |
|||
Ra(Ma.name,void 0,Ma.message)}c&&(c.fb=e)};var Ne=function(){return"https:"==J.location.protocol||M.G?"https://ssl.google-analytics.com":"http://www.google-analytics.com"},Ce=function(a){this.name="len";this.message=a+"-8192"},De=function(a){this.name="ff2post";this.message=a+"-2036"},Sa=function(a,b,c,d){b=b||Fa;if(d||2036>=a.length)gf(a,b,c);else if(8192>=a.length){if(0<=W.navigator.userAgent.indexOf("Firefox")&&![].reduce)throw new De(a.length);df(a,b)||ef(a,b)||Ee(a,b)||b()}else throw new Ce(a.length);},gf=function(a,b,c){c=c||Ne()+"/__utm.gif?"; |
|||
var d=new Image(1,1);d.src=c+a;d.onload=function(){d.onload=null;d.onerror=null;b()};d.onerror=function(){d.onload=null;d.onerror=null;b()}},ef=function(a,b){if(0!=Ne().indexOf(J.location.protocol))return!1;var c=W.XDomainRequest;if(!c)return!1;c=new c;c.open("POST",Ne()+"/p/__utm.gif");c.onerror=function(){b()};c.onload=b;c.send(a);return!0},df=function(a,b,c,d){var e=W.XMLHttpRequest;if(!e)return!1;var f=new e;if(!("withCredentials"in f))return!1;f.open("POST",c||Ne()+"/p/__utm.gif",!0);f.withCredentials= |
|||
!0;f.setRequestHeader("Content-Type","text/plain");f.onreadystatechange=function(){if(4==f.readyState){if(d)try{var a=f.responseText;if(1>a.length||"1"!=a.charAt(0))Ra("xhr","ver",a),b();else if(3<d.count++)Ra("xhr","tmr",""+d.count),b();else if(1==a.length)b();else{var c=a.charAt(1);if("d"==c){var e=d.gb;a=(a=b)||Fa;df("",a,"https://stats.g.doubleclick.net/j/collect?"+e,d)}else if("g"==c){var t="https://www.google.%/ads/ga-audiences?".replace("%","com");gf(d.google,b,t);var Za=a.substring(2);if(Za)if(/^[a-z.]{1,6}$/.test(Za)){var Ma= |
|||
"https://www.google.%/ads/ga-audiences?".replace("%",Za);gf(d.google,Fa,Ma)}else Ra("tld","bcc",Za)}else Ra("xhr","brc",c),b()}}catch(mb){b()}else b();f=null}};f.send(a);return!0},Ee=function(a,b){if(!J.body)return We(function(){Ee(a,b)},100),!0;a=encodeURIComponent(a);try{var c=J.createElement('<iframe name="'+a+'"></iframe>')}catch(e){c=J.createElement("iframe"),c.name=a}c.height="0";c.width="0";c.style.display="none";c.style.visibility="hidden";var d=Ne()+"/u/post_iframe.html";Ga(W,"beforeunload", |
|||
function(){c.src="";c.parentNode&&c.parentNode.removeChild(c)});setTimeout(b,1E3);J.body.appendChild(c);c.src=d;return!0};var qf=function(){this.G=this.w=!1;0==Ea()%1E4&&(H(142),this.G=!0);this.C={};this.D=[];this.U=0;this.S=[["www.google-analytics.com","","/plugins/"]];this._gasoCPath=this._gasoDomain=this.bb=void 0;Re();Se()};E=qf.prototype;E.oa=function(a,b){return this.hb(a,void 0,b)};E.hb=function(a,b,c){b&&H(23);c&&H(67);void 0==b&&(b="~"+M.U++);a=new U(b,a,c);M.C[b]=a;M.D.push(a);return a};E.u=function(a){a=a||"";return M.C[a]||M.hb(void 0,a)};E.pa=function(){return M.D.slice(0)};E.ab=function(){return M.D.length}; |
|||
E.aa=function(){this.w=!0};E.la=function(){this.G=!0};var Fe=function(a){if("prerender"==J.visibilityState)return!1;a();return!0};var M=new qf;var D=W._gat;D&&Ba(D._getTracker)?M=D:W._gat=M;var Z=new Y;(function(a){if(!Fe(a)){H(123);var b=!1,c=function(){if(!b&&Fe(a)){b=!0;var d=J,e=c;d.removeEventListener?d.removeEventListener("visibilitychange",e,!1):d.detachEvent&&d.detachEvent("onvisibilitychange",e)}};Ga(J,"visibilitychange",c)}})(function(){var a=W._gaq,b=!1;if(a&&Ba(a.push)&&(b="[object Array]"==Object.prototype.toString.call(Object(a)),!b)){Z=a;return}W._gaq=Z;b&&Z.push.apply(Z,a)});function Yc(a){var b=1,c;if(a)for(b=0,c=a.length-1;0<=c;c--){var d=a.charCodeAt(c);b=(b<<6&268435455)+d+(d<<14);d=b&266338304;b=0!=d?b^d>>21:b}return b};}).call(this); |
|||
@ -0,0 +1,240 @@ |
|||
// QRCODE reader Copyright 2011 Lazar Laszlo
|
|||
// http://www.webqr.com
|
|||
|
|||
var gCtx = null; |
|||
var gCanvas = null; |
|||
var c=0; |
|||
var stype=0; |
|||
var gUM=false; |
|||
var webkit=false; |
|||
var moz=false; |
|||
var v=null; |
|||
|
|||
var imghtml='<div id="qrfile"><canvas id="out-canvas" width="320" height="240"></canvas>'+ |
|||
'<div id="imghelp">drag and drop a QRCode here'+ |
|||
'<br>or select a file'+ |
|||
'<input type="file" onchange="handleFiles(this.files)"/>'+ |
|||
'</div>'+ |
|||
'</div>'; |
|||
|
|||
var vidhtml = '<video id="v" autoplay></video>'; |
|||
|
|||
function dragenter(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
} |
|||
|
|||
function dragover(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
} |
|||
function drop(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
|
|||
var dt = e.dataTransfer; |
|||
var files = dt.files; |
|||
if(files.length>0) |
|||
{ |
|||
handleFiles(files); |
|||
} |
|||
else |
|||
if(dt.getData('URL')) |
|||
{ |
|||
qrcode.decode(dt.getData('URL')); |
|||
} |
|||
} |
|||
|
|||
function handleFiles(f) |
|||
{ |
|||
var o=[]; |
|||
|
|||
for(var i =0;i<f.length;i++) |
|||
{ |
|||
var reader = new FileReader(); |
|||
reader.onload = (function(theFile) { |
|||
return function(e) { |
|||
gCtx.clearRect(0, 0, gCanvas.width, gCanvas.height); |
|||
|
|||
qrcode.decode(e.target.result); |
|||
}; |
|||
})(f[i]); |
|||
reader.readAsDataURL(f[i]); |
|||
} |
|||
} |
|||
|
|||
function initCanvas(w,h) |
|||
{ |
|||
gCanvas = document.getElementById("qr-canvas"); |
|||
gCanvas.style.width = w + "px"; |
|||
gCanvas.style.height = h + "px"; |
|||
gCanvas.width = w; |
|||
gCanvas.height = h; |
|||
gCtx = gCanvas.getContext("2d"); |
|||
gCtx.clearRect(0, 0, w, h); |
|||
} |
|||
|
|||
|
|||
function captureToCanvas() { |
|||
if(stype!=1) |
|||
return; |
|||
if(gUM) |
|||
{ |
|||
try{ |
|||
gCtx.drawImage(v,0,0); |
|||
try{ |
|||
qrcode.decode(); |
|||
} |
|||
catch(e){ |
|||
console.log(e); |
|||
setTimeout(captureToCanvas, 500); |
|||
}; |
|||
} |
|||
catch(e){ |
|||
console.log(e); |
|||
setTimeout(captureToCanvas, 500); |
|||
}; |
|||
} |
|||
} |
|||
|
|||
function htmlEntities(str) { |
|||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); |
|||
} |
|||
|
|||
function read(a) |
|||
{ |
|||
var html="<br>"; |
|||
if(a.indexOf("http://") === 0 || a.indexOf("https://") === 0) |
|||
html+="<a target='_blank' href='"+a+"'>"+a+"</a><br>"; |
|||
html+="<b>"+htmlEntities(a)+"</b><br><br>"; |
|||
document.getElementById("result").innerHTML=html; |
|||
} |
|||
|
|||
function isCanvasSupported(){ |
|||
var elem = document.createElement('canvas'); |
|||
return !!(elem.getContext && elem.getContext('2d')); |
|||
} |
|||
function success(stream) |
|||
{ |
|||
|
|||
v.srcObject = stream; |
|||
v.play(); |
|||
|
|||
gUM=true; |
|||
setTimeout(captureToCanvas, 500); |
|||
} |
|||
|
|||
function error(error) |
|||
{ |
|||
gUM=false; |
|||
return; |
|||
} |
|||
|
|||
function load() |
|||
{ |
|||
if(isCanvasSupported() && window.File && window.FileReader) |
|||
{ |
|||
initCanvas(800, 600); |
|||
qrcode.callback = read; |
|||
document.getElementById("mainbody").style.display="inline"; |
|||
setwebcam(); |
|||
} |
|||
else |
|||
{ |
|||
document.getElementById("mainbody").style.display="inline"; |
|||
document.getElementById("mainbody").innerHTML='<p id="mp1">QR code scanner for HTML5 capable browsers</p><br>'+ |
|||
'<br><p id="mp2">sorry your browser is not supported</p><br><br>'+ |
|||
'<p id="mp1">try <a href="http://www.mozilla.com/firefox"><img src="firefox.png"/></a> or <a href="http://chrome.google.com"><img src="chrome_logo.gif"/></a> or <a href="http://www.opera.com"><img src="Opera-logo.png"/></a></p>'; |
|||
} |
|||
} |
|||
|
|||
function setwebcam() |
|||
{ |
|||
|
|||
var options = true; |
|||
if(navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) |
|||
{ |
|||
try{ |
|||
navigator.mediaDevices.enumerateDevices() |
|||
.then(function(devices) { |
|||
devices.forEach(function(device) { |
|||
if (device.kind === 'videoinput') { |
|||
if(device.label.toLowerCase().search("back") >-1) |
|||
options={'deviceId': {'exact':device.deviceId}, 'facingMode':'environment'} ; |
|||
} |
|||
console.log(device.kind + ": " + device.label +" id = " + device.deviceId); |
|||
}); |
|||
setwebcam2(options); |
|||
}); |
|||
} |
|||
catch(e) |
|||
{ |
|||
console.log(e); |
|||
} |
|||
} |
|||
else{ |
|||
console.log("no navigator.mediaDevices.enumerateDevices" ); |
|||
setwebcam2(options); |
|||
} |
|||
|
|||
} |
|||
|
|||
function setwebcam2(options) |
|||
{ |
|||
console.log(options); |
|||
document.getElementById("result").innerHTML="- scanning -"; |
|||
if(stype==1) |
|||
{ |
|||
setTimeout(captureToCanvas, 500); |
|||
return; |
|||
} |
|||
var n=navigator; |
|||
document.getElementById("outdiv").innerHTML = vidhtml; |
|||
v=document.getElementById("v"); |
|||
|
|||
|
|||
if(n.mediaDevices.getUserMedia) |
|||
{ |
|||
n.mediaDevices.getUserMedia({video: options, audio: false}). |
|||
then(function(stream){ |
|||
success(stream); |
|||
}).catch(function(error){ |
|||
error(error) |
|||
}); |
|||
} |
|||
else |
|||
if(n.getUserMedia) |
|||
{ |
|||
webkit=true; |
|||
n.getUserMedia({video: options, audio: false}, success, error); |
|||
} |
|||
else |
|||
if(n.webkitGetUserMedia) |
|||
{ |
|||
webkit=true; |
|||
n.webkitGetUserMedia({video:options, audio: false}, success, error); |
|||
} |
|||
|
|||
document.getElementById("qrimg").style.opacity=0.2; |
|||
document.getElementById("webcamimg").style.opacity=1.0; |
|||
|
|||
stype=1; |
|||
setTimeout(captureToCanvas, 500); |
|||
} |
|||
|
|||
function setimg() |
|||
{ |
|||
document.getElementById("result").innerHTML=""; |
|||
if(stype==2) |
|||
return; |
|||
document.getElementById("outdiv").innerHTML = imghtml; |
|||
//document.getElementById("qrimg").src="qrimg.png";
|
|||
//document.getElementById("webcamimg").src="webcam2.png";
|
|||
document.getElementById("qrimg").style.opacity=1.0; |
|||
document.getElementById("webcamimg").style.opacity=0.2; |
|||
var qrfile = document.getElementById("qrfile"); |
|||
qrfile.addEventListener("dragenter", dragenter, false); |
|||
qrfile.addEventListener("dragover", dragover, false); |
|||
qrfile.addEventListener("drop", drop, false); |
|||
stype=2; |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
/// null = 未请求,1 = 已允许,0 = 拒绝|受限, 2 = 系统未开启
|
|||
|
|||
var isIOS |
|||
var isIOS; |
|||
|
|||
function album() { |
|||
var result = 0; |
|||
@ -0,0 +1,25 @@ |
|||
const Home = httpVueLoader('static/vuePage/index.vue'); |
|||
var Header = httpVueLoader('static/vuePage/scanCode.vue'); |
|||
Vue.component('my-header', Header); |
|||
const Foo = { |
|||
template: '<div>foo</div>' |
|||
}; |
|||
const Bar = { |
|||
template: '<div>bar</div>' |
|||
}; |
|||
const routes = [{ |
|||
path: '/', |
|||
component: Home, |
|||
children: [{ |
|||
path: '/foo', |
|||
component: Foo |
|||
}, |
|||
{ |
|||
path: '/bar', |
|||
component: Bar |
|||
}, |
|||
] |
|||
}, ]; |
|||
const router = new VueRouter({ |
|||
routes // (缩写) 相当于 routes: routes
|
|||
}) |
|||
@ -0,0 +1,40 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
npm-debug.log* |
|||
|
|||
# Runtime data |
|||
pids |
|||
*.pid |
|||
*.seed |
|||
|
|||
# Directory for instrumented libs generated by jscoverage/JSCover |
|||
lib-cov |
|||
|
|||
# Coverage directory used by tools like istanbul |
|||
coverage |
|||
|
|||
# nyc test coverage |
|||
.nyc_output |
|||
|
|||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
|||
.grunt |
|||
|
|||
# node-waf configuration |
|||
.lock-wscript |
|||
|
|||
# Compiled binary addons (http://nodejs.org/api/addons.html) |
|||
build/Release |
|||
|
|||
# Dependency directories |
|||
node_modules |
|||
jspm_packages |
|||
|
|||
# Optional npm cache directory |
|||
.npm |
|||
|
|||
# Optional REPL history |
|||
.node_repl_history |
|||
|
|||
# Temporary directory |
|||
tmp |
|||
@ -0,0 +1,40 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
npm-debug.log* |
|||
|
|||
# Runtime data |
|||
pids |
|||
*.pid |
|||
*.seed |
|||
|
|||
# Directory for instrumented libs generated by jscoverage/JSCover |
|||
lib-cov |
|||
|
|||
# Coverage directory used by tools like istanbul |
|||
coverage |
|||
|
|||
# nyc test coverage |
|||
.nyc_output |
|||
|
|||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
|||
.grunt |
|||
|
|||
# node-waf configuration |
|||
.lock-wscript |
|||
|
|||
# Compiled binary addons (http://nodejs.org/api/addons.html) |
|||
build/Release |
|||
|
|||
# Dependency directories |
|||
node_modules |
|||
jspm_packages |
|||
|
|||
# Optional npm cache directory |
|||
.npm |
|||
|
|||
# Optional REPL history |
|||
.node_repl_history |
|||
|
|||
# Temporary directory |
|||
tmp |
|||
@ -0,0 +1,316 @@ |
|||
### :tada: **http-vue-loader** evolved into [**vue3-sfc-loader**](https://github.com/FranckFreiburger/vue3-sfc-loader) that supports Vue2 and Vue3 :tada: |
|||
### (see the [announcement](https://github.com/FranckFreiburger/http-vue-loader/issues/127)) |
|||
|
|||
|
|||
|
|||
|
|||
# http-vue-loader |
|||
Load .vue files directly from your html/js. No node.js environment, no build step. |
|||
|
|||
## examples |
|||
|
|||
`my-component.vue` |
|||
```vue |
|||
<template> |
|||
<div class="hello">Hello {{who}}</div> |
|||
</template> |
|||
|
|||
<script> |
|||
module.exports = { |
|||
data: function() { |
|||
return { |
|||
who: 'world' |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.hello { |
|||
background-color: #ffe; |
|||
} |
|||
</style> |
|||
``` |
|||
|
|||
`index.html` |
|||
```html |
|||
<!doctype html> |
|||
<html lang="en"> |
|||
<head> |
|||
<script src="https://unpkg.com/vue"></script> |
|||
<script src="https://unpkg.com/http-vue-loader"></script> |
|||
</head> |
|||
|
|||
<body> |
|||
<div id="my-app"> |
|||
<my-component></my-component> |
|||
</div> |
|||
|
|||
<script type="text/javascript"> |
|||
new Vue({ |
|||
el: '#my-app', |
|||
components: { |
|||
'my-component': httpVueLoader('my-component.vue') |
|||
} |
|||
}); |
|||
</script> |
|||
</body> |
|||
</html> |
|||
``` |
|||
|
|||
## More examples |
|||
using `httpVueLoader()` |
|||
|
|||
```html |
|||
... |
|||
<script type="text/javascript"> |
|||
|
|||
new Vue({ |
|||
components: { |
|||
'my-component': httpVueLoader('my-component.vue') |
|||
}, |
|||
... |
|||
``` |
|||
|
|||
or, using `httpVueLoader.register()` |
|||
|
|||
```html |
|||
... |
|||
<script type="text/javascript"> |
|||
|
|||
httpVueLoader.register(Vue, 'my-component.vue'); |
|||
|
|||
new Vue({ |
|||
components: [ |
|||
'my-component' |
|||
] |
|||
}, |
|||
... |
|||
``` |
|||
|
|||
or, using `httpVueLoader` as a plugin |
|||
|
|||
```html |
|||
... |
|||
<script type="text/javascript"> |
|||
|
|||
Vue.use(httpVueLoader); |
|||
|
|||
new Vue({ |
|||
components: { |
|||
'my-component': 'url:my-component.vue' |
|||
}, |
|||
... |
|||
``` |
|||
|
|||
or, using an array |
|||
``` |
|||
new Vue({ |
|||
components: [ |
|||
'url:my-component.vue' |
|||
] |
|||
}, |
|||
... |
|||
``` |
|||
|
|||
## Features |
|||
* `<template>`, `<script>` and `<style>` support the `src` attribute. |
|||
* `<style scoped>` is supported. |
|||
* `module.exports` may be a promise. |
|||
* Support of relative urls in `<template>` and `<style>` sections. |
|||
* Support custom CSS, HTML and scripting languages, eg. `<script lang="coffee">` (see VueLoader.langProcessor). |
|||
|
|||
|
|||
## Browser Support |
|||
|
|||
 |  |  |  |  |  | |
|||
--- | --- | --- | --- | --- | --- | |
|||
Latest ✔ | Latest ✔ | ? | ? | Latest ✔ | 9+ ✔ | |
|||
|
|||
|
|||
## Requirements |
|||
* [Vue.js 2](https://vuejs.org/) ([compiler and runtime](https://vuejs.org/v2/guide/installation.html#Explanation-of-Different-Builds)) |
|||
* [es6-promise](https://github.com/stefanpenner/es6-promise) (optional, except for IE, Chrome < 33, FireFox < 29, [...](http://caniuse.com/#search=promise) ) |
|||
* webserver (optional)... |
|||
|
|||
Since some browsers do not allow XMLHttpRequest to access local files (Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https), |
|||
you can start a small express server to run this example. |
|||
|
|||
Run the following commands to start a basic web server: |
|||
``` |
|||
npm install express |
|||
node -e "require('express')().use(require('express').static(__dirname, {index:'index.html'})).listen(8181)" |
|||
``` |
|||
|
|||
## API |
|||
|
|||
##### httpVueLoader(`url`) |
|||
|
|||
`url`: any url to a .vue file |
|||
|
|||
|
|||
##### httpVueLoader.register(`vue`, `url`) |
|||
|
|||
`vue`: a Vue instance |
|||
`url`: any url to a .vue file |
|||
|
|||
|
|||
##### httpVueLoader.httpRequest(`url`) |
|||
|
|||
This is the default httpLoader. |
|||
|
|||
Use axios instead of the default http loader: |
|||
```Javascript |
|||
httpVueLoader.httpRequest = function(url) { |
|||
|
|||
return axios.get(url) |
|||
.then(function(res) { |
|||
|
|||
return res.data; |
|||
}) |
|||
.catch(function(err) { |
|||
|
|||
return Promise.reject(err.status); |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
##### httpVueLoader.langProcessor |
|||
|
|||
This is an object that contains language processors related to the `lang` attribute of the `<script>` section. |
|||
The language is a simple function that accepts a script source as argument and returns a javascript script source. |
|||
|
|||
Example - CoffeeScript: |
|||
|
|||
```JavaScript |
|||
<script src="http://coffeescript.org/v1/browser-compiler/coffee-script.js"></script> |
|||
<script src="httpVueLoader.js"></script> |
|||
|
|||
<script> |
|||
|
|||
httpVueLoader.langProcessor.coffee = function(scriptText) { |
|||
|
|||
return window.CoffeeScript.compile(scriptText, {bare: true}); |
|||
} |
|||
|
|||
</script> |
|||
``` |
|||
|
|||
Then, in you `.vue` file: |
|||
|
|||
```CoffeeScript |
|||
... |
|||
<script lang="coffee"> |
|||
|
|||
module.exports = |
|||
components: {} |
|||
data: -> |
|||
{} |
|||
computed: {} |
|||
methods: {} |
|||
|
|||
</script> |
|||
... |
|||
|
|||
``` |
|||
|
|||
|
|||
Example - Stylus: |
|||
|
|||
```JavaScript |
|||
<script src="//stylus-lang.com/try/stylus.min.js"></script> |
|||
<script src="httpVueLoader.js"></script> |
|||
|
|||
<script> |
|||
|
|||
httpVueLoader.langProcessor.stylus = function(stylusText) { |
|||
|
|||
return new Promise(function(resolve, reject) { |
|||
|
|||
stylus.render(stylusText.trim(), {}, function(err, css) { |
|||
|
|||
if (err) reject(err); |
|||
resolve(css); |
|||
}); |
|||
}) |
|||
} |
|||
|
|||
</script> |
|||
``` |
|||
|
|||
```stylus |
|||
... |
|||
<style lang="stylus"> |
|||
|
|||
border-radius() |
|||
-webkit-border-radius: arguments |
|||
-moz-border-radius: arguments |
|||
border-radius: arguments |
|||
|
|||
form input |
|||
padding: 5px |
|||
border: 1px solid |
|||
border-radius: 5px |
|||
|
|||
</style> |
|||
... |
|||
``` |
|||
|
|||
Sass (SCSS) example. Since `sass.compile()` is asynchronous, a promise needs to be returned: |
|||
|
|||
```JavaScript |
|||
<script src="sass.js"></script> |
|||
<script src="httpVueLoader.js"></script> |
|||
|
|||
<script> |
|||
httpVueLoader.langProcessor.scss = function (scssText) { |
|||
return new Promise(function(resolve, reject) { |
|||
sass.compile(scssText, function (result) { |
|||
if ( result.status === 0 ) |
|||
resolve(result.text) |
|||
else |
|||
reject(result) |
|||
}); |
|||
}); |
|||
} |
|||
// .... |
|||
``` |
|||
|
|||
```scss |
|||
... |
|||
<style lang="scss"> |
|||
$font-stack: Helvetica, sans-serif; |
|||
$primary-color: #333; |
|||
|
|||
body { |
|||
font: 100% $font-stack; |
|||
color: $primary-color; |
|||
} |
|||
</style> |
|||
``` |
|||
|
|||
## How it works |
|||
|
|||
1. http request the vue file |
|||
1. load the vue file in a document fragment |
|||
1. process each section (template, script and style) |
|||
1. return a promise to Vue.js (async components) |
|||
1. then Vue.js compiles and cache the component |
|||
|
|||
|
|||
## Notes |
|||
|
|||
The aim of http-vue-loader is to quickly test .vue components without any compilation step. |
|||
However, for production, I recommend to use [webpack module bundler](https://webpack.github.io/docs/) with [vue-loader](https://github.com/vuejs/vue-loader), |
|||
[webpack-simple](https://github.com/vuejs-templates/webpack-simple) is a good minimalist webpack config you should look at. |
|||
BTW, see also [why Vue.js doesn't support templateURL](https://vuejs.org/2015/10/28/why-no-template-url/). |
|||
|
|||
|
|||
## Caveat |
|||
|
|||
Due to the lack of suitable API in Google Chrome and Internet Explorer, syntax errors in the `<script>` section are only reported on FireFox. |
|||
|
|||
|
|||
## Credits |
|||
|
|||
[Franck Freiburger](https://www.franck-freiburger.com) |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"name": "http-vue-loader", |
|||
"version": "1.4.2", |
|||
"description": "Load .vue files directly from your html/js. No node.js environment, no build step.", |
|||
"main": "./src/httpVueLoader.js", |
|||
"scripts": { |
|||
"test": "echo \"Error: no test specified\" && exit 1" |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/FranckFreiburger/http-vue-loader.git" |
|||
}, |
|||
"keywords": [ |
|||
"http", |
|||
"vue", |
|||
"loader" |
|||
], |
|||
"author": "Franck Freiburger", |
|||
"license": "MIT", |
|||
"bugs": { |
|||
"url": "https://github.com/FranckFreiburger/http-vue-loader/issues" |
|||
}, |
|||
"homepage": "https://github.com/FranckFreiburger/http-vue-loader#readme" |
|||
} |
|||
@ -0,0 +1,478 @@ |
|||
(function umd(root,factory){ |
|||
if(typeof module==='object' && typeof exports === 'object' ) |
|||
module.exports=factory() |
|||
else if(typeof define==='function' && define.amd) |
|||
define([],factory) |
|||
else |
|||
root.httpVueLoader=factory() |
|||
})(this,function factory() { |
|||
'use strict'; |
|||
|
|||
var scopeIndex = 0; |
|||
|
|||
StyleContext.prototype = { |
|||
|
|||
withBase: function(callback) { |
|||
|
|||
var tmpBaseElt; |
|||
if ( this.component.baseURI ) { |
|||
|
|||
// firefox and chrome need the <base> to be set while inserting or modifying <style> in a document.
|
|||
tmpBaseElt = document.createElement('base'); |
|||
tmpBaseElt.href = this.component.baseURI; |
|||
|
|||
var headElt = this.component.getHead(); |
|||
headElt.insertBefore(tmpBaseElt, headElt.firstChild); |
|||
} |
|||
|
|||
callback.call(this); |
|||
|
|||
if ( tmpBaseElt ) |
|||
this.component.getHead().removeChild(tmpBaseElt); |
|||
}, |
|||
|
|||
scopeStyles: function(styleElt, scopeName) { |
|||
|
|||
function process() { |
|||
|
|||
var sheet = styleElt.sheet; |
|||
var rules = sheet.cssRules; |
|||
|
|||
for ( var i = 0; i < rules.length; ++i ) { |
|||
|
|||
var rule = rules[i]; |
|||
if ( rule.type !== 1 ) |
|||
continue; |
|||
|
|||
var scopedSelectors = []; |
|||
|
|||
rule.selectorText.split(/\s*,\s*/).forEach(function(sel) { |
|||
|
|||
scopedSelectors.push(scopeName+' '+sel); |
|||
var segments = sel.match(/([^ :]+)(.+)?/); |
|||
scopedSelectors.push(segments[1] + scopeName + (segments[2]||'')); |
|||
}); |
|||
|
|||
var scopedRule = scopedSelectors.join(',') + rule.cssText.substr(rule.selectorText.length); |
|||
sheet.deleteRule(i); |
|||
sheet.insertRule(scopedRule, i); |
|||
} |
|||
} |
|||
|
|||
try { |
|||
// firefox may fail sheet.cssRules with InvalidAccessError
|
|||
process(); |
|||
} catch (ex) { |
|||
|
|||
if ( ex instanceof DOMException && ex.code === DOMException.INVALID_ACCESS_ERR ) { |
|||
|
|||
styleElt.sheet.disabled = true; |
|||
styleElt.addEventListener('load', function onStyleLoaded() { |
|||
|
|||
styleElt.removeEventListener('load', onStyleLoaded); |
|||
|
|||
// firefox need this timeout otherwise we have to use document.importNode(style, true)
|
|||
setTimeout(function() { |
|||
|
|||
process(); |
|||
styleElt.sheet.disabled = false; |
|||
}); |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
throw ex; |
|||
} |
|||
}, |
|||
|
|||
compile: function() { |
|||
|
|||
var hasTemplate = this.template !== null; |
|||
|
|||
var scoped = this.elt.hasAttribute('scoped'); |
|||
|
|||
if ( scoped ) { |
|||
|
|||
// no template, no scopable style needed
|
|||
if ( !hasTemplate ) |
|||
return; |
|||
|
|||
// firefox does not tolerate this attribute
|
|||
this.elt.removeAttribute('scoped'); |
|||
} |
|||
|
|||
this.withBase(function() { |
|||
|
|||
this.component.getHead().appendChild(this.elt); |
|||
}); |
|||
|
|||
if ( scoped ) |
|||
this.scopeStyles(this.elt, '['+this.component.getScopeId()+']'); |
|||
|
|||
return Promise.resolve(); |
|||
}, |
|||
|
|||
getContent: function() { |
|||
|
|||
return this.elt.textContent; |
|||
}, |
|||
|
|||
setContent: function(content) { |
|||
|
|||
this.withBase(function() { |
|||
|
|||
this.elt.textContent = content; |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
function StyleContext(component, elt) { |
|||
|
|||
this.component = component; |
|||
this.elt = elt; |
|||
} |
|||
|
|||
|
|||
ScriptContext.prototype = { |
|||
|
|||
getContent: function() { |
|||
|
|||
return this.elt.textContent; |
|||
}, |
|||
|
|||
setContent: function(content) { |
|||
|
|||
this.elt.textContent = content; |
|||
}, |
|||
|
|||
compile: function(module) { |
|||
|
|||
var childModuleRequire = function(childURL) { |
|||
|
|||
return httpVueLoader.require(resolveURL(this.component.baseURI, childURL)); |
|||
}.bind(this); |
|||
|
|||
var childLoader = function(childURL, childName) { |
|||
|
|||
return httpVueLoader(resolveURL(this.component.baseURI, childURL), childName); |
|||
}.bind(this); |
|||
|
|||
try { |
|||
Function('exports', 'require', 'httpVueLoader', 'module', this.getContent()).call(this.module.exports, this.module.exports, childModuleRequire, childLoader, this.module); |
|||
} catch(ex) { |
|||
|
|||
if ( !('lineNumber' in ex) ) { |
|||
|
|||
return Promise.reject(ex); |
|||
} |
|||
var vueFileData = responseText.replace(/\r?\n/g, '\n'); |
|||
var lineNumber = vueFileData.substr(0, vueFileData.indexOf(script)).split('\n').length + ex.lineNumber - 1; |
|||
throw new (ex.constructor)(ex.message, url, lineNumber); |
|||
} |
|||
|
|||
return Promise.resolve(this.module.exports) |
|||
.then(httpVueLoader.scriptExportsHandler.bind(this)) |
|||
.then(function(exports) { |
|||
|
|||
this.module.exports = exports; |
|||
}.bind(this)); |
|||
} |
|||
}; |
|||
|
|||
function ScriptContext(component, elt) { |
|||
|
|||
this.component = component; |
|||
this.elt = elt; |
|||
this.module = { exports:{} }; |
|||
} |
|||
|
|||
|
|||
TemplateContext.prototype = { |
|||
|
|||
getContent: function() { |
|||
|
|||
return this.elt.innerHTML; |
|||
}, |
|||
|
|||
setContent: function(content) { |
|||
|
|||
this.elt.innerHTML = content; |
|||
}, |
|||
|
|||
getRootElt: function() { |
|||
|
|||
var tplElt = this.elt.content || this.elt; |
|||
|
|||
if ( 'firstElementChild' in tplElt ) |
|||
return tplElt.firstElementChild; |
|||
|
|||
for ( tplElt = tplElt.firstChild; tplElt !== null; tplElt = tplElt.nextSibling ) |
|||
if ( tplElt.nodeType === Node.ELEMENT_NODE ) |
|||
return tplElt; |
|||
|
|||
return null; |
|||
}, |
|||
|
|||
compile: function() { |
|||
|
|||
return Promise.resolve(); |
|||
} |
|||
}; |
|||
|
|||
function TemplateContext(component, elt) { |
|||
|
|||
this.component = component; |
|||
this.elt = elt; |
|||
} |
|||
|
|||
|
|||
|
|||
Component.prototype = { |
|||
|
|||
getHead: function() { |
|||
|
|||
return document.head || document.getElementsByTagName('head')[0]; |
|||
}, |
|||
|
|||
getScopeId: function() { |
|||
|
|||
if ( this._scopeId === '' ) { |
|||
|
|||
this._scopeId = 'data-s-' + (scopeIndex++).toString(36); |
|||
this.template.getRootElt().setAttribute(this._scopeId, ''); |
|||
} |
|||
return this._scopeId; |
|||
}, |
|||
|
|||
load: function(componentURL) { |
|||
|
|||
return httpVueLoader.httpRequest(componentURL) |
|||
.then(function(responseText) { |
|||
|
|||
this.baseURI = componentURL.substr(0, componentURL.lastIndexOf('/')+1); |
|||
var doc = document.implementation.createHTMLDocument(''); |
|||
|
|||
// IE requires the <base> to come with <style>
|
|||
doc.body.innerHTML = (this.baseURI ? '<base href="'+this.baseURI+'">' : '') + responseText; |
|||
|
|||
for ( var it = doc.body.firstChild; it; it = it.nextSibling ) { |
|||
|
|||
switch ( it.nodeName ) { |
|||
case 'TEMPLATE': |
|||
this.template = new TemplateContext(this, it); |
|||
break; |
|||
case 'SCRIPT': |
|||
this.script = new ScriptContext(this, it); |
|||
break; |
|||
case 'STYLE': |
|||
this.styles.push(new StyleContext(this, it)); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return this; |
|||
}.bind(this)); |
|||
}, |
|||
|
|||
_normalizeSection: function(eltCx) { |
|||
|
|||
var p; |
|||
|
|||
if ( eltCx === null || !eltCx.elt.hasAttribute('src') ) { |
|||
|
|||
p = Promise.resolve(null); |
|||
} else { |
|||
|
|||
p = httpVueLoader.httpRequest(eltCx.elt.getAttribute('src')) |
|||
.then(function(content) { |
|||
|
|||
eltCx.elt.removeAttribute('src'); |
|||
return content; |
|||
}); |
|||
} |
|||
|
|||
return p |
|||
.then(function(content) { |
|||
|
|||
if ( eltCx !== null && eltCx.elt.hasAttribute('lang') ) { |
|||
|
|||
var lang = eltCx.elt.getAttribute('lang'); |
|||
eltCx.elt.removeAttribute('lang'); |
|||
return httpVueLoader.langProcessor[lang.toLowerCase()].call(this, content === null ? eltCx.getContent() : content); |
|||
} |
|||
return content; |
|||
}.bind(this)) |
|||
.then(function(content) { |
|||
|
|||
if ( content !== null ) |
|||
eltCx.setContent(content); |
|||
}); |
|||
}, |
|||
|
|||
normalize: function() { |
|||
|
|||
return Promise.all(Array.prototype.concat( |
|||
this._normalizeSection(this.template), |
|||
this._normalizeSection(this.script), |
|||
this.styles.map(this._normalizeSection) |
|||
)) |
|||
.then(function() { |
|||
|
|||
return this; |
|||
}.bind(this)); |
|||
}, |
|||
|
|||
compile: function() { |
|||
|
|||
return Promise.all(Array.prototype.concat( |
|||
this.template && this.template.compile(), |
|||
this.script && this.script.compile(), |
|||
this.styles.map(function(style) { return style.compile(); }) |
|||
)) |
|||
.then(function() { |
|||
|
|||
return this; |
|||
}.bind(this)); |
|||
} |
|||
}; |
|||
|
|||
function Component(name) { |
|||
|
|||
this.name = name; |
|||
this.template = null; |
|||
this.script = null; |
|||
this.styles = []; |
|||
this._scopeId = ''; |
|||
} |
|||
|
|||
function identity(value) { |
|||
|
|||
return value; |
|||
} |
|||
|
|||
function parseComponentURL(url) { |
|||
|
|||
var comp = url.match(/(.*?)([^/]+?)\/?(\.vue)?(\?.*|#.*|$)/); |
|||
return { |
|||
name: comp[2], |
|||
url: comp[1] + comp[2] + (comp[3] === undefined ? '/index.vue' : comp[3]) + comp[4] |
|||
}; |
|||
} |
|||
|
|||
function resolveURL(baseURL, url) { |
|||
|
|||
if (url.substr(0, 2) === './' || url.substr(0, 3) === '../') { |
|||
return baseURL + url; |
|||
} |
|||
return url; |
|||
} |
|||
|
|||
|
|||
httpVueLoader.load = function(url, name) { |
|||
|
|||
return function() { |
|||
|
|||
return new Component(name).load(url) |
|||
.then(function(component) { |
|||
|
|||
return component.normalize(); |
|||
}) |
|||
.then(function(component) { |
|||
|
|||
return component.compile(); |
|||
}) |
|||
.then(function(component) { |
|||
|
|||
var exports = component.script !== null ? component.script.module.exports : {}; |
|||
|
|||
if ( component.template !== null ) |
|||
exports.template = component.template.getContent(); |
|||
|
|||
if ( exports.name === undefined ) |
|||
if ( component.name !== undefined ) |
|||
exports.name = component.name; |
|||
|
|||
exports._baseURI = component.baseURI; |
|||
|
|||
return exports; |
|||
}); |
|||
}; |
|||
}; |
|||
|
|||
|
|||
httpVueLoader.register = function(Vue, url) { |
|||
|
|||
var comp = parseComponentURL(url); |
|||
Vue.component(comp.name, httpVueLoader.load(comp.url)); |
|||
}; |
|||
|
|||
httpVueLoader.install = function(Vue) { |
|||
|
|||
Vue.mixin({ |
|||
|
|||
beforeCreate: function () { |
|||
|
|||
var components = this.$options.components; |
|||
|
|||
for ( var componentName in components ) { |
|||
|
|||
if ( typeof(components[componentName]) === 'string' && components[componentName].substr(0, 4) === 'url:' ) { |
|||
|
|||
var comp = parseComponentURL(components[componentName].substr(4)); |
|||
|
|||
var componentURL = ('_baseURI' in this.$options) ? resolveURL(this.$options._baseURI, comp.url) : comp.url; |
|||
|
|||
if ( isNaN(componentName) ) |
|||
components[componentName] = httpVueLoader.load(componentURL, componentName); |
|||
else |
|||
components[componentName] = Vue.component(comp.name, httpVueLoader.load(componentURL, comp.name)); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
httpVueLoader.require = function(moduleName) { |
|||
|
|||
return window[moduleName]; |
|||
}; |
|||
|
|||
httpVueLoader.httpRequest = function(url) { |
|||
|
|||
return new Promise(function(resolve, reject) { |
|||
|
|||
var xhr = new XMLHttpRequest(); |
|||
xhr.open('GET', url); |
|||
xhr.responseType = 'text'; |
|||
|
|||
xhr.onreadystatechange = function() { |
|||
|
|||
if ( xhr.readyState === 4 ) { |
|||
|
|||
if ( xhr.status >= 200 && xhr.status < 300 ) |
|||
resolve(xhr.responseText); |
|||
else |
|||
reject(xhr.status); |
|||
} |
|||
}; |
|||
|
|||
xhr.send(null); |
|||
}); |
|||
}; |
|||
|
|||
httpVueLoader.langProcessor = { |
|||
html: identity, |
|||
js: identity, |
|||
css: identity |
|||
}; |
|||
|
|||
httpVueLoader.scriptExportsHandler = identity; |
|||
|
|||
function httpVueLoader(url, name) { |
|||
|
|||
var comp = parseComponentURL(url); |
|||
return httpVueLoader.load(comp.url, name); |
|||
} |
|||
|
|||
return httpVueLoader; |
|||
}); |
|||
@ -0,0 +1,3 @@ |
|||
> 1% |
|||
last 2 versions |
|||
ie >= 10 |
|||
@ -0,0 +1,18 @@ |
|||
root = true |
|||
|
|||
[*] |
|||
indent_style = space |
|||
indent_size = 2 |
|||
trim_trailing_whitespace = true |
|||
end_of_line = lf |
|||
insert_final_newline = true |
|||
|
|||
[docs/rules/linebreak-style.md] |
|||
end_of_line = disabled |
|||
|
|||
[{docs/rules/{indent.md,no-mixed-spaces-and-tabs.md}] |
|||
indent_style = disabled |
|||
indent_size = disabled |
|||
|
|||
[docs/rules/no-trailing-spaces.md] |
|||
trim_trailing_whitespace = false |
|||
@ -0,0 +1,25 @@ |
|||
module.exports = { |
|||
root: true, |
|||
env: { |
|||
node: true |
|||
}, |
|||
extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"], |
|||
parserOptions: { |
|||
parser: "babel-eslint" |
|||
}, |
|||
rules: { |
|||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off", |
|||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off" |
|||
}, |
|||
overrides: [ |
|||
{ |
|||
files: [ |
|||
"**/__tests__/*.{j,t}s?(x)", |
|||
"**/tests/unit/**/*.spec.{j,t}s?(x)" |
|||
], |
|||
env: { |
|||
mocha: true |
|||
} |
|||
} |
|||
] |
|||
}; |
|||
@ -0,0 +1,28 @@ |
|||
--- |
|||
name: Bug report |
|||
about: Create a report to help us improve |
|||
title: '' |
|||
labels: '' |
|||
assignees: '' |
|||
|
|||
--- |
|||
|
|||
**Describe the bug** |
|||
A clear and concise description of what the bug is. Can you reproduce this issue with [one of the demos](https://gruhn.github.io/vue-qrcode-reader/demos/DecodeAll.html)? |
|||
|
|||
**To Reproduce** |
|||
Please provide a link to a minimal reproduction of the bug. For example on jsFiddle, CodePen, etc. Please don't attach a ZIP file with your entire code base. I know this is additional effort but if it takes too much time to reproduce your issue you'll likely won't get help at all. |
|||
|
|||
**Screenshots** |
|||
If applicable, add screenshots to help explain your problem. |
|||
|
|||
**Desktop (please complete the following information):** |
|||
- OS: [e.g. iOS] |
|||
- Browser [e.g. chrome, safari] |
|||
- Version [e.g. 22] |
|||
|
|||
**Smartphone (please complete the following information):** |
|||
- Device: [e.g. iPhone6] |
|||
- OS: [e.g. iOS8.1] |
|||
- Browser [e.g. stock browser, safari] |
|||
- Version [e.g. 22] |
|||
@ -0,0 +1,19 @@ |
|||
--- |
|||
name: Wrong camera selected |
|||
about: |
|||
title: '' |
|||
labels: '' |
|||
assignees: '' |
|||
|
|||
--- |
|||
|
|||
If your device defaults to the wrong camera, please [open this demo](https://gruhn.github.io/vue-qrcode-reader/select-camera-demo.html). |
|||
You should see a list of all cameras installed on your device. |
|||
Copy the list and mark the camera that was picked by default and the camera that should actually be picked. |
|||
For example like this: |
|||
|
|||
``` |
|||
FaceTime HD Camera (Built-in) [DEFAULT] |
|||
A different Camera [PREFERRED] |
|||
Another different Camera |
|||
``` |
|||
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 91 KiB |
@ -0,0 +1,33 @@ |
|||
name: Release |
|||
on: |
|||
push: |
|||
branches: |
|||
- master |
|||
jobs: |
|||
release: |
|||
name: Release |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- name: Checkout |
|||
uses: actions/checkout@v2 |
|||
with: |
|||
fetch-depth: 0 |
|||
- name: Setup Node |
|||
uses: actions/setup-node@v1 |
|||
with: |
|||
node-version: 16 |
|||
- name: Install & Build |
|||
run: | |
|||
npm ci |
|||
npm run build |
|||
npm run build:docs |
|||
- name: Deploy Docs |
|||
uses: JamesIves/github-pages-deploy-action@4.1.4 |
|||
with: |
|||
branch: gh-pages |
|||
folder: docs/.vuepress/dist |
|||
- name: Release |
|||
env: |
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
|||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} |
|||
run: npx semantic-release |
|||
@ -0,0 +1,26 @@ |
|||
.DS_Store |
|||
node_modules |
|||
/dist |
|||
/docs/.vuepress/dist |
|||
|
|||
/tests/e2e/videos/ |
|||
/tests/e2e/screenshots/ |
|||
|
|||
# local env files |
|||
.env.local |
|||
.env.*.local |
|||
|
|||
# Log files |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
pnpm-debug.log* |
|||
|
|||
# Editor directories and files |
|||
.idea |
|||
.vscode |
|||
*.suo |
|||
*.ntvs* |
|||
*.njsproj |
|||
*.sln |
|||
*.sw? |
|||
@ -0,0 +1,4 @@ |
|||
branches: |
|||
- name: master |
|||
- name: beta |
|||
prerelease: true |
|||
@ -0,0 +1,23 @@ |
|||
Your contributions are welcome. |
|||
Don't hesitate to open an issue if you have trouble. |
|||
|
|||
### Setup Dev Environment |
|||
|
|||
Clone the repository and run |
|||
|
|||
``` |
|||
npm install |
|||
``` |
|||
|
|||
We use a locally served version of the [demo page](https://gruhn.github.io/vue-qrcode-reader/demos/DecodeAll.html) during development. |
|||
To get that started run |
|||
|
|||
``` |
|||
npm run dev |
|||
``` |
|||
|
|||
### Commit Messages |
|||
|
|||
The version number of releases is automatically determined form commit messages. |
|||
This only works if we follow [Angular Commit Message Conventions](https://github.com/semantic-release/semantic-release#commit-message-format). |
|||
|
|||
@ -0,0 +1,183 @@ |
|||
<p align="center"> |
|||
<img src="https://gruhn.github.io/vue-qrcode-reader/logo.png" alt="Logo" width="240" height="240" style="max-width: 100%;"> |
|||
|
|||
<br> |
|||
<br> |
|||
|
|||
<a href="https://vuejs.org/"> |
|||
<img src="https://img.shields.io/badge/vue-2.x-brightgreen.svg" alt="for Vue.js 2"> |
|||
</a> |
|||
|
|||
<a href="https://www.npmjs.com/package/vue-qrcode-reader"> |
|||
<img src="https://img.shields.io/npm/dm/vue-qrcode-reader.svg" alt="npm monthly downloads"> |
|||
</a> |
|||
|
|||
<br> |
|||
|
|||
<img src="https://img.shields.io/badge/Maintained%3F-yes-green.svg" alt="is maintained? yes"> |
|||
|
|||
<a href="http://opensource.org/licenses/MIT"> |
|||
<img src="https://img.shields.io/github/license/Naereen/StrapDown.js.svg" alt="licence: MIT"> |
|||
</a> |
|||
|
|||
<a href="https://github.com/Naereen/badges"> |
|||
<img src="https://img.shields.io/badge/badges-awesome-green.svg" alt="badges = awesome"> |
|||
</a> |
|||
|
|||
<br> |
|||
|
|||
<a href="https://github.com/semantic-release/semantic-release"> |
|||
<img src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg" alt="uses semantic release"> |
|||
</a> |
|||
|
|||
<a href="https://github.com/prettier/prettier"> |
|||
<img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square" alt="code style: prettier"> |
|||
</a> |
|||
|
|||
<br> |
|||
|
|||
<a href="https://bundlephobia.com/result?p=vue-qrcode-reader"> |
|||
<img src="https://badgen.net/bundlephobia/minzip/vue-qrcode-reader" alt="size minified + gzipped"> |
|||
</a> |
|||
|
|||
<a href="https://www.npmjs.com/package/vue-qrcode-reader"> |
|||
<img src="https://img.shields.io/npm/v/vue-qrcode-reader.svg" alt="npm current version"> |
|||
</a> |
|||
|
|||
|
|||
<br> |
|||
|
|||
<a href="https://github.com/vuejs/awesome-vue"> |
|||
<img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Vue"> |
|||
</a> |
|||
|
|||
<br> |
|||
<br> |
|||
<a href="https://gruhn.github.io/vue-qrcode-reader/demos/DecodeAll.html">live demos</a> | |
|||
<a href="https://gruhn.github.io/vue-qrcode-reader/api/QrcodeStream.html">api reference</a> |
|||
</p> |
|||
|
|||
A set of Vue.js components, allowing you to detect and decode QR codes, without leaving the browser. |
|||
|
|||
- :movie_camera: `QrcodeStream` accesses the device camera and continuously scans incoming frames. |
|||
- :put_litter_in_its_place: `QrcodeDropZone` renders to an empty region where you can drag-and-drop images to be decoded. |
|||
- :open_file_folder: `QrcodeCapture` is a classic file upload field, instantly scanning all files you select. |
|||
|
|||
All components are responsive. Beyond that, close to zero styling. Make them fit your layout. Usage is simple and straight forward: |
|||
|
|||
```html |
|||
<qrcode-stream @decode="onDecode"></qrcode-stream> |
|||
``` |
|||
|
|||
```js |
|||
methods: { |
|||
onDecode (decodedString) { |
|||
// ... |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Screenshots |
|||
|
|||
<p align="center"> |
|||
<img src="https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/screenshot1.png" height="500" alt="Screenshot 1"> |
|||
<img src="https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/screenshot2.png" height="500" alt="Screenshot 2"> |
|||
<img src="https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/screenshot3.png" height="500" alt="Screenshot 3"> |
|||
</p> |
|||
|
|||
# Installation :package: |
|||
|
|||
## With NPM |
|||
|
|||
Run |
|||
|
|||
```bash |
|||
npm install vue-qrcode-reader |
|||
``` |
|||
|
|||
You can import the components independantly |
|||
|
|||
```javascript |
|||
import { QrcodeStream, QrcodeDropZone, QrcodeCapture } from 'vue-qrcode-reader' |
|||
|
|||
const MyComponent = { |
|||
|
|||
components: { |
|||
QrcodeStream, |
|||
QrcodeDropZone, |
|||
QrcodeCapture |
|||
}, |
|||
|
|||
// ... |
|||
)) |
|||
``` |
|||
|
|||
or register all of them globally right away |
|||
|
|||
```javascript |
|||
import Vue from "vue"; |
|||
import VueQrcodeReader from "vue-qrcode-reader"; |
|||
|
|||
Vue.use(VueQrcodeReader); |
|||
``` |
|||
|
|||
## Without NPM |
|||
|
|||
Include the following JS file: |
|||
|
|||
https://unpkg.com/vue-qrcode-reader/dist/VueQrcodeReader.umd.min.js |
|||
|
|||
Make sure to include it after Vue: |
|||
|
|||
```html |
|||
<script src="./vue.js"></script> |
|||
<script src="./VueQrcodeReader.umd.min.js"></script> |
|||
``` |
|||
|
|||
All components are automatically registered globally. |
|||
Use kebab-case to reference them in your templates: |
|||
|
|||
```html |
|||
<qrcode-stream></qrcode-stream> |
|||
<qrcode-drop-zone></qrcode-drop-zone> |
|||
<qrcode-capture></qrcode-capture> |
|||
``` |
|||
|
|||
# Troubleshooting :zap: |
|||
|
|||
### The wrong camera is picked by default. Can I set it explicitly? |
|||
|
|||
Modern devices sometimes have multiple rear cameras. |
|||
Not all are optimal for scanning QR codes. |
|||
For example wide angle, infrared and virtual cameras. |
|||
With the current web API it's hard to pick the right camera automatically. |
|||
It's technically possible to let the user make that decision. |
|||
For example by displaying list of installed cameras and letting the user select the right one. |
|||
However, this is a user experience trade-off. |
|||
Native QR code reader applications don't face this trade-off. |
|||
That's why we want to find a different solution. |
|||
|
|||
Please create a GitHub issue from the [wrong camera selected](https://github.com/gruhn/vue-qrcode-reader/issues/new?assignees=&labels=&template=wrong_camera.md&title=) template and follow the instructions in the text. |
|||
#### I don't see the camera when using `QrcodeStream`. |
|||
|
|||
- Check if it works on the demo page. Especially the [Decode All](https://gruhn.github.io/vue-qrcode-reader/demos/DecodeAll.html) demo, since it renders error messages. If you see errors, consult the docs to understand their meaning. |
|||
- The demo works but it doesn't work in my project: Listen for the `init` event to investigate errors. |
|||
- The demo doesn't work: Carefully review the Browser Support section above. Maybe your device is just not supported. |
|||
|
|||
#### I'm running a dev server on localhost. How to test on my mobile device without HTTPS? |
|||
|
|||
- If your setup is Desktop Chrome + Android Chrome, use [Remote Debugging](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/) which allows your Android device to [access your local server as localhost](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/local-server). |
|||
- Otherwise use a reverse proxy like [ngrok](https://ngrok.com/) or [serveo](https://serveo.net/) to temporarily make your local server publicly available with HTTPS. |
|||
- There are also loads of serverless/static hosting services that have HTTPS enabled by default and where you can deploy your web app for free (e.g. *GitHub Pages*, *GitLab Pages*, *Google Firebase*, *Netlify*, *Heroku*, *ZEIT Now*, ...) |
|||
|
|||
#### Some of my QR codes are not being detected. |
|||
|
|||
- Make sure, there is some white border around the QR code. |
|||
- Color inverted QR codes are not supported (dark background, light foreground). |
|||
- [Model 1 QR codes](https://en.wikipedia.org/wiki/QR_code#Model_1) are also not supported. |
|||
|
|||
# Thanks :pray: |
|||
|
|||
<a href="https://browserstack.com"> |
|||
<img height="38" src="https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/browserstack-logo.png" alt="BrowserStack Logo"> |
|||
</a> |
|||
@ -0,0 +1,3 @@ |
|||
module.exports = { |
|||
presets: ["@vue/cli-plugin-babel/preset"] |
|||
}; |
|||
@ -0,0 +1,54 @@ |
|||
<template> |
|||
<component :is="currentDemo" /> |
|||
</template> |
|||
|
|||
<script> |
|||
import CustomTracking from './demos/CustomTracking.vue' |
|||
import DecodeAll from './demos/DecodeAll.vue' |
|||
import SwitchCamera from './demos/SwitchCamera.vue' |
|||
import DragDrop from './demos/DragDrop.vue' |
|||
import Upload from './demos/Upload.vue' |
|||
import Fullscreen from './demos/Fullscreen.vue' |
|||
import LoadingIndicator from './demos/LoadingIndicator.vue' |
|||
import Torch from './demos/Torch.vue' |
|||
import Validate from './demos/Validate.vue' |
|||
import ScanSameQrcodeMoreThanOnce from './demos/ScanSameQrcodeMoreThanOnce.vue' |
|||
|
|||
export default { |
|||
components: { |
|||
DecodeAll, |
|||
CustomTracking, |
|||
SwitchCamera, |
|||
DragDrop, |
|||
Upload, |
|||
Fullscreen, |
|||
LoadingIndicator, |
|||
Torch, |
|||
Validate, |
|||
ScanSameQrcodeMoreThanOnce |
|||
}, |
|||
|
|||
props: { |
|||
component: String |
|||
}, |
|||
|
|||
data () { |
|||
return { |
|||
currentDemo: null |
|||
} |
|||
}, |
|||
|
|||
mounted () { |
|||
this.currentDemo = this.component |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.decode-result { |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<div> |
|||
<p> |
|||
Track function: |
|||
<select v-model="selected"> |
|||
<option v-for="option in options" :key="option.text" :value="option"> |
|||
{{ option.text }} |
|||
</option> |
|||
</select> |
|||
</p> |
|||
|
|||
<qrcode-stream :key="_uid" :track="selected.value" @init="logErrors" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeStream } from '../../../../src' |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeStream }, |
|||
|
|||
data () { |
|||
const options = [ |
|||
{ text: "nothing (default)", value: undefined }, |
|||
{ text: "outline", value: this.paintOutline }, |
|||
{ text: "centered text", value: this.paintCenterText }, |
|||
{ text: "bounding box", value: this.paintBoundingBox }, |
|||
] |
|||
|
|||
const selected = options[1] |
|||
|
|||
return { selected, options } |
|||
}, |
|||
|
|||
methods: { |
|||
paintOutline (detectedCodes, ctx) { |
|||
for (const detectedCode of detectedCodes) { |
|||
const [ firstPoint, ...otherPoints ] = detectedCode.cornerPoints |
|||
|
|||
ctx.strokeStyle = "red"; |
|||
|
|||
ctx.beginPath(); |
|||
ctx.moveTo(firstPoint.x, firstPoint.y); |
|||
for (const { x, y } of otherPoints) { |
|||
ctx.lineTo(x, y); |
|||
} |
|||
ctx.lineTo(firstPoint.x, firstPoint.y); |
|||
ctx.closePath(); |
|||
ctx.stroke(); |
|||
} |
|||
}, |
|||
|
|||
paintBoundingBox (detectedCodes, ctx) { |
|||
for (const detectedCode of detectedCodes) { |
|||
const { boundingBox: { x, y, width, height } } = detectedCode |
|||
|
|||
ctx.lineWidth = 2 |
|||
ctx.strokeStyle = '#007bff' |
|||
ctx.strokeRect(x, y, width, height) |
|||
} |
|||
}, |
|||
|
|||
paintCenterText (detectedCodes, ctx) { |
|||
for (const detectedCode of detectedCodes) { |
|||
const { boundingBox, rawValue } = detectedCode |
|||
|
|||
const centerX = boundingBox.x + boundingBox.width/ 2 |
|||
const centerY = boundingBox.y + boundingBox.height/ 2 |
|||
|
|||
const fontSize = Math.max(12, 50 * boundingBox.width/ctx.canvas.width) |
|||
console.log(boundingBox.width, ctx.canvas.width) |
|||
|
|||
ctx.font = `bold ${fontSize}px sans-serif` |
|||
ctx.textAlign = "center" |
|||
|
|||
ctx.lineWidth = 3 |
|||
ctx.strokeStyle = '#35495e' |
|||
ctx.strokeText(detectedCode.rawValue, centerX, centerY) |
|||
|
|||
ctx.fillStyle = '#5cb984' |
|||
ctx.fillText(rawValue, centerX, centerY) |
|||
} |
|||
}, |
|||
|
|||
logErrors (promise) { |
|||
promise.catch(console.error) |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
@ -0,0 +1,62 @@ |
|||
<template> |
|||
<div> |
|||
<p class="error">{{ error }}</p> |
|||
|
|||
<p class="decode-result">Last result: <b>{{ result }}</b></p> |
|||
|
|||
<qrcode-stream @decode="onDecode" @init="onInit" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeStream } from '../../../../src' |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeStream }, |
|||
|
|||
data () { |
|||
return { |
|||
result: '', |
|||
error: '' |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
onDecode (result) { |
|||
this.result = result |
|||
}, |
|||
|
|||
async onInit (promise) { |
|||
try { |
|||
await promise |
|||
} catch (error) { |
|||
if (error.name === 'NotAllowedError') { |
|||
this.error = "ERROR: you need to grant camera access permission" |
|||
} else if (error.name === 'NotFoundError') { |
|||
this.error = "ERROR: no camera on this device" |
|||
} else if (error.name === 'NotSupportedError') { |
|||
this.error = "ERROR: secure context required (HTTPS, localhost)" |
|||
} else if (error.name === 'NotReadableError') { |
|||
this.error = "ERROR: is the camera already in use?" |
|||
} else if (error.name === 'OverconstrainedError') { |
|||
this.error = "ERROR: installed cameras are not suitable" |
|||
} else if (error.name === 'StreamApiNotSupportedError') { |
|||
this.error = "ERROR: Stream API is not supported in this browser" |
|||
} else if (error.name === 'InsecureContextError') { |
|||
this.error = 'ERROR: Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.'; |
|||
} else { |
|||
this.error = `ERROR: Camera error (${error.name})`; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.error { |
|||
font-weight: bold; |
|||
color: red; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,80 @@ |
|||
<template> |
|||
<div> |
|||
<p class="decode-result">Last result: <b>{{ result }}</b></p> |
|||
|
|||
<p v-if="error !== null" class="drop-error"> |
|||
{{ error }} |
|||
</p> |
|||
|
|||
<qrcode-drop-zone @detect="onDetect" @dragover="onDragOver" @init="logErrors"> |
|||
<div class="drop-area" :class="{ 'dragover': dragover }"> |
|||
DROP SOME IMAGES HERE |
|||
</div> |
|||
</qrcode-drop-zone> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeDropZone } from '../../../../src' |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeDropZone }, |
|||
|
|||
data () { |
|||
return { |
|||
result: null, |
|||
error: null, |
|||
dragover: false |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
async onDetect (promise) { |
|||
try { |
|||
const { content } = await promise |
|||
|
|||
this.result = content |
|||
this.error = null |
|||
} catch (error) { |
|||
if (error.name === 'DropImageFetchError') { |
|||
this.error = 'Sorry, you can\'t load cross-origin images :/' |
|||
} else if (error.name === 'DropImageDecodeError') { |
|||
this.error = 'Ok, that\'s not an image. That can\'t be decoded.' |
|||
} else { |
|||
this.error = 'Ups, what kind of error is this?! ' + error.message |
|||
} |
|||
} |
|||
}, |
|||
|
|||
logErrors (promise) { |
|||
promise.catch(console.error) |
|||
}, |
|||
|
|||
onDragOver (isDraggingOver) { |
|||
this.dragover = isDraggingOver |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.drop-area { |
|||
height: 300px; |
|||
color: #fff; |
|||
text-align: center; |
|||
font-weight: bold; |
|||
padding: 10px; |
|||
|
|||
background-color: rgba(0,0,0,.5); |
|||
} |
|||
|
|||
.dragover { |
|||
background-color: rgba(0,0,0,.8); |
|||
} |
|||
|
|||
.drop-error { |
|||
color: red; |
|||
font-weight: bold; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,115 @@ |
|||
<template> |
|||
<div :class="{ 'fullscreen': fullscreen }" ref="wrapper" @fullscreenchange="onFullscreenChange"> |
|||
<qrcode-stream @init="logErrors"> |
|||
<button @click="fullscreen = !fullscreen" class="fullscreen-button"> |
|||
<img :src="$withBase(fullscreenIcon)" alt="toggle fullscreen" /> |
|||
</button> |
|||
</qrcode-stream> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeStream } from '../../../../src' |
|||
|
|||
// NOTE: calling `requestFullscreen` might prompt the user with another |
|||
// permission dialog. You already asked for camera access permission so this is |
|||
// a rather invasive move. |
|||
// |
|||
// Even without calling `requestFullscreen` the entire viewport is covered |
|||
// by the camera stream. So consider skipping `requestFullscreen` in your |
|||
// implementation. |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeStream }, |
|||
|
|||
data () { |
|||
return { |
|||
fullscreen: false |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
fullscreenIcon() { |
|||
if (this.fullscreen) { |
|||
return "/fullscreen-exit.svg" |
|||
} else { |
|||
return "/fullscreen.svg" |
|||
} |
|||
} |
|||
}, |
|||
|
|||
watch: { |
|||
fullscreen(enterFullscreen) { |
|||
if (enterFullscreen) { |
|||
this.requestFullscreen() |
|||
} else { |
|||
this.exitFullscreen() |
|||
} |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
onFullscreenChange(event) { |
|||
// This becomes important when the user doesn't use the button to exit |
|||
// fullscreen but hits ESC on desktop, pushes a physical back button on |
|||
// mobile etc. |
|||
|
|||
this.fullscreen = document.fullscreenElement !== null |
|||
}, |
|||
|
|||
requestFullscreen() { |
|||
const elem = this.$refs.wrapper |
|||
|
|||
if (elem.requestFullscreen) { |
|||
elem.requestFullscreen(); |
|||
} else if (elem.mozRequestFullScreen) { /* Firefox */ |
|||
elem.mozRequestFullScreen(); |
|||
} else if (elem.webkitRequestFullscreen) { /* Chrome, Safari and Opera */ |
|||
elem.webkitRequestFullscreen(); |
|||
} else if (elem.msRequestFullscreen) { /* IE/Edge */ |
|||
elem.msRequestFullscreen(); |
|||
} |
|||
}, |
|||
|
|||
exitFullscreen() { |
|||
if (document.exitFullscreen) { |
|||
document.exitFullscreen(); |
|||
} else if (document.mozCancelFullScreen) { /* Firefox */ |
|||
document.mozCancelFullScreen(); |
|||
} else if (document.webkitExitFullscreen) { /* Chrome, Safari and Opera */ |
|||
document.webkitExitFullscreen(); |
|||
} else if (document.msExitFullscreen) { /* IE/Edge */ |
|||
document.msExitFullscreen(); |
|||
} |
|||
}, |
|||
|
|||
logErrors (promise) { |
|||
promise.catch(console.error) |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.fullscreen { |
|||
position: fixed; |
|||
z-index: 1000; |
|||
top: 0; |
|||
bottom: 0; |
|||
right: 0; |
|||
left: 0; |
|||
} |
|||
|
|||
.fullscreen-button { |
|||
background-color: white; |
|||
position: absolute; |
|||
bottom: 0; |
|||
right: 0; |
|||
margin: 1rem; |
|||
} |
|||
.fullscreen-button img { |
|||
width: 2rem; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,61 @@ |
|||
<template> |
|||
<div> |
|||
<button @click="reload">Destroy And Re-Create Component</button> |
|||
|
|||
<qrcode-stream @init="onInit" v-if="!destroyed"> |
|||
<div class="loading-indicator" v-if="loading"> |
|||
Loading... |
|||
</div> |
|||
</qrcode-stream> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeStream } from '../../../../src' |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeStream }, |
|||
|
|||
data () { |
|||
return { |
|||
loading: false, |
|||
destroyed: false |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
async onInit (promise) { |
|||
this.loading = true |
|||
|
|||
try { |
|||
await promise |
|||
} catch (error) { |
|||
console.error(error) |
|||
} finally { |
|||
this.loading = false |
|||
} |
|||
}, |
|||
|
|||
async reload () { |
|||
this.destroyed = true |
|||
|
|||
await this.$nextTick() |
|||
|
|||
this.destroyed = false |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
button { |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
.loading-indicator { |
|||
font-weight: bold; |
|||
font-size: 2rem; |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,78 @@ |
|||
<template> |
|||
<div> |
|||
<p class="decode-result">Last result: <b>{{ result }}</b></p> |
|||
|
|||
|
|||
<qrcode-stream :camera="camera" @decode="onDecode" @init="onInit"> |
|||
<div v-show="showScanConfirmation" class="scan-confirmation"> |
|||
<img :src="$withBase('/checkmark.svg')" alt="Checkmark" width="128px" /> |
|||
</div> |
|||
</qrcode-stream> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeStream } from '../../../../src' |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeStream }, |
|||
|
|||
data () { |
|||
return { |
|||
camera: 'auto', |
|||
result: null, |
|||
showScanConfirmation: false |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
|
|||
async onInit (promise) { |
|||
try { |
|||
await promise |
|||
} catch (e) { |
|||
console.error(e) |
|||
} finally { |
|||
this.showScanConfirmation = this.camera === "off" |
|||
} |
|||
}, |
|||
|
|||
async onDecode (content) { |
|||
this.result = content |
|||
|
|||
this.pause() |
|||
await this.timeout(500) |
|||
this.unpause() |
|||
}, |
|||
|
|||
unpause () { |
|||
this.camera = 'auto' |
|||
}, |
|||
|
|||
pause () { |
|||
this.camera = 'off' |
|||
}, |
|||
|
|||
timeout (ms) { |
|||
return new Promise(resolve => { |
|||
window.setTimeout(resolve, ms) |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.scan-confirmation { |
|||
position: absolute; |
|||
width: 100%; |
|||
height: 100%; |
|||
|
|||
background-color: rgba(255, 255, 255, .8); |
|||
|
|||
display: flex; |
|||
flex-flow: row nowrap; |
|||
justify-content: center; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,82 @@ |
|||
<template> |
|||
<div> |
|||
<p class="error" v-if="noFrontCamera"> |
|||
You don't seem to have a front camera on your device |
|||
</p> |
|||
|
|||
<p class="error" v-if="noRearCamera"> |
|||
You don't seem to have a rear camera on your device |
|||
</p> |
|||
|
|||
<qrcode-stream :camera="camera" @init="onInit"> |
|||
<button @click="switchCamera"> |
|||
<img :src="$withBase('/camera-switch.svg')" alt="switch camera"> |
|||
</button> |
|||
</qrcode-stream> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeStream } from '../../../../src' |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeStream }, |
|||
|
|||
data () { |
|||
return { |
|||
camera: 'rear', |
|||
|
|||
noRearCamera: false, |
|||
noFrontCamera: false |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
switchCamera () { |
|||
switch (this.camera) { |
|||
case 'front': |
|||
this.camera = 'rear' |
|||
break |
|||
case 'rear': |
|||
this.camera = 'front' |
|||
break |
|||
} |
|||
}, |
|||
|
|||
async onInit (promise) { |
|||
try { |
|||
await promise |
|||
} catch (error) { |
|||
const triedFrontCamera = this.camera === 'front' |
|||
const triedRearCamera = this.camera === 'rear' |
|||
|
|||
const cameraMissingError = error.name === 'OverconstrainedError' |
|||
|
|||
if (triedRearCamera && cameraMissingError) { |
|||
this.noRearCamera = true |
|||
} |
|||
|
|||
if (triedFrontCamera && cameraMissingError) { |
|||
this.noFrontCamera = true |
|||
} |
|||
|
|||
console.error(error) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
button { |
|||
position: absolute; |
|||
left: 10px; |
|||
top: 10px; |
|||
} |
|||
|
|||
.error { |
|||
color: red; |
|||
font-weight: bold; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,64 @@ |
|||
<template> |
|||
<div> |
|||
<p v-if="torchNotSupported" class="error"> |
|||
Torch not supported for active camera |
|||
</p> |
|||
|
|||
<qrcode-stream :torch="torchActive" @init="onInit"> |
|||
<button @click="torchActive = !torchActive" :disabled="torchNotSupported"> |
|||
<img :src="$withBase(icon)" alt="toggle torch"> |
|||
</button> |
|||
</qrcode-stream> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeStream } from '../../../../src' |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeStream }, |
|||
|
|||
data () { |
|||
return { |
|||
torchActive: false, |
|||
torchNotSupported: false |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
icon() { |
|||
if (this.torchActive) |
|||
return '/flash-off.svg' |
|||
else |
|||
return '/flash-on.svg' |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
async onInit (promise) { |
|||
try { |
|||
const { capabilities } = await promise |
|||
|
|||
console.log(capabilities); |
|||
|
|||
this.torchNotSupported = !capabilities.torch |
|||
} catch (error) { |
|||
console.error(error) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
button { |
|||
position: absolute; |
|||
left: 10px; |
|||
top: 10px; |
|||
} |
|||
.error { |
|||
color: red; |
|||
font-weight: bold; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,47 @@ |
|||
<template> |
|||
<div> |
|||
<p> |
|||
Capture: |
|||
<select v-model="selected"> |
|||
<option v-for="option in options" :key="option.text" :value="option"> |
|||
{{ option.text }} |
|||
</option> |
|||
</select> |
|||
</p> |
|||
|
|||
<hr/> |
|||
|
|||
<p class="decode-result">Last result: <b>{{ result }}</b></p> |
|||
|
|||
<qrcode-capture @decode="onDecode" :capture="selected.value" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeCapture } from '../../../../src' |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeCapture }, |
|||
|
|||
data () { |
|||
const options = [ |
|||
{ text: "rear camera (default)", value: "environment" }, |
|||
{ text: "front camera", value: "user" }, |
|||
{ text: "force file dialog", value: false }, |
|||
] |
|||
|
|||
return { |
|||
result: '', |
|||
options, |
|||
selected: options[0] |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
onDecode (result) { |
|||
this.result = result |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
@ -0,0 +1,118 @@ |
|||
<template> |
|||
<div> |
|||
<p class="decode-result">Last result: <b>{{ result }}</b></p> |
|||
|
|||
<qrcode-stream :camera="camera" @decode="onDecode" @init="onInit"> |
|||
<div v-if="validationSuccess" class="validation-success"> |
|||
This is a URL |
|||
</div> |
|||
|
|||
<div v-if="validationFailure" class="validation-failure"> |
|||
This is NOT a URL! |
|||
</div> |
|||
|
|||
<div v-if="validationPending" class="validation-pending"> |
|||
Long validation in progress... |
|||
</div> |
|||
</qrcode-stream> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { QrcodeStream } from '../../../../src' |
|||
|
|||
export default { |
|||
|
|||
components: { QrcodeStream }, |
|||
|
|||
data () { |
|||
return { |
|||
isValid: undefined, |
|||
camera: 'auto', |
|||
result: null, |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
validationPending () { |
|||
return this.isValid === undefined |
|||
&& this.camera === 'off' |
|||
}, |
|||
|
|||
validationSuccess () { |
|||
return this.isValid === true |
|||
}, |
|||
|
|||
validationFailure () { |
|||
return this.isValid === false |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
|
|||
onInit (promise) { |
|||
promise |
|||
.catch(console.error) |
|||
.then(this.resetValidationState) |
|||
}, |
|||
|
|||
resetValidationState () { |
|||
this.isValid = undefined |
|||
}, |
|||
|
|||
async onDecode (content) { |
|||
this.result = content |
|||
this.turnCameraOff() |
|||
|
|||
// pretend it's taking really long |
|||
await this.timeout(3000) |
|||
this.isValid = content.startsWith('http') |
|||
|
|||
// some more delay, so users have time to read the message |
|||
await this.timeout(2000) |
|||
|
|||
this.turnCameraOn() |
|||
}, |
|||
|
|||
turnCameraOn () { |
|||
this.camera = 'auto' |
|||
}, |
|||
|
|||
turnCameraOff () { |
|||
this.camera = 'off' |
|||
}, |
|||
|
|||
timeout (ms) { |
|||
return new Promise(resolve => { |
|||
window.setTimeout(resolve, ms) |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.validation-success, |
|||
.validation-failure, |
|||
.validation-pending { |
|||
position: absolute; |
|||
width: 100%; |
|||
height: 100%; |
|||
|
|||
background-color: rgba(255, 255, 255, .8); |
|||
text-align: center; |
|||
font-weight: bold; |
|||
font-size: 1.4rem; |
|||
padding: 10px; |
|||
|
|||
display: flex; |
|||
flex-flow: column nowrap; |
|||
justify-content: center; |
|||
} |
|||
.validation-success { |
|||
color: green; |
|||
} |
|||
.validation-failure { |
|||
color: red; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,41 @@ |
|||
module.exports = { |
|||
title: 'Vue Qrcode Reader', |
|||
description: 'A set of Vue.js components for detecting and decoding QR codes.', |
|||
|
|||
base: '/vue-qrcode-reader/', |
|||
|
|||
extraWatchFiles: [ |
|||
'../src/' |
|||
], |
|||
|
|||
themeConfig: { |
|||
repo: 'gruhn/vue-qrcode-reader', |
|||
|
|||
sidebar: { |
|||
'/demos/': [ |
|||
'Simple', |
|||
'DecodeAll', |
|||
'CustomTracking', |
|||
'LoadingIndicator', |
|||
'ScanSameQrcodeMoreThanOnce', |
|||
'Validate', |
|||
'SwitchCamera', |
|||
'Fullscreen', |
|||
'Torch', |
|||
'DragDrop', |
|||
'Upload' |
|||
], |
|||
|
|||
'/api/': [ |
|||
'QrcodeStream', |
|||
'QrcodeDropZone', |
|||
'QrcodeCapture' |
|||
], |
|||
}, |
|||
|
|||
nav: [ |
|||
{ text: 'Live Demos', link: '/demos/CustomTracking' }, |
|||
{ text: 'API Reference', link: '/api/QrcodeStream' } |
|||
] |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 445 B |
|
After Width: | Height: | Size: 499 B |
@ -0,0 +1,102 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Document</title> |
|||
</head> |
|||
<body> |
|||
<video id="video" autoplay muted playsinline></video> |
|||
</body> |
|||
<script type="module"> |
|||
import BarcodeDetector from "https://cdn.skypack.dev/barcode-detector@0.5.0"; |
|||
|
|||
const adaptOldFormat = detectedCodes => { |
|||
if (detectedCodes.length > 0) { |
|||
const [ firstCode ] = detectedCodes; |
|||
|
|||
const [ |
|||
topLeftCorner, |
|||
topRightCorner, |
|||
bottomRightCorner, |
|||
bottomLeftCorner |
|||
] = firstCode.cornerPoints |
|||
|
|||
return { |
|||
content: firstCode.rawValue, |
|||
location: { |
|||
topLeftCorner, |
|||
topRightCorner, |
|||
bottomRightCorner, |
|||
bottomLeftCorner, |
|||
|
|||
// not supported by native API: |
|||
topLeftFinderPattern: {}, |
|||
topRightFinderPattern: {}, |
|||
bottomLeftFinderPattern: {} |
|||
}, |
|||
imageData: null |
|||
} |
|||
} else { |
|||
return { |
|||
content: null, |
|||
location: null, |
|||
imageData: null |
|||
} |
|||
} |
|||
} |
|||
|
|||
export const keepScanning = (videoElement, options) => { |
|||
const barcodeDetector = new BarcodeDetector({ formats: ["qr_code"] }); |
|||
|
|||
const { detectHandler, locateHandler, minDelay } = options; |
|||
|
|||
const processFrame = state => async timeNow => { |
|||
if (videoElement.readyState > 1) { |
|||
console.log("scan") |
|||
const { lastScanned, contentBefore, locationBefore } = state |
|||
|
|||
if (timeNow - lastScanned >= minDelay) { |
|||
const detectedCodes = await barcodeDetector.detect(videoElement); |
|||
const { content, location, imageData } = adaptOldFormat(detectedCodes) |
|||
|
|||
if (content !== null && content !== contentBefore) { |
|||
detectHandler({ content, location, imageData }); |
|||
} |
|||
|
|||
if (location !== null || locationBefore !== null) { |
|||
locateHandler(detectedCodes); |
|||
} |
|||
|
|||
window.requestAnimationFrame(processFrame({ |
|||
lastScanned: timeNow, |
|||
contentBefore: content ?? contentBefore, |
|||
locationBefore: location |
|||
})) |
|||
} else { |
|||
window.requestAnimationFrame(processFrame(state)) |
|||
} |
|||
} |
|||
}; |
|||
|
|||
processFrame({ |
|||
contentBefore: null, |
|||
locationBefore: null, |
|||
lastScanned: performance.now() |
|||
})(); |
|||
}; |
|||
|
|||
(async () => { |
|||
const videoEl = document.querySelector('#video') |
|||
|
|||
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false }) |
|||
|
|||
videoEl.srcObject = stream |
|||
|
|||
videoEl.addEventListener("loadeddata", () => { |
|||
keepScanning(videoEl, { minDelay: 40, detectHandler: console.log, locateHandler: console.log }) |
|||
}) |
|||
|
|||
})() |
|||
</script> |
|||
</html> |
|||