zimjs
Version:
JavaScript Canvas Framework - Code Creativity!<br> https://zimjs.com
1,431 lines (1,217 loc) • 4.03 MB
JavaScript
/*! ZIM - JavaScript Canvas Framework - Code Creativity
https://zimjs.com (c) 2023 modified MIT License
Donations welcome https://zimjs.com/donate
Node module - also see ES6 modules at https://zimjs.com/cdn
*/
// A zim namespace can be turned on with zns=true in a script before calling ZIM
// There is an ES6 module version available at https://zimjs.com/es6
// We have provided Node packages for ZIM and CreateJS at https://zimjs.com/npm
// There is ZIM SHIM for Adobe Animate at https://zimjs.com/animate
// There are templates for React and VUE at https://dev.zimjs.com
// We are happy to help with integration - join us on https://zimjs.com/slack
// We would recommend you try ZIM on its own.
// It is just so easy to paste the template into a text editor and go.
// Leave all your outside worries behind and simply enjoy coding with ZIM!
// Let us handle the complexities as a scroll through the code will show! ;-)
// Cheers,
// Dr Abstract, Pragma and ZIM Team
// With thanks...
// Thanks to Yoan Herrera for the NPM / Vue, Svelte, React and Angular
// templates at https://github.com/yoanhg421/zimjs-templates
// Thanks to ZzFX - Zuper Zmall Zound Zynth - Micro Edition for play() method of Synth
// MIT License - Copyright 2019 Frank Force - https://github.com/KilledByAPixel/ZzFX
// MIT License - Copyright 2021 George Francis - spline - https://github.com/georgedoescode
// Thanks to Josh Forisha - https://github.com/joshforisha for open-simplex-noise-js
// Thanks to David Bau - see copyright at http://davidbau.com/encode/seedrandom.js
// ZIM converted Noise() from https://www.npmjs.com/package/open-simplex-noise
// Thanks to Frank Los for coding the ZIM Keyboard()
// Thanks to Karel Rosseel for proof-reading the site
// Thanks Ami Hanya and team for such prolific work with ZIM and all the suggestions
// Of course, thanks to all the ZIM users - it is always nice to hear from you
// at https://forum.zimjs.com and https://zimjs.com/discord
// There are several dozen thanks through out the code as well - cheers!
// This is used for the build but in a browser would be
// import createjs from "https://zimjs.org/cdn/1.4.0/createjs";
import "@zimjs/createjs";
var WW = window||{}; // minify does not shorten window so this saves 1.5k
var createjs = WW.createjs; // the expected CreateJS namespace - import CreateJS first
var zns = WW.zns; // sets from outside whether zim namespace is to be used
var zon = WW.zon==null?true:WW.zon; // sets from outside whether zog() will output to console
var zimBlob; // stores zim.Blob (and zim.Window below) to not obscure JS Blob and Window on export
var zimWindow; // where we use export {zimBlob as Blob} and {zimWindow as Window}
var z_i; // iterator i in global namespace
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ZIM CODE
// INTRODUCTION
// The code is broken into modules that once were individually available.
// We then introduced ZIM Distill (tree-shaking) as a more efficient way to reduce code
// but we have kept the modules as follows:
// WRAP, CODE, DISPLAY, METHODS, CONTROLS, FRAME, META, and docs only: GAME, THREE, SOCKET, CAM, PIZZAZZ
// You can text search a bunch of ////... to get to these.
// The docs also have these modules but ordered differently
// FRAME, DISPLAY, METHODS, CONTROLS, CODE, WRAP, META, GAME, THREE, SOCKET, PIZZAZZ.
// Each entry in the Docs has a VIEW button at the bottom
// that will nicely display the code for that entry - from this document.
// So... there is perhaps, little need to be here ;-).
// The docs are created from the description in this document.
// https://zimjs.com/docs.html
// WARNING: the WRAP and the CODE module at the start here
// are not the focus of the ZIM Framework but rather helper functions
// and even some helper functions for DOM manipulation.
// You may want to skip ahead using //// with a space after to find your way ;-)
//////////////// ZIM WRAP //////////////
// Zim Wrap creates global wrapper functions for less typing
/*--
zog(item1, item2, etc.) ~ log
zog
global function
DESCRIPTION
Short version of console.log()
to log the item(s) to the console.
Use F12 to open your Browser console.
zog is dedicated to Pragma (Madeline Zen) who was coding with Dr Abstract (Dan Zen) from the start
Also comes in seven ZIM colors:
zogg("green");
zogp("pink");
zogb("blue");
zogr("red");
zogy("yellow");
zogo("orange");
zogs("salmon");
Note: If zon (comments on) is set to false before ZIM runs, then all zog() commands are turned off
EXAMPLE
zog("hello"); // logs hello to the console
END EXAMPLE
EXAMPLE
const circle = new Circle().center();
zogb(circle); // logs the circle object to the console with a blue Z marker
END EXAMPLE
EXAMPLE
const x = 10;
const y = 20;
zogo(x, y); // 10 20 with an orange Z marker
END EXAMPLE
PARAMETERS
item1, item2 (optional), etc. - items (expressions) to log to the console
RETURNS items it is logging separated by a space if more than one
--*///+0
// reported a bug in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1280818
// that after FF 46 binding the console did not show file and line number
// this is fixed in FF 50 - quite the conversation this stirred
var zog = zon?console.log.bind(console):function(){};
// Thanks Ami and Yalon for the style technique
var zogStyle = " border:thin solid black; color: black";
var zogg = zon?console.log.bind(console, "%c Z ", "background: #acd241;"+zogStyle):function(){};
var zogp = zon?console.log.bind(console, "%c Z ", "background: #e472c4;"+zogStyle):function(){};
var zogb = zon?console.log.bind(console, "%c Z ", "background: #50c4b7;"+zogStyle):function(){};
var zogr = zon?console.log.bind(console, "%c Z ", "background: #fb4758;"+zogStyle):function(){};
var zogy = zon?console.log.bind(console, "%c Z ", "background: #ebcb35;"+zogStyle):function(){};
var zogo = zon?console.log.bind(console, "%c Z ", "background: #f58e25;"+zogStyle):function(){};
var zogs = zon?console.log.bind(console, "%c Z ", "background: #fa8072;"+zogStyle):function(){};
var zogl = zon?console.log.bind(console, "%c Z ", "background: #eeeeee;"+zogStyle):function(){};
var zogd = zon?console.log.bind(console, "%c Z ", "background: #444444; border:thin solid black; color: white"):function(){};
//-0
/*--
zid(string) ~ id
zid
global function
DESCRIPTION
Short version of document.getElementById(string)
to access an HTML tag by its id.
EXAMPLE
zid("logo").addEventListener("click", ()=>{});
END EXAMPLE
PARAMETERS
string - the id of the tag you are wanting to access
RETURNS HTML tag with id of string or null if not found
--*///+1
function zid(s) {
z_d("1");
return document.getElementById(s);
} //-1
/*--
zss(string) ~ css
zss
global function
DESCRIPTION
Short version of document.getElementById(string).style
to access the style property of an HTML tag by the tag id.
EXAMPLE
zss("logo").margin = "10px";
END EXAMPLE
PARAMETERS
string - the id of the tag whose style you are wanting to access
RETURNS style property of HTML tag with id of string or undefined if not found
--*///+2
function zss(s) {
z_d("2");
if (document.getElementById(s)) {return document.getElementById(s).style;}
else if (zon) zogy("zss(): id not found");
} //-2
/*--
zgo(url, target, width, height, fullscreen, modal) ~ go
zgo
global function
DESCRIPTION
Short version of either window.location.href or window.open
to open a link in the same window or a specified window.
EXAMPLE
zid("logo").addEventListener("click", ()=>{zgo("https://zimjs.com");});
// with a ZIM object:
const button = new Button();
button.center();
button.on("click", ()=>{zgo("https://zimjs.com");});
END EXAMPLE
EXAMPLE
// all on one line and open url in new tab
new Button().center().tap(()=>{zgo("https://zimjs.com", "_blank");})
END EXAMPLE
PARAMETERS
url - the link to use (Absolute, Relative or Virtual)
target - (default null) the string name of a window (tab) _blank for new window each time
width - (default null) width of window (use with fullscreen true)
height - (default null) height of window (use with fullscreen true)
fullscreen - (default null) not really full screen but rather opens in a new window not tab
modal - (default false) set to true to force user to close window
RETURNS null if opening in same window or reference to the window otherwise
--*///+3
function zgo(u,t,w,h,f,m) {
z_d("3");
if ((zot(t) && t != "") || t == "_self") {
WW.location.href = u;
} else {
var added = "";
if (w) added += "width=" + w + ",";
if (h) added += "height=" + h + ",";
if (f) added += "fullscreen=yes,";
if (m) added += "modal=yes,alwaysRaised=yes";
return WW.open(u,t,added);
}
} //-3
/*--
zum(string) ~ num
zum
global function
DESCRIPTION
Takes the units off a string number.
Converts "10px" string from styles to number 10, for instance.
If there is no value then this will return 0.
EXAMPLE
// in HTML
<div id="logo" style="position:relative; left:10px">LOGO</div>
// in JavaScript
let left = zum(zss("logo").left); // converts 10px to the Number 10
left += 20; // adds 20 to 10
zss("logo").left = left + "px"; // assigns 30px to left style
END EXAMPLE
PARAMETERS
string - the string representation of a number eg. "10px"
RETURNS a Number
--*///+4
function zum(s) {
z_d("4");
if (zot(s)) return;
return Number(String(s).replace(/[^\d.-]/g, ''));
} //-4
/*--
zot(value) ~ not
zot
global function
DESCRIPTION
Test to see if value has no value (value must exist as var or parameter)
or if value has been set to null.
Good for setting function defaults.
Really just asking if the value == null.
Often we forget exactly how to do this - it is tricky:
value === null, value == undefined, value == 0, !value DO NOT WORK.
EXAMPLE
if (zot(width)) width = 100;
// equivalent to
if (width == null) width = 100;
END EXAMPLE
PARAMETERS
value - a variable or parameter you want to see if there is no value assigned
RETURNS Boolean true if value does not exist
--*///+4.5
function zot(v) {
return v==null; // both null and undefined match but not false or 0
}//-4.5
/*--
zop(e) ~ stop
zop
global function
DESCRIPTION
Stop event propagation to subsequently added existing listeners.
Must pass it e || window.event from your event function.
NOTE: this is not canceling the default action -
to cancel default action use e.preventDefault();
EXAMPLE
zid("button").addEventListener("click", e=>{
// do something
zop(e||window.event);
});
END EXAMPLE
PARAMETERS
e - the event object from your event function
collect the event object as e and then pass in e || window.event
RETURNS null
--*///+5
function zop(e) {
z_d("5");
if (zot(e)) return;
if (e.stopImmediatePropagation) e.stopImmediatePropagation();
if (WW.event) WW.event.cancelBubble=true;
} //-5
/*--
zil() ~ still
zil
global function
DESCRIPTION
Stop keys from moving content - arrows, spacebar, pgup, pgdown, home, end.
Stop scroll wheel from moving content - scrolling the canvas for instance.
ZIM Frame does this in the full, fit and outside scale modes.
If not using Frame, then you can do this once at the start of your code.
Returns an array of references to three listeners: [keydown, wheel and DOMMouseScroll].
Use these to removeEventListeners.
Also stores these on Window at window.zilK, window.zilW, window.zilS.
The arrows, etc, still work but just not their default window behaviour.
EXAMPLE
// at the top of your code
let listenersArray = zil();
// key and mousewheel arrows, spacebar, etc.
// will have their default actions stopped until you remove the listeners:
// window.removeEventListener("keydown", listenersArray[0]); // etc.
END EXAMPLE
RETURNS an Array
--*///+6
function zil() {
z_d("6");
if (WW.zilK) { // do not double apply
WW.removeEventListener("keydown", WW.zilK);
WW.removeEventListener("wheel", WW.zilW);
WW.removeEventListener("DOMMouseScroll", WW.zilS);
}
WW.zilK = function(e) {if (!e) e = event; if (e.keyCode && (e.keyCode >= 32 && e.keyCode <= 40)) e.preventDefault();};
WW.zilW = function(e) {if (!e) e = event; e.preventDefault();};
WW.zilS = WW.zilW;
WW.addEventListener("keydown", WW.zilK, {passive:false});
WW.addEventListener("wheel", WW.zilW, {passive:false});
WW.addEventListener("DOMMouseScroll", WW.zilS, {passive:false});
return [WW.zilK, WW.zilW, WW.zilS];
} //-6
/*--
zet(selector, first) ~ set
zet
global function
DESCRIPTION
Uses document.querySelectorAll() to get a list of tags.
Returns a ZIM Zet object which can be used to add events or styles to the set.
EXAMPLE
zet(".class").on("click", ()=>{}); // would add function event to all tags with the class
zet("p").css("color", "goldenrod"); // would make the text of all paragraphs goldenrod
zet("#test").css({color:"red", "background-color":"blue", paddingLeft:"20px"});
// set a custom open property on all section bars to false
zet("section .bar").prop("open", false);
// set the custom open property on all section bars to true and set the innerHTML to CLOSE
zet("section .bar").prop({open: true, innerHTML: "CLOSE"});
END EXAMPLE
PARAMETERS
selector - a CSS query selector such as a class, id, tag, or multiple selectors separated by commands
can also be complex selectors suchs as ".class img"
first - get the first occurance only of the set
METHODS (on the returned Zet object)
zet(selector).on(type, function) - a shortcut for addEventListener() and will be added to all tags matching the selector
zet(selector).off(type, function) - a shortcut for removeEventListener() and will be remove from all tags matching the selector
zet(selector).css(property, value) - gets and sets styles
- gets the first programmatic property if a single string property is passed
- sets the property to the value on each of the Zet's tags from the selector passed to zet()
- if an object of properties and values is passed as the single parameter then sets all these properties
- NOTE: style names do not need quotes unless the dash is used - so camelCase does not require quotes
- NOTE: remember that commas are used for objects - not the semi-colon as in CSS
zet(selector).prop(property, value) - gets or sets a property on a set of tags
- if an object of properties and values is provided as a single parameter, then sets all these on the set
- else if no value is set then returns an array of the set tags values for the property
- else if value is a single value then sets the property of the tags in the set to the value
PROPERTIES (on the returned Zet object)
tags - an HTML NodeList tag list
RETURNS Zet object with on(), off(), css() methods and tags property (NodeList tag list)
--*///+6.1
function zet(selector, first) {
z_d("6.1");
function Zet() {
var that = this;
this.on = function(type, call) {
if (zot(selector) || zot(type) || zot(call)) return;
var tags = that.tags;
for (var i=0; i<tags.length; i++) {
tags[i].addEventListener(type, call);
}
};
this.off = function(type, call) {
if (zot(selector) || zot(type) || zot(call)) return;
var tags = that.tags;
for (var i=0; i<tags.length; i++) {
tags[i].removeEventListener(type, call);
}
};
Object.defineProperty(that, 'tags', {
get: function() {
if (zot(selector)) return [];
if (typeof selector == 'string' || selector instanceof String) {
if (first) zogo(selector, document.querySelector(selector))
if (first) return [document.querySelector(selector)];
else return document.querySelectorAll(selector);
} else { // selector is already an object - assume a tag
if (typeof (selector).innerHTML == "string") return [selector];
return [];
}
},
set: function() {
}
});
this.css = function(property, value) {
// if property is object then assign all props in object
var tags = that.tags;
for (var i=0; i<tags.length; i++) {
if (arguments.length == 1 && arguments[0].constructor === {}.constructor) {
for (var p in property) {
tags[i].style[p] = property[p];
}
} else if (arguments.length == 1) {
return that.tags[0].style[property];
} else {
tags[i].style[property] = value;
}
}
};
this.prop = function(property, value) {
if (zot(property)) return;
var tags = that.tags;
var a = [];
for (var i=0; i<tags.length; i++) {
if (zot(value)) {
if (property.constructor === {}.constructor) {
for (var p in property) {
tags[i][p] = property[p];
}
} else {
a.push(tags[i][property]);
}
} else {
tags[i][property] = value;
}
}
if (zot(value)) return a;
};
}
return new Zet();
} //-6.1
/*--
zob(func, args, sig, scope) ~ object
zob
global function
DESCRIPTION
A system to build functions or classes that allow traditional parameters
or a configuration object passed in as a single parameter.
The configuration object has property names that match the function arguments.
To use zob on your own functions, pass in a function and the function's arguments
and insert zob into first line of your function as shown below.
Replace yourFunction with a reference to your function but keep arguments as is.
NOTE: in ES6 classes when extending a ZIM class,
do not use ZIM DUO when passing in parameters to super()
or else the methods might be obscured.
EXAMPLE
function test(a,b,c){
let duo; if (duo = zob(test, arguments)) return duo;
};
test(1,null,3); // traditional parameters in order
test({a:1,c:3}); // configuration object with zob
END EXAMPLE
NOTE: if you are running the function as a constructor with the new keyword
then you need to pass in this (keyword) as the last parameter (sig can be null)
this allows zob() to test to see if we need to rerun the function as a constructor
EXAMPLE
let duo; if (duo = zob(yourFunction, arguments, sig, this)) return duo;
END EXAMPLE
NOTE: if using an ES6 Class or minifying the file then you need to do an extra step
add a string version of the signature of your function above the duo call
then pass the signature in as a parameter to zob()
EXAMPLE
class Test extends Container {
constructor(a=1,b=2,c=3) {
super();
const sig = "a,b,c";
let duo; if (duo = zob(Test, arguments, sig, this)) return duo;
}
}
END EXAMPLE
many of the ZIM functions and classes use this "DUO" technique
the documentation for parameters will tell you if they support DUO
works also with JS6 default parameter values
PARAMETERS
func - reference to the function you want to use params or a config object with
args - reference to the arguments property of the function (literally, use "arguments" with no quotes)
sig - (default null) a string listing of the parameters just how they are in the () not including the ()
required if you are minifying the file as minifying changes the signature
scope - (default null) reference to this (litterally, use "this" without the quotes)
required if the function is being run with the new keyword
RETURNS um... a Boolean
--*///+7
function isDUO(a) {return a.length == 1 && a[0] != undefined && a[0].constructor === {}.constructor;}
function zob(func, args, sig, scope) {
var zimon = (zim && (zim.ZIMONON || ZIMONON));
if (isDUO(args)) {
z_d("7");
var zp = args[0];
var zm; for (var z in zp) {if (zm=z.match(/^(.+)gColor$/)) {zp[zm[1]+"ackgroundColor"] = zp[z]; delete zp[z];}}
var za = (zot(sig))?func.toString().split(/\n/,1)[0].match(/\((.*)\)/)[1].replace(/\s+/g,"").split(","):sig.replace(/\s+/g,"").split(",");
var zv = []; var zi; var zt;
for (zi=0; zi<za.length; zi++) {zt=za[zi].split("=")[0]; za[zi]=zt; zv.push(zp[zt]);}
for (zi in zp) {if (za.indexOf(zi)<0) {if (zon) zogy(func,"bad argument "+zi);}}
var zr; if (zr=(Object.prototype.isPrototypeOf.call(func.prototype,scope))?new (func.bind.apply(func,[null].concat(zv)))():func.apply(null,zv)) {if (zimon && !zr.arguments) {zr.arguments = [zp];} return zr;} else {return true;}
} else {
if (zimon && scope && args && zot(scope.arguments)) {
scope.arguments = Array.prototype.slice.call(args);
}
}
}//-7
/*--
zik(Array|function|object|Pick) ~ pick
zik
global function
DESCRIPTION - DEPRECIATED as of ZIM 10
Has now been replaced with ZIM Pick() class which formalizes zik() and ZIM VEE into a more general class.
See ZIM Pick() under the Code module Classes.
--*///+7.5
function zik(v) {
z_d("7.5");
z_d("17.6");
if (zot(v)) return;
return zim.Pick.choose(v);
}//-7.5
/*--
zta(item1, item2, etc.) ~ table
zta
global function
DESCRIPTION
Short version of console.table()
to log the Arrays or Objects to the console as a table
Use F12 to open your Browser console.
Note: if zon (comments on) is set to false before ZIM runs then all zta() commands will be turned off
EXAMPLE
zta(["will", "this", "wind"]); // logs as a table
END EXAMPLE
PARAMETERS
item1, item2 (optional), etc. - Arrays or Objects to log to the console
RETURNS items it is logging
--*///+7.6
var zta = console.table&&zon?console.table.bind(console):function(){};
//-7.6
/*--
zor(item1, item2, etc.) ~ or
zor
global function
DESCRIPTION
picks the first non-null defined item so WILL pick a 0
EXAMPLE
const a = 0;
const x = zor(a,10); // x is 0
const y = zor(x,10); // y is 0
// note:
const z = a||10; // z is 10
const c;
const w = zor(c,a,20); // w is 0
END EXAMPLE
PARAMETERS
item1, item2 (optional), etc. - objects to test in order
RETURNS first non-null defined item or undefined if no valid items
--*///+7.7
var zor = function() {
for(var i=0; i<arguments.length; i++) {
if (!zot(arguments[i])) return arguments[i];
}
};
//-7.7
// the above functions are global for quick usage
// start the zim module pattern - from here on, everything is stored on the zim namespace
var zim = function(zim) {
//////////////// ZIM CODE //////////////
// Zim Code adds some general code functionality along with Browser and DOM code
// some of these are common Web solutions over the years (sorry for lack of credit)
// SUBSECTION FEATURED
/*--
zim.chop = function(obj, cols, rows, tile, margin, scale)
chop
zim function
DESCRIPTION
Breaks up any DisplayObject into a grid of cols and rows and returns Tile or an array of Bitmap objects.
Handy to pass to a Scrambler().
See https://zimjs.com/explore/chop.html
See https://zimjs.com/cat/scrambler.html
NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
EXAMPLE
// A chopped Circle animating like pixels
chop(new Circle(200,red),20,20)
.center()
.noMouse()
.animate({
props:{alpha:0},
time:{min:.1, max:.4},
rewind:true,
loop:true,
sequence:.02
});
END EXAMPLE
EXAMPLE
// preload the pic in Frame() or F.loadAssets() assets parameter
new Scrambler(chop(new Pic("pic.jpg"),4,4)).center();
END EXAMPLE
PARAMETERS
obj - a ZIM DisplayObject like an asset() or Circle(), etc.
cols - the number of cols to break object into
rows - the number of rows to break object into
tile - (default true) return a Tile - set to false to return an array of Bitmaps
margin - (default 0) add a margin to image - will be outside bounds
scale - (default 1) set the scale - will scale vectors as caching to preserve quality
RETURNS a Tile or an array of Bitmaps depending on tile parameter
--*///+7.8
zim.chop = function(obj, cols, rows, tile, margin, scale) {
z_d("7.8");
if (zot(obj)) return;
if (zot(cols)) cols = 3;
if (zot(rows)) rows = 3;
if (zot(tile)) tile = true;
if (zot(margin)) margin = 0;
if (zot(scale)) scale = 1;
// removed in ZIM ZIM 01 patch - causing parts to be same size canvas as original
// if (obj.uncache) { // testing if it has a CreateJS uncache method
// obj = obj.cache(null,null,null,null,scale).cacheCanvas;
// }
// var w = obj.width/cols;
// var h = obj.height/rows;
if (!obj.getBounds) obj = new zim.Bitmap(obj); // added ZIM ZIM 02
var b = obj.getBounds();
if (zot(b)) zogy("ZIM chop() - obj must have bounds");
var w = b.width/cols;
var h = b.height/rows;
var m = margin;
var pieces = [];
zim.loop(rows, function (r) {
zim.loop(cols, function (c) {
var p = new zim.Bitmap(obj, w+m*2, h+m*2, c*w-m, r*h-m, scale);
if (margin != 0) {
p.setBounds(m,m,w,h);
p.regX = m;
p.regY = m;
} else {
p.setBounds(m,m,w,h);
}
pieces.push(p);
});
});
if (tile) return new zim.Tile(pieces,cols,rows,0,0,true).sca(obj.scaleX,obj.scaleY);
else return pieces;
};//-7.8
/*--
zim.shuffle = function(array, different)
shuffle
zim function
DESCRIPTION
Randomly shuffles elements of an array.
Actually changes the original array (and also returns it).
NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
EXAMPLE
const array = ["happy", "sad", "spooked"];
const randomFromArray = shuffle(array)[0];
// this will be randomized each time it is run
// or use ZIM pluck()
END EXAMPLE
EXAMPLE
const array = shuffle(["happy", "sad", "spooked"]);
loop(array, feeling=>{zog(feeling);});
// this will get random and unique elements of the array
END EXAMPLE
PARAMETERS
array - the Array to shuffle - or pass in any number of parameters which will be turned into an array and shuffled
different - (default false) set to true to make sure the resulting array is different in some way from the original
note: make sure to pass in an array for the first parameter
RETURNS the modified Array
--*///+8
zim.shuffle = function(array, different) {
z_d("8");
if (zot(array)) return;
if (!Array.isArray(array)) array = Object.values(arguments);
var i = array.length, j, temp;
if (i <= 1) return array;
if (different) {
var sur = {};
var arr = [];
for (var i=0; i<array.length; i++) {
sur[i] = array[i];
arr.push(i);
}
var original = zim.copy(arr);
zim.shuffle(arr);
while (zim.arraysEqual(original, arr)) {
zim.shuffle(arr);
}
for (i=0; i<arr.length; i++) {
array[i] = sur[arr[i]];
}
return array;
} else {
while(--i) {
j = Math.floor(Math.random()*(i+1));
temp=array[i];
array[i]=array[j];
array[j]=temp;
}
return array;
}
};//-8
/*--
zim.pluck = function(array, remove)
pluck
zim function
DESCRIPTION
Returns a random element from an array.
Same as array[Math.floor(Math.random()*array.length)].
Or if remove is true (default is false) then returns and removes random element from array.
NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
EXAMPLE
const colors = [orange, green, red];
zog(pluck(colors)); // orange, green or red
const answer = pluck(colors, true); // if this were red, red would be removed from array
END EXAMPLE
PARAMETERS
array - an array of objects - or pass in any number of parameters to pick from
remove - (default false) set to true to remove the selected item from the array (original array must be provided)
RETURNS a random item from an array
--*///+9.26
zim.pluck = function(array, remove) {
z_d("9.26");
if (zot(array) || array.length==0) return null;
if (!Array.isArray(array)) array = Object.values(arguments);
var i = Math.floor(Math.random()*array.length);
var answer = array[i];
if (remove) array.splice(i,1);
return answer;
};//-9.26
/*--
zim.rand = function(a, b, integer, negative)
rand
zim function
DESCRIPTION
Returns a random integer between and including a and b if integer is true.
Returns a random number (with decimals) including a and up to b but not b if integer is false.
b is optional and if left out will default to 0 (includes 0).
integer is a boolean and defaults to true.
If a and b are 0 then just returns Math.random().
NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
EXAMPLE
const speed = rand(10,20); // 10, 11, 12... 18, 19 or 20
const colors = [blue, yellow, green];
const color = colors[rand(colors.length-1)]; // note the length-1
// the equivalent of:
const color = colors[Math.floor(Math.random()*colors.length)];
// OR a technique often used without using rand():
const color = shuffle(colors)[0];
// here we get a speed that is either from 5 to 10 or -5 to -10
const speed = rand(5,10,null,true);
END EXAMPLE
PARAMETERS
a - the first Number for the range
if a and b are not provided, rand() acts like Math.random()
if parameter b is not provided, rand will use range 0 to and including a
b - (default 0) second Number for the range
it does not matter if a>b or a<b
integer - (default true) set to false to include decimals in results
if false, range will include decimals up to but not including the highest number
if a or b have decimals this is set to false
negative - (default false) includes the negative range as well as the positive
RETURNS a Number
--*///+9
zim.rand = function(a, b, integer, negative) {
z_d("9");
if (zot(a) && zot(b)) return Math.random();
if (zot(a) || isNaN(a)) a = 0;
if (zot(b) || isNaN(b)) b = 0;
if (a%1!=0 || b%1!=0) integer = false;
if (zot(integer)) integer = true;
if (negative) if (Math.random()>.5) {a*=-1; b*=-1;}
if (integer) if (a>b) {a++;} else if (b>a) {b++;}
var r;
if (a == 0 && b == 0) return 0;
else if (b == 0) r = Math.random()*a;
else r = Math.min(a,b) + Math.random()*(Math.max(a,b)-Math.min(a,b));
if (integer) return Math.floor(r);
else return r;
};//-9
/*--
zim.seedRandom = function(seed)
seedRandom
zim function
DESCRIPTION
Makes Math.random() work from a seed.
If set then ZIM rand() and ZIM VEE values will also be seeded with its value.
This means that rand() will repeat in order of its random results.
This allows, for instance, a generative art piece to be the same for a certain user.
Or lets a player replay a same random game level if the seedRandom() is set again with the same seed.
Setting the seedRandom() with no parameter returns to regular rand(), Math.random(), etc.
NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
EXAMPLE
seedRandom("hello");
zog(rand(10,100)) // each time the app is run, this will be the same random number between 10 and 100
zog(rand(10,100)) // this will probably be a different random number than the first - but the same each time
zog(rand()) // and this will be the same random number between 0 and 1 not including 1.
END EXAMPLE
EXAMPLE
// Remember the seed for a user with localStorage (like a cookie)
let seed = rand(100000000);
if (localStorage) {
if (localStorage.seed) seed = localStorage.seed;
else localStorage.seed = seed;
}
seedRandom(seed);
new Circle(100, [red, green, blue]).center();
// the user will always have the same random color
// unless no localStorage or localStorage is cleared
seedRandom(seed);
// below will make another circle the same color as the first
new Circle(100, [red, green, blue]).center().mov(0,300);
seedRandom(); // clears the seed
// below will be a random color each time
new Circle(100, [red, green, blue]).center().mov(200,300);
END EXAMPLE
PARAMETERS
seed - (default null) add any string or number to seed the Math.random()
and subsequently, ZIM rand() and ZIM VEE values from [] and {min, max}
passing in nothing or null will use/reset to regular Math.random()
RETURNS the seed
--*///+9.01
zim.seedRandom = function(seed) {
z_d("9.01");
// Copyright 2014 David Bau.
// See message at https://davidbau.com/encode/seedrandom.js
!function(a,b,c,d,e,f,g,h,i){function j(a){var b,c=a.length,e=this,f=0,g=e.i=e.j=0,h=e.S=[];for(c||(a=[c++]);d>f;)h[f]=f++;for(f=0;d>f;f++)h[f]=h[g=s&g+a[f%c]+(b=h[f])],h[g]=b;(e.g=function(a){for(var b,c=0,f=e.i,g=e.j,h=e.S;a--;)b=h[f=s&f+1],c=c*d+h[s&(h[f]=h[g=s&g+b])+(h[g]=b)];return e.i=f,e.j=g,c})(d)}function k(a,b){var c,d=[],e=typeof a;if(b&&"object"==e)for(c in a)try{d.push(k(a[c],b-1))}catch(f){+function(){}();}return d.length?d:"string"==e?a:a+"\0"}function l(a,b){for(var c,d=a+"",e=0;e<d.length;)b[s&e]=s&(c^=19*b[s&e])+d.charCodeAt(e++);return n(b)}function m(c){try{return o?n(o.randomBytes(d)):(a.crypto.getRandomValues(c=new Uint8Array(d)),n(c))}catch(e){return[+new Date,a,(c=a.navigator)&&c.plugins,a.screen,n(b)]}}function n(a){return String.fromCharCode.apply(0,a)}var o,p=c.pow(d,e),q=c.pow(2,f),r=2*q,s=d-1,t=c["seed"+i]=function(a,f,g){var h=[];f=1==f?{entropy:!0}:f||{};var o=l(k(f.entropy?[a,n(b)]:null==a?m():a,3),h),s=new j(h);return l(n(s.S),b),(f.pass||g||function(a,b,d){return d?(c[i]=a,b):a})(function(){for(var a=s.g(e),b=p,c=0;q>a;)a=(a+c)*d,b*=d,c=s.g(1);for(;a>=r;)a/=2,b/=2,c>>>=1;return(a+c)/b},o,"global"in f?f.global:this==c)};if(l(c[i](),b),g&&g.exports){g.exports=t;try{o=require("crypto")}catch(u){}}else h&&h.amd&&h(function(){return t})}(this,[],Math,256,6,52,"object"==typeof module&&module,"function"==typeof define&&define,"random");
if (!zim.randomO) zim.randomO = Math.random;
if (zot(seed)) Math.random = zim.randomO;
else Math.seedrandom(seed);
return seed;
};//-9.01
/*--
zim.odds = function(percent)
odds
zim function
DESCRIPTION
Returns true if random percent is within (less than or equal to) the percent parameter provided.
So, odds(20) would be if Math.random() <= .2
and odds(80) would be if Math.random() <= .8
and odds(50) would be half the time.
NOTE: odds(20) is the same as rand() <= .2 but just a little easier to remember and read.
NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
EXAMPLE
if (odds(20)) new Rectangle().center(); // 20% the time there will be a Rectangle
if (odds()) new Aud("yay.mp3").play(); // half the time play yay otherwise play boo
else new Aud("boo.mp3").play();
END EXAMPLE
PARAMETERS
percent - (default 50) odds() will return true if random percent is less than or equal to this percent
RETURNS a Boolean
--*///+9.1
zim.odds = function(percent) {
z_d("9.1");
if (zot(percent) || isNaN(percent)) percent = 50;
return Math.random() <= percent/100;
};//-9.1
/*--
zim.rarity = function(weights, shuffle, zimColors, dynamicPayload)
rarity
zim function
DESCRIPTION
Receives an object with properties each having a number value as to their frequency.
Returns an array with these properties multiplied by their frequency.
The array is shuffled so get any element such as the first to pick randomly from the weighted array.
Or pass in to any ZIM VEE value to pick in a weighted manner.
Can also pass into a series() to pick them in order.
NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
EXAMPLE
const colors = rarity({blue:50, green:40, yellow:10});
zog(colors[0]); // 50% chance blue, 40% chance green, 10% chance yellow
END EXAMPLE
EXAMPLE
const radius = rarity({10:5, 20:2, 30:1});
// The properties are 10, 20 and 30.
// The frequencies are 5, 2 and 1.
// The frequency numbers do not need to add to 100
// These add to 8 - so there is a 5/8 chance the radius is 10
// and 2/8 chance it is 20 and 1/8 chance it is 30
// radius is an array such as [20,10,10,30,10,10,20,10]
// or some such scrambled array
// An array is a ZIM VEE value
// and the radius will be picked randomly from the array
new Circle(radius, red).center();
// instead, a series might be desired:
radius = series(radius);
// this would tile radii of 20,10,10,30,10,10,20,10
new Tile(new Circle(radius, red), 4,2).center()
END EXAMPLE
EXAMPLE
STYLE = {color:rarity(red:99,black:1)}
loop(100, ()=>{
// probably 1 black circle - but very well could be more or less
new Circle(50).loc(rand(W), rand(H));
});
END EXAMPLE
EXAMPLE
// payload array example to easily set scale
// remember that rarity is shuffled automatically so [0] is a random item
const size = rarity({Big:[1,5], Med:[10,1], Small:[1,.5]})[0];
// the first item of each array above is the frequency
// the second item of each array above is the payload
const circle = new Circle(100,red).center().sca(size.payload); // 5, 1 or .5
new Label(size + " Circle").center(circle); // matching "Big", "Med" or "Small"
// otherwise this is the alternative
const size = rarity({Big:1, Med:10, Small:1})[0];
const lookup = {Big:5, Med:1, Small:.5};
const circle = new Circle(100,red).center().sca(lookup[size]); // 5, 1 or .5
new Label(size + " Circle").center(circle); // matching "Big", "Med" or "Small"
END EXAMPLE
EXAMPLE
// ZIM VEE
// the payload example with a range of scales for each size
// and picking randomly from a one of two sets of colors
const size = rarity({
Big:[1,{min:4, max:6}], // ZIM VEE range
Med:[10,{min:.5, max:1.5}],
Small:[1,{min:.2, max:.4}]
})[0];
const color = rarity({
Grey:[1,[grey,light,tin,silver,moon,fog]], // ZIM VEE random from array
Colored:[20,[pink,purple,blue,red]]
})[0]; // either Grey or Colored
const circle = new Circle(100,color.payload) // will pick from payload array
.center()
.sca(size.payload); // a scale picked from range
new Label(color + " " + size + " Circle").center(circle);
END EXAMPLE
EXAMPLE
// ZIM VEE without dynamicPayload (default)
const damage = rarity({large:[50,{min:20,max:30}], small:[50, {min:5,max:10}]})[0];
// imagine large is picked
zog(damage.payload) // imagine 25.5 is picked
zog(damage.payload) // so this will be 25.5
zog(damage.payload) // this will still be 25.5
// ZIM VEE with dynamicPayload parameter set to true
const damage = rarity({large:[50,{min:20,max:30}], small:[50, {min:5,max:10}]}, null, null, true)[0];
// imagine large is picked
zogr(damage.payload) // imagine 25.5 is picked
zogr(damage.payload) // this picks again - so it could be 22.4
zogg(damage.payload) // and this could be 29.0
damage.dynamicPayload = false; // turn off picking
zogg(damage.payload) // this will be 29.0
zogg(damage.payload) // this will be 29.0
damage.dynamicPayload = true; // back to picking
zogr(damage.payload) // this could be 21.3
zogr(damage.payload) // this could be 28.8
END EXAMPLE
PARAMETERS
weights - (default null) an object literal with options (property name) and frequencies (property value)
or the property value can be an array with [frequency, payload] (see further down)
eg. {green:70, blue:20, red:10} or {10:2, 20:2, 30:1}
The frequencies should be whole numbers
The frequencies can add to 100 to easily visualize percentage
but they can add up to any number and this will be used
to calculate the pick ratio for each one using frequency/total frequency
For instance with {10:2, 20:2, 30:1} the frequencies add up to 5
so the the chances of being picked are 2/5 for 10, 2/5 for 20 and 1/5 for 30
If an array with [frequency, payload] is provided then the frequency is used as the frequency
the payload is any object that will be added as a payload property to the option string
(the option is converted from a primative string to a String object so it can hold the payload)
This arrangement replaces the need for a look-up table (see examples)
| ZIM VEE |
The payload can be any object but will also be processed by ZIM VEE (Pick)
So if an array or object literal is desired use {noPick:[]} or {noPick:{}}
Also see dynamicPayload parameter
ZIM VEE literal types are:
[1,3,2] - random; {min:10, max:20} - range;
series(1,2,3) - order, function(){return result;} - function
shuffle - (default true) shuffle the resulting array
zimColors - (default true) set to false to not convert "blue" property value to ZIM blue
dynamicPayload - (default false) only pick once from ZIM VEE value in payload
or set to true to continue to pick from ZIM VEE value in payload each time property is accessed
note: at any time after rarity() is called, this can be set to true or false
RETURNS an array of properties repeated according to frequency
these properties may have a payload property if payloads are used
--*///+9.2
zim.rarity = function(weights, shuffle, zimColors, dynamicPayload) {
z_d("9.2");
if (zot(weights) || weights.constructor != {}.constructor) return [];
if (zot(shuffle)) shuffle = true;
if (zot(zimColors)) zimColors = true;
if (zot(dynamicPayload)) dynamicPayload = false;
var all = [];
zim.loop(weights, function (key,val) {
var i = zim.colors.indexOf(key);
if (i != -1) key = zim.colorsHex[i];
var num;
if (Array.isArray(val)) {
if (+key === +key) key = new Number(key);
else key = new String(key);
key._payload = val[1];
key.dynamicPayload = dynamicPayload;
Object.defineProperty(key, "payload", {
get:function() {
if (!this.dynamicPayload && this._lastPayload!=null) return this._lastPayload;
this._lastPayload = zim.Pick.choose(this._payload);
return this._lastPayload;
},
set:function(v) {
this._lastPayload = null;
this._payload = v;
}
});
num = val[0];
zim.loop(num, function() {
all.push(key);
});
} else {
num = val;
zim.loop(num, function() {
if (+key === +key) key = +key; // if number use number
all.push(key);
});
}
});
if (shuffle) zim.shuffle(all);
return all;
};//-9.2
/*--
zim.repeats = function(array, total)
repeats
zim function
DESCRIPTION
Returns the number of repeats in an array.
By default, this will be the max number of repeats - not the total repeats.
The number of repeats often offers a rarity feature.
NOTE: this does not determine the repeated value - this is not needed for rarity.
NOTE: a repeat is one less than the number of objects that are the same.
So if no objects are the same the repeats are 0.
If two objects are the same the repeats are 1.
If three objects are the same the repeats are 2, etc.
NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
EXAMPLE
const colors = [orange, green, red, orange, orange, green];
zog(repeats(colors)); // 2 - note, one less than the number of objects that repeat
zog(repeats(colors, false)) // 3 - two repeats with orange, one repeat with green
END EXAMPLE
PARAMETERS
array - an array of objects
total - (default false) set to true to return the total repeats
RETURNS the max number of repeats in an array or 0 if no objects are the same, one if two objects are the same, etc.
if total is true then returns all the repeats - possibly from different sets
--*///+9.25
zim.repeats = function(array, total) {
z_d("9.25");
if (zot(array) || !Array.isArray(array)) return 0;
var counter = {};
var max = 0;
var tot = 0;
zim.loop(array, function (a) {
if (counter[a] != null) {
counter[a]++;
tot++;
}
else counter[a] = 0;
if (counter[a] > max) max = counter[a];
});
return total?tot:max;
};//-9.25
/*--
zim.series = function(array|item1|obj, item2, item3)
series
zim function
DESCRIPTION
Returns a function that will return each value passed as a parameter (or an Array) in order
or an object literal with min and max.
This goes in sequence each time the function is called.
Use this to pass a series in to any ZIM VEE value so a looping series is obtained.
NOTE: was called makeSeries() which is now depricated
NOTE: as of ZIM 5.5.0 the zim namespace is no longer required (unless zns is set to true before running zim)
EXAMPLE
// note - do not call the variable name series
const colors = series(red, green, blue);
colors(); // red
colors(); // green
colors(); // blue
colors(); // red, etc.
// or
const colors = [red, green, blue];
const s = series(colors);
s(); // red
s(); // green
s(); // blue
s(); // red, etc.
new Tile(new Rectangle(10,10,[blue, red]), 10, 10).center(); // would randomize colors
new Tile(new Rectangle(10,10,series(blue, red)), 10, 10).center(); // would alternate colors
END EXAMPLE
EXAMPLE
STYLE = {color:series(pink, green, blue)}
loop(9, i=>{
new Circle(100).loc(110+i*100, 400)
});
END EXAMPLE
EXAMPLE
// ten rectangles getting higher by 20 each time
const s = series({min:10, max:200}).step(20);
loop(10, i=>{
new Rectangle(10, s, red).sca(1,-1).loc(100+i*20, 400);
});
END EXAMPLE
EXAMPLE
// added functionality as of ZIM 10.9.0
// start at index 3, reverse and don't go past 0
const nums = series(0, 1, 2, 3, 4).jump(3).reverse().constrain();
loop(5, ()=>{
zogb(nums()); // 3,2,1,0,0 (blue in console)
});
nums.reverse(false); // go forward - note, still starting at index 0
zogg(nums()); // 0 (green in console)
zogg(nums()); // 1
zogg(nums()); // 2
nums.bounce(); // go back and forth rather than default loop
loop(5, ()=>{
zogy(nums()); // 3,4,3,2,1 (yellow in console)
});
nums.step(3); // sitting at 0 then increase the step to 3
loop(5, ()=>{
zogr(nums()); // 0,3,2,1,4 (red in console)
// 3->6 bounces off 4 with two back to 2
// 2->-1 bounces off 0 with one forward to 1
// tricky but correct
});
zogp(nums.index); // 1 - coming back 3 steps from 4 to 1 as current index
END EXAMPLE
EXAMPLE
// make a checkerboard
const colors = series(black,white,black,white,black,white,black,white).flip();
const board = new Tile(new Rectangle(80,80,colors),8,8,1,1).center();
END EXAMPLE
PARAMETERS
array|item1|{min,max,step} - the first item - or an array of results that will be called in order as the resulting function is called
or an object with min, max and step properties to make a series of numbers from and including min and max (step defaults to 0)
this will make an array of values and then it is just like an array was entered initially.
when used with ZIM VEE - the values may be further ZIM VEE values (including more series values)
item2 - the second item if the first is not an array
item3 - the third item, etc. to as many items as needed
METHODS
step(num) - num defaults to 1 - the number of items to move the index - or use index property
every(num) - num defaults to 0 - steps to wait before moving to the next index - remain on blue for five, then go to yellow for five, etc.
jump(index) - jump to an index but do not run it - the next call to the series will run here
reverse(boolean) - boolean defaults to true - reverses direction - or pass in false to cancel reverse
bounce(boolean) - boolean defaults to true - back and forth between 0 and length-1 - or pass in false to cancel bounce
flip(boolean) - boolean defaults to true - go to the end then go in reverse - will do last one and first one twice - or pass false to cancel flip
constrain(boolean) - boolean defaults to true - keeps index between 0 and length-1 - or pass in false to cancel constrain
random(boolean) - boolean defaults to true - randomizes order of series each time it finishes - or pass in false to cancel random
mix(boolean) - boolean defaults to true - randomizes order of series but avoids duplicating on ends - or pass in false to cancel mix
shuffle(boolean) - boolean defaults to true - shuffles order of series but then will repeat with same order - or pass in false to cancel shuffle
PROPERTIES
type - a string of the type of object in this case, "series"
array - an array of items passed in to the function
length - the length of the series
index - get or set the current index of the series - what will run next
original - a copy of the original order
RETURNS a function that can be called many times - each time returning the next value in the series
--*///+13.61
zim.series = function() {
z_d("13.61");
var array;
var range;
if (arguments[0] == null) return null;
if (arguments.length == 0) return function(){};
if (arguments.length == 1 && Array.isArray(arguments[0])) {
array = arguments[0];
} else if (arguments.length == 1 && arguments[0].constructor == {}.constructor) {
range = arguments[0];
if (zot(range.min)) range.min = 0;
if (zot(range.max)) range.max = 1;
if (zot(range.step)) range.step = 1;
array = [];
zim.loop(Math.floor(Math.abs(range.max-range.min)+range.step), function (i) {
array.push(i+range.min);
}, null, null, range.step);
range = null;
} else {
array = Array.prototype.slice.call(arguments);
}
var count = 0;
var dir = 1;
var step = 1;
var every = 0;
var everyCount = 0;
var bounce = false;
var flip = false;
var constrain = false;
var randomize = false;
var mix = false;
var firstVal;
var lastVal;
var f = function(target, fromPick) {
if (every && everyCount%(every) != 0) {
everyCount++;
return lastVal;
}
everyCount++;
var length = array.length;
var val;
if ((randomize || mix) && count%length==0) zim.shuffle(array);
val = array[(length*10+count)%length];
if (count==0) firstVal = val;
if (mix && count%length==0 && length>1) {
while(val==lastVal) {
zim.shuffle(array);
val = array[(length*10+count)%length];
}
}
if (bounce) {
if (dir > 0 && count+dir*step >= length) {
dir = -1;
count = (length-1)-(count+step-(length-1));
} else if (dir < 0 && count+dir*step < 0) {
dir = 1;
count = step - count;
} else {
count += dir*step;
}
} else if (flip) {
if (dir > 0 && count+dir*step >= length) {
dir = -1;
count = (length-1)-(count+step-(length-1)) + step;
} else if (dir < 0 && count+dir*step < 0) {
dir = 1;
count = step - count - step;
} else {
count += dir*step;
}
} else if (constrain) {
if (dir > 0 && count+dir*step >= length) count = length-1;
else if (dir < 0 && count+dir*step < 0) count = 0;
else count += dir*step;
} else {
count += dir*step;
}
lastVal = val;
if (zim.isPick(val)) {
// adjusted in ZIM NFT to make literal version of series recursive
// tricky - literal versus Pick.choose() was reporting wrong noPick results from series
if (val.noPick && fromPick) return val;
return zim.Pick.choose(val);
}
return val;
};
var original = f.original = zim.copy(array);
f.array = array;
f.type = "series";
Object.defineProperty(f, 'index', {
get: function() {
var length = array.length;