This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
bytedo
/
wcui
Archived
1
0
Fork 0

init js folder

old
宇天 2017-03-30 01:40:59 +08:00
parent 1b79262cc0
commit b8d2daf02c
42 changed files with 18872 additions and 0 deletions

92
js/lib/avatar/avatar.js Normal file
View File

@ -0,0 +1,92 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2017-03-17 20:55:57
*
*/
"use strict";
// 1216fc0c668c09ade029ceae6db87f97cf560e21
define(function(){
var Avatar = function(){
this.sum = function(arr){
var sum = 0;
arr.forEach(function(it){
sum -= -it
})
}
};
Avatar.prototype = {
get: function(hash, size){
if(!hash)
return this.defafultImg;
if(!size || size < 100){
size = 100
}
var cv = document.createElement('canvas'),
ct = cv.getContext('2d'),
bg = hash.slice(-3),
color = hash.slice(-9, -6),
fixColor = color,
lens = hash.slice(0, 8).match(/([\w]{1})/g),
pos1 = hash.slice(8, 16).match(/([\w]{1})/g),
pos2 = hash.slice(16, 24).match(/([\w]{1})/g),
step = size / 10;
cv.width = size;
cv.height = size;
lens = lens.map(c => {
c = parseInt(c, 16);
return c % 8
})
pos1 = pos1.map(c => {
c = parseInt(c, 16);
return c % 4
})
pos2 = pos2.map(c => {
c = parseInt(c, 16);
return c % 4
})
fixColor = this.sum(lens) > 32 ? bg : color;
ct.fillStyle = '#' + bg;
ct.fillRect(0, 0, size, size)
for(var i = 1; i < 9; i++){
var xl = lens[i-1],
xp1 = pos1[i-1],
xp2 = pos2[i-1];
if(xl + xp1 > 8){
xl = 8 - xp1
}
ct.fillStyle = '#' + color;
ct.fillRect((xp1 + 1) * step, i * step, xl * step, step)
ct.fillStyle = '#' + color;
ct.fillRect((9 - xp1 - xl) * step, i * step, xl * step, step)
ct.fillStyle = '#' + fixColor;
ct.fillRect((xp2 + 1) * step, i * step, step, step)
ct.fillStyle = '#' + fixColor;
ct.fillRect((8 - xp2) * step, i * step, step, step)
}
return cv.toDataURL()
}
}
return new Avatar()
})

BIN
js/lib/avatar/def.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

153
js/lib/count/doui.count.js Normal file
View File

@ -0,0 +1,153 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2016-08-19 10:38:25
*
*/
"use strict";
define(['avalon'], function(av){
av.component('do:count', {
$replace: true,
$template: '<ul class="do-ui-count"><li class="num-box" ms-repeat-obj="list" ms-class="split:obj.opt === 0"><span class="num" ms-repeat="obj.val">{{el}}</span></li></ul>',
maxLen: 8,
speed: 1,
update: av.noop,
list: [],
$list: [],
total: 0,
style: 2,
$construct: function(opt, a, b){
var vm = av.mix(a, b)
document.head.insertAdjacentHTML('afterBegin', '<style>' +
'.do-ui-count {width: 100%;height: 50px;text-align:center;}' +
'.do-ui-count li.num-box {overflow:hidden;display: inline-block;position: relative;width: 40px;height: 50px;margin:0 15px;line-height: 50px;background: #09f;font-size: 30px;}' +
'.do-ui-count li.num-box .num {display: block;width: 100%;height: 50px;margin-top: 0;transition: .3s ease-in-out}' +
'.do-ui-count li.num-box.split {width: auto;margin:0;background: none;}' +
'</style>')
vm.total = vm.total >> 0
vm.maxLen = vm.maxLen || 8
return av.mix(opt, vm)
},
$ready: function(vm, ele){
function updateList(val){
val = numberFormat(val, vm.maxLen)
vm.$list = []
vm.$list = val.split('')
if(vm.style === 2){
vm.$list = vm.$list.reverse()
val = vm.$list.join('').replace(/([\d,]{3})/g, '$1,')
val = val.replace(/^,|,$/g, '')
vm.$list = val.split('').reverse()
}
vm.$list.forEach(function(it, i){
if(it === ','){
if(!vm.list[i])
vm.list.push({opt: 0, val: [it]})
}else{
if(vm.list[i]){
if(it !== vm.list[i].last){
vm.list[i].last = it
vm.list[i].val.push(it)
var curr = ele.querySelectorAll('.num-box')[i]
curr.querySelector('.num').style.marginTop = vm.speed * 50 + 'px'
setTimeout(function(){
vm.list[i].val.shift()
}, 300)
}
}else{
vm.list.push({opt: 1, last: it, val: [it]})
}
}
})
}
updateList(vm.total)
vm.update = function(val){
if(val < 0) //确定滚动方向
vm.speed = 1
else
vm.speed = -1
vm.total = (vm.total - 0) + val
}
vm.$watch('total', function(n, o){
if(n === o)
return
updateList(n)
})
},
})
//数字长度补全前面加0
function numberFormat(num, len){
num += ''
if(num.length >= len)
return num
while(num.length < len)
num = '0' + num
return num
}
return av
})

1
js/lib/count/doui.count.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";define(["avalon"],function(t){function e(t,e){if(t+="",t.length>=e)return t;for(;t.length<e;)t="0"+t;return t}return t.component("do:count",{$replace:!0,$template:'<ul class="do-ui-count"><li class="num-box" ms-repeat-obj="list" ms-class="split:obj.opt === 0"><span class="num" ms-repeat="obj.val">{{el}}</span></li></ul>',maxLen:8,speed:1,update:t.noop,list:[],$list:[],total:0,style:2,$construct:function(e,i,l){var n=t.mix(i,l);return document.head.insertAdjacentHTML("afterBegin","<style>.do-ui-count {width: 100%;height: 50px;text-align:center;}.do-ui-count li.num-box {overflow:hidden;display: inline-block;position: relative;width: 40px;height: 50px;margin:0 15px;line-height: 50px;background: #09f;font-size: 30px;}.do-ui-count li.num-box .num {display: block;width: 100%;height: 50px;margin-top: 0;transition: .3s ease-in-out}.do-ui-count li.num-box.split {width: auto;margin:0;background: none;}</style>"),n.total=n.total>>0,n.maxLen=n.maxLen||8,t.mix(e,n)},$ready:function(t,i){function l(l){l=e(l,t.maxLen),t.$list=[],t.$list=l.split(""),2===t.style&&(t.$list=t.$list.reverse(),l=t.$list.join("").replace(/([\d,]{3})/g,"$1,"),l=l.replace(/^,|,$/g,""),t.$list=l.split("").reverse()),t.$list.forEach(function(e,l){if(","===e)t.list[l]||t.list.push({opt:0,val:[e]});else if(t.list[l]){if(e!==t.list[l].last){t.list[l].last=e,t.list[l].val.push(e);var n=i.querySelectorAll(".num-box")[l];n.querySelector(".num").style.marginTop=50*t.speed+"px",setTimeout(function(){t.list[l].val.shift()},300)}}else t.list.push({opt:1,last:e,val:[e]})})}l(t.total),t.update=function(e){t.speed=0>e?1:-1,t.total=t.total-0+e},t.$watch("total",function(t,e){t!==e&&l(t)})}}),t});

View File

@ -0,0 +1,46 @@
# 日历组件文档
## 配置说明
```json
{
showTime: false, //对话框上显示时间
showCalendar: false, //显示日历对话框
disabled: false, //是否禁用
exclass: '', //输入框拓展样式, 用于外部调整输入框样式以适配各种场景
duplex: '',
format: '', // 日期显示格式
radius: 0, //日历输入框边框圆角半径
border: 1, //日历输入框边框大小
btns: { //切换年份/月份的按钮上的字符
prevYear: '<<',
nextYear: '>>',
prevMonth: '<',
nextMonth: '>'
},
callback: function(date){/*...*/}, //日期被修改后的回调,参数是被修改后的值
}
```
## 用法
```html
<!-- 把do:datepicker 标签放到想要放的地方即可 -->
<section>
<span class="label">示例1:</span>
<do:datepicker config="dp" duplex="ex1" border="1" class="date" radius="3"></do:datepicker>
</section>
<!--
其中config属性是指定日历组件的配置会自动从上一层controller里找,如果该属性为空则自动从上层controller中找与组件同名的属性找不到则使用组件默认配置;
其他的属性(除$id, config, id, class, tabindex, style, ms-*属性,data-*属性外,也可以用以配置组件,且优先级最高);
$id或identifier属性可以设定组件的$id值方便各模块之间进行通讯
-->
<!-- 引入分页组件 -->
<script>
require(['存放路径/doui.datepicker'], function(){
})
</script>
```

View File

@ -0,0 +1,40 @@
@charset "UTF-8";
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2016-02-14 14:00:06
*
*/
.do-ui-datepicker {position:relative;z-index:65534;width:100%;height:100%;}
.do-ui-datepicker a {text-decoration:none;}
.do-ui-datepicker .date-input {float:left;width:100%;height:100%;border:1px solid #ddd;line-height:18px;padding:0 5px;}
.do-ui-datepicker .calendar {position:absolute;z-index:65534;left:0;top:98%;width:230px;height:auto;min-height:60px;padding:10px;border:1px solid #ddd;background:#fff;font-size:13px;box-shadow:0 0 5px rgba(0,0,0,.1)}
.do-ui-datepicker .calendar-hd {width:100%;height:30px;line-height:30px;background:#f3f3f3;color:#666;text-align:center;}
.do-ui-datepicker .calendar-contrl {position:relative;width:90%;height:30px;margin:0 5%;line-height:30px;border-bottom:1px solid #eee;color:#09f;text-align:center;}
.do-ui-datepicker .calendar-contrl a {position:absolute;top:0;left:0;width:30px;color:#09f;}
.do-ui-datepicker .calendar-contrl .prev-month {left:30px;}
.do-ui-datepicker .calendar-contrl .next-month {left:auto;right:30px;}
.do-ui-datepicker .calendar-contrl .next-year {left:auto;right:0;}
.do-ui-datepicker .calendar-table {position:relative;width:90%;height:auto;margin:0 5%;line-height:25px;color:#888;text-align:center;}
.do-ui-datepicker .calendar-table .tr {width:100%;height:auto;min-height:25px;}
.do-ui-datepicker .calendar-table .tr.tr-hd {border-bottom:1px solid #eee;margin-bottom:3px;}
.do-ui-datepicker .calendar-table .tr .td {float:left;width:14.28%;height:25px;}
.do-ui-datepicker .calendar-table .tr .do-st-hand:hover {background:#b6def9;}
.do-ui-datepicker .calendar-table .tr .td.weeken {color:#f30;}
.do-ui-datepicker .calendar-table .tr .td.selected {background:#09f;color:#fff;}
.do-ui-datepicker .calendar-table .tr .td.disabled {color:#ddd;cursor:default;}
.do-ui-datepicker .time-contrl {position:relative;width:90%;height:30px;margin:5px 5%;line-height:30px;border-top:1px solid #eee;color:#888;}
.do-ui-datepicker .time-contrl label {float:left;height:20px;margin:5px 5px 5px 0;}
.do-ui-datepicker .time-contrl input {float:left;width:25px;height:20px;padding:0 3px;line-height:17px;outline:none;}
.do-ui-datepicker .time-contrl label:after {float:right;height:20px;line-height:20px;font-size:12px;}
.do-ui-datepicker .time-contrl .hours:after {content:"时"}
.do-ui-datepicker .time-contrl .minutes:after {content:"分"}
.do-ui-datepicker .time-contrl .seconds:after {content:"秒"}
.do-ui-datepicker .time-contrl .now {float:right;width:40px;height:20px;margin:5px;border:1px solid #ddd;border-radius:3px;line-height:20px;background:#f7f7f7;color:#888;text-align:center;}
.do-ui-datepicker .time-contrl .now:hover {background:#eee;}
.do-ui-datepicker .time-contrl .now:active {background:#e7e7e7;}
.do-ui-datepicker .calendar-tips {position:absolute;z-index:65535;left:25%;top:40%;width:50%;height:30px;line-height:30px;background:rgba(0,0,0,.7);color:#fff;font-size:12px;text-align:center;}

View File

@ -0,0 +1,375 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2016-02-14 13:58:39
*
*/
"use strict";
define(['avalon',
'css!./doui.datepicker.min'
],
function(av){
//父级vm
var parentVm = null
av.component('do:datepicker', {
$replace: true,
$template: '<div class="do-ui-datepicker do-fn-noselect"><input class="date-input" type="text" ms-duplex="dateVal" ms-on-focus="$focus" ms-click="$cancelBubble" ms-attr-disabled="disabled" ms-css="{borderWidth: border, borderRadius: radius}"><div class="calendar" ms-if="showCalendar" ms-click="$cancelBubble"><div class="calendar-hd">请选择日期</div><div class="calendar-contrl"><a href="javascript:;" ms-click="$turn(1, -1)" class="prev-year">{{btns.prevYear}}</a><a href="javascript:;" ms-click="$turn(0, -1)" class="prev-month">{{btns.prevMonth}}</a><a href="javascript:;" ms-click="$turn(0, 1)" class="next-month">{{btns.nextMonth}}</a><a href="javascript:;" ms-click="$turn(1, 1)" class="next-year">{{btns.nextYear}}</a><span class="date-display">{{calendar.year + \'-\' + calendar.month}}</span></div><ul class="calendar-table"><li class="tr tr-hd"><span class="td">日</span><span class="td">一</span><span class="td">二</span><span class="td">三</span><span class="td">四</span><span class="td">五</span><span class="td">六</span></li><li class="tr list do-fn-cl"><span class="td" ms-class="{weeken:el.weeken, \'do-st-hand\': !el.disable, disabled: el.disable, selected: el.selected}" ms-repeat="calendar.list" ms-click="$getDate(el.disable, el.day)">{{el.day}}</span></li></ul><div class="time-contrl" ms-if="showTime"><label class="hours"><input type="text" ms-duplex-time="calendar.hour" data-format="hour"></label><label class="minutes"><input type="text" ms-duplex-time="calendar.minute" data-format="minute"></label><label class="seconds"><input type="text" ms-duplex-time="calendar.second" data-format="second"></label><a href="javascript:;" class="now" ms-click="$now">现在</a></div><span class="calendar-tips" ms-if="tips">{{tips}}</span></div></div>',
$construct: function(opts, b, c){
var vm = av.mix(b, c)
/********** 获取上一级vm ***********/
var pctrl = vm.duplex.slice(0, vm.duplex.indexOf('.'))
parentVm = av.vmodels[pctrl]
/**********************************/
//获取初始值
vm.duplex = vm.duplex.slice(vm.duplex.indexOf('.') + 1)
var defaultVal = (new Function('v', 'return v.' + vm.duplex))(parentVm)
//日期格式化, 默认会调用过滤器的格式'Y-m-d H:i:s'
if(!vm.showTime && !vm.format){
vm.format = 'Y-m-d';
}
if(defaultVal === undefined){
if(vm.minDate){
defaultVal = vm.minDate, vm.format;
}else if(opts.maxDate){
defaultVal = vm.maxDate, vm.format;
}
}
opts.dateVal = defaultVal && av.filters.date(defaultVal, vm.format)
opts.calendar = {
list: [],
year: av.filters.date(opts.dateVal, 'Y'),
month: av.filters.date(opts.dateVal, 'm'),
day: av.filters.date(opts.dateVal, 'd') || 0,
hour: av.filters.date(opts.dateVal, 'H') || 0,
minute: av.filters.date(opts.dateVal, 'i') || 0,
second: av.filters.date(opts.dateVal, 's') || 0,
minYear: vm.minDate && av.filters.date(vm.minDate, 'Y') >> 0,
minMonth: vm.minDate && av.filters.date(vm.minDate, 'm') >> 0,
minDay: vm.minDate && av.filters.date(vm.minDate, 'd') >> 0 || 1,
maxYear: vm.maxDate && av.filters.date(vm.maxDate, 'Y') >> 0,
maxMonth: vm.maxDate && av.filters.date(vm.maxDate, 'm') >> 0,
maxDay: vm.maxDate && av.filters.date(vm.maxDate, 'd') >> 0
}
//移除部分属性
delete vm.minDate;
delete vm.maxDate;
return av.mix(opts, vm)
},
$init: function(vm, ele){
// av.log(vm)
var lastDate = {
year: vm.calendar.year >> 0,
month: vm.calendar.month >> 0,
day: vm.calendar.day >> 0
}
var timer
getCalendar() //初始化日历显示
//日历按钮,未超出限制时切换日历
vm.$turn = function(type, step){
var year = vm.calendar.year >> 0,
month = vm.calendar.month >> 0;
if(type === 1){
year += step;
}else{
month += step;
if(month < 1){
month = 12;
year--
}
if(month > 12){
month = 1;
year++
}
}
if(isLimited(year, month) === true){
vm.tips = '日期超出限制';
return;
}
vm.calendar.year = year;
vm.calendar.month = numberFormat(month, 2);
}
//选择日期
vm.$getDate = function(disabled, day){
if(disabled)
return;
vm.calendar.day = day;
changeStyle(day);
updateTime();
vm.showCalendar = !1;
}
//输入框获取焦点时,显示日历
vm.$focus = function(){
vm.showCalendar = !0;
}
//获取当前时间
vm.$now = function(){
var year = av.filters.date(null, 'Y') >> 0,
month = av.filters.date(null, 'm') >> 0,
day = av.filters.date(null, 'd') >> 0;
var isLimitYM = isLimited(year, month),
disabled = disabledDay(day, isLimitYM);
if(disabled){
vm.tips = '今天超出了限制日期';
return;
}
vm.calendar.year = year;
vm.calendar.month = month;
vm.calendar.day = day;
vm.calendar.hour = av.filters.date(null, 'H');
vm.calendar.minute = av.filters.date(null, 'i');
vm.calendar.second = av.filters.date(null, 's');
changeStyle(day);
updateTime();
vm.showCalendar = !1;
}
/******************************************************************************/
//计算日历数组
function getCalendar(){
var year = vm.calendar.year >> 0
var month = vm.calendar.month >> 0
var nums = getNumsOfYearMonth(year, month)
var numsFixed = -getDayByYearMonth(year, month) + 1
var isLimitYM = isLimited(year, month)
vm.calendar.list.clear();
for(var i = numsFixed; i <= nums; i++){
var day = {
weeken: !1,
day: '',
selected: !1,
disable: !0
}
if(i > 0){
var d = getDayByYearMonth(year, month, i)
day = {
weeken: d == 0 || d == 6,
day: i,
selected: isSelected(i),
disable: disabledDay(i, isLimitYM)
}
}
vm.calendar.list.push(day)
}
}
//判断当前年/月是否超出限制
function isLimited(year, month){
var limit = {
Y: vm.calendar.minYear,
M: vm.calendar.minMonth,
mY: vm.calendar.maxYear,
mM: vm.calendar.maxMonth,
}
var res = ''
if((!limit.Y && !limit.mY) || (!limit.M && !limit.mM))
return false
if(year){
if((limit.Y && year < limit.Y) || (limit.mY && year > limit.mY))
return true
}else{
return false
}
if(month){
if(year === limit.Y){
if(limit.M && month < limit.M)
return true
if(month == limit.M)
res += '-'
}
if(year === limit.mY){
if(limit.mM && month > limit.mM)
return true
if(month == limit.mM)
res += '+'
}
}
return res
}
//判断指定天数是否有效
function disabledDay(day, limitedYM){
var minD = vm.calendar.minDay
var maxD = vm.calendar.maxDay
if(limitedYM === '-')
return day < minD
if(limitedYM === '+')
return maxD && day > maxD
if(limitedYM === '-+')
return day < minD || (maxD && day > maxD)
return limitedYM
}
//判断指定天数是否被选中
function isSelected(day){
var year = vm.calendar.year >> 0
var month = vm.calendar.month >> 0
return !(lastDate.year !== year || lastDate.month !== month || lastDate.day !== day)
}
//修改当前选中日期的样式
function changeStyle(day){
vm.calendar.list.forEach(function(item){
if(item.day != day){
item.selected = !1
}else{
item.selected = !0
}
})
}
//更新时间
function updateTime(){
var cal = vm.calendar
var year = cal.year
var month = cal.month
var day = cal.day
var hour = cal.hour
var minute = cal.minute
var second = cal.second
var date = year + '-' + month + '-' + day
if(vm.showTime){
date += ' ' + hour + ':' + minute + ':' + second
}
lastDate = {
year: year >> 0,
month: month >> 0,
day: day >> 0
}
vm.dateVal = av.filters.date(date, vm.format)
}
/******************************************************************************/
vm.$watch('calendar.year', function(){
getCalendar();
})
vm.$watch('calendar.month', function(){
getCalendar();
})
vm.$watch('calendar.hour', function(v){
vm.calendar.hour = v
updateTime()
})
vm.$watch('calendar.minute', function(v){
vm.calendar.minute = v
updateTime()
})
vm.$watch('calendar.second', function(v){
vm.calendar.second = v
updateTime()
})
vm.$watch('showCalendar', function(v){
if(v || !vm.duplex)
return;
eval('parentVm.' + vm.duplex + ' = "' + vm.dateVal + '"');
vm.callback && vm.callback(vm.dateVal);
})
vm.$watch('tips', function(v){
if(!v)
return;
clearTimeout(timer);
timer = setTimeout(function(){
vm.tips = '';
}, 1500)
})
document.addEventListener('click', function(){
vm.showCalendar = !1;
})
},
showTime: false, //对话框上显示时间
showCalendar: false, //显示日历对话框
disabled: false, //是否禁用
exclass: '', //输入框拓展样式, 用于外部调整输入框样式以适配各种场景
tips: '',
duplex: '',
format: '', // 日期显示格式
dateVal: '',
radius: 0, //日历输入框边框圆角半径
border: 1, //日历输入框边框大小
btns: { //切换年份/月份的按钮上的字符
prevYear: '<<',
nextYear: '>>',
prevMonth: '<',
nextMonth: '>'
},
$focus: av.noop,
$turn: av.noop,
$getDate: av.noop,
$now: av.noop,
$cancelBubble: function(event){
event.stopPropagation && event.stopPropagation() || (event.cancelBubble = true);
},
callback: null, //日期被修改后的回调
})
//获取今年的年份/月份,返回的是数组
function getThisYearMonth(){
var oDate = new Date()
return [oDate.getFullYear(), oDate.getMonth() + 1]
}
//根据年份获取指定月份天数
function getNumsOfYearMonth(year, month){
return new Date(year, month, 0).getDate()
}
//判断指定年月第一天是星期几
function getDayByYearMonth(year, month, day){
day = day || 1
return new Date(year, month - 1, day).getDay()
}
//数字长度补全前面加0
function numberFormat(num, len){
num += ''
if(num.length === len)
return num
while(num.length < len)
num = '0' + num
return num
}
return av
})

View File

@ -0,0 +1 @@
@charset "UTF-8";.do-ui-datepicker{position:relative;z-index:65534;width:100%;height:100%}.do-ui-datepicker a{text-decoration:none}.do-ui-datepicker .date-input{float:left;width:100%;height:100%;border:1px solid #ddd;line-height:18px;padding:0 5px}.do-ui-datepicker .calendar{position:absolute;z-index:65534;left:0;top:98%;width:230px;height:auto;min-height:60px;padding:10px;border:1px solid #ddd;background:#fff;font-size:13px;box-shadow:0 0 5px rgba(0,0,0,.1)}.do-ui-datepicker .calendar-hd{width:100%;height:30px;line-height:30px;background:#f3f3f3;color:#666;text-align:center}.do-ui-datepicker .calendar-contrl{position:relative;width:90%;height:30px;margin:0 5%;line-height:30px;border-bottom:1px solid #eee;color:#09f;text-align:center}.do-ui-datepicker .calendar-contrl a{position:absolute;top:0;left:0;width:30px;color:#09f}.do-ui-datepicker .calendar-contrl .prev-month{left:30px}.do-ui-datepicker .calendar-contrl .next-month{left:auto;right:30px}.do-ui-datepicker .calendar-contrl .next-year{left:auto;right:0}.do-ui-datepicker .calendar-table{position:relative;width:90%;height:auto;margin:0 5%;line-height:25px;color:#888;text-align:center}.do-ui-datepicker .calendar-table .tr{width:100%;height:auto;min-height:25px}.do-ui-datepicker .calendar-table .tr.tr-hd{border-bottom:1px solid #eee;margin-bottom:3px}.do-ui-datepicker .calendar-table .tr .td{float:left;width:14.28%;height:25px}.do-ui-datepicker .calendar-table .tr .do-st-hand:hover{background:#b6def9}.do-ui-datepicker .calendar-table .tr .td.weeken{color:#f30}.do-ui-datepicker .calendar-table .tr .td.selected{background:#09f;color:#fff}.do-ui-datepicker .calendar-table .tr .td.disabled{color:#ddd;cursor:default}.do-ui-datepicker .time-contrl{position:relative;width:90%;height:30px;margin:5px 5%;line-height:30px;border-top:1px solid #eee;color:#888}.do-ui-datepicker .time-contrl label{float:left;height:20px;margin:5px 5px 5px 0}.do-ui-datepicker .time-contrl input{float:left;width:25px;height:20px;padding:0 3px;line-height:17px;outline:0}.do-ui-datepicker .time-contrl label:after{float:right;height:20px;line-height:20px;font-size:12px}.do-ui-datepicker .time-contrl .hours:after{content:"时"}.do-ui-datepicker .time-contrl .minutes:after{content:"分"}.do-ui-datepicker .time-contrl .seconds:after{content:"秒"}.do-ui-datepicker .time-contrl .now{float:right;width:40px;height:20px;margin:5px;border:1px solid #ddd;border-radius:3px;line-height:20px;background:#f7f7f7;color:#888;text-align:center}.do-ui-datepicker .time-contrl .now:hover{background:#eee}.do-ui-datepicker .time-contrl .now:active{background:#e7e7e7}.do-ui-datepicker .calendar-tips{position:absolute;z-index:65535;left:25%;top:40%;width:50%;height:30px;line-height:30px;background:rgba(0,0,0,.7);color:#fff;font-size:12px;text-align:center}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>测试1</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="../../css/base.min.css" rel="stylesheet">
<style>
.do-mod-test {width:800px;height:100px;margin:30px;padding:30px;border:1px solid #ddd;}
.do-mod-test section {float:left;;;;width:35%;height:35px;margin:0 3%;line-height:35px;}
.do-mod-test section:nth-child(2) {border:1px solid #ddd;}
.do-mod-test section .label {float:left;width:35%;height:35px;text-align:center;}
.do-mod-test section .date {float:left;width:65%;}
</style>
</head>
<body>
<div class="do-mod-test" ms-controller="test">
<section>
<span class="label">示例1:</span>
<do:datepicker config="dp" duplex="test.ex1" border="1" class="date" radius="3"></do:datepicker>
</section>
<section>
<span class="label">示例2:</span>
<do:datepicker config="dp" duplex="test.ex2" border="0" show-time="true" class="date"></do:datepicker>
</section>
</div>
<script src="../../avalon.modern.min.js"></script>
<script>
;(function(av){
require(['./datepicker/doui.datepicker'], function(){
var test = av.define({
$id: 'test',
ex1: '2016-01-02',
ex2: '2015-12-06 20:22:12',
dp: {
callback: function(d){
console.log(d)
},
maxDate: '2017-02-05',
format: 'Y 第W周'
}
});
av.scan()
})
})(avalon)
</script>
</body>
</html>

85
js/lib/drag/doc.md Normal file
View File

@ -0,0 +1,85 @@
# 拖拽插件
> 该插件可以让任意一个元素可以被拖拽,而不需要该元素是否具有定位属性。
> 使用时,在目标元素上添加`:drag`属性即可以实现拖拽功能。
## 依赖
> 依赖`yua`框架
## 浏览器兼容性
+ chrome
+ firefox
+ safari
+ IE10+
## 用法
> 只需要在要拖拽的元素上添加`:drag`即可;
> 如果要拖拽的元素不是当前元素,只需要给该属性增加一个值为想要拖拽元素的类名或ID。
> 具体请看示例:
> **注意:** `拖拽的元素不是本身时,只会往父级一级一级找相匹配的`
```html
<!DOCTYPE html>
<html>
<head>
<style>
* {margin:0;padding:0}
.box {width:100px;height:100px;background:#f30;}
</style>
</head>
<body :controller="test">
<div class="box" :drag></div>
<script src="/js/yua.js"></script>
<script>
require(['tool/drag/drag.min'], function(){
yua.define({
$id: 'test'
})
yua.scan()
})
</script>
</body>
</html>
```
```html
<!DOCTYPE html>
<html>
<head>
<style>
* {margin:0;padding:0}
.box {width:200px;height:100px;background:#aaa;}
.box .handle {width:200px;height:30px;background:#f30;}
</style>
</head>
<body :controller="test">
<div class="box">
<div class="handle" :drag="box"></div>
</div>
<script src="/js/yua.js"></script>
<script>
require(['tool/drag/drag.min'], function(){
yua.define({
$id: 'test'
})
yua.scan()
})
</script>
</body>
</html>
```

217
js/lib/drag/drag.js Normal file
View File

@ -0,0 +1,217 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2017-03-29 18:39:35
*
*/
"use strict";
define(['yua'], function(){
function getBindingCallback(elem, name, vmodels) {
var callback = elem.getAttribute(name)
if (callback) {
for (var i = 0, vm; vm = vmodels[i++]; ) {
if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") {
return vm[callback]
}
}
}
}
// 元素拖动
yua.directive('drag', {
priority: 1500,
init: function(binding){
binding.expr = '"' + binding.expr + '"'
yua(binding.element).css('cursor', 'move')
//取得拖动的3种状态回调
//按下,且拖拽之前
binding. beforedrag = getBindingCallback(binding.element, 'data-beforedrag', binding.vmodels)
//拖拽过程
binding.dragging = getBindingCallback(binding.element, 'data-dragging', binding.vmodels)
// 拖拽结束,且释放鼠标
binding.dragged = getBindingCallback(binding.element, 'data-dragged', binding.vmodels)
//获取是否允许溢出可视区
binding.overflow = !!binding.element.dataset.overflow
//方向,x轴, y轴, xy轴
binding.axis = 'xy'
if(!!binding.element.dataset.axis){
binding.axis = binding.element.dataset.axis
delete binding.element.dataset.axis
}
//默认不限制拖拽区域
binding.limit = false
if(!!binding.element.dataset.limit) {
binding.limit = binding.element.dataset.limit
delete binding.element.dataset.limit
}
// 如果不限制,则允许溢出
if(binding.limit === false) {
binding.overflow = true
}
delete binding.element.dataset.overflow
delete binding.element.dataset.beforedrag
delete binding.element.dataset.dragging
delete binding.element.dataset.dragged
},
update: function(val){
var _this = this,
target = val ? this.element.parentNode : this.element,
$drag = yua(this.element),
$doc = yua(document),
$target = null,
parent = null;
// val值不为空时, 获取真正的拖动元素
// 仅从父级上找
while(val && target){
if(target.classList.contains(val) || target.id === val){
break
}else{
target = target.parentNode
}
}
$target = yua(target);
// 限制范围为parent时,获取父级元素
if(this.limit === 'parent'){
parent = target.parentNode
}
var dx,dy,mx,my,ox,oy,tw,th,ww,wh,bst,bsl;
$drag.bind('mousedown', function(ev){
var gcs = getComputedStyle(target),
cst = gcs.transform.replace(/matrix\((.*)\)/, '$1'),
offset = $target.offset();
cst = cst !== 'none' ? cst.split(', ') : [1,0,0,1,0,0]
cst[4] -= 0
cst[5] -= 0
//记录初始的transform位移
dx = cst[4]
dy = cst[5]
//滚动条的偏移
bst = document.body.scrollTop
bsl = document.body.scrollLeft
// 计算元素的offset值, 需要修正
ox = offset.left - dx - bsl
oy = offset.top - dy - bst
mx = ev.pageX //按下鼠标的的坐标值
my = ev.pageY //按下鼠标的的坐标值
// 在按下时才获取窗口大小, 是为了防止人为的改变窗口大小,导致计算不准备
// 同时减少不必要的事件监听(页面上可能会很多可拖动元素)
ww = window.innerWidth;
wh = window.innerHeight;
// 同样,在点击之后获取元素的宽高,可保证获取到的是真实的值
tw = target.clientWidth;
th = target.clientHeight;
//拖拽前回调
if(_this.beforedrag){
_this.beforedrag.call(_this.vmodels, target, ox, oy)
}
//限制区域, 4个值依次是: 上, 下, 左, 右
var limit = [0, wh - th, 0, ww - tw]
if(_this.limit === 'parent') {
var pgcs = getComputedStyle(parent),
pcst = pgcs.transform.replace(/matrix\((.*)\)/, '$1'),
poffset = yua(parent).offset();
pcst = pcst !== 'none' ? pcst.split(', ') : [1,0,0,1,0,0]
var pox = poffset.left - pcst[4] - bsl,
poy = poffset.top - pcst[5] - bst;
limit = [poy, poy + parent.clientHeight - th, pox, pox + parent.clientWidth - tw]
}
var mvfn = $doc.bind('mousemove', function(ev){
//坐标轴限制
if(_this.axis !== 'y'){
cst[4] = ev.pageX - mx + dx
}
if(_this.axis !== 'x'){
cst[5] = ev.pageY - my + dy
}
var fox = ox + cst[4], //修正的offset
foy = oy + cst[5]; //修正的offset
//如果不允许溢出可视区
if(!_this.overflow){
if(_this.axis !== 'y'){
if(fox <= limit[2]) {
fox = limit[2]
//修正矩阵
cst[4] = fox - ox
}
if(fox >= limit[3]){
fox = limit[3]
//修正矩阵
cst[4] = fox - ox
}
}
if(_this.axis !== 'x'){
if(foy <= limit[0]) {
foy = limit[0]
//修正矩阵
cst[5] = foy - oy
}
if(foy >= limit[1]){
foy = limit[1]
//修正矩阵
cst[5] = foy - oy
}
}
}
$target.css({
transform: 'matrix(' + cst.join(', ') + ')'
})
//拖拽过程的回调
if(_this.dragging){
_this.dragging.call(_this.vmodels, target, fox, foy)
}
// 防止拖动到边缘时导致页面滚动
ev.preventDefault()
}),
upfn = $doc.bind('mouseup', function(ev){
$doc.unbind('mousemove', mvfn)
$doc.unbind('mouseup', upfn)
//结束回调
if(_this.dragged){
_this.dragged.call(_this.vmodels, target, fox, foy)
}
});
})
}
})
})

50
js/lib/layer/index.html Normal file
View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="../css/base.min.css" rel="stylesheet">
<style type="text/css">
body {background:url(/123.png) no-repeat;}
.a {display:inline-block;min-width:80px;height:32px;margin:10px;padding:0 10px;text-decoration:none;border:1px solid #ddd;line-height:30px;}
</style>
</head>
<body ms-controller="test">
<a class="a" href="javascript:;" ms-click="layer.alert('测试alert内容')">alert层</a>
<a class="a" href="javascript:;" ms-click="layer.confirm('测试confirm内容')">confirm层</a>
<a class="a" href="javascript:;" ms-click="layer.msg('测试msg内容')">msg层</a>
<a class="a" href="javascript:;" ms-click="layer.prompt('测试prompt内容')">prompt层</a>
<a class="a" href="javascript:;" ms-click="layer.loading('测试loading内容')">loading层</a>
<script src="../avalon.modern.min.js"></script>
<script type="text/javascript">
require(['layer/layer.js'], function(lay){
avalon.define({
$id: 'test'
})
layer.alert('测试loading内容blabla')
layer.confirm('测试loading内容blabla测试loading内容blabla测试loading内容blabla', {offset: {x: '550px', y: '690px'}})
layer.open({content: '测试loading内容blabla', offset: {x: '350px', y: '390px'}})
avalon.scan()
})
</script>
</body>
</html>

135
js/lib/layer/layer.js Normal file
View File

@ -0,0 +1,135 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2016-09-21 01:36:29
*
*/
"use strict";
define(['avalon', 'css!./skin/def'], function(av){
var prep = {
type: {
1: 'alert',
2: 'confirm',
3: 'msg',
4: 'loading',
5: 'iframe',
6: 'tips',
7: 'prompt'
},
init: {
type: 1,
shade: true,
shadeClose: false,
move: '.do-ui-layer-move',
title: '温馨提示',
content: '',
offset: {x: '300px', y: '200px'},
btns: ['确定', '取消'],
yes: av.noop,
no: av.noop,
success: av.noop
}
}
function uuid(type){
type = type || 1
return prep.type[type] + Math.round(Date.now()/1000).toString(16) + Math.random().toString(16).slice(2, 6)
}
var layer = {
alert: function(msg, conf){
conf = conf || {}
return layer.open(av.mix({content: msg}, conf))
},
confirm: function(msg, conf){
conf = conf || {}
return layer.open(av.mix({content: msg}, conf))
},
msg: function(msg, conf){
av.log(msg)
},
loading: function(msg, conf){
av.log(msg)
},
iframe: function(url, conf){
av.log(url)
},
tips: function(msg, conf){
av.log(msg)
},
prompt: function(callback){
av.log('23456')
},
use: function(conf, callback){
require(['css!./skin/' + conf.skin], callback)
}
}
var FN = function(conf){
this.ready(conf)
}
FN.prototype = {
init: {},
create: function(){
var layBox = document.createElement('div')
layBox.className = 'do-ui-layer skin-def'
layBox.style.left = this.init.offset.x
layBox.style.top = this.init.offset.y
layBox.setAttribute('ms-controller', this.init.$id)
layBox.innerHTML = '<div class="layer-title">{{title}}<a class="layer-min deficon"></a><a class="layer-close deficon"></a></div>' +
'<div class="layer-content">' +
'<span class="deficon icon-1"></span>' +
'<div class="detail" ms-html="content"></div>' +
'</div>' +
'<div class="layer-btns">' +
'<a href="javascript:;" class="layer-yes" ms-text="btns[0]"></a>' +
'<a href="javascript:;" class="layer-no" ms-text="btns[1]"></a>' +
'</div>'
return layBox
},
show: function(){
var layBox = this.create()
document.body.appendChild(layBox)
av.define(this.init)
av.scan(layBox)
},
ready: function(conf){
this.init = av.mix({$id: uuid()}, prep.init, conf)
this.show()
},
close: function(){
}
}
layer.open = function(conf){
av.log(conf)
return new FN(conf).init
}
if(!window.layer)
window.layer = layer
return layer
})

49
js/lib/layer/skin/def.css Normal file
View File

@ -0,0 +1,49 @@
@charset "UTF-8";
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2016-09-21 01:36:22
*
*/
.do-ui-layer, .do-ui-layer * {margin: 0;padding: 0;vertical-align: baseline;box-sizing:border-box;}
.do-ui-layer a {text-decoration:none;}
@font-face {font-family: "deficon";
src: url('def.eot?t=1474609176'); /* IE9*/
src: url('def.woff?t=1474609176') format('woff'), /* chrome, firefox */
url('def.ttf?t=1474609176') format('truetype'); /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
}
.do-ui-layer-shade {position:fixed;left:0;top:0;z-index:65534;width:100%;height:100%;background:rgba(255,255,255,.05);}
.do-ui-layer {position:fixed;left:50%;top:230px;z-index:65535;width:auto;height:auto;}
.do-ui-layer.skin-def {min-width:300px;background:#fff;color:#666;font-size:13px;box-shadow:0 0 10px rgba(0,0,0,.3);}
.do-ui-layer.skin-def .deficon {display: inline-block;font-family:"deficon" !important;font-style:normal;-webkit-font-smoothing: antialiased;-webkit-text-stroke-width: 0.2px;-moz-osx-font-smoothing: grayscale;}
.do-ui-layer.skin-def .icon-1:before {content:"\e605";color:#11b330;}
.do-ui-layer.skin-def .icon-2:before {content:"\e602";color:#f30;}
.do-ui-layer.skin-def .icon-3:before {content:"\e603";color:#f30;}
.do-ui-layer.skin-def .icon-4:before {content:"\e604";color:#f30;}
.do-ui-layer.skin-def .icon-5:before {content:"\e608";color:#09f;}
.do-ui-layer.skin-def .icon-6:before {content:"\e609";color:#ee0}
.do-ui-layer.skin-def .icon-7:before {content:"\e60a";color:#63e2c2}
.do-ui-layer.skin-def .icon-8:before {content:"\e606";color:#ee0}
.do-ui-layer.skin-def .icon-9:before {content:"\e607";color:#f30;}
.do-ui-layer.skin-def .layer-title {width:100%;height:40px;padding:0 8px;line-height:40px;background:#f3f3f3;font-size:14px;color:#454545;}
.do-ui-layer.skin-def .layer-min,.do-ui-layer.skin-def .layer-close {position:absolute;display:block;top:10px;width:20px;height:20px;line-height:20px;border:0;text-align:center;cursor:pointer;}
.do-ui-layer.skin-def .layer-min {right:40px;}
.do-ui-layer.skin-def .layer-close {right:10px;}
.do-ui-layer.skin-def .layer-close:hover,.do-ui-layer.skin-def .layer-min:hover {border: 1px solid #ddd;line-height: 18px;}
.do-ui-layer.skin-def .layer-min:before {content:"\e600";}
.do-ui-layer.skin-def .layer-close:before {content:"\e601";}
.do-ui-layer.skin-def .layer-content {width:100%;height:auto;min-height:100px;padding:10px;}
.do-ui-layer.skin-def .layer-content .deficon {float:left;width:60px;height:100%;line-height:50px;font-size:40px;text-align:center;}
.do-ui-layer.skin-def .layer-content .detail {float:left;width:auto;height:100%;margin-top:10px;padding:5px 15px;word-break:break-all;word-wrap: break-word;}
.do-ui-layer.skin-def .layer-btns {width:100%;height:40px;padding:0 5px;line-height:28px;font-size:14px;color:#454545;text-align:right;}
.do-ui-layer.skin-def .layer-btns a {display:inline-block;width:auto;min-width:60px;height:30px;margin:0 5px;padding:0 10px;line-height:28px;border:1px solid #ddd;color:#454545;text-align:center;background:#f3f3f3;}

BIN
js/lib/layer/skin/def.eot Normal file

Binary file not shown.

BIN
js/lib/layer/skin/def.ttf Normal file

Binary file not shown.

BIN
js/lib/layer/skin/def.woff Normal file

Binary file not shown.

27
js/lib/md5/Readme.md Normal file
View File

@ -0,0 +1,27 @@
# md5 加密组件
可对普通字符串和文件计算其对应的md5值。
组件符合AMD规范, 可使用require引入
### demo:
```javascript
require(['./md5'], function(SparkMD5){
var Spark = new SparkMD5()
var md5 = function(cont){
return Spark.sign.call(Spark, cont)
}
var file = /*...*/ //文件表单获取
var fs = new FileReader() // Chrome, IE9+, FF, Safari
fs.readAsBinaryString(file)
fs.onload = function(){ // 计算该文件的md5签名
var sign = md5(this.result)
}
})
```

1
js/lib/md5/md5.js Normal file

File diff suppressed because one or more lines are too long

57
js/lib/pages/ex-1.html Normal file
View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Examples-1</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="../css/base.min.css" rel="stylesheet">
<style>
.do-mod-test {width:800px;height:100px;margin:30px;padding:30px;border:1px solid #ddd;}
.do-mod-test .date-start {width:100%;height:35px;}
</style>
</head>
<body>
<div class="do-mod-test" ms-controller="test">
<div class="pages" ms-widget="pages" data-pages-skin="red"></div>
</div>
<script src="../avalon.modern.min.js"></script>
<script>
;(function(A){
require(['./pages/pages'], function(){
var test = A.define({
$id: 'test',
aa: '112233',
pages: {
callback: function(id){
console.log(id);
},
total: 30
}
});
A.scan();
})
})(avalon);
</script>
</body>
</html>

34
js/lib/pages/pages.css Normal file
View File

@ -0,0 +1,34 @@
.widget-pages .do-ui-pages {width:100%;height:30px;text-align:center;}
.widget-pages.h20 .do-ui-pages {height:20px;}
.widget-pages .do-ui-pages a {display:inline-block;width:auto;min-width:20px;height:30px;padding:0 10px;margin:0 3px;line-height:28px;}
.widget-pages .do-ui-pages .curr,.widget-pages .do-ui-pages .more {line-height:30px;padding:0;border:none!important;background:none!important;color:#666!important;cursor:default}
.widget-pages.h20 .do-ui-pages a {min-width:10px;height:20px;line-height:18px;}
.widget-pages.h20 .do-ui-pages .curr,.widget-pages.h20 .do-ui-pages .more {line-height:20px;}
.widget-pages .do-ui-pages .page-jump {display:inline-block;}
.widget-pages .do-ui-pages .page-jump span,.widget-pages .do-ui-pages .page-jump input{display:inline-block;}
.widget-pages .do-ui-pages .page-jump input {width:25px;height:18px;padding:0 3px;background:none;border:1px solid #ddd;}
.widget-pages.skin-default .do-ui-pages a {border:1px solid #ddd;background:#f3f3f3;color:#666;}
.widget-pages.skin-default .do-ui-pages a:hover {border:1px solid #e3e3e3;background:#e3e3e3;color:#666;}
.widget-pages.skin-default .do-ui-pages a:active {border:1px solid #ccc;background:#ccc;color:#666;}
.widget-pages.skin-blue .do-ui-pages a {border:1px solid #1b9af7;background:#1b9af7;color:#fff;}
.widget-pages.skin-blue .do-ui-pages a:hover {border:1px solid #13b5ff;background:#13b5ff;color:#fff;}
.widget-pages.skin-blue .do-ui-pages a:active {border:1px solid #1682cf;background:#1682cf;color:#fff;}
.widget-pages.skin-red .do-ui-pages a {border:1px solid #ff4351;background:#ff4351;color:#fff;}
.widget-pages.skin-red .do-ui-pages a:hover {border:1px solid #ff7680;background:#ff7680;color:#fff;}
.widget-pages.skin-red .do-ui-pages a:active {border:1px solid #f64c59;background:#f64c59;color:#fff;}
.widget-pages.skin-yellow .do-ui-pages a {border:1px solid #feae1b;background:#feae1b;color:#fff;}
.widget-pages.skin-yellow .do-ui-pages a:hover {border:1px solid #fec04e;background:#fec04e;color:#fff;}
.widget-pages.skin-yellow .do-ui-pages a:active {border:1px solid #f3ab26;background:#f3ab26;color:#fff;}
.widget-pages.skin-green .do-ui-pages a {border:1px solid #a5de37;background:#a5de37;color:#fff;}
.widget-pages.skin-green .do-ui-pages a:hover {border:1px solid #b9e563;background:#b9e563;color:#fff;}
.widget-pages.skin-green .do-ui-pages a:active {border:1px solid #a1d243;background:#a1d243;color:#fff;}
.widget-pages.skin-purple .do-ui-pages a {border:1px solid #7b72e9;background:#7b72e9;color:#fff;}
.widget-pages.skin-purple .do-ui-pages a:hover {border:1px solid #a49ef0;background:#a49ef0;color:#fff;}
.widget-pages.skin-purple .do-ui-pages a:active {border:1px solid #827ae1;background:#827ae1;color:#fff;}

127
js/lib/pages/pages.js Normal file
View File

@ -0,0 +1,127 @@
define(["avalon","text!./pages.tpl","css!./pages.css"], function (av, tpl) {
var widget = av.ui.pages = function(ele, data, vms){
var opts = av.mix({}, data.pagesOptions);
var height = opts.height || '',
skin = opts.skin || 'default';
delete opts.skin;
delete opts.height;
opts.pages = []; //无论是否定义,都会清掉,有点暴力
opts.$id = data.pagesId;
opts.$init = function(scan){
ele.classList.add('widget-pages', 'skin-' + skin, 'do-fn-noselect');
height && ele.classList.add(height);//非空时才添加,避免报错
ele.innerHTML = tpl;
calPages(Pager);
scan()
};
opts.$remove = function() {
ele.innerHTML = ''
};
opts.setUrl = function(id){
if(!Pager.url || id === '...' || Pager.curr === id || id > Pager.total || id < 1)
return 'javascript:;'
return Pager.url.replace('{id}', id)
};
opts.onJump = function(event, id){
event.preventDefault()
id = id >> 0;
jump(id, Pager);
};
opts.jumpPage = function(event){
var pid = Pager.jumpTxt;
if(pid > Pager.total)
Pager.jumpTxt = Pager.total;
if(event.keyCode == 13)
return jump(pid, Pager);
}
var Pager = av.define(opts);
Pager.$watch('total', function(v, old){
Pager.total = v >> 0 || 1; //自动转换成数字类型,防止传入的值为字符串时报错,如 '3'
old = old >> 0;
(v !== old) && calPages(Pager);
})
Pager.$watch('curr', function(v, old){
v = v >> 0 || 1;//自动转换成数字类型
old = old >> 0;
Pager.curr = v;
(v !== old) && calPages(Pager);
})
return Pager;
}
/**
* [calPages 计算要显示的页码数组并赋值给pages]
* @param {[type]} Pager [分页vm对象]
*/
function calPages(Pager){
if(Pager.total < 2){
Pager.pages.clear();
return;
}
var pageArr = [], len = (Pager.curr < Pager.max / 2) ? Pager.max : Math.floor(Pager.max / 2);
if(Pager.curr - len > 1)
pageArr.push('...');
for(var i = Pager.curr - len; i < Pager.curr + len && i <= Pager.total; i++){
if(i > 0)
pageArr.push(i)
}
if(Pager.curr + len < Pager.total)
pageArr.push('...');
Pager.pages = pageArr;
}
/**
* [jump 内部跳转函数]
* @param {[type]} id [要跳转去的页码]
* @param {[type]} Pager [分页vm对象]
*/
function jump(id, Pager){
if(id < 1)
id = Pager.jumpTxt = 1;
if(id > Pager.total)
id = Pager.jumpTxt = Pager.total;
if(Pager.curr === id)
return;
Pager.curr = Pager.jumpTxt = id;
Pager.callback && Pager.callback(id);
calPages(Pager);
}
//默认参数
widget.defaults = {
curr: 1, //当前页
total: 1, // 总页数默认为1即页面上不会显示分页条
max: 5, // 最多显示页码数
url: 'javascript:;', //页码按钮上的url,如'#!/page-{id}.html',其中{id}会被替换成该页码
pageJump: !1, //是否显示跳转表单
simpleMode: !1, //简单模式,即只有上一页和下一页
jumpTxt: 1, //跳转输入框显示的页码
pages: [], //页码数组
btns: { //除页码本身外的按钮上的字符
prev: '<<',
next: '>>',
home: '首页',
end: '末页'
},
callback: null //点击页码/上/下/首/末页的回调,页码无效或者为当前页的时候不会触发
}
return av;
})

1
js/lib/pages/pages.min.css vendored Normal file
View File

@ -0,0 +1 @@
.widget-pages .do-ui-pages{width:100%;height:30px;text-align:center}.widget-pages.h20 .do-ui-pages{height:20px}.widget-pages .do-ui-pages a{display:inline-block;width:auto;min-width:20px;height:30px;padding:0 10px;margin:0 3px;line-height:28px}.widget-pages .do-ui-pages .curr,.widget-pages .do-ui-pages .more{line-height:30px;padding:0;border:none!important;background:none!important;color:#666!important;cursor:default}.widget-pages.h20 .do-ui-pages a{min-width:10px;height:20px;line-height:18px}.widget-pages.h20 .do-ui-pages .curr,.widget-pages.h20 .do-ui-pages .more{line-height:20px}.widget-pages .do-ui-pages .page-jump,.widget-pages .do-ui-pages .page-jump input,.widget-pages .do-ui-pages .page-jump span{display:inline-block}.widget-pages .do-ui-pages .page-jump input{width:25px;height:18px;padding:0 3px;background:0 0;border:1px solid #ddd}.widget-pages.skin-default .do-ui-pages a{border:1px solid #ddd;background:#f3f3f3;color:#666}.widget-pages.skin-default .do-ui-pages a:hover{border:1px solid #e3e3e3;background:#e3e3e3;color:#666}.widget-pages.skin-default .do-ui-pages a:active{border:1px solid #ccc;background:#ccc;color:#666}.widget-pages.skin-blue .do-ui-pages a{border:1px solid #1b9af7;background:#1b9af7;color:#fff}.widget-pages.skin-blue .do-ui-pages a:hover{border:1px solid #13b5ff;background:#13b5ff;color:#fff}.widget-pages.skin-blue .do-ui-pages a:active{border:1px solid #1682cf;background:#1682cf;color:#fff}.widget-pages.skin-red .do-ui-pages a{border:1px solid #ff4351;background:#ff4351;color:#fff}.widget-pages.skin-red .do-ui-pages a:hover{border:1px solid #ff7680;background:#ff7680;color:#fff}.widget-pages.skin-red .do-ui-pages a:active{border:1px solid #f64c59;background:#f64c59;color:#fff}.widget-pages.skin-yellow .do-ui-pages a{border:1px solid #feae1b;background:#feae1b;color:#fff}.widget-pages.skin-yellow .do-ui-pages a:hover{border:1px solid #fec04e;background:#fec04e;color:#fff}.widget-pages.skin-yellow .do-ui-pages a:active{border:1px solid #f3ab26;background:#f3ab26;color:#fff}.widget-pages.skin-green .do-ui-pages a{border:1px solid #a5de37;background:#a5de37;color:#fff}.widget-pages.skin-green .do-ui-pages a:hover{border:1px solid #b9e563;background:#b9e563;color:#fff}.widget-pages.skin-green .do-ui-pages a:active{border:1px solid #a1d243;background:#a1d243;color:#fff}.widget-pages.skin-purple .do-ui-pages a{border:1px solid #7b72e9;background:#7b72e9;color:#fff}.widget-pages.skin-purple .do-ui-pages a:hover{border:1px solid #a49ef0;background:#a49ef0;color:#fff}.widget-pages.skin-purple .do-ui-pages a:active{border:1px solid #827ae1;background:#827ae1;color:#fff}

1
js/lib/pages/pages.min.js vendored Normal file
View File

@ -0,0 +1 @@
define(["avalon","text!./pages.tpl","css!./pages.css"],function(t,a){function e(t){if(t.total<2)return void t.pages.clear();var a=[],e=t.curr<t.max/2?t.max:Math.floor(t.max/2);t.curr-e>1&&a.push("...");for(var r=t.curr-e;r<t.curr+e&&r<=t.total;r++)r>0&&a.push(r);t.curr+e<t.total&&a.push("..."),t.pages=a}function r(t,a){1>t&&(t=a.jumpTxt=1),t>a.total&&(t=a.jumpTxt=a.total),a.curr!==t&&(a.curr=a.jumpTxt=t,a.callback&&a.callback(t),e(a))}var n=t.ui.pages=function(n,u){var c=t.mix({},u.pagesOptions),l=c.height||"",i=c.skin||"default";delete c.skin,delete c.height,c.pages=[],c.$id=u.pagesId,c.$init=function(t){n.classList.add("widget-pages","skin-"+i,"do-fn-noselect"),l&&n.classList.add(l),n.innerHTML=a,e(o),t()},c.$remove=function(){n.innerHTML=""},c.setUrl=function(t){return!o.url||"..."===t||o.curr===t||t>o.total||1>t?"javascript:;":o.url.replace("{id}",t)},c.onJump=function(t,a){t.preventDefault(),a>>=0,r(a,o)},c.jumpPage=function(t){var a=o.jumpTxt;return a>o.total&&(o.jumpTxt=o.total),13==t.keyCode?r(a,o):void 0};var o=t.define(c);return o.$watch("total",function(t,a){o.total=t>>0||1,a>>=0,t!==a&&e(o)}),o.$watch("curr",function(t,a){t=t>>0||1,a>>=0,o.curr=t,t!==a&&e(o)}),o};return n.defaults={curr:1,total:1,max:5,url:"javascript:;",pageJump:!1,simpleMode:!1,jumpTxt:1,pages:[],btns:{prev:"<<",next:">>",home:"首页",end:"末页"},callback:null},t});

13
js/lib/pages/pages.tpl Normal file
View File

@ -0,0 +1,13 @@
<div class="do-ui-pages">
<a class="rd3" ms-if="curr > 1 && !simpleMode" ms-attr="{href: setUrl(1)}" ms-click="onJump($event, 1)">{{btns.home}}</a>
<a class="rd3" ms-if="curr > 1" ms-attr="{href: setUrl(curr - 1)}" ms-click="onJump($event, curr - 1)">{{btns.prev}}</a>
<a class="rd3" ms-if-loop="!simpleMode || curr === el" ms-repeat="pages" ms-attr="{href: setUrl(el)}" ms-class="curr: curr === el" ms-class-1="more: '...' === el" ms-click="onJump($event, el)">{{el}}</a>
<a class="rd3" ms-if="curr < total" ms-attr="{href: setUrl(curr + 1)}" ms-click="onJump($event, curr + 1)">{{btns.next}}</a>
<a class="rd3" ms-if="curr < total && !simpleMode" ms-attr="{href: setUrl(total)}" ms-click="onJump($event, total)">{{btns.end}}</a>
<div class="page-jump" ms-if="pageJump && !simpleMode">
<span>共{{total}}页,跳转到第</span>
<input type="text" ms-duplex-number="jumpTxt" ms-keyup="jumpPage($event)">
<span>页</span>
<a class="rd3" ms-attr="{href: setUrl(jumpTxt)}" ms-click="onJump($event, jumpTxt)">确定</a>
</div>
</div>

1051
js/lib/request/ajax.js Normal file

File diff suppressed because it is too large Load Diff

1
js/lib/request/json.js Normal file
View File

@ -0,0 +1 @@
define(function(){"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(t){return 10>t?"0"+t:t}function this_value(){return this.valueOf()}function quote(t){return rx_escapable.lastIndex=0,rx_escapable.test(t)?'"'+t.replace(rx_escapable,function(t){var e=meta[t];return"string"==typeof e?e:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}function str(t,e){var r,n,o,u,f,a=gap,i=e[t];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(t)),"function"==typeof rep&&(i=rep.call(e,t,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,f=[],"[object Array]"===Object.prototype.toString.apply(i)){for(u=i.length,r=0;u>r;r+=1)f[r]=str(r,i)||"null";return o=0===f.length?"[]":gap?"[\n"+gap+f.join(",\n"+gap)+"\n"+a+"]":"["+f.join(",")+"]",gap=a,o}if(rep&&"object"==typeof rep)for(u=rep.length,r=0;u>r;r+=1)"string"==typeof rep[r]&&(n=rep[r],o=str(n,i),o&&f.push(quote(n)+(gap?": ":":")+o));else for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(o=str(n,i),o&&f.push(quote(n)+(gap?": ":":")+o));return o=0===f.length?"{}":gap?"{\n"+gap+f.join(",\n"+gap)+"\n"+a+"}":"{"+f.join(",")+"}",gap=a,o}}var rx_one=/^[\],:{}\s]*$/,rx_two=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,rx_three=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,rx_four=/(?:^|:|,)(?:\s*\[)+/g,rx_escapable=/[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=this_value,Number.prototype.toJSON=this_value,String.prototype.toJSON=this_value);var gap,indent,meta,rep;"function"!=typeof JSON.stringify&&(meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(t,e,r){var n;if(gap="",indent="","number"==typeof r)for(n=0;r>n;n+=1)indent+=" ";else"string"==typeof r&&(indent=r);if(rep=e,e&&"function"!=typeof e&&("object"!=typeof e||"number"!=typeof e.length))throw new Error("JSON.stringify");return str("",{"":t})}),"function"!=typeof JSON.parse&&(JSON.parse=function(text,reviver){function walk(t,e){var r,n,o=t[e];if(o&&"object"==typeof o)for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(n=walk(o,r),void 0!==n?o[r]=n:delete o[r]);return reviver.call(t,e,o)}var j;if(text=String(text),rx_dangerous.lastIndex=0,rx_dangerous.test(text)&&(text=text.replace(rx_dangerous,function(t){return"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})),rx_one.test(text.replace(rx_two,"@").replace(rx_three,"]").replace(rx_four,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}();})

237
js/lib/request/promise.js Normal file
View File

@ -0,0 +1,237 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2016-11-26 16:35:45
*
*/
"use strict";
define(function(){
var _Promise = function(callback){
this.callback = []
var _this = this
if(typeof this !== 'object')
throw new TypeError('Promises must be constructed via new')
if(typeof callback !== 'function')
throw new TypeError('Argument must be a function')
callback(function(val){
_resolve(_this, val)
}, function(val){
_reject(_this, val)
})
}
var self = {
_state: 1,
_fired: 1,
_val: 1,
callback: 1
}
_Promise.prototype = {
constructor: _Promise,
_state: 'pending',
_fired: false,
_fire: function(yes, no){
if(this._state === 'rejected'){
if(typeof no === 'function')
no(this._val)
else
throw this._val
}else{
if(typeof yes === 'function')
yes(this._val)
}
},
_then: function(yes, no){
if(this._fired){
var _this = this
fireCallback(_this, function(){
_this._fire(yes, no)
})
}else{
this.callback.push({yes: yes, no: no})
}
},
then: function(yes, no){
yes = typeof yes === 'function' ? yes : _yes
no = typeof no === 'function' ? no : _no
var _this = this
var next = new _Promise(function(resolve, reject){
_this._then(function(val){
try{
val = yes(val)
}catch(err){
return reject(err)
}
}, function(val){
try{
val = no(val)
}catch(err){
return reject(err)
}
resolve(val)
})
})
for(var i in _this){
if(!self[i])
next[i] = _this[i]
}
return next
},
done: done,
catch: fail,
fail: fail
}
_Promise.all = function(arr){
return _some(false, arr)
}
_Promise.race = function(arr){
return _some(true, arr)
}
_Promise.defer = defer
// -----------------------------------------------------------
function _yes(val){
return val
}
function _no(err){
throw err
}
function done(callback){
return this.then(callback, _no)
}
function fail(callback){
return this.then(_yes, callback)
}
function defer(){
var obj = {}
obj.promise = new this(function(yes, no){
obj.resolve = yes
obj.reject = no
})
return obj
}
//成功的回调
function _resolve(obj, val){
if(obj._state !== 'pending')
return
if(val && typeof val.then === 'function'){
var method = val instanceof _Promise ? '_then' : 'then'
val[method](function(v){
_transmit(obj, v, true)
}, function(v){
_transmit(obj, v, false)
})
}else{
_transmit(obj, val, true)
}
}
//失败的回调
function _reject(obj, val){
if(obj._state !== 'pending')
return
_transmit(obj, val, false)
}
// 改变Promise的_fired值并保持用户传参触发所有回调
function _transmit(obj, val, isResolved){
obj._fired = true
obj._val = val
obj._state = isResolved ? 'fulfilled' : 'rejected'
fireCallback(obj, function(){
for(var i in obj.callback){
obj._fire(obj.callback[i].yes, obj.callback[i].no)
}
})
}
function fireCallback(obj, callback){
var isAsync = false
if(typeof obj.async === 'boolean')
isAsync = obj.async
else
isAsync = obj.async = true
if(isAsync)
setTimeout(callback, 0)
else
callback()
}
function _some(bool, iterable){
iterable = Array.isArray(iterable) ? iterable : []
var n = 0
var res = []
var end = false
return new _Promise(function(yes, no){
if(!iterable.length)
no(res)
function loop(obj, idx){
obj.then(function(val){
if(!end){
res[idx] = val
n++
if(bool || n >= iterable.length){
yes(bool ? val : res)
end = true
}
}
}, function(val){
end = true
no(val)
})
}
for(var i = 0, len = iterable.length; i < len; i++){
loop(iterable[i], i)
}
})
}
// ---------------------------------------------------------------
var nativePromise = window.Promise
if(/native code/.test(nativePromise)){
nativePromise.prototype.done = done
nativePromise.prototype.fail = fail
if(!nativePromise.defer)
nativePromise.defer = defer
}
return window.Promise = nativePromise || _Promise
})

0
js/lib/request/promise.min.js vendored Normal file
View File

View File

@ -0,0 +1,739 @@
/**
* Request组件, modern版, 支持IE9+,chrome,FF
* @authors yutent (yutent@doui.cc)
* @date 2016-11-27 13:08:40
*
*/
"use strict";
define(function(){
var _request = function(url, protocol){
this.transport = true
protocol = (protocol + '').trim().toUpperCase()
this.xhr = Xhr()
this.opt = {
url: (url + '').trim(),
type: protocol || 'GET',
form: '',
headers: {},
timeoutID: 0,
uuid: Math.random().toString(16).substr(2)
}
},
_requestp = _request.prototype,
toS = Object.prototype.toString,
win = window,
doc = win.document,
encode = encodeURIComponent,
decode = decodeURIComponent,
noop = function(e, res){
if(e)
throw new Error(e + '')
};
// -----------------------------
// 本地协议判断正则
var rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/
var isLocal = false
try{
isLocal = rlocalProtocol.test(location.protocol)
}catch(e){}
var rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg
// ----------------- 一些兼容性预处理 --------------------
win.Xhr = function(){
return new XMLHttpRequest()
}
// var supportCors = 'withCredentials' in Xhr()
// ------------------- 几个解释方法 -----------------------
var Format = function(){
this.tagHooks = new function(){
this.option = doc.createElement('select')
this.thead = doc.createElement('table')
this.td = doc.createElement('tr')
this.area = doc.createElement('map')
this.tr = doc.createElement('tbody')
this.col = doc.createElement('colgroup')
this.legend = doc.createElement('fieldset')
this._default = doc.createElement('div')
this.g = doc.createElementNS('http://www.w3.org/2000/svg', 'svg')
this.optgroup = this.option
this.tbody = this.tfoot = this.colgroup = this.caption = this.thead
this.th = this.td
};
var _this = this
'circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use'.replace(/,/g, function(m){
_this.tagHooks[m] = _this.tagHooks.g //处理svg
})
this.rtagName = /<([\w:]+)/
this.rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig
this.scriptTypes = {
'text/javascript': 1,
'text/ecmascript': 1,
'application/ecmascript': 1,
'application/javascript': 1
}
this.rhtml = /<|&#?\w+;/
}
function serialize(p, obj, q){
var k
if(Array.isArray(obj)){
obj.forEach(function(it, i){
k = p ? (p + '[' + (Array.isArray(it) ? i : '') + ']') : i
if(typeof it === 'object'){
serialize(k, it, q)
}else{
q(k, it)
}
})
}else{
for(var i in obj){
k = p ? (p + '[' + i + ']') : i
if(typeof obj[i] === 'object'){
serialize(k, obj[i], q)
}else{
q(k, obj[i])
}
}
}
}
Format.prototype = {
parseJS: function(code){
code = (code + '').trim()
if(code){
if(code.indexOf('use strict') === 1){
var script = doc.createElement('script')
script.text = code
doc.head
.appendChild(script)
.parentNode
.removeChild(script)
}else{
eval(code)
}
}
},
parseXML: function(data, xml, tmp){
try{
tmp = new DOMParser();
xml = tmp.parseFromString(data, 'text/xml');
}catch(e){
xml = void 0;
}
if(!xml ||
!xml.documentElement ||
xml.getElementsByTagName('parsererror').length){
console.error('Invalid XML: ' + data)
}
return xml
},
parseHTML: function (html){
var fragment = (doc.createDocumentFragment()).cloneNode(false)
if(typeof html !== 'string')
return fragment
if(!this.rhtml.test(html)){
fragment.appendChild(document.createTextNode(html))
return fragment
}
html = html.replace(this.rxhtml, '<$1></$2>').trim()
var tag = (this.rtagName.exec(html) || ['', ''])[1].toLowerCase()
var wrap = this.tagHooks[tag] || this.tagHooks._default
var firstChild = null
//使用innerHTML生成的script节点不会触发请求与执行text属性
wrap.innerHTML = html
var script = wrap.getElementsByTagName('script')
if(script.length){
for(var i = 0, el; el = script[i++];){
if(this.scriptTypes[el.type]){
var tmp = (doc.createElement("script")).cloneNode(false)
el.attributes.forEach(function(attr){
tmp.setAttribute(attr.name, attr.value)
})
tmp.text = el.text
el.parentNode.replaceChild(tmp, el)
}
}
}
while(firstChild = wrap.firstChild){
fragment.appendChild(firstChild)
}
return fragment
},
param: function(obj){
if(!obj || typeof obj === 'string' || typeof obj === 'number')
return obj
var arr = []
var q = function(k, v){
if(/native code/.test(v))
return
v = (typeof v === 'function') ? v() : v
v = (toS.call(v) !== '[object File]') ? encode(v) : v
arr.push(encode(k) + '=' + v)
}
if(typeof obj === 'object')
serialize('', obj, q)
return arr.join('&')
},
parseForm: function(form){
var data = {}
for(var i = 0,field; field = form.elements[i++];){
switch(field.type){
case 'select-one':
case 'select-multiple':
if(field.name.length && !field.disabled){
for(var j = 0, opt;opt = field.options[j++];){
if(opt.selected){
data[field.name] = opt.value || opt.text
}
}
}
break;
case 'file':
if(field.name.length && !field.disabled){
data[field.name] = field.files[0]
}
break;
case undefined:
case 'submit':
case 'reset':
case 'button':
break; //按钮啥的, 直接忽略
case 'radio':
case 'checkbox':
// 只处理选中的
if(!field.checked)
break;
default:
if(field.name.length && !field.disabled){
data[field.name] = field.value
}
}
}
return data
},
merge: function(a, b){
if(typeof a !== 'object' || typeof b !== 'object')
throw new TypeError('argument must be an object')
if(Object.assign)
return Object.assign(a, b)
for(var i in b){
a[i] = b[i]
}
return a
}
}
var F = new Format()
// ---------------------------------------------------------
// -------------------- request 模块开始 --------------------
// ---------------------------------------------------------
var requestConvert = {
text: function(val){
return val
},
xml: function(val, xml){
return xml !== undefined ? xml : F.parseXML(val)
},
html: function(val){
return F.parseHTML(val)
},
json: function(val){
return JSON.parse(val)
},
script: function(val){
return F.parseJS(val)
},
jsonp: function(name){
var json = request.cache[name]
delete request.cache[name];
return json
}
}
var requestExtend = {
formData: function(){
if(this.opt.form){
var data = F.parseForm(this.opt.form)
F.merge(this.opt.data, data)
}
var form = new FormData()
for(var i in this.opt.data){
var el = this.opt.data[i]
if(Array.isArray(el)){
el.forEach(function(it){
form.append(i + '[]', it)
})
}else{
form.append(i, this.opt.data[i])
}
}
return form
},
jsonp: function(jsonpcallback){
win[jsonpcallback] = function(val){
delete win[jsonpcallback]
request.cache[jsonpcallback] = val
}
},
dispatch: function(self){
if(!this.transport)
return
var _this = this,
result = {
response: {
url: this.opt.url,
headers: {'content-type': ''}
},
request: {
url: this.opt.url,
headers: _this.opt.headers
},
status: self === null ? 504 : 200,
statusText: self === null ? 'Connected timeout' : 'ok',
text: '',
body: '',
error: null
};
//状态为4,既已成功, 则清除超时
clearTimeout(_this.opt.timeoutID);
if(typeof this.transport === 'object'
&& this.opt.type === 'JSONP'){
//移除script
// this.transport.parentNode.removeChild(this.transport);
//超时返回
if(self !== null){
var exec = !this.transport.readyState
|| this.transport.readyState === 'loaded'
|| this.transport.readyState === 'complete';
if(exec){
result.body = requestConvert.jsonp(this.opt.data.callback)
result.text = JSON.stringify(result.body)
}
}
this.callback(result.error, result)
}else{
//成功的回调
var isSucc = self ? ((self.status >= 200 && self.status < 300) || self.status === 304) : false,
headers = self && self.getAllResponseHeaders().split('\n') || [];
//处理返回的Header
headers.forEach(function(it, i){
it = it.trim()
if(it){
it = it.split(':')
result.response.headers[it.shift().toLowerCase()] = it.join(':').trim()
}
});
if(isSucc){
result.status = self.status
if(result.status === 204){
result.statusText = 'no content'
}else if(result.status === 304){
result.statusText = 'not modified'
}
}else{
result.status = self === null ? 504 : (self.status || 500)
result.statusText = self === null ? 'Connected timeout' : (self.statusText || 'Internal Server Error')
result.error = F.merge(new Error(result.statusText), {status: result.status})
}
try{
//处理返回的数据
var dataType = result.response.headers['content-type'].match(/json|xml|script|html/i) || ['text']
dataType = dataType[0].toLowerCase()
result.text = self && (self.responseText || self.responseXML) || ''
result.body = requestConvert[dataType](result.text, self && self.responseXML)
}catch(err){
result.error = err
result.statusText = 'parse error'
}
_this.callback(result.error, result)
}
delete _this.transport;
delete _this.opt
delete _this.xhr
}
}
// 设置表单类型, 支持2种, form/json
_requestp.type = function(t){
if(this.opt.formType === 'form-data')
return this
this.opt.formType = t || 'form'
if(t === 'form' || this.opt.type === 'GET')
this.set('content-type', 'application/x-www-form-urlencoded; charset=UTF-8')
else
this.set('content-type', 'application/json; charset=UTF-8')
return this
}
//设置头信息
_requestp.set = function(k, val){
if(!this.transport)
return
if(typeof k === 'object'){
for(var i in k){
i = i.toLowerCase()
this.opt.headers[i] = k[i]
}
}else if(typeof k === 'string'){
if(arguments.length < 2)
throw new Error('2 arguments required')
// 全转小写,避免重复写入
k = k.toLowerCase()
if(val === undefined)
delete this.opt.headers[k]
else
this.opt.headers[k] = val
}else{
throw new Error('arguments must be string/object, but [' + (typeof k) + '] given')
}
return this
}
//设置请求参数
_requestp.send = function(k, val){
if(!this.transport)
return
// 1. send方法可以多次调用, 但必须保证格式一致
// 2. 2次圴提交纯字符串也会抛出异常
if(typeof k === 'object'){
if(this.opt.data && (typeof this.opt.data === 'string'))
throw new Error('param can not be string and object at the same time')
if(!this.opt.data)
this.opt.data = {}
F.merge(this.opt.data, k)
}else{
if(typeof k === 'string'){
if(arguments.length === 1){
if(this.opt.data)
throw new Error('invalid param in function send')
this.opt.data = k
}else{
if(this.opt.data && (typeof this.opt.data === 'string'))
throw new Error('param can not be string and object at the same time')
if(!this.opt.data)
this.opt.data = {}
this.opt.data[k] = val
}
}else{
throw new Error('argument of send must be string/object, but [' + (typeof k) + '] given')
}
}
return this
}
//该方法用于 form-data类型的post请求的参数设置
_requestp.field = function(k, val){
if(!this.transport)
return
// 此类型优先级最高
this.opt.formType = 'form-data'
if(!this.opt.data || (this.opt.data && typeof this.opt.data !== 'object'))
this.opt.data = {}
if(arguments.length === 1 && typeof k === 'object'){
F.merge(this.opt.data, k)
}else if(arguments.length === 2){
this.opt.data[k] = val
}else{
throw new TypeError('argument must be an object, but ' + (typeof k) + ' given')
}
return this
}
//设置缓存
_requestp.cache = function(t){
if(!this.transport)
return
if(this.opt.type === 'GET')
this.opt.cache = !!t
return this
}
//取消网络请求
_requestp.abort = function(){
delete this.transport
if(!this.opt.form)
this.xhr.abort()
return this
}
//超时设置, 单位毫秒
_requestp.timeout = function(time){
if(typeof time !== 'number' || time < 1)
return this
this.opt.timeout = time
return this
}
_requestp.form = function(form){
if(typeof form === 'object' && form.nodeName === 'FORM'){
this.opt.type = 'POST'
this.opt.form = form
}
return this
}
var originAnchor = doc.createElement('a');
originAnchor.href = location.href;
_requestp.end = function(callback){
var _this = this;
// 回调已执行, 或已取消, 则直接返回, 防止重复执行
if(!this.transport)
return
if(!this.opt.url)
throw new Error('Invalid request url')
F.merge(this, requestExtend)
this.callback = callback || noop
// 1. url规范化
this.opt.url = this.opt.url.replace(/#.*$/, '').replace(/^\/\//, location.protocol + '//')
// 2. 处理跨域
if(typeof this.opt.crossDomain !== 'boolean'){
var anchor = doc.createElement('a')
try{
anchor.href = this.opt.url
// IE7及以下浏览器 '1'[0]的结果是 undefined
// IE7下需要获取绝对路径
var absUrl = !'1'[0] ? anchor.getAttribute('href', 4) : anchor.href
anchor.href = absUrl
anchor.async = true
this.opt.crossDomain = (originAnchor.protocol !== anchor.protocol) || (originAnchor.host !== anchor.host)
}catch(e){
this.opt.crossDomain = true
}
}
// 2.1 进一步处理跨域配置
if(this.opt.type === 'JSONP'){
//如果没有跨域自动转回xhr GET
if(!this.opt.crossDomain){
this.opt.type = 'GET';
}else{
this.opt.data['callback'] = this.opt.data['callback'] || ('jsonp' + request.cid++);
this.jsonp(this.opt.data['callback']); //创建临时处理方法
}
}
// 2.2 如果不是跨域请求则自动加上一条header信息用以标识这是ajax请求
if(!this.opt.crossDomain){
this.set('X-Requested-With', 'XMLHttpRequest')
}
// 3. data转字符串
this.opt.param = F.param(this.opt.data)
// 4. 设置Content-Type类型, 默认x-www-form-urlencoded
if(!this.opt.formType)
this.type('form')
// 5.处理GET请求
this.opt.hasContent = this.opt.type === 'POST' //是否为post请求
if(!this.opt.hasContent){
//GET请求直接把参数拼接到url上
if(this.opt.param){
this.opt.url += (/\?/.test(this.opt.url) ? '&' : '?') + this.opt.param
}
//加随机值,避免缓存
if(this.opt.cache === false)
this.opt.url += (/\?/.test(this.opt.url) ? '&' : '?') + '_=' + Math.random()
}else{
if(this.opt.formType === 'form-data'){
delete this.opt.headers['content-type']
this.opt.param = this.formData()
}else if(this.opt.formType !== 'form'){
this.opt.param = JSON.stringify(this.opt.data)
}
}
//jsonp
if(this.opt.type === 'JSONP'){
this.transport = doc.createElement('script')
this.transport.onerror = this.transport.onload = function(){
_this.dispatch(_this.transport)
}
this.transport.src = this.opt.url
doc.head.insertBefore(this.transport, doc.head.firstChild)
//6. 超时处理
if(this.opt.timeout && this.opt.timeout > 0){
this.opt.timeoutID = setTimeout(function(){
_this.transport.onerror = _this.transport.onload = null
_this.dispatch(null)
}, this.opt.timeout)
}
}else{
this.xhr.onreadystatechange = function(ev){
if(_this.opt.timeout && _this.opt.timeout > 0){
_this.opt['time' + this.readyState] = ev.timeStamp
if(this.readyState === 4){
_this.opt.isTimeout = _this.opt.time4 - _this.opt.time1 > _this.opt.timeout
}
}
if(this.readyState !== 4){
return
}
_this.dispatch(_this.opt.isTimeout ? null : _this.xhr)
}
// 6. 初始化xhr提交
this.xhr.open(this.opt.type, this.opt.url, true)
// 7. 设置头信息
for(var i in this.opt.headers){
if(this.opt.headers[i])
this.xhr.setRequestHeader(i, this.opt.headers[i])
}
// 8. 发起网络请求
_this.xhr.send(_this.opt.param)
//超时处理
if(this.opt.timeout && this.opt.timeout > 0){
this.xhr.timeout = this.opt.timeout;
}
}
}
// ---------------------- end ------------------------
if(!win.request){
win.request = {
get: function(url){
if(!url)
throw new Error('argument url is required')
return new _request(url, 'GET')
},
post: function(url){
if(!url)
throw new Error('argument url is required')
return new _request(url, 'POST')
},
jsonp: function(url){
if(!url)
throw new Error('argument url is required')
return new _request(url, 'JSONP')
},
cache: {},
cid: 0,
version: '1.0.0',
release: 'request ES5 version/1.0.0'
}
}
return request
})

0
js/lib/request/request.es5.min.js vendored Normal file
View File

860
js/lib/request/request.js Normal file
View File

@ -0,0 +1,860 @@
/**
* Request组件, full版, 支持IE6+,chrome,FF
* @authors yutent (yutent@doui.cc)
* @date 2016-11-27 13:08:40
*
*/
"use strict";
var r = []
if(!window.JSON)
r = ['./json'];
define(r, function(){
var _request = function(url, protocol){
this.transport = true
protocol = (protocol + '').trim().toUpperCase()
this.xhr = Xhr()
this.pool = {
url: (url + '').trim(),
type: protocol || 'GET',
form: '',
headers: {},
uuid: Math.random().toString(16).substr(2)
}
}
var _requestp = _request.prototype
var toS = Object.prototype.toString
var win = window
var doc = win.document
var encode = encodeURIComponent
var decode = decodeURIComponent
var noop = function(e, res){
if(e)
throw new Error(e + '')
}
// -----------------------------
// 本地协议判断正则
var rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/
var isLocal = false
try{
isLocal = rlocalProtocol.test(location.protocol)
}catch(e){}
var rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg
// ----------------- 一些兼容性预处理 --------------------
if(!win.FormData){
}
//IE8-
if(String.prototype.trim){
String.prototype.trim = function(){
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
}
}
//IE8-
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
var T, k;
if (this === null)
throw new TypeError('forEach is not a function of null');
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function")
throw new TypeError('callback is not a function');
if (arguments.length > 1) {
T = thisArg;
}
k = 0;
while (k < len) {
var kValue;
if (k in O) {
kValue = O[k];
callback.call(T, kValue, k, O);
}
k++;
}
};
}
/* if (!Array.prototype.filter) {
Array.prototype.filter = function(fun) {
'use strict';
if (this === void 0 || this === null) {
throw new TypeError('filter is not a function of null');
}
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError('callback is not a function');
}
var res = [];
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
if (i in t) {
var val = t[i];
if (fun.call(thisArg, val, i, t)) {
res.push(val);
}
}
}
return res;
};
}*/
// IE8-
if(!Array.isArray){
Array.isArray = function(arg) {
return toS.call(arg) === '[object Array]';
};
}
var IE = (function(){
if(window.VBArray){
var mode = document.documentMode
return mode ? mode : (window.XMLHttpRequest ? 7 : 6)
}
return 0
})();
win.Xhr = (function(){
var obj = [
"XMLHttpRequest",
"ActiveXObject('MSXML2.XMLHTTP.6.0')",
"ActiveXObject('MSXML2.XMLHTTP.3.0')",
"ActiveXObject('MSXML2.XMLHTTP')",
"ActiveXObject('Microsoft.XMLHTTP')"
]
//IE7- 本地打开文件不能使用原生XMLHttpRequest
obj[0] = (IE < 8 && IE !== 0 && isLocal) ? '!' : obj[0]
for(var i = 0,a; a = obj[i++];){
try{
if(eval('new ' + a)){
return new Function('return new ' + a)
}
}catch(e){}
}
})();
var supportCors = 'withCredentials' in Xhr()
// ------------------- 几个解释方法 -----------------------
var Format = function(){
this.tagHooks = new function(){
this.option = doc.createElement('select')
this.thead = doc.createElement('table')
this.td = doc.createElement('tr')
this.area = doc.createElement('map')
this.tr = doc.createElement('tbody')
this.col = doc.createElement('colgroup')
this.legend = doc.createElement('fieldset')
this._default = doc.createElement('div')
this.g = doc.createElementNS('http://www.w3.org/2000/svg', 'svg')
this.optgroup = this.option
this.tbody = this.tfoot = this.colgroup = this.caption = this.thead
this.th = this.td
};
var _this = this
'circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use'.replace(/,/g, function(m){
_this.tagHooks[m] = _this.tagHooks.g //处理svg
})
this.rtagName = /<([\w:]+)/
this.rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig
this.scriptTypes = {
'text/javascript': 1,
'text/ecmascript': 1,
'application/ecmascript': 1,
'application/javascript': 1
}
this.rhtml = /<|&#?\w+;/
}
function serialize(p, obj, q){
var k
if(Array.isArray(obj)){
obj.forEach(function(it, i){
k = p ? (p + '[' + (Array.isArray(it) ? i : '') + ']') : i
if(typeof it === 'object'){
serialize(k, it, q)
}else{
q(k, it)
}
})
}else{
for(var i in obj){
k = p ? (p + '[' + i + ']') : i
if(typeof obj[i] === 'object'){
serialize(k, obj[i], q)
}else{
q(k, obj[i])
}
}
}
}
Format.prototype = {
parseJS: function(code){
code = (code + '').trim()
if(code){
if(code.indexOf('use strict') === 1){
var script = doc.createElement('script')
script.text = code
doc.head
.appendChild(script)
.parentNode
.removeChild(script)
}else{
eval(code)
}
}
},
parseXML: function(data, xml, tmp){
try{
var mode = doc.documentMode
//标准浏览器
if(win.DOMParser && (!mode || mode > 8)){
tmp = new DOMParser()
xml = tmp.parseFromString(data, 'text/xml')
}else{// IE了
xml = new ActiveXObject('Microsoft.XMLDOM')
xml.async = 'false'
xml.loadXML(data)
}
}catch(e){
xml = void 0
}
if(!xml ||
!xml.documentElement ||
xml.getElementsByTagName('parsererror').length){
console.error('Invalid XML: ' + data)
}
return xml
},
parseHTML: function (html){
var fragment = (doc.createDocumentFragment()).cloneNode(false)
if(typeof html !== 'string')
return fragment
if(!this.rhtml.test(html)){
fragment.appendChild(document.createTextNode(html))
return fragment
}
html = html.replace(this.rxhtml, '<$1></$2>').trim()
var tag = (this.rtagName.exec(html) || ['', ''])[1].toLowerCase()
var wrap = this.tagHooks[tag] || this.tagHooks._default
var firstChild = null
//使用innerHTML生成的script节点不会触发请求与执行text属性
wrap.innerHTML = html
var script = wrap.getElementsByTagName('script')
if(script.length){
for(var i = 0, el; el = script[i++];){
if(this.scriptTypes[el.type]){
var tmp = (doc.createElement("script")).cloneNode(false)
el.attributes.forEach(function(attr){
tmp.setAttribute(attr.name, attr.value)
})
tmp.text = el.text
el.parentNode.replaceChild(tmp, el)
}
}
}
while(firstChild = wrap.firstChild){
fragment.appendChild(firstChild)
}
return fragment
},
param: function(obj){
if(!obj || typeof obj === 'string' || typeof obj === 'number')
return obj
var arr = []
var q = function(k, v){
if(/native code/.test(v))
return
v = (typeof v === 'function') ? v() : v
v = (toS.call(v) !== '[object File]') ? encode(v) : v
arr.push(encode(k) + '=' + v)
}
if(typeof obj === 'object')
serialize('', obj, q)
return arr.join('&')
},
parseForm: function(form){
var data = {}
for(var i = 0,field; field = form.elements[i++];){
switch(field.type){
case 'select-one':
case 'select-multiple':
if(field.name.length && !field.disabled){
for(var j = 0, opt;opt = field.options[j++];){
if(opt.selected){
data[field.name] = opt.value || opt.text
}
}
}
break;
case 'file':
if(field.name.length && !field.disabled){
data[field.name] = field.files[0]
}
break;
case undefined:
case 'submit':
case 'reset':
case 'button':
break; //按钮啥的, 直接忽略
case 'radio':
case 'checkbox':
// 只处理选中的
if(!field.checked)
break;
default:
if(field.name.length && !field.disabled){
data[field.name] = field.value
}
}
}
return data
},
merge: function(a, b){
if(typeof a !== 'object' || typeof b !== 'object')
throw new TypeError('argument must be an object')
if(Object.assign)
return Object.assign(a, b)
for(var i in b){
a[i] = b[i]
}
return a
}
}
var F = new Format()
// ---------------------------------------------------------
// -------------------- request 模块开始 --------------------
// ---------------------------------------------------------
var requestConvert = {
text: function(val){
return val
},
xml: function(val, xml){
return xml !== undefined ? xml : F.parseXML(val)
},
html: function(val){
return F.parseHTML(val)
},
json: function(val){
return JSON.parse(val)
},
script: function(val){
return F.parseJS(val)
},
jsonp: function(){
return window[this.jsonpCallback]
}
}
var requestExtend = {
formData: function(){
//现代浏览器直接切换为FormData方式
if(win.FormData){
if(this.pool.form){
console.log(this.pool.form.elements)
var data = F.parseForm(this.pool.form)
console.log(data)
F.merge(this.pool.data, data)
}
var form = new FormData()
for(var i in this.pool.data){
var el = this.pool.data[i]
if(Array.isArray(el)){
el.forEach(function(it){
form.append(i + '[]', it)
})
}else{
form.append(i, this.pool.data[i])
}
}
return form
// IE8- 使用iframe
}else{
this.transport = this.mkIframe(this.pool.uuid)
this.pool.form = this.mkForm(this.pool.form)
this.pool.field = []
for(var i in this.pool.data){
var val = this.pool.data[i]
if(Array.isArray(val)){
for(var j = 0,v; v = val[j++];){
var el = doc.createElement('input')
el.type = 'hidden'
el.name = i + '[]'
el.value = v
this.pool.field.push(el)
this.pool.form.appendChild(el)
}
}else{
var el = doc.createElement('input')
el.type = 'hidden'
el.name = i
el.value = val
this.pool.field.push(el)
this.pool.form.appendChild(el)
}
}
}
},
mkIframe: function(id){
var iframe = F.parseHTML('<iframe id="' + id + '" name="' + id + '" style="position: ' + (IE === 6 ? 'absolute' : 'fixed') + ';top: -9999px;left: 0"></iframe>').firstChild
return (doc.body || doc.documentElement).insertBefore(iframe, null)
},
mkForm: function(form){
if(!form)
form = doc.createElement('form')
form.target = this.pool.uuid
form.action = this.pool.url
form.method = 'POST'
form.enctype = 'multipart/form-data'
return form
},
dispatch: function(self, status){
var _this = this
if(!this.transport)
return
//状态为4,既已成功, 则清除超时
clearTimeout(_this.timeoutID)
if(status === 4)
self.status = status
//成功的回调
var isSucc = (self.status >= 200 && self.status < 300) || self.status === 304
var result = {
response: {
url: self.responseURL || self.URL,
headers: {'content-type': ''}
},
request: {
url: self.responseURL || self.URL,
headers: _this.pool.headers
},
status: self.status,
statusText: self.statusText || 'OK',
text: '',
body: '',
error: null
}
if(typeof _this.transport !== 'object'){
delete _this.transport
delete _this.pool
}
// 非iframe方式
if(!status){
var headers = self.getAllResponseHeaders()
headers = headers.split('\n')
headers.forEach(function(it, i){
it = it.trim()
if(it){
it = it.split(':')
result.response.headers[it.shift().toLowerCase()] = it.join(':').trim()
}
})
}
if(isSucc){
if(status === 204){
result.statusText = 'no content'
}else if(status === 304){
result.statusText = 'not modified'
}else{
//处理返回的数据
var dataType = result.response.headers['content-type'].match(/json|xml|script|html/i) || ['text']
dataType = dataType[0].toLowerCase()
var responseTXT = self.responseText || ''
var responseXML = self.responseXML || ''
try{
result.text = responseTXT || responseXML
result.body = requestConvert[dataType](responseTXT, responseXML)
}catch(e){
isSucc = false
result.error = e
result.statusText = 'parse error'
}
}
}else{
if(status){
result.status = 200
if(self.body){
result.text = self.body.innerHTML
result.body = result.text
var child = self.body.firstChild
if(child && child.nodeName.toUpperCase() === 'PRE'){
result.text = child.innerHTML
result.body = requestConvert.json(result.text)
}
}else{
result.text = result.body = self
}
//删除iframe
_this.transport.parentNode.removeChild(_this.transport)
//还原表单, 避免参数重复提交
if(_this.pool.field && _this.pool.field.length){
_this.pool.form.target = ''
_this.pool.form.action = ''
for(var i = 0,el; el = _this.pool.field[i++];){
_this.pool.form.removeChild(el)
}
}
}else{
result.status = result.status || 504
result.statusText = result.statusText || 'Connected timeout'
result.error = F.merge(new Error(result.statusText), {status: result.status})
}
}
_this.callback(result.error, result)
delete _this.transport
delete _this.pool
delete _this.xhr
}
}
// 设置表单类型, 支持2种, form/json
_requestp.type = function(t){
if(this.pool.formType === 'form-data')
return this
this.pool.formType = t || 'form'
if(t === 'form' || this.pool.type === 'GET')
this.set('content-type', 'application/x-www-form-urlencoded; charset=UTF-8')
else
this.set('content-type', 'application/json; charset=UTF-8')
return this
}
//设置头信息
_requestp.set = function(k, val){
if(!this.transport)
return
if(typeof k === 'object'){
for(var i in k){
i = i.toLowerCase()
this.pool.headers[i] = k[i]
}
}else if(typeof k === 'string'){
if(arguments.length < 2)
throw new Error('2 arguments required')
// 全转小写,避免重复写入
k = k.toLowerCase()
if(val === undefined)
delete this.pool.headers[k]
else
this.pool.headers[k] = val
}else{
throw new Error('arguments must be string/object, but [' + (typeof k) + '] given')
}
return this
}
//设置请求参数
_requestp.send = function(k, val){
if(!this.transport)
return
// 1. send方法可以多次调用, 但必须保证格式一致
// 2. 2次圴提交纯字符串也会抛出异常
if(typeof k === 'object'){
if(this.pool.data && (typeof this.pool.data === 'string'))
throw new Error('param can not be string and object at the same time')
if(!this.pool.data)
this.pool.data = {}
F.merge(this.pool.data, k)
}else{
if(typeof k === 'string'){
if(arguments.length === 1){
if(this.pool.data)
throw new Error('invalid param in function send')
this.pool.data = k
}else{
if(this.pool.data && (typeof this.pool.data === 'string'))
throw new Error('param can not be string and object at the same time')
if(!this.pool.data)
this.pool.data = {}
this.pool.data[k] = val
}
}else{
throw new Error('argument of send must be string/object, but [' + (typeof k) + '] given')
}
}
return this
}
//该方法用于 form-data类型的post请求的参数设置
_requestp.field = function(k, val){
if(!this.transport)
return
// 此类型优先级最高
this.pool.formType = 'form-data'
if(!this.pool.data || (this.pool.data && typeof this.pool.data !== 'object'))
this.pool.data = {}
if(arguments.length === 1 && typeof k === 'object'){
F.merge(this.pool.data, k)
}else if(arguments.length === 2){
this.pool.data[k] = val
}else{
throw new TypeError('argument must be an object, but ' + (typeof k) + ' given')
}
return this
}
//设置缓存
_requestp.cache = function(t){
if(!this.transport)
return
if(this.pool.type === 'GET')
this.pool.cache = !!t
return this
}
//取消网络请求
_requestp.abort = function(){
delete this.transport
if(!this.pool.form)
this.xhr.abort()
return this
}
//超时设置, 单位毫秒
_requestp.timeout = function(time){
if(typeof time !== 'number' || time < 1)
return this
this.pool.timeout = time
return this
}
_requestp.form = function(form){
if(typeof form === 'object' && form.nodeName === 'FORM'){
this.pool.type = 'POST'
this.pool.form = form
}
return this
}
var originAnchor = doc.createElement('a')
originAnchor.href = location.href
_requestp.end = function(callback){
var _this = this;
// 回调已执行, 或已取消, 则直接返回, 防止重复执行
if(!this.transport)
return
if(!this.pool.url)
throw new Error('Invalid request url')
F.merge(this, requestExtend)
this.callback = callback || noop
// 1. url规范化
this.pool.url = this.pool.url.replace(/#.*$/, '').replace(/^\/\//, location.protocol + '//')
// 2. data转字符串
this.pool.param = F.param(this.pool.data)
// 3. 处理跨域
if(typeof this.pool.crossDomain !== 'boolean'){
var anchor = doc.createElement('a')
try{
anchor.href = this.pool.url
// IE7及以下浏览器 '1'[0]的结果是 undefined
// IE7下需要获取绝对路径
var absUrl = !'1'[0] ? anchor.getAttribute('href', 4) : anchor.href
anchor.href = absUrl
this.pool.crossDomain = (originAnchor.protocol !== anchor.protocol) || (originAnchor.host !== anchor.host)
}catch(e){
this.pool.crossDomain = true
}
}
// 4. 设置Content-Type类型, 默认x-www-form-urlencoded
if(!this.pool.formType)
this.type('form')
// 5.处理GET请求
this.pool.hasContent = this.pool.type !== 'GET' //是否为post请求
if(!this.pool.hasContent){
//GET请求直接把参数拼接到url上
if(this.pool.param){
this.pool.url += (/\?/.test(this.pool.url) ? '&' : '?') + this.pool.param
}
//加随机值,避免缓存
if(this.pool.cache === false)
this.pool.url += (/\?/.test(this.pool.url) ? '&' : '?') + '_=' + Math.random()
}else{
if(this.pool.formType === 'form-data'){
delete this.pool.headers['content-type']
this.pool.param = this.formData()
}else if(this.pool.formType !== 'form'){
this.pool.param = JSON.stringify(this.pool.data)
}
}
// transport不恒为 true, 则说明是走iframe路线的
if(this.transport !== true){
this.transport.onload = function(ev){
_this.dispatch(this.contentWindow.document, 4)
}
this.pool.form.submit()
}else{
this.xhr.onreadystatechange = function(statusTxt){
if(this.readyState !== 4)
return
_this.dispatch(this)
}
// 6. 初始化xhr提交
this.xhr.open(this.pool.type, this.pool.url, true)
// 7. 设置头信息
for(var i in this.pool.headers){
if(this.pool.headers[i])
this.xhr.setRequestHeader(i, this.pool.headers[i])
}
// 8. 发起网络请求
this.xhr.send(this.pool.param)
}
//超时处理
//IE8- 不支持timeout属性的设置,
if(this.pool.timeout && this.pool.timeout > 0){
this.timeoutID = setTimeout(function(){
_this.abort()
}, this.pool.timeout)
}
}
// ---------------------- end ------------------------
if(!win.request){
win.request = {
get: function(url){
if(!url)
throw new Error('argument url is required')
return new _request(url, 'GET')
},
post: function(url){
if(!url)
throw new Error('argument url is required')
return new _request(url, 'POST')
},
version: '1.0.0',
release: 'request full version/1.0.0'
}
}
return request
})

1
js/lib/request/request.min.js vendored Normal file

File diff suppressed because one or more lines are too long

163
js/lib/router/router.js Normal file
View File

@ -0,0 +1,163 @@
define(['yua'], function(){
function Router(){
this.table = {get: []};
this.errorFn = null;
this.history = null;
this.hash = '';
this.started = false;
this.init = {};
}
var defaultOptions = {
prefix: /^(#!|#)[\/]?/, //hash前缀正则
historyOpen: true, //是否开启hash历史
allowReload: true //连续点击同一个链接是否重新加载
};
var isMouseUp = true;
var ruleRegExp = /(:id)|(\{id\})|(\{id:([A-z\d\,\[\]\{\}\-\+\*\?\!:\^\$]*)\})/g;
Router.prototype = {
error: function(callback){
this.errorFn = callback;
},
config: function(opts){
if(this.started)
return console.error('Router config has been set');
this.started = true;
if(!opts.allowReload)
opts.historyOpen = true;
this.init = yua.mix({}, defaultOptions, opts);
},
_getRegExp: function(rule, opts){
var re = rule.replace(ruleRegExp, function(m, p1, p2, p3, p4){
var w = '([\\w.-]';
if(p1 || p2){
return w + '+)';
}else{
if(!/^\{[\d\,]+\}$/.test(p4)){
w = '(';
}
return w + p4 + ')';
}
})
re = re.replace(/(([^\\])([\/]+))/g, '$2\\/').replace(/(([^\\])([\.]+))/g, '$2\\.').replace(/(([^\\])([\-]+))/g, '$2\\-').replace(/(\(.*)(\\[\-]+)(.*\))/g, '$1-$3');
re = '^' + re + '$';
opts.regexp = new RegExp(re)
return opts;
},
_add: function(method, rule, callback){
if(!this.started)
this.config({});
var table = this.table[method.toLowerCase()];
if (rule.charAt(0) !== "/") {
console.error('char "/" must be in front of router rule');
return;
}
rule = rule.replace(/^[\/]+|[\/]+$|\s+/g, '');
var opts = {};
opts.rule = rule;
opts.callback = callback;
yua.Array.ensure(table, this._getRegExp(rule, opts));
},
_route: function(method, hash){
var hash = hash.trim();
var table = this.table[method];
var init = this.init;
if(!init.allowReload && hash === this.history)
return;
if(init.historyOpen){
this.history = hash;
if(yua.ls)
yua.ls('lastHash', hash);
}
for(var i = 0, obj; obj = table[i++];){
var args = hash.match(obj.regexp);
if(args){
args.shift();
return obj.callback.apply(obj, args)
}
}
this.errorFn && this.errorFn(hash);
},
on: function(rule, callback){
this._add('get', rule, callback);
}
}
yua.bind(window, 'load', function(){
if(!yua.router.started)
return;
var prefix = yua.router.init.prefix;
var hash = location.hash;
hash = hash.replace(prefix, "").trim();
yua.router._route('get', hash);
});
if('onhashchange' in window){
window.addEventListener('hashchange', function(event){
if(!isMouseUp)
return;
var prefix = yua.router.init.prefix;
var hash = location.hash.replace(prefix, "").trim();
yua.router._route('get', hash)
})
}
//劫持页面上所有点击事件,如果事件源来自链接或其内部,
//并且它不会跳出本页,并且以"#/"或"#!/"开头那么触发updateLocation方法
yua.bind(document, "mousedown", function(event){
var defaultPrevented = "defaultPrevented" in event ? event['defaultPrevented'] : event.returnValue === false
if (defaultPrevented || event.ctrlKey || event.metaKey || event.which === 2)
return
var target = event.target
while (target.nodeName !== "A") {
target = target.parentNode
if (!target || target.tagName === "BODY") {
return
}
}
if (targetIsThisWindow(target.target)){
if(!yua.router.started)
return;
var href = target.getAttribute("href") || target.getAttribute("xlink:href"),
prefix = yua.router.init.prefix;
if (href === null || !prefix.test(href))
return
yua.router.hash = href.replace(prefix, "").trim();
event.preventDefault();
location.hash = href;
isMouseUp = false;
}
})
yua.bind(document, "mouseup", function(){
if(!isMouseUp){
yua.router._route('get', yua.router.hash);
isMouseUp = true;
}
})
//判定A标签的target属性是否指向自身
//thanks https://github.com/quirkey/sammy/blob/master/lib/sammy.js#L219
function targetIsThisWindow(targetWindow) {
if (!targetWindow || targetWindow === window.name || targetWindow === '_self' || (targetWindow === 'top' && window == window.top)) {
return true
}
return false
}
return yua.router = new Router;
})

1
js/lib/router/router.min.js vendored Normal file
View File

@ -0,0 +1 @@
define(["yua"],function(){function t(){this.table={get:[]},this.errorFn=null,this.history=null,this.hash="",this.started=!1,this.init={}}function e(t){return!t||t===window.name||"_self"===t||"top"===t&&window==window.top?!0:!1}var r={prefix:/^(#!|#)[\/]?/,historyOpen:!0,allowReload:!0},i=!0,a=/(:id)|(\{id\})|(\{id:([A-z\d\,\[\]\{\}\-\+\*\?\!:\^\$]*)\})/g;return t.prototype={error:function(t){this.errorFn=t},config:function(t){return this.started?console.error("Router config has been set"):(this.started=!0,t.allowReload||(t.historyOpen=!0),void(this.init=yua.mix({},r,t)))},_getRegExp:function(t,e){var r=t.replace(a,function(t,e,r,i,a){var n="([\\w.-]";return e||r?n+"+)":(/^\{[\d\,]+\}$/.test(a)||(n="("),n+a+")")});return r=r.replace(/(([^\\])([\/]+))/g,"$2\\/").replace(/(([^\\])([\.]+))/g,"$2\\.").replace(/(([^\\])([\-]+))/g,"$2\\-").replace(/(\(.*)(\\[\-]+)(.*\))/g,"$1-$3"),r="^"+r+"$",e.regexp=new RegExp(r),e},_add:function(t,e,r){this.started||this.config({});var i=this.table[t.toLowerCase()];if("/"!==e.charAt(0))return void console.error('char "/" must be in front of router rule');e=e.replace(/^[\/]+|[\/]+$|\s+/g,"");var a={};a.rule=e,a.callback=r,yua.Array.ensure(i,this._getRegExp(e,a))},_route:function(t,e){var e=e.trim(),r=this.table[t],i=this.init;if(i.allowReload||e!==this.history){i.historyOpen&&(this.history=e,yua.ls&&yua.ls("lastHash",e));for(var a,n=0;a=r[n++];){var o=e.match(a.regexp);if(o)return o.shift(),a.callback.apply(a,o)}this.errorFn&&this.errorFn(e)}},on:function(t,e){this._add("get",t,e)}},yua.bind(window,"load",function(){if(yua.router.started){var t=yua.router.init.prefix,e=location.hash;e=e.replace(t,"").trim(),yua.router._route("get",e)}}),"onhashchange"in window&&window.addEventListener("hashchange",function(){if(i){var t=yua.router.init.prefix,e=location.hash.replace(t,"").trim();yua.router._route("get",e)}}),yua.bind(document,"mousedown",function(t){var r="defaultPrevented"in t?t.defaultPrevented:t.returnValue===!1;if(!(r||t.ctrlKey||t.metaKey||2===t.which)){for(var a=t.target;"A"!==a.nodeName;)if(a=a.parentNode,!a||"BODY"===a.tagName)return;if(e(a.target)){if(!yua.router.started)return;var n=a.getAttribute("href")||a.getAttribute("xlink:href"),o=yua.router.init.prefix;if(null===n||!o.test(n))return;yua.router.hash=n.replace(o,"").trim(),t.preventDefault(),location.hash=n,i=!1}}}),yua.bind(document,"mouseup",function(){i||(yua.router._route("get",yua.router.hash),i=!0)}),yua.router=new t});

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="../css/base.min.css" rel="stylesheet">
<style type="text/css">
.preview {width:500px;height:300px;margin:auto;border:1px solid #ddd;}
</style>
</head>
<body ms-controller="test">
<div class="preview">
<img ms-attr-src="preview">
</div>
<input type="file" ms-duplex="file" id="file">
<a href="javascript:;" class="submit">提交</a>
<script src="../avalon.modern.js"></script>
<script type="text/javascript">
require(['uploader/uploader', 'ajax/ajax.min'], function(Up){
var uploader = new Up()
uploader.setUrl('http://up.qbox.me')
.onProgress(function(obj){
console.log(obj.speed, obj.progress, obj.loaded, obj.time)
})
.onEnd(function(res){
console.info(res)
})
var submit = document.querySelector('.submit')
var file = document.querySelector('#file')
submit.onclick = function(){
// console.log(file.files[0])
avalon.get('/upload.php?image=' + file.files[0].name, '', function(txt){
uploader.init()
.setField('file', file.files[0])
.setField('token', txt)
.setField('key', 'avatar/dsfghj')
.start()
}, 'text')
}
})
</script>
</body>
</html>

137
js/lib/uploader/uploader.js Normal file
View File

@ -0,0 +1,137 @@
/**
* Uploader 无刷新上传文件(next版)
* 只支持chrome/firefox/IE10+/opera12+
* @authors yutent (yutent@doui.cc)
* @date 2016-09-05 19:33:23
*
*/
"use strict";
define(function(){
/* var isIE = !!window.VBArray
function IEVersion(){
if(document.documentMode)
return document.documentMode
return window.XMLHttpRequest ? 7 : 6
}*/
var Uploader = function(){
this.init()
}
Uploader.prototype = {
init: function(){
this.xhr = new XMLHttpRequest()
this.form = new FormData()
this.field = {}
this.header = {}
return this
},
setUrl: function(url){
if(!url || typeof url !== 'string')
return console.error('url不能为空且必须是字符串')
this.url = url
return this
},
setField: function(key, val){
if(typeof key === 'object'){
for(var i in key){
this.field[i] = key[i]
}
}else{
this.field[key] = val
}
return this
},
setHeader: function(key, val){
if(typeof key === 'object'){
for(var i in key){
this.header[i] = key[i]
}
}else{
this.header[key] = val
}
return this
},
start: function(){
var _this = this
if(!this.url)
return console.error('请先设置url')
this.xhr.open('POST', this.url, true)
for(var i in this.field){
this.form.append(i, this.field[i])
}
var startTime = Date.now()
this.xhr.upload.addEventListener('progress', function(evt){
if(evt.lengthComputable && _this.progress){
var now = Date.now()
var upSize = (evt.loaded * 1000) / 1024
var res = {loaded: evt.loaded, time: now - startTime}
res.speed = upSize / res.time
if(res.speed < 10){
res.speed = Math.floor(res.speed * 1024) + ' B/s'
}else{
res.speed = res.speed.toFixed(2) + ' KB/s'
}
res.progress = Math.round(evt.loaded * 100 / evt.total)
_this.progress(res)
}
}, false)
this.xhr.onreadystatechange = function(){
if(_this.xhr.readyState === 4 &&
_this.xhr.status === 200 &&
_this.xhr.responseText !== ''){
var res = _this.xhr.responseText
try{
res = JSON.parse(res)
}catch(err){}
_this.end && _this.end(res)
}else{
if(_this.xhr.status !== 200 && _this.xhr.responseText)
_this.error && _this.error(_this.xhr.responseText)
}
}
this.xhr.send(this.form)
},
onProgress: function(fn){
this.progress = fn
return this
},
onEnd: function(fn){
this.end = fn
return this
},
onError: function(fn){
this.error = fn
return this
}
}
if(!window.Uploader)
window.Uploader = Uploader
return Uploader
})

0
js/lib/uploader/uploader.min.js vendored Normal file
View File

544
js/touch.js Normal file
View File

@ -0,0 +1,544 @@
var ua = navigator.userAgent.toLowerCase()
//http://stackoverflow.com/questions/9038625/detect-if-device-is-ios
function iOSversion() {
//https://developer.apple.com/library/prerelease/mac/releasenotes/General/WhatsNewInSafari/Articles/Safari_9.html
//http://mp.weixin.qq.com/s?__biz=MzA3MDQ4MzQzMg==&mid=256900619&idx=1&sn=b29f84cff0b8d7b9742e5d8b3cd8f218&scene=1&srcid=1009F9l4gh9nZ7rcQJEhmf7Q#rd
if (/iPad|iPhone|iPod/i.test(ua) && !window.MSStream) {
if ("backdropFilter" in document.documentElement.style) {
return 9
}
if (!!window.indexedDB) {
return 8
}
if (!!window.SpeechSynthesisUtterance) {
return 7
}
if (!!window.webkitAudioContext) {
return 6
}
if (!!window.matchMedia) {
return 5
}
if (!!window.history && 'pushState' in window.history) {
return 4
}
return 3
}
return NaN
}
var deviceIsAndroid = ua.indexOf('android') > 0
var deviceIsIOS = iOSversion()
var Recognizer = yua.gestureHooks = {
pointers: {},
//以AOP切入touchstart, touchmove, touchend, touchcancel回调
start: function (event, callback) {
//touches是当前屏幕上所有触摸点的列表;
//targetTouches是当前对象上所有触摸点的列表;
//changedTouches是涉及当前事件的触摸点的列表。
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches[i],
id = touch.identifier,
pointer = {
startTouch: mixLocations({}, touch),
startTime: Date.now(),
status: 'tapping',
element: event.target,
pressingHandler: Recognizer.pointers[id] && Recognizer.pointers[id].pressingHandler
}
Recognizer.pointers[id] = pointer;
callback(pointer, touch)
}
},
move: function (event, callback) {
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches[i]
var pointer = Recognizer.pointers[touch.identifier]
if (!pointer) {
return
}
if (!("lastTouch" in pointer)) {
pointer.lastTouch = pointer.startTouch
pointer.lastTime = pointer.startTime
pointer.deltaX = pointer.deltaY = pointer.duration = pointer.distance = 0
}
var time = Date.now() - pointer.lastTime
if (time > 0) {
var RECORD_DURATION = 70
if (time > RECORD_DURATION) {
time = RECORD_DURATION
}
if (pointer.duration + time > RECORD_DURATION) {
pointer.duration = RECORD_DURATION - time
}
pointer.duration += time;
pointer.lastTouch = mixLocations({}, touch)
pointer.lastTime = Date.now()
pointer.deltaX = touch.clientX - pointer.startTouch.clientX
pointer.deltaY = touch.clientY - pointer.startTouch.clientY
var x = pointer.deltaX * pointer.deltaX
var y = pointer.deltaY * pointer.deltaY
pointer.distance = Math.sqrt(x + y)
pointer.isVertical = x < y
callback(pointer, touch)
}
}
},
end: function (event, callback) {
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches[i],
id = touch.identifier,
pointer = Recognizer.pointers[id]
if (!pointer)
continue
callback(pointer, touch)
delete Recognizer.pointers[id]
}
},
//人工触发合成事件
fire: function (elem, type, props) {
if (elem) {
var event = document.createEvent('Events')
event.initEvent(type, true, true)
yua.mix(event, props)
elem.dispatchEvent(event)
}
},
//添加各种识别器
add: function (name, recognizer) {
function move(event) {
recognizer.touchmove(event)
}
function end(event) {
recognizer.touchend(event)
document.removeEventListener('touchmove', move)
document.removeEventListener('touchend', end)
document.removeEventListener('touchcancel', cancel)
}
function cancel(event) {
recognizer.touchcancel(event)
document.removeEventListener('touchmove', move)
document.removeEventListener('touchend', end)
document.removeEventListener('touchcancel', cancel)
}
recognizer.events.forEach(function (eventName) {
yua.eventHooks[eventName] = {
fix: function (el, fn) {
if (!el['touch-' + name]) {
el['touch-' + name] = '1'
el.addEventListener('touchstart', function (event) {
recognizer.touchstart(event)
document.addEventListener('touchmove', move)
document.addEventListener('touchend', end)
document.addEventListener('touchcancel', cancel)
})
}
return fn
}
}
})
}
}
var locations = ['screenX', 'screenY', 'clientX', 'clientY', 'pageX', 'pageY']
// 复制 touch 对象上的有用属性到固定对象上
function mixLocations(target, source) {
if (source) {
locations.forEach(function (key) {
target[key] = source[key]
})
}
return target
}
var supportPointer = !!navigator.pointerEnabled || !!navigator.msPointerEnabled
if (supportPointer) { // 支持pointer的设备可用样式来取消click事件的300毫秒延迟
root.style.msTouchAction = root.style.touchAction = 'none'
}
var tapRecognizer = {
events: ['tap'],
touchBoundary: 10,
tapDelay: 200,
needClick: function(target) {
//判定是否使用原生的点击事件, 否则使用sendClick方法手动触发一个人工的点击事件
switch (target.nodeName.toLowerCase()) {
case 'button':
case 'select':
case 'textarea':
if (target.disabled) {
return true
}
break;
case 'input':
// IOS6 pad 上选择文件如果不是原生的click弹出的选择界面尺寸错误
if ((deviceIsIOS && target.type === 'file') || target.disabled) {
return true
}
break;
case 'label':
case 'iframe':
case 'video':
return true
}
return false
},
needFocus: function(target) {
switch (target.nodeName.toLowerCase()) {
case 'textarea':
case 'select': //实测android下select也需要
return true;
case 'input':
switch (target.type) {
case 'button':
case 'checkbox':
case 'file':
case 'image':
case 'radio':
case 'submit':
return false
}
//如果是只读或disabled状态,就无须获得焦点了
return !target.disabled && !target.readOnly
default:
return false
}
},
focus: function(targetElement) {
var length;
//在iOS7下, 对一些新表单元素(如date, datetime, time, month)调用focus方法会抛错,
//幸好的是,我们可以改用setSelectionRange获取焦点, 将光标挪到文字的最后
var type = targetElement.type
if (deviceIsIOS && targetElement.setSelectionRange &&
type.indexOf('date') !== 0 && type !== 'time' && type !== 'month') {
length = targetElement.value.length
targetElement.setSelectionRange(length, length)
} else {
targetElement.focus()
}
},
findControl: function(labelElement) {
// 获取label元素所对应的表单元素
// 可以能过control属性, getElementById, 或用querySelector直接找其内部第一表单元素实现
if (labelElement.control !== undefined) {
return labelElement.control
}
if (labelElement.htmlFor) {
return document.getElementById(labelElement.htmlFor)
}
return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea')
},
fixTarget: function(target) {
if (target.nodeType === 3) {
return target.parentNode
}
if (window.SVGElementInstance && (target instanceof SVGElementInstance)) {
return target.correspondingUseElement;
}
return target
},
updateScrollParent: function(targetElement) {
//如果事件源元素位于某一个有滚动条的祖父元素中,那么保持其scrollParent与scrollTop值
var scrollParent = targetElement.tapScrollParent
if (!scrollParent || !scrollParent.contains(targetElement)) {
var parentElement = targetElement
do {
if (parentElement.scrollHeight > parentElement.offsetHeight) {
scrollParent = parentElement
targetElement.tapScrollParent = parentElement
break
}
parentElement = parentElement.parentElement
} while (parentElement)
}
if (scrollParent) {
scrollParent.lastScrollTop = scrollParent.scrollTop
}
},
touchHasMoved: function(event) {
//判定是否发生移动,其阀值是10px
var touch = event.changedTouches[0],
boundary = tapRecognizer.touchBoundary
return Math.abs(touch.pageX - tapRecognizer.pageX) > boundary ||
Math.abs(touch.pageY - tapRecognizer.pageY) > boundary
},
findType: function(targetElement) {
// 安卓chrome浏览器上模拟的 click 事件不能让 select 打开,故使用 mousedown 事件
return deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select' ?
'mousedown' : 'click'
},
sendClick: function(targetElement, event) {
// 在click之前触发tap事件
Recognizer.fire(targetElement, 'tap', {
touchEvent: event
})
var clickEvent, touch
//某些安卓设备必须先移除焦点之后模拟的click事件才能让新元素获取焦点
if (document.activeElement && document.activeElement !== targetElement) {
document.activeElement.blur()
}
touch = event.changedTouches[0]
// 手动触发点击事件,此时必须使用document.createEvent('MouseEvents')来创建事件
// 及使用initMouseEvent来初始化它
clickEvent = document.createEvent('MouseEvents')
clickEvent.initMouseEvent(tapRecognizer.findType(targetElement), true, true, window, 1, touch.screenX,
touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null)
clickEvent.touchEvent = event
targetElement.dispatchEvent(clickEvent)
},
touchstart: function(event) {
//忽略多点触摸
if (event.targetTouches.length !== 1) {
return true
}
//修正事件源对象
var targetElement = tapRecognizer.fixTarget(event.target)
var touch = event.targetTouches[0]
if (deviceIsIOS) {
// 判断是否是点击文字进行选择等操作如果是不需要模拟click
var selection = window.getSelection();
if (selection.rangeCount && !selection.isCollapsed) {
return true
}
var id = touch.identifier
//当 alert 或 confirm 时点击其他地方会触发touch事件identifier相同此事件应该被忽略
if (id && isFinite(tapRecognizer.lastTouchIdentifier) && tapRecognizer.lastTouchIdentifier === id) {
event.preventDefault()
return false
}
tapRecognizer.lastTouchIdentifier = id
tapRecognizer.updateScrollParent(targetElement)
}
//收集触摸点的信息
tapRecognizer.status = "tapping"
tapRecognizer.startTime = Date.now()
tapRecognizer.element = targetElement
tapRecognizer.pageX = touch.pageX
tapRecognizer.pageY = touch.pageY
// 如果点击太快,阻止双击带来的放大收缩行为
if ((tapRecognizer.startTime - tapRecognizer.lastTime) < tapRecognizer.tapDelay) {
event.preventDefault()
}
},
touchmove: function(event) {
if (tapRecognizer.status !== "tapping") {
return true
}
// 如果事件源元素发生改变,或者发生了移动,那么就取消触发点击事件
if (tapRecognizer.element !== tapRecognizer.fixTarget(event.target) ||
tapRecognizer.touchHasMoved(event)) {
tapRecognizer.status = tapRecognizer.element = 0
}
},
touchend: function(event) {
var targetElement = tapRecognizer.element
var now = Date.now()
//如果是touchstart与touchend相隔太久,可以认为是长按,那么就直接返回
//或者是在touchstart, touchmove阶段,判定其不该触发点击事件,也直接返回
if (!targetElement || now - tapRecognizer.startTime > tapRecognizer.tapDelay) {
return true
}
tapRecognizer.lastTime = now
var startTime = tapRecognizer.startTime
tapRecognizer.status = tapRecognizer.startTime = 0
targetTagName = targetElement.tagName.toLowerCase()
if (targetTagName === 'label') {
//尝试触发label上可能绑定的tap事件
Recognizer.fire(targetElement, 'tap', {
touchEvent: event
})
var forElement = tapRecognizer.findControl(targetElement)
if (forElement) {
tapRecognizer.focus(targetElement)
targetElement = forElement
}
} else if (tapRecognizer.needFocus(targetElement)) {
// 如果元素从touchstart到touchend经历时间过长,那么不应该触发点击事
// 或者此元素是iframe中的input元素,那么它也无法获点焦点
if ((now - startTime) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
tapRecognizer.element = 0
return false
}
tapRecognizer.focus(targetElement)
deviceIsAndroid && tapRecognizer.sendClick(targetElement, event)
return false
}
if (deviceIsIOS) {
//如果它的父容器的滚动条发生改变,那么应该识别为划动或拖动事件,不应该触发点击事件
var scrollParent = targetElement.tapScrollParent;
if (scrollParent && scrollParent.lastScrollTop !== scrollParent.scrollTop) {
return true
}
}
//如果这不是一个需要使用原生click的元素则屏蔽原生事件避免触发两次click
if (!tapRecognizer.needClick(targetElement)) {
event.preventDefault()
// 触发一次模拟的click
tapRecognizer.sendClick(targetElement, event)
}
},
touchcancel: function() {
tapRecognizer.startTime = tapRecognizer.element = 0
}
}
Recognizer.add("tap", tapRecognizer)
var pressRecognizer = {
events: ['longtap', 'doubletap'],
cancelPress: function (pointer) {
clearTimeout(pointer.pressingHandler)
pointer.pressingHandler = null
},
touchstart: function (event) {
Recognizer.start(event, function (pointer, touch) {
pointer.pressingHandler = setTimeout(function () {
if (pointer.status === 'tapping') {
Recognizer.fire(event.target, 'longtap', {
touch: touch,
touchEvent: event
})
}
pressRecognizer.cancelPress(pointer)
}, 800)
if (event.changedTouches.length !== 1) {
pointer.status = 0
}
})
},
touchmove: function (event) {
Recognizer.move(event, function (pointer) {
if (pointer.distance > 10 && pointer.pressingHandler) {
pressRecognizer.cancelPress(pointer)
if (pointer.status === 'tapping') {
pointer.status = 'panning'
}
}
})
},
touchend: function (event) {
Recognizer.end(event, function (pointer, touch) {
pressRecognizer.cancelPress(pointer)
if (pointer.status === 'tapping') {
pointer.lastTime = Date.now()
if (pressRecognizer.lastTap && pointer.lastTime - pressRecognizer.lastTap.lastTime < 300) {
Recognizer.fire(pointer.element, 'doubletap', {
touch: touch,
touchEvent: event
})
}
pressRecognizer.lastTap = pointer
}
})
},
touchcancel: function (event) {
Recognizer.end(event, function (pointer) {
pressRecognizer.cancelPress(pointer)
})
}
}
Recognizer.add('press', pressRecognizer)
var swipeRecognizer = {
events: ['swipe', 'swipeleft', 'swiperight', 'swipeup', 'swipedown'],
getAngle: function (x, y ) {
return Math.atan2(y, x) * 180 / Math.PI
},
getDirection: function (x, y) {
var angle = swipeRecognizer.getAngle(x, y)
if ((angle < -45) && (angle > -135)) {
return "up"
} else if ((angle >= 45) && (angle < 315)) {
return "down"
} else if ((angle > -45) && (angle <= 45)) {
return "right"
} else{
return "left"
}
},
touchstart: function (event) {
Recognizer.start(event, noop)
},
touchmove: function (event) {
Recognizer.move(event, noop)
},
touchend: function (event) {
if(event.changedTouches.length !== 1){
return
}
Recognizer.end(event, function (pointer, touch) {
var isflick = (pointer.distance > 30 && pointer.distance / pointer.duration > 0.65)
if (isflick) {
var extra = {
deltaX : pointer.deltaX,
deltaY: pointer.deltaY,
touch: touch,
touchEvent: event,
direction: swipeRecognizer.getDirection(pointer.deltaX, pointer.deltaY),
isVertical: pointer.isVertical
}
var target = pointer.element
Recognizer.fire(target, 'swipe', extra)
Recognizer.fire(target, 'swipe' + extra.direction, extra)
}
})
}
}
swipeRecognizer.touchcancel = swipeRecognizer.touchend
Recognizer.add('swipe', swipeRecognizer)

7026
js/yua-touch.js Normal file

File diff suppressed because it is too large Load Diff

6475
js/yua.js Normal file

File diff suppressed because it is too large Load Diff