raiutils 8.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +103 -0
- package/package.json +18 -0
- package/utils.js +868 -0
- package/utils.min.js +2 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Ray/Pecacheu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Utils.js
|
|
2
|
+
###### If you, like many developers, prefer native JavaScript to jQuery, but can't live without those one or two essential features, then you need Utils.js!
|
|
3
|
+
|
|
4
|
+
Note: For production code, the minified version *utils.min.js* should be used. There's also *utilsES5.js*. It's been converted to the older ES5 standard, so should be more compatible with older browsers. Expect IE. Literally no one cares about IE :(
|
|
5
|
+
|
|
6
|
+
*More detailed usage & help available in utils.js!*
|
|
7
|
+
|
|
8
|
+
#### The most commonly functions in this library are:
|
|
9
|
+
|
|
10
|
+
*utils.mkEl(t,p,c,s,i) utils.mkDiv(p,c,s,i)* To use these quickly and efficiently, just remember PCSI: Parent, class, style, innerHTML!
|
|
11
|
+
|
|
12
|
+
*utils.center(obj[,only[,type]])* Does what it says on the tin! Input an Element, choose whether you want X, Y, or by default, both, and change the centering type.
|
|
13
|
+
|
|
14
|
+
*UtilRect* Getting the bounds/position of an element used to be a complete mess with incompatibilities across every browser. Not anymore! Use UtilRects to keep track of your object positions! Simply access the *boundingRect* property of any Element and you can access it's *top*, *bottom*, *left*, *right*, *width*, and *height* on the client's screen! If you need this relative to the top of the page, it's as simple as `Element.boundingRect.top + window.scrollY`
|
|
15
|
+
|
|
16
|
+
*[Function].wrap* This can be quite useful in combination with functions like *setTimeout*, for example `setTimeout(console.log.wrap("Hello!"), 50)`. Also sets the *this* object inside the function to *arguments*, in case you want to access the calling arguments as well!
|
|
17
|
+
|
|
18
|
+
### Custom Classes
|
|
19
|
+
- `UtilRect` Better class for working with element bounds.
|
|
20
|
+
- `Easing` Easing functions for use with *utils.map*.
|
|
21
|
+
|
|
22
|
+
### Prototype Extensions
|
|
23
|
+
- `[Function].wrap(...) returns Function` Wrap a function so that it always has a preset argument list when called.
|
|
24
|
+
- `String.trim() polyfill`
|
|
25
|
+
- `[String].startsWith(s) returns Boolean` Like Java.
|
|
26
|
+
- `[String].endsWith(s) returns Boolean` Like Java.
|
|
27
|
+
- `[Array].clean(kz)` Remove 'empty' elements like 0, false, ' ', undefined, and NaN from an array. Set 'kz' to true to keep '0's
|
|
28
|
+
- `[Array].remove(item) returns Boolean` Remove first instance of item from array. Returns false if not found.
|
|
29
|
+
- `[Array].each(fn[,start[,end]]) returns Any` Calls fn on each index of array, with optional start and end index.
|
|
30
|
+
- `[Array].eachAsync(fn[,start[,end[,pe=true]]]) returns Any` Like `each` but async.
|
|
31
|
+
- `Number [Element].index` Represents an element's index in it's parent. Set to -1 if the element has no parent.
|
|
32
|
+
- `[Element].insertChildAt(el,i)` Inserts child at index. Appends child to end if index exceeds child count.
|
|
33
|
+
- `UtilRect [Element].boundingRect` Element's bounding rect as a `UtilRect` object.
|
|
34
|
+
- `UtilRect [Element].innerRect` Element's inner rect (excluding margin, padding, and border)
|
|
35
|
+
- `Math.cot(x) returns Number` No idea why this isn't built-in, but it's not.
|
|
36
|
+
- `[TouchList].get(id) returns Touch` Gets touch by id, returns null if none found.
|
|
37
|
+
- `[Uint8Array].toBase64([opts]) returns String` Polyfill; See MDN docs.
|
|
38
|
+
- `Uint8Array.fromBase64(str[, opts]) returns Uint8Array` Polyfill; See MDN docs.
|
|
39
|
+
- `RegExp.escape(string) return String` Polyfill; See MDN docs.
|
|
40
|
+
|
|
41
|
+
### Main *utils* Class
|
|
42
|
+
- `String utils.VER` Current library version.
|
|
43
|
+
- `Number utils.w` and `Number utils.h` Cross-platform window width and height.
|
|
44
|
+
- `Boolean utils.mobile` Will be **true** if running on a mobile device, based on the UserAgent.
|
|
45
|
+
- `utils.setCookie(name,value,exp,secure)` Set a cookie.
|
|
46
|
+
- `utils.getCookie(name) returns String or null` Get a cookie by name.
|
|
47
|
+
- `utils.remCookie(name)` Remove a cookie by name.
|
|
48
|
+
- `utils.setPropSafe(obj, path, val, onlyNull=false)` Set a nested property, even if higher levels don't exist. Useful for defining settings in a complex config object.
|
|
49
|
+
- `utils.getPropSafe(obj, path) returns Object` Gets a nested property, returns undefined if any level doesn't exist.
|
|
50
|
+
- `utils.copy(o[,sub]) returns Object` Deep (recursive) Object.create. Copies down to given sub levels, all levels if undefined.
|
|
51
|
+
- `utils.skinnedInput(el)` Fallback for when css *'appearance:none'* doesn't work. Generates container for input field for css skinning on unsupported browsers.
|
|
52
|
+
- `utils.numField(field[,min[,max[,decMax[,sym]]]])` Turns your boring input field into a mobile-friendly integer, decimal, or financial entry field with max/min & negative support!
|
|
53
|
+
- `utils.formatCost(n[,sym]) returns String` Format Number as currency. Uses '$' by default.
|
|
54
|
+
- `utils.fromDateTimeBox(el) returns Date` Convert value from 'datetime-local' input to Date object.
|
|
55
|
+
- `utils.toDateTimeBox(d[,sec]) returns String` Convert Date object into format to set 'datetime-local' optionally including seconds if 'sec' is **true**.
|
|
56
|
+
- `utils.formatDate(d[,opts]) returns String` Format Date object into a pretty string, with various options.
|
|
57
|
+
- `utils.months` Array of months from Jan to Dec.
|
|
58
|
+
- `utils.suffix(n) returns String` Add appropriate suffix to number. (ex. 31st, 12th, 22nd)
|
|
59
|
+
- `utils.fixedNum(n,len[,radix=10]) returns String` Fix number to a given minimum length with padded 0's. Adds '0b' for binary *(radix=2)* and '0x' for hex *(radix=16)*
|
|
60
|
+
- `utils.goBack()` For AJAX navigation. Presses back button.
|
|
61
|
+
- `utils.goForward()` For AJAX navigation. Presses forward button.
|
|
62
|
+
- `utils.go(url, data)` Push new history state for AJAX navigation. You can provide additional data to access later.
|
|
63
|
+
- `utils.onNav = function(data)` Callback called when the browser wants to navigate to a page. Provides the optional data argument supplied to `utils.go`. Also called when the page is first loaded, directly after `window.onload`.
|
|
64
|
+
- `utils.mkEl(tag,parent,class,styles,innerHTML) returns Element` Quickly create element with parent, classes, style properties (as key/value pairs), and innerHTML content. All parameters (except for tag) are optional.
|
|
65
|
+
- `utils.mkDiv` Same as utils.mkEl, but assumes 'div' for tag.
|
|
66
|
+
- `utils.addText(el,text)` Appends a TextNode with given text to element.
|
|
67
|
+
- `utils.textWidth(text,font) returns Number` Get predicted width of text given css font style.
|
|
68
|
+
- `utils.define(obj,name,get,set)` Add getter/setter pair to an existing object. Get or set may be null to disable.
|
|
69
|
+
- `utils.proto(obj,name,val[,static])` Define immutable, non-enumerable property or method in an object prototype (or object if 'static' is **true**).
|
|
70
|
+
- `utils.isBlank(o) returns Boolean` Check if string, array, or other object is empty.
|
|
71
|
+
- `utils.firstEmpty(arr) returns Number` Finds first empty (undefined/null) slot in array.
|
|
72
|
+
- `utils.firstEmptyChar(obj) returns String` Like *firstEmpty*, but uses letters from `utils.numToChar` instead.
|
|
73
|
+
- `utils.numToChar(n) returns String` Converts a number into letters (upper and lower) from a to Z.
|
|
74
|
+
- `utils.merge(target,source[,source2...]) returns target` Merges two (or more) objects, giving the last precedence. If both objects contain a property at the same index, and both are Arrays/Objects, they are merged.
|
|
75
|
+
- `utils.bounds(n,min=0,max=1) returns Number` Keeps value within max/min bounds. Also handles NaN or null.
|
|
76
|
+
- `utils.norm` *OR* `utils.normalize(n,min=0,max=1) returns Number` 'Normalizes' a value so that it ranges from min to max, but unlike `utils.bounds`, this function retains input's offset. This can be used to normalize angles.
|
|
77
|
+
- `utils.cutStr(s,rem) returns String` Finds and removes all instances of 'rem' contained within String 's'
|
|
78
|
+
- `utils.dCut(data,startString,endString[,index[,searchStart]]) returns String` Cuts text out of 'data' from first instance of 'startString' to next instance of 'endString'.
|
|
79
|
+
- `utils.dCutToLast(data,startString,endString[,index[,searchStart]]) returns String` Same as *utils.dCut* but using lastIndexOf for end search.
|
|
80
|
+
- `utils.dCutLast(data,startString,endString[,index[,searchStart]]) returns String` Same as *utils.dCut* but using lastIndexOf for start search.
|
|
81
|
+
- `utils.parseCSS(prop) returns Object` Given css property value 'prop', returns object with space-separated values from the property string.
|
|
82
|
+
- `utils.buildCSS(obj) returns String` Rebuilds css string from a *parseCSS* object.
|
|
83
|
+
- `utils.addClass(class,propList)` Create a css class and append it to the current document. Fill 'propList' object with key/value pairs repersenting the properties you want to add to the class.
|
|
84
|
+
- `utils.addId(id,propList)` Create a css selector and append it to the current document.
|
|
85
|
+
- `utils.addKeyframe(name,content)` Create a css keyframe and append it to the current document.
|
|
86
|
+
- `utils.removeSelector(name)` Remove a specific css selector (including the '.' or '#') from all stylesheets in the current document.
|
|
87
|
+
- `utils.getId(id[,parent]) returns Element` Shorthand way to get element by id. Parent defaults to document.
|
|
88
|
+
- `utils.getClassList(class[,parent]) returns Array` Shorthand way to get list of elements with class name. Parent defaults to document.
|
|
89
|
+
- `utils.getClassFirst(class[,parent]) returns Element` Get first element with class, otherwise return null. Parent defaults to document.
|
|
90
|
+
- `utils.getClassOnly(class[,parent]) returns Element` Get element with class if there's only one, otherwise return null. Parent defaults to document.
|
|
91
|
+
- `utils.hexToRgb(hex) returns Number` Converts HEX color to 24-bit RGB.
|
|
92
|
+
- `utils.rand(min,max[,res[,ease]]) returns Number` Generates random from min to max, optionally with a decimal resolution (10, 100, 1000, etc.) or custom ease, changing the probability of various numbers being generated.
|
|
93
|
+
- `utils.fromQuery(str) returns Object` Parses a url query string into an Object.
|
|
94
|
+
- `utils.toQuery(obj) returns String` Converts an object into a url query string.
|
|
95
|
+
- `utils.center(obj[,only[,type]])` Center objects with JavaScript, using a variety of methods. *See utils.js for details.*
|
|
96
|
+
- `utils.loadAjax(path,[callback[, meth[, body[, hdList]]]])` Loads a file and returns it's contents, using GET by default. *See utils.js for details.*
|
|
97
|
+
- `utils.loadJSONP(path,callback,timeout)` Loads a file at the address from a JSONP-enabled server. Callback is fired with either received data, or **false** if unsuccessful.
|
|
98
|
+
- `utils.loadFile(path,callback,timeout)` Good fallback for `utils.loadAjax`. Loads a file at the address via HTML object tag. Callback is fired with either received data, or **false** if unsuccessful.
|
|
99
|
+
- `utils.dlFile(filename,uri) returns Promise` Downloads a file from a link.
|
|
100
|
+
- `utils.dlData(filename,data)` Downloads a file generated from a Blob or ArrayBuffer.
|
|
101
|
+
- `utils.rad(deg)` / `utils.deg(rad)` Convert between radians and degrees.
|
|
102
|
+
- `utils.map(input,minIn,maxIn,minOut,maxOut,ease) returns Number` For unit translation and JS animation! See ease functions in *untils.js*.
|
|
103
|
+
- `utils.delay(ms) returns Promise` setTimeout but async.
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "raiutils",
|
|
3
|
+
"version": "8.6.6",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "The ultimate JavaScript companion library. You'll never need jQuery again!",
|
|
6
|
+
"repository": "https://github.com/Pecacheu/Utils.js",
|
|
7
|
+
"homepage": "https://github.com/Pecacheu/Utils.js",
|
|
8
|
+
"author": "Pecacheu",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"js",
|
|
12
|
+
"jquery",
|
|
13
|
+
"utilities",
|
|
14
|
+
"utils",
|
|
15
|
+
"utils.js",
|
|
16
|
+
"DOM"
|
|
17
|
+
]
|
|
18
|
+
}
|
package/utils.js
ADDED
|
@@ -0,0 +1,868 @@
|
|
|
1
|
+
//https://github.com/Pecacheu/Utils.js; MIT License
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
const utils = {VER:'v8.6.6'};
|
|
5
|
+
|
|
6
|
+
//Node.js compat
|
|
7
|
+
let UtilRect, P=(typeof window=='undefined')?
|
|
8
|
+
[{}, {back:()=>{},forward:()=>{}}, class{}, class{}, class{}, class{}, ()=>{}]:
|
|
9
|
+
[window, history, DOMRect, HTMLCollection, Element, NodeList, addEventListener];
|
|
10
|
+
|
|
11
|
+
(() => { //Utils Library
|
|
12
|
+
|
|
13
|
+
const [window, history, DOMRect, HTMLCollection,
|
|
14
|
+
Element, NodeList, addEventListener] = P;
|
|
15
|
+
|
|
16
|
+
//Add getter/setter to object
|
|
17
|
+
utils.define = (obj, name, get, set) => {
|
|
18
|
+
const t={}; if(get) t.get=get; if(set) t.set=set;
|
|
19
|
+
if(Array.isArray(name)) for(let n of name) Object.defineProperty(obj,n,t);
|
|
20
|
+
else Object.defineProperty(obj,name,t);
|
|
21
|
+
}
|
|
22
|
+
//Define prop in object prototype
|
|
23
|
+
utils.proto = (obj, name, val, st) => {
|
|
24
|
+
const t={value:val}; if(!st) obj=obj.prototype;
|
|
25
|
+
if(Array.isArray(name)) for(let n of name) Object.defineProperty(obj,n,t);
|
|
26
|
+
else Object.defineProperty(obj,name,t);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//Cookie Parsing
|
|
30
|
+
utils.setCookie = (name,value,exp,secure) => {
|
|
31
|
+
let c=encodeURIComponent(name)+'='+(value==null?'':encodeURIComponent(value))+';path=/';
|
|
32
|
+
if(exp != null) {
|
|
33
|
+
if(!(exp instanceof Date)) exp = new Date(exp);
|
|
34
|
+
c+=';expires='+exp.toUTCString();
|
|
35
|
+
}
|
|
36
|
+
if(secure) c+=';secure'; document.cookie=c;
|
|
37
|
+
}
|
|
38
|
+
utils.remCookie = name => {
|
|
39
|
+
document.cookie = encodeURIComponent(name)+'=;expires='+new Date(0).toUTCString();
|
|
40
|
+
}
|
|
41
|
+
utils.getCookie = name => {
|
|
42
|
+
const n1 = encodeURIComponent(name), n2 = ' '+n1, cl = document.cookie.split(';');
|
|
43
|
+
for(let i=0,l=cl.length,c,eq,sub; i<l; ++i) {
|
|
44
|
+
c = cl[i]; eq = c.indexOf('='); sub = c.substr(0,eq);
|
|
45
|
+
if(sub == n1 || sub == n2) return decodeURIComponent(c.substr(eq+1));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//Wrap a function so that it always has a preset argument list when called
|
|
50
|
+
//In the called function, 'this' is set to the caller's arguments, granting access to both
|
|
51
|
+
utils.proto(Function, 'wrap', function(/*...*/) {
|
|
52
|
+
const f=this, a=arguments; return function() {f.apply(arguments,a)}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
//Deep (recursive) Object.create
|
|
56
|
+
//Copies down to given sub levels. All levels if undefined
|
|
57
|
+
utils.copy = (o, sub) => {
|
|
58
|
+
if(sub===0 || typeof o != 'object') return o;
|
|
59
|
+
sub=sub>0?sub-1:null; let o2;
|
|
60
|
+
if(Array.isArray(o)) o2=new Array(o.length),
|
|
61
|
+
o.forEach((v,i) => {o2[i]=utils.copy(v,sub)});
|
|
62
|
+
else {o2={};for(let k in o) o2[k]=utils.copy(o[k],sub)}
|
|
63
|
+
return o2;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//UserAgent-based Mobile device detection
|
|
67
|
+
utils.deviceInfo = ua => {
|
|
68
|
+
const d={}; if(!ua) ua=navigator.userAgent;
|
|
69
|
+
if(!ua.startsWith("Mozilla/5.0 ")) return d;
|
|
70
|
+
let o=ua.indexOf(')'), os=d.rawOS=ua.substring(13,o), o2,o3;
|
|
71
|
+
if(os.startsWith("Windows")) {
|
|
72
|
+
o2=os.split('; '), d.os = "Windows";
|
|
73
|
+
d.type = o2.indexOf('WOW64')!=-1?'x64 PC; x86 Browser':o2.indexOf('x64')!=-1?'x64 PC':'x86 PC';
|
|
74
|
+
o2=os.indexOf("Windows NT "), d.version = os.substring(o2+11,os.indexOf(';',o2+12));
|
|
75
|
+
} else if(os.startsWith("iP")) {
|
|
76
|
+
o2=os.indexOf("OS"), d.os = "iOS", d.type = os.substr(0,os.indexOf(';'));
|
|
77
|
+
d.version = os.substring(o2+3, os.indexOf(' ',o2+4)).replace(/_/g,'.');
|
|
78
|
+
} else if(os.startsWith("Macintosh;")) {
|
|
79
|
+
o2=os.indexOf(" Mac OS X"), d.os = "MacOS", d.type = os.substring(11,o2)+" Mac";
|
|
80
|
+
d.version = os.substr(o2+10).replace(/_/g,'.');
|
|
81
|
+
} else if((o2=os.indexOf("Android"))!=-1) {
|
|
82
|
+
d.os = "Android", d.version = os.substring(o2+8, os.indexOf(';',o2+9));
|
|
83
|
+
o2=os.lastIndexOf(';'), o3=os.indexOf(" Build",o2+2);
|
|
84
|
+
d.type = os.substring(o2+2, o3==-1?undefined:o3);
|
|
85
|
+
} else if(os.startsWith("X11;")) {
|
|
86
|
+
os=os.substr(5).split(/[;\s]+/), o2=os.length;
|
|
87
|
+
d.os = (os[0]=="Linux"?'':"Linux ")+os[0];
|
|
88
|
+
d.type = os[o2-2], d.version = os[o2-1];
|
|
89
|
+
}
|
|
90
|
+
if(o2=Number(d.version)) d.version=o2;
|
|
91
|
+
o2=ua.indexOf(' ',o+2), o3=ua.indexOf(')',o2+1), o3=o3==-1?o2+1:o3+2;
|
|
92
|
+
d.engine = ua.substring(o+2,o2), d.browser = ua.substring(o3);
|
|
93
|
+
d.mobile = !!ua.match(/Mobi/i); return d;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
utils.device = utils.deviceInfo();
|
|
97
|
+
utils.mobile = utils.device.mobile;
|
|
98
|
+
|
|
99
|
+
//Get touch by id, returns null if none found
|
|
100
|
+
if(window.TouchList) utils.proto(TouchList, 'get', function(id) {
|
|
101
|
+
for(let k in this) if(this[k].identifier == id) return this[k]; return 0;
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
//Generates modified input field for css skinning on unsupported browsers. This is a JavaScript
|
|
105
|
+
//fallback for when css 'appearance:none' doesn't work. For Mobile Safari, this is usually
|
|
106
|
+
//needed with 'datetime-local', 'select-one', and 'select-multiple' input types
|
|
107
|
+
utils.skinnedInput = el => {
|
|
108
|
+
const cont = utils.mkDiv(null,el.className), is = el.style, type = el.type; el.className += ' isSub';
|
|
109
|
+
if(type == 'datetime-local' || type == 'select-one' || type == 'select-multiple') { //Datetime or Select
|
|
110
|
+
is.opacity = 0; is.top = '-100%'; el.siT = utils.mkEl('span',cont,'isText');
|
|
111
|
+
utils.mkEl('span',cont,'isArrow',{borderTopColor:getComputedStyle(el).color});
|
|
112
|
+
let si=siChange.bind(el); el.addEventListener('change',si); el.forceUpdate=si; si();
|
|
113
|
+
}
|
|
114
|
+
el.replaceWith(cont); cont.appendChild(el);
|
|
115
|
+
//Append StyleSheet
|
|
116
|
+
if(!document.isStyles) document.isStyles=true, utils.mkEl('style',document.body,null,null,'.isSub {'+
|
|
117
|
+
'width:100% !important; height:100% !important; border:none !important; display:inline-block !important;'+
|
|
118
|
+
'position:relative !important; box-shadow:none !important; margin:0 !important; padding:initial !important;'+
|
|
119
|
+
'} .isText {'+
|
|
120
|
+
'display:inline-block; height:100%; max-width:95%;'+
|
|
121
|
+
'overflow:hidden; text-overflow:ellipsis; white-space:nowrap;'+
|
|
122
|
+
'} .isArrow {'+
|
|
123
|
+
'width:0; height:0; display:inline-block; float:right; top:38%; position:relative;'+
|
|
124
|
+
'border-left:3px solid transparent; border-right:3px solid transparent;'+
|
|
125
|
+
'border-top:6px solid #000; vertical-align:middle;'+
|
|
126
|
+
'}');
|
|
127
|
+
}
|
|
128
|
+
function siChange() {
|
|
129
|
+
switch(this.type) {
|
|
130
|
+
case 'datetime-local': this.siT.textContent = utils.formatDate(utils.fromDateTimeBox(this)); break;
|
|
131
|
+
case 'select-one': this.siT.textContent = selBoxLabel(this); break;
|
|
132
|
+
case 'select-multiple': this.siT.textContent = mulBoxLabel(this);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function selBoxLabel(sb) {
|
|
137
|
+
const op = sb.options; if(op.selectedIndex != -1) return op[op.selectedIndex].label;
|
|
138
|
+
return "No Options Selected";
|
|
139
|
+
}
|
|
140
|
+
function mulBoxLabel(sb) {
|
|
141
|
+
const op = sb.options; let str = ''; for(let i=0,l=op.length; i<l; ++i)
|
|
142
|
+
if(op[i].selected) str += (str?', ':'')+op[i].label; return str||"No Options Selected";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/*Turns your boring <input> into a mobile-friendly number entry field with max/min & negative support!
|
|
146
|
+
min: Min value, default min safe int
|
|
147
|
+
max: Max value, default max safe int
|
|
148
|
+
decMax: Max decimal precision (eg. 3 is 0.001), default 0
|
|
149
|
+
sym: If a symbol (eg. '$') is given, uses currency mode
|
|
150
|
+
Tips:
|
|
151
|
+
- Use field.onnuminput in place of oninput, get number value with field.num
|
|
152
|
+
- On mobile, use star key for decimal point and pound key for negative
|
|
153
|
+
- You can set field.step in order to change the up/down arrow step size
|
|
154
|
+
- Use field.setRange to change min, max, and decMax*/
|
|
155
|
+
utils.numField=(f, min, max, decMax, sym) => {
|
|
156
|
+
const RM=RegExp(`[,${sym?RegExp.escape(sym):''}]`,'g');
|
|
157
|
+
f.type=(utils.mobile||decMax||sym)?'tel':'number';
|
|
158
|
+
f.setAttribute('pattern',"\\d*");
|
|
159
|
+
if(min==null) min=Number.MIN_SAFE_INTEGER;
|
|
160
|
+
if(max==null) max=Number.MAX_SAFE_INTEGER;
|
|
161
|
+
if(decMax==null) decMax=sym?2:0;
|
|
162
|
+
if(!f.step) f.step=1;
|
|
163
|
+
f.num=Math.max(0,min), f.ns='';
|
|
164
|
+
f.value=sym?utils.formatCost(f.num,sym):f.num.toString();
|
|
165
|
+
f.addEventListener('keydown',e => {
|
|
166
|
+
if(e.ctrlKey) return;
|
|
167
|
+
let k=e.key, kn=k.length==1&&Number.isFinite(Number(k)),
|
|
168
|
+
ns=f.ns, len=ns.length, dec=ns.indexOf('.');
|
|
169
|
+
|
|
170
|
+
if(k=='Tab' || k=='Enter') return;
|
|
171
|
+
else if(kn) {if(dec==-1 || len-dec < decMax+1) ns+=k} //Number
|
|
172
|
+
else if(k=='.' || k=='*') {if(decMax && dec==-1
|
|
173
|
+
&& f.num!=max && (min>=0 || f.num!=min)) { //Decimal
|
|
174
|
+
if(!len && min>0) ns=Math.floor(min)+'.';
|
|
175
|
+
else ns+='.';
|
|
176
|
+
}} else if(k=='Backspace' || k=='Delete') { //Backspace
|
|
177
|
+
if(min>0 && f.num==min && ns.endsWith('.')) ns='';
|
|
178
|
+
else ns=ns.slice(0,-1);
|
|
179
|
+
} else if(k=='-' || k=='#') {if(min<0 && !len) ns='-'} //Negative
|
|
180
|
+
else if(k=='ArrowUp') ns=null, f.set(f.num+Number(f.step)); //Up
|
|
181
|
+
else if(k=='ArrowDown') ns=null, f.set(f.num-Number(f.step)); //Down
|
|
182
|
+
|
|
183
|
+
if(ns !== null && ns !== f.ns) {
|
|
184
|
+
let neg=ns=='-'||ns=='-.', s=neg?'0':ns+(ns.endsWith('.')?'0':''),
|
|
185
|
+
nr=Number(s), n=Math.min(max,Math.max(min,nr));
|
|
186
|
+
if(!kn || f.num !== n || nr === min) {
|
|
187
|
+
f.ns=ns, f.num=n;
|
|
188
|
+
f.value = sym ? (neg?sym+'-0.00':utils.formatCost(n,sym)):
|
|
189
|
+
((neg?'-':'')+n+(ns.endsWith('.')&&(min<=0||n!=min)?'.0':''));
|
|
190
|
+
if(f.onnuminput) f.onnuminput.call(f);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
e.preventDefault();
|
|
194
|
+
});
|
|
195
|
+
f.set=n => {
|
|
196
|
+
if(typeof n=='string') n=n.replace(RM,'');
|
|
197
|
+
n=Math.min(max,Math.max(min,Number(n)||0));
|
|
198
|
+
f.num = decMax?Number(n.toFixed(decMax)):Math.round(n);
|
|
199
|
+
f.ns = f.num.toString();
|
|
200
|
+
f.value = sym?utils.formatCost(f.num,sym):f.ns;
|
|
201
|
+
f.ns=f.ns.replace(/^(-?)0+/,'$1');
|
|
202
|
+
if(f.onnuminput) f.onnuminput.call(f);
|
|
203
|
+
}
|
|
204
|
+
f.setRange=(nMin, nMax, nDecMax) => {
|
|
205
|
+
min=nMin, max=nMax, decMax=nDecMax;
|
|
206
|
+
}
|
|
207
|
+
f.addEventListener('input',() => f.set(f.value));
|
|
208
|
+
f.addEventListener('paste',e => {f.set(e.clipboardData.getData('text')); e.preventDefault()});
|
|
209
|
+
return f;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
//Format Number as currency. Uses '$' by default
|
|
213
|
+
utils.formatCost = (n, sym='$') => {
|
|
214
|
+
if(!n) return sym+'0.00';
|
|
215
|
+
const p=n.toFixed(2).split('.');
|
|
216
|
+
return sym+p[0].split('').reverse().reduce((a,n,i) =>
|
|
217
|
+
n=='-'?n+a:n+(i&&!(i%3)?',':'')+a,'')+'.'+p[1];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
//Convert value from 'datetime-local' input to Date object
|
|
221
|
+
utils.fromDateTimeBox = el => {
|
|
222
|
+
const v=el.value; if(!v) return new Date();
|
|
223
|
+
return new Date(v.replace(/-/g,'/').replace(/T/g,' '));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
//Convert Number to fixed-length
|
|
227
|
+
//Set radix to 16 for HEX
|
|
228
|
+
utils.fixedNum = function(n,len,radix=10) {
|
|
229
|
+
let s=Math.abs(n).toString(radix).toUpperCase();
|
|
230
|
+
return (n<0?'-':'')+(radix==16?'0x':radix==2?'0b':'')+'0'.repeat(Math.max(len-s.length,0))+s;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
utils.months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
234
|
+
function fixed2(n) {return n<=9?'0'+n:n}
|
|
235
|
+
|
|
236
|
+
//Convert Date object into format to set 'datetime-local'
|
|
237
|
+
//input value, optionally including seconds if 'sec' is true
|
|
238
|
+
utils.toDateTimeBox = (d, sec) => {
|
|
239
|
+
return d.getFullYear()+'-'+fixed2(d.getMonth()+1)+'-'+fixed2(d.getDate())+'T'+
|
|
240
|
+
fixed2(d.getHours())+':'+fixed2(d.getMinutes())+(sec?':'+fixed2(d.getSeconds()):'');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
//Format Date object into human-readable string
|
|
244
|
+
//opt:
|
|
245
|
+
// sec: True to include seconds
|
|
246
|
+
// ms: True or 3 to include milliseconds (requires sec), 2 or 1 to limit precision
|
|
247
|
+
// h24: True to use 24-hour time
|
|
248
|
+
// time: True to show time only, false to show date only, null to show both
|
|
249
|
+
// suf: False to drop date suffix (1st, 2nd, etc.)
|
|
250
|
+
// year: False to hide year, or a number to show year only if it differs from given year
|
|
251
|
+
// df: True to put date first instead of time
|
|
252
|
+
utils.formatDate = (d,opt={}) => {
|
|
253
|
+
let t='',yy,dd;
|
|
254
|
+
if(d==null || !d.getDate || !((yy=d.getFullYear())>1969)) return "[Invalid Date]";
|
|
255
|
+
if(opt.time==null||opt.time) {
|
|
256
|
+
let h=d.getHours(),pm=''; if(!opt.h24) {pm=' AM'; if(h>=12) pm=' PM',h-=12; if(!h) h=12}
|
|
257
|
+
t=h+':'+fixed2(d.getMinutes())+(opt.sec?':'+fixed2(d.getSeconds())
|
|
258
|
+
+(opt.ms?(d.getMilliseconds()/1000).toFixed(Number.isFinite(opt.ms)?opt.ms:3).substr(1):''):'');
|
|
259
|
+
t+=pm; if(opt.time) return t;
|
|
260
|
+
}
|
|
261
|
+
dd=d.getDate();
|
|
262
|
+
dd=utils.months[d.getMonth()]+' '+((opt.suf==null||opt.suf)?utils.suffix(dd):dd);
|
|
263
|
+
if((opt.year==null||opt.year) && opt.year!==yy) dd=dd+', '+yy;
|
|
264
|
+
return opt.df?dd+(t&&' '+t):(t&&t+' ')+dd;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
//Add appropriate suffix to number. (ex. 31st, 12th, 22nd)
|
|
268
|
+
utils.suffix = n => {
|
|
269
|
+
let j=n%10, k=n%100;
|
|
270
|
+
if(j==1 && k!=11) return n+"st";
|
|
271
|
+
if(j==2 && k!=12) return n+"nd";
|
|
272
|
+
if(j==3 && k!=13) return n+"rd";
|
|
273
|
+
return n+"th";
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
//Virtual Page Navigation
|
|
277
|
+
let H=history;
|
|
278
|
+
utils.goBack = H.back.bind(H);
|
|
279
|
+
utils.goForward = H.forward.bind(H);
|
|
280
|
+
utils.go = (url,st) => {H.pushState(st,'',url||location.pathname),doNav(st)}
|
|
281
|
+
addEventListener('popstate', (e) => doNav(e.state));
|
|
282
|
+
addEventListener('load', () => setTimeout(doNav.wrap(H.state),1));
|
|
283
|
+
function doNav(s) {if(utils.onNav) utils.onNav.call(null,s)}
|
|
284
|
+
|
|
285
|
+
//Create element of type with parent, className, style object, and innerHTML string
|
|
286
|
+
//(Just remember the order PCSI!) Use null to leave any parameter blank
|
|
287
|
+
utils.mkEl = (t,p,c,s,i) => {
|
|
288
|
+
const e=document.createElement(t);
|
|
289
|
+
if(c!=null) e.className=c; if(i!=null) e.innerHTML=i;
|
|
290
|
+
if(s && typeof s=='object') for(let k in s) {
|
|
291
|
+
if(k in e.style) e.style[k]=s[k]; else e.style.setProperty(k,s[k]);
|
|
292
|
+
}
|
|
293
|
+
if(p!=null) p.appendChild(e); return e;
|
|
294
|
+
}
|
|
295
|
+
utils.mkDiv = (p,c,s,i) => utils.mkEl('div',p,c,s,i);
|
|
296
|
+
utils.addText = (el, text) => el.appendChild(document.createTextNode(text));
|
|
297
|
+
|
|
298
|
+
//Get predicted width of text given CSS font style
|
|
299
|
+
utils.textWidth = (txt, font) => {
|
|
300
|
+
const c=window.TWCanvas||(window.TWCanvas=utils.mkEl('canvas')),
|
|
301
|
+
ctx=c.getContext('2d'); ctx.font=font; return ctx.measureText(txt).width;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
//It's useful for any canvas-style app to have the page dimensions on hand
|
|
305
|
+
utils.define(utils, 'w', ()=>innerWidth);
|
|
306
|
+
utils.define(utils, 'h', ()=>innerHeight);
|
|
307
|
+
|
|
308
|
+
/*Set a nested/recursive property in an object, even if the higher levels don't exist. Useful for defining settings in a complex config object
|
|
309
|
+
obj: Object to set property in
|
|
310
|
+
path: String (dot separated) or array defining the path to the property
|
|
311
|
+
val: Value to set
|
|
312
|
+
onlyNull: True to set value only if it doesn't exist*/
|
|
313
|
+
utils.setPropSafe = (obj, path, val, onlyNull=false) => {
|
|
314
|
+
if(typeof path=='string') path=path.split('.');
|
|
315
|
+
let li=path.length-1;
|
|
316
|
+
path.each(p => {typeof obj[p]=='object'?obj=obj[p]:obj=obj[p]={}},0,li);
|
|
317
|
+
li=path[li]; if(!onlyNull || obj[li]==null) return obj[li]=val;
|
|
318
|
+
return obj[li];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/*Gets a nested/recursive property in an object, returning undefined if it or any higher level doesn't exist. Useful for reading settings from a complex config object
|
|
322
|
+
obj: Object to read property from
|
|
323
|
+
path: String (dot separated) or array defining the path to the property*/
|
|
324
|
+
utils.getPropSafe = (obj, path) => {
|
|
325
|
+
if(typeof path=='string') path=path.split('.');
|
|
326
|
+
try {for(let p of path) obj=obj[p]; return obj} catch(_) {}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
//Remove 'empty' elements like 0, false, ' ', undefined, and NaN from array
|
|
330
|
+
//Often useful in combination with Array.split. Set 'keepZero' to true to keep '0's
|
|
331
|
+
//Function by: Pecacheu & https://stackoverflow.com/users/5445/cms
|
|
332
|
+
utils.proto(Array, 'clean', function(kz) {
|
|
333
|
+
for(let i=0,e,l=this.length; i<l; ++i) {
|
|
334
|
+
e=this[i]; if(utils.isBlank(e) || e === false ||
|
|
335
|
+
!kz && e === 0) this.splice(i--,1),l--;
|
|
336
|
+
} return this;
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
//Remove first instance of item from array. Returns false if not found
|
|
340
|
+
//Use a while loop to remove all instances
|
|
341
|
+
utils.proto(Array, 'remove', function(itm) {
|
|
342
|
+
const i=this.indexOf(itm); if(i==-1) return false;
|
|
343
|
+
this.splice(i,1); return true;
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
//Calls fn on each index of array
|
|
347
|
+
//fn: Callback function(element, index, length)
|
|
348
|
+
//st: Start index, optional. If negative, relative to end of array
|
|
349
|
+
//en: End index, optional. If negative, relative to end of array
|
|
350
|
+
//If fn returns '!', it will remove the element from the array
|
|
351
|
+
//Otherwise, if fn returns any non-null value,
|
|
352
|
+
//the loop is broken and the value is returned by each
|
|
353
|
+
function each(fn,st,en) {
|
|
354
|
+
let l=this.length,i=Math.max(st<0?l+st:(st||0),0),r;
|
|
355
|
+
if(en!=null) l=Math.min(en<0?l+en:en,l);
|
|
356
|
+
for(; i<l; ++i) if((r=fn(this[i],i,l))==='!') {
|
|
357
|
+
this instanceof HTMLCollection?this[i].remove():this.splice(i,1); --i,--l;
|
|
358
|
+
} else if(r!=null) return r;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
//Adds async support to each
|
|
362
|
+
//pe: true (default) to enable parallel async execution
|
|
363
|
+
async function eachAsync(fn,st,en,pe=true) {
|
|
364
|
+
let l=this.length,i=st=Math.max(st<0?l+st:(st||0),0),n,r=[];
|
|
365
|
+
if(en!=null) l=Math.min(en<0?l+en:en,l);
|
|
366
|
+
for(; i<l; ++i) {
|
|
367
|
+
n=fn(this[i],i,l);
|
|
368
|
+
if(!pe) { n=await n; if(n!=='!' && n!=null) return n; }
|
|
369
|
+
r.push(n);
|
|
370
|
+
}
|
|
371
|
+
if(pe) r=await Promise.all(r);
|
|
372
|
+
for(i=st,n=0; i<l; ++i,++n) if(r[n]==='!') {
|
|
373
|
+
this instanceof HTMLCollection?this[i].remove():this.splice(i,1); --i,--l;
|
|
374
|
+
} else if(r[n]!=null) return r[n];
|
|
375
|
+
}
|
|
376
|
+
[Array,HTMLCollection,NodeList].forEach(p => {utils.proto(p,'each',each), utils.proto(p,'eachAsync',eachAsync)});
|
|
377
|
+
|
|
378
|
+
const B64='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', B64URL=B64.replace('+/','-_'),
|
|
379
|
+
B64F={43:62,47:63,48:52,49:53,50:54,51:55,52:56,53:57,54:58,55:59,56:60,57:61,65:0,66:1,67:2,68:3,69:4,70:5,71:6,72:7,73:8,
|
|
380
|
+
74:9,75:10,76:11,77:12,78:13,79:14,80:15,81:16,82:17,83:18,84:19,85:20,86:21,87:22,88:23,89:24,90:25,97:26,98:27,99:28,
|
|
381
|
+
100:29,101:30,102:31,103:32,104:33,105:34,106:35,107:36,108:37,109:38,110:39,111:40,112:41,113:42,114:43,115:44,116:45,
|
|
382
|
+
117:46,118:47,119:48,120:49,121:50,122:51,45:62,95:63};
|
|
383
|
+
|
|
384
|
+
//Polyfill for Uint8Array.toBase64
|
|
385
|
+
if(!('toBase64' in Uint8Array.prototype)) utils.proto(Uint8Array, 'toBase64', function(opt) {
|
|
386
|
+
let l=this.byteLength, br=l%3, b=opt&&opt.alphabet==='base64url'?B64URL:B64, i=0,str='',chk; l-=br;
|
|
387
|
+
for(; i<l; i+=3) {
|
|
388
|
+
chk = (this[i]<<16)|(this[i+1]<<8)|this[i+2];
|
|
389
|
+
str += b[(chk&16515072)>>18] + b[(chk&258048)>>12] + b[(chk&4032)>>6] + b[chk&63];
|
|
390
|
+
}
|
|
391
|
+
if(br==1) {
|
|
392
|
+
chk = this[l];
|
|
393
|
+
str += b[(chk&252)>>2] + b[(chk&3)<<4];
|
|
394
|
+
if(!opt || !opt.omitPadding) str += '=';
|
|
395
|
+
} else if(br==2) {
|
|
396
|
+
chk = (this[l]<<8)|this[l+1];
|
|
397
|
+
str += b[(chk&64512)>>10] + b[(chk&1008)>>4] + b[(chk&15)<<2];
|
|
398
|
+
if(!opt || !opt.omitPadding) str += '==';
|
|
399
|
+
}
|
|
400
|
+
return str;
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
function b64Char(s,i) {
|
|
404
|
+
s=B64F[s.charCodeAt(i)];
|
|
405
|
+
if(s==null) throw "Bad char at "+i;
|
|
406
|
+
return s;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
//Polyfill for Uint8Array.fromBase64
|
|
410
|
+
if(!('fromBase64' in Uint8Array)) utils.proto(Uint8Array, 'fromBase64', str => {
|
|
411
|
+
let l=str.length, i=l-1;
|
|
412
|
+
for(; i>=0; --i) if(str.charCodeAt(i)!==61) break;
|
|
413
|
+
l=i+1,i=0; let br=l%4; l-=br; if(br==1) throw "Bad b64 len";
|
|
414
|
+
let arr=new Uint8Array(l*3/4+(br?br-1:0)), b=-1,chk;
|
|
415
|
+
for(; i<l; i+=4) {
|
|
416
|
+
chk = (b64Char(str,i)<<18)|(b64Char(str,i+1)<<12)|(b64Char(str,i+2)<<6)|b64Char(str,i+3);
|
|
417
|
+
arr[++b]=chk>>16, arr[++b]=chk>>8, arr[++b]=chk;
|
|
418
|
+
}
|
|
419
|
+
if(br==2) {
|
|
420
|
+
arr[++b] = (b64Char(str,i)<<2)|(b64Char(str,i+1)>>4);
|
|
421
|
+
} else if(br==3) {
|
|
422
|
+
chk = (b64Char(str,i)<<10)|(b64Char(str,i+1)<<4)|(b64Char(str,i+2)>>2);
|
|
423
|
+
arr[++b]=chk>>8, arr[++b]=chk;
|
|
424
|
+
}
|
|
425
|
+
return arr;
|
|
426
|
+
},1)
|
|
427
|
+
|
|
428
|
+
const R_ESC1=/[|\\{}()[\]^$+*?.]/g, R_ESC2=/-/g;
|
|
429
|
+
|
|
430
|
+
//Polyfill for RegExp.escape by https://github.com/sindresorhus
|
|
431
|
+
if(!('escape' in RegExp)) utils.proto(RegExp, 'escape', s => {
|
|
432
|
+
return s.replace(R_ESC1,'\\$&').replace(R_ESC2,'\\x2d');
|
|
433
|
+
},1)
|
|
434
|
+
|
|
435
|
+
//Get an element's index in its parent. Returns -1 if the element has no parent
|
|
436
|
+
utils.define(Element.prototype, 'index', function() {
|
|
437
|
+
const p=this.parentElement; if(!p) return -1;
|
|
438
|
+
return Array.prototype.indexOf.call(p.children, this);
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
//Insert child at index
|
|
442
|
+
utils.proto(Element, 'insertChildAt', function(el, i) {
|
|
443
|
+
if(i<0) i=0; if(i >= this.children.length) this.appendChild(el);
|
|
444
|
+
else this.insertBefore(el, this.children[i]);
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
//Get element bounding rect as UtilRect object
|
|
448
|
+
utils.boundingRect=e => new UtilRect(e.getBoundingClientRect());
|
|
449
|
+
utils.innerRect=e => {
|
|
450
|
+
let r=e.getBoundingClientRect(), s=getComputedStyle(e);
|
|
451
|
+
return new UtilRect(r.top+parseFloat(s.paddingTop)+parseFloat(s.borderTopWidth),
|
|
452
|
+
r.bottom-parseFloat(s.paddingBottom)-parseFloat(s.borderBottomWidth),
|
|
453
|
+
r.left+parseFloat(s.paddingLeft)+parseFloat(s.borderLeftWidth),
|
|
454
|
+
r.right-parseFloat(s.paddingRight)-parseFloat(s.borderRightWidth));
|
|
455
|
+
};
|
|
456
|
+
utils.define(Element.prototype,'boundingRect',function() {return utils.boundingRect(this)});
|
|
457
|
+
utils.define(Element.prototype,'innerRect',function() {return utils.innerRect(this)});
|
|
458
|
+
|
|
459
|
+
//No idea why this isn't built-in, but it's not
|
|
460
|
+
Math.cot = x => 1/Math.tan(x);
|
|
461
|
+
|
|
462
|
+
//Check if string, array, or other object is empty
|
|
463
|
+
utils.isBlank = s => {
|
|
464
|
+
if(s == null) return true;
|
|
465
|
+
if(typeof s == 'string') return !/\S/.test(s);
|
|
466
|
+
if(typeof s == 'object') {
|
|
467
|
+
if(typeof s.length == 'number') return s.length === 0;
|
|
468
|
+
return Object.keys(s).length === 0;
|
|
469
|
+
}
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
//Finds first empty (undefined) slot in array
|
|
474
|
+
utils.firstEmpty = arr => {
|
|
475
|
+
const len = arr.length;
|
|
476
|
+
for(let i=0; i<len; ++i) if(arr[i] == null) return i;
|
|
477
|
+
return len;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
//Like 'firstEmpty', but uses letters a-Z instead
|
|
481
|
+
utils.firstEmptyChar = obj => {
|
|
482
|
+
const keys = Object.keys(obj), len = keys.length;
|
|
483
|
+
for(let i=0; i<len; ++i) if(obj[keys[i]] == null) return keys[i];
|
|
484
|
+
return utils.numToChar(len);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
//Converts a number into letters (upper and lower) from a to Z
|
|
488
|
+
utils.numToChar = n => {
|
|
489
|
+
if(n<=25) return String.fromCharCode(n+97);
|
|
490
|
+
else if(n>=26 && n<=51) return String.fromCharCode(n+39);
|
|
491
|
+
let mVal, fVal;
|
|
492
|
+
if(n<2756) { mVal=rstCount(Math.floor(n/52)-1,52); fVal=rstCount(n,52); }
|
|
493
|
+
else if(n<143364) { mVal=rstCount(Math.floor((n-52)/2704)-1,52); fVal=rstCount(n-52,2704)+52; }
|
|
494
|
+
else if(n<7454980) { mVal=rstCount(Math.floor((n-2756)/140608)-1,52); fVal=rstCount(n-2756,140608)+2756; }
|
|
495
|
+
else return false; //More than "ZZZZ"? No. Just, no
|
|
496
|
+
return utils.numToChar(mVal)+utils.numToChar(fVal);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
//Use this to reset your counter each time 'maxVal' is reached
|
|
500
|
+
function rstCount(val, maxVal) { while(val >= maxVal) val -= maxVal; return val; }
|
|
501
|
+
|
|
502
|
+
//Semi-recursively merges two (or more) objects, giving the last precedence
|
|
503
|
+
//If both objects contain a property at the same index, and both are Arrays/Objects, they are merged
|
|
504
|
+
utils.merge = function(o/*, src1, src2...*/) {
|
|
505
|
+
for(let a=1,al=arguments.length,n,oP,nP; a<al; ++a) {
|
|
506
|
+
n = arguments[a]; for(let k in n) {
|
|
507
|
+
oP = o[k]; nP = n[k]; if(oP && nP) { //Conflict
|
|
508
|
+
if(oP.length >= 0 && nP.length >= 0) { //Both Array-like
|
|
509
|
+
for(let i=0,l=nP.length,ofs=oP.length; i<l; ++i) oP[i+ofs] = nP[i]; continue;
|
|
510
|
+
} else if(typeof oP == 'object' && typeof nP == 'object') { //Both Objects
|
|
511
|
+
for(let pk in nP) oP[pk] = nP[pk]; continue;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
o[k] = nP;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return o;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
//Keeps value within max/min bounds. Also handles NaN or null
|
|
521
|
+
utils.bounds = (n, min=0, max=1) => {
|
|
522
|
+
if(!(n>=min)) return min; if(!(n<=max)) return max; return n;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
//'Normalizes' a value so that it ranges from min to max, but unlike utils.bounds,
|
|
526
|
+
//this function retains input's offset. This can be used to normalize angles
|
|
527
|
+
utils.norm = utils.normalize = (n, min=0, max=1) => {
|
|
528
|
+
const c = Math.abs(max-min);
|
|
529
|
+
if(n < min) while(n < min) n += c; else while(n >= max) n -= c;
|
|
530
|
+
return n;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
//Finds and removes all instances of 'rem' contained within s
|
|
534
|
+
utils.cutStr = (s, rem) => {
|
|
535
|
+
let fnd; while((fnd=s.indexOf(rem)) != -1) {
|
|
536
|
+
s = s.slice(0, fnd)+s.slice(fnd+rem.length);
|
|
537
|
+
}
|
|
538
|
+
return s;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
//Cuts text out of 'data' from first instance of 'startString' to next instance of 'endString'
|
|
542
|
+
//(data,startString,endString[,index[,searchStart]])
|
|
543
|
+
//index: Optional object. index.s and index.t will be set to start and end indexes
|
|
544
|
+
utils.dCut = (d, ss, es, sd, st) => {
|
|
545
|
+
let is = d.indexOf(ss,st?st:undefined)+ss.length, it = d.indexOf(es,is);
|
|
546
|
+
if(sd) sd.s=is,sd.t=it; return (is < ss.length || it <= is)?'':d.substring(is,it);
|
|
547
|
+
}
|
|
548
|
+
utils.dCutToLast = (d, ss, es, sd, st) => {
|
|
549
|
+
let is = d.indexOf(ss,st?st:undefined)+ss.length, it = d.lastIndexOf(es);
|
|
550
|
+
if(sd) sd.s=is,sd.t=it; return (is < ss.length || it <= is)?'':d.substring(is,it);
|
|
551
|
+
}
|
|
552
|
+
utils.dCutLast = (d, ss, es, sd, st) => {
|
|
553
|
+
let is = d.lastIndexOf(ss,st?st:undefined)+ss.length, it = d.indexOf(es,is);
|
|
554
|
+
if(sd) sd.s=is,sd.t=it; return (is < ss.length || it <= is)?'':d.substring(is,it);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
//Given CSS property value 'prop', returns object with
|
|
558
|
+
//space-separated values from the property string
|
|
559
|
+
utils.parseCSS = prop => {
|
|
560
|
+
const pArr={}, pKey="", keyNum=0; prop=prop.trim();
|
|
561
|
+
function parseInner(str) {
|
|
562
|
+
if(str.indexOf(',') !== -1) {
|
|
563
|
+
const arr = utils.clean(str.split(','));
|
|
564
|
+
for(let i=0, l=arr.length; i<l; ++i) arr[i]=arr[i].trim();
|
|
565
|
+
return arr;
|
|
566
|
+
}
|
|
567
|
+
return str.trim();
|
|
568
|
+
}
|
|
569
|
+
while(prop.length > 0) {
|
|
570
|
+
if(prop[0] == '(' && prop.indexOf(')') !== -1 && pKey) {
|
|
571
|
+
let end=prop.indexOf(')'), pStr=prop.substring(1, end);
|
|
572
|
+
pArr[pKey] = parseInner(pStr);
|
|
573
|
+
pKey = ""; prop = prop.substring(end+1);
|
|
574
|
+
} else if(prop.search(/[#!\w]/) == 0) {
|
|
575
|
+
if(pKey) pArr[keyNum++] = pKey;
|
|
576
|
+
let end=prop.search(/[^#!\w-%]/); if(end==-1) end=prop.length;
|
|
577
|
+
pKey = prop.substring(0, end); prop = prop.substring(end);
|
|
578
|
+
} else {
|
|
579
|
+
prop = prop.substring(1);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if(pKey) pArr[keyNum] = pKey; return pArr;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
//Rebuilds CSS string from a parseCSS object
|
|
586
|
+
utils.buildCSS = propArr => {
|
|
587
|
+
const keyArr=Object.keys(propArr), l=keyArr.length; let pStr='', i=0;
|
|
588
|
+
while(i<l) {
|
|
589
|
+
const k=keyArr[i], v=propArr[keyArr[i]]; ++i;
|
|
590
|
+
if(0<=Number(k)) pStr+=v+' '; else pStr+=`${k}(${v}) `;
|
|
591
|
+
}
|
|
592
|
+
return pStr.substring(0, pStr.length-1);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function defaultStyle() {
|
|
596
|
+
const ss=document.styleSheets;
|
|
597
|
+
for(let s=0,j=ss.length; s<j; ++s) try { ss[s].cssRules; return ss[s]; } catch(e) {}
|
|
598
|
+
let ns=utils.mkEl('style',document.head); utils.addText(ns,''); return ns.sheet;
|
|
599
|
+
}
|
|
600
|
+
function toKey(k) {
|
|
601
|
+
return k.replace(/[A-Z]/g, s => '-'+s.toLowerCase());
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
//Create a CSS class and append it to the current document. Fill 'propList' object
|
|
605
|
+
//with key/value pairs representing the properties you want to add to the class
|
|
606
|
+
utils.addClass = (className, propList) => {
|
|
607
|
+
const style = defaultStyle(), keys = Object.keys(propList); let str='';
|
|
608
|
+
for(let i=0,l=keys.length; i<l; ++i) str += toKey(keys[i])+":"+propList[keys[i]]+";";
|
|
609
|
+
style.addRule("."+className,str);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
//Create a CSS selector and append it to the current document
|
|
613
|
+
utils.addId = (idName, propList) => {
|
|
614
|
+
const style = defaultStyle(), keys = Object.keys(propList); let str='';
|
|
615
|
+
for(let i=0,l=keys.length; i<l; ++i) str += toKey(keys[i])+":"+propList[keys[i]]+";";
|
|
616
|
+
style.addRule("#"+idName,str);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
//Create a CSS keyframe and append it to the current document
|
|
620
|
+
utils.addKeyframe = (name, content) => {
|
|
621
|
+
defaultStyle().addRule("@keyframes "+name,content);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
//Remove a specific css selector (including the '.' or '#') from all stylesheets in the current document
|
|
625
|
+
utils.removeSelector = name => {
|
|
626
|
+
for(let s=0,style,rList,j=document.styleSheets.length; s<j; ++s) {
|
|
627
|
+
style = document.styleSheets[s]; try { rList=style.cssRules; } catch(e) { continue; }
|
|
628
|
+
for(let key in rList) if(rList[key].constructor.name == "CSSStyleRule"
|
|
629
|
+
&& rList[key].selectorText == name) style.deleteRule(key);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
//Shorthand way to get element by id/class name, within a parent element (defaults to document)
|
|
634
|
+
//(class[,parent])
|
|
635
|
+
utils.getId = (c,p) => (p?p:document)['getElementById'](c);
|
|
636
|
+
utils.getClassList = (c,p) => (p?p:document)['getElementsByClassName'](c);
|
|
637
|
+
utils.getClassFirst = (c,p) => (c=utils.getClassList(c,p),c.length>0?c[0]:0);
|
|
638
|
+
utils.getClassOnly = (c,p) => (c=utils.getClassList(c,p),c.length==1?c[0]:0);
|
|
639
|
+
|
|
640
|
+
//Converts HEX color to 24-bit RGB
|
|
641
|
+
utils.hexToRgb = hex => {
|
|
642
|
+
const c = parseInt(hex.substr(1), 16);
|
|
643
|
+
return [(c >> 16) & 255, (c >> 8) & 255, c & 255];
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
//Generates random integer from min to max
|
|
647
|
+
utils.rand = (min, max, res, ease) => {
|
|
648
|
+
res=res||1; max*=res,min*=res; let r=Math.random();
|
|
649
|
+
return Math.round((ease?ease(r):r)*(max-min)+min)/res;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
//Parses a url query string into an Object
|
|
653
|
+
//Function by: Pecacheu (From Pecacheu's Apache Test Server)
|
|
654
|
+
utils.fromQuery = str => {
|
|
655
|
+
if(str.startsWith('?')) str = str.substr(1);
|
|
656
|
+
function parse(params, pairs) {
|
|
657
|
+
const pair = pairs[0], spl = pair.indexOf('='),
|
|
658
|
+
key = decodeURIComponent(pair.substr(0,spl)),
|
|
659
|
+
value = decodeURIComponent(pair.substr(spl+1));
|
|
660
|
+
//Handle multiple parameters of the same name
|
|
661
|
+
if(params[key] == null) params[key] = value;
|
|
662
|
+
else if(typeof params[key] == 'array') params[key].push(value);
|
|
663
|
+
else params[key] = [params[key],value];
|
|
664
|
+
return pairs.length == 1 ? params : parse(params, pairs.slice(1));
|
|
665
|
+
} return str.length == 0 ? {} : parse({}, str.split('&'));
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
//Converts an object into a url query string
|
|
669
|
+
utils.toQuery = obj => {
|
|
670
|
+
let str = ''; if(typeof obj != 'object') return encodeURIComponent(obj);
|
|
671
|
+
for(let key in obj) {
|
|
672
|
+
let val = obj[key]; if(typeof val == 'object') val = JSON.stringify(val);
|
|
673
|
+
str += '&'+key+'='+encodeURIComponent(val);
|
|
674
|
+
} return str.slice(1);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
//Various methods of centering objects using JavaScript
|
|
678
|
+
//obj: Object to center
|
|
679
|
+
//only: 'x' for only x axis centering, 'y' for only y axis, null for both
|
|
680
|
+
//type: 'calc', 'trans', 'move', or null, modes explained below
|
|
681
|
+
utils.center = (obj, only, type) => {
|
|
682
|
+
let os=obj.style;
|
|
683
|
+
if(!os.position) os.position="absolute";
|
|
684
|
+
if(type == 'calc') { //Responsive, but only if object size is static
|
|
685
|
+
if(!only || only == "x") os.left=`calc(50% - ${obj.clientWidth/2}px)`;
|
|
686
|
+
if(!only || only == "y") os.top=`calc(50% - ${obj.clientHeight/2}px)`;
|
|
687
|
+
} else if(type == 'move') { //Not responsive
|
|
688
|
+
if(!only || only == "x") os.left=`${utils.w/2 - obj.clientWidth/2}px`;
|
|
689
|
+
if(!only || only == "y") os.top=`${utils.h/2 - obj.clientHeight/2}px`;
|
|
690
|
+
} else if(type == 'trans') { //Responsive, doesn't create container
|
|
691
|
+
let trans = utils.cutStr(os.transform, "translateX(-50%)");
|
|
692
|
+
trans = utils.cutStr(trans, "translateY(-50%)");
|
|
693
|
+
if(!only || only == "x") os.left="50%", trans+="translateX(-50%)";
|
|
694
|
+
if(!only || only == "y") os.top="50%", trans+="translateY(-50%)";
|
|
695
|
+
if(trans) os.transform=trans;
|
|
696
|
+
} else { //Responsive, largest browser support
|
|
697
|
+
let cont=utils.mkEl("div", obj.parentNode, null, {display:'table',
|
|
698
|
+
position:'absolute', top:0, left:0, width:'100%', height:'100%'});
|
|
699
|
+
cont.appendChild(obj); os.display="table-cell";
|
|
700
|
+
if(!only || only == "x") os.textAlign="center";
|
|
701
|
+
if(!only || only == "y") os.verticalAlign="middle";
|
|
702
|
+
os.position="relative";
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
//Loads a file and returns its contents using HTTP GET
|
|
707
|
+
//Callback parameters: (err, data, req)
|
|
708
|
+
//err: Non-zero on error. Standard HTTP error codes
|
|
709
|
+
//data: Response text
|
|
710
|
+
//req: Full XMLHttpRequest object
|
|
711
|
+
//meth: Optional HTTP method, default is GET
|
|
712
|
+
//body: Optional body content
|
|
713
|
+
//hd: Optional header list
|
|
714
|
+
utils.loadAjax = (path, cb, meth, body, hd) => {
|
|
715
|
+
let R; try {R=new XMLHttpRequest()} catch(e) {return cb(e)}
|
|
716
|
+
if(hd) for(let k in hd) R.setRequestHeader(k,hd[k]);
|
|
717
|
+
R.open(meth||'GET',path); R.onreadystatechange = () => {
|
|
718
|
+
let s=R.status||-1;
|
|
719
|
+
if(R.readyState == R.DONE) cb(s==200?0:s, R.response, R);
|
|
720
|
+
}
|
|
721
|
+
R.send(body||undefined);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
//Good fallback for loadAjax. Loads a file at the address via HTML object tag
|
|
725
|
+
//Callback is fired with either received data, or 'false' if unsuccessful
|
|
726
|
+
utils.loadFile = (path, cb, timeout) => {
|
|
727
|
+
const obj = utils.mkEl('object', document.body, null, {position:'fixed', opacity:0});
|
|
728
|
+
obj.data = path;
|
|
729
|
+
let tmr = setTimeout(() => {
|
|
730
|
+
obj.remove(); tmr = null; cb(false);
|
|
731
|
+
}, timeout||4000);
|
|
732
|
+
obj.onload = () => {
|
|
733
|
+
if(!tmr) return; clearTimeout(tmr);
|
|
734
|
+
cb(obj.contentDocument.documentElement.outerHTML);
|
|
735
|
+
obj.remove();
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
//Loads a file at the address from a JSONP-enabled server. Callback
|
|
740
|
+
//is fired with either received data, or 'false' if unsuccessful
|
|
741
|
+
utils.loadJSONP = (path, cb, timeout) => {
|
|
742
|
+
const script = utils.mkEl('script', document.head), id = utils.firstEmptyChar(utils.lJSONCall);
|
|
743
|
+
script.type = 'application/javascript';
|
|
744
|
+
script.src = path+(path.indexOf('?')==-1?'?':'&')+'callback=utils.lJSONCall.'+id;
|
|
745
|
+
let tmr = setTimeout(() => { delete utils.lJSONCall[id]; cb(false); }, timeout||4000);
|
|
746
|
+
utils.lJSONCall[id] = data => {
|
|
747
|
+
if(tmr) clearTimeout(tmr); delete utils.lJSONCall[id]; cb(data);
|
|
748
|
+
}
|
|
749
|
+
document.head.removeChild(script);
|
|
750
|
+
}; utils.lJSONCall = [];
|
|
751
|
+
|
|
752
|
+
//Downloads a file from a link
|
|
753
|
+
utils.dlFile = (fn,uri) => {
|
|
754
|
+
return fetch(uri).then(r => { if(r.status != 200) throw "Code "+r.status; return r.blob(); })
|
|
755
|
+
.then(b => { utils.dlData(fn,b); });
|
|
756
|
+
}
|
|
757
|
+
//Downloads a file generated from a Blob or ArrayBuffer
|
|
758
|
+
utils.dlData = (fn,d) => {
|
|
759
|
+
let o,e=utils.mkEl('a',document.body,null,{display:'none'});
|
|
760
|
+
if(typeof d=='string') o=d; else {
|
|
761
|
+
if(!(d instanceof Blob)) d=Blob(d); o=URL.createObjectURL(d);
|
|
762
|
+
}
|
|
763
|
+
e.href=o,e.download=fn; e.click(); e.remove(); URL.revokeObjectURL(o);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
//Converts from radians to degrees, so you can work in degrees
|
|
767
|
+
//Function by: The a**hole who invented radians
|
|
768
|
+
utils.deg = rad => rad*180/Math.PI;
|
|
769
|
+
|
|
770
|
+
//Converts from degrees to radians, so you can convert back for given stupid library
|
|
771
|
+
//Function by: The a**hole who invented radians
|
|
772
|
+
utils.rad = deg => deg*Math.PI/180;
|
|
773
|
+
|
|
774
|
+
//Pecacheu's ultimate unit translation formula!
|
|
775
|
+
//This Version -- Bounds Checking: NO, Rounding: NO, Max/Min Switching: NO, Easing: YES
|
|
776
|
+
utils.map = (input, minIn, maxIn, minOut, maxOut, ease) => {
|
|
777
|
+
let i=(input-minIn)/(maxIn-minIn); return ((ease?ease(i):i)*(maxOut-minOut))+minOut;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
//setTimeout but async
|
|
781
|
+
utils.delay = ms => new Promise(r => setTimeout(r,ms));
|
|
782
|
+
|
|
783
|
+
UtilRect = function(t,b,l,r) {
|
|
784
|
+
if(!(this instanceof UtilRect)) return new UtilRect(t,b,l,r);
|
|
785
|
+
const f=Number.isFinite; let tt=0,bb=0,ll=0,rr=0;
|
|
786
|
+
utils.define(this,'x', ()=>ll, v=>{f(v)?(rr+=v-ll,ll=v):0});
|
|
787
|
+
utils.define(this,'y', ()=>tt, v=>{f(v)?(bb+=v-tt,tt=v):0});
|
|
788
|
+
utils.define(this,'top', ()=>tt, v=>{tt=f(v)?v:0});
|
|
789
|
+
utils.define(this,['bottom','y2'], ()=>bb, v=>{bb=f(v)?v:0});
|
|
790
|
+
utils.define(this,'left', ()=>ll, v=>{ll=f(v)?v:0});
|
|
791
|
+
utils.define(this,['right','x2'], ()=>rr, v=>{rr=f(v)?v:0});
|
|
792
|
+
utils.define(this,['width','w'], ()=>rr-ll, v=>{rr=v>=0?ll+v:0});
|
|
793
|
+
utils.define(this,['height','h'], ()=>bb-tt, v=>{bb=v>=0?tt+v:0});
|
|
794
|
+
utils.define(this,'centerX', ()=>ll/2+rr/2);
|
|
795
|
+
utils.define(this,'centerY', ()=>tt/2+bb/2);
|
|
796
|
+
if(t instanceof DOMRect || t instanceof UtilRect) tt=t.top, bb=t.bottom, ll=t.left, rr=t.right;
|
|
797
|
+
else tt=t, bb=b, ll=l, rr=r;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
//Check if rect contains point, other rect, or Element
|
|
801
|
+
utils.proto(UtilRect, 'contains', function(x,y) {
|
|
802
|
+
if(x instanceof Element) return this.contains(x.boundingRect);
|
|
803
|
+
if(x instanceof UtilRect) return x.x >= this.x && x.x2 <= this.x2 && x.y >= this.y && x.y2 <= this.y2;
|
|
804
|
+
return x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2;
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
//Check if rect overlaps rect or Element
|
|
808
|
+
utils.proto(UtilRect, 'overlaps', function(r) {
|
|
809
|
+
if(r instanceof Element) return this.overlaps(r.boundingRect);
|
|
810
|
+
if(!(r instanceof UtilRect)) return 0; let x,y;
|
|
811
|
+
if(r.x2-r.x >= this.x2-this.x) x = this.x >= r.x && this.x <= r.x2 || this.x2 >= r.x && this.x2 <= r.x2;
|
|
812
|
+
else x = r.x >= this.x && r.x <= this.x2 || r.x2 >= this.x && r.x2 <= this.x2;
|
|
813
|
+
if(r.y2-r.y >= this.y2-this.y) y = this.y >= r.y && this.y <= r.y2 || this.y2 >= r.y && this.y2 <= r.y2;
|
|
814
|
+
else y = r.y >= this.y && r.y <= this.y2 || r.y2 >= this.y && r.y2 <= this.y2;
|
|
815
|
+
return x&&y;
|
|
816
|
+
})
|
|
817
|
+
|
|
818
|
+
//Get distance from this rect to point, other rect, or Element
|
|
819
|
+
utils.proto(UtilRect, 'dist', function(x,y) {
|
|
820
|
+
if(x instanceof Element) return this.dist(x.boundingRect); let n=(x instanceof UtilRect);
|
|
821
|
+
y=Math.abs((n?x.centerY:y)-this.centerY), x=Math.abs((n?x.centerX:x)-this.centerX);
|
|
822
|
+
return Math.sqrt(x*x+y*y);
|
|
823
|
+
})
|
|
824
|
+
|
|
825
|
+
//Expand (or contract if negative) a UtilRect by num of pixels
|
|
826
|
+
//Useful for using UtilRect objects as element hitboxes. Returns self for chaining
|
|
827
|
+
utils.proto(UtilRect, 'expand', function(by) {
|
|
828
|
+
this.top -= by; this.left -= by; this.bottom += by;
|
|
829
|
+
this.right += by; return this;
|
|
830
|
+
})
|
|
831
|
+
|
|
832
|
+
})(); //End of Utils Library
|
|
833
|
+
|
|
834
|
+
//JavaScript Easing Library by: https://github.com/gre & https://gizma.com/easing
|
|
835
|
+
//t should be between 0 and 1
|
|
836
|
+
const Easing = {
|
|
837
|
+
//no easing, no acceleration
|
|
838
|
+
linear:t => t,
|
|
839
|
+
//accelerating from zero velocity
|
|
840
|
+
easeInQuad:t => t*t,
|
|
841
|
+
//decelerating to zero velocity
|
|
842
|
+
easeOutQuad:t => t*(2-t),
|
|
843
|
+
//acceleration until halfway, then deceleration
|
|
844
|
+
easeInOutQuad:t => t<.5 ? 2*t*t : -1+(4-2*t)*t,
|
|
845
|
+
//accelerating from zero velocity
|
|
846
|
+
easeInCubic:t => t*t*t,
|
|
847
|
+
//decelerating to zero velocity
|
|
848
|
+
easeOutCubic:t => (--t)*t*t+1,
|
|
849
|
+
//acceleration until halfway, then deceleration
|
|
850
|
+
easeInOutCubic:t => t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1,
|
|
851
|
+
//accelerating from zero velocity
|
|
852
|
+
easeInQuart:t => t*t*t*t,
|
|
853
|
+
//decelerating to zero velocity
|
|
854
|
+
easeOutQuart:t => 1-(--t)*t*t*t,
|
|
855
|
+
//acceleration until halfway, then deceleration
|
|
856
|
+
easeInOutQuart:t => t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t,
|
|
857
|
+
//accelerating from zero velocity
|
|
858
|
+
easeInQuint:t => t*t*t*t*t,
|
|
859
|
+
//decelerating to zero velocity
|
|
860
|
+
easeOutQuint:t => 1+(--t)*t*t*t*t,
|
|
861
|
+
//acceleration until halfway, then deceleration
|
|
862
|
+
easeInOutQuint:t => t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
//Node.js compat
|
|
866
|
+
if(typeof global!='undefined') {
|
|
867
|
+
global.utils=utils, global.UtilRect=UtilRect, global.Easing=Easing;
|
|
868
|
+
}
|
package/utils.min.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
//https://github.com/Pecacheu/Utils.js MIT -- Minified with https://javascript-minifier.com
|
|
2
|
+
"use strict";const utils={VER:"v8.6.6"};let UtilRect,P="undefined"==typeof window?[{},{back(){},forward(){}},class{},class{},class{},class{},()=>{}]:[window,history,DOMRect,HTMLCollection,Element,NodeList,addEventListener];(()=>{let[t,e,i,l,s,n,r]=P;function o(){switch(this.type){case"datetime-local":this.siT.textContent=utils.formatDate(utils.fromDateTimeBox(this));break;case"select-one":this.siT.textContent=u(this);break;case"select-multiple":this.siT.textContent=a(this)}}function u(t){let e=t.options;return -1!=e.selectedIndex?e[e.selectedIndex].label:"No Options Selected"}function a(t){let e=t.options,i="";for(let l=0,s=e.length;l<s;++l)e[l].selected&&(i+=(i?", ":"")+e[l].label);return i||"No Options Selected"}function d(t){return t<=9?"0"+t:t}utils.define=(t,e,i,l)=>{let s={};if(i&&(s.get=i),l&&(s.set=l),Array.isArray(e))for(let n of e)Object.defineProperty(t,n,s);else Object.defineProperty(t,e,s)},utils.proto=(t,e,i,l)=>{let s={value:i};if(l||(t=t.prototype),Array.isArray(e))for(let n of e)Object.defineProperty(t,n,s);else Object.defineProperty(t,e,s)},utils.setCookie=(t,e,i,l)=>{let s=encodeURIComponent(t)+"="+(null==e?"":encodeURIComponent(e))+";path=/";null!=i&&(i instanceof Date||(i=new Date(i)),s+=";expires="+i.toUTCString()),l&&(s+=";secure"),document.cookie=s},utils.remCookie=t=>{document.cookie=encodeURIComponent(t)+"=;expires="+new Date(0).toUTCString()},utils.getCookie=t=>{let e=encodeURIComponent(t),i=" "+e,l=document.cookie.split(";");for(let s=0,n=l.length,r,o,u;s<n;++s)if(o=(r=l[s]).indexOf("="),(u=r.substr(0,o))==e||u==i)return decodeURIComponent(r.substr(o+1))},utils.proto(Function,"wrap",function(){let t=this,e=arguments;return function(){t.apply(arguments,e)}}),utils.copy=(t,e)=>{if(0===e||"object"!=typeof t)return t;e=e>0?e-1:null;let i;if(Array.isArray(t))i=Array(t.length),t.forEach((t,l)=>{i[l]=utils.copy(t,e)});else for(let l in i={},t)i[l]=utils.copy(t[l],e);return i},utils.deviceInfo=t=>{let e={};if(t||(t=navigator.userAgent),!t.startsWith("Mozilla/5.0 "))return e;let i=t.indexOf(")"),l=e.rawOS=t.substring(13,i),s,n;return l.startsWith("Windows")?(s=l.split("; "),e.os="Windows",e.type=-1!=s.indexOf("WOW64")?"x64 PC; x86 Browser":-1!=s.indexOf("x64")?"x64 PC":"x86 PC",s=l.indexOf("Windows NT "),e.version=l.substring(s+11,l.indexOf(";",s+12))):l.startsWith("iP")?(s=l.indexOf("OS"),e.os="iOS",e.type=l.substr(0,l.indexOf(";")),e.version=l.substring(s+3,l.indexOf(" ",s+4)).replace(/_/g,".")):l.startsWith("Macintosh;")?(s=l.indexOf(" Mac OS X"),e.os="MacOS",e.type=l.substring(11,s)+" Mac",e.version=l.substr(s+10).replace(/_/g,".")):-1!=(s=l.indexOf("Android"))?(e.os="Android",e.version=l.substring(s+8,l.indexOf(";",s+9)),s=l.lastIndexOf(";"),n=l.indexOf(" Build",s+2),e.type=l.substring(s+2,-1==n?void 0:n)):l.startsWith("X11;")&&(s=(l=l.substr(5).split(/[;\s]+/)).length,e.os=("Linux"==l[0]?"":"Linux ")+l[0],e.type=l[s-2],e.version=l[s-1]),(s=Number(e.version))&&(e.version=s),s=t.indexOf(" ",i+2),n=-1==(n=t.indexOf(")",s+1))?s+1:n+2,e.engine=t.substring(i+2,s),e.browser=t.substring(n),e.mobile=!!t.match(/Mobi/i),e},utils.device=utils.deviceInfo(),utils.mobile=utils.device.mobile,t.TouchList&&utils.proto(TouchList,"get",function(t){for(let e in this)if(this[e].identifier==t)return this[e];return 0}),utils.skinnedInput=t=>{let e=utils.mkDiv(null,t.className),i=t.style,l=t.type;if(t.className+=" isSub","datetime-local"==l||"select-one"==l||"select-multiple"==l){i.opacity=0,i.top="-100%",t.siT=utils.mkEl("span",e,"isText"),utils.mkEl("span",e,"isArrow",{borderTopColor:getComputedStyle(t).color});let s=o.bind(t);t.addEventListener("change",s),t.forceUpdate=s,s()}t.replaceWith(e),e.appendChild(t),document.isStyles||(document.isStyles=!0,utils.mkEl("style",document.body,null,null,".isSub {width:100% !important; height:100% !important; border:none !important; display:inline-block !important;position:relative !important; box-shadow:none !important; margin:0 !important; padding:initial !important;} .isText {display:inline-block; height:100%; max-width:95%;overflow:hidden; text-overflow:ellipsis; white-space:nowrap;} .isArrow {width:0; height:0; display:inline-block; float:right; top:38%; position:relative;border-left:3px solid transparent; border-right:3px solid transparent;border-top:6px solid #000; vertical-align:middle;}"))},utils.numField=(t,e,i,l,s)=>{let n=RegExp(`[,${s?RegExp.escape(s):""}]`,"g");return t.type=utils.mobile||l||s?"tel":"number",t.setAttribute("pattern","\\d*"),null==e&&(e=Number.MIN_SAFE_INTEGER),null==i&&(i=Number.MAX_SAFE_INTEGER),null==l&&(l=s?2:0),t.step||(t.step=1),t.num=Math.max(0,e),t.ns="",t.value=s?utils.formatCost(t.num,s):t.num.toString(),t.addEventListener("keydown",n=>{if(n.ctrlKey)return;let r=n.key,o=1==r.length&&Number.isFinite(Number(r)),u=t.ns,a=u.length,d=u.indexOf(".");if("Tab"!=r&&"Enter"!=r){if(o?(-1==d||a-d<l+1)&&(u+=r):"."==r||"*"==r?l&&-1==d&&t.num!=i&&(e>=0||t.num!=e)&&(!a&&e>0?u=Math.floor(e)+".":u+="."):"Backspace"==r||"Delete"==r?u=e>0&&t.num==e&&u.endsWith(".")?"":u.slice(0,-1):"-"==r||"#"==r?e<0&&!a&&(u="-"):"ArrowUp"==r?(u=null,t.set(t.num+Number(t.step))):"ArrowDown"==r&&(u=null,t.set(t.num-Number(t.step))),null!==u&&u!==t.ns){let f="-"==u||"-."==u,c=Number(f?"0":u+(u.endsWith(".")?"0":"")),h=Math.min(i,Math.max(e,c));(!o||t.num!==h||c===e)&&(t.ns=u,t.num=h,t.value=s?f?s+"-0.00":utils.formatCost(h,s):(f?"-":"")+h+(u.endsWith(".")&&(e<=0||h!=e)?".0":""),t.onnuminput&&t.onnuminput.call(t))}n.preventDefault()}}),t.set=r=>{"string"==typeof r&&(r=r.replace(n,"")),r=Math.min(i,Math.max(e,Number(r)||0)),t.num=l?Number(r.toFixed(l)):Math.round(r),t.ns=t.num.toString(),t.value=s?utils.formatCost(t.num,s):t.ns,t.ns=t.ns.replace(/^(-?)0+/,"$1"),t.onnuminput&&t.onnuminput.call(t)},t.setRange=(t,s,n)=>{e=t,i=s,l=n},t.addEventListener("input",()=>t.set(t.value)),t.addEventListener("paste",e=>{t.set(e.clipboardData.getData("text")),e.preventDefault()}),t},utils.formatCost=(t,e="$")=>{if(!t)return e+"0.00";let i=t.toFixed(2).split(".");return e+i[0].split("").reverse().reduce((t,e,i)=>"-"==e?e+t:e+(i&&!(i%3)?",":"")+t,"")+"."+i[1]},utils.fromDateTimeBox=t=>{let e=t.value;return e?new Date(e.replace(/-/g,"/").replace(/T/g," ")):new Date},utils.fixedNum=function(t,e,i=10){let l=Math.abs(t).toString(i).toUpperCase();return(t<0?"-":"")+(16==i?"0x":2==i?"0b":"")+"0".repeat(Math.max(e-l.length,0))+l},utils.months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],utils.toDateTimeBox=(t,e)=>t.getFullYear()+"-"+d(t.getMonth()+1)+"-"+d(t.getDate())+"T"+d(t.getHours())+":"+d(t.getMinutes())+(e?":"+d(t.getSeconds()):""),utils.formatDate=(t,e={})=>{let i="",l,s;if(null==t||!t.getDate||!((l=t.getFullYear())>1969))return"[Invalid Date]";if(null==e.time||e.time){let n=t.getHours(),r="";if(e.h24||(r=" AM",n>=12&&(r=" PM",n-=12),n||(n=12)),i=n+":"+d(t.getMinutes())+(e.sec?":"+d(t.getSeconds())+(e.ms?(t.getMilliseconds()/1e3).toFixed(Number.isFinite(e.ms)?e.ms:3).substr(1):""):""),i+=r,e.time)return i}return s=t.getDate(),s=utils.months[t.getMonth()]+" "+(null==e.suf||e.suf?utils.suffix(s):s),(null==e.year||e.year)&&e.year!==l&&(s=s+", "+l),e.df?s+(i&&" "+i):(i&&i+" ")+s},utils.suffix=t=>{let e=t%10,i=t%100;return 1==e&&11!=i?t+"st":2==e&&12!=i?t+"nd":3==e&&13!=i?t+"rd":t+"th"};let f=e;function c(t){utils.onNav&&utils.onNav.call(null,t)}function h(t,e,i){let s=this.length,n=Math.max(e<0?s+e:e||0,0),r;for(null!=i&&(s=Math.min(i<0?s+i:i,s));n<s;++n)if("!"===(r=t(this[n],n,s)))this instanceof l?this[n].remove():this.splice(n,1),--n,--s;else if(null!=r)return r}async function p(t,e,i,s=!0){let n=this.length,r=e=Math.max(e<0?n+e:e||0,0),o,u=[];for(null!=i&&(n=Math.min(i<0?n+i:i,n));r<n;++r){if(o=t(this[r],r,n),!s&&"!"!==(o=await o)&&null!=o)return o;u.push(o)}for(s&&(u=await Promise.all(u)),r=e,o=0;r<n;++r,++o)if("!"===u[o])this instanceof l?this[r].remove():this.splice(r,1),--r,--n;else if(null!=u[o])return u[o]}utils.goBack=f.back.bind(f),utils.goForward=f.forward.bind(f),utils.go=(t,e)=>{f.pushState(e,"",t||location.pathname),c(e)},r("popstate",t=>c(t.state)),r("load",()=>setTimeout(c.wrap(f.state),1)),utils.mkEl=(t,e,i,l,s)=>{let n=document.createElement(t);if(null!=i&&(n.className=i),null!=s&&(n.innerHTML=s),l&&"object"==typeof l)for(let r in l)r in n.style?n.style[r]=l[r]:n.style.setProperty(r,l[r]);return null!=e&&e.appendChild(n),n},utils.mkDiv=(t,e,i,l)=>utils.mkEl("div",t,e,i,l),utils.addText=(t,e)=>t.appendChild(document.createTextNode(e)),utils.textWidth=(e,i)=>{let l=t.TWCanvas||(t.TWCanvas=utils.mkEl("canvas")),s=l.getContext("2d");return s.font=i,s.measureText(e).width},utils.define(utils,"w",()=>innerWidth),utils.define(utils,"h",()=>innerHeight),utils.setPropSafe=(t,e,i,l=!1)=>{"string"==typeof e&&(e=e.split("."));let s=e.length-1;return(e.each(e=>{t="object"==typeof t[e]?t[e]:t[e]={}},0,s),s=e[s],l&&null!=t[s])?t[s]:t[s]=i},utils.getPropSafe=(t,e)=>{"string"==typeof e&&(e=e.split("."));try{for(let i of e)t=t[i];return t}catch(l){}},utils.proto(Array,"clean",function(t){for(let e=0,i,l=this.length;e<l;++e)i=this[e],(utils.isBlank(i)||!1===i||!t&&0===i)&&(this.splice(e--,1),l--);return this}),utils.proto(Array,"remove",function(t){let e=this.indexOf(t);return -1!=e&&(this.splice(e,1),!0)}),[Array,l,n].forEach(t=>{utils.proto(t,"each",h),utils.proto(t,"eachAsync",p)});let $="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",_=$.replace("+/","-_"),g={43:62,47:63,48:52,49:53,50:54,51:55,52:56,53:57,54:58,55:59,56:60,57:61,65:0,66:1,67:2,68:3,69:4,70:5,71:6,72:7,73:8,74:9,75:10,76:11,77:12,78:13,79:14,80:15,81:16,82:17,83:18,84:19,85:20,86:21,87:22,88:23,89:24,90:25,97:26,98:27,99:28,100:29,101:30,102:31,103:32,104:33,105:34,106:35,107:36,108:37,109:38,110:39,111:40,112:41,113:42,114:43,115:44,116:45,117:46,118:47,119:48,120:49,121:50,122:51,45:62,95:63};function m(t,e){if(null==(t=g[t.charCodeAt(e)]))throw"Bad char at "+e;return t}"toBase64"in Uint8Array.prototype||utils.proto(Uint8Array,"toBase64",function(t){let e=this.byteLength,i=e%3,l=t&&"base64url"===t.alphabet?_:$,s=0,n="",r;for(e-=i;s<e;s+=3)n+=l[(16515072&(r=this[s]<<16|this[s+1]<<8|this[s+2]))>>18]+l[(258048&r)>>12]+l[(4032&r)>>6]+l[63&r];return 1==i?(n+=l[(252&(r=this[e]))>>2]+l[(3&r)<<4],t&&t.omitPadding||(n+="=")):2!=i||(n+=l[(64512&(r=this[e]<<8|this[e+1]))>>10]+l[(1008&r)>>4]+l[(15&r)<<2],t&&t.omitPadding||(n+="==")),n}),"fromBase64"in Uint8Array||utils.proto(Uint8Array,"fromBase64",t=>{let e=t.length,i=e-1;for(;i>=0&&61===t.charCodeAt(i);--i);e=i+1,i=0;let l=e%4;if(e-=l,1==l)throw"Bad b64 len";let s=new Uint8Array(3*e/4+(l?l-1:0)),n=-1,r;for(;i<e;i+=4)r=m(t,i)<<18|m(t,i+1)<<12|m(t,i+2)<<6|m(t,i+3),s[++n]=r>>16,s[++n]=r>>8,s[++n]=r;return 2==l?s[++n]=m(t,i)<<2|m(t,i+1)>>4:3==l&&(r=m(t,i)<<10|m(t,i+1)<<4|m(t,i+2)>>2,s[++n]=r>>8,s[++n]=r),s},1);let y=/[|\\{}()[\]^$+*?.]/g,x=/-/g;function b(t,e){for(;t>=e;)t-=e;return t}function C(){let t=document.styleSheets;for(let e=0,i=t.length;e<i;++e)try{return t[e].cssRules,t[e]}catch(l){}let s=utils.mkEl("style",document.head);return utils.addText(s,""),s.sheet}function v(t){return t.replace(/[A-Z]/g,t=>"-"+t.toLowerCase())}"escape"in RegExp||utils.proto(RegExp,"escape",t=>t.replace(y,"\\$&").replace(x,"\\x2d"),1),utils.define(s.prototype,"index",function(){let t=this.parentElement;return t?Array.prototype.indexOf.call(t.children,this):-1}),utils.proto(s,"insertChildAt",function(t,e){e<0&&(e=0),e>=this.children.length?this.appendChild(t):this.insertBefore(t,this.children[e])}),utils.boundingRect=t=>new UtilRect(t.getBoundingClientRect()),utils.innerRect=t=>{let e=t.getBoundingClientRect(),i=getComputedStyle(t);return new UtilRect(e.top+parseFloat(i.paddingTop)+parseFloat(i.borderTopWidth),e.bottom-parseFloat(i.paddingBottom)-parseFloat(i.borderBottomWidth),e.left+parseFloat(i.paddingLeft)+parseFloat(i.borderLeftWidth),e.right-parseFloat(i.paddingRight)-parseFloat(i.borderRightWidth))},utils.define(s.prototype,"boundingRect",function(){return utils.boundingRect(this)}),utils.define(s.prototype,"innerRect",function(){return utils.innerRect(this)}),Math.cot=t=>1/Math.tan(t),utils.isBlank=t=>null==t||("string"==typeof t?!/\S/.test(t):"object"==typeof t&&("number"==typeof t.length?0===t.length:0===Object.keys(t).length)),utils.firstEmpty=t=>{let e=t.length;for(let i=0;i<e;++i)if(null==t[i])return i;return e},utils.firstEmptyChar=t=>{let e=Object.keys(t),i=e.length;for(let l=0;l<i;++l)if(null==t[e[l]])return e[l];return utils.numToChar(i)},utils.numToChar=t=>{if(t<=25)return String.fromCharCode(t+97);if(t>=26&&t<=51)return String.fromCharCode(t+39);let e,i;if(t<2756)e=b(Math.floor(t/52)-1,52),i=b(t,52);else if(t<143364)e=b(Math.floor((t-52)/2704)-1,52),i=b(t-52,2704)+52;else{if(!(t<7454980))return!1;e=b(Math.floor((t-2756)/140608)-1,52),i=b(t-2756,140608)+2756}return utils.numToChar(e)+utils.numToChar(i)},utils.merge=function(t){for(let e=1,i=arguments.length,l,s,n;e<i;++e)for(let r in l=arguments[e]){if(s=t[r],n=l[r],s&&n){if(s.length>=0&&n.length>=0){for(let o=0,u=n.length,a=s.length;o<u;++o)s[o+a]=n[o];continue}if("object"==typeof s&&"object"==typeof n){for(let d in n)s[d]=n[d];continue}}t[r]=n}return t},utils.bounds=(t,e=0,i=1)=>t>=e?t<=i?t:i:e,utils.norm=utils.normalize=(t,e=0,i=1)=>{let l=Math.abs(i-e);if(t<e)for(;t<e;)t+=l;else for(;t>=i;)t-=l;return t},utils.cutStr=(t,e)=>{let i;for(;-1!=(i=t.indexOf(e));)t=t.slice(0,i)+t.slice(i+e.length);return t},utils.dCut=(t,e,i,l,s)=>{let n=t.indexOf(e,s||void 0)+e.length,r=t.indexOf(i,n);return l&&(l.s=n,l.t=r),n<e.length||r<=n?"":t.substring(n,r)},utils.dCutToLast=(t,e,i,l,s)=>{let n=t.indexOf(e,s||void 0)+e.length,r=t.lastIndexOf(i);return l&&(l.s=n,l.t=r),n<e.length||r<=n?"":t.substring(n,r)},utils.dCutLast=(t,e,i,l,s)=>{let n=t.lastIndexOf(e,s||void 0)+e.length,r=t.indexOf(i,n);return l&&(l.s=n,l.t=r),n<e.length||r<=n?"":t.substring(n,r)},utils.parseCSS=t=>{let e={},i="",l=0;function s(t){if(-1!==t.indexOf(",")){let e=utils.clean(t.split(","));for(let i=0,l=e.length;i<l;++i)e[i]=e[i].trim();return e}return t.trim()}for(t=t.trim();t.length>0;)if("("==t[0]&&-1!==t.indexOf(")")&&i){let n=t.indexOf(")"),r=t.substring(1,n);e[i]=s(r),i="",t=t.substring(n+1)}else if(0==t.search(/[#!\w]/)){i&&(e[l++]=i);let o=t.search(/[^#!\w-%]/);-1==o&&(o=t.length),i=t.substring(0,o),t=t.substring(o)}else t=t.substring(1);return i&&(e[l]=i),e},utils.buildCSS=t=>{let e=Object.keys(t),i=e.length,l="",s=0;for(;s<i;){let n=e[s],r=t[e[s]];++s,0<=Number(n)?l+=r+" ":l+=`${n}(${r}) `}return l.substring(0,l.length-1)},utils.addClass=(t,e)=>{let i=C(),l=Object.keys(e),s="";for(let n=0,r=l.length;n<r;++n)s+=v(l[n])+":"+e[l[n]]+";";i.addRule("."+t,s)},utils.addId=(t,e)=>{let i=C(),l=Object.keys(e),s="";for(let n=0,r=l.length;n<r;++n)s+=v(l[n])+":"+e[l[n]]+";";i.addRule("#"+t,s)},utils.addKeyframe=(t,e)=>{C().addRule("@keyframes "+t,e)},utils.removeSelector=t=>{for(let e=0,i,l,s=document.styleSheets.length;e<s;++e){i=document.styleSheets[e];try{l=i.cssRules}catch(n){continue}for(let r in l)"CSSStyleRule"==l[r].constructor.name&&l[r].selectorText==t&&i.deleteRule(r)}},utils.getId=(t,e)=>(e||document).getElementById(t),utils.getClassList=(t,e)=>(e||document).getElementsByClassName(t),utils.getClassFirst=(t,e)=>(t=utils.getClassList(t,e)).length>0?t[0]:0,utils.getClassOnly=(t,e)=>1==(t=utils.getClassList(t,e)).length?t[0]:0,utils.hexToRgb=t=>{let e=parseInt(t.substr(1),16);return[e>>16&255,e>>8&255,255&e]},utils.rand=(t,e,i,l)=>{e*=i=i||1,t*=i;let s=Math.random();return Math.round((l?l(s):s)*(e-t)+t)/i},utils.fromQuery=t=>{function e(t,i){let l=i[0],s=l.indexOf("="),n=decodeURIComponent(l.substr(0,s)),r=decodeURIComponent(l.substr(s+1));return null==t[n]?t[n]=r:"array"==typeof t[n]?t[n].push(r):t[n]=[t[n],r],1==i.length?t:e(t,i.slice(1))}return t.startsWith("?")&&(t=t.substr(1)),0==t.length?{}:e({},t.split("&"))},utils.toQuery=t=>{let e="";if("object"!=typeof t)return encodeURIComponent(t);for(let i in t){let l=t[i];"object"==typeof l&&(l=JSON.stringify(l)),e+="&"+i+"="+encodeURIComponent(l)}return e.slice(1)},utils.center=(t,e,i)=>{let l=t.style;if(l.position||(l.position="absolute"),"calc"==i)e&&"x"!=e||(l.left=`calc(50% - ${t.clientWidth/2}px)`),e&&"y"!=e||(l.top=`calc(50% - ${t.clientHeight/2}px)`);else if("move"==i)e&&"x"!=e||(l.left=`${utils.w/2-t.clientWidth/2}px`),e&&"y"!=e||(l.top=`${utils.h/2-t.clientHeight/2}px`);else if("trans"==i){let s=utils.cutStr(l.transform,"translateX(-50%)");s=utils.cutStr(s,"translateY(-50%)"),e&&"x"!=e||(l.left="50%",s+="translateX(-50%)"),e&&"y"!=e||(l.top="50%",s+="translateY(-50%)"),s&&(l.transform=s)}else utils.mkEl("div",t.parentNode,null,{display:"table",position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendChild(t),l.display="table-cell",e&&"x"!=e||(l.textAlign="center"),e&&"y"!=e||(l.verticalAlign="middle"),l.position="relative"},utils.loadAjax=(t,e,i,l,s)=>{let n;try{n=new XMLHttpRequest}catch(r){return e(r)}if(s)for(let o in s)n.setRequestHeader(o,s[o]);n.open(i||"GET",t),n.onreadystatechange=()=>{let t=n.status||-1;n.readyState==n.DONE&&e(200==t?0:t,n.response,n)},n.send(l||void 0)},utils.loadFile=(t,e,i)=>{let l=utils.mkEl("object",document.body,null,{position:"fixed",opacity:0});l.data=t;let s=setTimeout(()=>{l.remove(),s=null,e(!1)},i||4e3);l.onload=()=>{s&&(clearTimeout(s),e(l.contentDocument.documentElement.outerHTML),l.remove())}},utils.loadJSONP=(t,e,i)=>{let l=utils.mkEl("script",document.head),s=utils.firstEmptyChar(utils.lJSONCall);l.type="application/javascript",l.src=t+(-1==t.indexOf("?")?"?":"&")+"callback=utils.lJSONCall."+s;let n=setTimeout(()=>{delete utils.lJSONCall[s],e(!1)},i||4e3);utils.lJSONCall[s]=t=>{n&&clearTimeout(n),delete utils.lJSONCall[s],e(t)},document.head.removeChild(l)},utils.lJSONCall=[],utils.dlFile=(t,e)=>fetch(e).then(t=>{if(200!=t.status)throw"Code "+t.status;return t.blob()}).then(e=>{utils.dlData(t,e)}),utils.dlData=(t,e)=>{let i,l=utils.mkEl("a",document.body,null,{display:"none"});"string"==typeof e?i=e:(e instanceof Blob||(e=Blob(e)),i=URL.createObjectURL(e)),l.href=i,l.download=t,l.click(),l.remove(),URL.revokeObjectURL(i)},utils.deg=t=>180*t/Math.PI,utils.rad=t=>t*Math.PI/180,utils.map=(t,e,i,l,s,n)=>{let r=(t-e)/(i-e);return(n?n(r):r)*(s-l)+l},utils.delay=t=>new Promise(e=>setTimeout(e,t)),UtilRect=function(t,e,l,s){if(!(this instanceof UtilRect))return new UtilRect(t,e,l,s);let n=Number.isFinite,r=0,o=0,u=0,a=0;utils.define(this,"x",()=>u,t=>{n(t)&&(a+=t-u,u=t)}),utils.define(this,"y",()=>r,t=>{n(t)&&(o+=t-r,r=t)}),utils.define(this,"top",()=>r,t=>{r=n(t)?t:0}),utils.define(this,["bottom","y2"],()=>o,t=>{o=n(t)?t:0}),utils.define(this,"left",()=>u,t=>{u=n(t)?t:0}),utils.define(this,["right","x2"],()=>a,t=>{a=n(t)?t:0}),utils.define(this,["width","w"],()=>a-u,t=>{a=t>=0?u+t:0}),utils.define(this,["height","h"],()=>o-r,t=>{o=t>=0?r+t:0}),utils.define(this,"centerX",()=>u/2+a/2),utils.define(this,"centerY",()=>r/2+o/2),t instanceof i||t instanceof UtilRect?(r=t.top,o=t.bottom,u=t.left,a=t.right):(r=t,o=e,u=l,a=s)},utils.proto(UtilRect,"contains",function(t,e){return t instanceof s?this.contains(t.boundingRect):t instanceof UtilRect?t.x>=this.x&&t.x2<=this.x2&&t.y>=this.y&&t.y2<=this.y2:t>=this.x&&t<=this.x2&&e>=this.y&&e<=this.y2}),utils.proto(UtilRect,"overlaps",function(t){if(t instanceof s)return this.overlaps(t.boundingRect);if(!(t instanceof UtilRect))return 0;let e,i;return e=t.x2-t.x>=this.x2-this.x?this.x>=t.x&&this.x<=t.x2||this.x2>=t.x&&this.x2<=t.x2:t.x>=this.x&&t.x<=this.x2||t.x2>=this.x&&t.x2<=this.x2,i=t.y2-t.y>=this.y2-this.y?this.y>=t.y&&this.y<=t.y2||this.y2>=t.y&&this.y2<=t.y2:t.y>=this.y&&t.y<=this.y2||t.y2>=this.y&&t.y2<=this.y2,e&&i}),utils.proto(UtilRect,"dist",function(t,e){if(t instanceof s)return this.dist(t.boundingRect);let i=t instanceof UtilRect;return e=Math.abs((i?t.centerY:e)-this.centerY),Math.sqrt((t=Math.abs((i?t.centerX:t)-this.centerX))*t+e*e)}),utils.proto(UtilRect,"expand",function(t){return this.top-=t,this.left-=t,this.bottom+=t,this.right+=t,this})})();const Easing={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>t*(2-t),easeInOutQuad:t=>t<.5?2*t*t:-1+(4-2*t)*t,easeInCubic:t=>t*t*t,easeOutCubic:t=>--t*t*t+1,easeInOutCubic:t=>t<.5?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1,easeInQuart:t=>t*t*t*t,easeOutQuart:t=>1- --t*t*t*t,easeInOutQuart:t=>t<.5?8*t*t*t*t:1-8*--t*t*t*t,easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>1+--t*t*t*t*t,easeInOutQuint:t=>t<.5?16*t*t*t*t*t:1+16*--t*t*t*t*t};"undefined"!=typeof global&&(global.utils=utils,global.UtilRect=UtilRect,global.Easing=Easing);
|