UNPKG

zimjs

Version:

JavaScript Canvas Framework - Code Creativity!<br> https://zimjs.com

1,431 lines (1,217 loc) 4.03 MB
/*! 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;