Walking Trip …

Walking Trip

Walking Trip

Offenbach's Suite ... Warts 'n All

Offenbach's Suite ... Warts 'n All

 📅  

If this was interesting you may be interested in this too.

Posted in Photography, Trips | Tagged , , | 34 Comments

Python Matplotlib More Charts Tutorial

Python Matplotlib More Charts Tutorial

Python Matplotlib More Charts Tutorial

We doubt you’ll meet many programmers not interested in “peer to peer” software suite arrangements. Thinking about Python Matplotlib module, of course, “peer to peer” becomes interesting in that the difference between the presentation of various chart types amounts to a couple of lines of Python code, in essence.

With that in mind, we think to rewrite wholly fleshed out new code file pairs consisting of …

  • a Python code file specializing in creation of a display of a particular Matplotlib module chart type … supervised by a …
  • a PHP supervising code file specializing in helping Python code file creation of a display of a particular Matplotlib module chart type

… for any one newly considered Matplotlib module chart type sounds like “too much hard work”. Why not add intelligence to that original Pie Chart code file pairing Python and PHP set and work off that? We came up with this idea, and it came to fruition.

And so, that is the long answer to yesterday’s question as to whether our …

strange idea

… would work?! The short answer …

Yes … the strange idea worked

… in the new workflow to getting up a new Matplotlib module chart type into the mix of our “peer to peer” web application software suite …

  1. clone these two crontab records …

    ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "piechartpython_*.py"`; do /usr/bin/python3 $i ${i##*/} /home/rjmprogr/public_html/Python/ ; done'
    ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "piechartpython_*.py"`; do rm -f "$i"; done'

    ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "plot_*.png" -mmin +122`; do rm -f "$i"; done'
    ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "output_image_*.png" -mmin +122`; do rm -f "$i"; done'

    … to new ones, for the example of a Column Chart …

    ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "columnchartpython_*.py"`; do /usr/bin/python3 $i ${i##*/} /home/rjmprogr/public_html/Python/ ; done'
    ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "columnchartpython_*.py"`; do rm -f "$i"; done'

  2. make the coding changes … as per …
    the changed pie_chart_python.py Python … brought in a new Bar Chart …
    the changed pie_chart_python.php PHP … helped bring in a new Bar Chart
    … but then, thinking on it …
    the changed pie_chart_python.py Python … brought in new Column and Bar Charts (in their traditional guises) …
    the changed pie_chart_python.php PHP … helped bring in new Column and Bar Charts (in their traditional guises)
  3. and then … yay!!!! … the time saver … Linux web server command line soft links to the rescue again

    cd /home/rjmprogr/public_html/Python
    ln -s pie_chart_python.py bar_chart_python.py
    ln -s pie_chart_python.php bar_chart_python.php
    ln -s pie_chart_python.py column_chart_python.py
    ln -s pie_chart_python.php column_chart_python.php

    … to add onto the existant Matplotlib module Pie Chart coding, as of yesterday’s Python Matplotlib Pie Chart Labels Tutorial, interfacing now for Matplotlib module Bar Charts and Column Charts, where the logic for the new dropdown “peer to peer” links all happening via …

    file naming conventions

    … getting both Python and PHP to code differentiate chart types via what appears as the action URL/file name on the address bar/command line.

    Feel free, as such, to try the Pie Chart++ (now public) web application you can also try below.


    Previous relevant Python Matplotlib Pie Chart Labels Tutorial is shown below.

    Python Matplotlib Pie Chart Labels Tutorial

    Python Matplotlib Pie Chart Labels Tutorial

    Today, further to yesterday’s Python Matplotlib Pie Chart PHP Crontab Tutorial‘s Pie Chart via Python Matplotlib “guinea pig” phase, fleshing out Pie Chart labelling …

    … begging the question …

    Guinea pig to what?

    Do you cavye in?! Chortle, chortle. Well, our answer is …

    Cater for lots of other chart types Python Matplotlib can help interface to.

    … and we have a bit of a strange idea we want to try regarding this, while today’s second “not so strange” task preparatory work was to “abstract” the term “Pie Chart” and associated phrases out of being hardcodings to be a derivative of what URL is showing in the web browser address bar. Even if our “strange idea” disappoints, and we’ll let you know tomorrow, we think, we’d want to embark on this genericization anyway, the Earth being the distance away from Mars and Venus the way it is. As such …

    … are definitely worth a peruse.


    Previous relevant Python Matplotlib Pie Chart PHP Crontab Tutorial is shown below.

    Python Matplotlib Pie Chart PHP Crontab Tutorial

    Python Matplotlib Pie Chart PHP Crontab Tutorial

    Onto yesterday’s Python Matplotlib Pie Chart Primer Tutorial‘s work …

    • further to local Apache/PHP web server, like MAMP, PHP understanding and supervising a Python Matplotlib module based Pie Chart web application … today …
    • extending that PHP meets Python theme on the public RJM Programming we involve …
      1. updated PHP … to suit the use of …
      2. crontab

        ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "piechartpython_*.py"`; do /usr/bin/python3 $i ${i##*/} /home/rjmprogr/public_html/Python/ ; done'
        ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "piechartpython_*.py"`; do rm -f "$i"; done'
        ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "plot_*.png" -mmin +122`; do rm -f "$i"; done'
        ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "output_image_*.png" -mmin +122`; do rm -f "$i"; done'

        … means to run Python code where new arguments add image file folder and IP address personalization parts so that users do not stumble on other user Pie Chart specifications

    … all still dealing with a minimalist Pie Chart we’ll add complexity to into the future with …


    Previous relevant Python Matplotlib Pie Chart Primer Tutorial is shown below.

    Python Matplotlib Pie Chart Primer Tutorial

    Python Matplotlib Pie Chart Primer Tutorial

    We’re off on a new discovery tour regarding …

    • Python … as a programming language, and it’s module …
    • Matplotlib
    • … starting down this road with a simple …

      • command line Pie Chart application … Python pie_chart_python.py (superceeding this)

        # pie_chart_python.py
        # September - 2026
        # RJM Programming
        # Thanks to https://www.w3schools.com/python/default.asp

        import os
        import matplotlib

        # Set backend to Agg before importing pyplot
        matplotlib.use('Agg')

        import matplotlib.pyplot as plt
        import matplotlib.image as mpimg

        import numpy as np
        #import plotly.express as px
        from PIL import Image

        nums = []
        sofar = 0

        if len(nums) <= 0:
        while sofar < 100:
        print(f"Enter your percentage value regarding a pie chart where so far {sofar} has already been allocated.")
        anum = input()
        nums.append(int(anum))
        sofar += int(anum)

        y = np.array(nums)

        fig, ax = plt.subplots()

        ax.pie(y)

        fig.savefig("plot.png")

        # 1. Load the image
        img = mpimg.imread('plot.png')

        # 2. Process or manipulate the image (Optional)
        # For example, let's look at the shape or apply changes
        #print(f"Image shape: {img.shape}")

        # 3. Create the plot without displaying it
        plt.imshow(img)
        plt.axis('off') # Hide axis lines and labels

        # 4. Save the result to a file instead of showing it
        plt.savefig('output_image.png', bbox_inches='tight', pad_inches=0)
        plt.close()

        # Open the PNG image file
        #with Image.open("plot.png") as img:
        # Display the image using your system's default viewer
        #img.show()

        # Optional: Load the data into memory so you can safely close the file
        #img.load()

        exit()

        … and …
      • working on our local Apache/PHP MAMP web server (only, so far, the reason being our public web server has no graphical $DISPLAY existant, and so the plt.show() Python codeline cannot be used, so we need to think more on integrations there) … PHP pie_chart_python.php

        <?php
        // pie_chart_python.php
        // RJM Programming - September, 2026
        if (php_sapi_name() === 'cli') {
        $command = '/usr/local/bin/python3 ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'piechartpython.py'; // Your command
        if (1 == 1) {
        exec($command);
        } else {
        // Open the process pointer for reading
        $handle = popen($command, 'r');

        if ($handle) {
        // Read and output the buffer until the end of the file
        while (!feof($handle)) {
        echo fread($handle, 2048);
        flush(); // Send it to the browser immediately
        }
        pclose($handle);
        }
        }
        exit;
        } else if (strpos(str_replace('?','',$_SERVER['QUERY_STRING']), '&') === false) {
        $waspy=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pie_chart_python.py');
        if (strpos($waspy, "nums = []") !== false) {
        if (isset($_GET['array'])) {
        file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'piechartpython.py', str_replace("nums = []","nums = [" . str_replace('+',' ',urldecode($_GET['array'])) . "]",$waspy));
        }
        }
        //exec('php ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pie_chart_python.php > /dev/null 2> /dev/null');
        exec('cd ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . ' ; /Applications/MAMP/bin/php/php7.4.33/bin/php -n ./pie_chart_python.php > /dev/null 2> /dev/null ; open ./plot.png');
        sleep(3);
        }
        echo "<html><body style=\"background:url(./plot.png);background-repeat:no-repeat;background-size:contain;\"></body></html>";
        ?>

      … allowing a web browser address bar URL such as …


      http://localhost:8888/pie_chart_python.php?array=12,5,1,68

      … working for us, here, locally, displaying a Pie Chart …

      … via our local Python3 Matplotlib modularized environment.

      Did you know?

      Your installer for Python modules is likely to be one of, for the example of matplotlib module …


      pip install matplotlib

      … or …


      pip3 install matplotlib

      … corresponding to whether command line …


      python pie_chart_python.py

      … or …


      python3 pie_chart_python.py

      … works for you at your local system command line.

      If this was interesting you may be interested in this too.


      If this was interesting you may be interested in this too.


      If this was interesting you may be interested in this too.


      If this was interesting you may be interested in this too.

Posted in eLearning, Event-Driven Programming, Operating System, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a comment

Python Matplotlib Pie Chart Labels Tutorial

Python Matplotlib Pie Chart Labels Tutorial

Python Matplotlib Pie Chart Labels Tutorial

Today, further to yesterday’s Python Matplotlib Pie Chart PHP Crontab Tutorial‘s Pie Chart via Python Matplotlib “guinea pig” phase, fleshing out Pie Chart labelling …

… begging the question …

Guinea pig to what?

Do you cavye in?! Chortle, chortle. Well, our answer is …

Cater for lots of other chart types Python Matplotlib can help interface to.

… and we have a bit of a strange idea we want to try regarding this, while today’s second “not so strange” task preparatory work was to “abstract” the term “Pie Chart” and associated phrases out of being hardcodings to be a derivative of what URL is showing in the web browser address bar. Even if our “strange idea” disappoints, and we’ll let you know tomorrow, we think, we’d want to embark on this genericization anyway, the Earth being the distance away from Mars and Venus the way it is. As such …

… are definitely worth a peruse.


Previous relevant Python Matplotlib Pie Chart PHP Crontab Tutorial is shown below.

Python Matplotlib Pie Chart PHP Crontab Tutorial

Python Matplotlib Pie Chart PHP Crontab Tutorial

Onto yesterday’s Python Matplotlib Pie Chart Primer Tutorial‘s work …

  • further to local Apache/PHP web server, like MAMP, PHP understanding and supervising a Python Matplotlib module based Pie Chart web application … today …
  • extending that PHP meets Python theme on the public RJM Programming we involve …
    1. updated PHP … to suit the use of …
    2. crontab

      ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "piechartpython_*.py"`; do /usr/bin/python3 $i ${i##*/} /home/rjmprogr/public_html/Python/ ; done'
      ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "piechartpython_*.py"`; do rm -f "$i"; done'
      ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "plot_*.png" -mmin +122`; do rm -f "$i"; done'
      ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "output_image_*.png" -mmin +122`; do rm -f "$i"; done'

      … means to run Python code where new arguments add image file folder and IP address personalization parts so that users do not stumble on other user Pie Chart specifications

… all still dealing with a minimalist Pie Chart we’ll add complexity to into the future with …


Previous relevant Python Matplotlib Pie Chart Primer Tutorial is shown below.

Python Matplotlib Pie Chart Primer Tutorial

Python Matplotlib Pie Chart Primer Tutorial

We’re off on a new discovery tour regarding …

  • Python … as a programming language, and it’s module …
  • Matplotlib
  • … starting down this road with a simple …

    • command line Pie Chart application … Python pie_chart_python.py (superceeding this)

      # pie_chart_python.py
      # September - 2026
      # RJM Programming
      # Thanks to https://www.w3schools.com/python/default.asp

      import os
      import matplotlib

      # Set backend to Agg before importing pyplot
      matplotlib.use('Agg')

      import matplotlib.pyplot as plt
      import matplotlib.image as mpimg

      import numpy as np
      #import plotly.express as px
      from PIL import Image

      nums = []
      sofar = 0

      if len(nums) <= 0:
      while sofar < 100:
      print(f"Enter your percentage value regarding a pie chart where so far {sofar} has already been allocated.")
      anum = input()
      nums.append(int(anum))
      sofar += int(anum)

      y = np.array(nums)

      fig, ax = plt.subplots()

      ax.pie(y)

      fig.savefig("plot.png")

      # 1. Load the image
      img = mpimg.imread('plot.png')

      # 2. Process or manipulate the image (Optional)
      # For example, let's look at the shape or apply changes
      #print(f"Image shape: {img.shape}")

      # 3. Create the plot without displaying it
      plt.imshow(img)
      plt.axis('off') # Hide axis lines and labels

      # 4. Save the result to a file instead of showing it
      plt.savefig('output_image.png', bbox_inches='tight', pad_inches=0)
      plt.close()

      # Open the PNG image file
      #with Image.open("plot.png") as img:
      # Display the image using your system's default viewer
      #img.show()

      # Optional: Load the data into memory so you can safely close the file
      #img.load()

      exit()

      … and …
    • working on our local Apache/PHP MAMP web server (only, so far, the reason being our public web server has no graphical $DISPLAY existant, and so the plt.show() Python codeline cannot be used, so we need to think more on integrations there) … PHP pie_chart_python.php

      <?php
      // pie_chart_python.php
      // RJM Programming - September, 2026
      if (php_sapi_name() === 'cli') {
      $command = '/usr/local/bin/python3 ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'piechartpython.py'; // Your command
      if (1 == 1) {
      exec($command);
      } else {
      // Open the process pointer for reading
      $handle = popen($command, 'r');

      if ($handle) {
      // Read and output the buffer until the end of the file
      while (!feof($handle)) {
      echo fread($handle, 2048);
      flush(); // Send it to the browser immediately
      }
      pclose($handle);
      }
      }
      exit;
      } else if (strpos(str_replace('?','',$_SERVER['QUERY_STRING']), '&') === false) {
      $waspy=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pie_chart_python.py');
      if (strpos($waspy, "nums = []") !== false) {
      if (isset($_GET['array'])) {
      file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'piechartpython.py', str_replace("nums = []","nums = [" . str_replace('+',' ',urldecode($_GET['array'])) . "]",$waspy));
      }
      }
      //exec('php ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pie_chart_python.php > /dev/null 2> /dev/null');
      exec('cd ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . ' ; /Applications/MAMP/bin/php/php7.4.33/bin/php -n ./pie_chart_python.php > /dev/null 2> /dev/null ; open ./plot.png');
      sleep(3);
      }
      echo "<html><body style=\"background:url(./plot.png);background-repeat:no-repeat;background-size:contain;\"></body></html>";
      ?>

    … allowing a web browser address bar URL such as …


    http://localhost:8888/pie_chart_python.php?array=12,5,1,68

    … working for us, here, locally, displaying a Pie Chart …

    … via our local Python3 Matplotlib modularized environment.

    Did you know?

    Your installer for Python modules is likely to be one of, for the example of matplotlib module …


    pip install matplotlib

    … or …


    pip3 install matplotlib

    … corresponding to whether command line …


    python pie_chart_python.py

    … or …


    python3 pie_chart_python.py

    … works for you at your local system command line.

    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.

Posted in eLearning, Event-Driven Programming, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a comment

Python Matplotlib Pie Chart PHP Crontab Tutorial

Python Matplotlib Pie Chart PHP Crontab Tutorial

Python Matplotlib Pie Chart PHP Crontab Tutorial

Onto yesterday’s Python Matplotlib Pie Chart Primer Tutorial‘s work …

  • further to local Apache/PHP web server, like MAMP, PHP understanding and supervising a Python Matplotlib module based Pie Chart web application … today …
  • extending that PHP meets Python theme on the public RJM Programming we involve …
    1. updated PHP … to suit the use of …
    2. crontab

      ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "piechartpython_*.py"`; do /usr/bin/python3 $i ${i##*/} /home/rjmprogr/public_html/Python/ ; done'
      ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "piechartpython_*.py"`; do rm -f "$i"; done'
      ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "plot_*.png" -mmin +122`; do rm -f "$i"; done'
      ksh -c 'for i in `find /home/rjmprogr/public_html/Python -name "output_image_*.png" -mmin +122`; do rm -f "$i"; done'

      … means to run Python code where new arguments add image file folder and IP address personalization parts so that users do not stumble on other user Pie Chart specifications

… all still dealing with a minimalist Pie Chart we’ll add complexity to into the future with …


Previous relevant Python Matplotlib Pie Chart Primer Tutorial is shown below.

Python Matplotlib Pie Chart Primer Tutorial

Python Matplotlib Pie Chart Primer Tutorial

We’re off on a new discovery tour regarding …

  • Python … as a programming language, and it’s module …
  • Matplotlib
  • … starting down this road with a simple …

    • command line Pie Chart application … Python pie_chart_python.py (superceeding this)

      # pie_chart_python.py
      # September - 2026
      # RJM Programming
      # Thanks to https://www.w3schools.com/python/default.asp

      import os
      import matplotlib

      # Set backend to Agg before importing pyplot
      matplotlib.use('Agg')

      import matplotlib.pyplot as plt
      import matplotlib.image as mpimg

      import numpy as np
      #import plotly.express as px
      from PIL import Image

      nums = []
      sofar = 0

      if len(nums) <= 0:
      while sofar < 100:
      print(f"Enter your percentage value regarding a pie chart where so far {sofar} has already been allocated.")
      anum = input()
      nums.append(int(anum))
      sofar += int(anum)

      y = np.array(nums)

      fig, ax = plt.subplots()

      ax.pie(y)

      fig.savefig("plot.png")

      # 1. Load the image
      img = mpimg.imread('plot.png')

      # 2. Process or manipulate the image (Optional)
      # For example, let's look at the shape or apply changes
      #print(f"Image shape: {img.shape}")

      # 3. Create the plot without displaying it
      plt.imshow(img)
      plt.axis('off') # Hide axis lines and labels

      # 4. Save the result to a file instead of showing it
      plt.savefig('output_image.png', bbox_inches='tight', pad_inches=0)
      plt.close()

      # Open the PNG image file
      #with Image.open("plot.png") as img:
      # Display the image using your system's default viewer
      #img.show()

      # Optional: Load the data into memory so you can safely close the file
      #img.load()

      exit()

      … and …
    • working on our local Apache/PHP MAMP web server (only, so far, the reason being our public web server has no graphical $DISPLAY existant, and so the plt.show() Python codeline cannot be used, so we need to think more on integrations there) … PHP pie_chart_python.php

      <?php
      // pie_chart_python.php
      // RJM Programming - September, 2026
      if (php_sapi_name() === 'cli') {
      $command = '/usr/local/bin/python3 ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'piechartpython.py'; // Your command
      if (1 == 1) {
      exec($command);
      } else {
      // Open the process pointer for reading
      $handle = popen($command, 'r');

      if ($handle) {
      // Read and output the buffer until the end of the file
      while (!feof($handle)) {
      echo fread($handle, 2048);
      flush(); // Send it to the browser immediately
      }
      pclose($handle);
      }
      }
      exit;
      } else if (strpos(str_replace('?','',$_SERVER['QUERY_STRING']), '&') === false) {
      $waspy=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pie_chart_python.py');
      if (strpos($waspy, "nums = []") !== false) {
      if (isset($_GET['array'])) {
      file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'piechartpython.py', str_replace("nums = []","nums = [" . str_replace('+',' ',urldecode($_GET['array'])) . "]",$waspy));
      }
      }
      //exec('php ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pie_chart_python.php > /dev/null 2> /dev/null');
      exec('cd ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . ' ; /Applications/MAMP/bin/php/php7.4.33/bin/php -n ./pie_chart_python.php > /dev/null 2> /dev/null ; open ./plot.png');
      sleep(3);
      }
      echo "<html><body style=\"background:url(./plot.png);background-repeat:no-repeat;background-size:contain;\"></body></html>";
      ?>

    … allowing a web browser address bar URL such as …


    http://localhost:8888/pie_chart_python.php?array=12,5,1,68

    … working for us, here, locally, displaying a Pie Chart …

    … via our local Python3 Matplotlib modularized environment.

    Did you know?

    Your installer for Python modules is likely to be one of, for the example of matplotlib module …


    pip install matplotlib

    … or …


    pip3 install matplotlib

    … corresponding to whether command line …


    python pie_chart_python.py

    … or …


    python3 pie_chart_python.py

    … works for you at your local system command line.

    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.

Posted in eLearning, Event-Driven Programming, Installers, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , | Leave a comment

Python Matplotlib Pie Chart Primer Tutorial

Python Matplotlib Pie Chart Primer Tutorial

Python Matplotlib Pie Chart Primer Tutorial

We’re off on a new discovery tour regarding …

  • Python … as a programming language, and it’s module …
  • Matplotlib
  • … starting down this road with a simple …

    • command line Pie Chart application … Python pie_chart_python.py (superceeding this)

      # pie_chart_python.py
      # September - 2026
      # RJM Programming
      # Thanks to https://www.w3schools.com/python/default.asp

      import os
      import matplotlib

      # Set backend to Agg before importing pyplot
      matplotlib.use('Agg')

      import matplotlib.pyplot as plt
      import matplotlib.image as mpimg

      import numpy as np
      #import plotly.express as px
      from PIL import Image

      nums = []
      sofar = 0

      if len(nums) <= 0:
      while sofar < 100:
      print(f"Enter your percentage value regarding a pie chart where so far {sofar} has already been allocated.")
      anum = input()
      nums.append(int(anum))
      sofar += int(anum)

      y = np.array(nums)

      fig, ax = plt.subplots()

      ax.pie(y)

      fig.savefig("plot.png")

      # 1. Load the image
      img = mpimg.imread('plot.png')

      # 2. Process or manipulate the image (Optional)
      # For example, let's look at the shape or apply changes
      #print(f"Image shape: {img.shape}")

      # 3. Create the plot without displaying it
      plt.imshow(img)
      plt.axis('off') # Hide axis lines and labels

      # 4. Save the result to a file instead of showing it
      plt.savefig('output_image.png', bbox_inches='tight', pad_inches=0)
      plt.close()

      # Open the PNG image file
      #with Image.open("plot.png") as img:
      # Display the image using your system's default viewer
      #img.show()

      # Optional: Load the data into memory so you can safely close the file
      #img.load()

      exit()

      … and …
    • working on our local Apache/PHP MAMP web server (only, so far, the reason being our public web server has no graphical $DISPLAY existant, and so the plt.show() Python codeline cannot be used, so we need to think more on integrations there) … PHP pie_chart_python.php

      <?php
      // pie_chart_python.php
      // RJM Programming - September, 2026
      if (php_sapi_name() === 'cli') {
      $command = '/usr/local/bin/python3 ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'piechartpython.py'; // Your command
      if (1 == 1) {
      exec($command);
      } else {
      // Open the process pointer for reading
      $handle = popen($command, 'r');

      if ($handle) {
      // Read and output the buffer until the end of the file
      while (!feof($handle)) {
      echo fread($handle, 2048);
      flush(); // Send it to the browser immediately
      }
      pclose($handle);
      }
      }
      exit;
      } else if (strpos(str_replace('?','',$_SERVER['QUERY_STRING']), '&') === false) {
      $waspy=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pie_chart_python.py');
      if (strpos($waspy, "nums = []") !== false) {
      if (isset($_GET['array'])) {
      file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'piechartpython.py', str_replace("nums = []","nums = [" . str_replace('+',' ',urldecode($_GET['array'])) . "]",$waspy));
      }
      }
      //exec('php ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'pie_chart_python.php > /dev/null 2> /dev/null');
      exec('cd ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . ' ; /Applications/MAMP/bin/php/php7.4.33/bin/php -n ./pie_chart_python.php > /dev/null 2> /dev/null ; open ./plot.png');
      sleep(3);
      }
      echo "<html><body style=\"background:url(./plot.png);background-repeat:no-repeat;background-size:contain;\"></body></html>";
      ?>

    … allowing a web browser address bar URL such as …


    http://localhost:8888/pie_chart_python.php?array=12,5,1,68

    … working for us, here, locally, displaying a Pie Chart …

    … via our local Python3 Matplotlib modularized environment.

    Did you know?

    Your installer for Python modules is likely to be one of, for the example of matplotlib module …


    pip install matplotlib

    … or …


    pip3 install matplotlib

    … corresponding to whether command line …


    python pie_chart_python.py

    … or …


    python3 pie_chart_python.py

    … works for you at your local system command line.

    If this was interesting you may be interested in this too.

Posted in eLearning, GUI, Installers, Operating System, Tutorials | Tagged , , , , , , , , , , , , , , | Leave a comment

Worldwide Country Quiz Game Revisit Tutorial

Worldwide Country Quiz Game Revisit Tutorial

Worldwide Country Quiz Game Revisit Tutorial

We’re revisiting the Worldwide Quiz of Worldwide Country Quiz Game Wikipedia Images Tutorial to …

  • add more questions
  • add a WorldBank summary button

… via …

… you can also try below clicking here.


Previous relevant Worldwide Country Quiz Game Wikipedia Images Tutorial is shown below.

Worldwide Country Quiz Game Wikipedia Images Tutorial

Worldwide Country Quiz Game Wikipedia Images Tutorial

We’re revisiting the Wordwide Country Quiz web application of Worldwide Country Quiz Game Moderation Tutorial for a few reasons …

  • we wanted to add Wikipedia (thanks) image optional functionality to add to the Video content of interest functionality supplied after a user answers a quiz question
  • we wanted to fix up an “Andorra annoyance” we’ll explain later
  • we wanted to fit content above the fold better on this MacBook Air laptop screen tweaking some widths and heights and cellspacing and cellpadding and border widths

Here’s the new Javascript function for the new “Wikipedia images related to …” a link onclick logic …


function startwt(selovalue) {
var was=document.title;
notsofast=false;
document.getElementById('addthis').value='';
if (was.indexOf('Country ') != 0) document.title='Country ' + was;
document.getElementById('tdmydiv').style.width='800px;'
document.getElementById('tdmydiv').style.height='720px;'
document.getElementById('mydiv').style.width='800px;'
document.getElementById('mydiv').style.height='720px;'
document.getElementById('mydiv').innerHTML=document.getElementById('mydiv').innerHTML.split('<br>')[0] + '<br><iframe id=ourcanvas style="border:6px solid yellow;width:800px;height:690px;cursor:progress;" src="/PHP/fgc/?tzexact=' + encodeURIComponent(selovalue.substring(0,1).toUpperCase() + selovalue.substring(1)) + '&tznickname=' + encodeURIComponent(selovalue.replace(/_/g,' ')) + '' + '" title="Thanks, Wikipedia"></iframe>';
document.getElementById('mydiv').style.cursor='progress';
if (document.getElementById('iready')) { document.getElementById('iready').style.cursor='progress'; }
setTimeout(mydivptr, 1000);
}

function mydivptr() {
if (document.getElementById('addthis').value != '0') {
if (document.getElementById('iready')) { document.getElementById('iready').style.cursor='progress'; }
setTimeout(mydivptr, 1000);
} else {
document.getElementById('mydiv').style.cursor='pointer';
if (document.getElementById('ourcanvas')) { document.getElementById('ourcanvas').style.cursor='pointer'; }
if (document.getElementById('iready')) { document.getElementById('iready').style.cursor='pointer'; }
}
}

… a “progress” cursor or two hopefully showing the user it might take some time displaying these Wikipedia images.

The “Andorra annoyance” was a bit of a kludge whereby, previously, we’d placed Andorra in every answer set for all the questions. Why? Well, sorry Andorra, but you do not show up in the overall GeoChart map of the wooooooorrrrlllllddd, and this has the advantage that a map is still produced for a data set that is “only Andorra” (rather than using Latitude 90 or -90 which can cause issues). But one question had dual correct answers as a result, and that is not fair, so we separated “display” data set members from “answer” data set members, enabling us to give Andorra less exposure, so to speak!

Feel free to try the Worldwide Country Quiz‘s worldwide_country_quiz.htm, changed in this way, benefitting from the adage “every picture is worth a thousand words”.


Previous relevant Worldwide Country Quiz Game Moderation Tutorial is shown below.

Worldwide Country Quiz Game Moderationation Tutorial

Worldwide Country Quiz Game Moderation Tutorial

Blogs have become a bigger and bigger force in the online world. They allow for various forms of moderation from fully vetted to “allowed through to the keeper”. Today, for our quiz we add some functionality that is …

  • suitable for Moderation at the top level, where a user knows the Moderation Password … or …
  • suitable for those helpful users offering question ideas, and because they probably do not know the Moderation Password, can, alternatively, still pass their question idea on by sending an Email to the Moderator of the Quiz … oops … that’s me, folks

With these matters, we find, many heads are better than one, and we hope we get some of your suggestions here. If they are suitable, we’ll definitely add them into the mix (of questions) permanently.

Obviously, on the scale of Moderation seriousness, our email moderation arrangement is pretty loose. More stringent ones normally involve the use of an underlying database, and queueing suggested question entries, that a Moderator user, on a blog or CMS system, would, hopefully, monitor, to various levels of immediacy.

The issue of bots or software robots is very pertinent for uploaders of information, these days. Telling whether something uploaded to a website is for real or not, was a question long ago, broached with the many different kinds of CAPTCHA challenge-response tests that are so often seen at blog websites and CMS and eCommerce websites these days, to try to weed out those software robots, and only have an audience of real people … oh, by the way … this is for real … know you can’t see it, but we just spilled something, like any normal robotperson would do. We’ve done little bits and pieces with CAPTCHA here at this blog.

Another idea you can try for feedback on a blog or CMS is a poll, and there are heaps of ideas for polls programmers (like us) have written.

Adding onto day before yesterday’s Worldwide Country Quiz Game Personalization Tutorial, this time around, the supervising “parent” HTML and Javascript now looks like this (and whose code you could peruse and/or download via worldwide_country_quiz.htm,changed from yesterday in this way) was introduced to a new piece of PHP whose job it is to …

  • both detect incorrect Moderation Passwords, and try to hide Moderation Passwords from the public, and if all okay, with the URL data coming in, otherwise …
  • rewrite the “parent” supervising HTML and Javascript to include the new question into the mix of questions, up the top

… and its code you could call worldwide_country_quiz.php for your perusal.


Previous relevant Worldwide Country Quiz Game Personalization Tutorial is shown below.

Worldwide Country Quiz Game Personalization Tutorial

Worldwide Country Quiz Game Personalization Tutorial

You often come across the term “Personalization” with regard to software functionality. In broad brush terms we see the idea of …

Personalization of software is a means by which functionality is handed over to the user to shape its function, and quite often its appearance, on top of default settings built into the software

… which might be an important consideration, especially if the software is going to be sold and/or used widely.

Around here, we see “Personalization” closely related to “Accountability” …

“accountability” with a web application, for us, goes along the lines of having a web application whose data can be recalled and reused for another time

… in that we like to give users more control, by them being able to record a previous set of instructions they “lobbed onto” that was interesting to them.

With today’s “Personalization” work, building on yesterday’s Worldwide Country Quiz Game YouTube Tutorial, we want the user of our “Worldwide Country Quiz” to be able to add their own tailored questions into the mix of questions stored in the Javascript url_array[] array containing URLs to Google Chart Geo Chart executions for each quiz question. This new functionality is based on ISO Two Character Country Codes you might remember us also using when we presented TimeZone Country Places Primer Tutorial

Into the mix of the logic here, we need to venture into the world of two letter ISO Country Codes, and we work this with help from http://php.net/manual/en/datetimezone.getlocation.php and http://stackoverflow.com/questions/17842003/php-intl-country-code-2-chars-to-country-name and http://php.net/manual/en/function.timezone-identifiers-list.php and http://www.timezoneconverter.com/ to “hardcode” our HTML select “dropdown” elements we create in the process, and offer TimeZone information HTML select “dropdown” elements as well.

… some time back.

Yesterday’s YouTube video functionality introduced some potential “roadblocks” to a good UXy experience with too many Javascript alert and/or prompt windows getting in the way of setting up this YouTube viewing, so we’ve hived off alert and prompt windows to only happen after a button press if a number of other webpage interactions occur, as well, and then “transmission” is resumed … haven’t we ALL heard that?!

This time around, the supervising “parent” HTML and Javascript now looks like this (and whose code you could peruse and/or download via worldwide_country_quiz.htm, changed from yesterday in this way) or played in that other method mentioned above, just with the Google Chart Geo Chart, with this alternative live run (with its amended PHP code you could call geo_chart.php, unchanged from yesterday).

Thanks to pixabay for the free background image.


Previous relevant Worldwide Country Quiz Game YouTube Tutorial is shown below.

Worldwide Country Quiz Game YouTube Tutorial

Worldwide Country Quiz Game YouTube Tutorial

Building on our start to our “Worldwide Country Quiz” (game) project with Worldwide Country Quiz Game Primer Tutorial as shown below, today we add functionality that uses the marvellous YouTube API for embedding videos into HTML iframe elements, to offer the user a video viewing experience in parallel, and optional, regarding their Worldwide Quiz question followup thoughts.

The primary followup thought is to interface to the biggest online Video repository, YouTube, and use their great API to embed YouTube videos within HTML iframe elements on your webpage. Perusing YouTube you may have noticed that for many years now at YouTube Share button options, there have been ways to have YouTube help you to “cut” the video embedding HTML code necessary to display a video. Well, we do that in an HTML iframe element, optionally, in our quiz (game). The use of this added YouTube video playing functionality is dynamic by nature, and only changing the flow of the rest of the quiz if the user decides to click an HTML a element to start the ball rolling, and so we use Javascript DOM techniques on those wonderfully flexible HTML div elements to achieve this.

Another matter we have started out on today is the support the Google Chart Geo Chart has for Regional work, rather than just Country based lookups. Here, the Geo Chart accepts an International ISO Two Character Country Code to hone in on City names that we can display within those countries. We might be refining this functionality as time goes on, with a less restrictive and “hardcoded” regime.

The supervising “parent” HTML and Javascript now looks like this (and whose code you could peruse and/or download via worldwide_country_quiz.htm, changed from yesterday in this way) or played in that other method mentioned above, just with the Google Chart Geo Chart, with this alternative live run (with its amended PHP code you could call geo_chart.php, modified in this way).

We again hope you enjoy the quiz.


Previous relevant Worldwide Country Quiz Game Primer Tutorial is shown below.

Worldwide Country Quiz Game Primer Tutorial

Worldwide Country Quiz Game Primer Tutorial

It feels like we’ve been here before, because of Country Quiz Game Suite YouTube Tutorial‘s Flag/Capital/Currency “listy” approach, but the fact is, as you program, you find interesting alternative ways to do things, and you hope that alternative ways of doing things also present themselves. This latter concept is primarily good, in our eyes, because it is likely you’ll improve on an earlier effort, given its thinking and logic “added on” to your current thinking and logic.

And so today’s “Worldwide Country Quiz” came about for us thinking about that previous Country Quiz Game Suite YouTube Tutorial idea with a Google Chart mapping component addition (thanks). Then we thought of the Google Chart Geo Chart and wondered where it could fit into our line of thinking. We’d forgotten how it worked, and indeed, revisiting the Google Chart Geo Chart webpage, as with the MAMP “revisit” recently, we saw that “functionality” had extended, and moved on. It this like the John Lennon song? But the new Geo Chart functionality added to the traditional “marker” functionality we knew, might need to be looked at another day, whereas a rethink of the “marker” functionality and some additional user passed data, we decided, would be enough to swing it so that we could create this new “Worldwide Country Quiz” via …

  • adding to the Google Chart Geo Chart interface web application two new optional data items, namely [&]guess[=][CountryNameAnswer] and [&]question[=][QuestionThatGivesYouTheCountryNameAnswer] … and …
  • forming a possible relationship for this Google Chart Geo Chart to be an HTML iframe “child” of a “parent” (probably) HTML web application that takes an array of this “question” and “guess” data and forms URLs to the Google Chart. Geo Chart “child” who communicates back up to their parent by adjusting the …
  • HTML h4 “score” element of the supervising “parent” is being monitored via a “setTimeout” function by the “parent” .themselves, a change signifying the reason to go and pick out array question URL from the array we talked about earlier

The pleasing we found about this design, that we didn’t plan, but it became apparent, was that this “quiz” functionality can be used by just using the Google Chart Geo Chart web application on its own, have two players hovering, and while one player looks away, the other “questioning” player can, at the “rejigged” title.Javascript prompt window designate a guess and question data item, specifying the [&]onclick[=]Y as they do this, and proceed pretty much as normal with the rest of the normal Google Chart Geo Chart specification steps they normally do, but perhaps best off giving every Country they give a Value too the same Value, so that later when the “answering” player is allowed to turn around, what they see is a Javascript prompt window showing the “questioning” player’s quiz question with several equally tinted Country outlines (that are the only ones whose onclick “select” logic will trigger, when clicked or tapped) will show as the Multiple Choice choices constructed by the “questioning” player as the challenge for the “answering” player to pick (or click or tap) correctly.

Only in the case of that Google Chart Geo Chart being supervised by a (probably) HTML and Javascript web application that has any innerHTML type HTML element named “score”, a correct answer scores (all of) one point, but even if wrong, triggers an incrementing of the “goes” count part of the. …

parent.document.getElementById('score').innerHTML='Score: ' + [childDeterminedScoreValue] + '/' + [childDeterminedGoesBit];

… design, enough to give the supervising “parent” the means by which to know that the “show” (quiz) should go on.

This “Double Jeopardy” feeling game can be played via this supervised “parent” HTML and Javascript live run (whose code you could peruse and/or download via worldwide_country_quiz.html) or played in that other method mentioned above, just with the Google Chart Geo Chart, with this alternative live run (with its amended PHP code you could call geo_chart.php, modified in this way).

We hope you enjoy either or both of the quiz (game) itself, or the design features of it.


Previous relevant Country Quiz Game Suite YouTube Tutorial is shown below.

Country Quiz Game Suite YouTube Tutorial

Country Quiz Game Suite YouTube Tutorial

Today we are adding optional functionality to our previous web application from Country Quiz Game Suite Tutorial as shown below, and so it seems apt that we add one word for the title of today’s tutorial called “Country Quiz Game Suite YouTube Tutorial”, and that added word is “YouTube”, the biggest repository of videos in the online world.

So we are trying to spice up the “dry” look of the “Country Quiz Game Suite” of web applications by adding optional lookups of YouTube so that the user can choose to watch a video about the last question that was asked about in the quiz. Is there a reason to use a “YouTube” search for this, rather than a search engine search? For us, yes, and this can be put down to the wonders of the YouTube API designed for embedding in HTML iframe elements, which you can read a thread of blog posts about from Karaoke via YouTube API in Iframe Email Tutorial down. This ability is a huge UX positive for use of these web applications, on mobile platforms in particular, because the YouTube video “interface” parts of the webpage can be placed on the webpage the user is currently using, rather than having to open any new windows, and have the user be in danger of “losing the plot”.

Added to that advantage is the way the user can do research and development on topics that may interest them. To us, the most important point here.

So far we’ve talked about what the changes involve, and now we’ll turn our attention to how these changes were achieved. In broad brush terms …

  • we introduce new external Javascript you could call country_quiz.js called by the parent …

    … using a call like …

  • <script type='text/javascript' src='country_quiz.js' defer='defer'></script>
  • the defer HTML script tag attribute used to delay its loading to the end of loading, and also placed so that …
  • the call of the external Javascript is placed after local Javascript script tag client logic, because …
  • we “overload” the local Javascript “function check()” with a version from the external Javascript country_quiz.js, and we’ve discussed this Javascript style of overloading, before, when we presented Javascript Function Overload Primer Tutorial (where we overloaded the “encodeURIComponent” method) … and in that …
  • external Javascript country_quiz.js “function check()” potentially loads YouTube API functionality to an HTML iframe element which was first created via …
  • a one off setTimeout piece of logic is called by the external Javascript country_quiz.js to …
    • append the HTML for that YouTube API HTML iframe “container” element to document.body
    • append to the first HTML h3 element an HTML input type=checkbox, initially checked, toggling the use of that option YouTube API (to HTML iframe element) functionality

    … this setTimeout methodology remaining as independent from any potential logic clashes with parent HTML Javascript onload event logic

    Nothing changes about the peer to peer nature of this “Country Quiz Game Suite” of web applications, so we encourage you to try any/all of them … let’s try … flags.


    Previous relevant Country Quiz Game Suite Tutorial is shown below.

    Country Quiz Game Suite Tutorial

    Country Quiz Game Suite Tutorial

    Think we may have done a (web application software) set before, but honestly cannot locate it, but in any case we are here today to tell you about one of the joys of server side programming, in our case PHP.

    That joy, for us, is when you get into a pattern of completely peer to peer software components, in our case PHP serverside web applications. What do we mean by peer to peer in the way we feel about the thought? It means that several, usually small, completely independent web applications can …

    • as such, be easily unit tested within themselves … but …
    • they each have a similar, and really easy, approach to, just right at the end of coding … ie. when they are all individually coded … link them to be pointable at each other “peer to peer” … guess the difference here, unlike our penchant for “parent/child” (which we are also very fond of), each web application component is independent and of “equal” status in our tiny little woooorrrrrrlllld!

    … and this really appeals to us. Sometimes such an arrangement can be thought of as a “suite” of web applications (or programs).

    So what is that really simple mechanism of linking …

    ? We just have a rearranged HTML select (dropdown) element as below (exemplified below for the “Country Capital Quiz”) …


    <select onchange=' location.href="http://www.rjmprogramming.com.au/PHP/country_" + this.value.toLowerCase() + "_quiz.php"; '><option value=Capital>Capital</option><option value=Currency>Currency</option><option value=Flag>Flag</option></select>

    … but just arrange for the “home” top HTML option element to point at the quiz theme of interest for that piece of web application PHP software.

    And the placement of such a dropdown? Well, we often annoy our HTML h1 elements with such dropdowns, but we’ve been a little more UXy today, by replacing an otherwise less dynamic (HTML (table) th element) heading (single word), with something dynamic, and perhaps useful, to some quizzer users out there in “net” land!

    And this is what we like to think of as “clobbering” (but we encourage you to think of a less aggressive word perhaps) something not very dynamic with something dynamic, but optionally so, in that it is up to the user whether they make use of the added functionality, or not, is another way “some of us we like to roll”.

    Now, perhaps you are thinking it rather cheeky to say that this is so peer to peer with the quiz concepts being so different, and yes, that is so, but what does bring things together is the generosity of the “net” and its data resources to find out information, so, we’d like to thank …

    • Country Flag Quiz … thanks to Tiki Wiki CMS … effectively giving up a country list to work with … and …
    • Country Currency Quiz … thanks to this link links countries to currencies (and (3 letter) country codes and (3 letter) currency codes) … and …
    • Country Capital Quiz … thanks to this link links countries to capitals

    If you don’t have an interest in a server side language like PHP to glean such information, please consider Ajax programming techniques to be able to (just) stick with HTML and Javascript and CSS (client side) coding. But if you do … your PHP homework for today is to read about the file_get_contents method.


    Previous relevant Country Flag Quiz Game Primer Tutorial is shown below.

    Country Flag Quiz Game Primer Tutorial

    Country Flag Quiz Game Primer Tutorial

    Often, the simpler you make the working of a web application, the more appealing it is. Not always, but often. Lots of us like to test our knowledge with a quiz, and lots of us have our specialty subjects. So, today’s “Country Flag Quiz” web application, that we also talk about at WordPress 4.1.1’s Country Flag Quiz Game Primer Tutorial, will be right up the alley of some people. Got to say that it felt like a huge challenge to get a decent score in this game, speaking personally, and found myself resorting to … but we digress … to come up with a pass mark for the game.

    Today’s game we wrote in PHP (that you could call country_flag_quiz.php and which you can try with today’s live run link) and used its Supercalifragilisticexpialidocious glob method … we’ve spoken about (quite a bit) here.

    We need to thank the excellent Tiki Wiki CMS product (which you can try for yourself at this test website) for the help with the country flag imagery, as well, today.

    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.


    If this was interesting you may be interested in this too.

Posted in eLearning, Event-Driven Programming, Tutorials | Tagged , , , , , , , , , , , , , , , , , | Leave a comment

ComboBox Idea Datalist Tutorial

ComboBox Idea Datalist Tutorial

ComboBox Idea Datalist Tutorial

Onto yesterday’s ComboBox Idea Primer Tutorial‘s …

Button/Select

Input/Datalist ( … today,
as a second Combobox idea
we add …)

<button onclick="document.getElementById('ssave').focus();" id=bsave><span style='background-color:white;min-width:40px;padding:2 2 2 2;' onclick="event.stopPropagation();" id=ssave onblur=lsit(this); contenteditable="true"></span><select onclick="event.stopPropagation();" style='margin-left:100px;' contenteditable=yes onchange=recallsm(this); id=savedd><option id=defopt value=''>Optionally enter a value ...</option><option value=1>1</option></select></button>

<input oninput="if (1 == 11) { maggage(this); } else { setTimeout(premassage,200); }" id=myinput list="comboboxlist" value="">
<datalist id="comboboxlist">
<option value="List item 1">
<option value="List item 2">
<option value="List item 3">
<option value="List item 4">
</datalist>
Pros Cons
Streamlined look
Reselects obvious
Bit klunky
Reselects awkward

… and decided, as a Javascript ploy to use, with either idea, if an option value differs from it’s associated text (ie. it’s innerHTML or innerText) we’d represent a dropdown or datalist selected button or input element representation as …


innerHTML (value)

… which becomes more interesting, we’re hoping, by today’s additional functionality allowing a user to enter a URL (when the ? link appears) whose content is scoured for either …

  1. select … or …
  2. datalist

… first such element to glean content (especially as the ComboBox concept is all to do with “data”) to adopt for either guise of ComboBox. With the Input/Datalist one you maybe select a datalist option, but to have another go, you may have to change the input value to nothing for a datalist to be offered again. The Button/Select is more our hybrid not having this idea in place.

Feel free to try the changed second draft ComboBox ideas further explorations …


Previous relevant ComboBox Idea Primer Tutorial is shown below.

ComboBox Idea Primer Tutorial

ComboBox Idea Primer Tutorial

We’ve long been interested in what used to be a cute feature of GUI widgetry desktop web applications (before any mobile anything) called “ComboBox” as talked about at Button Innards Primer Tutorial. To us, what a “ComboBox”‘s essence is …

Combination of interactively keyboard entry and/or a dropdown means of data definition

… and, like with that tutorial linked to above, it is an HTML button element we start with …


<button onclick="document.getElementById('ssave').focus();" id=bsave><span style='background-color:white;min-width:40px;padding:2 2 2 2;' onclick="event.stopPropagation();" id=ssave onblur=lsit(this); contenteditable="true"></span><select onclick="event.stopPropagation();" style='margin-left:100px;' contenteditable=yes onchange=recallsm(this); id=savedd><option value=''>Optionally enter a value ...</option><option value=1>1</option></select></button>

… in our revisit to the concept, today, with a first draft ComboBox ideas exploratory effort.


Previous relevant Button Innards Primer Tutorial is shown below.

Button Innards Primer Tutorial

Button Innards Primer Tutorial

Yesterday’s …

… combine, today, starting down, again, the road to “button innards” interest.

We thought the “how we got there” concept of embedding the form elements of data into “button innards” (ie. it’s innerHTML) in our new “button innards” web application interesting. Of course you don’t have to do this simple exercise of calculating time elapsed between two datetimes this way, but what we wanted to show was that with lots of things in web design, by now, there is more than one way to crack an egg …


Previous relevant Bookmark Exporting Filtering Sharing Tutorial is shown below.

Bookmark Exporting Filtering Sharing Tutorial

Bookmark Exporting Filtering Sharing Tutorial

Yesterday’s Making of Ffmpeg Video Overall Effects Tutorial‘s “first draft” feel of our inhouse Bookmark Export Filtering web application showed just that. Yes, there was quite a lot to improve on, the way we saw it being …

  • adding a (local system) file browsing means (called by our <iframe scrolling=no frameborder=0 id=cbi data-style=’border-top:1px solid black;border-bottom:1px solid black;border-left:2px solid yellow;border-right:1px solid yellow;’ style=’width:173px;height:228px;margin-top:-194px;’ src=’/HTMLCSS/client_browsing.htm?totype=html&d=189786754′></iframe> file browsing helper) …

    function yesthreethree(restis) {
    if (document.getElementById('bmkdata')) {
    if (restis.indexOf('data:') == 0) {
    if (restis.indexOf(';base64,') != -1) {
    document.getElementById('bmkdata').value='' + window.atob(restis.split(';base64,')[1]);
    analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
    } else if (restis.indexOf(';utf8,') != -1) {
    document.getElementById('bmkdata').value='' + (restis.split(';utf8,')[1]);
    analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
    }
    } else {
    document.getElementById('bmkdata').value=restis;
    analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
    }
    }
    }

    … by which to add Web Browser Bookmark Exporting Data File Content into that lower HTML textarea element (and unlock the two UL/LI and Table button elements) … as well as …
  • add a details/summary “reveal” means by which the user can show working actual HTML elements in addition to the HTML coding we’ve only be showing (with that “first draft”)
  • add Sharing functionality via either Email or SMS conduits

… and because the amounts of data can be large (and too big for a method=GET HTML form arrangement that clientside HTML/Javascript is capable of servicing) we were glad adding this Sharing functionality that yesterday we’d already started down the line of thinking that the …

  • lower HTML textarea content … would always be associated with …
  • hashtag … representation

… because with these methodologies the web server restrictions on (address bar) URL lengths (ie. error 414) melt away, at least for data lengths we’ve encountered with our testing so far, and that applies also to …

  • “mailto:” “a” URL links to Email a recipient … or …
  • “sms:” “a” URL links to SMS a recipient

… Sharing


var mysubject='My Bookmarked Links ...', xlocationhref='';
if (('' + location.hash).indexOf('bmkdata=') != -1) { locationhash=location.hash; }

function doemail() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
mysubject='My Bookmarked Links ...';
if (document.getElementById('dtone').value.trim() != '' && document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' to ' + document.getElementById('dttwo').value.trim() + ' ... ';
} else if (document.getElementById('dtone').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' ... ';
} else if (document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links up until ' + document.getElementById('dttwo').value.trim() + ' ... ';
}
if (ulli && !utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}

function dosms() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
if (ulli && !utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}

function fillin() {
if (document.getElementById('rt') && document.getElementById('rd') && document.getElementById('rs')) {
if (document.getElementById('rd').innerHTML == document.getElementById('rs').outerHTML) {
document.getElementById('rd').innerHTML=document.getElementById('rs').outerHTML + document.getElementById('rt').value;
}
}
}

// Coming back from an email or SMS link click ...


if (document.URL.indexOf('?') == -1 && ('' + location.hash).indexOf('bmkdata=') != -1) {
setTimeout(function(){
document.getElementById('resulting').innerHTML=decodeURIComponent(('' + location.hash).split('bmkdata=')[1]).replace('><summary', ' open><summary').replace('<details title=', '<details data-title=');
fillin();
}, 5000);
}

… concepts, happily. Keeping it all “just clientside” means we can be talking meaningfully to a larger chunk of audience … if you’re listening, that is?!

So, again, feel free to try our “second draft” dated_bookmarks.html Dated Bookmark Export web application, yourself, perhaps below.


Previous relevant Making of Ffmpeg Video Overall Effects Tutorial is shown below.

Making of Ffmpeg Video Overall Effects Tutorial

Making of Ffmpeg Video Overall Effects Tutorial

At the end of yesterday’s Ffmpeg Video Overall Effects Tutorial there was an Unordered List …

… of links, am sure a lot of you would guess, were gleaned (within our Google Chrome web browser History (of that day)) Bookmarked (by us) webpages. We actually compiled via (the clunky and fairly slow) …

  • left half (of screen) with (Google Chrome) History showing, and right half with BBEdit Text Editor open at a new (ostensibly empty) file with loads of empty records available, and dragged URL records (identifiable, for us, via an asterisk, indicating a webpage was Bookmarked) from the left into the right to bring over the URLs (only) above, the rest constructed in a more manual way … and another way could have been …
  • use Google Chrome webpage showing Bookmark list and use its Export Bookmarks (no other choice but “All”) to a file that could be waded through to derive the list above … and then we thought of chance to code for …
  • hybrid “inhouse” clientside (ie. HTML/Javascript/CSS) web application …

    … so far asking for the content of a web browser Export Bookmarks operation’s output file’s HTML (in the case of Google Chrome) content in an HTML textarea element (the user can fill in themselves), and filterable via two input type=datetime-local starting and ending datetime(s) of interest (the user can fill in, optionally)

… and we wish we could have got that hour and a half back, from yesterday, via swapping yesterday for today?!

Feel free to try our “first draft” proof of concept Dated Bookmark Export web application, yourself.

Did you know?

Not all web browser brands have any Export Bookmarks functionality, like Google Chrome offers here on macOS. We’re going to be examining others, over time, and try to make our inhouse logic fit in with how that Bookmark data looks, on export, should we find any other Web Browser brands offering this functionality.


Previous relevant Ffmpeg Video Overall Effects Tutorial is shown below.

Ffmpeg Video Overall Effects Tutorial

Ffmpeg Video Overall Effects Tutorial

Today’s work (all on our macOS MAMP local Apache/PHP/MySQL web server environment) is only “toe dipping” in a “great big sea of possibilities” regarding the great ffmpeg‘s “filtering style abilities” further to yesterday’s Ffmpeg Video Subliminal Message Tutorial, but …

  • as you try out things you discover others …
  • can be a foot in the door researching this type of work yourself …

… we’re hoping. And it’s not as if ffmpeg was on it’s own here with the second concept below …

  1. fade in and fade out video effect … inspired by Top 20 best commands for FFmpeg to try …

    ffmpeg -i brush__turkey.m4v -vf "fade=in:0:5,fade=out:73:5" brushturkey_fadein_fadeout.m4v
  2. colour balancing improvements regarding an underexposed video

… as the …

  • Gimp image editor’s abilities (matching a lot of what you read Photoshop can help with too) with Curves helped out … along with …
  • Python expertly designed to link Gimp and ffmpeg “whole of video” manipulations

… enacting …

  1. selection of a video still image, ideally not containing the Brush Turkey …
  2. open image in Gimp
  3. “Aoife”‘esque Colours -> Curves…
  4. opens Adjust Colour Curves subwindow (initially a straight line)
  5. but dragging it to become a curve in a way that brightens the underexposed image to improve … we then …
  6. click + button to Save presets to a name …
  7. controlled by left pointing infilled triangle icon click that Manages Presets … and we ended up with …
  8. bush_-_turkey.presets


  9. and then we discovered this Converts GIMP colour curve to something the FFmpeg can read in the curve filter excellent project, the extraction of the Python there allowing us to go …

    python3 ../gimp_curves_ffmpeg.py

    … to output brush.out

  10. which formed most of the final curves — ffmpeg examples inspired ffmpeg command …

    ffmpeg -y -i brush__turkey.m4v -filter_complex "
    [0:v]
    curves=
    master='0.0/0 0.01171875/0.033751864777061671 0.0234375/0.067435644319434537 0.03515625/0.10098325339242978 0.046875/0.1343266067613586 0.05859375/0.16739761919153215 0.0703125/0.20012820544826168 0.08203125/0.23245028029685835 0.09375/0.26429575850263332 0.10546875/0.29559655483089792 0.1171875/0.32628458404696314 0.12890625/0.35629176091614018 0.140625/0.38555000020374036 0.15234375/0.41399121667507488 0.1640625/0.44154732509545486 0.17578125/0.46815024023019147 0.1875/0.49373187684459596 0.19921875/0.51822414970397945 0.2109375/0.54155897357365324 0.22265625/0.56366826321892849 0.234375/0.58448393340511628 0.24609375/0.6039378988975278 0.2578125/0.62196207446147445 0.26953125/0.63848837486226717 0.28125/0.65344871486521738 0.29296875/0.66677500923563593 0.3046875/0.67964838149075235 0.31640625/0.6910493179152648 0.328125/0.70214399210941336 0.33984375/0.71293769240972005 0.3515625/0.72343570715270744 0.36328125/0.73364332467489723 0.375/0.74356583331281156 0.38671875/0.75320852140297312 0.3984375/0.76257667728190348 0.41015625/0.77167558928612512 0.421875/0.78051054575216006 0.43359375/0.78908683501653032 0.4453125/0.7974097454157586 0.45703125/0.80548456528636625 0.46875/0.81331658296487608 0.48046875/0.82091108678780988 0.4921875/0.82827336509169003 0.50390625/0.83540870621303842 0.515625/0.84232239848837742 0.52734375/0.84901973025422939 0.5390625/0.85550598984711601 0.55078125/0.86178646560355965 0.5625/0.86786644586008233 0.57421875/0.87375121895320629 0.5859375/0.87944607321945378 0.59765625/0.88495629699534695 0.609375/0.89028717861740791 0.62109375/0.89544400642215893 0.6328125/0.90043206874612158 0.64453125/0.90525665392581867 0.65625/0.9099230502977721 0.66796875/0.91443654619850423 0.6796875/0.91880242996453687 0.69140625/0.92302598993239249 0.703125/0.92711251443859277 0.71484375/0.93106729181966053 0.7265625/0.93489561041211733 0.73828125/0.93860275855248565 0.75/0.94219402457728751 0.76171875/0.94567469682304517 0.7734375/0.94905006362628086 0.78515625/0.95232541332351639 0.796875/0.95550603425127423 0.80859375/0.9585972147460764 0.8203125/0.96160424314444504 0.83203125/0.96453240778290228 0.84375/0.96738699699797048 0.85546875/0.97017329912617156 0.8671875/0.97289660250402765 0.87890625/0.97556219546806111 0.890625/0.97817536635479407 0.90234375/0.98074140350074845 0.9140625/0.98326559524244661 0.92578125/0.98575322991641046 0.9375/0.98820959585916268 0.94921875/0.99063998140722487 0.9609375/0.99304967489711937 0.97265625/0.99544396466536833 0.984375/0.9978281390484941 0.99609375/1":red="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1":green="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1":blue="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1'
    [v]" -map '[v]' -an brushturkey_better_colour.m4v

… was crucial to our discoveries today

  1. fade in and fade out video effect …
  2. colour balancing improvements regarding an underexposed video …

Further reading for you here, could involve …


Previous relevant Ffmpeg Video Subliminal Message Tutorial is shown below.

Ffmpeg Video Subliminal Message Tutorial

Ffmpeg Video Subliminal Message Tutorial

Revisiting the brilliant ffmpeg, as discussed at the recent Ffmpeg Install and Public Face Tutorial

  • the other day we were reading the excellent Fastest way to extract frames using ffmpeg? and noted it down for a future discussion … and then today …
  • we were inspired by a Brush Turkey which we videoed via an iPhone’s Camera app’s Video mode of capture option …
  • downloaded to this MacBook Air (via AirDrop … good for Apple sharing when the sizes get big … raw video 1:19s long)

And that’s where we are at here, thinking this would be a good opportunity to try creating a Subliminal Message video. But before we go on and “give the game away” you may want to see the result of our Subliminal Message insertion via ffmpeg theme to our work, in action, below. Unlock or give up to unlock the content below the video quiz below …

What is the secret word?
Raw video … to it’s slides …
ffmpeg -i ../IMG_2784.MOV -r 1/1 %03d.jpg
ffmpeg -i %03d.jpg brush_turkey.m4v
Hard guess?
ffmpeg -r 8 -i %03d.jpg brush-turkey.m4v
Easier guess?
ffmpeg -r 6 -i %03d.jpg brush__turkey.m4v


Previous relevant Ffmpeg Install and Public Face Tutorial is shown below.

Ffmpeg Install and Public Face Tutorial

Ffmpeg Install and Public Face Tutorial

Our recent web server VPS migration can not only be …

  • a migration of functionality (eg. this WordPress blog) … as well as …
  • an opportunity to open up new vistas of serverside functionality

… such as today’s ffmpeg install, which is only feasible because of the …

  • additional diskspace … chance on new web server allows for …
  • chance to install ffmpeg …

    dnf config-manager --set-enabled powertools
    dnf install --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm -y
    dnf install --nogpgcheck https://mirrors.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-$(rpm -E %rhel).noarch.rpm -y
    dnf install ffmpeg

    … thanks to this great link, along with ImageMagick …

… on this new “public face” (even more public when DNS is applied later).

Codewise this involved …

… further to recent work touching these ideas with Animated GIF Creator Overlayed Images Top Left Tutorial.

See some of this public install of ffmpeg here.


Previous relevant Animated GIF Creator Overlayed Images Top Left Tutorial is shown below.

Animated GIF Creator Overlayed Images Top Left Tutorial

Animated GIF Creator Overlayed Images Top Left Tutorial

To add to the recent Animated GIF Creator Overlayed Images Tutorial‘s user interaction logic, we add a way to control those two “overlay” CSS properties …

  • left
  • top

… but more in the sense that we allow CSS …

  • margin-eft
  • margin-top

… come into the picture, after a dalliance with CSS calc thoughts.

This allows for offsetted images be part of the “overlay mix” that you can retry this at our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator.


Previous relevant Animated GIF Creator Overlayed Images Tutorial is shown below.

Animated GIF Creator Overlayed Images Tutorial

Animated GIF Creator Overlayed Images Tutorial

We wanted to add an “Overlayed Images” functionality component to the “Animated GIF Creator” of Animated GIF Creator Video Intranet Tutorial, today.

The work involves both of the HTML design “big concepts” we are keen on at this blog …

  • overlay … via CSS control of
    1. position:absolute;top:0px;left:0px;
    2. opacity:1.0;
    3. z-index:1;
  • reveal … lately, mostly, via HTML use of …
    1. details
    2. summary

… the latter being the “container” for our interactive input be able to control overlay items 2 and 3, which affect the “overlayed images” output display via the clicking of a new “Overlay Images” button. That button …


<input disabled onclick="overlaythem();" id="overlayit" type="button" title='Overlay Images' value='Overlay Images'></input>

… starts off as disabled … and works with new HTML div elements …


<div id=overlayedj><h1>&nbsp;</h1><br></div><div id=overlayedi></div>

… until at least two images are defined, at which time the button becomes enabled …


function onfit(ithis) {
if (ithis.value == '' && ('' + ithis.placeholder).indexOf(']') != -1) {
ithis.value=('' + ithis.placeholder).split('[')[1].split(']')[0];
}
ithis.type='number';
}

function overlayds(inm, newop, newzi) {
var outdets='';
var divrect=null;
var poslt=' data-style=\"position:absolute;left:px;top:px;\" ';
var reposit='';
if (!document.getElementById('detsum' + ('' + inm))) {
if (document.getElementById('overlayedi').innerHTML == '') {
document.getElementById('overlayedi').title='Overlayed Images Below ... Click for It in New Window ...';
document.getElementById('overlayedj').innerHTML='<h1>Overlayed Images Below ...</h1><br>';
divrect=document.getElementById('overlayedi').getBoundingClientRect();
poslt=' data-style=\"position:absolute;left:' + ('' + divrect.left).split('.')[0].trim() + 'px;top:' + ('' + eval(200 + eval('' + ('' + divrect.top).split('.')[0]))).trim() + 'px;\" ';
} else {
poslt=' data-style=\"' + document.getElementById('detsum1').getAttribute('data-style') + '\" ';
}
outdets='&nbsp;&nbsp;<details ' + poslt + ' id=\"detsum' + ('' + inm) + '\" style=\" display:inline-block; \" open><summary id=\"sumdet' + ('' + inm) + '\">Overlay</summary><input onchange=overlayds(' + inm + ',this.value,String.fromCharCode(32)); onblur=overlayds(' + inm + ',this.value,String.fromCharCode(32)); type=text onfocus=onfit(this); id=\"opacity' + ('' + inm) + '\" style=\" display:inline-block; \" placeholder=\"Opacity [1.00]\" value=\"\" step=0.01 min=0.00 max=1.00></input><br><input onfocus=onfit(this); onchange=overlayds(' + inm + ',String.fromCharCode(32),this.value); onblur=overlayds(' + inm + ',String.fromCharCode(32),this.value); type=text id=\"zindex' + ('' + inm) + '\" style=\" display:inline-block; \" placeholder=\"Z-Index [1]\" value=\"\" min==999 max=999 step=1></input><br></details>';
reposit=poslt.replace(' data-',' ');
reposit=reposit.replace('\"', '\"opacity:1.0;z-index:1;');
if (document.getElementById('overlayedi').innerHTML == '') {
document.getElementById('overlayedi').innerHTML='<img id=\"imgdet' + ('' + inm) + '\" src=\"' + document.getElementById('slideshow' + ('' + inm).replace(/^1$/g,'')).value + '\" ' + reposit + '></img>';
document.getElementById('overlayedi').style.width=('' + document.getElementById('imgdet' + ('' + inm)).width).replace('px','') + 'px';
document.getElementById('overlayedi').style.height=('' + document.getElementById('imgdet' + ('' + inm)).height).replace('px','') + 'px';
document.getElementById('overlayedi').onclick = function(){ var woo=window.open('','_blank','top=100,left=100,width=' + document.getElementById('overlayedi').style.width.replace('px','') + ',height=' + document.getElementById('overlayedi').style.height.replace('px','')); woo.document.write('<html><head><title>Overlayed Images Below ...</title></head><body>' + document.getElementById('overlayedi').outerHTML.replace(/left\:[\ ]*/g,'left:0.0').replace(/top\:[\ ]*/g,'top:0.0') + '</body></html>'); woo.document.title='Overlayed Images Below ...'; };
} else {
document.getElementById('overlayedi').innerHTML+='<img id=\"imgdet' + ('' + inm) + '\" src=\"' + document.getElementById('slideshow' + ('' + inm).replace(/^1$/g,'')).value + '\" ' + reposit + '></img>';
}
} else {
document.getElementById('detsum' + ('' + inm)).style.display='inline-block';
if (newop != ' ') {
if (newop.trim() == '') {
document.getElementById('imgdet' + inm).style.opacity='1.0';
} else {
document.getElementById('imgdet' + inm).style.opacity='' + newop;
}
}
if (newz != ' ') {
if (newz.trim() == '') {
document.getElementById('imgdet' + inm).style.zIndex='1';
} else {
document.getElementById('imgdet' + inm).style.zIndex='' + newzi;
}
}
}
return outdets;
}

function overlaythem() {
var suffidea=1;
while (document.getElementById('slideshow' + ('' + suffidea).replace(/^1$/g, '')).value != '') {
document.getElementById('ours' + ('' + suffidea)).innerHTML+=overlayds(suffidea,'','');
suffidea++;
}
}

You can try this at our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator.


Previous relevant Animated GIF Creator Video Intranet Tutorial is shown below.

Animated GIF Creator Video Intranet Tutorial

Animated GIF Creator Video Intranet Tutorial

With the recent Animated GIF Creator Video Revisit Tutorial (preceding Animated GIF Creator Video Share Tutorial) we wrote …

… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …

… setting up what could be “lost functionality”, but today, we come around to either adding a new …


Convert to Video

… dropdown option should all be ritchy ditch, and if not, often we will add new “Advice” dropdown options to remind the user what they’d need to arrange to get to a “Convert to Video” scenario.

Selecting “Convert to Video” sets up a hydrid “Internet/Intranet” feeling scenario where …

  • the user is using our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator containing this new “Convert to Video” option off the “ImageMagick” dropdown …
  • the user defines some image slide data … eg. browsing to create data-URIs (but careful not to do too many) …
  • the user selects that “Convert to Video” option …
  • a window.open popup calls URL like …
    HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php?video=1687644
    … in a new window …
  • those data-URIs are converted into /tmp/jmtmp0*.[imageExt] files … ready for …
  • ffmpeg commands like …

    ffmpeg -framerate 0.1 -i /tmp/jmtmp%03d.jpeg -c:v libvpx-vp9 -pix_fmt yuva420p -lossless 1 -c:a copy video.webm; ffmpeg -i video.webm video.mov 2>> video.bad

    … be nested in PHP exec (via MAMP local web server incarnation of the inhouse Animated GIF Creator) as a way to create videos …
  • which can be downloaded within that public domain “parent” inhouse Animated GIF Creator session

Which begs the question …


How do we know when to offer the "Convert to Video" option on that dropdown?

This logic is centred around a few useful ideas (with this cross domain scenario ruling out Ajax and Iframe src= definitions, as useful ways to go) …

  1. open the MAMP local web server “HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php” URLs in an iframe (with onload event logics) pointed at by a window.open second argument (effectively avoiding any loose useful or not popup windows hanging around) …
  2. the most we can ask at the receiver is that window.opener is defined … and if so, just at that discovery …
  3. an image called “amhere.jpg” is created via PHP GD functionality … and back at the “public domain” parent within the iframe onload logic (where with contentWindow or contentDocument do not expect a document.body or even a document) …
  4. we attempt to “hotlink” that MAMP local web server image … as per …

    var tryit='http://localhost:8888/PHP/animegif/amhere.jpg';

    document.getElementById('ctvopt').value='advice';
    document.getElementById('ctvopt').innerHTML='Advice on Convert to Video';

    if (tryit != '') {
    var im=new Image();

    im.onload = function() {
    document.getElementById('ctvopt').value='video';
    console.log('this.height=' + eval('' + this.height));
    if (eval('' + this.height) >= 20) {
    document.getElementById('ctvopt').innerHTML='Convert to Video';
    } else {
    document.getElementById('ctvopt').innerHTML='Convert to Video (but ffmpeg not installed or in unexpected place)';
    }
    document.getElementById('imsel').title='All except Convert to Video, which needs ffmpeg installed, use ImageMagick';
    };


    im.src=tryit;
    tryit='';
    }


Previous relevant Animated GIF Creator Video Revisit Tutorial is shown below.

Animated GIF Creator Video Revisit Tutorial

Animated GIF Creator Video Revisit Tutorial

We hope you realize that our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changed emailhtml.php inhouse email creator helper serverside PHP web application) can be accessed …

  1. in that public RJM Programming mode of use above … but even better can be …
  2. download relevant code to macOS MAMP local web server places off /Applications/MAMP/htdocs/ $_SERVER[‘DOCUMENT_ROOT’] places …

    … and check the code for the validity of any ImageMagick paths … and if not all these conditions, simulate the same and cobble it together in the code

… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …


brew install ffmpeg

… to open a whole new woooooorrrrlllllddd of video creation opportunities using this MAMP local web URL …


HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php# Animated GIF Creator on MAMP local web server

And so, onto yesterday’s Animated GIF Creator PDF Last Reveal Tutorial we decided to revisit video creation parts of our inhouse Animated GIF Creator on this MacBook Air. We needed to reinstall ffmpeg, and we show that in today’s animated GIF tutorial picture and also created the “video.mov” video below, in the process …

… we tweaked out of “video/mp4” ffmpeg created videos thinking towards “video/webm” or “video.mov” ffmpeg created videos that suit the “few frames but spaced out video” we wanted to achieve for all practical purposes, that “video/mp4” playing too fast for us to see anything much but the first and last slide …

<?php

$videofr=" -r " . (1000 / $delay) . " ";
$videofr=" -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS ";
$videomime="video/mp4";
$videoext=".mp4";

$videofr=" -framerate 0.1 ";
$videoext=".webm";
$videomime="video/webm";
$videomidbit="-c:v libvpx-vp9 -pix_fmt yuva420p -lossless 1 -c:a copy";
//$videocmd=$videopath . "ffmpeg -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS " . " -i " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "%03d.jpg " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mp4 2> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
//$svideocmd="ffmpeg -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS " . " -i " . "%03d.jpg " . "video.mp4";
$videocmd=$videopath . "ffmpeg " . $videofr . " -i " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "%03d.jpg " . $videomidbit . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " 2> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$svideocmd="ffmpeg " . $videofr . " -i " . "%03d.jpg " . $videomidbit . " " . "video" . $videoext;
$videocmd.="; " . $videopath . "ffmpeg -i " . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mov 2>> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$svideocmd.="; " . "ffmpeg -i " . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mov 2>> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$videohtml="<video title='" . $svideocmd . "' controls id=ivideo type='" . $videomime . "'><source src='video.mov'></source><source src='video" . $videoext . "'></source></video>";
$videosuffix="";

?>


Previous relevant Animated GIF Creator PDF Last Reveal Tutorial is shown below.

Animated GIF Creator PDF Last Reveal Tutorial

Animated GIF Creator PDF Last Reveal Tutorial

As a web application programmer we like buttons. There are “buttons” and there are buttons. Yes, there is an HTML button element, and it and the input type=button element render as that part of a webpage most recognizable to users the world over. You click or tap this button and something usually happens.

But we also enjoy “Emoji Buttons” for we graphically challenged programmers. Using an emoji text and graphic can make span elements or p elements or lots of other HTML elements that have an innerHTML property, be like a very succinct button like entity, also appreciated around here because it takes up so little webpage “real estate”.

And so onto yesterday’s Animated GIF Creator PDF Order Tutorial we set about, today, “revealing” any enduring animated GIF and/or PDF created during a previous session, using Emoji Buttons as the email/SMS sharing action buttons.

We say “revealing” because, like the way “Emoji Buttons” save webpage “real estate”, so can the use of the HTML5 details/summary element combination. It is in that summary “enduring” header part of that combination we can place some “Emoji Buttons”. You will (once you start creating and sharing animated GIFs and/or PDFs) see from our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changed emailhtml.php inhouse email creator helper serverside PHP web application) that we place three Emoji Buttons …

  1. 📟 SMS (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “sms:” link click/tap
  2. 📧 Email (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “mailto:” link click/tap
  3. 📧 Email (Animated GIF or PDF) Enduring relevance (ie. data URIs involved, so do not rely on any absolute URL whose content might change or start not to exist down the track) … via PHP mail function means of sending email with HTML attachment containing the relevant Animated GIF or PDF

Yes, “Emoji Buttons” can have their size controlled but not by the usual CSS width and height properties, but by the CSS font-size property. Here is the new Javascript function that those Emoji Button “onclick” logic points to …

<?php eho ”

function askes(isemail, isoab) {
var esask='', izhr=null, izform=null;
var isoa=document.getElementById(isoab.id.replace('b',''));
if (isemail) {
esask=prompt('Please enter email address to send this to', '');
if (esask != null) {
if (esask.indexOf('@') != -1) {
if (isoa.outerHTML.indexOf('<img') == 0) {
//alert('More Animated GIF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "');
izform.append('subj', 'My Latest Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><img style=\"width:100%;height:900px;\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "\"></img></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else if (isoa.outerHTML.indexOf('<a') != 0) {
//alert('More PDF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "');
izform.append('subj', 'My Latest PDF via Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "\"></object></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else {
isoa.href='mailto:' + esask.trim() + '?' + isoa.href.split('?')[1];
isoa.click();
}
}
}
} else {
esask=prompt('Please enter SMS number to send this to', '');
if (esask != null) {
if ((esask.trim() != '' && esask.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
isoa.href='sms:' + esask.trim() + '&' + isoa.href.split('&')[1];
isoa.click();
}
}
}
}

“; ?>

Cute, huh?!


Previous relevant Animated GIF Creator PDF Order Tutorial is shown below.

Animated GIF Creator PDF Order Tutorial

Animated GIF Creator PDF Order Tutorial

Order becomes an interesting subject to us today regarding the PDF conversions possible in amongst the Animated GIF creating of yesterday’s Animated GIF Creator PDF Share Tutorial.

That is because we may have some advanced users out there that want that possibility of differentiating the data of …

  • animated GIF … containing all features asked for, in totality … from the chance for the user to have any of the PDF conversion option sets below …
  • PDF … one of …
    1. no PDF conversion
    2. full PDF conversion … with the user choices regarding raw image content and title and watermarking and ImageMagick and GD modifiers
    3. light PDF conversion … with the user choices regarding just raw image content (the only option out of these last three available before today’s work)
    4. medium PDF conversion … with the user choices regarding just ImageMagick and GD modifiers

… perhaps as a “before and after” tool regarding presentations, maybe?

No dropdowns used here (enforcing an order), just the user’s order in which they choose to select the “PDF conversion” option from the ImageMagick dropdown (in relation to other selections) determining how and when this PDF conversion occurs in the workflow through to creating the animated GIF with our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application. Just remember to select PDF Conversion as early as possible to do that “light PDF conversion” option above and last thing if you are interested in “full PDF conversion” (where you can create PDF documents with the bells and whistles ImageMagick and GD modifiers can offer) and today’s tutorial picture is an example of “medium PDF conversion” (when we ordered our settings by first Grayscale, second PDF conversion and last title and watermarking options). Clicking or tapping the ImageMagick link can get the user to the Javascript popup window where they might define the email or SMS recipient for that PDF conversion data file download.


Previous relevant Animated GIF Creator PDF Share Tutorial is shown below.

Animated GIF Creator PDF Share Tutorial

Animated GIF Creator PDF Share Tutorial

To share yesterday’s Animated GIF Creator PDF Conversion Tutorial PDF data formatted image slides (and then onto an animated GIF) would be a step forward, wouldn’t you say?

What sharing conduits do we code for? We always intended …

  • email … but as the day wore on trying to get the usage to work on mobile and non-mobile we decided to relinquish our wish to not have to create a user specific enduring (until that same browser type and user combination share) PDF document for the user request … mainly to get mobile email downloads to be friendly … and so this opened the door for …
  • SMS … to access that enduring PDF as a URL in the SMS message (that becomes a link for the recipient)

… the email methodology used being that “midair feeling” Ajax/FormData approach in our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application …

<?php echo ”

function emailhtmlit() {
var induri=ginduri;
var pemail='" . urldecode($_GET['outpdf']) . urldecode($_POST['outpdf']) . "';
if (pemail != null) {
if (pemail.indexOf('@') != -1 || (pemail.trim() != '' && pemail.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
if (induri.trim() == '') {
if (induri == '') {
document.getElementById('pdfproposed').src='./animegif.pdf?rand=' + Math.floor(Math.random() * 19854654);
} else {
ginduri='found';
}
setTimeout(emailhtmlit, 15000);
} else {
ginduri='';
var zhr = new XMLHttpRequest();
var zform=new FormData();
if (pemail.indexOf('@') != -1) { zform.append('to', pemail); }
zform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . "animegif.pdf" . "');
zform.append('subj', 'My PDF version of Animated GIF via RJM Programming ... ');
zform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"><embed style=\"width:100%;height:900px;\" type=\"application/pdf\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></embed></object></div></body>'));
//zform.append('tdhuhta', ('<body><iframe srcdoc=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></iframe></body>'));
zhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
zhr.send(zform);
if (pemail.indexOf('@') == -1) {
var hrefp=document.getElementById('pdfsms').href.split('&body=')[0];
hrefp+=pemail.trim() + '&body=' + document.getElementById('pdfsms').href.split('&body=')[1];
document.getElementById('pdfsms').href=hrefp;
document.getElementById('pdfsms').click();
}
}
}
}
}

“; ?>

… and this called on our PHP email creator helper to better interface in its $_POST arguments reading section in our changed emailhtml.php inhouse email creator helper serverside PHP web application …

<?php

$psacv="";
$okayin=true;
$subd="";
$subptitle='';
$subdata="";
$subfile="";
$getpostfound=false;
<br>
foreach( $_POST as $name=>$val ) {
if ($val != "") {
// blah blah blah //file_put_contents("jnv." . $name, "jnv.inv00 " . strlen($phpcont));
if ($name == "tdhuhta" && $subfile != "" && $subdata != "" && strpos(str_replace("+"," ",urldecode($val)), $subfile) !== false) {
if (strpos(str_replace("+"," ",urldecode($val)), ' srcdoc="') !== false || strpos(str_replace("+"," ",urldecode($val)), ' data="') !== false) {
if (strpos(str_replace("+"," ",urldecode($val)), ' title="') !== false) { //file_put_contents('haaa.aaa',explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1]);
if (!file_exists('../PHP/animegif/' . explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1] . 'animegif.pdf')) {
if (file_exists('../PHP/animegif/animegif.pdf')) {
copy('../PHP/animegif/animegif.pdf', '../PHP/animegif/' . explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1] . 'animegif.pdf');
}
}
$psacv=explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0] . 'animegif.pdf';
$subptitle='<a target=_blank href="' . explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0] . 'animegif.pdf">Mobile whole PDF</a><br>';
}
$vrest=explode('"', explode(' srcdoc="',str_replace("+"," ",urldecode($val)))[1])[0];
$val=urlencode("<body>" . $subptitle . "<iframe style='width:100%;height:95vh;overflow:auto;-webkit-overflow-scrolling:touch;' srcdoc='<div style=width:92%;height:95vh;overflow:auto;-webkit-overflow-scrolling:touch;><object id=myobj style=width:95%;height:95vh; type=application/pdf data=\"" . $subdata . "\"></object></div>'></iframe></body>");
}
// blah blah blah
if ($name == "inline") {
if (strpos(str_replace("+"," ",urldecode($val)), 'animegif.pdf') !== false && isset($_POST['tdhuhta'])) {
if (file_exists(str_replace("+"," ",urldecode($val)))) {
$subfile=str_replace("+"," ",urldecode($val));
$subdata='data:application/pdf;base64,' . base64_encode(file_get_contents(str_replace("+"," ",urldecode($val))));
$val="";
$name="x";
$okayin=false;
}
}
// blah blah blah
}
}

?>

Creating the PDFs got helped out in “reliability terms” via the introduced animegif.php inhouse Wait Around Longer serverside PHP web application.


Previous relevant Animated GIF Creator PDF Conversion Tutorial is shown below.

Animated GIF Creator PDF Conversion Tutorial

Animated GIF Creator PDF Conversion Tutorial

Up to yesterday’s Animated GIF Creator Slide Specific Application Tutorial the recent progress had us …

  • knowing the good places to intervene between the original slide image data being read in and the animated GIF slide output … and …
  • involvement of ImageMagick … so, today, these two help us down the path to …
  • adding PDF as an output format possibility, in addition to the animated GIF and other ideas floated as output formats

… with our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application.

We have more “reliability work” and “email sharing work” to go after today’s start, but it primarily called on ImageMagick command line’s talent for a command like …


convert /tmp/imtmp0*.*[gGmMiI]* /tmp/imtmp000.pdf

… to “concatenate” into the one output PDF file (called “/tmp/imtmp000.pdf”) the slides, arranged by our code into that “/tmp/imtmp0*.*[gGmMiI]*” (file specification) arrangement above. Yes, we meant “convert” above, as “mogrify” (batch work) appears not to be able to perform this task.

We hope you stay the course!


Previous relevant Animated GIF Creator Slide Specific Application Tutorial is shown below.

Animated GIF Creator Slide Specific Application Tutorial

Animated GIF Creator Slide Specific Application Tutorial

It’s all fine and good improving on the ImageMagick and GD and Exif functionality modifications like with yesterday’s Animated GIF Creator Exif Rotation Compensation Tutorial, but in reality, if you are going to start creating animated GIFs to explain a process, you are going to want to apply these “slide modifiers” on a slide by slide basis, rather than enforcing a “whole of animated GIF slide set” paradigm, as for the last few days worth of work.

And so, we decided to do what we often do, “sliding in” more functionality (chortle, chortle). We tend to want to …

  • start with hardcoded text (or element) … somewhere … today it happens to be in an HTML span element that once involved just …

    <span id="smyim"></span>

    … and used to get filled, Javascript DOM wise, when needed via …

    document.getElementById('smyim').innerHTML='ImageMagick switches: '; // and yes, it remains that way even now, but read on ...
  • add intelligence (quite often that being onclick logic(s)) to that hardcoded element via …

    <span id=smyim title=Application onclick=applyto(); style=cursor:pointer;text-decoration:underline;></span>
  • that serves the purpose, as the user clicks/taps it (alerted to that fact, perhaps, because we underline the element and add an appropriate cursor when hovering over it (plus a title)), of calling Javascript …

    function applyto() {
    var huhto=prompt('Apply ImageMagick and/or GD to which slides, in comma separated list, counting starting with 1? Defaults to applying to all slides. Comma delimit. Negatives mean all but. Ranges can be specified. For example ... 2,4-7,9', document.getElementById('appliedto').value);
    if (huhto == null) { huhto=''; }
    if (huhto.trim() == '') {
    document.getElementById('appliedto').value='';
    document.getElementById('smyim').title='Application';
    } else {
    document.getElementById('appliedto').value=huhto.trim();
    document.getElementById('smyim').title=huhto.trim();
    }
    }
  • to glean an (often times out of the normal workflow of the web application) informational piece of data, interactively, from the user, via a Javascript prompt popup

So that’s the clientside of this work … “alerting the user to the existance of the functionality” you might say.

And then there’s “the application” of that nuanced user requirement. And that’s where the “inhouse ‘our’ prefix to wrapper function name paradigms” come in handy. We introduced “blanket” functionality thoughts via this approach, and so to “partially undo” that thinking, we make the “our” prefix conditional, as is available to us with our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application (helped out by a changed exif_rotation_check.php inhouse Exif detector PHP helper), as per

<?php

function ourcomplicated($inio, $iappl) {
$ideasl=explode(",", str_replace(' ','',$iappl));
$xour="our";
for ($iqa=0; $iqa<sizeof($ideasl); $iqa++) {
if (trim($ideasl[$iqa]) != '') {
$xour="";
if (('-' . $inio) == trim($ideasl[$iqa])) { return ""; }
$ideasr=explode("-", str_replace(' ','',trim($ideasl[$iqa])));
for ($iqb=0; $iqb<sizeof($ideasr); $iqb++) {
if (('' . $inio) == trim($ideasr[$iqb])) {
return "our";
}
if ($iqb == 1) {
if (trim($ideasr[1]) == "") { $ideasr[1]="99999999"; }
if ($inio >= $ideasr[0] && $inio <= $ideasr[1]) { return "our"; }
}
}
}
}
return $xour;
}


?>

teamed with

<?php

$ours="our";
for ($io=1; $io$ours=ourcomplicated($io, $iappliedto);
// blah blah blah
if (strpos($aphoto[0], ";base64,") !== false) {
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromstring(base64_decode('" . explode(";base64,",$aphoto[0])[1] . "'));", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachonestring));
} else if (strpos(strtolower($aphoto[0]), "//") !== false) {
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromstring(@file_get_contents('" . $prefix . $aphoto[0] . "'));", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".jp") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
//file_put_contents('qpqp.qpqp', $eachone . "\n\n" . $atext . "\n\n" . str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromjpeg('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
//file_put_contents('qpqpqp.qpqpqp', $gifphp);
} else if (strpos(strtolower($aphoto[0]), ".png") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefrompng('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".gif") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromgif('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".bmp") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefrombmp('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
}
// blah blah blah
}

?>

We use these “new abilities” better explaining “the abscence or otherwise of Exif checking”, and the implications of that in the animated GIF creator woooooorrrrrrlllllddd, contrasting the first two slides, showing one with “No Exif checking” (the bad old days) versus “With Exif checking” (the renaissance of liberated thinking in the South South East woooorrrrrlllldddd) in today’s animated GIF presentation.

Get the picture?!


Previous relevant Animated GIF Creator Exif Rotation Compensation Tutorial is shown below.

Animated GIF Creator Exif Rotation Compensation Tutorial

Animated GIF Creator Exif Rotation Compensation Tutorial

The recent couple of days of work like yesterday’s Animated GIF Creator GD Transformations Interfacing Tutorial have given us “rotation functionality” tools to counteract the unusual effects Exif …

Exchangeable image file format (officially Exif, according to JEIDA/JEITA/CIPA specifications) is a standard that specifies the formats for images, sound, and ancillary tags used by digital cameras (including smartphones), scanners and other systems handling image and sound files recorded by digital cameras.

… can have converting mobile device camera created images into animated GIFs via our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.

But with some photos it’s there in the photo’s metadata information letting the future user know what orientation the camera of that mobile device was in as you took the photo. In order to help our animated GIF creator, the first slide image is scoured for Exif metadata and if found, a suitable rotation correction can be applied to the slides there and then. In order to scour for Exif metadata we needed to write a exif_rotation_check.php inhouse Exif detector PHP helper.


Previous relevant Animated GIF Creator GD Transformations Interfacing Tutorial is shown below.

Animated GIF Creator GD Transformations Interfacing Tutorial

Animated GIF Creator GD Transformations Interfacing Tutorial

The PHP GD image library is so much more useful than for the “filters” interfaced to with yesterday’s Animated GIF Creator GD Filter Interfacing Tutorial. Under an “umbrella term” transformations, today we add interfacing to GD functionality …

… into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application. We found it more user friendly to tailor the asking of numerical parameters for these GD calls work via …

<?php

$gfcds='';
$gdfs=[];
if (strpos(($imvgn . $imafn . $imqty . $imgds), '|') !== false) {
$gdfs=explode(';', trim(explode('|', $imvgn . $imafn . $imqty . $imgds)[1]));
for ($itf=0; $itf<sizeof($gdfs); $itf++) {
if (trim($gdfs[$itf]) != '') {
if (trim($gdfs[$itf]) == strtolower(trim($gdfs[$itf]))) {
$gfcds.=chr(10) . " imagefilter(" . "$" . "oimo, IMG_FILTER_" . strtoupper(trim($gdfs[$itf])) . "); " . chr(10);
} else if (trim($gdfs[$itf]) == strtoupper(trim($gdfs[$itf]))) {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo, IMG_" . str_replace(" ","_",strtoupper(trim( str_replace(explode(' ',$gdfs[$itf])[0] . ' ','',$gdfs[$itf]) ))) . "); " . chr(10);
} else if (strpos(strtoupper(trim($gdfs[$itf])), "SCALE") !== false) {
$gfcds.=chr(10) . " $" . "oimo=image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . str_replace(" ","_",(trim( str_replace(explode(',',explode(" ",(trim($gdfs[$itf])))[0])[0],'',$gdfs[$itf]) ))) . "); " . chr(10);
} else if (strpos(strtoupper(trim($gdfs[$itf])), "COPY") === false) {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . str_replace(" ","_",(trim( str_replace(explode(',',explode(" ",(trim($gdfs[$itf])))[0])[0],'',$gdfs[$itf]) ))) . "); " . chr(10);
} else {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . "," . "$" . "oimo" . explode("," . "$" . "oimo", $gdfs[$itf])[1] . "); " . chr(10);
}
}
}
}

?>

… to work with modified PHP writes PHP of the ilk …

<?php echo ”

function ourimagecreatefromstring(\$inidis) {
global \$imvgn, \$imafn, \$imqty, \$imgds, \$gfcds;
if ((\$imvgn . \$imafn . \$imqty . \$imgds) != '' && strpos(\$inidis, ';base64,') !== false) {
\$extis=str_replace('jpeg','jpg',explode('/', explode(';base64,', \$inidis)[0])[1]);
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, base64_encode(explode(';base64,', \$inidis)[1]));
\$fzis=filesize(\$sofarout);
if (trim(explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0]) != '') {
exec('" . $immogpath . "mogrify ' . explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0] . ' /tmp/imtmp.' . \$extis);
}
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false ||
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
\$oimo=null;
if (strtolower(\$extis) == 'png') {
\$oimo=imagecreatefrompng('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'bmp') {
\$oimo=imagecreatefrombmp('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'gif') {
\$oimo=imagecreatefromgif('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
\$oimo=imagecreatefromjpeg('/tmp/imtmp.' . \$extis); " . $gfcds . "
}
if (\$oimo) { return \$oimo; }
} else if ((\$imvgn . \$imafn . \$imqty . \$imgds) != '') {
\$extis=str_replace('jpeg','jpg','" . $inextis . "');
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
if (strpos(strtolower((\$imvgn . \$imafn . \$imqty . \$imgds)), '-format ') === false) { \$imvgn.=' -format " . str_replace('jpg','jpeg',$inextis) . " '; }
if (trim(explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0]) != '') {
exec('" . $immogpath . "mogrify ' . explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0] . ' /tmp/imtmp.' . \$extis);
}
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
\$oimo=null;
if (strtolower(\$extis) == 'png') {
\$oimo=imagecreatefrompng('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'bmp') {
\$oimo=imagecreatefrombmp('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'gif') {
\$oimo=imagecreatefromgif('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
\$oimo=imagecreatefromjpeg('/tmp/imtmp.' . \$extis); " . $gfcds . "
}
if (\$oimo) { return \$oimo; }
}
return imagecreatefromstring(\$inidis);
}

“; “?>

… and to get there clientwise, in the Javascript …


function gdadd(oselo) {
var pari=1, thispari='0';
if (oselo.value != '') {
//alert(oselo.value);
var oselovalue=oselo.value.replace(',' + '$' + 'oimo','~' + '$' + 'oimo').split(',')[0];
//alert(oselovalue);
var oseloval=oselo.value;
if (document.getElementById('imgds')) { if (document.getElementById('imgds').value == '') { document.getElementById('imgds').value='|'; } else { document.getElementById('imgds').value+=';'; } }
oseloval=oseloval.replace(',' + '$' + 'oimo','~' + '$' + 'oimo');
oseloval=oseloval.replace(oseloval.split(',')[0], '');
while (oseloval.indexOf(',') != -1) {
//alert(oseloval);
if ((oseloval + ' ').substring(1).split(',')[0].trim() != '') {
if ((' ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' ').toLowerCase().indexOf(' colour ') != -1) {
thispari=prompt('Enter' + (' ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' ').toLowerCase().replace(' colour ', ' red,green,blue colour ') + 'parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
if (thispari == null) {
thispari='0';
} else if (thispari.trim() == '') {
thispari='0';
} else if (thispari.indexOf(',') == -1) {
oselovalue+=',' + thispari + '';
} else {
oselovalue+=',imagecolorallocate(' + '$' + 'oimo,' + thispari + ')';
}
} else {
thispari=prompt('Enter ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
if (thispari == null) {
thispari='0';
} else {
oselovalue+=',' + thispari;
}
}
oseloval=oseloval.replace(',' + (oseloval + ' ').substring(1).split(',')[0].trim(),'').trim();
} else {
thispari=prompt('Enter numerical parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
oseloval=oseloval.replace(',','');
if (thispari == null) {
thispari='0';
} else {
oselovalue+=',' + thispari;
}
}
//oseloval=oseloval.replace(oseloval.split(',')[0] + ',', '');
//oseloval=oseloval.replace(',','');
pari++;
}
if (document.getElementById('gdopt')) {
document.getElementById('gdopt').innerHTML+='  ' + oselovalue.replace('~',',');
}
if (document.getElementById('imgds')) {
document.getElementById('imgds').value+=oselovalue.replace('~',',');
//document.getElementById('imgds').style.display='inline-block';
//document.getElementById('imgds').style.width='300px';
if (document.getElementById('smyim').innerHTML == '') { document.getElementById('smyim').innerHTML='ImageMagick switches: '; }
} else if (document.getElementById('myim')) {
document.getElementById('myim').innerHTML='<span is=smyim>ImageMagick switches: </span><input id=imqty name=imqty type=text style=display:inline-block; value=\"\"></input><input id=imgds name=imgds type=hidden style=display:inline-block; value=\"|' + oselovalue.replace('~',',') + '\"></input>';
document.getElementById('imqty').style.width='300px';
}
oselo.value='';
}
}


Previous relevant Animated GIF Creator GD Filter Interfacing Tutorial is shown below.

Animated GIF Creator GD Filter Interfacing Tutorial

Animated GIF Creator GD Filter Interfacing Tutorial

We are into image modification on the way to creating “compiled” animated GIF images, at the moment. Yesterday’s Animated GIF Creator ImageMagick Interfacing Tutorial got us interfacing (or integrating) …

  • ImageMagick batch processing “mogrify” ideas … and, today, it is the turn of …
  • GD image filters …

… into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.


Previous relevant Animated GIF Creator ImageMagick Interfacing Tutorial is shown below.

Animated GIF Creator ImageMagick Interfacing Tutorial

Animated GIF Creator ImageMagick Interfacing Tutorial

Yes, you guessed it! On top of yesterday’s ImageMagick Vignette Primer Tutorial, today we want to interface the ImageMagick batch processing “mogrify” ideas into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.

“Interfacing” (or integrating) into code often has you asking the question …


Where do we intervene to place our logic?

We’ve decided to “wrap” our PHP GD calls …

… into inhouse “our” prefixed function versions as per (PHP writing out PHP) …

<?php echo ”

\$imvgn='" . $imvgn . "'; /" . "/ vignette factor
\$imafn='" . $imafn . "'; /" . "/ affine transformation
\$imqty='" . $imqty . "'; /" . "/ image conversion and/or quality RE jpeg


function ourimagecreatefromstring(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '' && strpos(\$inidis, ';base64,') !== false) {
\$extis=str_replace('jpeg','jpg',explode('/', explode(';base64,', \$inidis)[0])[1]);
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, base64_encode(explode(';base64,', \$inidis)[1]));
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false ||
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
} else if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis=str_replace('jpeg','jpg','" . $inextis . "');
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
if (strpos(strtolower((\$imvgn . \$imafn . \$imqty)), '-format ') === false) { \$imvgn.=' -format " . str_replace('jpg','jpeg',$inextis) . " '; }
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromstring(\$inidis);
}

function ourimagecreatefromjpeg(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='jpg';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromjpeg(\$inidis);
}

function ourimagecreatefrompng(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='png';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefrompng(\$inidis);
}

function ourimagecreatefromgif(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='gif';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromgif(\$inidis);
}

function ourimagecreatefrombmp(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='bmp';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefrombmp(\$inidis);
}

“; ?>

… and change existant calls into (equivalent inhouse) “our” function calls as above.


Previous relevant ImageMagick Vignette Primer Tutorial is shown below.

ImageMagick Vignette Primer Tutorial

ImageMagick Vignette Primer Tutorial

Continuing on with the ImageMagick batch processing “mogrify” ideas of yesterday’s ImageMagick Batch Image Conversion Affine Transformation Tutorial and the “vignette” image editing ideas of Gimp Vignette Primer Tutorial, as below, today, we took some pet photos with an iPad’s Camera app and shared them off the Photos app via two Mail sharing option emails containing seven attachments each. Using an iPad, the JPEG “jpg” output files were too big for our inhouse Animated GIF Creator PHP web application to handle, and so to perform the …

  • animated GIF presentation, off these downloaded email photo attachments … and along the way …
  • quality adjusted them (yes, “mogrify” does not stuff JPG to JPG conversions, and we used “mogrify” -quality 20% switch here) … and …
  • rotate them 180 degrees (“mogrify” uses switches -affine -1,0,0,-1,0,0 -transform +repage here) … and …
  • vignette 50% (“mogrify” uses switch -vignette 50 here)

… on the way to compiling into an animated GIF image, and we turned to ImageMagick again, using its affine transformation talents, along with its awesome vignetting talents …




… where the last two dropdown options will be similar, the last showing the input image into ImageMagick can be an animated GIF that is truely treated like an animated GIF.

By the way, should you be interested in photo orientation off a mobile device camera, have a read of PHP Exif Image Information Revisit Tutorial.

Nala and Luna and Charlie bid you a fond farewell!


Previous relevant Gimp Vignette Primer Tutorial is shown below.

Gimp Vignette Primer Tutorial

Gimp Vignette Primer Tutorial

The last time we talked about the miraculous, redolent and amazing image editor called Gimp am sure there was someone in a shower … it stands to reason … and one of those showerers, surely, would have been singing The Gimp Song … and if not … why not? … but we digress … anyway we had the Gimp Transparency Primer Tutorial as shown below go into some image transparency issues with Gimp.

In today’s tutorial we make use of a great tutorial (even so far as with direct quotes below) called Add a Vignette to a Photograph with GIMP (thanks) to try a photographic technique called vignetting on one of the photographs we added, recently, into the mix of those of the Custom Header Image mix at this blog … specifically the one of Nala, the dog, on the door ledge. Need to warn you here and now that if there was the time all over again, it would be better achieved that second time around, but this is not the point with learning, but rather getting some starting point with a great “product” like Gimp, and trying it yourself, once you have a method. It boils down to:

  1. Open Gimp graphical editor application
  2. File->Open … pick your image file
  3. Layer->New Layer (we’ll call “Vg”) … pick Foreground Colour
  4. In the Layers dialog, click on your “Vg” layer to select it, and select Soft light from the “Mode” drop-down box
  5. Right click on your “Vg” layer and go to Add Layer Mask. In the dialog that pops up, you want “Initialise Layer Mask to” set to “White (full opacity)”. Click “Add”
  6. Below Opacity bar select Link icon next to Eye icon, which will already be showing
  7. Use the freeform select tool (press F to bring this up) and draw a selection somewhere around the primary point of interest in your photo
  8. Use your bucket tool (Shift+B) and click within the selection to fill it
  9. Deselect your selection with Select->None
  10. Go to Filters->Blur->Gaussian Blur. In the dialog that comes up, you want “Radius” set to a very large amount; a tenth of the longest edge of the photo is not too much
  11. Click on your “Vg” layer to select it (if it isn’t already selected), and then slide the opacity slider towards the right until the effect is subtle enough. Our (ever so subtle) example of Nala, in the tutorial, used an opacity of about 66%
  12. Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)

Here is an image comparison link.

As with most Gimp ideas, jump in and give it a go, as you’ll find your own ways and means of using this great product … am pretty sure.

Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.


Previous relevant Gimp Transparency Primer Tutorial is shown below.

Gimp Transparency Primer Tutorial

Gimp Transparency Primer Tutorial

Here is a tutorial that adds to a previous Gimp Layers Primer Tutorial as shown below, and gives you more insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.

Today’s tutorial where we construct a Birthday Card that needs tweaking for the words in front to be seen a bit more clearly, by making the image behind a bit more transparent, changes the transparency of a single image via:

  1. Open Gimp graphical editor application
  2. File->Open Layers … pick your image file
  3. If Layers window not showing, make it show via Windows->Layers – Brushes
  4. Below Opacity bar select Link icon next to Eye icon, which will already be showing
  5. Change Opacity bar setting to a value of Transparency (100% is Opaque, 0% is Transparent) that suits … today we do 70%
  6. File->Export
  7. Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)

As with most Gimp ideas, jump in and give it a go, as you’ll find your own ways and means of using this great product … am pretty sure.

Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.


Previous relevant Gimp Layers Primer Tutorial is shown below.

Gimp Layers Primer Tutorial

Gimp Layers Primer Tutorial

Here is a tutorial that gives you an insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.

Transparency (or its obverse, opacity) can be used to have the one image achieve several “ends” (ie. purposes). Although it is a bit of a clumsy example in the tutorial, you can see that the technique can be used for artistic purposes … often called “Photoshopping” (named after the more famous, and also brilliant, rival product, Photoshop).

Lots of those classic “Photoshopping” techniques can be achieved in Gimp, and some other tutorials at this blog touch on that.

Am sure you can imagine what the concept of a layer is with regard to image manipulation. Within Gimp, for beginners not used to this concept, you find yourself underestimating and underplaying what can be achieved with the various layers of a multi-layered image. In simplistic terms each layer has the functionality in Gimp to be treated as a whole new image, and this is the best way to think of it when trying to achieve what you want to achieve with Gimp.

Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in eLearning, Event-Driven Programming, GUI, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , | Leave a comment

ComboBox Idea Primer Tutorial

ComboBox Idea Primer Tutorial

ComboBox Idea Primer Tutorial

We’ve long been interested in what used to be a cute feature of GUI widgetry desktop web applications (before any mobile anything) called “ComboBox” as talked about at Button Innards Primer Tutorial. To us, what a “ComboBox”‘s essence is …

Combination of interactively keyboard entry and/or a dropdown means of data definition

… and, like with that tutorial linked to above, it is an HTML button element we start with …


<button onclick="document.getElementById('ssave').focus();" id=bsave><span style='background-color:white;min-width:40px;padding:2 2 2 2;' onclick="event.stopPropagation();" id=ssave onblur=lsit(this); contenteditable="true"></span><select onclick="event.stopPropagation();" style='margin-left:100px;' contenteditable=yes onchange=recallsm(this); id=savedd><option value=''>Optionally enter a value ...</option><option value=1>1</option></select></button>

… in our revisit to the concept, today, with a first draft ComboBox ideas exploratory effort.


Previous relevant Button Innards Primer Tutorial is shown below.

Button Innards Primer Tutorial

Button Innards Primer Tutorial

Yesterday’s …

… combine, today, starting down, again, the road to “button innards” interest.

We thought the “how we got there” concept of embedding the form elements of data into “button innards” (ie. it’s innerHTML) in our new “button innards” web application interesting. Of course you don’t have to do this simple exercise of calculating time elapsed between two datetimes this way, but what we wanted to show was that with lots of things in web design, by now, there is more than one way to crack an egg …


Previous relevant Bookmark Exporting Filtering Sharing Tutorial is shown below.

Bookmark Exporting Filtering Sharing Tutorial

Bookmark Exporting Filtering Sharing Tutorial

Yesterday’s Making of Ffmpeg Video Overall Effects Tutorial‘s “first draft” feel of our inhouse Bookmark Export Filtering web application showed just that. Yes, there was quite a lot to improve on, the way we saw it being …

  • adding a (local system) file browsing means (called by our <iframe scrolling=no frameborder=0 id=cbi data-style=’border-top:1px solid black;border-bottom:1px solid black;border-left:2px solid yellow;border-right:1px solid yellow;’ style=’width:173px;height:228px;margin-top:-194px;’ src=’/HTMLCSS/client_browsing.htm?totype=html&d=189786754′></iframe> file browsing helper) …

    function yesthreethree(restis) {
    if (document.getElementById('bmkdata')) {
    if (restis.indexOf('data:') == 0) {
    if (restis.indexOf(';base64,') != -1) {
    document.getElementById('bmkdata').value='' + window.atob(restis.split(';base64,')[1]);
    analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
    } else if (restis.indexOf(';utf8,') != -1) {
    document.getElementById('bmkdata').value='' + (restis.split(';utf8,')[1]);
    analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
    }
    } else {
    document.getElementById('bmkdata').value=restis;
    analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
    }
    }
    }

    … by which to add Web Browser Bookmark Exporting Data File Content into that lower HTML textarea element (and unlock the two UL/LI and Table button elements) … as well as …
  • add a details/summary “reveal” means by which the user can show working actual HTML elements in addition to the HTML coding we’ve only be showing (with that “first draft”)
  • add Sharing functionality via either Email or SMS conduits

… and because the amounts of data can be large (and too big for a method=GET HTML form arrangement that clientside HTML/Javascript is capable of servicing) we were glad adding this Sharing functionality that yesterday we’d already started down the line of thinking that the …

  • lower HTML textarea content … would always be associated with …
  • hashtag … representation

… because with these methodologies the web server restrictions on (address bar) URL lengths (ie. error 414) melt away, at least for data lengths we’ve encountered with our testing so far, and that applies also to …

  • “mailto:” “a” URL links to Email a recipient … or …
  • “sms:” “a” URL links to SMS a recipient

… Sharing


var mysubject='My Bookmarked Links ...', xlocationhref='';
if (('' + location.hash).indexOf('bmkdata=') != -1) { locationhash=location.hash; }

function doemail() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
mysubject='My Bookmarked Links ...';
if (document.getElementById('dtone').value.trim() != '' && document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' to ' + document.getElementById('dttwo').value.trim() + ' ... ';
} else if (document.getElementById('dtone').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' ... ';
} else if (document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links up until ' + document.getElementById('dttwo').value.trim() + ' ... ';
}
if (ulli && !utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}

function dosms() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
if (ulli && !utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}

function fillin() {
if (document.getElementById('rt') && document.getElementById('rd') && document.getElementById('rs')) {
if (document.getElementById('rd').innerHTML == document.getElementById('rs').outerHTML) {
document.getElementById('rd').innerHTML=document.getElementById('rs').outerHTML + document.getElementById('rt').value;
}
}
}

// Coming back from an email or SMS link click ...


if (document.URL.indexOf('?') == -1 && ('' + location.hash).indexOf('bmkdata=') != -1) {
setTimeout(function(){
document.getElementById('resulting').innerHTML=decodeURIComponent(('' + location.hash).split('bmkdata=')[1]).replace('><summary', ' open><summary').replace('<details title=', '<details data-title=');
fillin();
}, 5000);
}

… concepts, happily. Keeping it all “just clientside” means we can be talking meaningfully to a larger chunk of audience … if you’re listening, that is?!

So, again, feel free to try our “second draft” dated_bookmarks.html Dated Bookmark Export web application, yourself, perhaps below.


Previous relevant Making of Ffmpeg Video Overall Effects Tutorial is shown below.

Making of Ffmpeg Video Overall Effects Tutorial

Making of Ffmpeg Video Overall Effects Tutorial

At the end of yesterday’s Ffmpeg Video Overall Effects Tutorial there was an Unordered List …

… of links, am sure a lot of you would guess, were gleaned (within our Google Chrome web browser History (of that day)) Bookmarked (by us) webpages. We actually compiled via (the clunky and fairly slow) …

  • left half (of screen) with (Google Chrome) History showing, and right half with BBEdit Text Editor open at a new (ostensibly empty) file with loads of empty records available, and dragged URL records (identifiable, for us, via an asterisk, indicating a webpage was Bookmarked) from the left into the right to bring over the URLs (only) above, the rest constructed in a more manual way … and another way could have been …
  • use Google Chrome webpage showing Bookmark list and use its Export Bookmarks (no other choice but “All”) to a file that could be waded through to derive the list above … and then we thought of chance to code for …
  • hybrid “inhouse” clientside (ie. HTML/Javascript/CSS) web application …

    … so far asking for the content of a web browser Export Bookmarks operation’s output file’s HTML (in the case of Google Chrome) content in an HTML textarea element (the user can fill in themselves), and filterable via two input type=datetime-local starting and ending datetime(s) of interest (the user can fill in, optionally)

… and we wish we could have got that hour and a half back, from yesterday, via swapping yesterday for today?!

Feel free to try our “first draft” proof of concept Dated Bookmark Export web application, yourself.

Did you know?

Not all web browser brands have any Export Bookmarks functionality, like Google Chrome offers here on macOS. We’re going to be examining others, over time, and try to make our inhouse logic fit in with how that Bookmark data looks, on export, should we find any other Web Browser brands offering this functionality.


Previous relevant Ffmpeg Video Overall Effects Tutorial is shown below.

Ffmpeg Video Overall Effects Tutorial

Ffmpeg Video Overall Effects Tutorial

Today’s work (all on our macOS MAMP local Apache/PHP/MySQL web server environment) is only “toe dipping” in a “great big sea of possibilities” regarding the great ffmpeg‘s “filtering style abilities” further to yesterday’s Ffmpeg Video Subliminal Message Tutorial, but …

  • as you try out things you discover others …
  • can be a foot in the door researching this type of work yourself …

… we’re hoping. And it’s not as if ffmpeg was on it’s own here with the second concept below …

  1. fade in and fade out video effect … inspired by Top 20 best commands for FFmpeg to try …

    ffmpeg -i brush__turkey.m4v -vf "fade=in:0:5,fade=out:73:5" brushturkey_fadein_fadeout.m4v
  2. colour balancing improvements regarding an underexposed video

… as the …

  • Gimp image editor’s abilities (matching a lot of what you read Photoshop can help with too) with Curves helped out … along with …
  • Python expertly designed to link Gimp and ffmpeg “whole of video” manipulations

… enacting …

  1. selection of a video still image, ideally not containing the Brush Turkey …
  2. open image in Gimp
  3. “Aoife”‘esque Colours -> Curves…
  4. opens Adjust Colour Curves subwindow (initially a straight line)
  5. but dragging it to become a curve in a way that brightens the underexposed image to improve … we then …
  6. click + button to Save presets to a name …
  7. controlled by left pointing infilled triangle icon click that Manages Presets … and we ended up with …
  8. bush_-_turkey.presets


  9. and then we discovered this Converts GIMP colour curve to something the FFmpeg can read in the curve filter excellent project, the extraction of the Python there allowing us to go …

    python3 ../gimp_curves_ffmpeg.py

    … to output brush.out

  10. which formed most of the final curves — ffmpeg examples inspired ffmpeg command …

    ffmpeg -y -i brush__turkey.m4v -filter_complex "
    [0:v]
    curves=
    master='0.0/0 0.01171875/0.033751864777061671 0.0234375/0.067435644319434537 0.03515625/0.10098325339242978 0.046875/0.1343266067613586 0.05859375/0.16739761919153215 0.0703125/0.20012820544826168 0.08203125/0.23245028029685835 0.09375/0.26429575850263332 0.10546875/0.29559655483089792 0.1171875/0.32628458404696314 0.12890625/0.35629176091614018 0.140625/0.38555000020374036 0.15234375/0.41399121667507488 0.1640625/0.44154732509545486 0.17578125/0.46815024023019147 0.1875/0.49373187684459596 0.19921875/0.51822414970397945 0.2109375/0.54155897357365324 0.22265625/0.56366826321892849 0.234375/0.58448393340511628 0.24609375/0.6039378988975278 0.2578125/0.62196207446147445 0.26953125/0.63848837486226717 0.28125/0.65344871486521738 0.29296875/0.66677500923563593 0.3046875/0.67964838149075235 0.31640625/0.6910493179152648 0.328125/0.70214399210941336 0.33984375/0.71293769240972005 0.3515625/0.72343570715270744 0.36328125/0.73364332467489723 0.375/0.74356583331281156 0.38671875/0.75320852140297312 0.3984375/0.76257667728190348 0.41015625/0.77167558928612512 0.421875/0.78051054575216006 0.43359375/0.78908683501653032 0.4453125/0.7974097454157586 0.45703125/0.80548456528636625 0.46875/0.81331658296487608 0.48046875/0.82091108678780988 0.4921875/0.82827336509169003 0.50390625/0.83540870621303842 0.515625/0.84232239848837742 0.52734375/0.84901973025422939 0.5390625/0.85550598984711601 0.55078125/0.86178646560355965 0.5625/0.86786644586008233 0.57421875/0.87375121895320629 0.5859375/0.87944607321945378 0.59765625/0.88495629699534695 0.609375/0.89028717861740791 0.62109375/0.89544400642215893 0.6328125/0.90043206874612158 0.64453125/0.90525665392581867 0.65625/0.9099230502977721 0.66796875/0.91443654619850423 0.6796875/0.91880242996453687 0.69140625/0.92302598993239249 0.703125/0.92711251443859277 0.71484375/0.93106729181966053 0.7265625/0.93489561041211733 0.73828125/0.93860275855248565 0.75/0.94219402457728751 0.76171875/0.94567469682304517 0.7734375/0.94905006362628086 0.78515625/0.95232541332351639 0.796875/0.95550603425127423 0.80859375/0.9585972147460764 0.8203125/0.96160424314444504 0.83203125/0.96453240778290228 0.84375/0.96738699699797048 0.85546875/0.97017329912617156 0.8671875/0.97289660250402765 0.87890625/0.97556219546806111 0.890625/0.97817536635479407 0.90234375/0.98074140350074845 0.9140625/0.98326559524244661 0.92578125/0.98575322991641046 0.9375/0.98820959585916268 0.94921875/0.99063998140722487 0.9609375/0.99304967489711937 0.97265625/0.99544396466536833 0.984375/0.9978281390484941 0.99609375/1":red="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1":green="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1":blue="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1'
    [v]" -map '[v]' -an brushturkey_better_colour.m4v

… was crucial to our discoveries today

  1. fade in and fade out video effect …
  2. colour balancing improvements regarding an underexposed video …

Further reading for you here, could involve …


Previous relevant Ffmpeg Video Subliminal Message Tutorial is shown below.

Ffmpeg Video Subliminal Message Tutorial

Ffmpeg Video Subliminal Message Tutorial

Revisiting the brilliant ffmpeg, as discussed at the recent Ffmpeg Install and Public Face Tutorial

  • the other day we were reading the excellent Fastest way to extract frames using ffmpeg? and noted it down for a future discussion … and then today …
  • we were inspired by a Brush Turkey which we videoed via an iPhone’s Camera app’s Video mode of capture option …
  • downloaded to this MacBook Air (via AirDrop … good for Apple sharing when the sizes get big … raw video 1:19s long)

And that’s where we are at here, thinking this would be a good opportunity to try creating a Subliminal Message video. But before we go on and “give the game away” you may want to see the result of our Subliminal Message insertion via ffmpeg theme to our work, in action, below. Unlock or give up to unlock the content below the video quiz below …

What is the secret word?
Raw video … to it’s slides …
ffmpeg -i ../IMG_2784.MOV -r 1/1 %03d.jpg
ffmpeg -i %03d.jpg brush_turkey.m4v
Hard guess?
ffmpeg -r 8 -i %03d.jpg brush-turkey.m4v
Easier guess?
ffmpeg -r 6 -i %03d.jpg brush__turkey.m4v


Previous relevant Ffmpeg Install and Public Face Tutorial is shown below.

Ffmpeg Install and Public Face Tutorial

Ffmpeg Install and Public Face Tutorial

Our recent web server VPS migration can not only be …

  • a migration of functionality (eg. this WordPress blog) … as well as …
  • an opportunity to open up new vistas of serverside functionality

… such as today’s ffmpeg install, which is only feasible because of the …

  • additional diskspace … chance on new web server allows for …
  • chance to install ffmpeg …

    dnf config-manager --set-enabled powertools
    dnf install --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm -y
    dnf install --nogpgcheck https://mirrors.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-$(rpm -E %rhel).noarch.rpm -y
    dnf install ffmpeg

    … thanks to this great link, along with ImageMagick …

… on this new “public face” (even more public when DNS is applied later).

Codewise this involved …

… further to recent work touching these ideas with Animated GIF Creator Overlayed Images Top Left Tutorial.

See some of this public install of ffmpeg here.


Previous relevant Animated GIF Creator Overlayed Images Top Left Tutorial is shown below.

Animated GIF Creator Overlayed Images Top Left Tutorial

Animated GIF Creator Overlayed Images Top Left Tutorial

To add to the recent Animated GIF Creator Overlayed Images Tutorial‘s user interaction logic, we add a way to control those two “overlay” CSS properties …

  • left
  • top

… but more in the sense that we allow CSS …

  • margin-eft
  • margin-top

… come into the picture, after a dalliance with CSS calc thoughts.

This allows for offsetted images be part of the “overlay mix” that you can retry this at our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator.


Previous relevant Animated GIF Creator Overlayed Images Tutorial is shown below.

Animated GIF Creator Overlayed Images Tutorial

Animated GIF Creator Overlayed Images Tutorial

We wanted to add an “Overlayed Images” functionality component to the “Animated GIF Creator” of Animated GIF Creator Video Intranet Tutorial, today.

The work involves both of the HTML design “big concepts” we are keen on at this blog …

  • overlay … via CSS control of
    1. position:absolute;top:0px;left:0px;
    2. opacity:1.0;
    3. z-index:1;
  • reveal … lately, mostly, via HTML use of …
    1. details
    2. summary

… the latter being the “container” for our interactive input be able to control overlay items 2 and 3, which affect the “overlayed images” output display via the clicking of a new “Overlay Images” button. That button …


<input disabled onclick="overlaythem();" id="overlayit" type="button" title='Overlay Images' value='Overlay Images'></input>

… starts off as disabled … and works with new HTML div elements …


<div id=overlayedj><h1>&nbsp;</h1><br></div><div id=overlayedi></div>

… until at least two images are defined, at which time the button becomes enabled …


function onfit(ithis) {
if (ithis.value == '' && ('' + ithis.placeholder).indexOf(']') != -1) {
ithis.value=('' + ithis.placeholder).split('[')[1].split(']')[0];
}
ithis.type='number';
}

function overlayds(inm, newop, newzi) {
var outdets='';
var divrect=null;
var poslt=' data-style=\"position:absolute;left:px;top:px;\" ';
var reposit='';
if (!document.getElementById('detsum' + ('' + inm))) {
if (document.getElementById('overlayedi').innerHTML == '') {
document.getElementById('overlayedi').title='Overlayed Images Below ... Click for It in New Window ...';
document.getElementById('overlayedj').innerHTML='<h1>Overlayed Images Below ...</h1><br>';
divrect=document.getElementById('overlayedi').getBoundingClientRect();
poslt=' data-style=\"position:absolute;left:' + ('' + divrect.left).split('.')[0].trim() + 'px;top:' + ('' + eval(200 + eval('' + ('' + divrect.top).split('.')[0]))).trim() + 'px;\" ';
} else {
poslt=' data-style=\"' + document.getElementById('detsum1').getAttribute('data-style') + '\" ';
}
outdets='&nbsp;&nbsp;<details ' + poslt + ' id=\"detsum' + ('' + inm) + '\" style=\" display:inline-block; \" open><summary id=\"sumdet' + ('' + inm) + '\">Overlay</summary><input onchange=overlayds(' + inm + ',this.value,String.fromCharCode(32)); onblur=overlayds(' + inm + ',this.value,String.fromCharCode(32)); type=text onfocus=onfit(this); id=\"opacity' + ('' + inm) + '\" style=\" display:inline-block; \" placeholder=\"Opacity [1.00]\" value=\"\" step=0.01 min=0.00 max=1.00></input><br><input onfocus=onfit(this); onchange=overlayds(' + inm + ',String.fromCharCode(32),this.value); onblur=overlayds(' + inm + ',String.fromCharCode(32),this.value); type=text id=\"zindex' + ('' + inm) + '\" style=\" display:inline-block; \" placeholder=\"Z-Index [1]\" value=\"\" min==999 max=999 step=1></input><br></details>';
reposit=poslt.replace(' data-',' ');
reposit=reposit.replace('\"', '\"opacity:1.0;z-index:1;');
if (document.getElementById('overlayedi').innerHTML == '') {
document.getElementById('overlayedi').innerHTML='<img id=\"imgdet' + ('' + inm) + '\" src=\"' + document.getElementById('slideshow' + ('' + inm).replace(/^1$/g,'')).value + '\" ' + reposit + '></img>';
document.getElementById('overlayedi').style.width=('' + document.getElementById('imgdet' + ('' + inm)).width).replace('px','') + 'px';
document.getElementById('overlayedi').style.height=('' + document.getElementById('imgdet' + ('' + inm)).height).replace('px','') + 'px';
document.getElementById('overlayedi').onclick = function(){ var woo=window.open('','_blank','top=100,left=100,width=' + document.getElementById('overlayedi').style.width.replace('px','') + ',height=' + document.getElementById('overlayedi').style.height.replace('px','')); woo.document.write('<html><head><title>Overlayed Images Below ...</title></head><body>' + document.getElementById('overlayedi').outerHTML.replace(/left\:[\ ]*/g,'left:0.0').replace(/top\:[\ ]*/g,'top:0.0') + '</body></html>'); woo.document.title='Overlayed Images Below ...'; };
} else {
document.getElementById('overlayedi').innerHTML+='<img id=\"imgdet' + ('' + inm) + '\" src=\"' + document.getElementById('slideshow' + ('' + inm).replace(/^1$/g,'')).value + '\" ' + reposit + '></img>';
}
} else {
document.getElementById('detsum' + ('' + inm)).style.display='inline-block';
if (newop != ' ') {
if (newop.trim() == '') {
document.getElementById('imgdet' + inm).style.opacity='1.0';
} else {
document.getElementById('imgdet' + inm).style.opacity='' + newop;
}
}
if (newz != ' ') {
if (newz.trim() == '') {
document.getElementById('imgdet' + inm).style.zIndex='1';
} else {
document.getElementById('imgdet' + inm).style.zIndex='' + newzi;
}
}
}
return outdets;
}

function overlaythem() {
var suffidea=1;
while (document.getElementById('slideshow' + ('' + suffidea).replace(/^1$/g, '')).value != '') {
document.getElementById('ours' + ('' + suffidea)).innerHTML+=overlayds(suffidea,'','');
suffidea++;
}
}

You can try this at our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator.


Previous relevant Animated GIF Creator Video Intranet Tutorial is shown below.

Animated GIF Creator Video Intranet Tutorial

Animated GIF Creator Video Intranet Tutorial

With the recent Animated GIF Creator Video Revisit Tutorial (preceding Animated GIF Creator Video Share Tutorial) we wrote …

… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …

… setting up what could be “lost functionality”, but today, we come around to either adding a new …


Convert to Video

… dropdown option should all be ritchy ditch, and if not, often we will add new “Advice” dropdown options to remind the user what they’d need to arrange to get to a “Convert to Video” scenario.

Selecting “Convert to Video” sets up a hydrid “Internet/Intranet” feeling scenario where …

  • the user is using our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator containing this new “Convert to Video” option off the “ImageMagick” dropdown …
  • the user defines some image slide data … eg. browsing to create data-URIs (but careful not to do too many) …
  • the user selects that “Convert to Video” option …
  • a window.open popup calls URL like …
    HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php?video=1687644
    … in a new window …
  • those data-URIs are converted into /tmp/jmtmp0*.[imageExt] files … ready for …
  • ffmpeg commands like …

    ffmpeg -framerate 0.1 -i /tmp/jmtmp%03d.jpeg -c:v libvpx-vp9 -pix_fmt yuva420p -lossless 1 -c:a copy video.webm; ffmpeg -i video.webm video.mov 2>> video.bad

    … be nested in PHP exec (via MAMP local web server incarnation of the inhouse Animated GIF Creator) as a way to create videos …
  • which can be downloaded within that public domain “parent” inhouse Animated GIF Creator session

Which begs the question …


How do we know when to offer the "Convert to Video" option on that dropdown?

This logic is centred around a few useful ideas (with this cross domain scenario ruling out Ajax and Iframe src= definitions, as useful ways to go) …

  1. open the MAMP local web server “HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php” URLs in an iframe (with onload event logics) pointed at by a window.open second argument (effectively avoiding any loose useful or not popup windows hanging around) …
  2. the most we can ask at the receiver is that window.opener is defined … and if so, just at that discovery …
  3. an image called “amhere.jpg” is created via PHP GD functionality … and back at the “public domain” parent within the iframe onload logic (where with contentWindow or contentDocument do not expect a document.body or even a document) …
  4. we attempt to “hotlink” that MAMP local web server image … as per …

    var tryit='http://localhost:8888/PHP/animegif/amhere.jpg';

    document.getElementById('ctvopt').value='advice';
    document.getElementById('ctvopt').innerHTML='Advice on Convert to Video';

    if (tryit != '') {
    var im=new Image();

    im.onload = function() {
    document.getElementById('ctvopt').value='video';
    console.log('this.height=' + eval('' + this.height));
    if (eval('' + this.height) >= 20) {
    document.getElementById('ctvopt').innerHTML='Convert to Video';
    } else {
    document.getElementById('ctvopt').innerHTML='Convert to Video (but ffmpeg not installed or in unexpected place)';
    }
    document.getElementById('imsel').title='All except Convert to Video, which needs ffmpeg installed, use ImageMagick';
    };


    im.src=tryit;
    tryit='';
    }


Previous relevant Animated GIF Creator Video Revisit Tutorial is shown below.

Animated GIF Creator Video Revisit Tutorial

Animated GIF Creator Video Revisit Tutorial

We hope you realize that our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changed emailhtml.php inhouse email creator helper serverside PHP web application) can be accessed …

  1. in that public RJM Programming mode of use above … but even better can be …
  2. download relevant code to macOS MAMP local web server places off /Applications/MAMP/htdocs/ $_SERVER[‘DOCUMENT_ROOT’] places …

    … and check the code for the validity of any ImageMagick paths … and if not all these conditions, simulate the same and cobble it together in the code

… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …


brew install ffmpeg

… to open a whole new woooooorrrrlllllddd of video creation opportunities using this MAMP local web URL …


HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php# Animated GIF Creator on MAMP local web server

And so, onto yesterday’s Animated GIF Creator PDF Last Reveal Tutorial we decided to revisit video creation parts of our inhouse Animated GIF Creator on this MacBook Air. We needed to reinstall ffmpeg, and we show that in today’s animated GIF tutorial picture and also created the “video.mov” video below, in the process …

… we tweaked out of “video/mp4” ffmpeg created videos thinking towards “video/webm” or “video.mov” ffmpeg created videos that suit the “few frames but spaced out video” we wanted to achieve for all practical purposes, that “video/mp4” playing too fast for us to see anything much but the first and last slide …

<?php

$videofr=" -r " . (1000 / $delay) . " ";
$videofr=" -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS ";
$videomime="video/mp4";
$videoext=".mp4";

$videofr=" -framerate 0.1 ";
$videoext=".webm";
$videomime="video/webm";
$videomidbit="-c:v libvpx-vp9 -pix_fmt yuva420p -lossless 1 -c:a copy";
//$videocmd=$videopath . "ffmpeg -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS " . " -i " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "%03d.jpg " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mp4 2> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
//$svideocmd="ffmpeg -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS " . " -i " . "%03d.jpg " . "video.mp4";
$videocmd=$videopath . "ffmpeg " . $videofr . " -i " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "%03d.jpg " . $videomidbit . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " 2> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$svideocmd="ffmpeg " . $videofr . " -i " . "%03d.jpg " . $videomidbit . " " . "video" . $videoext;
$videocmd.="; " . $videopath . "ffmpeg -i " . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mov 2>> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$svideocmd.="; " . "ffmpeg -i " . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mov 2>> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$videohtml="<video title='" . $svideocmd . "' controls id=ivideo type='" . $videomime . "'><source src='video.mov'></source><source src='video" . $videoext . "'></source></video>";
$videosuffix="";

?>


Previous relevant Animated GIF Creator PDF Last Reveal Tutorial is shown below.

Animated GIF Creator PDF Last Reveal Tutorial

Animated GIF Creator PDF Last Reveal Tutorial

As a web application programmer we like buttons. There are “buttons” and there are buttons. Yes, there is an HTML button element, and it and the input type=button element render as that part of a webpage most recognizable to users the world over. You click or tap this button and something usually happens.

But we also enjoy “Emoji Buttons” for we graphically challenged programmers. Using an emoji text and graphic can make span elements or p elements or lots of other HTML elements that have an innerHTML property, be like a very succinct button like entity, also appreciated around here because it takes up so little webpage “real estate”.

And so onto yesterday’s Animated GIF Creator PDF Order Tutorial we set about, today, “revealing” any enduring animated GIF and/or PDF created during a previous session, using Emoji Buttons as the email/SMS sharing action buttons.

We say “revealing” because, like the way “Emoji Buttons” save webpage “real estate”, so can the use of the HTML5 details/summary element combination. It is in that summary “enduring” header part of that combination we can place some “Emoji Buttons”. You will (once you start creating and sharing animated GIFs and/or PDFs) see from our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changed emailhtml.php inhouse email creator helper serverside PHP web application) that we place three Emoji Buttons …

  1. 📟 SMS (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “sms:” link click/tap
  2. 📧 Email (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “mailto:” link click/tap
  3. 📧 Email (Animated GIF or PDF) Enduring relevance (ie. data URIs involved, so do not rely on any absolute URL whose content might change or start not to exist down the track) … via PHP mail function means of sending email with HTML attachment containing the relevant Animated GIF or PDF

Yes, “Emoji Buttons” can have their size controlled but not by the usual CSS width and height properties, but by the CSS font-size property. Here is the new Javascript function that those Emoji Button “onclick” logic points to …

<?php eho ”

function askes(isemail, isoab) {
var esask='', izhr=null, izform=null;
var isoa=document.getElementById(isoab.id.replace('b',''));
if (isemail) {
esask=prompt('Please enter email address to send this to', '');
if (esask != null) {
if (esask.indexOf('@') != -1) {
if (isoa.outerHTML.indexOf('<img') == 0) {
//alert('More Animated GIF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "');
izform.append('subj', 'My Latest Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><img style=\"width:100%;height:900px;\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "\"></img></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else if (isoa.outerHTML.indexOf('<a') != 0) {
//alert('More PDF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "');
izform.append('subj', 'My Latest PDF via Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "\"></object></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else {
isoa.href='mailto:' + esask.trim() + '?' + isoa.href.split('?')[1];
isoa.click();
}
}
}
} else {
esask=prompt('Please enter SMS number to send this to', '');
if (esask != null) {
if ((esask.trim() != '' && esask.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
isoa.href='sms:' + esask.trim() + '&' + isoa.href.split('&')[1];
isoa.click();
}
}
}
}

“; ?>

Cute, huh?!


Previous relevant Animated GIF Creator PDF Order Tutorial is shown below.

Animated GIF Creator PDF Order Tutorial

Animated GIF Creator PDF Order Tutorial

Order becomes an interesting subject to us today regarding the PDF conversions possible in amongst the Animated GIF creating of yesterday’s Animated GIF Creator PDF Share Tutorial.

That is because we may have some advanced users out there that want that possibility of differentiating the data of …

  • animated GIF … containing all features asked for, in totality … from the chance for the user to have any of the PDF conversion option sets below …
  • PDF … one of …
    1. no PDF conversion
    2. full PDF conversion … with the user choices regarding raw image content and title and watermarking and ImageMagick and GD modifiers
    3. light PDF conversion … with the user choices regarding just raw image content (the only option out of these last three available before today’s work)
    4. medium PDF conversion … with the user choices regarding just ImageMagick and GD modifiers

… perhaps as a “before and after” tool regarding presentations, maybe?

No dropdowns used here (enforcing an order), just the user’s order in which they choose to select the “PDF conversion” option from the ImageMagick dropdown (in relation to other selections) determining how and when this PDF conversion occurs in the workflow through to creating the animated GIF with our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application. Just remember to select PDF Conversion as early as possible to do that “light PDF conversion” option above and last thing if you are interested in “full PDF conversion” (where you can create PDF documents with the bells and whistles ImageMagick and GD modifiers can offer) and today’s tutorial picture is an example of “medium PDF conversion” (when we ordered our settings by first Grayscale, second PDF conversion and last title and watermarking options). Clicking or tapping the ImageMagick link can get the user to the Javascript popup window where they might define the email or SMS recipient for that PDF conversion data file download.


Previous relevant Animated GIF Creator PDF Share Tutorial is shown below.

Animated GIF Creator PDF Share Tutorial

Animated GIF Creator PDF Share Tutorial

To share yesterday’s Animated GIF Creator PDF Conversion Tutorial PDF data formatted image slides (and then onto an animated GIF) would be a step forward, wouldn’t you say?

What sharing conduits do we code for? We always intended …

  • email … but as the day wore on trying to get the usage to work on mobile and non-mobile we decided to relinquish our wish to not have to create a user specific enduring (until that same browser type and user combination share) PDF document for the user request … mainly to get mobile email downloads to be friendly … and so this opened the door for …
  • SMS … to access that enduring PDF as a URL in the SMS message (that becomes a link for the recipient)

… the email methodology used being that “midair feeling” Ajax/FormData approach in our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application …

<?php echo ”

function emailhtmlit() {
var induri=ginduri;
var pemail='" . urldecode($_GET['outpdf']) . urldecode($_POST['outpdf']) . "';
if (pemail != null) {
if (pemail.indexOf('@') != -1 || (pemail.trim() != '' && pemail.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
if (induri.trim() == '') {
if (induri == '') {
document.getElementById('pdfproposed').src='./animegif.pdf?rand=' + Math.floor(Math.random() * 19854654);
} else {
ginduri='found';
}
setTimeout(emailhtmlit, 15000);
} else {
ginduri='';
var zhr = new XMLHttpRequest();
var zform=new FormData();
if (pemail.indexOf('@') != -1) { zform.append('to', pemail); }
zform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . "animegif.pdf" . "');
zform.append('subj', 'My PDF version of Animated GIF via RJM Programming ... ');
zform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"><embed style=\"width:100%;height:900px;\" type=\"application/pdf\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></embed></object></div></body>'));
//zform.append('tdhuhta', ('<body><iframe srcdoc=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></iframe></body>'));
zhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
zhr.send(zform);
if (pemail.indexOf('@') == -1) {
var hrefp=document.getElementById('pdfsms').href.split('&body=')[0];
hrefp+=pemail.trim() + '&body=' + document.getElementById('pdfsms').href.split('&body=')[1];
document.getElementById('pdfsms').href=hrefp;
document.getElementById('pdfsms').click();
}
}
}
}
}

“; ?>

… and this called on our PHP email creator helper to better interface in its $_POST arguments reading section in our changed emailhtml.php inhouse email creator helper serverside PHP web application …

<?php

$psacv="";
$okayin=true;
$subd="";
$subptitle='';
$subdata="";
$subfile="";
$getpostfound=false;
<br>
foreach( $_POST as $name=>$val ) {
if ($val != "") {
// blah blah blah //file_put_contents("jnv." . $name, "jnv.inv00 " . strlen($phpcont));
if ($name == "tdhuhta" && $subfile != "" && $subdata != "" && strpos(str_replace("+"," ",urldecode($val)), $subfile) !== false) {
if (strpos(str_replace("+"," ",urldecode($val)), ' srcdoc="') !== false || strpos(str_replace("+"," ",urldecode($val)), ' data="') !== false) {
if (strpos(str_replace("+"," ",urldecode($val)), ' title="') !== false) { //file_put_contents('haaa.aaa',explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1]);
if (!file_exists('../PHP/animegif/' . explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1] . 'animegif.pdf')) {
if (file_exists('../PHP/animegif/animegif.pdf')) {
copy('../PHP/animegif/animegif.pdf', '../PHP/animegif/' . explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1] . 'animegif.pdf');
}
}
$psacv=explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0] . 'animegif.pdf';
$subptitle='<a target=_blank href="' . explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0] . 'animegif.pdf">Mobile whole PDF</a><br>';
}
$vrest=explode('"', explode(' srcdoc="',str_replace("+"," ",urldecode($val)))[1])[0];
$val=urlencode("<body>" . $subptitle . "<iframe style='width:100%;height:95vh;overflow:auto;-webkit-overflow-scrolling:touch;' srcdoc='<div style=width:92%;height:95vh;overflow:auto;-webkit-overflow-scrolling:touch;><object id=myobj style=width:95%;height:95vh; type=application/pdf data=\"" . $subdata . "\"></object></div>'></iframe></body>");
}
// blah blah blah
if ($name == "inline") {
if (strpos(str_replace("+"," ",urldecode($val)), 'animegif.pdf') !== false && isset($_POST['tdhuhta'])) {
if (file_exists(str_replace("+"," ",urldecode($val)))) {
$subfile=str_replace("+"," ",urldecode($val));
$subdata='data:application/pdf;base64,' . base64_encode(file_get_contents(str_replace("+"," ",urldecode($val))));
$val="";
$name="x";
$okayin=false;
}
}
// blah blah blah
}
}

?>

Creating the PDFs got helped out in “reliability terms” via the introduced animegif.php inhouse Wait Around Longer serverside PHP web application.


Previous relevant Animated GIF Creator PDF Conversion Tutorial is shown below.

Animated GIF Creator PDF Conversion Tutorial

Animated GIF Creator PDF Conversion Tutorial

Up to yesterday’s Animated GIF Creator Slide Specific Application Tutorial the recent progress had us …

  • knowing the good places to intervene between the original slide image data being read in and the animated GIF slide output … and …
  • involvement of ImageMagick … so, today, these two help us down the path to …
  • adding PDF as an output format possibility, in addition to the animated GIF and other ideas floated as output formats

… with our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application.

We have more “reliability work” and “email sharing work” to go after today’s start, but it primarily called on ImageMagick command line’s talent for a command like …


convert /tmp/imtmp0*.*[gGmMiI]* /tmp/imtmp000.pdf

… to “concatenate” into the one output PDF file (called “/tmp/imtmp000.pdf”) the slides, arranged by our code into that “/tmp/imtmp0*.*[gGmMiI]*” (file specification) arrangement above. Yes, we meant “convert” above, as “mogrify” (batch work) appears not to be able to perform this task.

We hope you stay the course!


Previous relevant Animated GIF Creator Slide Specific Application Tutorial is shown below.

Animated GIF Creator Slide Specific Application Tutorial

Animated GIF Creator Slide Specific Application Tutorial

It’s all fine and good improving on the ImageMagick and GD and Exif functionality modifications like with yesterday’s Animated GIF Creator Exif Rotation Compensation Tutorial, but in reality, if you are going to start creating animated GIFs to explain a process, you are going to want to apply these “slide modifiers” on a slide by slide basis, rather than enforcing a “whole of animated GIF slide set” paradigm, as for the last few days worth of work.

And so, we decided to do what we often do, “sliding in” more functionality (chortle, chortle). We tend to want to …

  • start with hardcoded text (or element) … somewhere … today it happens to be in an HTML span element that once involved just …

    <span id="smyim"></span>

    … and used to get filled, Javascript DOM wise, when needed via …

    document.getElementById('smyim').innerHTML='ImageMagick switches: '; // and yes, it remains that way even now, but read on ...
  • add intelligence (quite often that being onclick logic(s)) to that hardcoded element via …

    <span id=smyim title=Application onclick=applyto(); style=cursor:pointer;text-decoration:underline;></span>
  • that serves the purpose, as the user clicks/taps it (alerted to that fact, perhaps, because we underline the element and add an appropriate cursor when hovering over it (plus a title)), of calling Javascript …

    function applyto() {
    var huhto=prompt('Apply ImageMagick and/or GD to which slides, in comma separated list, counting starting with 1? Defaults to applying to all slides. Comma delimit. Negatives mean all but. Ranges can be specified. For example ... 2,4-7,9', document.getElementById('appliedto').value);
    if (huhto == null) { huhto=''; }
    if (huhto.trim() == '') {
    document.getElementById('appliedto').value='';
    document.getElementById('smyim').title='Application';
    } else {
    document.getElementById('appliedto').value=huhto.trim();
    document.getElementById('smyim').title=huhto.trim();
    }
    }
  • to glean an (often times out of the normal workflow of the web application) informational piece of data, interactively, from the user, via a Javascript prompt popup

So that’s the clientside of this work … “alerting the user to the existance of the functionality” you might say.

And then there’s “the application” of that nuanced user requirement. And that’s where the “inhouse ‘our’ prefix to wrapper function name paradigms” come in handy. We introduced “blanket” functionality thoughts via this approach, and so to “partially undo” that thinking, we make the “our” prefix conditional, as is available to us with our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application (helped out by a changed exif_rotation_check.php inhouse Exif detector PHP helper), as per

<?php

function ourcomplicated($inio, $iappl) {
$ideasl=explode(",", str_replace(' ','',$iappl));
$xour="our";
for ($iqa=0; $iqa<sizeof($ideasl); $iqa++) {
if (trim($ideasl[$iqa]) != '') {
$xour="";
if (('-' . $inio) == trim($ideasl[$iqa])) { return ""; }
$ideasr=explode("-", str_replace(' ','',trim($ideasl[$iqa])));
for ($iqb=0; $iqb<sizeof($ideasr); $iqb++) {
if (('' . $inio) == trim($ideasr[$iqb])) {
return "our";
}
if ($iqb == 1) {
if (trim($ideasr[1]) == "") { $ideasr[1]="99999999"; }
if ($inio >= $ideasr[0] && $inio <= $ideasr[1]) { return "our"; }
}
}
}
}
return $xour;
}


?>

teamed with

<?php

$ours="our";
for ($io=1; $io$ours=ourcomplicated($io, $iappliedto);
// blah blah blah
if (strpos($aphoto[0], ";base64,") !== false) {
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromstring(base64_decode('" . explode(";base64,",$aphoto[0])[1] . "'));", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachonestring));
} else if (strpos(strtolower($aphoto[0]), "//") !== false) {
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromstring(@file_get_contents('" . $prefix . $aphoto[0] . "'));", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".jp") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
//file_put_contents('qpqp.qpqp', $eachone . "\n\n" . $atext . "\n\n" . str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromjpeg('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
//file_put_contents('qpqpqp.qpqpqp', $gifphp);
} else if (strpos(strtolower($aphoto[0]), ".png") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefrompng('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".gif") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromgif('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".bmp") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefrombmp('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
}
// blah blah blah
}

?>

We use these “new abilities” better explaining “the abscence or otherwise of Exif checking”, and the implications of that in the animated GIF creator woooooorrrrrrlllllddd, contrasting the first two slides, showing one with “No Exif checking” (the bad old days) versus “With Exif checking” (the renaissance of liberated thinking in the South South East woooorrrrrlllldddd) in today’s animated GIF presentation.

Get the picture?!


Previous relevant Animated GIF Creator Exif Rotation Compensation Tutorial is shown below.

Animated GIF Creator Exif Rotation Compensation Tutorial

Animated GIF Creator Exif Rotation Compensation Tutorial

The recent couple of days of work like yesterday’s Animated GIF Creator GD Transformations Interfacing Tutorial have given us “rotation functionality” tools to counteract the unusual effects Exif …

Exchangeable image file format (officially Exif, according to JEIDA/JEITA/CIPA specifications) is a standard that specifies the formats for images, sound, and ancillary tags used by digital cameras (including smartphones), scanners and other systems handling image and sound files recorded by digital cameras.

… can have converting mobile device camera created images into animated GIFs via our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.

But with some photos it’s there in the photo’s metadata information letting the future user know what orientation the camera of that mobile device was in as you took the photo. In order to help our animated GIF creator, the first slide image is scoured for Exif metadata and if found, a suitable rotation correction can be applied to the slides there and then. In order to scour for Exif metadata we needed to write a exif_rotation_check.php inhouse Exif detector PHP helper.


Previous relevant Animated GIF Creator GD Transformations Interfacing Tutorial is shown below.

Animated GIF Creator GD Transformations Interfacing Tutorial

Animated GIF Creator GD Transformations Interfacing Tutorial

The PHP GD image library is so much more useful than for the “filters” interfaced to with yesterday’s Animated GIF Creator GD Filter Interfacing Tutorial. Under an “umbrella term” transformations, today we add interfacing to GD functionality …

… into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application. We found it more user friendly to tailor the asking of numerical parameters for these GD calls work via …

<?php

$gfcds='';
$gdfs=[];
if (strpos(($imvgn . $imafn . $imqty . $imgds), '|') !== false) {
$gdfs=explode(';', trim(explode('|', $imvgn . $imafn . $imqty . $imgds)[1]));
for ($itf=0; $itf<sizeof($gdfs); $itf++) {
if (trim($gdfs[$itf]) != '') {
if (trim($gdfs[$itf]) == strtolower(trim($gdfs[$itf]))) {
$gfcds.=chr(10) . " imagefilter(" . "$" . "oimo, IMG_FILTER_" . strtoupper(trim($gdfs[$itf])) . "); " . chr(10);
} else if (trim($gdfs[$itf]) == strtoupper(trim($gdfs[$itf]))) {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo, IMG_" . str_replace(" ","_",strtoupper(trim( str_replace(explode(' ',$gdfs[$itf])[0] . ' ','',$gdfs[$itf]) ))) . "); " . chr(10);
} else if (strpos(strtoupper(trim($gdfs[$itf])), "SCALE") !== false) {
$gfcds.=chr(10) . " $" . "oimo=image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . str_replace(" ","_",(trim( str_replace(explode(',',explode(" ",(trim($gdfs[$itf])))[0])[0],'',$gdfs[$itf]) ))) . "); " . chr(10);
} else if (strpos(strtoupper(trim($gdfs[$itf])), "COPY") === false) {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . str_replace(" ","_",(trim( str_replace(explode(',',explode(" ",(trim($gdfs[$itf])))[0])[0],'',$gdfs[$itf]) ))) . "); " . chr(10);
} else {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . "," . "$" . "oimo" . explode("," . "$" . "oimo", $gdfs[$itf])[1] . "); " . chr(10);
}
}
}
}

?>

… to work with modified PHP writes PHP of the ilk …

<?php echo ”

function ourimagecreatefromstring(\$inidis) {
global \$imvgn, \$imafn, \$imqty, \$imgds, \$gfcds;
if ((\$imvgn . \$imafn . \$imqty . \$imgds) != '' && strpos(\$inidis, ';base64,') !== false) {
\$extis=str_replace('jpeg','jpg',explode('/', explode(';base64,', \$inidis)[0])[1]);
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, base64_encode(explode(';base64,', \$inidis)[1]));
\$fzis=filesize(\$sofarout);
if (trim(explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0]) != '') {
exec('" . $immogpath . "mogrify ' . explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0] . ' /tmp/imtmp.' . \$extis);
}
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false ||
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
\$oimo=null;
if (strtolower(\$extis) == 'png') {
\$oimo=imagecreatefrompng('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'bmp') {
\$oimo=imagecreatefrombmp('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'gif') {
\$oimo=imagecreatefromgif('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
\$oimo=imagecreatefromjpeg('/tmp/imtmp.' . \$extis); " . $gfcds . "
}
if (\$oimo) { return \$oimo; }
} else if ((\$imvgn . \$imafn . \$imqty . \$imgds) != '') {
\$extis=str_replace('jpeg','jpg','" . $inextis . "');
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
if (strpos(strtolower((\$imvgn . \$imafn . \$imqty . \$imgds)), '-format ') === false) { \$imvgn.=' -format " . str_replace('jpg','jpeg',$inextis) . " '; }
if (trim(explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0]) != '') {
exec('" . $immogpath . "mogrify ' . explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0] . ' /tmp/imtmp.' . \$extis);
}
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
\$oimo=null;
if (strtolower(\$extis) == 'png') {
\$oimo=imagecreatefrompng('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'bmp') {
\$oimo=imagecreatefrombmp('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'gif') {
\$oimo=imagecreatefromgif('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
\$oimo=imagecreatefromjpeg('/tmp/imtmp.' . \$extis); " . $gfcds . "
}
if (\$oimo) { return \$oimo; }
}
return imagecreatefromstring(\$inidis);
}

“; “?>

… and to get there clientwise, in the Javascript …


function gdadd(oselo) {
var pari=1, thispari='0';
if (oselo.value != '') {
//alert(oselo.value);
var oselovalue=oselo.value.replace(',' + '$' + 'oimo','~' + '$' + 'oimo').split(',')[0];
//alert(oselovalue);
var oseloval=oselo.value;
if (document.getElementById('imgds')) { if (document.getElementById('imgds').value == '') { document.getElementById('imgds').value='|'; } else { document.getElementById('imgds').value+=';'; } }
oseloval=oseloval.replace(',' + '$' + 'oimo','~' + '$' + 'oimo');
oseloval=oseloval.replace(oseloval.split(',')[0], '');
while (oseloval.indexOf(',') != -1) {
//alert(oseloval);
if ((oseloval + ' ').substring(1).split(',')[0].trim() != '') {
if ((' ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' ').toLowerCase().indexOf(' colour ') != -1) {
thispari=prompt('Enter' + (' ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' ').toLowerCase().replace(' colour ', ' red,green,blue colour ') + 'parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
if (thispari == null) {
thispari='0';
} else if (thispari.trim() == '') {
thispari='0';
} else if (thispari.indexOf(',') == -1) {
oselovalue+=',' + thispari + '';
} else {
oselovalue+=',imagecolorallocate(' + '$' + 'oimo,' + thispari + ')';
}
} else {
thispari=prompt('Enter ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
if (thispari == null) {
thispari='0';
} else {
oselovalue+=',' + thispari;
}
}
oseloval=oseloval.replace(',' + (oseloval + ' ').substring(1).split(',')[0].trim(),'').trim();
} else {
thispari=prompt('Enter numerical parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
oseloval=oseloval.replace(',','');
if (thispari == null) {
thispari='0';
} else {
oselovalue+=',' + thispari;
}
}
//oseloval=oseloval.replace(oseloval.split(',')[0] + ',', '');
//oseloval=oseloval.replace(',','');
pari++;
}
if (document.getElementById('gdopt')) {
document.getElementById('gdopt').innerHTML+='  ' + oselovalue.replace('~',',');
}
if (document.getElementById('imgds')) {
document.getElementById('imgds').value+=oselovalue.replace('~',',');
//document.getElementById('imgds').style.display='inline-block';
//document.getElementById('imgds').style.width='300px';
if (document.getElementById('smyim').innerHTML == '') { document.getElementById('smyim').innerHTML='ImageMagick switches: '; }
} else if (document.getElementById('myim')) {
document.getElementById('myim').innerHTML='<span is=smyim>ImageMagick switches: </span><input id=imqty name=imqty type=text style=display:inline-block; value=\"\"></input><input id=imgds name=imgds type=hidden style=display:inline-block; value=\"|' + oselovalue.replace('~',',') + '\"></input>';
document.getElementById('imqty').style.width='300px';
}
oselo.value='';
}
}


Previous relevant Animated GIF Creator GD Filter Interfacing Tutorial is shown below.

Animated GIF Creator GD Filter Interfacing Tutorial

Animated GIF Creator GD Filter Interfacing Tutorial

We are into image modification on the way to creating “compiled” animated GIF images, at the moment. Yesterday’s Animated GIF Creator ImageMagick Interfacing Tutorial got us interfacing (or integrating) …

  • ImageMagick batch processing “mogrify” ideas … and, today, it is the turn of …
  • GD image filters …

… into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.


Previous relevant Animated GIF Creator ImageMagick Interfacing Tutorial is shown below.

Animated GIF Creator ImageMagick Interfacing Tutorial

Animated GIF Creator ImageMagick Interfacing Tutorial

Yes, you guessed it! On top of yesterday’s ImageMagick Vignette Primer Tutorial, today we want to interface the ImageMagick batch processing “mogrify” ideas into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.

“Interfacing” (or integrating) into code often has you asking the question …


Where do we intervene to place our logic?

We’ve decided to “wrap” our PHP GD calls …

… into inhouse “our” prefixed function versions as per (PHP writing out PHP) …

<?php echo ”

\$imvgn='" . $imvgn . "'; /" . "/ vignette factor
\$imafn='" . $imafn . "'; /" . "/ affine transformation
\$imqty='" . $imqty . "'; /" . "/ image conversion and/or quality RE jpeg


function ourimagecreatefromstring(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '' && strpos(\$inidis, ';base64,') !== false) {
\$extis=str_replace('jpeg','jpg',explode('/', explode(';base64,', \$inidis)[0])[1]);
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, base64_encode(explode(';base64,', \$inidis)[1]));
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false ||
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
} else if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis=str_replace('jpeg','jpg','" . $inextis . "');
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
if (strpos(strtolower((\$imvgn . \$imafn . \$imqty)), '-format ') === false) { \$imvgn.=' -format " . str_replace('jpg','jpeg',$inextis) . " '; }
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromstring(\$inidis);
}

function ourimagecreatefromjpeg(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='jpg';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromjpeg(\$inidis);
}

function ourimagecreatefrompng(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='png';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefrompng(\$inidis);
}

function ourimagecreatefromgif(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='gif';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromgif(\$inidis);
}

function ourimagecreatefrombmp(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='bmp';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefrombmp(\$inidis);
}

“; ?>

… and change existant calls into (equivalent inhouse) “our” function calls as above.


Previous relevant ImageMagick Vignette Primer Tutorial is shown below.

ImageMagick Vignette Primer Tutorial

ImageMagick Vignette Primer Tutorial

Continuing on with the ImageMagick batch processing “mogrify” ideas of yesterday’s ImageMagick Batch Image Conversion Affine Transformation Tutorial and the “vignette” image editing ideas of Gimp Vignette Primer Tutorial, as below, today, we took some pet photos with an iPad’s Camera app and shared them off the Photos app via two Mail sharing option emails containing seven attachments each. Using an iPad, the JPEG “jpg” output files were too big for our inhouse Animated GIF Creator PHP web application to handle, and so to perform the …

  • animated GIF presentation, off these downloaded email photo attachments … and along the way …
  • quality adjusted them (yes, “mogrify” does not stuff JPG to JPG conversions, and we used “mogrify” -quality 20% switch here) … and …
  • rotate them 180 degrees (“mogrify” uses switches -affine -1,0,0,-1,0,0 -transform +repage here) … and …
  • vignette 50% (“mogrify” uses switch -vignette 50 here)

… on the way to compiling into an animated GIF image, and we turned to ImageMagick again, using its affine transformation talents, along with its awesome vignetting talents …




… where the last two dropdown options will be similar, the last showing the input image into ImageMagick can be an animated GIF that is truely treated like an animated GIF.

By the way, should you be interested in photo orientation off a mobile device camera, have a read of PHP Exif Image Information Revisit Tutorial.

Nala and Luna and Charlie bid you a fond farewell!


Previous relevant Gimp Vignette Primer Tutorial is shown below.

Gimp Vignette Primer Tutorial

Gimp Vignette Primer Tutorial

The last time we talked about the miraculous, redolent and amazing image editor called Gimp am sure there was someone in a shower … it stands to reason … and one of those showerers, surely, would have been singing The Gimp Song … and if not … why not? … but we digress … anyway we had the Gimp Transparency Primer Tutorial as shown below go into some image transparency issues with Gimp.

In today’s tutorial we make use of a great tutorial (even so far as with direct quotes below) called Add a Vignette to a Photograph with GIMP (thanks) to try a photographic technique called vignetting on one of the photographs we added, recently, into the mix of those of the Custom Header Image mix at this blog … specifically the one of Nala, the dog, on the door ledge. Need to warn you here and now that if there was the time all over again, it would be better achieved that second time around, but this is not the point with learning, but rather getting some starting point with a great “product” like Gimp, and trying it yourself, once you have a method. It boils down to:

  1. Open Gimp graphical editor application
  2. File->Open … pick your image file
  3. Layer->New Layer (we’ll call “Vg”) … pick Foreground Colour
  4. In the Layers dialog, click on your “Vg” layer to select it, and select Soft light from the “Mode” drop-down box
  5. Right click on your “Vg” layer and go to Add Layer Mask. In the dialog that pops up, you want “Initialise Layer Mask to” set to “White (full opacity)”. Click “Add”
  6. Below Opacity bar select Link icon next to Eye icon, which will already be showing
  7. Use the freeform select tool (press F to bring this up) and draw a selection somewhere around the primary point of interest in your photo
  8. Use your bucket tool (Shift+B) and click within the selection to fill it
  9. Deselect your selection with Select->None
  10. Go to Filters->Blur->Gaussian Blur. In the dialog that comes up, you want “Radius” set to a very large amount; a tenth of the longest edge of the photo is not too much
  11. Click on your “Vg” layer to select it (if it isn’t already selected), and then slide the opacity slider towards the right until the effect is subtle enough. Our (ever so subtle) example of Nala, in the tutorial, used an opacity of about 66%
  12. Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)

Here is an image comparison link.

As with most Gimp ideas, jump in and give it a go, as you’ll find your own ways and means of using this great product … am pretty sure.

Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.


Previous relevant Gimp Transparency Primer Tutorial is shown below.

Gimp Transparency Primer Tutorial

Gimp Transparency Primer Tutorial

Here is a tutorial that adds to a previous Gimp Layers Primer Tutorial as shown below, and gives you more insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.

Today’s tutorial where we construct a Birthday Card that needs tweaking for the words in front to be seen a bit more clearly, by making the image behind a bit more transparent, changes the transparency of a single image via:

  1. Open Gimp graphical editor application
  2. File->Open Layers … pick your image file
  3. If Layers window not showing, make it show via Windows->Layers – Brushes
  4. Below Opacity bar select Link icon next to Eye icon, which will already be showing
  5. Change Opacity bar setting to a value of Transparency (100% is Opaque, 0% is Transparent) that suits … today we do 70%
  6. File->Export
  7. Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)

As with most Gimp ideas, jump in and give it a go, as you’ll find your own ways and means of using this great product … am pretty sure.

Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.


Previous relevant Gimp Layers Primer Tutorial is shown below.

Gimp Layers Primer Tutorial

Gimp Layers Primer Tutorial

Here is a tutorial that gives you an insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.

Transparency (or its obverse, opacity) can be used to have the one image achieve several “ends” (ie. purposes). Although it is a bit of a clumsy example in the tutorial, you can see that the technique can be used for artistic purposes … often called “Photoshopping” (named after the more famous, and also brilliant, rival product, Photoshop).

Lots of those classic “Photoshopping” techniques can be achieved in Gimp, and some other tutorials at this blog touch on that.

Am sure you can imagine what the concept of a layer is with regard to image manipulation. Within Gimp, for beginners not used to this concept, you find yourself underestimating and underplaying what can be achieved with the various layers of a multi-layered image. In simplistic terms each layer has the functionality in Gimp to be treated as a whole new image, and this is the best way to think of it when trying to achieve what you want to achieve with Gimp.

Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in eLearning, Event-Driven Programming, GUI, Tutorials | Tagged , , , , , , , , , , , , , | Leave a comment

Editable Calendar PHP Tutorial

Editable Calendar PHP Tutorial

Editable Calendar PHP Tutorial

Up until yesterday’s Editable Calendar Collaboration Tutorial the roles for PHP were …

  • help bring non default yellow span element text over to any Google Translate language translation …
  • help reduce URL size for SMS links … otherwise …
  • if called under other conditions pass content through to the HTML webpage

… and today, just before “otherwise” above, we want to add …

Offer calendar timestamp equivalents that are not the Gregorian calendar, out of Julian, French, Jewish

… which PHP Calendar Functions can help with, via HTML iframe src attribute calling URLs like …


https://www.rjmprogramming.com.au/calendar_contenteditable.php?docals=y&monththe=8&yearthe=2026&datethe=1

involving, in the changed calendar_contenteditable.php helping PHP …


<?php
// calendar_contenteditable.php
// RJM Programming - September, 2026
// Help out calendar_contenteditable.html
error_reporting(E_ALL);

function extended_jdtofrench($juliandate) { // thanks to https://www.php.net/manual/en/function.jdtofrench.php
if ($juliandate > 2380945) {
// jdtofrench () only accepts dates until september 1806
$gregorian_date = jdtogregorian ($juliandate);
$temp = explode ('/', $gregorian_date);
$year = $temp[2];
$juliandate = gregoriantojd ($temp[0], $temp[1], 1805);
$republican_date = jdtofrench ($juliandate);
$republican_date = explode ('/', $republican_date);
$diff = $year - 1805;
$republican_date[2] = $republican_date[2] + $diff;
} else {
$republican_date = jdtofrench($juliandate);
}
return $republican_date[1] . '/' . (1 + $republican_date[0]) . '/' . $republican_date[2];
}

function mdytodmy($inv) {
$things=explode('/', $inv);
if (sizeof($things) == 3) {
return '' . $things[1] . '/' . (1 + $things[0]) . '/' . $things[2];
}
return $inv;
}

function mdytodmyj($inv) {
$things=explode('/', $inv);
if (sizeof($things) == 3) {
return '' . $things[1] . '/' . (0 + $things[0]) . '/' . $things[2];
}
return $inv;
}

if (isset($_GET['docals']) && isset($_GET['monththe']) && isset($_GET['yearthe'])) {
//data-gregorian='' data-julian='' data-jewish='' data-french=''
$onl='';
$jewishtsh='';
$frenchts='';
$gregdate='1';
$gregmonth='' . $_GET['monththe'];
$gregyear='' . $_GET['yearthe'];
if (isset($_GET['datethe'])) {
$gregdate='' . $_GET['datethe'];
}
$jdnum=gregoriantojd($gregmonth, $gregdate, $gregyear);
$gregoriants='' . $gregmonth . '/' . $gregdate . '/' . $gregyear;
$frenchts=extended_jdtofrench(($jdnum + 2));
$jewishts=jdtojewish(($jdnum + 1));
$juliants=jdtojulian($jdnum);
$jewishtsz=jdtojewish(($jdnum + 1), true);
$jewishtsh=iconv('WINDOWS-1255', 'UTF-8', $jewishtsz);
$onl.=" var ispans=0, spans=parent.document.getElementsByTagName('span'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-gregorian', '" . mdytodmy($gregoriants) . "'); spans[1].setAttribute('data-gregorian', '" . mdytodmy($gregoriants) . "'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-julian', '" . mdytodmy($juliants) . "'); spans[1].setAttribute('data-julian', '" . mdytodmy($juliants) . "'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-french', '" . $frenchts . "'); spans[1].setAttribute('data-french', '" . $frenchts . "'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-jewish', '" . mdytodmyj($jewishts) . "'); spans[1].setAttribute('data-jewish', '" . mdytodmyj($jewishts) . "'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-jewishy', '" . $jewishtsh . "'); spans[1].setAttribute('data-jewishy', '" . $jewishtsh . "'); ";


if (5 == 5) {
for ($ioff=1; $ioff<=31; $ioff++) {
$jdnum++;
$gregoriants='' . $gregmonth . '/' . (1 + $ioff) . '/' . $gregyear;
$frenchts=extended_jdtofrench(($jdnum + 2));
$jewishts=jdtojewish(($jdnum + 1));
$juliants=jdtojulian($jdnum);
$jewishtsz=jdtojewish(($jdnum + 1), true);
$jewishtsh=iconv('WINDOWS-1255', 'UTF-8', $jewishtsz);
$onl.=" if (eval('' + spans.length) > " . ($ioff + 1) . ") { ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-gregorian', '" . mdytodmy($gregoriants) . "'); ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-julian', '" . mdytodmy($juliants) . "'); ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-french', '" . $frenchts . "'); ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-jewish', '" . mdytodmyj($jewishts) . "'); ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-jewishy', '" . $jewishtsh . "'); ";
$onl.=" } ";
}
}

echo "<html><head><meta charset='UTF-8'/></head><body onload=\"" . $onl . "\"></body></html>";
exit;
} else
if (isset($_POST['tihis'])) {
if (isset($_POST['ticpc'])) {
if (strpos(str_replace('+',' ',urldecode($_POST['ticpc'])) . '~$#', '.cpc~$#') !== false && strpos('~$#' . str_replace('+',' ',urldecode($_POST['ticpc'])), '~$#tmp' . DIRECTORY_SEPARATOR) !== false) {
//file_put_contents('zx.zx9','' . str_replace('+',' ',urldecode($_POST['ticpc'])));
file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . str_replace('+',' ',urldecode($_POST['ticpc'])), str_replace('+',' ',urldecode($_POST['tihis'])));
}
} else {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
}
exit;
} else if (isset($_GET['tihis'])) {
if (strpos(str_replace('+',' ',urldecode($_GET['tihis'])) . '~$#', '.cpc~$#') !== false && strpos('~$#' . str_replace('+',' ',urldecode($_GET['tihis'])), '~$#tmp' . DIRECTORY_SEPARATOR) !== false) {
if (file_exists(str_replace('+',' ',urldecode($_GET['tihis'])))) {
$newih=file_get_contents(str_replace('+',' ',urldecode($_GET['tihis'])));
if (trim($newih) == $newih) {
file_put_contents(str_replace('+',' ',urldecode($_GET['tihis'])), file_get_contents(str_replace('+',' ',urldecode($_GET['tihis']))) . ' ' );
} else if (3 == 4) {
unlink(str_replace('+',' ',urldecode($_GET['tihis'])));
}
if (strpos($newih, '<cap' . 'tion ') !== false) {
if (str_replace('?','',explode('tihis=', $_SERVER['QUERY_STRING'])[0]) != '') {
$usualis=file_get_contents('http://www.rjmprogramming.com.au/calendar_contenteditable.html?' . str_replace('?','',explode('tihis=', $_SERVER['QUERY_STRING'])[0]));
} else if (strpos($newih, '<cap' . 'tion data-durl="') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . basename(explode('"',explode('<cap' . 'tion data-durl="', $newih)[1])[0]));
} else {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
}
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
} else if (file_exists('tihis.txt')) {
$newih=file_get_contents('tihis.txt');
unlink('tihis.txt');
if (strpos($newih, '<cap' . 'tion ') !== false) {
if (strpos($newih, '<cap' . 'tion data-durl="') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . basename(explode('"',explode('<cap' . 'tion data-durl="', $newih)[1])[0]));
} else {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
}
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
?>

… called and applicable via yellow span element ondblclick event logic …


function othercals(ospan) {
var doall=false, spanos=[ospan], relv=false;
if (ospan.outerHTML.indexOf(' data-julian=') != -1) {
var thep=prompt('Can put other calendar type information after first three dots, as required regarding ' + ospan.outerHTML + ' if you enter a suitable data- prefixing answer? Append answer by ++ to apply to all the yellow span elements.', '');
if (thep == null) { thep=''; } else { thep=thep.toLowerCase(); }
if ((thep + '~').indexOf('++~') != -1) { doall=true; thep=thep.replace(/\+\+$/g,''); spanos=document.getElementsByTagName('span'); }
for (var ii=0; ii<spanos.length; ii++) {
if (spanos[ii].outerHTML.indexOf(' data-' + thep.replace(/data\-/g,'') + '="') != -1) {
relv=true;
spanos[ii].innerHTML=spanos[ii].innerHTML.replace('...', '... ' + spanos[ii].outerHTML.split(' data-' + thep.replace(/data\-/g,'') + '="')[1].split('"')[0] + ' ');
spanos[ii].title=spanos[ii].title.replace(' other calendar types', ' other calendar types, such as your ' + thep.replace(/data\-/g,'').substring(0,1).toUpperCase() + thep.replace(/data\-/g,'').substring(1).toLowerCase().replace(/y$/g,''));
}
}
if (relv) {
changesmade=true;
inplay=true;
lastinplayc=document.body.innerHTML + ' ';
}
}
}

… by the changed tenth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Collaboration Tutorial is shown below.

Editable Calendar Collaboration Tutorial

Editable Calendar Collaboration Tutorial

Yesterday’s Editable Calendar Cross Domain Sharing Tutorial had us …

  • sharing … and caring and we all know sharing can lead to …
  • collaboration

… and today we’re encouraging users to join in with others sharing editable calendars via email (and though the hashtagging goes beyond SMS lengths we’re including that as an option too, and we may find a way down the track that the lengths of data can be reduced to suit an SMS message …


function doemail(inidea) {
var newurl=document.URL.split('//')[0] + '//' + 'www.rjmprogramming.com.au/calendar_contenteditable.html' + '?_x_tr_tl=' + document.getElementById('language').value + '&month=' + themonth + '&theyear=' + theyear + '#' + encodeURIComponent('<h' + '1' + (('' + document.body.innerHTML).split(('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>');
var zfrom='youllneverfindthis', zto='youllneverfindthis';
var azx=top.document.getElementById('xae' + 'mail');
if (!azx) { azx=top.document.createElement("a"); }
top.document.body.appendChild(azx);
azx.style = "display: none";
azx.target = "_top";
azx.id = 'xae' + 'mail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h1')[0].innerHTML.split('<select')[0].split('</font>')[0].split('>')[eval(-1 + document.getElementsByTagName('h1')[0].innerHTML.split('<select')[0].split('</font>')[0].split('>').length)].replace(/\ \;/g,' ')) + '%20' + encodeURIComponent(document.getElementsByTagName('h1')[0].innerHTML.split('</select>')[1].split('</font>')[0].split('>')[eval(-1 + document.getElementsByTagName('h1')[0].innerHTML.split('</select>')[1].split('</font>')[0].split('>').length)].split('<')[0].replace(/\ \;/g,' ') + ' ... ') + '%20' + encodeURIComponent(document.getElementById('calcap').innerText) + '&body=' + encodeURIComponent(newurl); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
var newurl=document.URL.split('//')[0] + '//' + 'www.rjmprogramming.com.au/calendar_contenteditable.html' + '?_x_tr_tl=' + document.getElementById('language').value + '&month=' + themonth + '&theyear=' + theyear + '#' + encodeURIComponent('<h' + '1' + (('' + document.body.innerHTML).split(('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>');
if (1 == 1) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('findeditablecalendaremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('findeditablecalendaremailee');
}
window.localStorage.setItem('findeditablecalendaremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('findeditablecalendarsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('findeditablecalendarsmsno');
}
window.localStorage.setItem('findeditablecalendarsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
top.document.body.appendChild(azx);
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(newurl.replace('#','&').replace('?','#')); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
}
return '';
}

… we’ll see). Calendars can give that date and time context to collaboration between users, and so, we invite you to try this with the changed ninth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Cross Domain Sharing Tutorial is shown below.

Editable Calendar Cross Domain Sharing Tutorial

Editable Calendar Cross Domain Sharing Tutorial

Unless you are new to the party you’ll know that with yesterday’s Editable Calendar Styling Tutorial‘s Editable Calendar project …


Google Translate

… is our go to regarding the internationalization and language translation functionality. We get to Google Translate, where permitted (up to you, and whether you have popup blockers happening with the web browser you are using), via a popup window, which means, in that scenario, there are two distinct domains in play …

  1. Google Translate‘s domain “child” window … and …
  2. RJM Programming‘s domain “parent” window

… and so the communications between parent and child (regarding any Saved calendar data a user may want to apply and remember), in this scenario, are restricted, but asking Under what conditions can window.opener.parentfunc() type of call work when the domains are different? reminded us of the talents of window.postMessage to help here, thanks. It got us to …

Be listening out at the RJM Programming domain document.URL scenario “parent” window …

window.addEventListener("message", (event) => { // thanks to https://www.google.com/search?q=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&rlz=1C5OZZY_en&oq=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTI2NzQwajBqN6gCALACAA&sourceid=chrome&source=chrome.ob&ie=UTF-8
var thislang='';
// Always verify the origin of the sender for security
if (event.origin !== "https://www-rjmprogramming-com-au.translate.goog") return;

if (('' + event.data).indexOf(encodeURIComponent('<h' + '1')) != -1) {
if (document.getElementById('language').outerHTML.indexOf(' data-lcode="') != -1 && document.getElementById('language').outerHTML.indexOf(' data-lcode=""') == -1) {
thislang=document.getElementById('language').getAttribute('data-lcode');
}
document.body.innerHTML='<h' + '1' + decodeURIComponent(('' + event.data).split(encodeURIComponent('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>';
if (thislang.trim() != '') { document.getElementById('language').value=thislang; document.getElementById('language').setAttribute('data-lcode', thislang); }
inplay=true;
lastinplayc=document.body.innerHTML + ' ';
fromfor=true;
setTimeout(lsit, 2000);
}
});
Be broadcasting at the Google Translate domain document.URL scenario “child” window …

if (window.opener) {
if (document.URL.indexOf('&iam=') != -1) {
window.opener.postMessage(encodeURIComponent(document.body.innerHTML), decodeURIComponent(document.URL.split('&iam=')[1].split('&')[0].split('#')[0]));
} else {
window.opener.postMessage(encodeURIComponent(document.body.innerHTML), 'https://rjmprogramming.com.au');
}
}

… for our Editable Calendar project.

Also, today, we introduce Month, Year “breadcrumb” caption element …

CSS caption element SVG background image based arrows … thanks to this great advice

#calcap { // thanks to https://www.google.com/search?q=background+arrow+background+svg+images+at+left+and+right+of+an+element&rlz=1C5OZZY_en&oq=background+arrow+background+svg+images+at+left+and+right+of+an+element&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTM1ODAyajBqN6gCALACAA&sourceid=chrome&source=chrome.ob&ie=UTF-8
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); background-color: rgb(240,240,240); border-radius: 8px;
background-image:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>'),
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>');
background-position: left center, right center;
background-repeat: no-repeat;
}
Javascript caption element ondblclick event logic (based on cursor positioning)

function incit(e) {
var x=-999, y=-999;
var rectis=e.target.getBoundingClientRect();
if (e.touches) {
if (e.touches[0].pageX) {
x = e.touches[0].pageX;
y = e.touches[0].pageY;
} else {
x = e.touches[0].clientX;
y = e.touches[0].clientY;
}
} else if (e.clientX || e.clientY) {
x = e.clientX;
y = e.clientY;
} else {
x = e.pageX;
x = e.pageY;
}
if (x >= 0 && y >= 0 && document.getElementById('calcap').getAttribute('data-thism') != '') {
var curm=eval('' + document.getElementById('calcap').getAttribute('data-thism'));
var cury=eval('' + document.getElementById('calcap').getAttribute('data-thisy'));
if (x <= eval(eval('' + rectis.left) + eval(eval('' + rectis.width) / 2))) {
themonth=eval(-1 + curm);
if (themonth < 0) {
themonth=11;
theyear=eval(-1 + cury);
}
e.target.innerHTML='' + mths[themonth] + ', ' + theyear;
calcapv='';
newmy(e.target);
} else {
themonth=eval(1 + curm);
if (themonth > 11) {
themonth=0;
theyear=eval(1 + cury);
}
e.target.innerHTML='' + mths[themonth] + ', ' + theyear;
calcapv='';
newmy(e.target);
}
}
}

… in the changed eighth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Styling Tutorial is shown below.

Editable Calendar Styling Tutorial

Editable Calendar Styling Tutorial

Today is Styling Day regarding our current Editable Calendar project we last talked about with yesterday’s Editable Calendar Save and Recall Tutorial. We cannot recall ever arranging the body element background exactly as we did (probably because we only had the top and the bottom of the screen for context, the middle being the calendar) in the CSS below …


<style>
td {
height: 15%;
vertical-align: top;
padding: 2 2 2 2;
}

td {
border-radius: 8px;
background-color: rgb(252,252,252);
}

.pad {
border-color: transparent;
background-color: transparent;
}

td span {
background: linear-gradient(to bottom, rgba(170,170,170,0.3) 30%, yellow 30%);
font-weight: bold;
}

#calcap {
font-weight: bold;
text-shadow: -1px 1px 1px #952dff;
}

#bsave {
background-color: orange;
}

#calcap {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); background-color: rgb(240,240,240); border-radius: 8px;
}

th {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); color: blue; background-color: rgb(240,240,240); border-radius: 8px;
}

button {
border-radius: 8px;
}

select {
border-radius: 8px;
}

table {
border-radius: 6px;
background-color: transparent;
}

body {
background-image: linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0.8)),url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAMB0R0_H50ZU4AW0t51IqB0WwOyF1BEmSPy3MMxisNythxHs69b4lcx8&s=10);
background-size: contain;
background-repeat: repeat;
}

</style>

… where some judiciously placed background-color:transparent helps this out with transparency ideas. We find applying border-radius a good way to “soften” the look, and “text-shadow” as a way to allow for a 3D look to innerHTML contenteditable=true types of elements.

Yet again, feel free to try this changed, better styled seventh draft Editable Calendar you can try below.


Previous relevant Editable Calendar Save and Recall Tutorial is shown below.

Editable Calendar Save and Recall Tutorial

Editable Calendar Save and Recall Tutorial

Regarding the recent Editable Calendar Get Arguments Tutorial it set up …

  • Month, Year as a dynamic user editable concept … worthy of …
  • coding to allow for the Saving and Recall of user calendar data according to their Month, Year value …

… in window.localStorage data repository particular to the web browser and device they are using.

To deploy this, we’ve decided on creating a scenario with …

  • a new dropdown (ie. select) element containing Month, Year Saved value choices … nested …

    <button id=bsave translate=yes onclick=lsit(); style=display:none;>Save <select onchange=recallsm(this); id=savedd style=display:none;><option translate=yes value=''>Optionally recall a saved Editable Calendar month ...</option></select></button>

    …within …
  • a new button element that a user clicks/taps to Save the current calendar data regarding the displayed Month, Year into window.localStorage data repository …

    function recallsm(osel) {
    if (osel.value.trim() != '') {
    if (osel.value.substring(0,1) == '-') {
    oselvalue=osel.value;
    setTimeout(function(){
    try {
    window.localStorage.removeItem('editable_calendar_' + oselvalue.substring(1).replace(',','_').replace('.','_'));
    } catch(jghfjhg) { }
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }, 2000);
    } else {
    themonth=eval(-1 + eval('' + osel.value.replace(',','.').split('.')[0]));
    theyear=eval('' + eval('' + osel.value.replace(',','.').split('.')[1]));
    usuff='&year=' + theyear + '&month=' + themonth;
    now = new Date(eval('' + theyear), eval('' + themonth), 1);
    document.body.innerHTML=decodeURIComponent(('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)));
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }
    }
    }

    function lscheck() {
    if (origdd == '') { origdd=document.getElementById('savedd').innerHTML; }
    Object.keys(window.localStorage).forEach(function(key){
    if (('' + key).indexOf('editable_calendar_') == 0) {
    //alert(('' + key));
    //alert(('' + key).split('editable_calendar_')[1]);
    //alert(document.getElementById('savedd').innerHTML);
    if (document.getElementById('savedd').innerHTML.indexOf('>' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>') == -1) {
    //alert('2:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').innerHTML+='<option value="' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    document.getElementById('savedd').innerHTML+='<option value="-' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">Forget ' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    //alert('3:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').style.display='inline-block';
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    });
    }

    function lsit() {
    if (inplay && lastinplayc != '') {
    var curval=('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)).replace(/^undefined/g,'').replace(/^null/g,'');
    if (curval != '') {
    if (decodeURIComponent(curval) != lastinplayc) {
    window.localStorage.removeItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear);
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    }
    } else {
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    }
    }
    }

    function inpcheck() {
    if (inplay) {
    if (document.body.innerHTML != lastinplayc) {
    lastinplayc=document.body.innerHTML.trim();
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    }

… in the changed sixth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Get Arguments Tutorial is shown below.

Editable Calendar Get Arguments Tutorial

Editable Calendar Get Arguments Tutorial

Get arguments are those ? or & delimited parts to URLs you sometimes see up at the address bar of web browsers. We’d be lost without them. And, though Google Translate uses it’s own set arrangement of get arguments for this “static URL” language translation mode of use we’ve been using in this Editable Calendar recent project, it does not mean that Google Translate will stop you also using any of your own “personalized” get argument arrangements as well, that it just ends up “sending through to the keeper” …


if (document.URL.indexOf('month=') != -1 && document.URL.indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + document.URL.split('year=')[1].split('&')[0].split('#')[0]), eval('' + document.URL.split('month=')[1].split('&')[0].split('#')[0]), 1);
} else if (('' + parent.document.URL).indexOf('month=') != -1 && ('' + parent.document.URL).indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + ('' + parent.document.URL).split('year=')[1].split('&')[0].split('#')[0]), eval('' + ('' + parent.document.URL).split('month=')[1].split('&')[0].split('#')[0]), 1);
}

Yes, the first cab off the rank, tipping our toes into thoughts using this, building on yesterday’s Editable Calendar English User Entry Translation Tutorial, is to …

  • take what used to be a pretty static …

    Month, Year

    … HTML caption element … and …

  • apply the global attribute …

    contenteditable=true

    … to it, making it editable and open to more personalization regarding your Editable Calendar, on the way to what we hope ends up being a more useful web application tool … while …
  • that HTML caption element’s onblur event …

    var now = new Date(), firstDayIndex=0, daysThisMonth=0;
    var origih=''; // is filled with innards of the original calendar table element at document.body onload
    var calcapv=''; // is updated with HTML caption element innards before any user interaction
    var usuff=''; // personalized get arguments go here
    var changesmade=false; // goes to true when user blurb onkeypress happens within calendar tbody span elements or if the Month, Year changes

    function newmy(ocap) {
    if (ocap.innerHTML != calcapv) {
    if (ocap.innerHTML.indexOf(',') != -1) {
    if (mths.indexOf(ocap.innerHTML.split(',')[0].trim()) != -1 && eval('' + ocap.innerHTML.split(',')[1].trim().length) == 4) {
    //changesmade=true;
    usuff='&year=' + ocap.innerHTML.split(',')[1].trim() + '&month=' + mths.indexOf(ocap.innerHTML.split(',')[0].trim());
    now = new Date(eval('' + ocap.innerHTML.split(',')[1].trim()), eval('' + mths.indexOf(ocap.innerHTML.split(',')[0].trim())), 1);
    document.getElementById('wp-calendar').innerHTML=origih;
    thismonth();
    }
    }
    }
    }

    … can be used to store our “personalized get arguments”
  • ready to be appended to the (web browser address bar) URL reaching Google Translate …

    var valvalue='';

    function ChangeLcodeNow(){
    var val = document.getElementById('language'), dd=null, pprefix='';
    docURL=documentURL.split('rjmprogramming.com.au/')[eval(-1 + documentURL.split('rjmprogramming.com.au/').length)];
    if (docURL.indexOf('.translate.goog/') != -1) { docURL=docURL.split('.translate.goog/')[1]; }
    if (changesmade) { docURL=docURL.replace('.html','.php').replace('.htm','.php'); ajaxit(); }
    if (documentURL.indexOf('&_x_tr_tl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_blank', 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    documentURL=documentURL.replace('&_x_tr_tl=', '&xtrtl=');
    } else if (documentURL.indexOf('&xtrtl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_top');
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    }
    }

    … and through to our own RJM Programming code

… the one in today’s tutorial picture featuring …


https://www-rjmprogramming-com-au.translate.goog/calendar_contenteditable?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=zh-CN&year=2026&month=1

Again, feel free to try this in the changed fifth draft Editable Calendar you can try below, helped out by a tweaked calendar_contenteditable.php helping PHP newish player.


Previous relevant Editable Calendar English User Entry Translation Tutorial is shown below.

Editable Calendar English User Entry Translation Tutorial

Editable Calendar English User Entry Translation Tutorial

For today’s work, we were thinking of using window.sessionStorage logic, but because of the advice of Can a window.open Google Translate popup window access the sessionStorage written by the originating window from another domain? warning us off, that what we regard as an “ever so cute” solution is not a goer, alas! So, what else is going to help regarding the dilemma …

Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out …

… we referenced when presenting Editable Calendar Google Translate Tutorial before the recent Editable Calendar Intl Tutorial? Today’s work plonks them back in by involving PHP, as our server side language that can write to and read from ascii text files stored on our RJM Programming domain web server. By, when needed, getting Google Translate to, when required (ie. we’ve decided this is when the keyboard onkeypress happens), reference a .php based RJM Programming URL, leaves that opportunity to …

Shape to write the calendar table innards to an ascii web server text file via Ajax … client side …

function ajaxit() {
var zhr = new XMLHttpRequest();
var zform = new FormData();
zform.append('tihis', document.getElementById('wp-calendar').innerHTML);
zhr.open('post', '/' + docURL.split('?')[0].split('#')[0].replace('.html','.php').replace('.htm','.php'), true);
zhr.send(zform);
}
Write the calendar table innards to an ascii web server text file via PHP … server side …
<?php

if (isset($_POST['tihis'])) {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
exit;
}

?>
As needed, very soon after, as Google Translate is called with a .php based URL, substitute this saved table innards into a read from default representation of the HTML via PHP … server side …
<?php

if (file_exists('tihis.txt')) {
$newih=file_get_contents('tihis.txt');
unlink('tihis.txt');
if (strpos($newih, '<cap' . 'tion ') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
}

?>
Leaving any document.body onload event logics out (in first codeline of such onload event Javascript function), in such a scenario, leaving it all to Google Translate’s talents … client side …

if (('' + parent.document.URL).indexOf('.php') != -1) { return ''; }

Feel free to try this in the changed fourth draft Editable Calendar you can try below, helped out by a new calendar_contenteditable.php helping PHP new player.


Previous relevant Editable Calendar Intl Tutorial is shown below.

Editable Calendar Intl Tutorial

Editable Calendar Intl Tutorial

Because the theme of our current Editable Calendar web application revolves around time stamps and day names and month names regarding yesterday’s Editable Calendar Google Translate Tutorial that means, regarding language internationalization ideas …

  • as well as yesterday’s Google Translate ideas … also available is …
  • JavaScript Intl Object Reference ideas

… that can help with language translations, and we make available to the user via a toggling “-” (to “+”) HTML element modus operandi.

Especially, as we need more work improving the Google Translate interfacing, trying this changed third draft Editable Calendar you can try below, is definitely worth considering.


Previous relevant Editable Calendar Google Translate Tutorial is shown below.

Editable Calendar Google Translate Tutorial

Editable Calendar Google Translate Tutorial

Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …


Google Translate

… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.

Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changed second draft Editable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.


Previous relevant Editable Calendar Primer Tutorial is shown below.

Editable Calendar Primer Tutorial

Editable Calendar Primer Tutorial

Today’s new Editable Calendar themed inhouse web application combines …

Today’s steps of progress in this project …

  1. establish the current month and year’s “month showing” calendar into a webpage … and …
  2. within that calendar date cell we place a span element with contenteditable=true … allowing …
  3. user to add content into the calendar

… in this first draft “proof of new to us concept” Editable Calendar you can try below. We hope you tune in tomorrow for more progress!


Previous relevant Event Calendar Collaboration Textarea Highlight Linking Tutorial is shown below.

Event Calendar Collaboration Textarea Highlight Linking Tutorial

Event Calendar Collaboration Textarea Highlight Linking Tutorial

For the first time we can remember, with our Events in Month web application of yesterday’s Event Calendar Collaboration Remembering Recipient Tutorial

  • we’re channelling how in emails “word strings” starting with “http” become links … and so …
  • in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available

Here is the relevant Javascript we ended up with, for this …


var thesel='', showsel=false, lastsel='';

function showthesel() {
var uwords=[], iuw=1, ils=0;
if (showsel) {
showsel=false;
if (lastsel != thesel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
lastsel=thesel;
thesel='';
}
if (lastsel != '' && thesel == '') {
uwords=lastsel.toLowerCase().split('http');
ils=eval(4 + eval('' + uwords[0].length));
for (iuw=1; iuw<uwords.length; iuw++) {
window.open('//' + lastsel.substring(ils).split('//')[1].split(' ')[0].split(String.fromCharCode(10))[0].split(String.fromCharCode(13))[0], '_blank', 'top=' + eval(10 * iuw + 50) + ',left=' + eval(10 * iuw + 50) + ',width=600,height=600');
ils+=eval(4 + eval('' + uwords[iuw].length));
}
}
}
}

function handleSelection() { // thanks to https://stackoverflow.com/questions/46651479/reacting-to-selection-changes-in-an-html-textarea
const activeElement = document.activeElement;

// Make sure this is your textarea
if (activeElement && activeElement.outerHTML.indexOf('<textarea') == 0) {
const range = {
start: activeElement.selectionStart,
end: activeElement.selectionEnd
};
// Do something with your range
if (eval('' + range.end) > eval('' + range.start)) {
thesel=(activeElement.value.substring(range.start).substring(0, eval(eval('' + range.end) - eval('' + range.start) )));
if (!showsel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
showsel=true;
setTimeout(showthesel, 5000);
}
}
}
}

document.addEventListener('selectionchange', handleSelection);

Also, today, we have a few CSS tweaks in the changed events_in_month.htm parent of the Events in Month web application. Feel free to try it for yourself.

Stop Press

Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!


Previous relevant Event Calendar Collaboration Remembering Recipient Tutorial is shown below.

Event Calendar Collaboration Remembering Recipient Tutorial

Event Calendar Collaboration Remembering Recipient Tutorial

When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …

  • When it comes to procedures …
  • Aaaaaarrrrgggghhhh
  • Nuances count … regarding …
  • Errrrrrr
  • Saving time …
  • Saving frustration

… resulting in the “easy to remember” acronym WANESS … we rest our cases!

What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.

Global variables initialization …

var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …

if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …

function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}

function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic

function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}

//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}

return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}

var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}

Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changed events_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!


Previous relevant Event Calendar Collaboration Tutorial is shown below.

Event Calendar Collaboration Tutorial

Event Calendar Collaboration Tutorial

The Event Calendar web application project, of Event Calendar PHP Bookmark Tutorial, from last year is worth a revisit, the reason being …

  • it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
  • each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
  • hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs

… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!

Timing became really important with this integration of …

  • email
  • SMS

… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …

  1. Month
  2. Year

… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …

Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons

<h1>Events in Month &nbsp;&nbsp;<button style=display:none; title='Share via Email' id=semail onclick="doemail('');">&#128231;</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>&#128223;</button> </h1>
New static HTML div element placed to the bottom of the body element …

<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …

var bigbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1].split('&')[0]) : '';
if (bigbih != '' && bigbih.indexOf('xae' + 'mail') == -1) {
bigbih+='<a target=_blank id=xae' + 'mail style=display:none; href="mailto:"></a>';
}
if (bigbih != '' && bigbih.indexOf('xas' + 'ms') == -1) {
bigbih+='<a target=_blank id=xas' + 'ms style=display:none; href="sms:"></a>';
}
var viatext='', smallbih='', wes=null, sparewes=null;
On or around document.body onload event we can analyze any such location.hash hashtagging data …

if (bigbih.indexOf('</he' + 'ad>') != -1 || bigbih.indexOf('<b' + 'o' + 'dy') != -1 || bigbih.indexOf('<h' + '1') == 0) {
//bigbih=bigbih.replace('" id="eventcalendar"', 'margin-top:50px;" id="eventcalendar"');
viatext=' (via contact at ' + ('' + new Date()) + ')';
if (bigbih.indexOf('<h' + '1') == 0) {
smallbih=bigbih;
} else {
smallbih=bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].replace(bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].split('>')[0] + '>', '');
}
wes=windowopen('./events_in_month.htm','_blank','top=40,left=40,width=800,height=800');
//alert(bigbih);
if (wes) {
setTimeout(function(){
wes.document.write(bigbih);
}, 3000);
//document.body.innerHTML=bigbih;
setTimeout(function(){
wes.document.getElementById('semail').style.display='inline-block';
wes.document.getElementById('ssms').style.display='inline-block';
wes.document.getElementsByTagName('h1')[0].style.display='none';
wes.document.getElementsByTagName('h3')[0].style.display='none';
//wes.setviatext(viatext);
//wes.setphpthere(' ');
//wes.togglewords();
}, 5000);
}
}
A window.open wrapper windowopen Javascript function …

function windowopen(poneis, ptwois, pthreeis) {
document.getElementById('divas').innerHTML='<iframe name=ovif id=ovif style="position:absolute;top:140px;left:0px;width:100%;height:100%;z-index:99;" src="' + poneis + '" onload=wopencheck(this);></iframe>';
//document.getElementsByTagName('h1')[0].style.opacity='0.0';
//document.getElementsByTagName('h3')[0].style.opacity='0.0';
sparewes=window.open(poneis.split('.htm')[0] + '.php', 'ovif');
//sparewes=window.open(poneis, 'ovif');
sparewes.document.write(bigbih);
setTimeout(function(){
sparewes.document.getElementById('semail').style.display='inline-block';
sparewes.document.getElementById('ssms').style.display='inline-block';
var sdem=sparewes.document.getElementById('semail').outerHTML;
var sdss=sparewes.document.getElementById('ssms').outerHTML;
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdss,'');
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdem,'');
sparewes.document.getElementsByTagName('h1')[0].style.display='none';
sparewes.document.getElementsByTagName('h3')[0].style.display='none';
sparewes.document.getElementById('dmore').innerHTML+=(' ' + sdem.replace('doeJUNKmail(', 'parent.doe' + 'mail(') + ' ' + sdss.replace('dosJUNKms(', 'parent.dos' + 'ms(') + '<br>');
//sparewes.setviatext(viatext);
//sparewes.setphpthere(' ');
//sparewes.togglewords();
}, 5000);
return null;
}
Is augmented by other new Javascript functions …

function hfanalyze() {
var fio=document.getElementById('ecform');
if (forceit) { forceit=false; return true; }
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
//alert(fions.length);
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value.replace(/\?\>/g,'? >'));
delm='&';
}
//alert(testurl.length);
//if (eval('' + testurl.length) < 750) { return true; }
//if (lastbut.value.indexOf('New') == 0 || 11 == 11) {
if (22 == 22) {
//wo=window.open('', '_blank', 'top=50,left=50,height=600,width=600');
//wo.document.write('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
return ('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
} else if (phpthere != '') {
if (1 == 1) {
postit(fio, false, testurl);
} else {
fio.target='phpif';
fio.method='POST';
fio.action='./events_in_month.php';
if (!document.getElementById('dmore')) {
document.getElementById('ddmore').innerHTML='<input type=hidden id=caltitle name=caltitle value="' + document.getElementById('myoldsel').options[document.getElementById('myoldsel').selectedIndex].innerText + '"></input><input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
} else {
document.getElementById('dmore').innerHTML='<input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
}
}
return 'true';
}
return 'false';
}

function doemail(inidea) {
var azx=document.getElementById('xae' + 'mail');
if (!azx) { azx=document.createElement("a"); }
//if (1 == 1) {
//document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
//}
azx.style = "display: none";
azx.id = 'xaem' + 'ail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h2')[0].innerHTML.split('&')[0].split('<')[0].replace(/\`/g,'').replace(/^Events\ in\ /g, 'Events in Month ').substring(0)) + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze())); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead.', snum)).replace(/^null/g,'');
} else {
snum=('' + prompt('Please enter SMS number to send to.', snum)).replace(/^null/g,'');
}
if (snum.indexOf('@') != -1) {
return doemail(snum);
} else if (snum.trim() != '') {
alert('Snum=' + snum + '?');
var azx=document.getElementById('xas' + 'ms');
if (azx == null) { azx=document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()));
azx.click();
}
return '';
}

function wopencheck(iois) {
var aconto = null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
aconto.body.innerHTML=smallbih;
aconto.setviatext(viatext);
}
}
}

function setviatext(towhat) {
viatext=towhat;
}

… to help bed down this new sharing and collaboration functionality in a changed events_in_month.htm parent of the Events in Month web application.


Previous relevant Event Calendar PHP Bookmark Tutorial is shown below.

Event Calendar PHP Bookmark Tutorial

Event Calendar PHP Bookmark Tutorial

Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to


Record to Remember via Bookmark

… use the web browser Bookmarks to help you recall an Events in Month report with long entries.

Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …


https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1

… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!

To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …


var documentURL=document.URL;

… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …


#August__2023_00000__1

… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).

Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).

Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …


<style>
#eventcalendar {
background-color: #fcfcfc;
}

td {
padding-top: 2px;
padding-left: 2px;
padding-right: 2px;
padding-bottom: 12px;
}

.dayb {
color: white;
background-color: red;
padding: 5 5 5 5;
border-radius: 80px;
margin-bottom: 15px;
}

.dow {
color: purple;
font-style: bold;
}

.selday {
margin-left: 8px;
background-color: rgba(255,0,0,0.7);
display: inline-block;
width: 50px;
border-color: transparent;
text-overflow: ellipsis;
}

.tablurb {
background: linear-gradient(to right, white, lightpink, pink);
}

input[type="submit"] {
margin-bottom: 3px;
border-radius: 180px;
}

input[type="number"] {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f3f3f3;
padding: 2 2 2 2;
}

#smonth {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f9f9f9;
padding: 2 2 2 2;
}

.boldtitle {
background-color: rgba(0, 211, 107, 0.2);
}

.boldtitle + .tablurb {
margin-top: 8px;
}


.selhistory {
border-radius: 180px;
background-color: lightpink;
padding: 2 2 2 2;
}
</style>

… and we thank this webpage for the heads up regarding how to calculate week numbers within a year data item displays now available in …


Previous relevant Event Calendar PHP Tutorial is shown below.

Event Calendar PHP Tutorial

Event Calendar PHP Tutorial

Let’s face it. Serverside PHP is just great! It opens up so many opportunities regarding data in your web applications.

As such, onto yesterday’s Event Calendar New Window Tutorial logic we now have a …


Record to Remember

… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.

The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …


<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm

$hcont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.htm');
$cont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php');
$ipad=server_remote_addr();
$ipadless=$ipad;
$js='';

function server_remote_addr() {
global $ipadless;
$rma = $_SERVER['REMOTE_ADDR'];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
// you can add different browsers with the same way ..
$ipadless=str_replace(".", "_", str_replace(":", "_", $rma));
if(preg_match('/(chromium)[ \/]([\w.]+)/', $ua))
$rma = '000000'.$rma;
elseif(preg_match('/(chrome)[ \/]([\w.]+)/', $ua))
$rma = '00000'.$rma;
elseif(preg_match('/(safari)[ \/]([\w.]+)/', $ua))
$rma = '0000'.$rma;
elseif(preg_match('/(opera)[ \/]([\w.]+)/', $ua))
$rma = '000'.$rma;
elseif(preg_match('/(msie)[ \/]([\w.]+)/', $ua))
$rma = '00'.$rma;
elseif(preg_match('/(mozilla)[ \/]([\w.]+)/', $ua))
$rma = '0'.$rma;
return str_replace(".", "_", str_replace(":", "_", $rma));
}

$itdone=false;
if (isset($_GET['init'])) {
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
echo "<html><head><script type='text/javascript'> " . $js . " </script></head><body><p>" . $ipad . "</p></body></html>";
} else if (isset($_POST['phpthere']) && isset($_POST['bigurl']) && isset($_POST['caltitle'])) {
//file_put_contents('xz.xz', 'l');
if (strpos($cont, '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n") === false) {
//file_put_contents('xz.xzz', 'l');
$cont=str_replace('?' . '>', '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n" . '?' . '>', $cont);
file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php', $cont);
}
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
//file_put_contents('xz.xzzz', "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>");
echo "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>";
} else {
echo $hcont;
}
exit;

//
//
// January__1970_00000__1=http://localhost:8888/events_in_month.htm?caltitle=January%2C%201970&i01.00=&ta01.00=kgjfjhf%20jhgfjhf%20jhkgkjhg%20jhgkjhg%20jkghhg%20jkhgkjhg%20kjhgkjhg&i02.00=&ta02.00=kjhgkjh%20kjhgkjhg%20jkhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg%20kjgkjhg&i03.00=&ta03.00=kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg&i04.00=&ta04.00=&i05.00=&ta05.00=&i06.00=&ta06.00=&i07.00=&ta07.00=&i08.00=&ta08.00=&i09.00=&ta09.00=&i10.00=&ta10.00=&i11.00=&ta11.00=&i12.00=&ta12.00=&i13.00=&ta13.00=&i14.00=&ta14.00=&i15.00=&ta15.00=&i16.00=&ta16.00=&i17.00=&ta17.00=&i18.00=&ta18.00=&i19.00=&ta19.00=&i20.00=&ta20.00=&i21.00=&ta21.00=&i22.00=&ta22.00=&i23.00=&ta23.00=&i24.00=&ta24.00=&i25.00=&ta25.00=&i26.00=&ta26.00=&i27.00=&ta27.00=&i28.00=&ta28.00=&i29.00=&ta29.00=&i30.00=&ta30.00=&i31.00=&ta31.00=
?>

Yes, the parent needed to change for our events_in_month.htm parent of Events in Month web application role.


Previous relevant Event Calendar New Window Tutorial is shown below.

Event Calendar New Window Tutorial

Event Calendar New Window Tutorial

Onto yesterday’s Event Calendar Remembered Tutorial‘s “Mystery Dilemma”

But, there’s an inherent weakness with the design, we’ll go into more into the future.

… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.

Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.

Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …

  1. bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
  2. pick Inspect option …
  3. be in Elements tab of your Web Inspector … and …
  4. highlight top <html> tag …
  5. Context Menu with a right click or two finger gesture …
  6. pick Copy -> Copy outerHTML … so that …
  7. your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
  8. Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)

The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …


function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}

… the form submit buttons are reworded accordingly.

Another idea from this blog thread’s inspiration …

… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.

And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …

  • text shadow means by which the text of Event Calendar can be more impactive

    <div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
  • dropdown element conditional styling

    <style>
    .dayb {
    color: white;
    background-color: red;
    padding: 5 5 5 5;
    }


    .dow {
    color: purple;
    font-style: bold;
    }

    .selday {
    margin-left: 8px;
    background-color: rgba(255,0,0,0.7);
    display: inline-block;
    width: 50px;
    }
    </style>



    if (thislabel.substring(0,1) == 'i') {
    if (thisval.trim() != '') {
    document.getElementById(thislabel.replace('i', 'sel')).style.color='white';
    document.getElementById(thislabel.replace('i', 'sel')).style.backgroundColor='red';

    }
    document.getElementById(thislabel.replace('i', 'opt')).innerText=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + '); //.replace(/\+$/g, ' ');
    document.getElementById(thislabel).value=thisval.replace(/\+$/g, ' ');
    } else {
    document.getElementById(thislabel).value=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + ');
    }
    }
  • placeholder on Blurb conditional existence

    var ourdata='';
    // ...
    if (documentURL.indexOf('?') != -1) {
    if (documentURL.indexOf('?caltitle=') != -1) { ourdata='data-'; }
    // ...
    trtemplate='<tr id=tr01.00><td style=width:22%;><span id=sone01.00 class=dow>' + dotw[adate.getDay()].toUpperCase().substring(0,3) + '</span><br><br><span id=stwo01.00 class=dayb>1<span onblur=sepit(this); contenteditable=true id=sp01.00></span><input type=hidden id=i01.00 name=i01.00 value=""></input><select data-dow=' + dotw[adate.getDay()].toUpperCase().substring(0,3) + ' class=selday onchange="selit(this);" id=sel01.00><option id=opt01.00 value=""></option><option title="All such in month (ie. weekly)" value="...">...</option><option title="And ..." value="&...">&</option><option value=Bold>Bold Title</option><option value=Clone>Clone</option></select></span></td><td class=blurb title="What is on?" id=tb01.00><span title="Event title" style="font-style:bold;color:blue;" id=bd01.00></span><textarea name=ta01.00 id=ta01.00 style="width:100%;height=100%;" ' + ourdata + 'placeholder="Blurb ..." class=tablurb></textarea></td></tr>';
    // ...
    }
    // ...
    }

… for our “second draft” events_in_month.htm Events in Month web application.


Previous relevant Event Calendar Remembered Tutorial is shown below.

Event Calendar Remembered Tutorial

Event Calendar Remembered Tutorial

We were inspired by an Event Calendar pamplette we saw the other day …

… to write a new “proof of concept” Events in Month web application, whose content can be recalled via the web browser’s Bookmark methodologies.

We liked the ideas for day of week and/or date of month grouping arrangements we included, being, the way we interpreted it …

  • just on this day in this month … default
  • on this day of the week throughout the month in question … “
  • on this day and some others in that month in question … “&” … to start with and further amendments available via contenteditable=true span element
  • “Clone” value allows for multiple separated “blurbs” for the one signature day

But, there’s an inherent weakness with the design, we’ll go into more into the future. For now, you can try it yourself below …

Stop Press

This is where we get to for a “second draft” we’ll get into, further, tomorrow …

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in eLearning, Event-Driven Programming, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a comment