update for compatibility ie11
This commit is contained in:
parent
037e2b0812
commit
58f6adb7ba
132 changed files with 26633 additions and 32 deletions
|
|
@ -15,6 +15,104 @@ class Browser
|
|||
//Logger::addLog('http.browser',$this->user);
|
||||
}
|
||||
|
||||
public static function get()
|
||||
{
|
||||
// Make case insensitive.
|
||||
$t = strtolower($_SERVER['HTTP_USER_AGENT']);
|
||||
|
||||
// If the string *starts* with the string, strpos returns 0 (i.e., FALSE). Do a ghetto hack and start with a space.
|
||||
// "[strpos()] may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE."
|
||||
// http://php.net/manual/en/function.strpos.php
|
||||
$t = " " . $t;
|
||||
|
||||
// Humans / Regular Users
|
||||
if (strpos($t, 'opera') || strpos($t, 'opr/')) {
|
||||
return 'Opera';
|
||||
} elseif (strpos($t, 'edge')) {
|
||||
return 'Edge';
|
||||
} elseif (strpos($t, 'chrome')) {
|
||||
return 'Chrome';
|
||||
} elseif (strpos($t, 'safari')) {
|
||||
return 'Safari';
|
||||
} elseif (strpos($t, 'firefox')) {
|
||||
return 'Firefox';
|
||||
} elseif (strpos($t, 'msie') || strpos($t, 'trident/7')) {
|
||||
return 'Internet Explorer';
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_firefox_version() {
|
||||
// Make case insensitive.
|
||||
$t = strtolower($_SERVER['HTTP_USER_AGENT']);
|
||||
|
||||
// If the string *starts* with the string, strpos returns 0 (i.e., FALSE). Do a ghetto hack and start with a space.
|
||||
// "[strpos()] may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE."
|
||||
// http://php.net/manual/en/function.strpos.php
|
||||
$t = " " . $t;
|
||||
|
||||
// Firefox Users
|
||||
if (strpos($t, 'firefox')) {
|
||||
preg_match('/rv:(.*)\)/', $_SERVER['HTTP_USER_AGENT'], $matches, PREG_OFFSET_CAPTURE);
|
||||
if(isset($matches[1])) {
|
||||
return intval($matches[1][0]);
|
||||
}else{
|
||||
return 'no-version';
|
||||
}
|
||||
}
|
||||
return 'not-firefox';
|
||||
}
|
||||
|
||||
public static function get_ip() {
|
||||
// IP si internet partagé
|
||||
if (isset($_SERVER['HTTP_CLIENT_IP'])) {
|
||||
return $_SERVER['HTTP_CLIENT_IP'];
|
||||
}
|
||||
// IP derrière un proxy
|
||||
elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
return $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
}
|
||||
// Sinon : IP normale
|
||||
else {
|
||||
return (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '');
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_os() {
|
||||
$user_agent = $_SERVER['HTTP_USER_AGENT'];
|
||||
$os_platform = "Inconnu";
|
||||
$os_array = array(
|
||||
'/windows nt 10/i' => 'Windows 10',
|
||||
'/windows nt 6.3/i' => 'Windows 8.1',
|
||||
'/windows nt 6.2/i' => 'Windows 8',
|
||||
'/windows nt 6.1/i' => 'Windows 7',
|
||||
'/windows nt 6.0/i' => 'Windows Vista',
|
||||
'/windows nt 5.2/i' => 'Windows Server 2003/XP x64',
|
||||
'/windows nt 5.1/i' => 'Windows XP',
|
||||
'/windows xp/i' => 'Windows XP',
|
||||
'/windows nt 5.0/i' => 'Windows 2000',
|
||||
'/windows me/i' => 'Windows ME',
|
||||
'/win98/i' => 'Windows 98',
|
||||
'/win95/i' => 'Windows 95',
|
||||
'/win16/i' => 'Windows 3.11',
|
||||
'/macintosh|mac os x/i' => 'Mac OS X',
|
||||
'/mac_powerpc/i' => 'Mac OS 9',
|
||||
'/linux/i' => 'Linux',
|
||||
'/ubuntu/i' => 'Ubuntu',
|
||||
'/iphone/i' => 'iPhone',
|
||||
'/ipod/i' => 'iPod',
|
||||
'/ipad/i' => 'iPad',
|
||||
'/android/i' => 'Android',
|
||||
'/blackberry/i' => 'BlackBerry',
|
||||
'/webos/i' => 'Mobile'
|
||||
);
|
||||
foreach ($os_array as $regex => $value) {
|
||||
if (preg_match($regex, $user_agent)) {
|
||||
$os_platform = $value;
|
||||
}
|
||||
}
|
||||
return $os_platform;
|
||||
}
|
||||
|
||||
protected function get_browser_name()
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -277,6 +277,43 @@ class Url
|
|||
return $url . "/" . BASE_SERVER_DIRECTORY;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getPageName(){
|
||||
|
||||
$url = parse_url($_SERVER['REQUEST_URI']);
|
||||
$urlTrim = trim($url['path'], '/');
|
||||
$urlParts = explode('/', $urlTrim);
|
||||
|
||||
//suppression des sous repertoires du BASE_SERVER_DIRECTORY
|
||||
$basePath = explode( '/', BASE_SERVER_DIRECTORY);
|
||||
foreach($basePath as $subDir) {
|
||||
if ($urlParts[0] == $subDir) {
|
||||
array_shift($urlParts);
|
||||
}
|
||||
}
|
||||
|
||||
//Récupération du nom de la page
|
||||
if (isset($urlParts[0])) {
|
||||
//il se peut que l'on ait des variable avec ? dans l'url
|
||||
$urlQuery = explode('?', $urlParts[0]);
|
||||
$urlQueryPageName = $urlQuery[0];
|
||||
($urlQueryPageName == 'index' || $urlQueryPageName == '') ? $page['name'] = 'index' : $page['name'] = $urlQueryPageName;
|
||||
unset($urlParts[0]);
|
||||
} else {
|
||||
$page['name'] = 'index';
|
||||
}
|
||||
|
||||
$page['name'] = strtolower($page['name']);
|
||||
|
||||
//si c'est une page de controle de formulaire : on renomme la page
|
||||
if ($page['name'] == 'control') {
|
||||
$page['control'] = true;
|
||||
($urlParts[1] == 'index' || $urlParts[1] == '') ? $page['name']='index' : $page['name']=$urlParts[1];
|
||||
unset($urlParts[1]);
|
||||
}
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiens le fragment depuis une variable serveur,
|
||||
* ce qui est selon moi possible avec une bonne configuration serveur
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@
|
|||
@section('top-css')
|
||||
<link rel="stylesheet" href="{{ \MVC\Classe\Url::asset_rewrite('assets/bootstrap-5.0.0-beta1-dist/css/bootstrap.min.css')}}">
|
||||
<link rel="stylesheet" href="{{ \MVC\Classe\Url::asset_rewrite('assets/css/custom.css')}}">
|
||||
@if(\MVC\Classe\Browser::get() == 'Internet Explorer')
|
||||
<link rel="stylesheet" href="{{\MVC\Classe\Url::asset_rewrite('assets/html5-simple-date-input-polyfill-master/html5-simple-date-input-polyfill.css')}}">
|
||||
<!--<link rel="stylesheet" href="{{\MVC\Classe\Url::asset_rewrite('assets/hyperform-0.12.0/css/hyperform.css')}}">-->
|
||||
@endif
|
||||
@show
|
||||
|
||||
</head>
|
||||
|
|
@ -37,6 +41,15 @@
|
|||
<body>
|
||||
|
||||
@section('top-javascript')
|
||||
@if(\MVC\Classe\Browser::get() == 'Internet Explorer')
|
||||
<!-- Polyfill.io will load polyfills your browser needs -->
|
||||
<script src="https://polyfill.io/v3/polyfill.min.js?features=default%2CNumber.parseInt%2CNumber.parseFloat%2CArray.prototype.find%2CArray.prototype.includes"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.12.1/polyfill.min.js" integrity="sha512-uzOpZ74myvXTYZ+mXUsPhDF+/iL/n32GDxdryI2SJronkEyKC8FBFRLiBQ7l7U/PTYebDbgTtbqTa6/vGtU23A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
|
||||
<script src="{{\MVC\Classe\Url::asset_rewrite('assets/html5-simple-date-input-polyfill-master/html5-simple-date-input-polyfill.js')}}"></script>
|
||||
<script src="{{\MVC\Classe\Url::asset_rewrite('assets/hyperform-0.12.0/dist/hyperform.js')}}"></script>
|
||||
<script>hyperform(window);</script>
|
||||
@endif
|
||||
@show
|
||||
|
||||
@yield('body')
|
||||
|
|
@ -45,6 +58,7 @@
|
|||
<script src="{{ \MVC\Classe\Url::asset_rewrite('assets/bootstrap-5.0.0-beta1-dist/js/bootstrap.min.js')}}"></script>
|
||||
<script src="{{ \MVC\Classe\Url::asset_rewrite('assets/js/custom.js')}}"></script>
|
||||
|
||||
@if(\MVC\Classe\Browser::get() !== 'Internet Explorer')
|
||||
<script>
|
||||
|
||||
/*
|
||||
|
|
@ -71,6 +85,7 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
@endif
|
||||
@show
|
||||
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -7,43 +7,44 @@
|
|||
@endsection
|
||||
|
||||
@section('content')
|
||||
<h1>%%PAGE%% - VUE.js Controlleur</h1>
|
||||
<br/><br/><br/>
|
||||
<div id="app">
|
||||
<div>
|
||||
<input v-model="searchText" placeholder="Search...">
|
||||
</div>
|
||||
<div v-if="is_loading">
|
||||
<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
|
||||
</div>
|
||||
<div v-if="items" >
|
||||
<a href="#" v-for="item in itemsSearched" :key="item.id">
|
||||
<div>
|
||||
<div>
|
||||
<h2>
|
||||
@{{ item.title }}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
@{{ item.description.slice(0, 300) + "..." }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>Year : @{{ item.release_date }}</span>
|
||||
<span>Director : @{{ item.director }}</span>
|
||||
<span>Producer : @{{ item.producer }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h1>ghibli - VUE.js Controlleur</h1>
|
||||
<br/><br/><br/>
|
||||
<div id="app">
|
||||
<div>
|
||||
<input v-model="searchText" placeholder="Search...">
|
||||
</div>
|
||||
<div v-if="is_loading" id="is-loading">
|
||||
<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
|
||||
</div>
|
||||
<div v-if="items" >
|
||||
<a href="#" v-for="item in itemsSearched" :key="item.id">
|
||||
<div>
|
||||
<div>
|
||||
<h2>
|
||||
@{{ item.title }}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
@{{ item.description.slice(0, 300) + "..." }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>Year : @{{ item.release_date }}</span>
|
||||
<span>Director : @{{ item.director }}</span>
|
||||
<span>Producer : @{{ item.producer }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('bottom-javascript')
|
||||
@parent
|
||||
<script>
|
||||
@if(\MVC\Classe\Browser::get() !== 'Internet Explorer')
|
||||
const vue = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
|
|
@ -53,11 +54,11 @@
|
|||
},
|
||||
mounted() {
|
||||
axios
|
||||
.get('https://ghibliapi.herokuapp.com/films')
|
||||
.then(response => {
|
||||
this.items = response.data;
|
||||
.get('https://ghibliapi.herokuapp.com/films')
|
||||
.then(response => {
|
||||
this.items = response.data;
|
||||
this.is_loading = false
|
||||
})
|
||||
})
|
||||
.catch(error => console.log(error))
|
||||
},
|
||||
computed : {
|
||||
|
|
@ -78,5 +79,42 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
@else
|
||||
const vue = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
items: [],
|
||||
searchText: '',
|
||||
is_loading: true,
|
||||
},
|
||||
mounted: function() {
|
||||
axios
|
||||
.get('https://ghibliapi.herokuapp.com/films')
|
||||
.then(function(response) {
|
||||
this.items = response.data;
|
||||
this.is_loading = false;
|
||||
document.getElementById('is-loading').style.display = 'none';
|
||||
})
|
||||
.catch(function(error) {console.log(error)})
|
||||
},
|
||||
computed: {
|
||||
itemsSearched : function(){
|
||||
var self = this;
|
||||
if( this.searchText == ''){
|
||||
return this.items;
|
||||
}
|
||||
return this.items.filter(function(item){
|
||||
// https://www.reddit.com/r/vuejs/comments/62kfae/how_do_i_create_very_simple_instant_search_filter/
|
||||
// Must be of string type
|
||||
return item.title.toLowerCase().indexOf(self.searchText) >= 0 ||
|
||||
item.producer.toLowerCase().indexOf(self.searchText) >= 0 ||
|
||||
item.director.toLowerCase().indexOf(self.searchText) >= 0 ||
|
||||
item.release_date.toString().indexOf(self.searchText) >= 0;
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@endif
|
||||
</script>
|
||||
@endsection
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 liorwohl
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# html5-simple-date-input-polyfill
|
||||
Just include this simple script and IE (>=10) and Firefox will support `<input type="date">` without any dependencies, not even jQuery! 🎉
|
||||
|
||||
Support dynamically created inputs, so can be used in single page applications.
|
||||
|
||||
Support [AngularJS](https://github.com/angular/angular.js) (and possibly other libraries) bindings.
|
||||
|
||||
# Usage
|
||||
|
||||
#### browserify
|
||||
|
||||
`npm install html5-simple-date-input-polyfill --save`
|
||||
|
||||
`require('html5-simple-date-input-polyfill');`
|
||||
|
||||
#### Browser
|
||||
|
||||
`<link rel="stylesheet" href="html5-simple-date-input-polyfill.css" />`
|
||||
|
||||
`<script src="html5-simple-date-input-polyfill.min.js"></script>`
|
||||
|
||||
#### SCSS (optional)
|
||||
`@import "../node_modules/html5-simple-date-input-polyfill/html5-simple-date-input-polyfill.scss";`
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
.calendar,
|
||||
.calendar select,
|
||||
.calendar table,
|
||||
.calendar td,
|
||||
.calendar th {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
text-shadow: none;
|
||||
height: auto;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
z-index:1000;
|
||||
}
|
||||
|
||||
.calendarContainer{
|
||||
display:block !important;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
position: absolute;
|
||||
border: 1px solid #c0c0c0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar select {
|
||||
margin: 3px 5px;
|
||||
border: 1px solid #c0c0c0;
|
||||
}
|
||||
|
||||
.calendar td,
|
||||
.calendar th {
|
||||
width: 14%;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar td {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.calendar .selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
function calendarExtender (theInput) {
|
||||
|
||||
var self = this;
|
||||
|
||||
this.theInput = theInput;
|
||||
this.container = null;
|
||||
this.theCalDiv = null;
|
||||
this.selectedDate = new Date();
|
||||
|
||||
this.init = function () {
|
||||
this.getDateFromInput();
|
||||
this.createCal();
|
||||
};
|
||||
|
||||
//update selectedDate with the date from the input, return true if changed
|
||||
this.getDateFromInput = function () {
|
||||
if (this.theInput.value) {
|
||||
var possibleNewDate = new Date(this.theInput.value);
|
||||
if (Date.parse(this.theInput.value) && possibleNewDate.toDateString() !== this.selectedDate.toDateString()) {
|
||||
this.selectedDate = possibleNewDate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
//create the calendar html and events
|
||||
this.createCal = function () {
|
||||
//creating a container div around the input, the calendar will also be there
|
||||
this.container = document.createElement('div');
|
||||
this.container.className = 'calendarContainer';
|
||||
this.container.style.display = 'inline-block';
|
||||
this.theInput.parentNode.replaceChild(this.container, this.theInput);
|
||||
this.container.appendChild(this.theInput);
|
||||
|
||||
//the calendar div
|
||||
this.theCalDiv = document.createElement('div');
|
||||
this.theCalDiv.className = 'calendar';
|
||||
this.theCalDiv.style.display = 'none';
|
||||
this.container.appendChild(this.theCalDiv);
|
||||
|
||||
//the year and month selects inside the calendar
|
||||
this.creathYearAndMonthSelects();
|
||||
|
||||
//the days table inside the calendar
|
||||
this.createMonthTable();
|
||||
|
||||
//open the calendar when the input get focus, also on various click events to capture it in all corner cases
|
||||
this.theInput.addEventListener('focus', function () { self.theCalDiv.style.display = ''; });
|
||||
this.theInput.addEventListener('mouseup', function () { self.theCalDiv.style.display = ''; });
|
||||
this.theInput.addEventListener('mousedown', function () { self.theCalDiv.style.display = ''; });
|
||||
|
||||
//update the calendar if the date changed manually in the input
|
||||
this.theInput.addEventListener('keyup', function () {
|
||||
if (self.getDateFromInput()) {
|
||||
self.updateSelecteds();
|
||||
}
|
||||
});
|
||||
|
||||
//close the calendar when clicking outside of the input or calendar
|
||||
document.addEventListener('click', function (e) {
|
||||
if (e.target.parentNode !== self.container &&
|
||||
e.target.parentNode.parentNode !== self.container &&
|
||||
e.target.parentNode.parentNode !== self.theCalDiv
|
||||
) {
|
||||
self.theCalDiv.style.display = 'none';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//create the year and month selects html
|
||||
this.creathYearAndMonthSelects = function () {
|
||||
//the year selector inside the calendar
|
||||
var yearSelect = this.createRangeSelect(new Date().getFullYear() - 80, new Date().getFullYear() + 20, this.selectedDate.getFullYear());
|
||||
yearSelect.className = 'yearSelect';
|
||||
this.theCalDiv.appendChild(yearSelect);
|
||||
yearSelect.onchange = function () {
|
||||
self.selectedDate.setYear(this.value);
|
||||
self.selectDate();
|
||||
self.createMonthTable();
|
||||
self.theInput.focus();
|
||||
};
|
||||
|
||||
//the month selector inside the calendar
|
||||
var monthsNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
||||
var monthSelect = this.createRangeSelect(0, 11, this.selectedDate.getMonth(), monthsNames);
|
||||
monthSelect.className = 'monthSelect';
|
||||
this.theCalDiv.appendChild(monthSelect);
|
||||
monthSelect.onchange = function () {
|
||||
self.selectedDate.setMonth(this.value);
|
||||
self.selectDate();
|
||||
self.createMonthTable();
|
||||
self.theInput.focus();
|
||||
};
|
||||
};
|
||||
|
||||
//update the year and month selects with the right selected value (if date changed externally)
|
||||
this.updateSelecteds = function () {
|
||||
this.theCalDiv.querySelector('.yearSelect').value = this.selectedDate.getFullYear();
|
||||
this.theCalDiv.querySelector('.monthSelect').value = this.selectedDate.getMonth();
|
||||
this.createMonthTable();
|
||||
};
|
||||
|
||||
//create the days table
|
||||
this.createMonthTable = function () {
|
||||
var year = this.selectedDate.getFullYear(); //get the year (2015)
|
||||
var month = this.selectedDate.getMonth(); //get the month number (0-11)
|
||||
var startDay = new Date(year, month, 1).getDay(); //first weekday of month (0-6)
|
||||
var maxDays = new Date(this.selectedDate.getFullYear(), month + 1, 0).getDate(); //get days in month (1-31)
|
||||
|
||||
//if there was a table before, remove it
|
||||
var oldTables = this.theCalDiv.getElementsByTagName('table');
|
||||
if (oldTables.length > 0) {
|
||||
this.theCalDiv.removeChild(oldTables[0]);
|
||||
}
|
||||
|
||||
//the table and header for the month days
|
||||
var theTable = document.createElement('table');
|
||||
theTable.innerHTML = '<tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th></tr>';
|
||||
this.theCalDiv.appendChild(theTable);
|
||||
|
||||
//create the days cols according to the selected month days
|
||||
var aRow;
|
||||
var aCell;
|
||||
for (var cellNum = 0; cellNum < maxDays + startDay; cellNum++) {
|
||||
|
||||
//crate a table row in the begining and after each 7 cells
|
||||
if (cellNum % 7 === 0) {
|
||||
aRow = theTable.insertRow(-1);
|
||||
}
|
||||
|
||||
aCell = aRow.insertCell(-1);
|
||||
|
||||
if (cellNum + 1 > startDay) {
|
||||
|
||||
var dayNum = cellNum + 1 - startDay;
|
||||
aCell.innerHTML = dayNum;
|
||||
if (dayNum === this.selectedDate.getDate()) {
|
||||
aCell.className = 'selected';
|
||||
}
|
||||
|
||||
//when clicking on a day in the days table
|
||||
aCell.addEventListener('click', function () {
|
||||
|
||||
//mark the dey with 'selected' css class
|
||||
self.theCalDiv.querySelector('.selected').className = '';
|
||||
this.className = 'selected';
|
||||
|
||||
self.selectedDate.setDate(parseInt(this.innerHTML));
|
||||
self.selectDate();
|
||||
self.theInput.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//copy the selected date to the input field
|
||||
this.selectDate = function () {
|
||||
|
||||
var monthText = this.selectedDate.getMonth() + 1;
|
||||
if (monthText < 10) {
|
||||
monthText = '0' + monthText;
|
||||
}
|
||||
|
||||
var dayText = this.selectedDate.getDate();
|
||||
if (dayText < 10) {
|
||||
dayText = '0' + dayText;
|
||||
}
|
||||
|
||||
this.theInput.value = '' + this.selectedDate.getFullYear() + '-' + monthText + '-' + dayText + '';
|
||||
|
||||
//make angular see the change
|
||||
var fakeEvent = document.createEvent('KeyboardEvent');
|
||||
fakeEvent.initEvent("change", true, false);
|
||||
this.theInput.dispatchEvent(fakeEvent);
|
||||
};
|
||||
|
||||
//helper function to create html select tags
|
||||
this.createRangeSelect = function (min, max, selected, namesArray) {
|
||||
var aOption;
|
||||
var curNum;
|
||||
var theText;
|
||||
|
||||
var theSelect = document.createElement('select');
|
||||
|
||||
for (curNum = min; curNum <= max; curNum++) {
|
||||
aOption = document.createElement('option');
|
||||
theSelect.appendChild(aOption);
|
||||
|
||||
if (namesArray) {
|
||||
theText = namesArray[curNum - min];
|
||||
} else {
|
||||
theText = curNum;
|
||||
}
|
||||
|
||||
aOption.text = theText;
|
||||
aOption.value = curNum;
|
||||
|
||||
if (curNum === selected) {
|
||||
aOption.selected = true;
|
||||
}
|
||||
};
|
||||
|
||||
return theSelect;
|
||||
}
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
//return false if the browser dont support input[type=date]
|
||||
function checkDateInputSupport () {
|
||||
var input = document.createElement('input');
|
||||
input.setAttribute('type','date');
|
||||
|
||||
var notADateValue = 'not-a-date';
|
||||
input.setAttribute('value', notADateValue);
|
||||
|
||||
return !(input.value === notADateValue);
|
||||
}
|
||||
|
||||
//will add the calendarExtender to all inputs in the page
|
||||
function addcalendarExtenderToDateInputs () {
|
||||
//get and loop all the input[type=date]s in the page that dont have "haveCal" class yet
|
||||
var dateInputs = document.querySelectorAll('input[type=date]:not(.haveCal)');
|
||||
[].forEach.call(dateInputs, function (dateInput) {
|
||||
//call calendarExtender function on the input
|
||||
new calendarExtender(dateInput);
|
||||
//mark that it have calendar
|
||||
dateInput.classList.add('haveCal');
|
||||
});
|
||||
}
|
||||
|
||||
//run the above code on any <input type='date'> in the document, also on dynamically created ones
|
||||
//check if type=date is supported or if not mobile, they have built-in support for type='date'
|
||||
if (!checkDateInputSupport() && typeof window.orientation === 'undefined') {
|
||||
addcalendarExtenderToDateInputs();
|
||||
//this is also on mousedown event so it will capture new inputs that might joined to the dom dynamically
|
||||
document.querySelector('body').addEventListener('mousedown', function (event) {
|
||||
addcalendarExtenderToDateInputs();
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
function calendarExtender(c){var a=this;this.theInput=c;this.theCalDiv=this.container=null;this.selectedDate=new Date;this.init=function(){this.getDateFromInput();this.createCal()};this.getDateFromInput=function(){if(this.theInput.value){var b=new Date(this.theInput.value);if(Date.parse(this.theInput.value)&&b.toDateString()!==this.selectedDate.toDateString())return this.selectedDate=b,!0}return!1};this.createCal=function(){this.container=document.createElement("div");this.container.className="calendarContainer";
|
||||
this.container.style.display="inline-block";this.theInput.parentNode.replaceChild(this.container,this.theInput);this.container.appendChild(this.theInput);this.theCalDiv=document.createElement("div");this.theCalDiv.className="calendar";this.theCalDiv.style.display="none";this.container.appendChild(this.theCalDiv);this.creathYearAndMonthSelects();this.createMonthTable();this.theInput.addEventListener("focus",function(){a.theCalDiv.style.display=""});this.theInput.addEventListener("mouseup",function(){a.theCalDiv.style.display=
|
||||
""});this.theInput.addEventListener("mousedown",function(){a.theCalDiv.style.display=""});this.theInput.addEventListener("keyup",function(){a.getDateFromInput()&&a.updateSelecteds()});document.addEventListener("click",function(b){b.target.parentNode!==a.container&&b.target.parentNode.parentNode!==a.container&&b.target.parentNode.parentNode!==a.theCalDiv&&(a.theCalDiv.style.display="none")})};this.creathYearAndMonthSelects=function(){var b=this.createRangeSelect((new Date).getFullYear()-80,(new Date).getFullYear()+
|
||||
20,this.selectedDate.getFullYear());b.className="yearSelect";this.theCalDiv.appendChild(b);b.onchange=function(){a.selectedDate.setYear(this.value);a.selectDate();a.createMonthTable();a.theInput.focus()};b=this.createRangeSelect(0,11,this.selectedDate.getMonth(),"January February March April May June July August September October November December".split(" "));b.className="monthSelect";this.theCalDiv.appendChild(b);b.onchange=function(){a.selectedDate.setMonth(this.value);a.selectDate();a.createMonthTable();
|
||||
a.theInput.focus()}};this.updateSelecteds=function(){this.theCalDiv.querySelector(".yearSelect").value=this.selectedDate.getFullYear();this.theCalDiv.querySelector(".monthSelect").value=this.selectedDate.getMonth();this.createMonthTable()};this.createMonthTable=function(){var b=this.selectedDate.getFullYear(),f=this.selectedDate.getMonth(),b=(new Date(b,f,1)).getDay(),f=(new Date(this.selectedDate.getFullYear(),f+1,0)).getDate(),c=this.theCalDiv.getElementsByTagName("table");0<c.length&&this.theCalDiv.removeChild(c[0]);
|
||||
c=document.createElement("table");c.innerHTML="<tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th></tr>";this.theCalDiv.appendChild(c);for(var g,e,d=0;d<f+b;d++)if(0===d%7&&(g=c.insertRow(-1)),e=g.insertCell(-1),d+1>b){var h=d+1-b;e.innerHTML=h;h===this.selectedDate.getDate()&&(e.className="selected");e.addEventListener("click",function(){a.theCalDiv.querySelector(".selected").className="";this.className="selected";a.selectedDate.setDate(parseInt(this.innerHTML));
|
||||
a.selectDate();a.theInput.focus()})}};this.selectDate=function(){var b=this.selectedDate.getMonth()+1;10>b&&(b="0"+b);var a=this.selectedDate.getDate();10>a&&(a="0"+a);this.theInput.value=""+this.selectedDate.getFullYear()+"-"+b+"-"+a+"";b=document.createEvent("KeyboardEvent");b.initEvent("change",!0,!1);this.theInput.dispatchEvent(b)};this.createRangeSelect=function(b,a,c,g){var e,d,h,f=document.createElement("select");for(d=b;d<=a;d++)e=document.createElement("option"),f.appendChild(e),h=g?g[d-
|
||||
b]:d,e.text=h,e.value=d,d===c&&(e.selected=!0);return f};this.init()}function checkDateInputSupport(){var c=document.createElement("input");c.setAttribute("type","date");c.setAttribute("value","not-a-date");return"not-a-date"!==c.value}function addcalendarExtenderToDateInputs(){var c=document.querySelectorAll("input[type=date]:not(.haveCal)");[].forEach.call(c,function(a){new calendarExtender(a);a.classList.add("haveCal")})}
|
||||
checkDateInputSupport()||"undefined"!==typeof window.orientation||(addcalendarExtenderToDateInputs(),document.querySelector("body").addEventListener("mousedown",function(c){addcalendarExtenderToDateInputs()}));
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
@mixin reset() {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
text-shadow: none;
|
||||
height: auto;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
&, select, table, th, td {
|
||||
@include reset();
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
border: 1px solid #c0c0c0;
|
||||
text-align: center;
|
||||
|
||||
select {
|
||||
margin: 3px 5px;
|
||||
border: 1px solid #c0c0c0;
|
||||
}
|
||||
|
||||
th, td {
|
||||
width: 14%;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "html5-simple-date-input-polyfill",
|
||||
"version": "1.1.0",
|
||||
"description": "input type date polyfill",
|
||||
"main": "html5-simple-date-input-polyfill.js",
|
||||
"author": {
|
||||
"name": "Lior Wohl",
|
||||
"email": "liorwohl@gmail.com"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/liorwohl/html5-simple-date-input-polyfill.git"
|
||||
},
|
||||
"bugs": "https://github.com/liorwohl/html5-simple-date-input-polyfill/issues",
|
||||
"keywords": ["html5","date","datepicker","type"],
|
||||
"analyze": true,
|
||||
"license": "MIT",
|
||||
"readme": "ERROR: No README data found!"
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<link rel="stylesheet" href="html5-simple-date-input-polyfill.css" />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
normal:
|
||||
<input type="date" />
|
||||
|
||||
with value:
|
||||
<input type="date" value="2015-03-28" />
|
||||
|
||||
dynamically created:
|
||||
<script>
|
||||
setTimeout(function(){
|
||||
var input = document.createElement("input");
|
||||
input.type = "date";
|
||||
document.body.appendChild(input);
|
||||
}, 2000);
|
||||
</script>
|
||||
|
||||
<script src="html5-simple-date-input-polyfill.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
2
public/assets/hyperform-0.12.0/.gitignore
vendored
Normal file
2
public/assets/hyperform-0.12.0/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/node_modules
|
||||
npm-debug.log
|
||||
8
public/assets/hyperform-0.12.0/.npmignore
Normal file
8
public/assets/hyperform-0.12.0/.npmignore
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
**/.*
|
||||
bower.json
|
||||
bower_components
|
||||
examples
|
||||
.gitignore
|
||||
Makefile
|
||||
node_modules
|
||||
.travis.yml
|
||||
7
public/assets/hyperform-0.12.0/.travis.yml
Normal file
7
public/assets/hyperform-0.12.0/.travis.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- node
|
||||
env:
|
||||
global:
|
||||
- secure: JsPlO0dP7mK7GEqUovqWsXzXY2cVRPvFM6ZJn3QIRRvXTrP4u/qcDpvKvgwR+DvVObLa9/exxEwcIqeYZJz5XKjET2JIgozJERACkJkZkiAjHWyDCuVd271OzNJ968TsOA9Rzzdq3fuv5wBiqjNuW3DK0nQwJiW4iL5ygsyOw0KNEcNVyvipWUY7n0GkIlnCPfi2SqaD+nQxocbsRuDAyCPEl5b01E3IgJPOuHUhBz4r2Dfs9eO5hf1eQSaPCDflvFyKvHIsfwUuN2NzgBXmT0quuoSTxR+V6k69eo2+FuShfOd8+0Z7FlyFnTA6bniBKaVbJ45GXYDA8ecGtYbmUqaT0iA7aUIuHvCBonW2m+x8tWvDR/mDFavdE6loR/RgR1mBtuY+dCKDNBBKFwss3iQar2aMepiU8AvrXq5ViV3rZk6ZFgIsKZ7QHW4kCghhOm0oXLPzaid4kezF9wIw4navEJh9x3+2CPwSh2++IhA8RiyJ5/XDBN5EoDQqxwP6fS3CJUImgM4qal0jRr20YJNeiovkH+yzVDW8frbecLQbpN+GEWyt1Wm9BG8nVccIej7Tc/ndUAnZLsYTv5qhwYp72XnHq/6fLVc4mTpCbA5hNAB8sWiep2NKvujbU7HYaPvoy5tb690ExAuBb4vHS4pND/9w3P5XmdCocVMamOo=
|
||||
- secure: I9qTcPli1f0IqgzGwQURRp9qi5vWVt0aKKQzUJNfNnhp7zMJEIm3+kTwuORxaFWi8CKq9qkqvL+MmQX3stfkJYCe3NrzHxCb15VgbysGOQtkH2L4WmXZbSWoQVMM9m8RxPZ8WSGdOsLZrdDJWDw2qKHg9RGEhdr9E5PMeOXnIWZx3Le/kdEKBct2QQquWUrKAYcGYfDAejQhj8hzjYBm/fmEchBq3voxUKZ0NwgIOlkgg3pjs9Y/Er/OCzagFkBWuR8HadbhLhPU2CMbnwO97ntUC3taTZHae+xI6n5/4WK5mUZx1M5q5AmriOZ6M/mbxkxrcRx4Ohyzzh/fyoei4Xp4K5MEoibl7L0wD8J58FOEmPn3kUWgHXl17SuQvPpGj98G9Xz5rwUYza5SBLUvtjPkPfQj2j1NXOpD/Vdj2ibidN2EVB2XmkTc99M9TNFVPWmxxfzMjpCQ/4s7sSJKXrjuIFaPUpgqdcP8F6UsAjXXfxnV1AgEGADCeJ7cNnOqq11lqQdfaD2Gk4Zh7OYgeikbQUWgRe4DAdhnfrbFZ22DgLj8hw1NyKCAYl7MxtjA3abZZ2y5v9k+PP4j+T7ijJEClNZ/0MAwbqarOtQnmPAkPjB62nPC4IOxjhnu/JSmBONTAoANvtvnRbHEgsW+bFPS+enjecmy8SRYsFk1NLg=
|
||||
508
public/assets/hyperform-0.12.0/CHANGELOG.md
Normal file
508
public/assets/hyperform-0.12.0/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
# Changelog
|
||||
|
||||
## UNRELEASED
|
||||
|
||||
## v0.12.0
|
||||
|
||||
* fix return values for ValidityState properties on non-input elements
|
||||
* add ES module build
|
||||
* fix dependency cycle. Rollup runs now w/o warnings
|
||||
* add support for calling Hyperform on arbitrary DOM nodes
|
||||
* add index.d.ts to npm package
|
||||
|
||||
## v0.11.0
|
||||
|
||||
* switch build tool to rollup
|
||||
* remove deprecated underscore names
|
||||
* fix revalidation for forms with novalidate attribute
|
||||
|
||||
## v0.10.2
|
||||
|
||||
* fix form validation error for detached elements
|
||||
|
||||
## v0.10.1
|
||||
|
||||
* fix event properties being set too late
|
||||
|
||||
## v0.10.0
|
||||
|
||||
* add new event "implicit_submit", that allows to prevent implicit submits
|
||||
|
||||
## v0.9.23
|
||||
|
||||
* fix application of class hf-user-invalid to checkboxes/radio buttons
|
||||
|
||||
## v0.9.22
|
||||
|
||||
* fix select boxes not respectin placeholder options
|
||||
* validate dates stricter
|
||||
* add TypeScript declarations
|
||||
|
||||
## v0.9.21
|
||||
|
||||
* fix problem, where badInput was not detected with hyperform(form) calls (issue #49)
|
||||
|
||||
## v0.9.20
|
||||
|
||||
* hide empty warnings with CSS
|
||||
* fix select boxes with disabled options selected validating as required
|
||||
|
||||
## v0.9.19
|
||||
|
||||
* fix custom validation messages gone missing (regression from v0.9.17)
|
||||
|
||||
## v0.9.18
|
||||
|
||||
* prevent infinite loops from custom validators
|
||||
|
||||
## v0.9.17
|
||||
|
||||
* add tests to npm package
|
||||
* live-update warnings, when `setCustomValidity()` is called
|
||||
* properly delete custom messages
|
||||
|
||||
## v0.9.16
|
||||
|
||||
* fix radio button warnings still being multiplied on submit
|
||||
|
||||
## v0.9.15
|
||||
|
||||
* update some dependencies
|
||||
* add `CHANGELOG.md`
|
||||
* prevent `is_validation_candidate()` from running twice for each validation
|
||||
* re-allow validation of elements without `name` attribute, if asked directly
|
||||
* fix validation for partly required radio groups
|
||||
* on form validation show only one warning per radio group
|
||||
|
||||
## v0.9.14
|
||||
|
||||
* prevent non-candidates from being handled
|
||||
|
||||
## v0.9.13
|
||||
|
||||
* fix tabbing into fields triggering revalidation
|
||||
|
||||
## v0.9.12
|
||||
|
||||
* fix `element.noValidate` being broken
|
||||
|
||||
## v0.9.11
|
||||
|
||||
* do not validate elements without name
|
||||
* add guard against trying to remove a detached warning
|
||||
* start removing default imports and add comments
|
||||
* switch to `babel-preset-env`
|
||||
* add a command to quickly generate a `.po` file for l10n
|
||||
|
||||
## v0.9.10
|
||||
|
||||
* Connect error messages via `aria-describedby`
|
||||
|
||||
## v0.9.9
|
||||
|
||||
* update README
|
||||
|
||||
## v0.9.8
|
||||
|
||||
* Fix for IE 10 & 11 not supporting multiple parameters for classList add() and remove() methods
|
||||
|
||||
## v0.9.7
|
||||
|
||||
* fix "novalidateOnElements" not added to settings
|
||||
|
||||
## v0.9.6
|
||||
|
||||
* use translation for base language, if available
|
||||
|
||||
## v0.9.5
|
||||
|
||||
* trigger a "forminvalid" event
|
||||
|
||||
## v0.9.4
|
||||
|
||||
* add option to autoload Hyperform
|
||||
|
||||
## v0.9.3
|
||||
|
||||
* fix bower.json's main field
|
||||
|
||||
## v0.9.2
|
||||
|
||||
* fix wrong value in `tooShort` message
|
||||
|
||||
## v0.9.1
|
||||
|
||||
* convert renderer methods to camelCase, too
|
||||
|
||||
## v0.9.0
|
||||
|
||||
* switch from snake_case to camelCase for public API
|
||||
* upgrade ava (please run "npm install" after pull)
|
||||
* remove mobile clients from test matrix, since they throw strange SauceLabs errors
|
||||
* split "make test" in sub-targets
|
||||
* extend saucelabs browsers to mobile
|
||||
* fix Safari `<=` 9 throwing error on uninstalling properties
|
||||
* enable tests for IE 9/10
|
||||
* crank up the test matrix
|
||||
* implement better "clicked on child of `<button>`" detection
|
||||
|
||||
## v0.8.15
|
||||
|
||||
* fix IE `<=` 10 not knowing HTMLDocument
|
||||
|
||||
## v0.8.14
|
||||
|
||||
* reintroduce accidentally deleted Firefox safe-guard
|
||||
|
||||
## v0.8.13
|
||||
|
||||
* change the way we evaluate prevented submit event
|
||||
* for whatever reason `insertRule()` now needs an explicit index
|
||||
|
||||
## v0.8.12
|
||||
|
||||
* add support for `\n` in error messages
|
||||
* fix child nodes mixing up `event.target` of button clicks
|
||||
|
||||
## v0.8.11
|
||||
|
||||
* allow filtering is_validation_candidate result
|
||||
|
||||
## v0.8.10
|
||||
|
||||
* make logging optional with debug=bool setting
|
||||
* add `.hf-user-valid`
|
||||
* add more classes to mirror pseudo-classes
|
||||
* adapt the date rendering in date and time input error messages
|
||||
|
||||
## v0.8.9
|
||||
|
||||
* fix IE not setting `defaultPrevented`
|
||||
|
||||
## v0.8.8
|
||||
|
||||
* catch case, where native properties cannot be overloaded
|
||||
|
||||
## v0.8.7
|
||||
|
||||
* ignore non-essential files on (bower|npm) install
|
||||
|
||||
## v0.8.6
|
||||
|
||||
* fix the `prevent_implicit_submit` switch
|
||||
|
||||
## v0.8.5
|
||||
|
||||
* add setting `prevent_implicit_submit`
|
||||
* apparently `originalTarget` is a protected getter on `Event`
|
||||
* streamline form submission better
|
||||
|
||||
## v0.8.4
|
||||
|
||||
* fix some problems with non-AJAX form submission
|
||||
|
||||
## v0.8.3
|
||||
|
||||
* filter attributes before being set/get
|
||||
|
||||
## v0.8.2
|
||||
|
||||
* fix evaluation of "formnovalidate" on submit buttons
|
||||
|
||||
## v0.8.1
|
||||
|
||||
* fix polyfills not being applied in global context
|
||||
* create hook registry w/o prototype
|
||||
* add add_filter as alias to add_hook
|
||||
* add current value as param to filters
|
||||
* add call_filter to filter a value through hooks
|
||||
* add hook infrastructure
|
||||
|
||||
## v0.8.0
|
||||
|
||||
* rename hyperform.register to hyperform.add_validator
|
||||
|
||||
## v0.7.7
|
||||
|
||||
* unify form submission a bit
|
||||
* add name=value of submit button to form submit
|
||||
|
||||
## v0.7.6
|
||||
|
||||
* disallow multiple wrappers per form
|
||||
|
||||
## v0.7.5
|
||||
|
||||
* complete uninstall process
|
||||
* define attribute helpers
|
||||
* fix calling issue and tests
|
||||
* add a "polyunfill" method mirroring polyfill()
|
||||
* create dedicated "polyfill" method
|
||||
* add a hybrid re-evaluation strategy
|
||||
* fix leaking implementation
|
||||
* allow renderer to be reset to default
|
||||
|
||||
## v0.7.4
|
||||
|
||||
* add /css to "files" setting in package.json
|
||||
* remove necessity for some ES6 shims
|
||||
* fix detection of revalidate=never
|
||||
* `const`-antize all the things!
|
||||
* fall back to attribute data-validator for custom validation messages
|
||||
* fix "blur" event delegation
|
||||
* allow "onblur" for settings.revalidate
|
||||
* support "never" for settings.revalidate
|
||||
* add support for per-element custom messages
|
||||
* make naming of component clearer
|
||||
* uninstall more properties
|
||||
* refactor validity checkers
|
||||
* factor out the huge validity state checkers
|
||||
* fix calls to polyfill w/ changed signature
|
||||
* change rest of polyfills to use explicit element arg
|
||||
* change some polyfills from `this` to explicit arg
|
||||
* set aria-live=polite on warnings
|
||||
* enhance setting propagation
|
||||
* switch case for a class
|
||||
* add a destroy method to Wrapper
|
||||
* polyfill some properties like element.maxLength
|
||||
* trim email/url before validation
|
||||
* fix possible loop in bad_input validator
|
||||
* postpone creation of DOM elements
|
||||
* add support for children of `<datalist>` not being validated
|
||||
* change overwrite behavior of property_installer
|
||||
* add missing methods to `<form>`
|
||||
|
||||
## v0.7.3
|
||||
|
||||
* branch out attach/detach renderer
|
||||
* fix naming error
|
||||
|
||||
## v0.7.2
|
||||
|
||||
* make classes for validation / warnings configurable
|
||||
* fix get_wrapper import :(
|
||||
* make Wrapper.get_wrapped a standalone function
|
||||
|
||||
## v0.7.1
|
||||
|
||||
* fix wrong values in error messages
|
||||
* fix bogged export
|
||||
|
||||
## v0.7.0
|
||||
|
||||
* add proper AMD and CJS versions
|
||||
|
||||
## v0.6.3
|
||||
|
||||
* fix evaluation of original badInput
|
||||
|
||||
## v0.6.2
|
||||
|
||||
* try to evaluate the original badInput state, too
|
||||
|
||||
## v0.6.1
|
||||
|
||||
* allow non-boolean returns in custom validators
|
||||
|
||||
## v0.6.0
|
||||
|
||||
* s/hyperform.add_renderer/hyperform.set_renderer/
|
||||
|
||||
## v0.5.12
|
||||
|
||||
* focus first invalid field on submit validation
|
||||
|
||||
## v0.5.11
|
||||
|
||||
* trigger a submit event manually when catching the original form submit
|
||||
* fix wrong validity calculation
|
||||
* call warning renderer for all radio buttons w/ same name
|
||||
* change the way custom validators are called in customError
|
||||
|
||||
## v0.5.10
|
||||
|
||||
* fix setCustomValidity setter
|
||||
|
||||
## v0.5.9
|
||||
|
||||
* fix try to set property on possible primitive
|
||||
|
||||
## v0.5.8
|
||||
|
||||
* know your own wrapper functions...
|
||||
|
||||
## v0.5.7
|
||||
|
||||
* trigger "validate" on form before submit
|
||||
|
||||
## v0.5.6
|
||||
|
||||
* do report errors on input validation
|
||||
|
||||
## v0.5.5
|
||||
|
||||
* fix reportValidity not removing warning
|
||||
|
||||
## v0.5.4
|
||||
|
||||
* fix the way the wrapped container is fetched
|
||||
* implement shortcut to find wrapper for element
|
||||
|
||||
## v0.5.3
|
||||
|
||||
* fix problems with maxlength (D'oh!) and Unicode string length
|
||||
|
||||
## v0.5.2
|
||||
|
||||
* apparently getElementsByName is not available on Element
|
||||
|
||||
## v0.5.1
|
||||
|
||||
* support dates in get_next_value
|
||||
* put step validation consts in own file
|
||||
* confirm step working as specced
|
||||
* fix ms calculation
|
||||
|
||||
## v0.5.0
|
||||
|
||||
* add support for @accept
|
||||
* fix minor errors
|
||||
|
||||
## v0.4.8
|
||||
|
||||
* add proper classes hf-(in)valid and aria-invalid on validation
|
||||
|
||||
## v0.4.7
|
||||
|
||||
* run validation for _all_ inputs of a form
|
||||
|
||||
## v0.4.6
|
||||
|
||||
* fix annoying errors
|
||||
|
||||
## v0.4.5
|
||||
|
||||
* add Wrapper.install to allow adding fields dynamically
|
||||
|
||||
## v0.4.4
|
||||
|
||||
* add support for a non-custom "valid" event
|
||||
|
||||
## v0.4.3
|
||||
|
||||
* enhance `<fieldset>` support
|
||||
|
||||
## v0.4.2
|
||||
|
||||
* add support for non-standard "novalidate" on `<input>` elements
|
||||
|
||||
## v0.4.1
|
||||
|
||||
* make sure the registry always returns an array
|
||||
* allow more than one custom validator per element
|
||||
|
||||
## v0.4.0
|
||||
|
||||
* add a registry for user-defined validators
|
||||
|
||||
## v0.3.1
|
||||
|
||||
* update README
|
||||
|
||||
## v0.3.0
|
||||
|
||||
* correct wrong typeof test
|
||||
* change public API to simple callable
|
||||
* support novalidate in submit catcher
|
||||
* add first versions of step(Up|Down)
|
||||
* fix type detection for valueAs*
|
||||
* make step validation for months more robust
|
||||
|
||||
## v0.2.4
|
||||
|
||||
* add checkmarks to feature table
|
||||
|
||||
## v0.2.3
|
||||
|
||||
* update README
|
||||
|
||||
## v0.2.2
|
||||
|
||||
* reset the validity again, when an element becomes valid
|
||||
* set the validation msg via original setCustomValidity
|
||||
* add styles
|
||||
* determine the type of an input more reliably
|
||||
|
||||
## v0.2.1
|
||||
|
||||
* fix issues and mask the WeakMap in message_store
|
||||
* catch form submission and call our own reportValidity
|
||||
* fix bugs
|
||||
* react appropriately in reportValidity, when event is canceled
|
||||
* publish Renderer.set as hyperform.add_renderer
|
||||
|
||||
## v0.2.0
|
||||
|
||||
* fix step validator for type=month
|
||||
* allow overflowing months / dates
|
||||
|
||||
## v0.1.9
|
||||
|
||||
* restrict npm package to src and dist folders
|
||||
* change l10n infrastructure
|
||||
|
||||
## v0.1.8
|
||||
|
||||
* make code more robust thanks to tests
|
||||
* update bower keyword list
|
||||
* fix npm version script
|
||||
|
||||
## v0.1.7
|
||||
|
||||
* add bower.json (for the good old times)
|
||||
* publish original method as `_original_method`
|
||||
|
||||
## v0.1.6
|
||||
|
||||
* support "jsnext:main" in package.json
|
||||
* implement most of ValidityState.badInput
|
||||
* disallow mark() on primitives
|
||||
|
||||
## v0.1.5
|
||||
|
||||
* fix problem with string_to_date parser
|
||||
|
||||
## v0.1.4
|
||||
|
||||
* fix determining current version during npm version bump
|
||||
|
||||
## v0.1.3
|
||||
|
||||
* switch to npm version for bumping versions
|
||||
|
||||
## v0.1.2
|
||||
|
||||
* allow step validator to consume most date types
|
||||
* add license
|
||||
|
||||
## v0.1.1
|
||||
|
||||
* support date types in max/min validators
|
||||
* prepare date support for validators
|
||||
* put type information in single place
|
||||
* fix minor issues
|
||||
* mark all polyfills with a "hyperform" property
|
||||
* change the way element.validity is installed
|
||||
* allow capturing all inputs via prototype
|
||||
* fix array search, rebuild
|
||||
* change sprintf implementation
|
||||
* fix getting Date from a week number
|
||||
* fix valueAs* and add jsdom to tests
|
||||
* fix padding in date_to_string
|
||||
* fix valueAs*.install
|
||||
* add valueAsNumber and fix issues with valueAsDate
|
||||
* add polyfill for valueAsDate
|
||||
* add version to interface
|
||||
* fix some errors
|
||||
* add build infrastructure
|
||||
* start implementing the HTML5 form validation API
|
||||
81
public/assets/hyperform-0.12.0/CONTRIBUTING.md
Normal file
81
public/assets/hyperform-0.12.0/CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Contributing to Hyperform
|
||||
|
||||
Cool, thanks for joining in and welcome aboard! If you have Node.js and `make`
|
||||
installed, you are ready to start.
|
||||
|
||||
**Before you start editing:** If you don’t directly fix an already reported
|
||||
issue, please do open a new one before! Otherwise there might be the chance,
|
||||
that your work is not fully aligned with Hyperform’s goals, and your time
|
||||
wasted.
|
||||
|
||||
## Set-Up
|
||||
|
||||
Log in to [GitHub](https://github.com) and fork
|
||||
[Hyperform](https://github.com/hyperform/hyperform) (button in the upper-right
|
||||
corner). Then switch to your terminal:
|
||||
|
||||
```sh
|
||||
$ git clone git@github.com:YourUserName/hyperform.git
|
||||
$ cd hyperform
|
||||
$ npm install
|
||||
# now you're ready to go. Try your first build to see if everything works:
|
||||
$ make -B && git status
|
||||
```
|
||||
|
||||
Git should show no file changes. Start editing the files in `src` and build
|
||||
again with `make`.
|
||||
|
||||
## Testing Your Edit
|
||||
|
||||
For this you need a [SauceLabs](https://saucelabs.com/) account. It’s free to
|
||||
register and allows testing in a bunch of browsers concurrently. Export your
|
||||
SauceLabs API token from your profile page to your shell:
|
||||
|
||||
```sh
|
||||
$ export SAUCE_USERNAME=your_saucelabs_user
|
||||
$ export SAUCE_ACCESS_KEY=your_api_key
|
||||
```
|
||||
|
||||
Then you can run all tests with a single command:
|
||||
|
||||
```sh
|
||||
$ make test
|
||||
```
|
||||
|
||||
If you do not want to create a SauceLabs account, you can also do the tests
|
||||
manually:
|
||||
|
||||
```sh
|
||||
$ make test-syntax
|
||||
$ make test-unit
|
||||
```
|
||||
|
||||
and then open `test/functional/index.html` in your browser and verify, that
|
||||
all tests return green.
|
||||
|
||||
**Attention:** The functional tests are performed on `dist/hyperform.js`. Don’t
|
||||
forget to `make` that file prior to testing!
|
||||
|
||||
## Keeping a Look at the File Size
|
||||
|
||||
If you have [`gnuplot`](http://gnuplot.sourceforge.net/) installed, try
|
||||
|
||||
```sh
|
||||
$ make cmpsize
|
||||
```
|
||||
|
||||
This produces a nice little chart of how the size of `dist/hyperform.min.js`
|
||||
changed over time. If you notice a huge peak at the very end, maybe there could
|
||||
be one or the other byte shoved off before you commit :wink:.
|
||||
|
||||
## Submitting a Pull Request
|
||||
|
||||
See [Github’s help page](https://help.github.com/articles/using-pull-requests/)
|
||||
on how that works exactly (with screenshots!). Please try to make title and
|
||||
description of the change request meaningful.
|
||||
|
||||
## If Something Goes Wrong
|
||||
|
||||
If you encounter any problem, grab Manuel on
|
||||
[Twitter](https://twitter.com/m_strehl) or via
|
||||
[e-mail](http://www.manuel-strehl.de/about/contact).
|
||||
18
public/assets/hyperform-0.12.0/LICENSE.md
Normal file
18
public/assets/hyperform-0.12.0/LICENSE.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Copyright © 2016 Manuel Strehl <boldewyn@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM-
|
||||
PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
74
public/assets/hyperform-0.12.0/Makefile
Normal file
74
public/assets/hyperform-0.12.0/Makefile
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
ROLLUP := ./node_modules/.bin/rollup
|
||||
ROLLUP_ARGS := -c
|
||||
|
||||
JSHINT := node_modules/.bin/jshint
|
||||
JSHINT_ARGS :=
|
||||
|
||||
all: js
|
||||
.PHONY: all
|
||||
|
||||
js: dist/hyperform.js dist/hyperform.min.js \
|
||||
dist/hyperform.amd.js dist/hyperform.amd.min.js \
|
||||
dist/hyperform.cjs.js dist/hyperform.cjs.min.js \
|
||||
dist/hyperform.esm.js dist/hyperform.esm.min.js
|
||||
.PHONY: js
|
||||
|
||||
# see
|
||||
# https://stackoverflow.com/a/10609434/113195
|
||||
# for this trick to invoke rollup just once for all files
|
||||
dist/hyperform.amd.min.js \
|
||||
dist/hyperform.cjs.min.js \
|
||||
dist/hyperform.esm.min.js \
|
||||
dist/hyperform.min.js \
|
||||
dist/hyperform.js \
|
||||
dist/hyperform.amd.js \
|
||||
dist/hyperform.cjs.js \
|
||||
dist/hyperform.esm.js: intermediate-build-step
|
||||
|
||||
.INTERMEDIATE: intermediate-build-step
|
||||
intermediate-build-step: src/hyperform.js src/*.js src/*/*.js
|
||||
@echo "* build $@"
|
||||
@mkdir -p dist
|
||||
@$(ROLLUP) $(ROLLUP_ARGS)
|
||||
|
||||
test: test-syntax test-unit test-functional
|
||||
.PHONY: test
|
||||
|
||||
test-syntax:
|
||||
@echo "* run syntax tests"
|
||||
@$(JSHINT) $(JSHINT_ARGS) src
|
||||
.PHONY: test-syntax
|
||||
|
||||
test-unit:
|
||||
@echo "* run unit tests"
|
||||
@node_modules/.bin/ava
|
||||
.PHONY: test-unit
|
||||
|
||||
test-functional:
|
||||
@echo "* run functional tests"
|
||||
@node_modules/.bin/karma start karma.conf.js
|
||||
.PHONY: test-functional
|
||||
|
||||
version:
|
||||
@# needs a VERSION= variable on the command line!
|
||||
@# assumes line 3 in bower.json is the version!
|
||||
@if [ ! -z '$(VERSION)' ]; then \
|
||||
sed -i '/^export default '"'"'[0-9.]\+'"'"';$$/c\export default '"'"'$(VERSION)'"'"';' src/version.js; \
|
||||
sed -i '3c\ "version": "$(VERSION)",' bower.json; \
|
||||
sed -i 's/## UNRELEASED$$/## UNRELEASED\n\n## v$(VERSION)/' CHANGELOG.md; \
|
||||
fi
|
||||
.PHONY: version
|
||||
|
||||
GNUPLOT_STYLE := impulses
|
||||
|
||||
cmpsize:
|
||||
git log --reverse --pretty=format:%H dist/hyperform.min.js | \
|
||||
( \
|
||||
while read x; do git show "$$x:dist/hyperform.min.js" | wc -c ; done; \
|
||||
cat dist/hyperform.min.js | wc -c \
|
||||
) | \
|
||||
gnuplot -p -e "set ylabel 'bytes'; set key outside; set key above; plot '< cat' using 1 title 'size of dist/hyperform.min.js' with $(GNUPLOT_STYLE)"
|
||||
.PHONY: cmpsize
|
||||
|
||||
translate.po: src/*.js src/*/*.js
|
||||
xgettext -LJavascript -k_ -o $@ --from-code utf-8 $^
|
||||
191
public/assets/hyperform-0.12.0/README.md
Normal file
191
public/assets/hyperform-0.12.0/README.md
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
# 
|
||||
[](https://cdnjs.com/libraries/hyperform)
|
||||
## Capture form validation back from the browser
|
||||
|
||||
Hyperform is your one-stop solution for client-side form handling.
|
||||
|
||||
It features a complete implementation of the HTML5 form validation API in
|
||||
JavaScript, replaces the browser’s native methods (if they are even
|
||||
implemented…), and enriches your toolbox with custom events and hooks.
|
||||
|
||||
Not pumped yet? Then [take a look](https://hyperform.js.org/examples.html) at
|
||||
our awesome [examples](https://hyperform.js.org/examples.html).
|
||||
|
||||
## Installation
|
||||
|
||||
### Embed from a CDN
|
||||
|
||||
Get up and running with Hyperform by embedding it from a CDN:
|
||||
[CDNJS](https://cdnjs.com/libraries/hyperform)
|
||||
|
||||
```html
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/hyperform/0.9.5/hyperform.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/hyperform/0.9.5/hyperform.min.css">
|
||||
```
|
||||
|
||||
or unpkg.com:
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/hyperform"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/hyperform@latest/css/hyperform.css">
|
||||
```
|
||||
|
||||
### Install locally
|
||||
|
||||
The easiest way is installing via `npm`:
|
||||
|
||||
```sh
|
||||
npm install hyperform
|
||||
```
|
||||
|
||||
or you can use Bower:
|
||||
|
||||
```sh
|
||||
bower install hyperform
|
||||
```
|
||||
|
||||
or download the [current version as ZIP
|
||||
archive](https://github.com/hyperform/hyperform/archive/master.zip).
|
||||
|
||||
Then embed `dist/hyperform.min.js` in your file:
|
||||
|
||||
```html
|
||||
<script src="path/to/hyperform/dist/hyperform.min.js"></script>
|
||||
```
|
||||
|
||||
or require it in your code:
|
||||
|
||||
```js
|
||||
const hyperform = require('hyperform');
|
||||
```
|
||||
|
||||
In old browsers you will need polyfills for the following features:
|
||||
[`WeakMap`](https://github.com/Benvie/WeakMap) (IE 10 and lower),
|
||||
[`element.classList`](https://github.com/remy/polyfills) (IE 9 and lower),
|
||||
`array.filter`, `array.every`, `Object.keys` and
|
||||
`Object.defineProperty` (IE 8 and lower). For IE 9+ support simply add this
|
||||
line to use [Polyfill.io’s service](https://polyfill.io):
|
||||
|
||||
```html
|
||||
<script src="https://polyfill.io/v2/polyfill.min.js?features=Element.prototype.classList,WeakMap"></script>
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
You can let Hyperform take over a single form:
|
||||
|
||||
```js
|
||||
hyperform(document.forms[0]);
|
||||
```
|
||||
|
||||
or all forms, current and future ones:
|
||||
|
||||
```js
|
||||
hyperform(window);
|
||||
```
|
||||
|
||||
Configure settings as second argument:
|
||||
|
||||
```js
|
||||
hyperform(window, { revalidate: 'never' });
|
||||
```
|
||||
|
||||
If you only need a certain feature once, you can access it directly by name:
|
||||
|
||||
```js
|
||||
if (hyperform.willValidate(some_input_element)) {
|
||||
var is_valid = hyperform.validityState(some_input_element).valid;
|
||||
}
|
||||
```
|
||||
|
||||
[The full documentation](https://hyperform.js.org/docs/) provides you with all
|
||||
the nitty-gritty details and tricks.
|
||||
|
||||
### What About the UI?
|
||||
|
||||
You might be wondering, how to get nifty datepickers and range sliders and
|
||||
stuff. Unfortunately, this is out of topic for Hyperform, but despair not!
|
||||
[Hyperform UI](https://github.com/hyperform/hyperform-ui) (beta) is here to
|
||||
fill in the gaps with the help of jQuery UI.
|
||||
|
||||
> “jQuery UI? Isn’t that that thing that they had before React?” — “No, that’s
|
||||
> Backbone.” — “But before that?” — “No, that was Kendo.” — “...?”
|
||||
|
||||
If you had these thoughts right now, rest assured. For the purpose of input
|
||||
widgets there is still close to no other library, that is complete, themable,
|
||||
accessible and has wide browser support. Just try it yourself!
|
||||
|
||||
## Examples
|
||||
|
||||
[Yes, please! The more the better.](https://hyperform.js.org/examples.html)
|
||||
|
||||
## Status
|
||||
|
||||
The target is 100% support for the [HTML5 validation
|
||||
API](https://html.spec.whatwg.org/multipage/forms.html#constraints). Currently
|
||||
supported:
|
||||
|
||||
| feature | status |
|
||||
| ---------------------------- | ----------- |
|
||||
| `willValidate` | :full_moon: |
|
||||
| `setCustomValidity(message)` | :full_moon: |
|
||||
| `validity.valueMissing` | :full_moon: |
|
||||
| `validity.typeMismatch` | :full_moon: |
|
||||
| `validity.patternMismatch` | :full_moon: |
|
||||
| `validity.tooLong` | :full_moon: |
|
||||
| `validity.tooShort` | :full_moon: |
|
||||
| `validity.rangeUnderflow` | :full_moon: |
|
||||
| `validity.rangeOverflow` | :full_moon: |
|
||||
| `validity.stepMismatch` | :full_moon: |
|
||||
| `validity.badInput` | :full_moon: |
|
||||
| `validity.customError` | :full_moon: |
|
||||
| `validity.valid` | :full_moon: |
|
||||
| `checkValidity()` | :full_moon: |
|
||||
| `reportValidity()` | :full_moon: |
|
||||
| `validationMessage` | :full_moon: |
|
||||
| `valueAsDate` | :full_moon: |
|
||||
| `valueAsNumber` | :full_moon: |
|
||||
| `valueLow` / `valueHigh` | :new_moon: |
|
||||
| `stepUp(n)` / `stepDown(n)` | :full_moon: |
|
||||
| `accept` attribute | :full_moon: |
|
||||
| support for `novalidate` | :full_moon: |
|
||||
|
||||
Current test status: [](https://travis-ci.org/hyperform/hyperform)
|
||||
|
||||
### Browser Support
|
||||
|
||||
Hyperform is fully tested and supported in
|
||||
|
||||
* Chrome (latest 3)
|
||||
* Firefox (latest 3 and ESR)
|
||||
* MS Edge (latest)
|
||||
* IE down to version 9 (yes, you've read that correctly) when `WeakMap` for IE
|
||||
≤ 10 and `classList` for IE 9 are polyfilled
|
||||
* Safari. _Caveat:_ In versions ≤ 9 [polyfills do not
|
||||
work](https://github.com/hyperform/hyperform/issues/16). However, form
|
||||
validation and direct method calling works as expected.)
|
||||
|
||||
## Contributing
|
||||
|
||||
Cool, yes! Head over to the [contributing guide](CONTRIBUTING.md) for details.
|
||||
|
||||
## Changelog
|
||||
|
||||
We maintain an up-to date [changelog named `CHANGELOG.md`](CHANGELOG.md)
|
||||
alongside this file.
|
||||
|
||||
## License
|
||||
|
||||
This library is released under the terms of the [MIT license](LICENSE.md).
|
||||
|
||||
## Contributors
|
||||
|
||||
Hyperform is developed by [Manuel Strehl](https://twitter.com/m_strehl) with
|
||||
contributions by
|
||||
[Andrey Volynkin](https://github.com/Avol-V),
|
||||
[Daniel Wang](https://github.com/pvnr0082t),
|
||||
[Darlan Mendonça](https://github.com/darlanmendonca),
|
||||
[Christoph Dörfel](https://github.com/Garbanas),
|
||||
[Josh Farneman](https://github.com/farneman),
|
||||
[Casey Corcoran](https://github.com/snaptopixel),
|
||||
and many people reporting issues.
|
||||
29
public/assets/hyperform-0.12.0/bower.json
Normal file
29
public/assets/hyperform-0.12.0/bower.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "hyperform",
|
||||
"version": "0.12.0",
|
||||
"homepage": "https://github.com/hyperform/hyperform",
|
||||
"authors": [
|
||||
"Manuel Strehl"
|
||||
],
|
||||
"description": "Capture form validation back from the browser",
|
||||
"main": "src/hyperform.js",
|
||||
"moduleType": "es6",
|
||||
"keywords": [
|
||||
"html5",
|
||||
"form",
|
||||
"forms",
|
||||
"input",
|
||||
"validation"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"examples",
|
||||
"Makefile",
|
||||
"src",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
37
public/assets/hyperform-0.12.0/css/hyperform.css
Normal file
37
public/assets/hyperform-0.12.0/css/hyperform.css
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
.hf-warning {
|
||||
background: linear-gradient(to bottom, rgba(255,255,255,.9), rgba(255,255,255,.75));
|
||||
border: 1px solid rgba(0,0,0,.2);
|
||||
border-radius: 1px;
|
||||
box-shadow: 0 12px 10px -10px rgba(0,0,0,.5);
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
color: #621;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
padding: .25em .5em;
|
||||
pointer-events: none;
|
||||
/* make sure, \n is preserved in messages. */
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.hf-warning:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* :invalid is not yet supported in IE 9, so split selectors */
|
||||
|
||||
.hf-invalid + .hf-warning {
|
||||
display: none;
|
||||
position: absolute;
|
||||
}
|
||||
:invalid + .hf-warning {
|
||||
display: none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.hf-invalid:focus + .hf-warning:not(:empty) {
|
||||
display: block;
|
||||
}
|
||||
:invalid:focus + .hf-warning:not(:empty) {
|
||||
display: block;
|
||||
}
|
||||
3111
public/assets/hyperform-0.12.0/dist/hyperform.amd.js
vendored
Normal file
3111
public/assets/hyperform-0.12.0/dist/hyperform.amd.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
2
public/assets/hyperform-0.12.0/dist/hyperform.amd.min.js
vendored
Normal file
2
public/assets/hyperform-0.12.0/dist/hyperform.amd.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3109
public/assets/hyperform-0.12.0/dist/hyperform.cjs.js
vendored
Normal file
3109
public/assets/hyperform-0.12.0/dist/hyperform.cjs.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
2
public/assets/hyperform-0.12.0/dist/hyperform.cjs.min.js
vendored
Normal file
2
public/assets/hyperform-0.12.0/dist/hyperform.cjs.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3107
public/assets/hyperform-0.12.0/dist/hyperform.esm.js
vendored
Normal file
3107
public/assets/hyperform-0.12.0/dist/hyperform.esm.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
2
public/assets/hyperform-0.12.0/dist/hyperform.esm.min.js
vendored
Normal file
2
public/assets/hyperform-0.12.0/dist/hyperform.esm.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3112
public/assets/hyperform-0.12.0/dist/hyperform.js
vendored
Normal file
3112
public/assets/hyperform-0.12.0/dist/hyperform.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
2
public/assets/hyperform-0.12.0/dist/hyperform.min.js
vendored
Normal file
2
public/assets/hyperform-0.12.0/dist/hyperform.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
12
public/assets/hyperform-0.12.0/examples/examples.css
Normal file
12
public/assets/hyperform-0.12.0/examples/examples.css
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
main {
|
||||
max-width: 1024px;
|
||||
margin: 1.5em auto;
|
||||
}
|
||||
|
||||
:not(button).hf-invalid {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
:not(button).hf-valid {
|
||||
border-color: green !important;
|
||||
}
|
||||
28
public/assets/hyperform-0.12.0/examples/simple.html
Normal file
28
public/assets/hyperform-0.12.0/examples/simple.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Simple Form</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="../css/hyperform.css">
|
||||
<link rel="stylesheet" href="examples.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<form id="simple">
|
||||
<p>
|
||||
<label>
|
||||
A required field. Try submitting without filling it in.<br>
|
||||
<input type="text" name="foo" required class="form-control">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<button class="pure-button pure-button-primary">go!</button>
|
||||
</p>
|
||||
</form>
|
||||
</main>
|
||||
<script src="../dist/hyperform.js"></script>
|
||||
<script>hyperform(window);</script>
|
||||
</body>
|
||||
</html>
|
||||
67
public/assets/hyperform-0.12.0/examples/types.html
Normal file
67
public/assets/hyperform-0.12.0/examples/types.html
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Different Element Types</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="../css/hyperform.css">
|
||||
<link rel="stylesheet" href="examples.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<form id="types">
|
||||
<div class="row">
|
||||
<p class="col-md-4 form-group">
|
||||
<label for="number">Number:</label>
|
||||
<input type="number" name="number" id="number" class="form-control">
|
||||
</p>
|
||||
<p class="col-md-4 form-group">
|
||||
<label for="url">URL:</label>
|
||||
<input type="url" name="url" id="url" class="form-control">
|
||||
</p>
|
||||
<p class="col-md-4 form-group">
|
||||
<label for="email">E-Mail address:</label>
|
||||
<input type="email" name="email" id="email" class="form-control">
|
||||
</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p class="col-md-4 form-group">
|
||||
<label for="tel">Phone number:</label>
|
||||
<input type="tel" name="tel" id="tel" class="form-control">
|
||||
<span class="help-block">there is no default validation associated with those.</span>
|
||||
</p>
|
||||
<p class="col-md-4 form-group">
|
||||
<label for="color">Color:</label>
|
||||
<input type="color" name="color" id="color" class="form-control">
|
||||
<span class="help-block">must be in format <code>#[a-f0-9]{6}</code>.</span>
|
||||
</p>
|
||||
<p class="col-md-4 form-group">
|
||||
<label for="range">Range:</label>
|
||||
<input type="range" name="range" id="range" class="form-control">
|
||||
</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p class="col-md-4 form-group">
|
||||
<label for="date">Date:</label>
|
||||
<input type="date" name="date" id="date" class="form-control">
|
||||
</p>
|
||||
<p class="col-md-4 form-group">
|
||||
<label for="time">Time:</label>
|
||||
<input type="time" name="time" id="time" class="form-control">
|
||||
<span class="help-block">in 1-minute steps</span>
|
||||
</p>
|
||||
<p class="col-md-4 form-group">
|
||||
<label for="month">Month:</label>
|
||||
<input type="month" name="month" id="month" class="form-control">
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
<button class="btn btn-primary">go!</button>
|
||||
</p>
|
||||
</form>
|
||||
</main>
|
||||
<script src="../dist/hyperform.js"></script>
|
||||
<script>hyperform(window);</script>
|
||||
</body>
|
||||
</html>
|
||||
110
public/assets/hyperform-0.12.0/index.d.ts
vendored
Normal file
110
public/assets/hyperform-0.12.0/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
interface HyperformOptions {
|
||||
/** When true, disable the high-level API. Default: false. */
|
||||
strict?: boolean;
|
||||
|
||||
/** Prevent Enter in input fields to submit the form. Default: false. */
|
||||
preventImplicitSubmit?: boolean;
|
||||
|
||||
/** Whether and when fields should be re-validated automatically. */
|
||||
revalidate?: 'oninput' | 'onblur' | 'hybrid' | 'onsubmit' | 'never';
|
||||
|
||||
/** Whether the non-standard valid event should be triggered. Default: true. */
|
||||
validEvent?: boolean;
|
||||
|
||||
/** Whether the <fieldset> element should be treated like a normal input field, e.g. allowing it to receive an error message. Default: true. */
|
||||
extendFieldset?: boolean;
|
||||
|
||||
/** Whether input fields with the non-standard attribute novalidate should be exempted from validation. Default: true. */
|
||||
novalidateOnElements?: boolean;
|
||||
|
||||
/** CSS class names to use instead of the default ones. */
|
||||
classes?: {
|
||||
warning?: string,
|
||||
valid?: string,
|
||||
invalid?: string,
|
||||
validated?: string
|
||||
}
|
||||
|
||||
/** Whether to include input elements without name attribute as validation candidates. Default: false. */
|
||||
validateNameless?: boolean;
|
||||
}
|
||||
|
||||
export interface HyperformRenderer {
|
||||
/** called when a warning should become visible */
|
||||
attachWarning: (warning: HTMLElement, element: HTMLElement) => void;
|
||||
|
||||
/** called when a warning should vanish */
|
||||
detachWarning: (warning: HTMLElement, element: HTMLElement) => void;
|
||||
|
||||
/** called when feedback to an element's state should be handled ie: showing and hiding warnings */
|
||||
showWarning: (element: HTMLElement, wholeFormValidated: boolean) => void;
|
||||
|
||||
/** set the warning's content */
|
||||
setMessage: (warning: HTMLElement, message: string, element: HTMLElement) => void;
|
||||
}
|
||||
|
||||
export interface HyperformValidator {
|
||||
(element: HTMLElement): boolean;
|
||||
}
|
||||
|
||||
export interface HyperformStatic {
|
||||
version: string;
|
||||
|
||||
/** initializes hyperform on a specific form or globally */
|
||||
(target: Window | HTMLFormElement, options?: HyperformOptions): void;
|
||||
|
||||
/** TODO: add documentation */
|
||||
ValidityState(element: HTMLElement): ValidityState;
|
||||
|
||||
/** check, if an element will be subject to HTML5 validation at all */
|
||||
willValidate(element: HTMLElement): boolean;
|
||||
|
||||
/** represents the value of the element, interpreted as a date */
|
||||
valueAsDate(element: HTMLElement, value: any): Date | null;
|
||||
|
||||
/** represents the value of the element, interpreted as a number */
|
||||
valueAsNumber(element: HTMLElement, value: any): Number;
|
||||
|
||||
/** add custom validation logic for specific elements */
|
||||
addValidator(element: HTMLElement, validate: HyperformValidator): void;
|
||||
|
||||
/** override default renderer methods */
|
||||
setRenderer<T extends keyof HyperformRenderer>(renderer: T, action: HyperformRenderer[T]): void;
|
||||
|
||||
/** check an element's validity with respect to it's form */
|
||||
checkValidity(element: HTMLElement): boolean;
|
||||
|
||||
/** check element's validity and report an error back to the user */
|
||||
reportValidity(element: HTMLElement): boolean;
|
||||
|
||||
/** set a custom validity message or delete it with an empty string */
|
||||
setCustomValidity(element: HTMLElement, message: string): void;
|
||||
|
||||
/** TODO: add documentation */
|
||||
stepDown(element: HTMLElement, amount: number): void;
|
||||
|
||||
/** TODO: add documentation */
|
||||
stepUp(element: HTMLElement, amount: number): void;
|
||||
|
||||
/** get the validation message for an element, empty string, if the element satisfies all constraints. */
|
||||
validationMessage(element: HTMLElement): string;
|
||||
|
||||
/** set the language for Hyperform’s messages */
|
||||
setLanguage(lang: string): HyperformStatic;
|
||||
|
||||
/** add a lookup catalog "string: translation" for a language */
|
||||
addTranslation(lang: string, catalog: any): HyperformStatic;
|
||||
|
||||
/** register custom error messages per element */
|
||||
setMessage(element: HTMLElement, validator: HyperformValidator, message: string): HyperformStatic;
|
||||
|
||||
/** TODO: add documentation and types */
|
||||
addHook(hook: any, action: any, position: any): HyperformStatic;
|
||||
|
||||
/** TODO: add documentation and types */
|
||||
removeHook(hook: any, action: any): HyperformStatic;
|
||||
}
|
||||
|
||||
declare const Hyperform: HyperformStatic;
|
||||
|
||||
export default Hyperform;
|
||||
130
public/assets/hyperform-0.12.0/karma.conf.js
Normal file
130
public/assets/hyperform-0.12.0/karma.conf.js
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
var customLaunchers = {};
|
||||
|
||||
[
|
||||
//['chrome', 'dev', 'Windows 10'],
|
||||
['chrome', 'beta', 'Windows 10'],
|
||||
['chrome', '76.0', 'Windows 10'],
|
||||
['chrome', '75.0', 'Windows 10'],
|
||||
['chrome', '74.0', 'Windows 10'],
|
||||
['chrome', '48.0', 'Linux'],
|
||||
['chrome', '69.0', 'macOS 10.14'],
|
||||
['firefox', 'dev', 'Windows 10'],
|
||||
['firefox', 'beta', 'Windows 10'],
|
||||
['firefox', '68', 'Windows 10'], /* ESR */
|
||||
['firefox', '67', 'Windows 10'],
|
||||
['firefox', '66', 'Windows 10'],
|
||||
['firefox', '45.0', 'Linux'],
|
||||
['firefox', '68', 'macOS 10.14'],
|
||||
['MicrosoftEdge', '18', 'Windows 10'],
|
||||
['MicrosoftEdge', '17', 'Windows 10'],
|
||||
['internet explorer', '11.0', 'Windows 10'],
|
||||
['internet explorer', '11.0', 'Windows 8.1'],
|
||||
['internet explorer', '10.0', 'Windows 7'],
|
||||
['internet explorer', '9.0', 'Windows 7'],
|
||||
['safari', '12.0', 'macOS 10.14'],
|
||||
['safari', '11.0', 'OS X 10.13'],
|
||||
// TODO: ['iphone', '8.1', 'OS X 10.10'],
|
||||
// TODO: ['iphone', '10.0', 'Mac 10.11'],
|
||||
// TODO: ['android', '4.4', 'Linux'],
|
||||
].forEach(set => {
|
||||
customLaunchers['sl_'+set.join('_').replace(/[^a-zA-Z0-9_]+/g, '_')] = {
|
||||
base: 'SauceLabs',
|
||||
browserName: set[0],
|
||||
version: set[1],
|
||||
platform: set[2],
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = function(config) {
|
||||
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['mocha'],
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
// for IE 10 support
|
||||
'test/functional/weakmap.min.js',
|
||||
// for IE 9 support
|
||||
'test/functional/classList.min.js',
|
||||
'dist/hyperform.js',
|
||||
'test/functional/test.*.js',
|
||||
{ pattern: 'test/functional/blank.html', watched: false, included: true, served: true, nocache: true, }
|
||||
],
|
||||
|
||||
proxies: {
|
||||
// blank.html loading files from another base dir
|
||||
'/blank.html': '/base/test/functional/blank.html',
|
||||
'/weakmap.min.js': '/base/test/functional/weakmap.min.js',
|
||||
'/classList.min.js': '/base/test/functional/classList.min.js',
|
||||
'/dist/hyperform.js': '/base/dist/hyperform.js',
|
||||
},
|
||||
|
||||
preprocessors: [],
|
||||
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [
|
||||
],
|
||||
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
},
|
||||
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
//reporters: ['progress'],
|
||||
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
//browsers: ['Chrome', 'Firefox'],
|
||||
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: true,
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: 5,
|
||||
|
||||
browserDisconnectTimeout: 5000,
|
||||
|
||||
sauceLabs: {
|
||||
testName: 'Hyperform functional tests',
|
||||
recordScreenshots: true,
|
||||
},
|
||||
customLaunchers: customLaunchers,
|
||||
browsers: Object.keys(customLaunchers),
|
||||
reporters: ['dots', 'saucelabs'],
|
||||
})
|
||||
}
|
||||
7057
public/assets/hyperform-0.12.0/package-lock.json
generated
Normal file
7057
public/assets/hyperform-0.12.0/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
86
public/assets/hyperform-0.12.0/package.json
Normal file
86
public/assets/hyperform-0.12.0/package.json
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"name": "hyperform",
|
||||
"version": "0.12.0",
|
||||
"description": "Capture form validation back from the browser",
|
||||
"main": "dist/hyperform.cjs.js",
|
||||
"types": "index.d.ts",
|
||||
"jsnext:main": "src/hyperform.js",
|
||||
"module": "dist/hyperform.esm.js",
|
||||
"style": "css/hyperform.css",
|
||||
"scripts": {
|
||||
"test": "jshint src && ava",
|
||||
"preversion": "npm test",
|
||||
"version": "make version VERSION=$npm_package_version && make all && git add src/version.js dist/ bower.json CHANGELOG.md",
|
||||
"postversion": "git push && git push --tags"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/hyperform/hyperform.git"
|
||||
},
|
||||
"keywords": [
|
||||
"html5",
|
||||
"form",
|
||||
"forms",
|
||||
"input",
|
||||
"validation"
|
||||
],
|
||||
"author": "Manuel Strehl",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/hyperform/hyperform/issues"
|
||||
},
|
||||
"homepage": "https://hyperform.js.org/",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.8.4",
|
||||
"@babel/preset-env": "^7.8.4",
|
||||
"@babel/register": "^7.8.3",
|
||||
"ava": "^3.5.1",
|
||||
"jsdom": "^9.10.0",
|
||||
"jshint": "^2.11.0",
|
||||
"karma": "^4.4.1",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-firefox-launcher": "^1.3.0",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-sauce-launcher": "^4.0.0",
|
||||
"mocha": "^7.1.1",
|
||||
"rollup": "^1.31.1",
|
||||
"rollup-plugin-babel": "^4.3.3",
|
||||
"rollup-plugin-banner": "^0.2.1",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-terser": "^5.2.0",
|
||||
"rollup-plugin-uglify": "^6.0.4",
|
||||
"selenium-webdriver": "^3.0.1"
|
||||
},
|
||||
"files": [
|
||||
"css",
|
||||
"dist",
|
||||
"src",
|
||||
"test",
|
||||
"index.d.ts",
|
||||
"rollup.config.js"
|
||||
],
|
||||
"jshintConfig": {
|
||||
"esversion": 6,
|
||||
"strict": "global",
|
||||
"laxbreak": true,
|
||||
"globals": {
|
||||
"window": true,
|
||||
"document": true
|
||||
}
|
||||
},
|
||||
"ava": {
|
||||
"require": [
|
||||
"@babel/register",
|
||||
"./test/helpers/setup-browser-env.js"
|
||||
],
|
||||
"files": [
|
||||
"test/unit/**/*.js"
|
||||
],
|
||||
"concurrency": 5
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"@babel/env"
|
||||
]
|
||||
}
|
||||
}
|
||||
63
public/assets/hyperform-0.12.0/rollup.config.js
Normal file
63
public/assets/hyperform-0.12.0/rollup.config.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import { uglify } from 'rollup-plugin-uglify';
|
||||
import banner from 'rollup-plugin-banner';
|
||||
|
||||
const hybanner = banner('hyperform.js.org');
|
||||
|
||||
export default [{
|
||||
input: 'src/hyperform.js',
|
||||
output: [
|
||||
{
|
||||
file: 'dist/hyperform.js',
|
||||
format: 'iife',
|
||||
name: 'hyperform',
|
||||
plugins: [hybanner],
|
||||
},
|
||||
{
|
||||
file: 'dist/hyperform.min.js',
|
||||
format: 'iife',
|
||||
name: 'hyperform',
|
||||
plugins: [uglify(), hybanner],
|
||||
},
|
||||
{
|
||||
file: 'dist/hyperform.cjs.js',
|
||||
format: 'cjs',
|
||||
plugins: [hybanner],
|
||||
},
|
||||
{
|
||||
file: 'dist/hyperform.cjs.min.js',
|
||||
format: 'cjs',
|
||||
plugins: [uglify(), hybanner],
|
||||
},
|
||||
{
|
||||
file: 'dist/hyperform.amd.js',
|
||||
format: 'amd',
|
||||
plugins: [hybanner],
|
||||
},
|
||||
{
|
||||
file: 'dist/hyperform.amd.min.js',
|
||||
format: 'amd',
|
||||
plugins: [uglify(), hybanner],
|
||||
},
|
||||
{
|
||||
file: 'dist/hyperform.esm.js',
|
||||
format: 'esm',
|
||||
plugins: [hybanner],
|
||||
},
|
||||
{
|
||||
file: 'dist/hyperform.esm.min.js',
|
||||
format: 'esm',
|
||||
plugins: [terser({
|
||||
module: true,
|
||||
}), hybanner],
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
resolve(),
|
||||
babel({
|
||||
exclude: 'node_modules/**' // only transpile our source code
|
||||
}),
|
||||
],
|
||||
}];
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* internal storage for custom error messages
|
||||
*/
|
||||
const store = new WeakMap();
|
||||
|
||||
|
||||
/**
|
||||
* register custom error messages per element
|
||||
*/
|
||||
const custom_messages = {
|
||||
|
||||
set(element, validator, message) {
|
||||
const messages = store.get(element) || {};
|
||||
messages[validator] = message;
|
||||
store.set(element, messages);
|
||||
return custom_messages;
|
||||
},
|
||||
|
||||
get(element, validator, _default=undefined) {
|
||||
const messages = store.get(element);
|
||||
if (messages === undefined || ! (validator in messages)) {
|
||||
const data_id = 'data-' + validator.replace(/[A-Z]/g, '-$&').toLowerCase();
|
||||
if (element.hasAttribute(data_id)) {
|
||||
/* if the element has a data-validator attribute, use this as fallback.
|
||||
* E.g., if validator == 'valueMissing', the element can specify a
|
||||
* custom validation message like this:
|
||||
* <input data-value-missing="Oh noes!">
|
||||
*/
|
||||
return element.getAttribute(data_id);
|
||||
}
|
||||
return _default;
|
||||
}
|
||||
return messages[validator];
|
||||
},
|
||||
|
||||
delete(element, validator=null) {
|
||||
if (! validator) {
|
||||
return store.delete(element);
|
||||
}
|
||||
const messages = store.get(element) || {};
|
||||
if (validator in messages) {
|
||||
delete(messages[validator]);
|
||||
store.set(element, messages);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default custom_messages;
|
||||
87
public/assets/hyperform-0.12.0/src/components/hooks.js
Normal file
87
public/assets/hyperform-0.12.0/src/components/hooks.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
const registry = Object.create(null);
|
||||
|
||||
|
||||
/**
|
||||
* run all actions registered for a hook
|
||||
*
|
||||
* Every action gets called with a state object as `this` argument and with the
|
||||
* hook's call arguments as call arguments.
|
||||
*
|
||||
* @return mixed the returned value of the action calls or undefined
|
||||
*/
|
||||
export function call_hook(hook) {
|
||||
var result;
|
||||
const call_args = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
if (hook in registry) {
|
||||
result = registry[hook].reduce((function(args) {
|
||||
|
||||
return function(previousResult, currentAction) {
|
||||
const interimResult = currentAction.apply({
|
||||
state: previousResult,
|
||||
hook: hook,
|
||||
}, args);
|
||||
return (interimResult !== undefined)? interimResult : previousResult;
|
||||
};
|
||||
|
||||
})(call_args), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a value through hooked functions
|
||||
*
|
||||
* Allows for additional parameters:
|
||||
* js> do_filter('foo', null, current_element)
|
||||
*/
|
||||
export function do_filter(hook, initial_value) {
|
||||
var result = initial_value;
|
||||
var call_args = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
if (hook in registry) {
|
||||
result = registry[hook].reduce(function(previousResult, currentAction) {
|
||||
call_args[0] = previousResult;
|
||||
const interimResult = currentAction.apply({
|
||||
state: previousResult,
|
||||
hook: hook,
|
||||
}, call_args);
|
||||
return (interimResult !== undefined)? interimResult : previousResult;
|
||||
}, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove an action again
|
||||
*/
|
||||
export function remove_hook(hook, action) {
|
||||
if (hook in registry) {
|
||||
for (let i = 0; i < registry[hook].length; i++) {
|
||||
if (registry[hook][i] === action) {
|
||||
registry[hook].splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export { remove_hook as remove_filter };
|
||||
|
||||
/**
|
||||
* add an action to a hook
|
||||
*/
|
||||
export function add_hook(hook, action, position) {
|
||||
if (! (hook in registry)) {
|
||||
registry[hook] = [];
|
||||
}
|
||||
if (position === undefined) {
|
||||
position = registry[hook].length;
|
||||
}
|
||||
registry[hook].splice(position, 0, action);
|
||||
}
|
||||
export { add_hook as add_filter };
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* the following validation messages are from Firefox source,
|
||||
* http://mxr.mozilla.org/mozilla-central/source/dom/locales/en-US/chrome/dom/dom.properties
|
||||
* released under MPL license, http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
const catalog = {
|
||||
en: {
|
||||
TextTooLong: 'Please shorten this text to %l characters or less (you are currently using %l characters).',
|
||||
ValueMissing: 'Please fill out this field.',
|
||||
CheckboxMissing: 'Please check this box if you want to proceed.',
|
||||
RadioMissing: 'Please select one of these options.',
|
||||
FileMissing: 'Please select a file.',
|
||||
SelectMissing: 'Please select an item in the list.',
|
||||
InvalidEmail: 'Please enter an email address.',
|
||||
InvalidURL: 'Please enter a URL.',
|
||||
PatternMismatch: 'Please match the requested format.',
|
||||
PatternMismatchWithTitle: 'Please match the requested format: %l.',
|
||||
NumberRangeOverflow: 'Please select a value that is no more than %l.',
|
||||
DateRangeOverflow: 'Please select a value that is no later than %l.',
|
||||
TimeRangeOverflow: 'Please select a value that is no later than %l.',
|
||||
NumberRangeUnderflow: 'Please select a value that is no less than %l.',
|
||||
DateRangeUnderflow: 'Please select a value that is no earlier than %l.',
|
||||
TimeRangeUnderflow: 'Please select a value that is no earlier than %l.',
|
||||
StepMismatch: 'Please select a valid value. The two nearest valid values are %l and %l.',
|
||||
StepMismatchOneValue: 'Please select a valid value. The nearest valid value is %l.',
|
||||
BadInputNumber: 'Please enter a number.',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* the global language Hyperform will use
|
||||
*/
|
||||
var language = 'en';
|
||||
|
||||
|
||||
/**
|
||||
* the base language according to BCP47, i.e., only the piece before the first hyphen
|
||||
*/
|
||||
var base_lang = 'en';
|
||||
|
||||
|
||||
/**
|
||||
* set the language for Hyperform’s messages
|
||||
*/
|
||||
export function set_language(newlang) {
|
||||
language = newlang;
|
||||
base_lang = newlang.replace(/[-_].*/, '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* add a lookup catalog "string: translation" for a language
|
||||
*/
|
||||
export function add_translation(lang, new_catalog) {
|
||||
if (! (lang in catalog)) {
|
||||
catalog[lang] = {};
|
||||
}
|
||||
for (let key in new_catalog) {
|
||||
if (new_catalog.hasOwnProperty(key)) {
|
||||
catalog[lang][key] = new_catalog[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return `s` translated into the current language
|
||||
*
|
||||
* Defaults to the base language and then English if the former has no
|
||||
* translation for `s`.
|
||||
*/
|
||||
export default function(s) {
|
||||
if ((language in catalog) && (s in catalog[language])) {
|
||||
return catalog[language][s];
|
||||
} else if ((base_lang in catalog) && (s in catalog[base_lang])) {
|
||||
return catalog[base_lang][s];
|
||||
} else if (s in catalog.en) {
|
||||
return catalog.en[s];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { get_wrapper } from './wrapper';
|
||||
import mark from '../tools/mark';
|
||||
|
||||
|
||||
/**
|
||||
* the internal storage for messages
|
||||
*/
|
||||
const store = new WeakMap();
|
||||
|
||||
|
||||
/* jshint -W053 *//* allow new String() */
|
||||
/**
|
||||
* handle validation messages
|
||||
*
|
||||
* Falls back to browser-native errors, if any are available. The messages
|
||||
* are String objects so that we can mark() them.
|
||||
*/
|
||||
const message_store = {
|
||||
|
||||
set(element, message, is_custom=false) {
|
||||
if (element instanceof window.HTMLFieldSetElement) {
|
||||
const wrapped_form = get_wrapper(element);
|
||||
if (wrapped_form && ! wrapped_form.settings.extendFieldset) {
|
||||
/* make this a no-op for <fieldset> in strict mode */
|
||||
return message_store;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof message === 'string') {
|
||||
message = new String(message);
|
||||
}
|
||||
if (is_custom) {
|
||||
message.is_custom = true;
|
||||
}
|
||||
mark(message);
|
||||
store.set(element, message);
|
||||
|
||||
/* allow the :invalid selector to match */
|
||||
if ('_original_setCustomValidity' in element) {
|
||||
element._original_setCustomValidity(message.toString());
|
||||
}
|
||||
|
||||
return message_store;
|
||||
},
|
||||
|
||||
get(element) {
|
||||
var message = store.get(element);
|
||||
if (message === undefined && ('_original_validationMessage' in element)) {
|
||||
/* get the browser's validation message, if we have none. Maybe it
|
||||
* knows more than we. */
|
||||
message = new String(element._original_validationMessage);
|
||||
}
|
||||
return message? message : new String('');
|
||||
},
|
||||
|
||||
delete(element, is_custom=false) {
|
||||
if ('_original_setCustomValidity' in element) {
|
||||
element._original_setCustomValidity('');
|
||||
}
|
||||
var message = store.get(element);
|
||||
if (message && is_custom && ! message.is_custom) {
|
||||
/* do not delete "native" messages, if asked */
|
||||
return false;
|
||||
}
|
||||
return store.delete(element);
|
||||
},
|
||||
|
||||
};
|
||||
/* jshint +W053 */
|
||||
|
||||
export { message_store };
|
||||
32
public/assets/hyperform-0.12.0/src/components/registry.js
Normal file
32
public/assets/hyperform-0.12.0/src/components/registry.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
const internal_registry = new WeakMap();
|
||||
|
||||
|
||||
/**
|
||||
* A registry for custom validators
|
||||
*
|
||||
* slim wrapper around a WeakMap to ensure the values are arrays
|
||||
* (hence allowing > 1 validators per element)
|
||||
*/
|
||||
const custom_validator_registry = {
|
||||
|
||||
set(element, validator) {
|
||||
const current = internal_registry.get(element) || [];
|
||||
current.push(validator);
|
||||
internal_registry.set(element, current);
|
||||
return custom_validator_registry;
|
||||
},
|
||||
|
||||
get(element) {
|
||||
return internal_registry.get(element) || [];
|
||||
},
|
||||
|
||||
delete(element) {
|
||||
return internal_registry.delete(element);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default custom_validator_registry;
|
||||
111
public/assets/hyperform-0.12.0/src/components/renderer.js
Normal file
111
public/assets/hyperform-0.12.0/src/components/renderer.js
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { message_store } from './message_store';
|
||||
import { get_wrapper } from './wrapper';
|
||||
import generate_id from '../tools/generate_id';
|
||||
import { get_radiogroup } from '../tools/get_radiogroup';
|
||||
|
||||
|
||||
const warningsCache = new WeakMap();
|
||||
|
||||
|
||||
const DefaultRenderer = {
|
||||
|
||||
/**
|
||||
* called when a warning should become visible
|
||||
*/
|
||||
attachWarning: function(warning, element) {
|
||||
/* should also work, if element is last,
|
||||
* http://stackoverflow.com/a/4793630/113195 */
|
||||
element.parentNode.insertBefore(warning, element.nextSibling);
|
||||
},
|
||||
|
||||
/**
|
||||
* called when a warning should vanish
|
||||
*/
|
||||
detachWarning: function(warning, element) {
|
||||
/* be conservative here, since an overwritten attachWarning() might not
|
||||
* actually have attached the warning. */
|
||||
if (warning.parentNode) {
|
||||
warning.parentNode.removeChild(warning);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* called when feedback to an element's state should be handled
|
||||
*
|
||||
* i.e., showing and hiding warnings
|
||||
*/
|
||||
showWarning: function(element, whole_form_validated=false) {
|
||||
/* don't render error messages on subsequent radio buttons of the
|
||||
* same group. This assumes, that element.validity.valueMissing is the only
|
||||
* possible validation failure for radio buttons. */
|
||||
if (whole_form_validated && element.type === 'radio' &&
|
||||
get_radiogroup(element)[0] !== element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const msg = message_store.get(element).toString();
|
||||
var warning = warningsCache.get(element);
|
||||
|
||||
if (msg) {
|
||||
if (! warning) {
|
||||
const wrapper = get_wrapper(element);
|
||||
warning = document.createElement('div');
|
||||
warning.className = wrapper && wrapper.settings.classes.warning || 'hf-warning';
|
||||
warning.id = generate_id();
|
||||
warning.setAttribute('aria-live', 'polite');
|
||||
warningsCache.set(element, warning);
|
||||
}
|
||||
|
||||
element.setAttribute('aria-errormessage', warning.id);
|
||||
if (!element.hasAttribute('aria-describedby')) {
|
||||
element.setAttribute('aria-describedby', warning.id);
|
||||
}
|
||||
Renderer.setMessage(warning, msg, element);
|
||||
Renderer.attachWarning(warning, element);
|
||||
|
||||
} else if (warning && warning.parentNode) {
|
||||
if (element.getAttribute('aria-describedby') === warning.id) {
|
||||
element.removeAttribute('aria-describedby');
|
||||
}
|
||||
element.removeAttribute('aria-errormessage');
|
||||
Renderer.detachWarning(warning, element);
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* set the warning's content
|
||||
*
|
||||
* Overwrite this method, if you want, e.g., to allow HTML in warnings
|
||||
* or preprocess the content.
|
||||
*/
|
||||
setMessage: function(warning, message, element) {
|
||||
warning.textContent = message;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
const Renderer = {
|
||||
|
||||
attachWarning: DefaultRenderer.attachWarning,
|
||||
detachWarning: DefaultRenderer.detachWarning,
|
||||
showWarning: DefaultRenderer.showWarning,
|
||||
setMessage: DefaultRenderer.setMessage,
|
||||
|
||||
set: function(renderer, action) {
|
||||
if (! action) {
|
||||
action = DefaultRenderer[renderer];
|
||||
}
|
||||
Renderer[renderer] = action;
|
||||
},
|
||||
|
||||
getWarning: element => warningsCache.get(element),
|
||||
|
||||
};
|
||||
|
||||
|
||||
export default Renderer;
|
||||
181
public/assets/hyperform-0.12.0/src/components/setup.js
Normal file
181
public/assets/hyperform-0.12.0/src/components/setup.js
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { catch_submit, uncatch_submit } from '../tools/catch_submit';
|
||||
import ValidityState from '../polyfills/validityState';
|
||||
import reportValidity from '../polyfills/reportValidity';
|
||||
import polyfill from '../tools/polyfill';
|
||||
import polyunfill from '../tools/polyunfill';
|
||||
|
||||
|
||||
const element_prototypes = [
|
||||
window.HTMLButtonElement.prototype,
|
||||
window.HTMLInputElement.prototype,
|
||||
window.HTMLSelectElement.prototype,
|
||||
window.HTMLTextAreaElement.prototype,
|
||||
window.HTMLFieldSetElement.prototype,
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* get the appropriate function to revalidate form elements
|
||||
*/
|
||||
function get_revalidator(method='hybrid') {
|
||||
return function(event) {
|
||||
if (event.target instanceof window.HTMLButtonElement ||
|
||||
event.target instanceof window.HTMLTextAreaElement ||
|
||||
event.target instanceof window.HTMLSelectElement ||
|
||||
event.target instanceof window.HTMLInputElement) {
|
||||
|
||||
if (event.target.form && event.target.form.hasAttribute('novalidate')) {
|
||||
/* do nothing, if the form forbids it. This still allows manual
|
||||
* validation via, e.g., input.reportValidity(), but mirrors browser
|
||||
* behavior, that are also completely silent in this case. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (method === 'hybrid') {
|
||||
/* "hybrid" somewhat simulates what browsers do. See for example
|
||||
* Firefox's :-moz-ui-invalid pseudo-class:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-ui-invalid */
|
||||
if (event.type === 'blur' &&
|
||||
event.target.value !== event.target.defaultValue ||
|
||||
ValidityState(event.target).valid) {
|
||||
/* on blur, update the report when the value has changed from the
|
||||
* default or when the element is valid (possibly removing a still
|
||||
* standing invalidity report). */
|
||||
reportValidity(event.target);
|
||||
} else if ((event.type === 'keyup' && event.keyCode !== 9) ||
|
||||
event.type === 'change') {
|
||||
if (ValidityState(event.target).valid) {
|
||||
// report instantly, when an element becomes valid,
|
||||
// postpone report to blur event, when an element is invalid
|
||||
reportValidity(event.target);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (event.type !== 'keyup' || event.keyCode !== 9) {
|
||||
/* do _not_ validate, when the user "tabbed" into the field initially,
|
||||
* i.e., a keyup event with keyCode 9 */
|
||||
reportValidity(event.target);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* run a function on all found elements
|
||||
*/
|
||||
function execute_on_elements(fn, elements) {
|
||||
if (elements instanceof window.Element) {
|
||||
elements = [ elements ];
|
||||
}
|
||||
|
||||
const elements_length = elements.length;
|
||||
|
||||
for (let i = 0; i < elements_length; i++) {
|
||||
fn(elements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get a function, that removes hyperform behavior again
|
||||
*/
|
||||
function get_destructor(hform) {
|
||||
const form = hform.form;
|
||||
return function() {
|
||||
uncatch_submit(form);
|
||||
form.removeEventListener('keyup', hform.revalidator);
|
||||
form.removeEventListener('change', hform.revalidator);
|
||||
form.removeEventListener('blur', hform.revalidator, true);
|
||||
if (form === window || form.nodeType === 9) {
|
||||
hform.uninstall(element_prototypes);
|
||||
polyunfill(window.HTMLFormElement);
|
||||
} else if (form instanceof window.HTMLFormElement ||
|
||||
form instanceof window.HTMLFieldSetElement) {
|
||||
hform.uninstall(form.elements);
|
||||
if (form instanceof window.HTMLFormElement) {
|
||||
polyunfill(form);
|
||||
}
|
||||
} else if (form instanceof window.HTMLElement) {
|
||||
hform.observer.disconnect();
|
||||
for (let subform of Array.prototype.slice.call(form.getElementsByTagName('form'))) {
|
||||
hform.uninstall(subform.elements);
|
||||
polyunfill(subform);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* add hyperform behavior to a freshly initialized wrapper
|
||||
*/
|
||||
export function add_behavior(hform) {
|
||||
const form = hform.form;
|
||||
const settings = hform.settings;
|
||||
|
||||
hform.revalidator = get_revalidator(settings.revalidate);
|
||||
hform.observer = { disconnect() {} };
|
||||
|
||||
hform.install = elements => execute_on_elements(polyfill, elements);
|
||||
hform.uninstall = elements => execute_on_elements(polyunfill, elements);
|
||||
|
||||
hform._destruct = get_destructor(hform);
|
||||
|
||||
catch_submit(form, settings.revalidate === 'never');
|
||||
|
||||
if (form === window || form.nodeType === 9) {
|
||||
/* install on the prototypes, when called for the whole document */
|
||||
hform.install(element_prototypes);
|
||||
polyfill(window.HTMLFormElement);
|
||||
} else if (form instanceof window.HTMLFormElement ||
|
||||
form instanceof window.HTMLFieldSetElement) {
|
||||
hform.install(form.elements);
|
||||
if (form instanceof window.HTMLFormElement) {
|
||||
polyfill(form);
|
||||
}
|
||||
} else if (form instanceof window.HTMLElement) {
|
||||
for (let subform of Array.prototype.slice.call(hform.form.getElementsByTagName('form'))) {
|
||||
hform.install(subform.elements);
|
||||
polyfill(subform);
|
||||
}
|
||||
hform.observer = new window.MutationObserver(mutationsList => {
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'childList') {
|
||||
for (let subform of Array.prototype.slice.call(mutation.addedNodes)) {
|
||||
if (subform instanceof window.HTMLFormElement) {
|
||||
hform.install(subform.elements);
|
||||
polyfill(subform);
|
||||
}
|
||||
}
|
||||
for (let subform of Array.prototype.slice.call(mutation.removedNodes)) {
|
||||
if (subform instanceof window.HTMLFormElement) {
|
||||
hform.uninstall(subform.elements);
|
||||
polyunfill(subform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
hform.observer.observe(form, {subtree: true, childList: true});
|
||||
} else {
|
||||
throw new Error('Hyperform must be used with a node or window.');
|
||||
}
|
||||
|
||||
if (settings.revalidate === 'oninput' || settings.revalidate === 'hybrid') {
|
||||
/* in a perfect world we'd just bind to "input", but support here is
|
||||
* abysmal: http://caniuse.com/#feat=input-event */
|
||||
form.addEventListener('keyup', hform.revalidator);
|
||||
form.addEventListener('change', hform.revalidator);
|
||||
}
|
||||
if (settings.revalidate === 'onblur' || settings.revalidate === 'hybrid') {
|
||||
/* useCapture=true, because `blur` doesn't bubble. See
|
||||
* https://developer.mozilla.org/en-US/docs/Web/Events/blur#Event_delegation
|
||||
* for a discussion */
|
||||
form.addEventListener('blur', hform.revalidator, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
export const default_step = {
|
||||
'datetime-local': 60,
|
||||
datetime: 60,
|
||||
time: 60,
|
||||
};
|
||||
|
||||
export const step_scale_factor = {
|
||||
'datetime-local': 1000,
|
||||
datetime: 1000,
|
||||
date: 86400000,
|
||||
week: 604800000,
|
||||
time: 1000,
|
||||
};
|
||||
|
||||
export const default_step_base = {
|
||||
week: -259200000,
|
||||
};
|
||||
|
||||
export const default_min = {
|
||||
range: 0,
|
||||
};
|
||||
|
||||
export const default_max = {
|
||||
range: 100,
|
||||
};
|
||||
32
public/assets/hyperform-0.12.0/src/components/types.js
Normal file
32
public/assets/hyperform-0.12.0/src/components/types.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
/* and datetime-local? Spec says “Nah!” */
|
||||
export const dates = [ 'datetime', 'date', 'month', 'week', 'time', ];
|
||||
|
||||
export const plain_numbers = [ 'number', 'range', ];
|
||||
|
||||
/* everything that returns something meaningful for valueAsNumber and
|
||||
* can have the step attribute */
|
||||
export const numbers = dates.concat(plain_numbers, 'datetime-local');
|
||||
|
||||
/* the spec says to only check those for syntax in validity.typeMismatch.
|
||||
* ¯\_(ツ)_/¯ */
|
||||
export const type_checked = [ 'email', 'url', ];
|
||||
|
||||
/* check these for validity.badInput */
|
||||
export const input_checked = [ 'email', 'date', 'month', 'week', 'time',
|
||||
'datetime', 'datetime-local', 'number', 'range', 'color', ];
|
||||
|
||||
export const text = [ 'text', 'search', 'tel', 'password', ].concat(type_checked);
|
||||
|
||||
/* input element types, that are candidates for the validation API.
|
||||
* Missing from this set are: button, hidden, menu (from <button>), reset and
|
||||
* the types for non-<input> elements. */
|
||||
export const validation_candidates = [ 'checkbox', 'color', 'file', 'image',
|
||||
'radio', 'submit', ].concat(numbers, text);
|
||||
|
||||
/* all known types of <input> */
|
||||
export const inputs = ['button', 'hidden', 'reset'].concat(validation_candidates);
|
||||
|
||||
/* apparently <select> and <textarea> have types of their own */
|
||||
export const non_inputs = ['select-one', 'select-multiple', 'textarea'];
|
||||
65
public/assets/hyperform-0.12.0/src/components/wrapper.js
Normal file
65
public/assets/hyperform-0.12.0/src/components/wrapper.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
const instances = new WeakMap();
|
||||
|
||||
|
||||
/**
|
||||
* wrap <form>s, window or document, that get treated with the global
|
||||
* hyperform()
|
||||
*/
|
||||
export default function Wrapper(form, settings) {
|
||||
|
||||
/* do not allow more than one instance per form. Otherwise we'd end
|
||||
* up with double event handlers, polyfills re-applied, ... */
|
||||
var existing = instances.get(form);
|
||||
if (existing) {
|
||||
existing.settings = settings;
|
||||
return existing;
|
||||
}
|
||||
|
||||
this.form = form;
|
||||
this.settings = settings;
|
||||
this.observer = null;
|
||||
|
||||
instances.set(form, this);
|
||||
}
|
||||
|
||||
|
||||
Wrapper.prototype = {
|
||||
destroy() {
|
||||
instances.delete(this.form);
|
||||
if (this._destruct) {
|
||||
this._destruct();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* try to get the appropriate wrapper for a specific element by looking up
|
||||
* its parent chain
|
||||
*
|
||||
* @return Wrapper | undefined
|
||||
*/
|
||||
export function get_wrapper(element) {
|
||||
var wrapped;
|
||||
|
||||
if (element.form) {
|
||||
/* try a shortcut with the element's <form> */
|
||||
wrapped = instances.get(element.form);
|
||||
}
|
||||
|
||||
/* walk up the parent nodes until document (including) */
|
||||
while (! wrapped && element) {
|
||||
wrapped = instances.get(element);
|
||||
element = element.parentNode;
|
||||
}
|
||||
|
||||
if (! wrapped) {
|
||||
/* try the global instance, if exists. This may also be undefined. */
|
||||
wrapped = instances.get(window);
|
||||
}
|
||||
|
||||
return wrapped;
|
||||
}
|
||||
101
public/assets/hyperform-0.12.0/src/hyperform.js
Normal file
101
public/assets/hyperform-0.12.0/src/hyperform.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import checkValidity from './polyfills/checkValidity';
|
||||
import reportValidity from './polyfills/reportValidity';
|
||||
import setCustomValidity from './polyfills/setCustomValidity';
|
||||
import stepDown from './polyfills/stepDown';
|
||||
import stepUp from './polyfills/stepUp';
|
||||
import validationMessage from './polyfills/validationMessage';
|
||||
import ValidityState from './polyfills/validityState';
|
||||
import valueAsDate from './polyfills/valueAsDate';
|
||||
import valueAsNumber from './polyfills/valueAsNumber';
|
||||
import willValidate from './polyfills/willValidate';
|
||||
import custom_messages from './components/custom_messages';
|
||||
import { add_hook, remove_hook } from './components/hooks';
|
||||
import { set_language, add_translation } from './components/localization';
|
||||
import CustomValidatorRegistry from './components/registry';
|
||||
import Renderer from './components/renderer';
|
||||
import Wrapper from './components/wrapper';
|
||||
import { add_behavior } from './components/setup';
|
||||
import version from './version';
|
||||
|
||||
|
||||
/**
|
||||
* public hyperform interface:
|
||||
*/
|
||||
function hyperform(form, {
|
||||
classes,
|
||||
debug=false,
|
||||
extendFieldset,
|
||||
novalidateOnElements,
|
||||
preventImplicitSubmit=false,
|
||||
revalidate,
|
||||
strict=false,
|
||||
validEvent,
|
||||
validateNameless=false,
|
||||
}={}) {
|
||||
|
||||
if (! classes) {
|
||||
classes = {};
|
||||
}
|
||||
if (extendFieldset === undefined) {
|
||||
extendFieldset = ! strict;
|
||||
}
|
||||
if (novalidateOnElements === undefined) {
|
||||
novalidateOnElements = ! strict;
|
||||
}
|
||||
if (preventImplicitSubmit === undefined) {
|
||||
preventImplicitSubmit = false;
|
||||
}
|
||||
if (revalidate === undefined) {
|
||||
/* other recognized values: 'oninput', 'onblur', 'onsubmit' and 'never' */
|
||||
revalidate = strict? 'onsubmit' : 'hybrid';
|
||||
}
|
||||
if (validEvent === undefined) {
|
||||
validEvent = ! strict;
|
||||
}
|
||||
|
||||
const settings = { debug, strict, preventImplicitSubmit, revalidate,
|
||||
validEvent, extendFieldset, classes, novalidateOnElements,
|
||||
validateNameless,
|
||||
};
|
||||
|
||||
if (form instanceof window.NodeList ||
|
||||
form instanceof window.HTMLCollection ||
|
||||
form instanceof Array) {
|
||||
return Array.prototype.map.call(form,
|
||||
element => hyperform(element, settings));
|
||||
}
|
||||
|
||||
const wrapper = new Wrapper(form, settings);
|
||||
add_behavior(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
hyperform.version = version;
|
||||
|
||||
hyperform.checkValidity = checkValidity;
|
||||
hyperform.reportValidity = reportValidity;
|
||||
hyperform.setCustomValidity = setCustomValidity;
|
||||
hyperform.stepDown = stepDown;
|
||||
hyperform.stepUp = stepUp;
|
||||
hyperform.validationMessage = validationMessage;
|
||||
hyperform.ValidityState = ValidityState;
|
||||
hyperform.valueAsDate = valueAsDate;
|
||||
hyperform.valueAsNumber = valueAsNumber;
|
||||
hyperform.willValidate = willValidate;
|
||||
|
||||
hyperform.setLanguage = lang => { set_language(lang); return hyperform; };
|
||||
hyperform.addTranslation = (lang, catalog) => { add_translation(lang, catalog); return hyperform; };
|
||||
hyperform.setRenderer = (renderer, action) => { Renderer.set(renderer, action); return hyperform; };
|
||||
hyperform.addValidator = (element, validator) => { CustomValidatorRegistry.set(element, validator); return hyperform; };
|
||||
hyperform.setMessage = (element, validator, message) => { custom_messages.set(element, validator, message); return hyperform; };
|
||||
hyperform.addHook = (hook, action, position) => { add_hook(hook, action, position); return hyperform; };
|
||||
hyperform.removeHook = (hook, action) => { remove_hook(hook, action); return hyperform; };
|
||||
|
||||
if (document.currentScript && document.currentScript.hasAttribute('data-hf-autoload')) {
|
||||
hyperform(window);
|
||||
}
|
||||
|
||||
export default hyperform;
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { get_validated_elements } from '../tools/get_validated_elements';
|
||||
import return_hook_or from '../tools/return_hook_or';
|
||||
import trigger_event from '../tools/trigger_event';
|
||||
import ValidityState from './validityState';
|
||||
import { get_wrapper } from '../components/wrapper';
|
||||
|
||||
|
||||
/**
|
||||
* check an element's validity with respect to it's form
|
||||
*/
|
||||
const checkValidity = return_hook_or('checkValidity', function(element) {
|
||||
/* if this is a <form>, check validity of all child inputs */
|
||||
if (element instanceof window.HTMLFormElement) {
|
||||
return get_validated_elements(element).map(checkValidity).every(b=>b);
|
||||
}
|
||||
|
||||
/* default is true, also for elements that are no validation candidates */
|
||||
const valid = ValidityState(element).valid;
|
||||
if (valid) {
|
||||
const wrapped_form = get_wrapper(element);
|
||||
if (wrapped_form && wrapped_form.settings.validEvent) {
|
||||
trigger_event(element, 'valid');
|
||||
}
|
||||
} else {
|
||||
trigger_event(element, 'invalid', { cancelable: true });
|
||||
}
|
||||
|
||||
return valid;
|
||||
});
|
||||
|
||||
|
||||
export default checkValidity;
|
||||
70
public/assets/hyperform-0.12.0/src/polyfills/properties.js
Normal file
70
public/assets/hyperform-0.12.0/src/polyfills/properties.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import install_property from '../tools/property_installer';
|
||||
import uninstall_property from '../tools/property_uninstaller';
|
||||
import { do_filter } from '../components/hooks';
|
||||
|
||||
|
||||
const gA = prop => function() {
|
||||
return do_filter('attr_get_'+prop, this.getAttribute(prop), this);
|
||||
};
|
||||
|
||||
const sA = prop => function(value) {
|
||||
this.setAttribute(prop, do_filter('attr_set_'+prop, value, this));
|
||||
};
|
||||
|
||||
const gAb = prop => function() {
|
||||
return do_filter('attr_get_'+prop, this.hasAttribute(prop), this);
|
||||
};
|
||||
|
||||
const sAb = prop => function(value) {
|
||||
if (do_filter('attr_set_'+prop, value, this)) {
|
||||
this.setAttribute(prop, prop);
|
||||
} else {
|
||||
this.removeAttribute(prop);
|
||||
}
|
||||
};
|
||||
|
||||
const gAn = prop => function() {
|
||||
return do_filter('attr_get_'+prop, Math.max(0, Number(this.getAttribute(prop))), this);
|
||||
};
|
||||
|
||||
const sAn = prop => function(value) {
|
||||
value = do_filter('attr_set_'+prop, value, this);
|
||||
if (/^[0-9]+$/.test(value)) {
|
||||
this.setAttribute(prop, value);
|
||||
}
|
||||
};
|
||||
|
||||
function install_properties(element) {
|
||||
for (let prop of [ 'accept', 'max', 'min', 'pattern', 'placeholder', 'step', ]) {
|
||||
install_property(element, prop, {
|
||||
get: gA(prop),
|
||||
set: sA(prop),
|
||||
});
|
||||
}
|
||||
|
||||
for (let prop of [ 'multiple', 'required', 'readOnly', ]) {
|
||||
install_property(element, prop, {
|
||||
get: gAb(prop.toLowerCase()),
|
||||
set: sAb(prop.toLowerCase()),
|
||||
});
|
||||
}
|
||||
|
||||
for (let prop of [ 'minLength', 'maxLength', ]) {
|
||||
install_property(element, prop, {
|
||||
get: gAn(prop.toLowerCase()),
|
||||
set: sAn(prop.toLowerCase()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function uninstall_properties(element) {
|
||||
for (let prop of [ 'accept', 'max', 'min', 'pattern', 'placeholder', 'step',
|
||||
'multiple', 'required', 'readOnly', 'minLength', 'maxLength', ]) {
|
||||
uninstall_property(element, prop);
|
||||
}
|
||||
}
|
||||
|
||||
export { install_properties, uninstall_properties };
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { get_validated_elements } from '../tools/get_validated_elements';
|
||||
import trigger_event from '../tools/trigger_event';
|
||||
import Renderer from '../components/renderer';
|
||||
import ValidityState from './validityState';
|
||||
import { get_wrapper } from '../components/wrapper';
|
||||
|
||||
|
||||
/**
|
||||
* check element's validity and report an error back to the user
|
||||
*/
|
||||
export default function reportValidity(element) {
|
||||
/* if this is a <form>, report validity of all child inputs */
|
||||
if (element instanceof window.HTMLFormElement) {
|
||||
element.__hf_form_validation = true;
|
||||
const form_valid = get_validated_elements(element).map(reportValidity).every(b=>b);
|
||||
delete(element.__hf_form_validation);
|
||||
return form_valid;
|
||||
}
|
||||
|
||||
/* we copy checkValidity() here, b/c we have to check if the "invalid"
|
||||
* event was canceled. */
|
||||
const valid = ValidityState(element).valid;
|
||||
var event;
|
||||
if (valid) {
|
||||
const wrapped_form = get_wrapper(element);
|
||||
if (wrapped_form && wrapped_form.settings.validEvent) {
|
||||
event = trigger_event(element, 'valid', { cancelable: true });
|
||||
}
|
||||
} else {
|
||||
event = trigger_event(element, 'invalid', { cancelable: true });
|
||||
}
|
||||
|
||||
if (! event || ! event.defaultPrevented) {
|
||||
Renderer.showWarning(element, (element.form && element.form.__hf_form_validation));
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { message_store } from '../components/message_store';
|
||||
import Renderer from '../components/renderer';
|
||||
import validity_state_checkers from '../tools/validity_state_checkers';
|
||||
|
||||
|
||||
/**
|
||||
* set a custom validity message or delete it with an empty string
|
||||
*/
|
||||
export default function setCustomValidity(element, msg) {
|
||||
if (! msg) {
|
||||
message_store.delete(element, true);
|
||||
} else {
|
||||
message_store.set(element, msg, true);
|
||||
}
|
||||
/* live-update the warning */
|
||||
const warning = Renderer.getWarning(element);
|
||||
if (warning) {
|
||||
Renderer.setMessage(warning, msg, element);
|
||||
}
|
||||
/* update any classes if the validity state changes */
|
||||
validity_state_checkers.valid(element);
|
||||
}
|
||||
28
public/assets/hyperform-0.12.0/src/polyfills/stepDown.js
Normal file
28
public/assets/hyperform-0.12.0/src/polyfills/stepDown.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_next_valid from '../tools/get_next_valid';
|
||||
import get_type from '../tools/get_type';
|
||||
import { numbers } from '../components/types';
|
||||
import valueAsNumber from './valueAsNumber';
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default function stepDown(element, n=1) {
|
||||
if (numbers.indexOf(get_type(element)) === -1) {
|
||||
throw new window.DOMException('stepDown encountered invalid type',
|
||||
'InvalidStateError');
|
||||
}
|
||||
if ((element.getAttribute('step') || '').toLowerCase() === 'any') {
|
||||
throw new window.DOMException('stepDown encountered step "any"',
|
||||
'InvalidStateError');
|
||||
}
|
||||
|
||||
const prev = get_next_valid(element, n)[0];
|
||||
|
||||
if (prev !== null) {
|
||||
valueAsNumber(element, prev);
|
||||
}
|
||||
}
|
||||
28
public/assets/hyperform-0.12.0/src/polyfills/stepUp.js
Normal file
28
public/assets/hyperform-0.12.0/src/polyfills/stepUp.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_next_valid from '../tools/get_next_valid';
|
||||
import get_type from '../tools/get_type';
|
||||
import { numbers } from '../components/types';
|
||||
import valueAsNumber from './valueAsNumber';
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default function stepUp(element, n=1) {
|
||||
if (numbers.indexOf(get_type(element)) === -1) {
|
||||
throw new window.DOMException('stepUp encountered invalid type',
|
||||
'InvalidStateError');
|
||||
}
|
||||
if ((element.getAttribute('step') || '').toLowerCase() === 'any') {
|
||||
throw new window.DOMException('stepUp encountered step "any"',
|
||||
'InvalidStateError');
|
||||
}
|
||||
|
||||
const next = get_next_valid(element, n)[1];
|
||||
|
||||
if (next !== null) {
|
||||
valueAsNumber(element, next);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { message_store } from '../components/message_store';
|
||||
|
||||
|
||||
/**
|
||||
* get the validation message for an element, empty string, if the element
|
||||
* satisfies all constraints.
|
||||
*/
|
||||
export default function validationMessage(element) {
|
||||
const msg = message_store.get(element);
|
||||
if (! msg) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/* make it a primitive again, since message_store returns String(). */
|
||||
return msg.toString();
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import is_validation_candidate from '../tools/is_validation_candidate';
|
||||
import mark from '../tools/mark';
|
||||
import validity_state_checkers from '../tools/validity_state_checkers';
|
||||
|
||||
|
||||
/**
|
||||
* the validity state constructor
|
||||
*/
|
||||
const ValidityState = function(element) {
|
||||
if (! (element instanceof window.HTMLElement)) {
|
||||
throw new Error('cannot create a ValidityState for a non-element');
|
||||
}
|
||||
|
||||
const cached = ValidityState.cache.get(element);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
if (! (this instanceof ValidityState)) {
|
||||
/* working around a forgotten `new` */
|
||||
return new ValidityState(element);
|
||||
}
|
||||
|
||||
this.element = element;
|
||||
ValidityState.cache.set(element, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* the prototype for new validityState instances
|
||||
*/
|
||||
const ValidityStatePrototype = {};
|
||||
ValidityState.prototype = ValidityStatePrototype;
|
||||
|
||||
ValidityState.cache = new WeakMap();
|
||||
|
||||
/* small wrapper around the actual validator to check if the validator
|
||||
* should actually be called. `this` refers to the ValidityState object. */
|
||||
const checker_getter = (prop, func) => {
|
||||
return function() {
|
||||
if (! is_validation_candidate(this.element)) {
|
||||
/* not being validated == valid by default
|
||||
* return value == false for all props except "valid", because we test
|
||||
* problems like badInput here */
|
||||
return prop === 'valid';
|
||||
}
|
||||
return func(this.element);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* copy functionality from the validity checkers to the ValidityState
|
||||
* prototype
|
||||
*/
|
||||
for (let prop in validity_state_checkers) {
|
||||
Object.defineProperty(ValidityStatePrototype, prop, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: checker_getter(prop, validity_state_checkers[prop]),
|
||||
set: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* mark the validity prototype, because that is what the client-facing
|
||||
* code deals with mostly, not the property descriptor thing */
|
||||
mark(ValidityStatePrototype);
|
||||
|
||||
export default ValidityState;
|
||||
46
public/assets/hyperform-0.12.0/src/polyfills/valueAsDate.js
Normal file
46
public/assets/hyperform-0.12.0/src/polyfills/valueAsDate.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_type from '../tools/get_type';
|
||||
import string_to_date from '../tools/string_to_date';
|
||||
import date_to_string from '../tools/date_to_string';
|
||||
import { dates } from '../components/types';
|
||||
|
||||
|
||||
/**
|
||||
* implement the valueAsDate functionality
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/multipage/forms.html#dom-input-valueasdate
|
||||
*/
|
||||
export default function valueAsDate(element, value=undefined) {
|
||||
const type = get_type(element);
|
||||
if (dates.indexOf(type) > -1) {
|
||||
if (value !== undefined) {
|
||||
/* setter: value must be null or a Date() */
|
||||
if (value === null) {
|
||||
element.value = '';
|
||||
} else if (value instanceof Date) {
|
||||
if (isNaN(value.getTime())) {
|
||||
element.value = '';
|
||||
} else {
|
||||
element.value = date_to_string(value, type);
|
||||
}
|
||||
} else {
|
||||
throw new window.DOMException(
|
||||
'valueAsDate setter encountered invalid value', 'TypeError');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const value_date = string_to_date(element.value, type);
|
||||
return value_date instanceof Date? value_date : null;
|
||||
|
||||
} else if (value !== undefined) {
|
||||
/* trying to set a date on a not-date input fails */
|
||||
throw new window.DOMException(
|
||||
'valueAsDate setter cannot set date on this element',
|
||||
'InvalidStateError');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_type from '../tools/get_type';
|
||||
import string_to_number from '../tools/string_to_number';
|
||||
import { numbers } from '../components/types';
|
||||
import valueAsDate from './valueAsDate';
|
||||
|
||||
|
||||
/**
|
||||
* implement the valueAsNumber functionality
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/multipage/forms.html#dom-input-valueasnumber
|
||||
*/
|
||||
export default function valueAsNumber(element, value=undefined) {
|
||||
const type = get_type(element);
|
||||
if (numbers.indexOf(type) > -1) {
|
||||
if (type === 'range' && element.hasAttribute('multiple')) {
|
||||
/* @see https://html.spec.whatwg.org/multipage/forms.html#do-not-apply */
|
||||
return NaN;
|
||||
}
|
||||
|
||||
if (value !== undefined) {
|
||||
/* setter: value must be NaN or a finite number */
|
||||
if (isNaN(value)) {
|
||||
element.value = '';
|
||||
} else if (typeof value === 'number' && window.isFinite(value)) {
|
||||
try {
|
||||
/* try setting as a date, but... */
|
||||
valueAsDate(element, new Date(value));
|
||||
} catch (e) {
|
||||
/* ... when valueAsDate is not responsible, ... */
|
||||
if (! (e instanceof window.DOMException)) {
|
||||
throw e;
|
||||
}
|
||||
/* ... set it via Number.toString(). */
|
||||
element.value = value.toString();
|
||||
}
|
||||
} else {
|
||||
throw new window.DOMException(
|
||||
'valueAsNumber setter encountered invalid value', 'TypeError');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return string_to_number(element.value, type);
|
||||
|
||||
} else if (value !== undefined) {
|
||||
/* trying to set a number on a not-number input fails */
|
||||
throw new window.DOMException(
|
||||
'valueAsNumber setter cannot set number on this element',
|
||||
'InvalidStateError');
|
||||
}
|
||||
|
||||
return NaN;
|
||||
}
|
||||
12
public/assets/hyperform-0.12.0/src/polyfills/willValidate.js
Normal file
12
public/assets/hyperform-0.12.0/src/polyfills/willValidate.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import is_validation_candidate from '../tools/is_validation_candidate';
|
||||
|
||||
|
||||
/**
|
||||
* check, if an element will be subject to HTML5 validation at all
|
||||
*/
|
||||
export default function willValidate(element) {
|
||||
return is_validation_candidate(element);
|
||||
}
|
||||
323
public/assets/hyperform-0.12.0/src/tools/catch_submit.js
Normal file
323
public/assets/hyperform-0.12.0/src/tools/catch_submit.js
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import trigger_event, { create_event } from './trigger_event';
|
||||
import matches from './matches';
|
||||
import { get_validated_elements } from './get_validated_elements';
|
||||
import reportValidity from '../polyfills/reportValidity';
|
||||
import { text as text_types } from '../components/types';
|
||||
import { get_wrapper } from '../components/wrapper';
|
||||
|
||||
|
||||
/**
|
||||
* submit a form, because `element` triggered it
|
||||
*
|
||||
* This method also dispatches a submit event on the form prior to the
|
||||
* submission. The event contains the trigger element as `submittedVia`.
|
||||
*
|
||||
* If the element is a button with a name, the name=value pair will be added
|
||||
* to the submitted data.
|
||||
*/
|
||||
function submit_form_via(element) {
|
||||
/* apparently, the submit event is not triggered in most browsers on
|
||||
* the submit() method, so we do it manually here to model a natural
|
||||
* submit as closely as possible.
|
||||
* Now to the fun fact: If you trigger a submit event from a form, what
|
||||
* do you think should happen?
|
||||
* 1) the form will be automagically submitted by the browser, or
|
||||
* 2) nothing.
|
||||
* And as you already suspected, the correct answer is: both! Firefox
|
||||
* opts for 1), Chrome for 2). Yay! */
|
||||
var event_got_cancelled;
|
||||
|
||||
var submit_event = create_event('submit', { cancelable: true });
|
||||
/* force Firefox to not submit the form, then fake preventDefault() */
|
||||
submit_event.preventDefault();
|
||||
Object.defineProperty(submit_event, 'defaultPrevented', {
|
||||
value: false,
|
||||
writable: true,
|
||||
});
|
||||
Object.defineProperty(submit_event, 'preventDefault', {
|
||||
value: () => submit_event.defaultPrevented = event_got_cancelled = true,
|
||||
writable: true,
|
||||
});
|
||||
trigger_event(element.form, submit_event, {}, { submittedVia: element });
|
||||
|
||||
if (! event_got_cancelled) {
|
||||
add_submit_field(element);
|
||||
window.HTMLFormElement.prototype.submit.call(element.form);
|
||||
window.setTimeout(() => remove_submit_field(element));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* if a submit button was clicked, add its name=value by means of a type=hidden
|
||||
* input field
|
||||
*/
|
||||
function add_submit_field(button) {
|
||||
if (['image', 'submit'].indexOf(button.type) > -1 && button.name) {
|
||||
const wrapper = get_wrapper(button.form) || {};
|
||||
var submit_helper = wrapper.submit_helper;
|
||||
if (submit_helper) {
|
||||
if (submit_helper.parentNode) {
|
||||
submit_helper.parentNode.removeChild(submit_helper);
|
||||
}
|
||||
} else {
|
||||
submit_helper = document.createElement('input');
|
||||
submit_helper.type = 'hidden';
|
||||
wrapper.submit_helper = submit_helper;
|
||||
}
|
||||
submit_helper.name = button.name;
|
||||
submit_helper.value = button.value;
|
||||
button.form.appendChild(submit_helper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* remove a possible helper input, that was added by `add_submit_field`
|
||||
*/
|
||||
function remove_submit_field(button) {
|
||||
if (['image', 'submit'].indexOf(button.type) > -1 && button.name) {
|
||||
const wrapper = get_wrapper(button.form) || {};
|
||||
const submit_helper = wrapper.submit_helper;
|
||||
if (submit_helper && submit_helper.parentNode) {
|
||||
submit_helper.parentNode.removeChild(submit_helper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* check a form's validity and submit it
|
||||
*
|
||||
* The method triggers a cancellable `validate` event on the form. If the
|
||||
* event is cancelled, form submission will be aborted, too.
|
||||
*
|
||||
* If the form is found to contain invalid fields, focus the first field.
|
||||
*/
|
||||
function check(button) {
|
||||
/* trigger a "validate" event on the form to be submitted */
|
||||
const val_event = trigger_event(button.form, 'validate',
|
||||
{ cancelable: true });
|
||||
if (val_event.defaultPrevented) {
|
||||
/* skip the whole submit thing, if the validation is canceled. A user
|
||||
* can still call form.submit() afterwards. */
|
||||
return;
|
||||
}
|
||||
|
||||
var valid = true;
|
||||
var first_invalid;
|
||||
button.form.__hf_form_validation = true;
|
||||
get_validated_elements(button.form).map(element => {
|
||||
if (! reportValidity(element)) {
|
||||
valid = false;
|
||||
if (! first_invalid && ('focus' in element)) {
|
||||
first_invalid = element;
|
||||
}
|
||||
}
|
||||
});
|
||||
delete(button.form.__hf_form_validation);
|
||||
|
||||
if (valid) {
|
||||
submit_form_via(button);
|
||||
} else if (first_invalid) {
|
||||
/* focus the first invalid element, if validation went south */
|
||||
first_invalid.focus();
|
||||
/* tell the tale, if anyone wants to react to it */
|
||||
trigger_event(button.form, 'forminvalid');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* test if node is a submit button
|
||||
*/
|
||||
function is_submit_button(node) {
|
||||
return (
|
||||
/* must be an input or button element... */
|
||||
(node.nodeName === 'INPUT' ||
|
||||
node.nodeName === 'BUTTON') &&
|
||||
|
||||
/* ...and have a submitting type */
|
||||
(node.type === 'image' || node.type === 'submit')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* test, if the click event would trigger a submit
|
||||
*/
|
||||
function is_submitting_click(event, button) {
|
||||
return (
|
||||
/* prevented default: won't trigger a submit */
|
||||
! event.defaultPrevented &&
|
||||
|
||||
/* left button or middle button (submits in Chrome) */
|
||||
(! ('button' in event) ||
|
||||
event.button < 2) &&
|
||||
|
||||
/* must be a submit button... */
|
||||
is_submit_button(button) &&
|
||||
|
||||
/* the button needs a form, that's going to be submitted */
|
||||
button.form &&
|
||||
|
||||
/* again, if the form should not be validated, we're out of the game */
|
||||
! button.form.hasAttribute('novalidate')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* test, if the keypress event would trigger a submit
|
||||
*/
|
||||
function is_submitting_keypress(event) {
|
||||
return (
|
||||
/* prevented default: won't trigger a submit */
|
||||
! event.defaultPrevented &&
|
||||
|
||||
(
|
||||
(
|
||||
/* ...and <Enter> was pressed... */
|
||||
event.keyCode === 13 &&
|
||||
|
||||
/* ...on an <input> that is... */
|
||||
event.target.nodeName === 'INPUT' &&
|
||||
|
||||
/* ...a standard text input field (not checkbox, ...) */
|
||||
text_types.indexOf(event.target.type) > -1
|
||||
) || (
|
||||
/* or <Enter> or <Space> was pressed... */
|
||||
(event.keyCode === 13 ||
|
||||
event.keyCode === 32) &&
|
||||
|
||||
/* ...on a submit button */
|
||||
is_submit_button(event.target)
|
||||
)
|
||||
) &&
|
||||
|
||||
/* there's a form... */
|
||||
event.target.form &&
|
||||
|
||||
/* ...and the form allows validation */
|
||||
! event.target.form.hasAttribute('novalidate')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* catch clicks to children of <button>s
|
||||
*/
|
||||
function get_clicked_button(element) {
|
||||
if (is_submit_button(element)) {
|
||||
return element;
|
||||
} else if (matches(element, 'button:not([type]) *, button[type="submit"] *')) {
|
||||
return get_clicked_button(element.parentNode);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return event handler to catch explicit submission by click on a button
|
||||
*/
|
||||
function get_click_handler(ignore=false) {
|
||||
return function(event) {
|
||||
const button = get_clicked_button(event.target);
|
||||
if (button && is_submitting_click(event, button)) {
|
||||
event.preventDefault();
|
||||
if (ignore || button.hasAttribute('formnovalidate')) {
|
||||
/* if validation should be ignored, we're not interested in any checks */
|
||||
submit_form_via(button);
|
||||
} else {
|
||||
check(button);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
const click_handler = get_click_handler();
|
||||
const ignored_click_handler = get_click_handler(true);
|
||||
|
||||
|
||||
/**
|
||||
* catch implicit submission by pressing <Enter> in some situations
|
||||
*/
|
||||
function get_keypress_handler(ignore) {
|
||||
return function keypress_handler(event) {
|
||||
if (is_submitting_keypress(event)) {
|
||||
event.preventDefault();
|
||||
|
||||
const wrapper = get_wrapper(event.target.form) || { settings: {} };
|
||||
if (wrapper.settings.preventImplicitSubmit) {
|
||||
/* user doesn't want an implicit submit. Cancel here. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* check, that there is no submit button in the form. Otherwise
|
||||
* that should be clicked. */
|
||||
const el = event.target.form.elements.length;
|
||||
var submit;
|
||||
for (let i = 0; i < el; i++) {
|
||||
if (['image', 'submit'].indexOf(event.target.form.elements[i].type) > -1) {
|
||||
submit = event.target.form.elements[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* trigger an "implicit_submit" event on the form to be submitted */
|
||||
const implicit_event = trigger_event(event.target.form, 'implicit_submit',
|
||||
{
|
||||
cancelable: true
|
||||
}, {
|
||||
trigger: event.target,
|
||||
submittedVia: submit || event.target,
|
||||
});
|
||||
if (implicit_event.defaultPrevented) {
|
||||
/* skip the submit, if implicit submit is canceled */
|
||||
return;
|
||||
}
|
||||
|
||||
if (submit) {
|
||||
submit.click();
|
||||
} else if (ignore) {
|
||||
submit_form_via(event.target);
|
||||
} else {
|
||||
check(event.target);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
const keypress_handler = get_keypress_handler();
|
||||
const ignored_keypress_handler = get_keypress_handler(true);
|
||||
|
||||
|
||||
/**
|
||||
* catch all relevant events _prior_ to a form being submitted
|
||||
*
|
||||
* @param bool ignore bypass validation, when an attempt to submit the
|
||||
* form is detected. True, when the wrapper's revalidate
|
||||
* setting is 'never'.
|
||||
*/
|
||||
export function catch_submit(listening_node, ignore=false) {
|
||||
if (ignore) {
|
||||
listening_node.addEventListener('click', ignored_click_handler);
|
||||
listening_node.addEventListener('keypress', ignored_keypress_handler);
|
||||
} else {
|
||||
listening_node.addEventListener('click', click_handler);
|
||||
listening_node.addEventListener('keypress', keypress_handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* decommission the event listeners from catch_submit() again
|
||||
*/
|
||||
export function uncatch_submit(listening_node) {
|
||||
listening_node.removeEventListener('click', ignored_click_handler);
|
||||
listening_node.removeEventListener('keypress', ignored_keypress_handler);
|
||||
listening_node.removeEventListener('click', click_handler);
|
||||
listening_node.removeEventListener('keypress', keypress_handler);
|
||||
}
|
||||
18
public/assets/hyperform-0.12.0/src/tools/comma_split.js
Normal file
18
public/assets/hyperform-0.12.0/src/tools/comma_split.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import trim from './trim';
|
||||
|
||||
|
||||
/**
|
||||
* split a string on comma and trim the components
|
||||
*
|
||||
* As specified at
|
||||
* https://html.spec.whatwg.org/multipage/infrastructure.html#split-a-string-on-commas
|
||||
* plus removing empty entries.
|
||||
*/
|
||||
export default function(str) {
|
||||
return str.split(',')
|
||||
.map(item => trim(item))
|
||||
.filter(b=>b);
|
||||
}
|
||||
66
public/assets/hyperform-0.12.0/src/tools/date_to_string.js
Normal file
66
public/assets/hyperform-0.12.0/src/tools/date_to_string.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import sprintf from './sprintf';
|
||||
import get_week_of_year from './get_week_of_year';
|
||||
|
||||
|
||||
function pad(num, size=2) {
|
||||
var s = num + '';
|
||||
while (s.length < size) {
|
||||
s = '0' + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* calculate a string from a date according to HTML5
|
||||
*/
|
||||
export default function date_to_string(date, element_type) {
|
||||
if (! (date instanceof Date)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (element_type) {
|
||||
case 'datetime':
|
||||
return date_to_string(date, 'date') + 'T' +
|
||||
date_to_string(date, 'time');
|
||||
|
||||
case 'datetime-local':
|
||||
return sprintf('%s-%s-%sT%s:%s:%s.%s',
|
||||
date.getFullYear(),
|
||||
pad(date.getMonth() + 1),
|
||||
pad(date.getDate()),
|
||||
pad(date.getHours()),
|
||||
pad(date.getMinutes()),
|
||||
pad(date.getSeconds()),
|
||||
pad(date.getMilliseconds(), 3)
|
||||
).replace(/(:00)?\.000$/, '');
|
||||
|
||||
case 'date':
|
||||
return sprintf('%s-%s-%s',
|
||||
date.getUTCFullYear(),
|
||||
pad(date.getUTCMonth() + 1),
|
||||
pad(date.getUTCDate()));
|
||||
|
||||
case 'month':
|
||||
return sprintf('%s-%s', date.getUTCFullYear(),
|
||||
pad(date.getUTCMonth() + 1));
|
||||
|
||||
case 'week': {
|
||||
const params = get_week_of_year(date);
|
||||
return sprintf.call(null, '%s-W%s', params[0], pad(params[1]));
|
||||
}
|
||||
|
||||
case 'time':
|
||||
return sprintf('%s:%s:%s.%s',
|
||||
pad(date.getUTCHours()),
|
||||
pad(date.getUTCMinutes()),
|
||||
pad(date.getUTCSeconds()),
|
||||
pad(date.getUTCMilliseconds(), 3)
|
||||
).replace(/(:00)?\.000$/, '');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
23
public/assets/hyperform-0.12.0/src/tools/format_date.js
Normal file
23
public/assets/hyperform-0.12.0/src/tools/format_date.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
export default function(date, part=undefined) {
|
||||
switch (part) {
|
||||
case 'date':
|
||||
return (date.toLocaleDateString || date.toDateString).call(date);
|
||||
case 'time':
|
||||
return (date.toLocaleTimeString || date.toTimeString).call(date);
|
||||
case 'month':
|
||||
return ('toLocaleDateString' in date)?
|
||||
date.toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
})
|
||||
:
|
||||
date.toDateString();
|
||||
// case 'week':
|
||||
// TODO
|
||||
default:
|
||||
return (date.toLocaleString || date.toString).call(date);
|
||||
}
|
||||
}
|
||||
20
public/assets/hyperform-0.12.0/src/tools/generate_id.js
Normal file
20
public/assets/hyperform-0.12.0/src/tools/generate_id.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* counter that will be incremented with every call
|
||||
*
|
||||
* Will enforce uniqueness, as long as no more than 1 hyperform scripts
|
||||
* are loaded. (In that case we still have the "random" part below.)
|
||||
*/
|
||||
var uid = 0;
|
||||
|
||||
|
||||
/**
|
||||
* generate a random ID
|
||||
*
|
||||
* @see https://gist.github.com/gordonbrander/2230317
|
||||
*/
|
||||
export default function(prefix='hf_') {
|
||||
return prefix + ( uid++ ) + Math.random().toString(36).substr(2);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* return a new Date() representing the ISO date for a week number
|
||||
*
|
||||
* @see http://stackoverflow.com/a/16591175/113195
|
||||
*/
|
||||
export default function(week, year) {
|
||||
const date = new Date(Date.UTC(year, 0, 1 + (week - 1) * 7));
|
||||
|
||||
if (date.getUTCDay() <= 4/* thursday */) {
|
||||
date.setUTCDate(date.getUTCDate() - date.getUTCDay() + 1);
|
||||
} else {
|
||||
date.setUTCDate(date.getUTCDate() + 8 - date.getUTCDay());
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
93
public/assets/hyperform-0.12.0/src/tools/get_next_valid.js
Normal file
93
public/assets/hyperform-0.12.0/src/tools/get_next_valid.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import date_to_string from './date_to_string';
|
||||
import string_to_number from './string_to_number';
|
||||
import get_type from './get_type';
|
||||
import _ from '../components/localization';
|
||||
import { default_step, step_scale_factor, default_step_base, default_min,
|
||||
default_max, } from '../components/step_defaults';
|
||||
import { dates } from '../components/types';
|
||||
|
||||
|
||||
/**
|
||||
* get previous and next valid values for a stepped input element
|
||||
*/
|
||||
export default function(element, n=1) {
|
||||
const type = get_type(element);
|
||||
|
||||
const aMin = element.getAttribute('min');
|
||||
let min = default_min[type] || NaN;
|
||||
if (aMin) {
|
||||
const pMin = string_to_number(aMin, type);
|
||||
if (! isNaN(pMin)) {
|
||||
min = pMin;
|
||||
}
|
||||
}
|
||||
|
||||
const aMax = element.getAttribute('max');
|
||||
let max = default_max[type] || NaN;
|
||||
if (aMax) {
|
||||
const pMax = string_to_number(aMax, type);
|
||||
if (! isNaN(pMax)) {
|
||||
max = pMax;
|
||||
}
|
||||
}
|
||||
|
||||
const aStep = element.getAttribute('step');
|
||||
let step = default_step[type] || 1;
|
||||
if (aStep && aStep.toLowerCase() === 'any') {
|
||||
/* quick return: we cannot calculate prev and next */
|
||||
return [_('any value'), _('any value')];
|
||||
} else if (aStep) {
|
||||
const pStep = string_to_number(aStep, type);
|
||||
if (! isNaN(pStep)) {
|
||||
step = pStep;
|
||||
}
|
||||
}
|
||||
|
||||
const default_value = string_to_number(element.getAttribute('value'), type);
|
||||
|
||||
const value = string_to_number(element.value ||
|
||||
element.getAttribute('value'), type);
|
||||
|
||||
if (isNaN(value)) {
|
||||
/* quick return: we cannot calculate without a solid base */
|
||||
return [_('any valid value'), _('any valid value')];
|
||||
}
|
||||
|
||||
const step_base = (
|
||||
! isNaN(min)? min : (
|
||||
! isNaN(default_value)? default_value : (
|
||||
default_step_base[type] || 0
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const scale = step_scale_factor[type] || 1;
|
||||
|
||||
var prev = step_base +
|
||||
Math.floor((value - step_base) / (step * scale)) * (step * scale) * n;
|
||||
var next = step_base +
|
||||
(Math.floor((value - step_base) / (step * scale)) + 1) * (step * scale) * n;
|
||||
|
||||
if (prev < min) {
|
||||
prev = null;
|
||||
} else if (prev > max) {
|
||||
prev = max;
|
||||
}
|
||||
|
||||
if (next > max) {
|
||||
next = null;
|
||||
} else if (next < min) {
|
||||
next = min;
|
||||
}
|
||||
|
||||
/* convert to date objects, if appropriate */
|
||||
if (dates.indexOf(type) > -1) {
|
||||
prev = date_to_string(new Date(prev), type);
|
||||
next = date_to_string(new Date(next), type);
|
||||
}
|
||||
|
||||
return [prev, next];
|
||||
}
|
||||
16
public/assets/hyperform-0.12.0/src/tools/get_radiogroup.js
Normal file
16
public/assets/hyperform-0.12.0/src/tools/get_radiogroup.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* get all radio buttons (including `element`) that belong to element's
|
||||
* radio group
|
||||
*/
|
||||
export function get_radiogroup(element) {
|
||||
if (element.form) {
|
||||
return Array.prototype.filter.call(
|
||||
element.form.elements,
|
||||
radio => radio.type === 'radio' && radio.name === element.name
|
||||
);
|
||||
}
|
||||
return [element];
|
||||
}
|
||||
33
public/assets/hyperform-0.12.0/src/tools/get_type.js
Normal file
33
public/assets/hyperform-0.12.0/src/tools/get_type.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { inputs } from '../components/types';
|
||||
|
||||
|
||||
/**
|
||||
* get the element's type in a backwards-compatible way
|
||||
*/
|
||||
export default function(element) {
|
||||
if (element instanceof window.HTMLTextAreaElement) {
|
||||
return 'textarea';
|
||||
|
||||
} else if (element instanceof window.HTMLSelectElement) {
|
||||
return element.hasAttribute('multiple')? 'select-multiple' : 'select-one';
|
||||
|
||||
} else if (element instanceof window.HTMLButtonElement) {
|
||||
return (element.getAttribute('type') || 'submit').toLowerCase();
|
||||
|
||||
} else if (element instanceof window.HTMLInputElement) {
|
||||
const attr = (element.getAttribute('type') || '').toLowerCase();
|
||||
if (attr && inputs.indexOf(attr) > -1) {
|
||||
return attr;
|
||||
} else {
|
||||
/* perhaps the DOM has in-depth knowledge. Take that before returning
|
||||
* 'text'. */
|
||||
return element.type || 'text';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { get_wrapper } from '../components/wrapper';
|
||||
|
||||
|
||||
/**
|
||||
* filter a form's elements for the ones needing validation prior to
|
||||
* a submit
|
||||
*
|
||||
* Returns an array of form elements.
|
||||
*/
|
||||
export function get_validated_elements(form) {
|
||||
const wrapped_form = get_wrapper(form);
|
||||
|
||||
return Array.prototype.filter.call(form.elements, element => {
|
||||
/* it must have a name (or validating nameless inputs is allowed) */
|
||||
if (element.getAttribute('name') ||
|
||||
(wrapped_form && wrapped_form.settings.validateNameless)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
35
public/assets/hyperform-0.12.0/src/tools/get_week_of_year.js
Normal file
35
public/assets/hyperform-0.12.0/src/tools/get_week_of_year.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
/* For a given date, get the ISO week number
|
||||
*
|
||||
* Source: http://stackoverflow.com/a/6117889/113195
|
||||
*
|
||||
* Based on information at:
|
||||
*
|
||||
* http://www.merlyn.demon.co.uk/weekcalc.htm#WNR
|
||||
*
|
||||
* Algorithm is to find nearest thursday, it's year
|
||||
* is the year of the week number. Then get weeks
|
||||
* between that date and the first day of that year.
|
||||
*
|
||||
* Note that dates in one year can be weeks of previous
|
||||
* or next year, overlap is up to 3 days.
|
||||
*
|
||||
* e.g. 2014/12/29 is Monday in week 1 of 2015
|
||||
* 2012/1/1 is Sunday in week 52 of 2011
|
||||
*/
|
||||
export default function(d) {
|
||||
/* Copy date so don't modify original */
|
||||
d = new Date(+d);
|
||||
d.setUTCHours(0, 0, 0);
|
||||
/* Set to nearest Thursday: current date + 4 - current day number
|
||||
* Make Sunday's day number 7 */
|
||||
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay()||7));
|
||||
/* Get first day of year */
|
||||
const yearStart = new Date(d.getUTCFullYear(),0,1);
|
||||
/* Calculate full weeks to nearest Thursday */
|
||||
const weekNo = Math.ceil(( ( (d - yearStart) / 86400000) + 1)/7);
|
||||
/* Return array of year and week number */
|
||||
return [d.getUTCFullYear(), weekNo];
|
||||
}
|
||||
17
public/assets/hyperform-0.12.0/src/tools/is_field.js
Normal file
17
public/assets/hyperform-0.12.0/src/tools/is_field.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
export default function(element) {
|
||||
return (
|
||||
element instanceof window.HTMLButtonElement ||
|
||||
element instanceof window.HTMLInputElement ||
|
||||
element instanceof window.HTMLSelectElement ||
|
||||
element instanceof window.HTMLTextAreaElement ||
|
||||
element instanceof window.HTMLFieldSetElement ||
|
||||
element === window.HTMLButtonElement.prototype ||
|
||||
element === window.HTMLInputElement.prototype ||
|
||||
element === window.HTMLSelectElement.prototype ||
|
||||
element === window.HTMLTextAreaElement.prototype ||
|
||||
element === window.HTMLFieldSetElement.prototype
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { do_filter } from '../components/hooks';
|
||||
import { validation_candidates, non_inputs } from '../components/types';
|
||||
import { get_wrapper } from '../components/wrapper';
|
||||
import get_type from '../tools/get_type';
|
||||
|
||||
|
||||
/**
|
||||
* check if an element should be ignored due to any of its parents
|
||||
*
|
||||
* Checks <fieldset disabled> and <datalist>.
|
||||
*/
|
||||
function is_in_disallowed_parent(element) {
|
||||
let p = element.parentNode;
|
||||
while (p && p.nodeType === 1) {
|
||||
if (p instanceof window.HTMLFieldSetElement &&
|
||||
p.hasAttribute('disabled')) {
|
||||
/* quick return, if it's a child of a disabled fieldset */
|
||||
return true;
|
||||
} else if (p.nodeName.toUpperCase() === 'DATALIST') {
|
||||
/* quick return, if it's a child of a datalist
|
||||
* Do not use HTMLDataListElement to support older browsers,
|
||||
* too.
|
||||
* @see https://html.spec.whatwg.org/multipage/forms.html#the-datalist-element:barred-from-constraint-validation
|
||||
*/
|
||||
return true;
|
||||
} else if (p === element.form) {
|
||||
/* the outer boundary. We can stop looking for relevant elements. */
|
||||
break;
|
||||
}
|
||||
p = p.parentNode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* check if an element is a candidate for constraint validation
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/multipage/forms.html#barred-from-constraint-validation
|
||||
*/
|
||||
export default function(element) {
|
||||
|
||||
/* allow a shortcut via filters, e.g. to validate type=hidden fields */
|
||||
const filtered = do_filter('is_validation_candidate', null, element);
|
||||
if (filtered !== null) {
|
||||
return !! filtered;
|
||||
}
|
||||
|
||||
/* it must be any of those elements */
|
||||
if (element instanceof window.HTMLSelectElement
|
||||
||
|
||||
element instanceof window.HTMLTextAreaElement
|
||||
||
|
||||
element instanceof window.HTMLButtonElement
|
||||
||
|
||||
element instanceof window.HTMLInputElement) {
|
||||
|
||||
const type = get_type(element);
|
||||
/* its type must be in the whitelist */
|
||||
if (non_inputs.indexOf(type) > -1 ||
|
||||
validation_candidates.indexOf(type) > -1) {
|
||||
|
||||
/* it mustn't be disabled or readonly */
|
||||
if (! element.hasAttribute('disabled') &&
|
||||
! element.hasAttribute('readonly')) {
|
||||
|
||||
const wrapped_form = get_wrapper(element);
|
||||
|
||||
if (
|
||||
/* the parent form doesn't allow non-standard "novalidate" attributes... */
|
||||
(wrapped_form && ! wrapped_form.settings.novalidateOnElements) ||
|
||||
/* ...or it doesn't have such an attribute/property */
|
||||
(! element.hasAttribute('novalidate') && ! element.noValidate)
|
||||
) {
|
||||
|
||||
/* it isn't part of a <fieldset disabled> */
|
||||
if (! is_in_disallowed_parent(element)) {
|
||||
|
||||
/* then it's a candidate */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* this is no HTML5 validation candidate... */
|
||||
return false;
|
||||
}
|
||||
21
public/assets/hyperform-0.12.0/src/tools/mark.js
Normal file
21
public/assets/hyperform-0.12.0/src/tools/mark.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* mark an object with a '__hyperform=true' property
|
||||
*
|
||||
* We use this to distinguish our properties from the native ones. Usage:
|
||||
* js> mark(obj);
|
||||
* js> assert(obj.__hyperform === true)
|
||||
*/
|
||||
export default function(obj) {
|
||||
if (['object', 'function'].indexOf(typeof obj) > -1) {
|
||||
delete obj.__hyperform;
|
||||
Object.defineProperty(obj, '__hyperform', {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: true,
|
||||
});
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
13
public/assets/hyperform-0.12.0/src/tools/matches.js
Normal file
13
public/assets/hyperform-0.12.0/src/tools/matches.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
/* shim layer for the Element.matches method */
|
||||
|
||||
const ep = window.Element.prototype;
|
||||
const native_matches = ep.matches ||
|
||||
ep.matchesSelector ||
|
||||
ep.msMatchesSelector ||
|
||||
ep.webkitMatchesSelector;
|
||||
|
||||
export default function(element, selector) {
|
||||
return native_matches.call(element, selector);
|
||||
}
|
||||
70
public/assets/hyperform-0.12.0/src/tools/polyfill.js
Normal file
70
public/assets/hyperform-0.12.0/src/tools/polyfill.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import install_property from './property_installer';
|
||||
import is_field from './is_field';
|
||||
import mark from './mark';
|
||||
|
||||
import checkValidity from '../polyfills/checkValidity';
|
||||
import reportValidity from '../polyfills/reportValidity';
|
||||
import setCustomValidity from '../polyfills/setCustomValidity';
|
||||
import stepDown from '../polyfills/stepDown';
|
||||
import stepUp from '../polyfills/stepUp';
|
||||
import validationMessage from '../polyfills/validationMessage';
|
||||
import ValidityState from '../polyfills/validityState';
|
||||
import valueAsDate from '../polyfills/valueAsDate';
|
||||
import valueAsNumber from '../polyfills/valueAsNumber';
|
||||
import willValidate from '../polyfills/willValidate';
|
||||
import { install_properties } from '../polyfills/properties';
|
||||
|
||||
|
||||
const polyfills = {
|
||||
checkValidity: {
|
||||
value: mark(function() { return checkValidity(this); }),
|
||||
},
|
||||
reportValidity: {
|
||||
value: mark(function() { return reportValidity(this); }),
|
||||
},
|
||||
setCustomValidity: {
|
||||
value: mark(function (msg) { return setCustomValidity(this, msg); }),
|
||||
},
|
||||
stepDown: {
|
||||
value: mark(function(n=1) { return stepDown(this, n); }),
|
||||
},
|
||||
stepUp: {
|
||||
value: mark(function(n=1) { return stepUp(this, n); }),
|
||||
},
|
||||
validationMessage: {
|
||||
get: mark(function() { return validationMessage(this); }),
|
||||
},
|
||||
validity: {
|
||||
get: mark(function() { return ValidityState(this); }),
|
||||
},
|
||||
valueAsDate: {
|
||||
get: mark(function() { return valueAsDate(this); }),
|
||||
set: mark(function(value) { valueAsDate(this, value); }),
|
||||
},
|
||||
valueAsNumber: {
|
||||
get: mark(function() { return valueAsNumber(this); }),
|
||||
set: mark(function(value) { valueAsNumber(this, value); }),
|
||||
},
|
||||
willValidate: {
|
||||
get: mark(function() { return willValidate(this); }),
|
||||
},
|
||||
};
|
||||
|
||||
export default function(element) {
|
||||
if (is_field(element)) {
|
||||
|
||||
for (let prop in polyfills) {
|
||||
install_property(element, prop, polyfills[prop]);
|
||||
}
|
||||
|
||||
install_properties(element);
|
||||
|
||||
} else if (element instanceof window.HTMLFormElement ||
|
||||
element === window.HTMLFormElement.prototype) {
|
||||
install_property(element, 'checkValidity', polyfills.checkValidity);
|
||||
install_property(element, 'reportValidity', polyfills.reportValidity);
|
||||
}
|
||||
}
|
||||
29
public/assets/hyperform-0.12.0/src/tools/polyunfill.js
Normal file
29
public/assets/hyperform-0.12.0/src/tools/polyunfill.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import is_field from './is_field';
|
||||
import uninstall_property from './property_uninstaller';
|
||||
import { uninstall_properties } from '../polyfills/properties';
|
||||
|
||||
|
||||
export default function(element) {
|
||||
if (is_field(element)) {
|
||||
|
||||
uninstall_property(element, 'checkValidity');
|
||||
uninstall_property(element, 'reportValidity');
|
||||
uninstall_property(element, 'setCustomValidity');
|
||||
uninstall_property(element, 'stepDown');
|
||||
uninstall_property(element, 'stepUp');
|
||||
uninstall_property(element, 'validationMessage');
|
||||
uninstall_property(element, 'validity');
|
||||
uninstall_property(element, 'valueAsDate');
|
||||
uninstall_property(element, 'valueAsNumber');
|
||||
uninstall_property(element, 'willValidate');
|
||||
|
||||
uninstall_properties(element);
|
||||
|
||||
} else if (element instanceof window.HTMLFormElement) {
|
||||
uninstall_property(element, 'checkValidity');
|
||||
uninstall_property(element, 'reportValidity');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { get_wrapper } from '../components/wrapper';
|
||||
|
||||
|
||||
/**
|
||||
* add `property` to an element
|
||||
*
|
||||
* ATTENTION! This function will search for an equally named property on the
|
||||
* *prototype* of an element, if element is a concrete DOM node. Do not use
|
||||
* it as general-purpose property installer.
|
||||
*
|
||||
* js> installer(element, 'foo', { value: 'bar' });
|
||||
* js> assert(element.foo === 'bar');
|
||||
*/
|
||||
export default function(element, property, descriptor) {
|
||||
descriptor.configurable = true;
|
||||
descriptor.enumerable = true;
|
||||
if ('value' in descriptor) {
|
||||
descriptor.writable = true;
|
||||
}
|
||||
|
||||
/* on concrete instances, i.e., <input> elements, the naive lookup
|
||||
* yields undefined. We have to look on its prototype then. On elements
|
||||
* like the actual HTMLInputElement object the first line works. */
|
||||
let original_descriptor = Object.getOwnPropertyDescriptor(element, property);
|
||||
if (original_descriptor === undefined) {
|
||||
original_descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), property);
|
||||
}
|
||||
|
||||
if (original_descriptor) {
|
||||
|
||||
if (original_descriptor.configurable === false) {
|
||||
/* Safari <= 9 and PhantomJS will end up here :-( Nothing to do except
|
||||
* warning */
|
||||
const wrapper = get_wrapper(element);
|
||||
if (wrapper && wrapper.settings.debug) {
|
||||
/* global console */
|
||||
console.log('[hyperform] cannot install custom property '+property);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we already installed that property... */
|
||||
if ((original_descriptor.get && original_descriptor.get.__hyperform) ||
|
||||
(original_descriptor.value && original_descriptor.value.__hyperform)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* publish existing property under new name, if it's not from us */
|
||||
Object.defineProperty(
|
||||
element,
|
||||
'_original_'+property,
|
||||
original_descriptor
|
||||
);
|
||||
}
|
||||
|
||||
delete element[property];
|
||||
Object.defineProperty(element, property, descriptor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { get_wrapper } from '../components/wrapper';
|
||||
|
||||
|
||||
/**
|
||||
* remove `property` from element and restore _original_property, if present
|
||||
*/
|
||||
export default function(element, property) {
|
||||
try {
|
||||
delete element[property];
|
||||
} catch (e) {
|
||||
/* Safari <= 9 and PhantomJS will end up here :-( Nothing to do except
|
||||
* warning */
|
||||
const wrapper = get_wrapper(element);
|
||||
if (wrapper && wrapper.settings.debug) {
|
||||
/* global console */
|
||||
console.log('[hyperform] cannot uninstall custom property '+property);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const original_descriptor = Object.getOwnPropertyDescriptor(element,
|
||||
'_original_'+property);
|
||||
|
||||
if (original_descriptor) {
|
||||
Object.defineProperty(element, property, original_descriptor);
|
||||
}
|
||||
|
||||
}
|
||||
23
public/assets/hyperform-0.12.0/src/tools/return_hook_or.js
Normal file
23
public/assets/hyperform-0.12.0/src/tools/return_hook_or.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { call_hook } from '../components/hooks.js';
|
||||
|
||||
|
||||
/**
|
||||
* return either the data of a hook call or the result of action, if the
|
||||
* former is undefined
|
||||
*
|
||||
* @return function a function wrapper around action
|
||||
*/
|
||||
export default function(hook, action) {
|
||||
return function() {
|
||||
const data = call_hook(hook, Array.prototype.slice.call(arguments));
|
||||
|
||||
if (data !== undefined) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return action.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
34
public/assets/hyperform-0.12.0/src/tools/sprintf.js
Normal file
34
public/assets/hyperform-0.12.0/src/tools/sprintf.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
export default function(str, ...args) {
|
||||
const args_length = args.length;
|
||||
var global_index = 0;
|
||||
|
||||
return str.replace(/%([0-9]+\$)?([sl])/g, (match, position, type) => {
|
||||
var local_index = global_index;
|
||||
if (position) {
|
||||
local_index = Number(position.replace(/\$$/, '')) - 1;
|
||||
}
|
||||
global_index += 1;
|
||||
|
||||
var arg = '';
|
||||
if (args_length > local_index) {
|
||||
arg = args[local_index];
|
||||
}
|
||||
|
||||
if (arg instanceof Date ||
|
||||
typeof arg === 'number' ||
|
||||
arg instanceof Number) {
|
||||
/* try getting a localized representation of dates and numbers, if the
|
||||
* browser supports this */
|
||||
if (type === 'l') {
|
||||
arg = (arg.toLocaleString || arg.toString).call(arg);
|
||||
} else {
|
||||
arg = arg.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return arg;
|
||||
});
|
||||
}
|
||||
49
public/assets/hyperform-0.12.0/src/tools/string_to_date.js
Normal file
49
public/assets/hyperform-0.12.0/src/tools/string_to_date.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_date_from_week from './get_date_from_week';
|
||||
|
||||
|
||||
/**
|
||||
* calculate a date from a string according to HTML5
|
||||
*/
|
||||
export default function(string, element_type) {
|
||||
let date;
|
||||
switch (element_type) {
|
||||
case 'datetime':
|
||||
if (! /^([0-9]{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9])(?:\.([0-9]{1,3}))?)?$/.test(string)) {
|
||||
return null;
|
||||
}
|
||||
date = new Date(string+'z');
|
||||
return isNaN(date.valueOf())? null : date;
|
||||
|
||||
case 'date':
|
||||
if (! /^([0-9]{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/.test(string)) {
|
||||
return null;
|
||||
}
|
||||
date = new Date(string);
|
||||
return isNaN(date.valueOf())? null : date;
|
||||
|
||||
case 'month':
|
||||
if (! /^([0-9]{4})-(0[1-9]|1[012])$/.test(string)) {
|
||||
return null;
|
||||
}
|
||||
date = new Date(string);
|
||||
return isNaN(date.valueOf())? null : date;
|
||||
|
||||
case 'week':
|
||||
if (! /^([0-9]{4})-W(0[1-9]|[1234][0-9]|5[0-3])$/.test(string)) {
|
||||
return null;
|
||||
}
|
||||
return get_date_from_week(Number(RegExp.$2), Number(RegExp.$1));
|
||||
|
||||
case 'time':
|
||||
if (! /^([01][0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9])(?:\.([0-9]{1,3}))?)?$/.test(string)) {
|
||||
return null;
|
||||
}
|
||||
date = new Date('1970-01-01T'+string+'z');
|
||||
return date;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
17
public/assets/hyperform-0.12.0/src/tools/string_to_number.js
Normal file
17
public/assets/hyperform-0.12.0/src/tools/string_to_number.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import string_to_date from './string_to_date';
|
||||
|
||||
|
||||
/**
|
||||
* calculate a number from a string according to HTML5
|
||||
*/
|
||||
export default function(string, element_type) {
|
||||
const rval = string_to_date(string, element_type);
|
||||
if (rval !== null) {
|
||||
return +rval;
|
||||
}
|
||||
/* not parseFloat, because we want NaN for invalid values like "1.2xxy" */
|
||||
return Number(string);
|
||||
}
|
||||
60
public/assets/hyperform-0.12.0/src/tools/trigger_event.js
Normal file
60
public/assets/hyperform-0.12.0/src/tools/trigger_event.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
'use strict';
|
||||
|
||||
/* the following code is borrowed from the WebComponents project, licensed
|
||||
* under the BSD license. Source:
|
||||
* <https://github.com/webcomponents/webcomponentsjs/blob/5283db1459fa2323e5bfc8b9b5cc1753ed85e3d0/src/WebComponents/dom.js#L53-L78>
|
||||
*/
|
||||
// defaultPrevented is broken in IE.
|
||||
// https://connect.microsoft.com/IE/feedback/details/790389/event-defaultprevented-returns-false-after-preventdefault-was-called
|
||||
const workingDefaultPrevented = (function() {
|
||||
const e = document.createEvent('Event');
|
||||
e.initEvent('foo', true, true);
|
||||
e.preventDefault();
|
||||
return e.defaultPrevented;
|
||||
})();
|
||||
|
||||
if (!workingDefaultPrevented) {
|
||||
const origPreventDefault = window.Event.prototype.preventDefault;
|
||||
window.Event.prototype.preventDefault = function() {
|
||||
if (!this.cancelable) {
|
||||
return;
|
||||
}
|
||||
|
||||
origPreventDefault.call(this);
|
||||
|
||||
Object.defineProperty(this, 'defaultPrevented', {
|
||||
get: function() {
|
||||
return true;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
}
|
||||
/* end of borrowed code */
|
||||
|
||||
|
||||
export function create_event(name, { bubbles=true, cancelable=false, }={}) {
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent(name, bubbles, cancelable);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
export default function(element, event, {
|
||||
bubbles=true,
|
||||
cancelable=false,
|
||||
}={}, payload={}) {
|
||||
if (! (event instanceof window.Event)) {
|
||||
event = create_event(event, { bubbles, cancelable });
|
||||
}
|
||||
|
||||
for (let key in payload) {
|
||||
if (payload.hasOwnProperty(key)) {
|
||||
event[key] = payload[key];
|
||||
}
|
||||
}
|
||||
|
||||
element.dispatchEvent(event);
|
||||
|
||||
return event;
|
||||
}
|
||||
14
public/assets/hyperform-0.12.0/src/tools/trim.js
Normal file
14
public/assets/hyperform-0.12.0/src/tools/trim.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
const ws_on_start_or_end = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
|
||||
|
||||
|
||||
/**
|
||||
* trim a string of whitespace
|
||||
*
|
||||
* We don't use String.trim() to remove the need to polyfill it.
|
||||
*/
|
||||
export default function(str) {
|
||||
return str.replace(ws_on_start_or_end, '');
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* patch String.length to account for non-BMP characters
|
||||
*
|
||||
* @see https://mathiasbynens.be/notes/javascript-unicode
|
||||
* We do not use the simple [...str].length, because it needs a ton of
|
||||
* polyfills in older browsers.
|
||||
*/
|
||||
export default function(str) {
|
||||
return str.match(/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g).length;
|
||||
}
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* Implement constraint checking functionality defined in the HTML5 standard
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/multipage/forms.html#dom-cva-validity
|
||||
* @return bool true if the test fails [!], false otherwise
|
||||
*/
|
||||
|
||||
|
||||
import format_date from './format_date';
|
||||
import get_next_valid from './get_next_valid';
|
||||
import get_type from './get_type';
|
||||
import sprintf from './sprintf';
|
||||
import string_to_number from './string_to_number';
|
||||
import string_to_date from './string_to_date';
|
||||
import unicode_string_length from './unicode_string_length';
|
||||
import custom_messages from '../components/custom_messages';
|
||||
import _ from '../components/localization';
|
||||
import { message_store } from '../components/message_store';
|
||||
import CustomValidatorRegistry from '../components/registry';
|
||||
import { get_wrapper } from '../components/wrapper';
|
||||
import test_bad_input from '../validators/bad_input';
|
||||
import test_max from '../validators/max';
|
||||
import test_maxlength from '../validators/maxlength';
|
||||
import test_min from '../validators/min';
|
||||
import test_minlength from '../validators/minlength';
|
||||
import test_pattern from '../validators/pattern';
|
||||
import test_required from '../validators/required';
|
||||
import test_step from '../validators/step';
|
||||
import test_type from '../validators/type';
|
||||
|
||||
|
||||
/**
|
||||
* boilerplate function for all tests but customError
|
||||
*/
|
||||
function check(test, react) {
|
||||
return element => {
|
||||
const invalid = ! test(element);
|
||||
if (invalid) {
|
||||
react(element);
|
||||
}
|
||||
return invalid;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create a common function to set error messages
|
||||
*/
|
||||
function set_msg(element, msgtype, _default) {
|
||||
message_store.set(element, custom_messages.get(element, msgtype, _default));
|
||||
}
|
||||
|
||||
|
||||
const badInput = check(test_bad_input, element => set_msg(element, 'badInput',
|
||||
_('Please match the requested type.')));
|
||||
|
||||
|
||||
function customError(element) {
|
||||
/* prevent infinite loops when the custom validators call setCustomValidity(),
|
||||
* which in turn calls this code again. We check, if there is an already set
|
||||
* custom validity message there. */
|
||||
if (element.__hf_custom_validation_running) {
|
||||
const msg = message_store.get(element);
|
||||
return (msg && msg.is_custom);
|
||||
}
|
||||
|
||||
/* check, if there are custom validators in the registry, and call
|
||||
* them. */
|
||||
const custom_validators = CustomValidatorRegistry.get(element);
|
||||
const cvl = custom_validators.length;
|
||||
var valid = true;
|
||||
|
||||
if (cvl) {
|
||||
element.__hf_custom_validation_running = true;
|
||||
for (let i = 0; i < cvl; i++) {
|
||||
const result = custom_validators[i](element);
|
||||
if (result !== undefined && ! result) {
|
||||
valid = false;
|
||||
/* break on first invalid response */
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete(element.__hf_custom_validation_running);
|
||||
}
|
||||
|
||||
/* check, if there are other validity messages already */
|
||||
if (valid) {
|
||||
const msg = message_store.get(element);
|
||||
valid = ! (msg.toString() && ('is_custom' in msg));
|
||||
}
|
||||
|
||||
return ! valid;
|
||||
}
|
||||
|
||||
|
||||
const patternMismatch = check(test_pattern, element => {
|
||||
set_msg(element, 'patternMismatch',
|
||||
element.title?
|
||||
sprintf(_('PatternMismatchWithTitle'), element.title)
|
||||
:
|
||||
_('PatternMismatch')
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* TODO: when rangeOverflow and rangeUnderflow are both called directly and
|
||||
* successful, the inRange and outOfRange classes won't get removed, unless
|
||||
* element.validityState.valid is queried, too.
|
||||
*/
|
||||
const rangeOverflow = check(test_max, element => {
|
||||
const type = get_type(element);
|
||||
const wrapper = get_wrapper(element);
|
||||
const outOfRangeClass = wrapper && wrapper.settings.classes.outOfRange || 'hf-out-of-range';
|
||||
const inRangeClass = wrapper && wrapper.settings.classes.inRange || 'hf-in-range';
|
||||
|
||||
let msg;
|
||||
|
||||
switch (type) {
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
case 'datetime-local':
|
||||
msg = sprintf(_('DateRangeOverflow'),
|
||||
format_date(string_to_date(element.getAttribute('max'), type), type));
|
||||
break;
|
||||
case 'time':
|
||||
msg = sprintf(_('TimeRangeOverflow'),
|
||||
format_date(string_to_date(element.getAttribute('max'), type), type));
|
||||
break;
|
||||
// case 'number':
|
||||
default:
|
||||
msg = sprintf(_('NumberRangeOverflow'),
|
||||
string_to_number(element.getAttribute('max'), type));
|
||||
break;
|
||||
}
|
||||
|
||||
set_msg(element, 'rangeOverflow', msg);
|
||||
element.classList.add(outOfRangeClass);
|
||||
element.classList.remove(inRangeClass);
|
||||
});
|
||||
|
||||
|
||||
const rangeUnderflow = check(test_min, element => {
|
||||
const type = get_type(element);
|
||||
const wrapper = get_wrapper(element);
|
||||
const outOfRangeClass = wrapper && wrapper.settings.classes.outOfRange || 'hf-out-of-range';
|
||||
const inRangeClass = wrapper && wrapper.settings.classes.inRange || 'hf-in-range';
|
||||
|
||||
let msg;
|
||||
|
||||
switch (type) {
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
case 'datetime-local':
|
||||
msg = sprintf(_('DateRangeUnderflow'),
|
||||
format_date(string_to_date(element.getAttribute('min'), type), type));
|
||||
break;
|
||||
case 'time':
|
||||
msg = sprintf(_('TimeRangeUnderflow'),
|
||||
format_date(string_to_date(element.getAttribute('min'), type), type));
|
||||
break;
|
||||
// case 'number':
|
||||
default:
|
||||
msg = sprintf(_('NumberRangeUnderflow'),
|
||||
string_to_number(element.getAttribute('min'), type));
|
||||
break;
|
||||
}
|
||||
|
||||
set_msg(element, 'rangeUnderflow', msg);
|
||||
element.classList.add(outOfRangeClass);
|
||||
element.classList.remove(inRangeClass);
|
||||
});
|
||||
|
||||
|
||||
const stepMismatch = check(test_step, element => {
|
||||
const list = get_next_valid(element);
|
||||
const min = list[0];
|
||||
const max = list[1];
|
||||
let sole = false;
|
||||
let msg;
|
||||
|
||||
if (min === null) {
|
||||
sole = max;
|
||||
} else if (max === null) {
|
||||
sole = min;
|
||||
}
|
||||
|
||||
if (sole !== false) {
|
||||
msg = sprintf(_('StepMismatchOneValue'), sole);
|
||||
} else {
|
||||
msg = sprintf(_('StepMismatch'), min, max);
|
||||
}
|
||||
set_msg(element, 'stepMismatch', msg);
|
||||
});
|
||||
|
||||
|
||||
const tooLong = check(test_maxlength, element => {
|
||||
set_msg(element, 'tooLong',
|
||||
sprintf(_('TextTooLong'), element.getAttribute('maxlength'),
|
||||
unicode_string_length(element.value)));
|
||||
});
|
||||
|
||||
|
||||
const tooShort = check(test_minlength, element => {
|
||||
set_msg(element, 'tooShort',
|
||||
sprintf(_('Please lengthen this text to %l characters or more (you are currently using %l characters).'),
|
||||
element.getAttribute('minlength'),
|
||||
unicode_string_length(element.value)));
|
||||
});
|
||||
|
||||
|
||||
const typeMismatch = check(test_type, element => {
|
||||
let msg = _('Please use the appropriate format.');
|
||||
const type = get_type(element);
|
||||
|
||||
if (type === 'email') {
|
||||
if (element.hasAttribute('multiple')) {
|
||||
msg = _('Please enter a comma separated list of email addresses.');
|
||||
} else {
|
||||
msg = _('InvalidEmail');
|
||||
}
|
||||
} else if (type === 'url') {
|
||||
msg = _('InvalidURL');
|
||||
} else if (type === 'file') {
|
||||
msg = _('Please select a file of the correct type.');
|
||||
}
|
||||
|
||||
set_msg(element, 'typeMismatch', msg);
|
||||
});
|
||||
|
||||
|
||||
const valueMissing = check(test_required, element => {
|
||||
let msg = _('ValueMissing');
|
||||
const type = get_type(element);
|
||||
|
||||
if (type === 'checkbox') {
|
||||
msg = _('CheckboxMissing');
|
||||
} else if (type === 'radio') {
|
||||
msg = _('RadioMissing');
|
||||
} else if (type === 'file') {
|
||||
if (element.hasAttribute('multiple')) {
|
||||
msg = _('Please select one or more files.');
|
||||
} else {
|
||||
msg = _('FileMissing');
|
||||
}
|
||||
} else if (element instanceof window.HTMLSelectElement) {
|
||||
msg = _('SelectMissing');
|
||||
}
|
||||
|
||||
set_msg(element, 'valueMissing', msg);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* the "valid" property calls all other validity checkers and returns true,
|
||||
* if all those return false.
|
||||
*
|
||||
* This is the major access point for _all_ other API methods, namely
|
||||
* (check|report)Validity().
|
||||
*/
|
||||
const valid = element => {
|
||||
const wrapper = get_wrapper(element);
|
||||
const validClass = wrapper && wrapper.settings.classes.valid || 'hf-valid';
|
||||
const invalidClass = wrapper && wrapper.settings.classes.invalid || 'hf-invalid';
|
||||
const userInvalidClass = wrapper && wrapper.settings.classes.userInvalid || 'hf-user-invalid';
|
||||
const userValidClass = wrapper && wrapper.settings.classes.userValid || 'hf-user-valid';
|
||||
const inRangeClass = wrapper && wrapper.settings.classes.inRange || 'hf-in-range';
|
||||
const outOfRangeClass = wrapper && wrapper.settings.classes.outOfRange || 'hf-out-of-range';
|
||||
const validatedClass = wrapper && wrapper.settings.classes.validated || 'hf-validated';
|
||||
|
||||
element.classList.add(validatedClass);
|
||||
|
||||
for (let checker of [badInput, customError, patternMismatch, rangeOverflow,
|
||||
rangeUnderflow, stepMismatch, tooLong, tooShort,
|
||||
typeMismatch, valueMissing]) {
|
||||
if (checker(element)) {
|
||||
element.classList.add(invalidClass);
|
||||
element.classList.remove(validClass);
|
||||
element.classList.remove(userValidClass);
|
||||
if ((
|
||||
(element.type === 'checkbox' || element.type === 'radio') &&
|
||||
element.checked !== element.defaultChecked
|
||||
) ||
|
||||
/* the following test is trivially false for checkboxes/radios */
|
||||
element.value !== element.defaultValue) {
|
||||
element.classList.add(userInvalidClass);
|
||||
} else {
|
||||
element.classList.remove(userInvalidClass);
|
||||
}
|
||||
element.setAttribute('aria-invalid', 'true');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
message_store.delete(element);
|
||||
element.classList.remove(invalidClass);
|
||||
element.classList.remove(userInvalidClass);
|
||||
element.classList.remove(outOfRangeClass);
|
||||
element.classList.add(validClass);
|
||||
element.classList.add(inRangeClass);
|
||||
if (element.value !== element.defaultValue) {
|
||||
element.classList.add(userValidClass);
|
||||
} else {
|
||||
element.classList.remove(userValidClass);
|
||||
}
|
||||
element.setAttribute('aria-invalid', 'false');
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
export default {
|
||||
badInput,
|
||||
customError,
|
||||
patternMismatch,
|
||||
rangeOverflow,
|
||||
rangeUnderflow,
|
||||
stepMismatch,
|
||||
tooLong,
|
||||
tooShort,
|
||||
typeMismatch,
|
||||
valueMissing,
|
||||
valid,
|
||||
};
|
||||
63
public/assets/hyperform-0.12.0/src/validators/bad_input.js
Normal file
63
public/assets/hyperform-0.12.0/src/validators/bad_input.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_type from '../tools/get_type';
|
||||
import string_to_date from '../tools/string_to_date';
|
||||
import { input_checked } from '../components/types';
|
||||
|
||||
|
||||
/**
|
||||
* test whether the element suffers from bad input
|
||||
*/
|
||||
export default function(element) {
|
||||
const type = get_type(element);
|
||||
|
||||
if (input_checked.indexOf(type) === -1) {
|
||||
/* we're not interested, thanks! */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* the browser hides some bad input from the DOM, e.g. malformed numbers,
|
||||
* email addresses with invalid punycode representation, ... We try to resort
|
||||
* to the original method here. The assumption is, that a browser hiding
|
||||
* bad input will hopefully also always support a proper
|
||||
* ValidityState.badInput */
|
||||
if (! element.value) {
|
||||
if ('_original_validity' in element &&
|
||||
! element._original_validity.__hyperform) {
|
||||
return ! element._original_validity.badInput;
|
||||
}
|
||||
/* no value and no original badInput: Assume all's right. */
|
||||
return true;
|
||||
}
|
||||
|
||||
var result = true;
|
||||
switch (type) {
|
||||
case 'color':
|
||||
result = /^#[a-f0-9]{6}$/.test(element.value);
|
||||
break;
|
||||
case 'number':
|
||||
case 'range':
|
||||
result = ! isNaN(Number(element.value));
|
||||
break;
|
||||
case 'datetime':
|
||||
case 'date':
|
||||
case 'month':
|
||||
case 'week':
|
||||
case 'time':
|
||||
result = string_to_date(element.value, type) !== null;
|
||||
break;
|
||||
case 'datetime-local':
|
||||
result = /^([0-9]{4,})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9])(?:\.([0-9]{1,3}))?)?$/.test(element.value);
|
||||
break;
|
||||
case 'tel':
|
||||
/* spec says No! Phone numbers can have all kinds of formats, so this
|
||||
* is expected to be a free-text field. */
|
||||
// TODO we could allow a setting 'phone_regex' to be evaluated here.
|
||||
break;
|
||||
case 'email':
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
36
public/assets/hyperform-0.12.0/src/validators/max.js
Normal file
36
public/assets/hyperform-0.12.0/src/validators/max.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_type from '../tools/get_type';
|
||||
import { dates } from '../components/types';
|
||||
import string_to_date from '../tools/string_to_date';
|
||||
|
||||
|
||||
/**
|
||||
* test the max attribute
|
||||
*
|
||||
* we use Number() instead of parseFloat(), because an invalid attribute
|
||||
* value like "123abc" should result in an error.
|
||||
*/
|
||||
export default function(element) {
|
||||
const type = get_type(element);
|
||||
|
||||
if (! element.value || ! element.hasAttribute('max')) {
|
||||
/* we're not responsible here */
|
||||
return true;
|
||||
}
|
||||
|
||||
let value, max;
|
||||
if (dates.indexOf(type) > -1) {
|
||||
value = string_to_date(element.value, type);
|
||||
value = value === null? NaN : +value;
|
||||
max = string_to_date(element.getAttribute('max'), type);
|
||||
max = max === null? NaN : +max;
|
||||
} else {
|
||||
value = Number(element.value);
|
||||
max = Number(element.getAttribute('max'));
|
||||
}
|
||||
|
||||
/* we cannot validate invalid values and trust on badInput, if isNaN(value) */
|
||||
return (isNaN(max) || isNaN(value) || value <= max);
|
||||
}
|
||||
35
public/assets/hyperform-0.12.0/src/validators/maxlength.js
Normal file
35
public/assets/hyperform-0.12.0/src/validators/maxlength.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_type from '../tools/get_type';
|
||||
import unicode_string_length from '../tools/unicode_string_length';
|
||||
import { text as text_types } from '../components/types';
|
||||
|
||||
|
||||
/**
|
||||
* test the maxlength attribute
|
||||
*/
|
||||
export default function(element) {
|
||||
if (
|
||||
! element.value
|
||||
||
|
||||
text_types.indexOf(get_type(element)) === -1
|
||||
||
|
||||
! element.hasAttribute('maxlength')
|
||||
||
|
||||
! element.getAttribute('maxlength') // catch maxlength=""
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const maxlength = parseInt(element.getAttribute('maxlength'), 10);
|
||||
|
||||
/* check, if the maxlength value is usable at all.
|
||||
* We allow maxlength === 0 to basically disable input (Firefox does, too).
|
||||
*/
|
||||
if (isNaN(maxlength) || maxlength < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return unicode_string_length(element.value) <= maxlength;
|
||||
}
|
||||
36
public/assets/hyperform-0.12.0/src/validators/min.js
Normal file
36
public/assets/hyperform-0.12.0/src/validators/min.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_type from '../tools/get_type';
|
||||
import { dates } from '../components/types';
|
||||
import string_to_date from '../tools/string_to_date';
|
||||
|
||||
|
||||
/**
|
||||
* test the min attribute
|
||||
*
|
||||
* we use Number() instead of parseFloat(), because an invalid attribute
|
||||
* value like "123abc" should result in an error.
|
||||
*/
|
||||
export default function(element) {
|
||||
const type = get_type(element);
|
||||
|
||||
if (! element.value || ! element.hasAttribute('min')) {
|
||||
/* we're not responsible here */
|
||||
return true;
|
||||
}
|
||||
|
||||
let value, min;
|
||||
if (dates.indexOf(type) > -1) {
|
||||
value = string_to_date(element.value, type);
|
||||
value = value === null? NaN : +value;
|
||||
min = string_to_date(element.getAttribute('min'), type);
|
||||
min = min === null? NaN : +min;
|
||||
} else {
|
||||
value = Number(element.value);
|
||||
min = Number(element.getAttribute('min'));
|
||||
}
|
||||
|
||||
/* we cannot validate invalid values and trust on badInput, if isNaN(value) */
|
||||
return (isNaN(min) || isNaN(value) || value >= min);
|
||||
}
|
||||
33
public/assets/hyperform-0.12.0/src/validators/minlength.js
Normal file
33
public/assets/hyperform-0.12.0/src/validators/minlength.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_type from '../tools/get_type';
|
||||
import unicode_string_length from '../tools/unicode_string_length';
|
||||
import { text as text_types } from '../components/types';
|
||||
|
||||
|
||||
/**
|
||||
* test the minlength attribute
|
||||
*/
|
||||
export default function(element) {
|
||||
if (
|
||||
! element.value
|
||||
||
|
||||
text_types.indexOf(get_type(element)) === -1
|
||||
||
|
||||
! element.hasAttribute('minlength')
|
||||
||
|
||||
! element.getAttribute('minlength') // catch minlength=""
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const minlength = parseInt(element.getAttribute('minlength'), 10);
|
||||
|
||||
/* check, if the minlength value is usable at all. */
|
||||
if (isNaN(minlength) || minlength < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return unicode_string_length(element.value) >= minlength;
|
||||
}
|
||||
15
public/assets/hyperform-0.12.0/src/validators/pattern.js
Normal file
15
public/assets/hyperform-0.12.0/src/validators/pattern.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* test the pattern attribute
|
||||
*/
|
||||
export default function(element) {
|
||||
return (
|
||||
! element.value
|
||||
||
|
||||
! element.hasAttribute('pattern')
|
||||
||
|
||||
(new RegExp('^(?:'+ element.getAttribute('pattern') +')$')).test(element.value)
|
||||
);
|
||||
}
|
||||
71
public/assets/hyperform-0.12.0/src/validators/required.js
Normal file
71
public/assets/hyperform-0.12.0/src/validators/required.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { get_radiogroup } from '../tools/get_radiogroup';
|
||||
|
||||
|
||||
function has_submittable_option(select) {
|
||||
/* Definition of the placeholder label option:
|
||||
* https://www.w3.org/TR/html5/sec-forms.html#element-attrdef-select-required
|
||||
* Being required (the first constraint in the spec) is trivially true, since
|
||||
* this function is only called for such selects.
|
||||
*/
|
||||
const has_placeholder_option = (
|
||||
! select.multiple &&
|
||||
select.size <= 1 &&
|
||||
select.options.length > 0 &&
|
||||
select.options[0].parentNode == select &&
|
||||
select.options[0].value === ''
|
||||
);
|
||||
return (
|
||||
/* anything selected at all? That's redundant with the .some() call below,
|
||||
* but more performant in the most probable error case. */
|
||||
select.selectedIndex > -1 &&
|
||||
Array.prototype.some.call(
|
||||
select.options,
|
||||
option => {
|
||||
return (
|
||||
/* it isn't the placeholder option */
|
||||
(! has_placeholder_option || option.index !== 0) &&
|
||||
/* it isn't disabled */
|
||||
! option.disabled &&
|
||||
/* and it is, in fact, selected */
|
||||
option.selected);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* test the required attribute
|
||||
*/
|
||||
export default function(element) {
|
||||
if (element.type === 'radio') {
|
||||
/* the happy (and quick) path for radios: */
|
||||
if (element.hasAttribute('required') && element.checked) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const radiogroup = get_radiogroup(element);
|
||||
|
||||
/* if any radio in the group is required, we need any (not necessarily the
|
||||
* same) radio to be checked */
|
||||
if (radiogroup.some(radio => radio.hasAttribute('required'))) {
|
||||
return radiogroup.some(radio => radio.checked);
|
||||
}
|
||||
/* not required, validation passes */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! element.hasAttribute('required')) {
|
||||
/* nothing to do */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (element instanceof window.HTMLSelectElement) {
|
||||
return has_submittable_option(element);
|
||||
}
|
||||
|
||||
return (element.type === 'checkbox')? element.checked : (!! element.value);
|
||||
}
|
||||
66
public/assets/hyperform-0.12.0/src/validators/step.js
Normal file
66
public/assets/hyperform-0.12.0/src/validators/step.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import get_type from '../tools/get_type';
|
||||
import { numbers } from '../components/types';
|
||||
import { default_step, step_scale_factor, default_step_base } from '../components/step_defaults';
|
||||
import string_to_number from '../tools/string_to_number';
|
||||
|
||||
|
||||
/**
|
||||
* test the step attribute
|
||||
*/
|
||||
export default function(element) {
|
||||
const type = get_type(element);
|
||||
|
||||
if (! element.value ||
|
||||
numbers.indexOf(type) === -1 ||
|
||||
(element.getAttribute('step') || '').toLowerCase() === 'any') {
|
||||
/* we're not responsible here. Note: If no step attribute is given, we
|
||||
* need to validate against the default step as per spec. */
|
||||
return true;
|
||||
}
|
||||
|
||||
let step = element.getAttribute('step');
|
||||
if (step) {
|
||||
step = string_to_number(step, type);
|
||||
} else {
|
||||
step = default_step[type] || 1;
|
||||
}
|
||||
|
||||
if (step <= 0 || isNaN(step)) {
|
||||
/* error in specified "step". We cannot validate against it, so the value
|
||||
* is true. */
|
||||
return true;
|
||||
}
|
||||
|
||||
const scale = step_scale_factor[type] || 1;
|
||||
|
||||
let value = string_to_number(element.value, type);
|
||||
let min = string_to_number(element.getAttribute('min') ||
|
||||
element.getAttribute('value') || '', type);
|
||||
|
||||
if (isNaN(value)) {
|
||||
/* we cannot compare an invalid value and trust that the badInput validator
|
||||
* takes over from here */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isNaN(min)) {
|
||||
min = default_step_base[type] || 0;
|
||||
}
|
||||
|
||||
if (type === 'month') {
|
||||
/* type=month has month-wide steps. See
|
||||
* https://html.spec.whatwg.org/multipage/forms.html#month-state-%28type=month%29
|
||||
*/
|
||||
min = (new Date(min)).getUTCFullYear() * 12 + (new Date(min)).getUTCMonth();
|
||||
value = (new Date(value)).getUTCFullYear() * 12 + (new Date(value)).getUTCMonth();
|
||||
}
|
||||
|
||||
const result = Math.abs(min - value) % (step * scale);
|
||||
|
||||
return (result < 0.00000001 ||
|
||||
/* crappy floating-point arithmetics! */
|
||||
result > (step * scale) - 0.00000001);
|
||||
}
|
||||
105
public/assets/hyperform-0.12.0/src/validators/type.js
Normal file
105
public/assets/hyperform-0.12.0/src/validators/type.js
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import comma_split from '../tools/comma_split';
|
||||
import get_type from '../tools/get_type';
|
||||
import trim from '../tools/trim';
|
||||
import { type_checked } from '../components/types';
|
||||
|
||||
|
||||
/* we use a dummy <a> where we set the href to test URL validity
|
||||
* The definition is out of the "global" scope so that JSDOM can be instantiated
|
||||
* after loading Hyperform for tests.
|
||||
*/
|
||||
var url_canary;
|
||||
|
||||
/* see https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address */
|
||||
const email_pattern = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
|
||||
/**
|
||||
* test the type-inherent syntax
|
||||
*/
|
||||
export default function(element) {
|
||||
const type = get_type(element);
|
||||
|
||||
if ((type !== 'file' && ! element.value) ||
|
||||
(type !== 'file' && type_checked.indexOf(type) === -1)) {
|
||||
/* we're not responsible for this element */
|
||||
return true;
|
||||
}
|
||||
|
||||
var is_valid = true;
|
||||
|
||||
switch (type) {
|
||||
case 'url': {
|
||||
if (! url_canary) {
|
||||
url_canary = document.createElement('a');
|
||||
}
|
||||
const value = trim(element.value);
|
||||
url_canary.href = value;
|
||||
is_valid = (url_canary.href === value ||
|
||||
url_canary.href === value+'/');
|
||||
break;
|
||||
}
|
||||
case 'email':
|
||||
if (element.hasAttribute('multiple')) {
|
||||
is_valid = comma_split(element.value)
|
||||
.every(value => email_pattern.test(value));
|
||||
} else {
|
||||
is_valid = email_pattern.test(trim(element.value));
|
||||
}
|
||||
break;
|
||||
case 'file':
|
||||
if ('files' in element && element.files.length &&
|
||||
element.hasAttribute('accept')) {
|
||||
const patterns = comma_split(element.getAttribute('accept'))
|
||||
.map(pattern => {
|
||||
if (/^(audio|video|image)\/\*$/.test(pattern)) {
|
||||
pattern = new RegExp('^'+RegExp.$1+'/.+$');
|
||||
}
|
||||
return pattern;
|
||||
});
|
||||
|
||||
if (! patterns.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
fileloop:
|
||||
for (let i = 0; i < element.files.length; i++) {
|
||||
/* we need to match a whitelist, so pre-set with false */
|
||||
let file_valid = false;
|
||||
|
||||
patternloop:
|
||||
for (let j = 0; j < patterns.length; j++) {
|
||||
const file = element.files[i];
|
||||
const pattern = patterns[j];
|
||||
|
||||
let fileprop = file.type;
|
||||
|
||||
if (typeof pattern === 'string' && pattern.substr(0, 1) === '.') {
|
||||
if (file.name.search('.') === -1) {
|
||||
/* no match with any file ending */
|
||||
continue patternloop;
|
||||
}
|
||||
|
||||
fileprop = file.name.substr(file.name.lastIndexOf('.'));
|
||||
}
|
||||
|
||||
if (fileprop.search(pattern) === 0) {
|
||||
/* we found one match and can quit looking */
|
||||
file_valid = true;
|
||||
break patternloop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! file_valid) {
|
||||
is_valid = false;
|
||||
break fileloop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return is_valid;
|
||||
}
|
||||
2
public/assets/hyperform-0.12.0/src/version.js
Normal file
2
public/assets/hyperform-0.12.0/src/version.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
'use strict';
|
||||
export default '0.12.0';
|
||||
13
public/assets/hyperform-0.12.0/test/functional/blank.html
Normal file
13
public/assets/hyperform-0.12.0/test/functional/blank.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Mocha</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="weakmap.min.js"></script>
|
||||
<script src="classList.min.js"></script>
|
||||
<script src="../../dist/hyperform.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
2
public/assets/hyperform-0.12.0/test/functional/classList.min.js
vendored
Normal file
2
public/assets/hyperform-0.12.0/test/functional/classList.min.js
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
|
||||
if("document" in self){if(!("classList" in document.createElement("_"))){(function(j){"use strict";if(!("Element" in j)){return}var a="classList",f="prototype",m=j.Element[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p<o;p++){if(p in this&&this[p]===q){return p}}return -1},n=function(o,p){this.name=o;this.code=DOMException[o];this.message=p},g=function(p,o){if(o===""){throw new n("SYNTAX_ERR","An invalid or illegal string was specified")}if(/\s/.test(o)){throw new n("INVALID_CHARACTER_ERR","String contains an invalid character")}return c.call(p,o)},d=function(s){var r=k.call(s.getAttribute("class")||""),q=r?r.split(/\s+/):[],p=0,o=q.length;for(;p<o;p++){this.push(q[p])}this._updateClassName=function(){s.setAttribute("class",this.toString())}},e=d[f]=[],i=function(){return new d(this)};n[f]=Error[f];e.item=function(o){return this[o]||null};e.contains=function(o){o+="";return g(this,o)!==-1};e.add=function(){var s=arguments,r=0,p=s.length,q,o=false;do{q=s[r]+"";if(g(this,q)===-1){this.push(q);o=true}}while(++r<p);if(o){this._updateClassName()}};e.remove=function(){var t=arguments,s=0,p=t.length,r,o=false,q;do{r=t[s]+"";q=g(this,r);while(q!==-1){this.splice(q,1);o=true;q=g(this,r)}}while(++s<p);if(o){this._updateClassName()}};e.toggle=function(p,q){p+="";var o=this.contains(p),r=o?q!==true&&"remove":q!==false&&"add";if(r){this[r](p)}if(q===true||q===false){return q}else{return !o}};e.toString=function(){return this.join(" ")};if(b.defineProperty){var l={get:i,enumerable:true,configurable:true};try{b.defineProperty(m,a,l)}catch(h){if(h.number===-2146823252){l.enumerable=false;b.defineProperty(m,a,l)}}}else{if(b[f].__defineGetter__){m.__defineGetter__(a,i)}}}(self))}else{(function(){var b=document.createElement("_");b.classList.add("c1","c2");if(!b.classList.contains("c2")){var c=function(e){var d=DOMTokenList.prototype[e];DOMTokenList.prototype[e]=function(h){var g,f=arguments.length;for(g=0;g<f;g++){h=arguments[g];d.call(this,h)}}};c("add");c("remove")}b.classList.toggle("c3",false);if(b.classList.contains("c3")){var a=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(d,e){if(1 in arguments&&!this.contains(d)===!e){return e}else{return a.call(this,d)}}}b=null}())}};
|
||||
20
public/assets/hyperform-0.12.0/test/functional/index.html
Normal file
20
public/assets/hyperform-0.12.0/test/functional/index.html
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Mocha</title>
|
||||
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
<script src="weakmap.min.js"></script>
|
||||
<script src="classList.min.js"></script>
|
||||
<script src="../../node_modules/mocha/mocha.js"></script>
|
||||
<script src="../../dist/hyperform.js"></script>
|
||||
<script>mocha.setup('bdd')</script>
|
||||
<script src="test.regressions.js"></script>
|
||||
<script src="test.functional.js"></script>
|
||||
<script>mocha.run()</script>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue