Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[build] Add a script that updates social tags for examples #4684

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

michaelfiber
Copy link
Contributor

The bash script added, when run from the src dir, will find all the HTML files in ../examples, figure out the title and image URL from the filename, and update the appropriate tags.

@michaelfiber
Copy link
Contributor Author

Here's an example of a file that has been processed by the script:

<!doctype html>
<html lang="EN-us">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <title>core 2d camera platformer</title>

    <meta name="title" content="core 2d camera platformer">
    <meta name="description" content="raylib [core] example - core 2d camera platformer">
    <meta name="keywords" content="raylib, programming, examples, html5, C, C++, library, learn, games, videogames">
    <meta name="viewport" content="width=device-width">

    <!-- Open Graph metatags for sharing -->
    <meta property="og:type" content="website" />
    <meta property="og:title" content="core 2d camera platformer">
    <meta property="og:image:type" content="image/png">
    <meta property="og:image" content="https://raw.githubusercontent.com/raysan5/raylib/master/examples/core/core_2d_camera_platformer.png">
    <meta property="og:image:alt" content="raylib [core] example - core 2d camera platformer" />
    <meta property="og:site_name" content="raylib - core 2d camera platformer">
    <meta property="og:url" content="https://www.raylib.com/examples/core/core_2d_camera_platformer.html">
    <meta property="og:description" content="raylib [core] example - core 2d camera platformer">

    <!-- Twitter metatags for sharing -->
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:site" content="@raysan5">
    <meta name="twitter:title" content="core 2d camera platformer">
    <meta name="twitter:image" content="https://raw.githubusercontent.com/raysan5/raylib/master/examples/core/core_2d_camera_platformer.png">
    <meta name="twitter:image:alt" content="raylib [core] example - core 2d camera platformer">
    <meta name="twitter:url" content="https://www.raylib.com/examples/core/core_2d_camera_platformer.html">
    <meta name="twitter:description" content="raylib [core] example - core 2d camera platformer">

    <!-- Favicon -->
    <link rel="shortcut icon" href="https://www.raylib.com/favicon.ico">

    <style>
      body { font-family: arial; margin: 0; padding: none; }

      #header {
        width: 100%;
        height: 80px;
        background-color: #888888;
      }

      /* NOTE: raylib logo is embedded in the page as base64 png image */
      #logo {
        width:64px;
        height:64px;
        float:left;
        position:relative;
        margin:10px;
        background-image:url('data:image/png;base64,\
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADs\
MAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjExR/NCNwAAA7JJREFUaEPtk0FyWzEMQ+37X7fZhxX4\
YY3AD1OKF1nkzTRlSBCCLeVBnvl/AUdaELOunPno1kts1kixdtEZKVs+xIxebBkZsVknn/L5nFGDLR8T4zVC9fX19S/+tTFijr\
YK4jUjbPUtqBHpnEE6PkZD7jQZV8n5Recw1XQKciZuPaEtR6UjNs5ENVGMsBVqpPtER0ZMOhpyp8m4YL4OjD9yxsyZxnQycfMJ\
ETNSzsRE1+dihK3YMiJmpHTW3xpmXPC6BXlCHfqnBlsjY5hxf/6EVEOM2BTEi0fYCX4ONSI6Kq3Blg/prIOMq2CsRur4KQ0x64\
SdjOufEDEdHZGOhmz5RDHCVqhRuQ86YsVskbc+GXchLiHnFyYH+UigQDVGnImbT8hwFkgLg2qiM8JO6Ylx1FNLa3DmYwqCTsZd\
4BPqGJG7MwKzpeiWKTKxXkLMVE3MSOmsdwxLH6Rd/wCCLSNDx6djeKfJuArGeoYamRHpaEjnCBYZVy8hZqo2GI36qPjsiOiMsB\
XGcev4Mx9TLGTchbgEjN/uz6jGrBvDjg+LTNx8Qp2CbG2xMKgmOiPslJ4Yxx+eSnSkzlosZNwFPiHl7FRTkLNRJm4+IeVM0ymI\
H42wE/wcKalQI4MRl4EW3p6VcRWMua8F6WjIlqZDxvVPiHQ6CjVbYkV9ohhhp/Rk1wiYgpyJ78i4CsZbjkb8Qx+ihvzu3RPaKo\
gZkY6GlEeMsKdPSOFIC8VoOusg44L5c+T8ouOoGhWbdWJ8tMi4egkxo4hoh2yNTGf3iIyr5Lyic4bRENXo+lvDjAt4C1Hk/OKt\
UaAj0+n4dMSZ2D+hrYJsaYh2SClG2jV9kJKKzhlGQ1SsW299Mq6C8dYZHTExo8fzieI5ivipYnYy7nwJqGKmOYyRwfiUBXITfh\
5qSHRGWEkfqJqURgvsdHyWYv7Ko8DnYYegk3EB00cxprdrJRzFd7YQzawu8L1GMTYS/KpPaAFTkIn1EmJmspJSs5xBzSyGhlkB\
mlxfNFiP5mw4wlbMh4F5Ddxp5jNINBdCEz9zPOC1zD7Q0HBdmXndwv0TMtydEdzlWJT4VZ8Qt9Qn4/onxMIwa5ZYGJU5yufBiC\
jwE50AGjLCVuS8Yt4H7OgZLKK5EKOsLviEWJSL/+0uMi7gLUSBseYwqEbXvSHCec1CJvZPyHCmYQffaBBfOTCGHM2aEbZi1+gO\
1XTWVXMnzrhAn5DSOZVsiQlHnSITKzGj6DeTcZWc/3oy7h9//PF4PL4BlvsWrb6RE+oAAAAASUVORK5CYII=');
      }

      .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
      div.emscripten { text-align: center; }
      div.emscripten_border { border: 1px solid black; }

      /* NOTE: Canvas *must not* have any border or padding, or mouse coords will be wrong */
      canvas.emscripten {
        border: 0px none;
        background: black;
        width: 100%;
      }

      .spinner {
        height: 30px;
        width: 30px;
        margin: 0;
        margin-top: 20px;
        margin-left: 20px;
        display: inline-block;
        vertical-align: top;
        -webkit-animation: rotation .8s linear infinite;
        -moz-animation: rotation .8s linear infinite;
        -o-animation: rotation .8s linear infinite;
        animation: rotation 0.8s linear infinite;
        border-left: 5px solid black;
        border-right: 5px solid black;
        border-bottom: 5px solid black;
        border-top: 5px solid red;
        border-radius: 100%;
        background-color: rgb(245, 245, 245);
      }
      @-webkit-keyframes rotation {
        from {-webkit-transform: rotate(0deg);}
        to {-webkit-transform: rotate(360deg);}
      }
      @-moz-keyframes rotation {
        from {-moz-transform: rotate(0deg);}
        to {-moz-transform: rotate(360deg);}
      }
      @-o-keyframes rotation {
        from {-o-transform: rotate(0deg);}
        to {-o-transform: rotate(360deg);}
      }
      @keyframes rotation {
        from {transform: rotate(0deg);}
        to {transform: rotate(360deg);}
      }

      #status {
        display: inline-block;
        vertical-align: top;
        margin-top: 30px;
        margin-left: 20px;
        font-weight: bold;
        color: rgb(40, 40, 40);
      }

      #progress {
        height: 0px;
        width: 0px;
      }

      #controls {
        display: inline-block;
        float: right;
        vertical-align: top;
        margin-top: 15px;
        margin-right: 20px;
      }

      #output {
        border-left: 0px none;
        border-right: 0px none;
        border-bottom: 0px none;
        padding-left: 0;
        padding-right: 0;
        width: 100%;
        height: 140px;
        margin: 0 auto;
        margin-top: 10px;
        display: block;
        background-color: black;
        color: rgb(37, 174, 38);
        font-family: 'Lucida Console', Monaco, monospace;
        outline: none;
      }

      input[type=button] {
        background-color: lightgray;
        border: 4px solid darkgray;
        color: black;
        text-decoration: none;
        cursor: pointer;
        width: 140px;
        height: 50px;
        margin-left: 10px;
      }

      input[type=button]:hover {
        background-color: #f5f5f5ff;
        border-color: black;
      }
    </style>
  </head>
  <body>
    <div id="header">
        <a id="logo" href="https://www.raylib.com"></a>

        <div class="spinner" id='spinner'></div>
        <div class="emscripten" id="status">Downloading...</div>

        <span id='controls'>
          <span><input type="button" value="📜 SOURCE CODE" onclick="location.href='https://github.com/raysan5/raylib';"/></span>
          <span><input type="button" value="🖵 FULLSCREEN" onclick="Module.requestFullscreen(false, false)"></span>
          <span><input type="button" id="btn-audio" value="🔇 MUTE" onclick="toggleAudio()"></span>
        </span>

        <div class="emscripten">
          <progress value="0" max="100" id="progress" hidden=1></progress>
        </div>
    </div>

    <div class="emscripten_border">
      <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
    </div>

    <textarea id="output" rows="8"></textarea>

    <script type='text/javascript' src="https://cdn.jsdelivr.net/gh/eligrey/FileSaver.js/dist/FileSaver.min.js"> </script>
    <script type='text/javascript'>
        function saveFileFromMEMFSToDisk(memoryFSname, localFSname)     // This can be called by C/C++ code
        {
            var isSafari = false; // Not supported, navigator.userAgent access is being restricted
            //var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
            var data = FS.readFile(memoryFSname);
            var blob;

            if (isSafari) blob = new Blob([data.buffer], { type: "application/octet-stream" });
            else blob = new Blob([data.buffer], { type: "application/octet-binary" });

            // NOTE: SaveAsDialog is a browser setting. For example, in Google Chrome,
            // in Settings/Advanced/Downloads section you have a setting:
            // 'Ask where to save each file before downloading' - which you can set true/false.
            // If you enable this setting it would always ask you and bring the SaveAsDialog
            saveAs(blob, localFSname);
        }
    </script>
    <script type='text/javascript'>
        var statusElement = document.querySelector('#status');
        var progressElement = document.querySelector('#progress');
        var spinnerElement = document.querySelector('#spinner');
        var Module = {
            preRun: [],
            postRun: [],
            print: (function() {
                var element = document.querySelector('#output');

                if (element) element.value = '';    // Clear browser cache

                return function(text) {
                    if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
                    // These replacements are necessary if you render to raw HTML
                    //text = text.replace(/&/g, "&amp;");
                    //text = text.replace(/</g, "&lt;");
                    //text = text.replace(/>/g, "&gt;");
                    //text = text.replace('\n', '<br>', 'g');
                    console.log(text);

                    if (element) {
                        element.value += text + "\n";
                        element.scrollTop = element.scrollHeight; // focus on bottom
                    }
                };
            })(),
            printErr: function(text) {
                if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');

                console.error(text);
            },
            canvas: (function() {
                var canvas = document.querySelector('#canvas');

                // As a default initial behavior, pop up an alert when webgl context is lost.
                // To make your application robust, you may want to override this behavior before shipping!
                // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
                canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);

                return canvas;
            })(),
            setStatus: function(text) {
                if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
                if (text === Module.setStatus.last.text) return;

                var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
                var now = Date.now();

                if (m && now - Module.setStatus.last.time < 30) return; // If this is a progress update, skip it if too soon

                Module.setStatus.last.time = now;
                Module.setStatus.last.text = text;

                if (m) {
                    text = m[1];
                    progressElement.value = parseInt(m[2])*100;
                    progressElement.max = parseInt(m[4])*100;
                    progressElement.hidden = true;
                    spinnerElement.hidden = false;
                } else {
                    progressElement.value = null;
                    progressElement.max = null;
                    progressElement.hidden = true;
                    if (!text) spinnerElement.style.display = 'none';
                }

                statusElement.innerHTML = text;
            },
            totalDependencies: 0,
            monitorRunDependencies: function(left) {
                this.totalDependencies = Math.max(this.totalDependencies, left);
                Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
            },
            //noInitialRun: true
        };

        Module.setStatus('Downloading...');

        window.onerror = function() {
            Module.setStatus('Exception thrown, see JavaScript console');
            spinnerElement.style.display = 'none';
            Module.setStatus = function(text) { if (text) Module.printErr('[post-exception status] ' + text); };
        };
    </script>

    <!-- REF: https://developers.google.com/web/updates/2018/11/web-audio-autoplay -->
    <script type='text/javascript'>
        var audioBtn = document.querySelector('#btn-audio');

        // An array of all contexts to resume on the page
        const audioContexList = [];
        (function() {
            // A proxy object to intercept AudioContexts and
            // add them to the array for tracking and resuming later
            self.AudioContext = new Proxy(self.AudioContext, {
                construct(target, args) {
                    const result = new target(...args);
                    audioContexList.push(result);
                    if (result.state == "suspended") audioBtn.value = "🔈 RESUME";
                    return result;
                }
            });
        })();

        function toggleAudio() {
            var resumed = false;
            audioContexList.forEach(ctx => {
                if (ctx.state == "suspended") { ctx.resume(); resumed = true; }
                else if (ctx.state == "running") ctx.suspend();
            });

            if (resumed) audioBtn.value = "🔇 MUTE";
            else audioBtn.value = "🔈 RESUME";
        }
    </script>
    <script async type="text/javascript" src="core_2d_camera_platformer.js"></script>
  </body>
</html>

@raysan5
Copy link
Owner

raysan5 commented Jan 14, 2025

@michaelfiber thanks for providing the script! I actually had a similar implementation in C that I used for latest release but it's not published.

I prefer to keep this script out of raylib src directory but not sure where it should be placed, maybe .github directory is a good place considering is a build system process... also considering moving it to raylib.com, considering is a process related to web examples building... not sure... opinions?

In any case, an action would be required to build examples on change and update raylib.com repo with changes/additions.

EDIT: Just a reminder that required action should run on Linux server.

@raysan5 raysan5 changed the title Add a script that updates social tags for examples [build] Add a script that updates social tags for examples Jan 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants