Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

16 Good

About happywire

  • Rank
    Jr. Member

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Trying to come up with a strategy to handle images. variations and webp versions thereof. From what I understand from the docs and posts here this can be done in the following ways. In a template on first page load of that template. In the admin panel on upload of an image with a hook intercepting the process. Given both a try and am not sure if this works for me or if I am doing something wrong. Things time out or the versions expected are not created. This more so with ImageMagick doing the encoding rather than the native image resizer. At this stage I would also do not know how to visually output verbose log of the encoding process. Now I guess there are following options.. 1. Local variations and webp versions are connected with pages and db Given I use ImageMagick locally to be able to pass it the options similar to here. https://www.smashingmagazine.com/2015/06/efficient-image-resizing-with-imagemagick/ I then have all the variations for a single or multiple images. How do I "sync" those with the db and the page I want those images and their variations to reside? How can I import prepared variations of an image into a page while not importing all variations as a single image? By giving the webp versions a certain filename suffix, could those also be made variations of the original image? I found this topic but that approach creates pages and possibly loads all images as a single image? I need to have the 1 original image and then other files as variations of that original image. Best would be if I could drop the original and all variations and webp versions into the assets folder for that page. Then simply do a refresh of that page in the admin panel and have a script connect the original, variations and webp versions with that page and the db. This so that I can then work with it in the page template with the images object. That should hold an array of let's say, all urls to all the images, all image filenames and ideally width and height values. The description would be given manually on the single original image for example. 2. Local files are uploaded with a module I have found http://modules.processwire.com/modules/jquery-file-upload/ Am wondering if that supports to upload 1 master image and read the variations/webp versions as such, perhaps by checking the image filename? 3.Local files are uploaded and managed with a module I have also found https://mediamanager.kongondo.com/. Same thing here, not sure if that supports what I am trying to get done. Manage one original image per page and assign that variations of it before I upload it. Ideally I like to see something like upload - image - variations and then be able to pick all variations from the corresponding assets folder - save, done. I can now use the images object for the page in the template and work with the images array. 4.Local files and all variations are uploaded as single images for the page in question This is simply done with the native upload for images. All images are singular and have no variations. Somehow getting option 1 working would be awesome. However if I am missing a concept here or misunderstand how files and images are supposed to be handled with ProcessWire then please let me know. At this stage I am not looking for output generating modules or solutions. But rather something that can perhaps easily add existing images to certain ProcessWire pages.
  2. I installed the ImageSizerEngineIMagick module and am using @Soma 's hook to create image variations in the admin panel when uploading a new image. 1. How can I limit the ImageSizerEngineIMagick module to not create image variations that are above the original file's dimensions? For example I have this image. https://www.jpl.nasa.gov/spaceimages/details.php?id=PIA13128 Full resolution. https://photojournal.jpl.nasa.gov/jpeg/PIA13128.jpg Using Soma's hook and adding the image variation sizes I want to be created like this.. <?php // https://gist.github.com/somatonic/5685631 // not all the hook is inserted here, this is just to show the image size variations public function sizeImage($event) { $inputfield = $event->object; if ($inputfield->name != 'images') return; $defaultOptions = array( 'upscaling' => false, 'cropping' => false, 'quality' => 100 ); $image = $event->argumentsByName("pagefile"); $image->size(9000, 0, $defaultOptions); $image->size(7680, 0, $defaultOptions); $image->size(3200, 0, $defaultOptions); $image->size(2560, 0, $defaultOptions); $image->size(1920, 0, $defaultOptions); $image->size(1280, 0, $defaultOptions); $image->size(800, 0, $defaultOptions); $image->size(768, 0, $defaultOptions); $image->size(240, 0, $defaultOptions); $image->size(180, 0, $defaultOptions); } ..I get the following image size variations. This means that regardless of image dimensions there are variations created that are above the original file dimension. How can I tell the module or include what logic in the hook that I do not want to create image variations that are above the dimensions of the original image? 2. The original is max 2MB big and has a max width of 4801px. Having the quality set to 100 in the module I get images that have a bigger file size than the original at a lower dimension. An image variation of 1920px width is created with 2.9MB. An image variation of 2560px width is created with 4.4MB. An image variation of 3200px width is created with 6.0MB. Setting the quality to 90 following image variations are created. Does that mean I cannot put the quality setting of the module to 100 as otherwise image variations that have a higher file size than the original are being created at lower dimensions of the original? Does ImageMagick work like this with the quality being 100?
  3. This is exactly what I am trying to do. Create image variations outside a page load, so not inside the template file but during the initial upload process in the backed/admin panel when then thumbnail is generated. Had a look at the docs you like for the Pageimage class but I am not sure how to use that info. Reading through this https://selftaughtcoders.com/from-idea-to-launch/lesson-14/how-to-use-classes-in-php/ and then for example inserting <?php $galleryImg = new Pageimage; $defaultOptions = array( 'upscaling' => false, 'cropping' => false, 'quality' => 90, ); $galleryImg->size(1280, 0, $defaultOptions); into _func.php of the Intermediate profile does not create the desired 1200px width image variation. Would be super grateful to learn how I can generate image variations during the initial upload process in the admin panel. Thank you.
  4. @MoritzLost Great post, thank you. New to PW. I am having trouble getting the beginning of your function to work in the Intermediate profile. <?php // ########################## // I have this in _func.php function buildResponsiveImage(Pageimage $img, int $standard_width): string { $default_img = $img->maxWidth($standard_width); return '<img src="' . $default_img->url() . '" alt="' . $img->description() . '">'; } // ########################## // In a Gallery page I created and where I added a gallery-page template that does have the Images field I put this. // There I also added two images, both larger than 7000px width. <?php namespace ProcessWire; // gallery-page.php template file $content = $page->images; $content .= buildResponsiveImage($page->images, 1200); // ########################## // In the _main.php file I like to output the code in the main tag. <!-- main content --> <div id='content'> <h1><?php echo $title; ?></h1> <?php echo $content; ?> </div> "Fatal error: Uncaught TypeError: Argument 1 passed to ProcessWire\buildResponsiveImage() must be an instance of ProcessWire\Pageimage, instance of ProcessWire\Pageimages given". Thank you for any help or learning resources to get this right.
  5. Yeah, someone else also did highly recommend to do that course. Have been watching the first couple hours through this site http://learnwebdev.net/ and had so many concepts suddenly make a lot of sense. Window object, this, execution context, lexical scope. Just the first 2h of that cleared up a lot of what I was missing all this time.
  6. @louisstephens In that thread you linked I found and since I cannot edit my above post I meant that in fact the source from the later , "srcset, sizes and picture support" uses art direction instead of sizes and dpi, however I also read through the MarkupSrcSet thread and it does not seem correct to me at least, however I might be able to look into that repo to learn how image variations are created server-side so that I can have an image array to work with without affecting the frontend. If you are unsure, test the output with https://ausi.github.io/respimagelint/ it is the definite answer to having correct syntax when it comes to responsive images, be this using art direction or sizes.
  7. That approach/code uses art direction as opposed to changing image sizes. When I started learning responsive images I also went for that approach thinking that having different source tags meant that each different source is a different size of the same image. Have a look at this detailed post https://dev.opera.com/articles/responsive-images/ . That code in the post you linked uses the "Changing image sizes, high-DPI images & art direction use case". It means that when you resize the browser window not the same image at a different size will be shown but a completely different one, one that you choose to show. This also means that when you resize that source will be downloaded again and again as you keep resizing, more requests, bad performance. However I do not have the need for art direction. I am after responsive images, the same identical image, albeit with a different size where I let the browser decide what version is best for the given viewport. That is the last example in that post I linked, the "Changing image sizes, high-DPI images & different image types use case". Like this, for example once you go from wide viewport to narrow, i.e. landscape to portrait, the smaller size of the image will only be downloaded once. When you go back into landscape mode the wider image is cached and not re-downloaded. To make all this more fun, different browsers handle this differently. If what I write does not make sense read through the 10 parts starting here https://cloudfour.com/thinks/responsive-images-101-definitions/ . That really helped me understand it properly.
  8. This. I certainly need to create an image_array of all image variations and store that in the db. Found this topic. Alongside https://processwire.com/blog/posts/processwire-3.0.10-expands-image-resize-options/ and this part of the docs https://processwire.com/api/ref/pageimage/ . Now when I include something like this, having installed the ImageMagick module as per this post https://processwire.com/blog/posts/processwire-3.0.10-expands-image-resize-options/ <?php namespace ProcessWire; // gallery-page.php template file $content = ""; // $page->images; if (count($page->images)) { // the page has one or more images foreach ($page->images as $image) { // http://cheatsheet.processwire.com/files/image-methods/image-size-width-height-options/ // $defaultOptions = array( 'upscaling' => true, 'cropping' => true, 'quality' => 90 ); // $image->size($width, $height, $options) $defaultOptions = array( 'upscaling' => false, 'cropping' => false, 'quality' => 100 ); $img_size_9000 = $image->size(9000, 0, $defaultOptions); $img_size_8400 = $image->size(8400, 0, $defaultOptions); $img_size_8000 = $image->size(8000, 0, $defaultOptions); $img_size_7680 = $image->size(7680, 0, $defaultOptions); $img_size_6000 = $image->size(6000, 0, $defaultOptions); $img_size_5400 = $image->size(5400, 0, $defaultOptions); $img_size_4096 = $image->size(4096, 0, $defaultOptions); $img_size_3840 = $image->size(3840, 0, $defaultOptions); $img_size_3200 = $image->size(3200, 0, $defaultOptions); $img_size_2560 = $image->size(2560, 0, $defaultOptions); $img_size_2048 = $image->size(2048, 0, $defaultOptions); $img_size_1920 = $image->size(1920, 0, $defaultOptions); $img_size_1600 = $image->size(1600, 0, $defaultOptions); $img_size_1280 = $image->size(1280, 0, $defaultOptions); $img_size_1140 = $image->size(1140, 0, $defaultOptions); $img_size_1024 = $image->size(1024, 0, $defaultOptions); $img_size_960 = $image->size(960, 0, $defaultOptions); $img_size_900 = $image->size(900, 0, $defaultOptions); $img_size_800 = $image->size(800, 0, $defaultOptions); $img_size_768 = $image->size(768, 0, $defaultOptions); $img_size_640 = $image->size(640, 0, $defaultOptions); $img_size_525 = $image->size(525, 0, $defaultOptions); $img_size_425 = $image->size(425, 0, $defaultOptions); $img_size_320 = $image->size(320, 0, $defaultOptions); $img_size_240 = $image->size(240, 0, $defaultOptions); $img_size_180 = $image->size(180, 0, $defaultOptions); $content .= "<a href=" . $img_size_9000->httpUrl . ">$img_size_9000->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_8400->httpUrl . ">$img_size_8400->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_8000->httpUrl . ">$img_size_8000->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_7680->httpUrl . ">$img_size_7680->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_6000->httpUrl . ">$img_size_6000->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_5400->httpUrl . ">$img_size_5400->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_4096->httpUrl . ">$img_size_4096->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_3840->httpUrl . ">$img_size_3840->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_3200->httpUrl . ">$img_size_3200->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_2560->httpUrl . ">$img_size_2560->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_2048->httpUrl . ">$img_size_2048->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_1920->httpUrl . ">$img_size_1920->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_1600->httpUrl . ">$img_size_1600->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_1280->httpUrl . ">$img_size_1280->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_1140->httpUrl . ">$img_size_1140->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_1024->httpUrl . ">$img_size_1024->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_960->httpUrl . ">$img_size_960->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_900->httpUrl . ">$img_size_900->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_800->httpUrl . ">$img_size_800->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_768->httpUrl . ">$img_size_768->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_640->httpUrl . ">$img_size_640->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_525->httpUrl . ">$img_size_525->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_425->httpUrl . ">$img_size_425->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_320->httpUrl . ">$img_size_320->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_240->httpUrl . ">$img_size_240->httpUrl</a>" . "</br>"; $content .= "<a href=" . $img_size_180->httpUrl . ">$img_size_180->httpUrl</a>" . "</br>"; } } and upload for example following image https://fee.org/media/24571/nasa_space_outer_universe-min.jpg and that being 7857 × 7462 pixels wide I get the following. 1. The image variations are not created during file upload in the backend. They are created only when I visit the Gallery page on the frontend. How can I create the image variations when uploading in the backend without touching the frontend? Doing this locally is not an issue, though if this is being done on the live site it won't be good. First time I loaded the Gallery page it crashed the local server, on 2nd load it would display the image variation links. 2. Each time I reload the Gallery page it does take some time to load, considerably more than usual (1-2 seconds), I assume this is due to the $image->size method being called and added to $content for each image size and this happens each time the page loads. 3. Despite the $defaultOptions set to upscaling=>false there are still httpUrls created for image variations larger than the original image. How can I only get httpUrls for image variations that are not larger than the original image? So in this example I would expect to only get httpUrl up to 7680px width. Thank you. I will have a look at this right this moment, still trying to figure out how to create image variations in the backend/admin panel rather than on first time of page load.
  9. Is this due to not having enough reputation or is my account somehow blocked from such actions? At the moment I cannot edit my posts or give Like or Thanks to other people's posts. Thank you.
  10. Yes, that is it. So simple! Thank you heaps. That will get me going. I did search in the forums and docs/tuts. Do you have good resources for me to follow through with delayed output with regards to making proper responsive images. On my list is this. 1. Define image sizes and upon upload have ProcessWire create those. a) Return the array of all created image urls. b) Ideally create webp versions of those images as well. For webp support I found this 2. In the end I would like to write a function that I can include in a region that will output proper responsive image syntax similar to this. <picture class="pic-id-578 projects-picture" ><source sizes="(min-width: 1700px) 469px, (min-width: 1200px) 27.71vw, (min-width: 780px) calc(50vw - 80px), (min-width: 640px) calc(50vw - 24px), calc(100vw - 32px)" srcset=" https://example.com/picture-640x427.webp 640w, https://example.com/picture-525x350.webp 525w, https://example.com/picture-425x284.webp 425w, https://example.com/picture-320x214.webp 320w, https://example.com/picture-240x160.webp 240w, https://example.com/picture-180x120.webp 180w " type="image/webp" /> <img class="lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://example.com/picture-320x214.jpg" alt="Some useful alternative text" sizes="(min-width: 1700px) 469px, (min-width: 1200px) 27.71vw, (min-width: 780px) calc(50vw - 80px), (min-width: 640px) calc(50vw - 24px), calc(100vw - 32px)" data-srcset="https://example.com/picture-640x427.jpg 640w, https://example.com/picture-525x350.jpg 525w, https://example.com/picture-425x284.jpg 425w, https://example.com/picture-320x214.jpg 320w, https://example.com/picture-240x160.jpg 240w, https://example.com/picture-180x120.jpg 180w" /> </picture> Coming from WP I did write this function. <?php function responsive_picture($acf_image_array, $media_condition, $type) { // Get the global $theme_name_pagename variable global $theme_name_pagename; // var_export( $theme_name_pagename ); // Get the global $theme_name_category_slug variable global $theme_name_category_slug; // var_export( $theme_name_category_slug ); // Get the global $theme_name_custom_post_type_slug variable from archive.php global $theme_name_custom_post_type_slug; // var_export( $theme_name_custom_post_type_slug ); // Make an array where the sources for the image will be placed $sources = array(); // If the field for the post/page/cpt is filled then do stuff, otherwise don't if ($acf_image_array) { foreach ($acf_image_array['sizes'] as $image_size_key => $image_size_value) { if (preg_match('/^(\d+)$/', $image_size_key)) { if (preg_match('/-' . $image_size_key . 'x\d+\.(jpeg|jpg|png)$/i', $image_size_value)) { array_push( $sources, array( 'width' => $image_size_key, 'height' => $acf_image_array['sizes'][$image_size_key . '-height'], 'src' => $image_size_value, ) ); } } } // end foreach ( $acf_image_array['sizes'] as $image_size_key => $image_size_value ) // http://php.net/manual/en/function.array-reverse.php // $sources = array_reverse( $sources ); // return var_export( $sources ); // exit; $srcset = array(); foreach ($sources as $index => $source_values) { array_push($srcset, $source_values['src'] . ' ' . $source_values['width'] . 'w'); } $srcset = implode(', ', $srcset); // preg_replace .jpg with .webp // http://php.net/manual/en/function.preg-replace.php $srcset_webp = $srcset; $pattern = '/.jpg /'; $replacement = '.webp '; $srcset_webp = preg_replace($pattern, $replacement, $srcset_webp); // var_export($srcset_webp); // Depending on the post, page or custom post type, set the picture class accordingly if (null !== $theme_name_pagename) { // echo esc_html( $pagename ); print '<picture class="pic-id-' . esc_html($acf_image_array['id']) . ' page-' . esc_html($theme_name_pagename) . '-picture">'; } if (null !== $theme_name_category_slug) { // echo esc_html( $category_slug ); print '<picture class="pic-id-' . esc_html($acf_image_array['id']) . ' category-' . esc_html($theme_name_category_slug) . '-picture">'; } if (null !== $theme_name_custom_post_type_slug) { // var_export( esc_html( $post_type ) ); // var_export( $theme_name_custom_post_type_slug ); // https://codex.wordpress.org/Function_Reference/is_post_type_archive // Differentiate between single- and archive- if (is_post_type_archive($theme_name_custom_post_type_slug)) { // print 'is_post_type_archive'; print '<picture class="pic-id-' . esc_html($acf_image_array['id']) . ' archive-' . esc_html($theme_name_custom_post_type_slug) . '-picture">'; } else { print '<picture class="pic-id-' . esc_html($acf_image_array['id']) . ' single-' . esc_html($theme_name_custom_post_type_slug) . '-picture">'; } } else { // null === $theme_name_custom_post_type_slug // var_export( $theme_name_custom_post_type_slug ); $post_type = get_post_type(); if ('' !== $post_type && 'page' !== $post_type) { $post_type_data = get_post_type_object($post_type); $theme_name_custom_post_type_single_slug = $post_type_data->rewrite['slug']; // var_export( esc_html( 'single ' . $theme_name_custom_post_type_single_slug ) ); print '<picture class="pic-id-' . esc_html($acf_image_array['id']) . ' single-' . esc_html($theme_name_custom_post_type_single_slug) . '-picture">'; } } // This part of the function is for the webp file format START ;) print '<source '; // print 'sizes="' . esc_html( $media_condition . $viewport ) . '" '; // leave space between attributes print 'sizes="' . esc_html($media_condition) . '" '; // leave space between attributes print 'srcset="' . esc_html($srcset_webp) . '" '; // leave space between attributes print 'type="image/' . esc_html($type) . '">'; // either webp or jp2 and close <source> here // This part of the function is for the webp file format END ;) // Default loading // print '<img '; // print 'src="' . esc_html( $acf_image_array['sizes'][320] ) . '" '; // default image size leave space between attributes // print 'alt="' . esc_html( $acf_image_array['alt'] ) . '" '; // leave space between attributes // print 'sizes="' . esc_html( $media_condition ) . '" '; // leave space between attributes // print 'srcset="' . esc_html( $srcset ) . '">'; // close img tag here // print '</picture>'; // With lazysizes print '<img class="lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" '; // leave space between attributes print 'data-src="' . esc_html($acf_image_array['sizes'][320]) . '" '; // default image size leave space between attributes Alternatively you can simply define a fallback src via the data-src attribute. print 'alt="' . esc_html($acf_image_array['alt']) . '" '; // leave space between attributes print 'sizes="' . esc_html($media_condition) . '" '; // leave space between attributes print 'data-srcset="' . esc_html($srcset) . '">'; // close img tag here print '</picture>'; } } In a template I would then go on to call that function for example with the following arguments. <?php // Get the array values for the field responsive_picture for the current post $responsive_picture_array = get_field( 'responsive_picture' ); responsive_picture( $responsive_picture_array, '(min-width: 1700px) 469px, (min-width: 1200px) 27.71vw, (min-width: 780px) calc(50vw - 80px), (min-width: 640px) calc(50vw - 24px), calc(100vw - 32px)', 'webp' ); ?> So that is my aim really that I am working towards. Once they all check out with this https://github.com/ausi/respimagelint I will be satisfied. Any sections in the forums/docs/tuts/api/recepies that you can think of, kindly fire away, thank you.
  11. Using the Intermediate profile and did follow through https://processwire.com/docs/tutorials/default-site-profile/ and am now trying to define a very simple gallery region. I made a simple gallery-page.php template with following code. <?php namespace ProcessWire; // gallery-page.php template file $content = $page->images; if (count($page->images)) { // the page has one or more images foreach ($page->images as $image => $value) { echo $value; echo "picture markup to follow.."; } } Ideally this should be output via the _main.php output file. <!-- main content --> <div id='content'> <h1><?php echo $title; ?></h1> <?php echo $content; ?> </div> Having it like above inserts the image urls and the text "picture markup.. " right at the top of the page and then only the image urls inside the Gallery section of the page. I understand why this happens. $content is once defined as $page-> image; but the if(count.. condition is inserted directly at the top of the page because it is not delyed output any more. Now when I try to do <?php namespace ProcessWire; // gallery-page.php template file $content = if (count($page->images)) { // the page has one or more images foreach ($page->images as $image => $value) { echo $value; echo "picture markup to follow.."; } } naturally it throws an error. How can I have the template code that I want to be stored inside the $content variable please? So something like $content = and then put all my code that I like for the images to be in that?
  12. Ha, thx heaps for the detailed reply. As you can see I understood it wrong and thank you for getting back about it. Much appreciated! So, I am just learning JS basics (doing this course at the moment udemyDOTcom/javascript-bootcamp-2016/ ), and to clarify/repeat, what is happening here is called type coercion as explained here for example https://eloquentjavascript.net/01_values.html#h_AY+YGu6qyM , right? Integer is a full number, so no comma behind it, in this case either 0 or 1. Since Math.random outputs floating numbers, meaning with comma and lots of numbers behind the comma, then those numbers, the decimals, are just cut off, truncated, like you say. Then you say this and I think this is highly important to understand because no method is called. So instead of converting the floating result with Math.floor one can just use the logical operator to get an integer, right? The snippet shows how many times from 10'000 runs Math.random() outputs a true integer without type coercion and how many times it outputs a floating number, right?
  13. Ok, now at least I understand it better. The | 0 part of it checks if math.random() is 0 or something else, if it is 0 it leaves it as that and if it is more than 0 it will make it become 1. Good to learn this. But just like you say, that + 1 - 1 part of it is just confusing or poorly written code. Perhaps this is/was there put on purpose to see how Sam would handle explaining it? If you don't mind Sam, is there more context to this or just a line of random code?
  14. Possibly because multiplication goes over OR since you have Math.random() times 2 OR 0, saying that since there will never be the case of running the OR 2 part behind Math.random it puts Math.random() times 2 in brackets and evaluates that first. Then it just leaves the OR 0 part as well as the + 1 - 1 part. We can then take the + 1 - 1 part away and are left with just Math.random times 2 OR 0.
  • Create New...