<?xml version='1.0' encoding='UTF-8'?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title><![CDATA[CampSoftware 2026 Blog]]></title>
<link>https://campsoftware.com/blog/</link>
<atom:link href="https://campsoftware.com/blog/rss.xml" rel="self" type="application/rss+xml" />
<description><![CDATA[Updates, notes, and releases from CampSoftware.]]></description>
<lastBuildDate>Thu, 15 Jan 2026 00:00:00 +0000</lastBuildDate>
<language>en</language>
<copyright>Copyright (C) CampSoftware.com</copyright>
<item>
<title><![CDATA[Uptime Watcher is Now on the Mac App Store!]]></title>
<description><![CDATA[<h1>Uptime Watcher is Now on the Mac App Store!</h1>
<p>Date: 2026-01-15</p>
<p>Our brand new Mac App, <a href="https://campsoftware.com/products/uptime-watcher.php">Uptime Watcher</a>, has officially launched on the Mac App Store!</p>
<p><a href="https://campsoftware.com/products/uptime-watcher.php"></a></p>
<img src="https://CampSoftware.com/blog/postMedia/UptimeWatcherChecksShort.png" alt="Uptime Watcher Checks" style="width:800px; height:auto;">
<p>
<br><br></p>
<p><a href="https://apps.apple.com/us/app/uptime-watcher/id6757622057?mt=12"></a></p>
<img src="https://CampSoftware.com/blog/postMedia/MacAppStoreDownloadBadge.png" alt="Mac App Store Download Badge" style="width:300px; height:auto;">
<p>
<br><br></p>
<p>I developed Uptime Watcher to monitor our websites and those of our clients, and I couldn't wait to share it.</p>
<p>Uptime Watcher is designed to keep an eye on the availability and performance of websites and services. It offers real-time monitoring, visual logging, and automated email notifications for any downtime or performance issues.</p>
<!--more-->
<p>Details: <a href="https://campsoftware.com/products/uptime-watcher.php">https://campsoftware.com/products/uptime-watcher.php</a></p>
<p><strong>My Journey to Building Uptime Watcher</strong></p>
<p>Just 20 days ago, I had never even heard of Go or Wails. In that short time, I've transformed from a novice into a published app developer! I navigated through learning how these technologies work, writing code, building the app, and handling Mac App Store signing, notarizing, stapling, and even generating an App Store package.</p>
<p>It’s been a while since I created a desktop or mobile app; I transitioned from Xojo to PHP/MySQL development in 2018. While PHP allows for browser-based solutions without needing an app, I've been eager to revisit some utility apps that I'd shelved. Although I considered returning to Xojo, friends with experience in the platform helped steer me towards exploring Go.</p>
<p><strong>Discovering Go and Wails</strong></p>
<p>Inspired by AI suggestions, I decided to give Go a try. My first attempt was a "Hello World" console app that ran effortlessly in the Terminal. Go’s lack of a GUI IDE like Xojo meant I had to rely on a text editor for coding. Thankfully, I used JetBrains PhpStorm, which, while primarily for PHP, accommodates text-based coding quite well. PhpStorm offers panes for file lists, an editor, a terminal, and powerful AI features—all within a single interface. The AI even has a handy "Apply" button for quick edits!</p>
<p>A pivotal moment came when a friend introduced me to Go + Wails—a Go front-end similar to how Electron works for Node.js. I was thrilled to install Wails, which allowed me to create a minimal web browser app that communicated with a PHP server. This experience may lead to my next Mac Store app! :)</p>
<p>Realizing the potential of Go + Wails, I upgraded to JetBrains IntelliJ Ultimate, which supports multiple languages, including PHP and Go. This upgrade enhanced my workflow with better code formatting, hints, typeahead capabilities, and file structure overviews.</p>
<p><strong>The Creation of Uptime Watcher</strong></p>
<p>Uptime Watcher emerged as my second Go + Wails app. Given that I host websites, web apps, and FileMaker servers, I need to know immediately if something goes wrong. The app checks for specific text in responses every minute. If the text isn’t found, it sends a “DOWN” email. If the response is too slow, a “SLOW” email is dispatched. Once the issues are resolved, I’ll send a “RECOVERED” email.</p>
<p>I designed it to avoid flooding users with emails during issues. For example, a change from UP to DOWN triggers one email, and a transition from DOWN back to UP sends another. Emails about SLOW responses are generated only after several consecutive checks to reduce notifications for brief hiccups.</p>
<p><strong>Looking Ahead</strong></p>
<p>I still have a few improvements planned for the next version—my list is growing!</p>
<p>If you need a desktop or web app, I’d love to collaborate!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20260115-uptime-watcher-is-now-on-the-mac-app-store</link>
<guid>https://campsoftware.com/blog/post.php?post=20260115-uptime-watcher-is-now-on-the-mac-app-store</guid>
<pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Apple Music Artist Smart Playlists on Mac]]></title>
<description><![CDATA[<h1>Apple Music Artist Smart Playlists on Mac</h1>
<p>Date: 2025-08-25</p>
<p>I listen to music all day long as a work. The beat keeps me moving like a caller keeping rowers synchronized. </p>
<p>While I pay for Apple Music streaming but also have a big mp3 library which is aging. When I removed the mp3s leaving only streamed tracks, my Playlist lost many of my favorite Artists. That lead me to creating a Smart Playlists of Artists.</p>
<!--more-->
<p><img src="https://CampSoftware.com/blog/postMedia/IMG_1498.png" alt=""></p>
<p>Just like when I write code for Web Apps, I broke the problem down to individual steps...</p>
<p><strong>Step 1:</strong> Copy all the tracks in the playlist to the clipboard. Paste into a text file, open in Excel, copy the Artist column, paste into another text file and save as Artists.txt.</p>
<p><strong>Step 2:</strong> Prepare the file by removing duplicates and sorting in in reverse, z to a. I used BBEdit. The reason for the reverse sort is that when you add items to Smart Playlists by clicking + on the first row, the first item will be at the bottom and the last item will be at the top.</p>
<p><strong>Step 3:</strong> Add the Artists to a Smart Playlist. I used <a href="https://www.keyboardmaestro.com/main/">Keyboard Maestro</a> to read the file and for each row, switch to Music, click the + button, and then paste the Artist name. I added about 500 Artists in about 4 minutes with a .3 sec pause between. </p>
<p>Here's the <a href="https://www.keyboardmaestro.com/main/">Keyboard Maestro</a> Macro.</p>
<p><img src="https://CampSoftware.com/blog/postMedia/IMG_1417.png" alt=""></p>
<p>Next thing I need to think about is manging updates!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20250825-apple-music-artist-smart-playlists-on-mac</link>
<guid>https://campsoftware.com/blog/post.php?post=20250825-apple-music-artist-smart-playlists-on-mac</guid>
<pubDate>Mon, 25 Aug 2025 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[FMDump Migrator Success!]]></title>
<description><![CDATA[<h1>FMDump Migrator Success!</h1>
<p>Date: 2025-08-24</p>
<p>FMDump Migrator was able to generate Xanadu PHP code to import data from FileMaker into existing MySQL tables today! </p>
<p>I've migrated tables manually quite few times and wanted to automate for a while. The automation took a bit, but I think I could now migrate a bunch of tables in a day depending on how much it needed massaging. In Step 2 you can see five massages.</p>
<!--more-->
<p>It's so easy now. FMDump from FileMaker to MySQL Create/Insert statements. Load the Dump files to MySQL. Then Migrator can use the Create statements to generate the SQL for new columns and PHP to set and massage the data.</p>
<p>I recently was helping a client with a migration and they refused to have a migration process since the import was a "one time thing". It resulted in a hot mess. If they would have just planned and created a process they would have been much happier! </p>
<p>Currently, we are migrating a clients FileMaker database we created in 2008. Like many, they were no longer interested in paying to rent FileMaker. Instead, they will now use a Xanadu Web App written in PHP/MySQL/Bootstrap.</p>
<p><strong>Migrate Steps</strong></p>
<p><strong>Step 1</strong>: Paste CREATE TABLE statements from <a href="https://campsoftware.com/products/fmdump.php">FMDump</a> for the Source and from an existing MySQL table for the Target. Then click the Update Columns button.
<img src="https://CampSoftware.com/blog/postMedia/image_7.png" alt=""></p>
<p><strong>Step 2</strong>: Columns with Matching Names will have the Source Column set. Below is the end result for the Contacts table. On the Left, the Source Unused and Used Columns are shown. Beyond the Left, the Target Columns are listed and the assigned Source Column. If the Target Column does not exist, it can be created in ColumnNameAdd. Types are Green if they match, pink if they exist and do not match, and purple if the target does not exist.</p>
<p>Massage Rules is the most power part. If data needs massaging, I can optionally call the PHP function messageValue which handles Append or Select. Appends are for appending data together. Selects help with Popup Menus and their value lists.</p>
<p><img src="https://CampSoftware.com/blog/postMedia/image_8.png" alt=""></p>
<p><strong>Step 3</strong>: Generate Code! Left side is SQL to generate the new columns. Right side is PHP code generated to move and massage the data using the Massage Rules.
<img src="https://CampSoftware.com/blog/postMedia/image_9.png" alt=""></p>
<p><strong>Step 4</strong>: Generate Code Collection makes a list of all SQL and PHP code generated. Currently I copy paste the generated code into the SQL and PHP blocks of a PHP page that runs the code.
<img src="https://CampSoftware.com/blog/postMedia/image_10.png" alt=""></p>
<p>That's it! FMDump the FileMaker data, the FMDump Migrator to set and massage!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20250824-fmdump-migrator-success</link>
<guid>https://campsoftware.com/blog/post.php?post=20250824-fmdump-migrator-success</guid>
<pubDate>Sun, 24 Aug 2025 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[PHP Function: strRandomNoun]]></title>
<description><![CDATA[<h1>PHP Function: strRandomNoun</h1>
<p>Date: 2025-08-20</p>
<p>Yesterday I needed a PHP password generator for the soon to be migrated FileMaker users logins. I found a list of nouns and needed a random picker. A file seemed better than in memory. AI then suggested going to a random byte, then getting the next line which is fast even with large files!</p>
<p>PHP
<img src="https://CampSoftware.com/blog/postMedia/image_6.png" alt=""></p>
<p>PHP as Text</p>
<pre><code>function strRandomNoun() {
    // Open File
    $filePath = PATH_ROOT_XAN . 'data-nouns-2315.txt';
    $fileSize = \filesize( $filePath );
    $file = \fopen( $filePath, 'r' );

    // Loop
    do {

        // Random Noun
        $randomByte = \random_int( 0, $fileSize - 1 );
        \fseek( $file, $randomByte );
        if ( $randomByte !== 0 ) {
            \fgets( $file );
        }
        $randomLine = \fgets( $file );

        $noun = \trim( $randomLine );

    } while ( $noun === '' || $noun === false );

    // Close File
    \fclose( $file );

    return $noun;
}</code></pre>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20250820-php-function-strrandomnoun</link>
<guid>https://campsoftware.com/blog/post.php?post=20250820-php-function-strrandomnoun</guid>
<pubDate>Wed, 20 Aug 2025 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[FMDump Migrator]]></title>
<description><![CDATA[<h1>FMDump Migrator</h1>
<p>Date: 2025-08-17</p>
<iframe width="1024" height="576" src="https://www.youtube.com/embed/1cB48gvau_8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p>FMDump for FileMaker is getting a helper tool called FMDump Migrator! It helps to import a table into an existing table. Paste in the CREATE RECORD statements, match up columns, or add and match.</p>
<p>When ready it will export MySQL to create columns and PHP to loop and "set fields". Here's a quick demo!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20250817-fmdump-migrator</link>
<guid>https://campsoftware.com/blog/post.php?post=20250817-fmdump-migrator</guid>
<pubDate>Sun, 17 Aug 2025 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Xanadu: How a Contact Record is Loaded]]></title>
<description><![CDATA[<h1>Xanadu: How a Contact Record is Loaded</h1>
<p>Date: 2025-08-17</p>
<iframe width="1024" height="576" src="https://www.youtube.com/embed/Ild8vED0ZW8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p>Today on a <a href="https://www.reddit.com/r/filemaker/comments/1ms1pse/does_claris_leverage_vendor_lockin_against_its/?sort=old">FileMaker Reddit Post</a>, I was asked what the code looks like. I start with a selected Contact record and starting with index.php, I show an over view of the code path and the code for the Contacts Info Card.</p>
<!--more-->
<p>It's interesting to hear similar stories from fellow Developers like on that Reddit Post. FileMaker and Xojo have both, but separately, raised pricing. No official announcements have been made from either company, but Devs feel like the companies are going after enterprise customers bags of cash and not so interested in small workgroups. </p>
<p>It's so bad that some Clients now want to move away from FileMaker over the pricing. FileMaker is the only platform that I know of that DEMANDS my Clients to pay them FOREVER just to keep using what I create.</p>
<p>Xojo pricing is nice as the Dev pays and can freely distributable create apps. Xojo's issue though is that they keep archiving bugs instead of fixing them. Xojo recently hiked their prices so high that Citizen Devs stated they are done. This is after Xojo replaced Web 1.0 with Web 2.0 without an upgrade path. I was expected to convert the code myself. Why not convert to something else?</p>
<p>The greed is enormous and Devs are over it. These closed-source companies like FileMaker and Xojo want more and more money. Meanwhile while Devs and Clients are not getting the value these companies think they provide.</p>
<p>It's taken a while, but I've been happy moving to PHP/MySQL/Bootstrap. The thing is with open-source platforms, the gatekeepers vanish. I don't need to worry about raising prices, changing directions, or killing off products anymore.</p>
<p>The thing I like most about Xanadu is having control again. I refuse to be held captive again.</p>
<p>Get in touch if you have questions!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20250817-xanadu-how-a-contact-record-is-loaded</link>
<guid>https://campsoftware.com/blog/post.php?post=20250817-xanadu-how-a-contact-record-is-loaded</guid>
<pubDate>Sun, 17 Aug 2025 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Convert Reports in FileMaker to Xanadu Web App]]></title>
<description><![CDATA[<h1>Convert Reports in FileMaker to Xanadu Web App</h1>
<p>Date: 2025-07-28</p>
<p>We've been converting FileMaker databases to <a href="https://campsoftware.com/products/xanadu.php">Xanadu</a> Web Apps over the past few years. One conversion category is Reports. Reports vary, but there are four types: </p>
<ul>
<li>'Simple' with zero or minimal parameters with a simple query.</li>
<li>'Flexible' reporting on ad hoc record sets with a simple query.</li>
<li>'Forms' with data placed at specific coordinates.</li>
<li>'Customized' with complex formatting, more than one format, or multiple queries.</li>
</ul>
<p>For most reports, we use <a href="https://github.com/mpdf/mpdf">mPDF</a>, a popular open-source PHP library for generating PDFs. We'll create a header, footer, and body in HTML using CSS for the styling and applying the CSS <code>page-break-inside: avoid;</code> to content that should overflow to the next page if it won't fit on the current page. Once the HTML is passed, mPDF generates the PDF and the content flows from page to page. That works great for the Simple and Flexible reports that use the full width of the page.</p>
<!--more-->
<p>Reports that are Forms or Customized present a challenge in fitting content to fixed spaces. A Form field must fit in the space provided. Directories of information in columns must flow from one column to another and across pages. </p>
<p>In FileMaker, we have a script to create a Customized Report for a Member Directory. In addition to the column and page flow, the Directory also has an index by Name, an index by Category, and of course page numbers. That's complex formatting!</p>
<p>The FileMaker script gathers each Members Info Into a Card that should appear and sorted by Category and Member Name. In a loop we check if the first Member Card fits on the Page and note the Page Number. That repeats until a Member will not fit on the Page. At this point, we'll know which Member Cards fit on Page 1.  The remaining Members are processed ending with a list of Member Cards and the Page Number they are on. That's just the Members Info pages. We still need to create the indexes and add page numbers. We create the Category index starting with Page 1. The Member Name index resumes the Page Numbering using the last page number from the Category index. The we recreate the Member Info using the last page number from the Member Name index. Whew!</p>
<p>I looked into how to do this in PHP, but I learned there's no way to calculate how tall a fixed width div since the HTML is never rendered. It turns out that mPDF has the ability to measure div height. Here's some mPDF code:</p>
<pre><code class="language-PHP">$beginY = $mpdf-&gt;y; // Store the starting Y position
$mpdf-&gt;WriteHTML( $combinedCardsHTML ); // Render the combined cards
$endY = $mpdf-&gt;y; // Measure where the content ended
$batchHeight = $endY - $beginY; // Calculate the height of the batch</code></pre>
<p>The PHP to replicate how to determine which Members Info Cards appear on a Page worked out great. In the pic below you can see the red line below the simulated Member Cards that fit into a column. Looking at the red line across the columns you can see where the fit vs not fit break generally is. </p>
<p><strong>Column of Cards</strong> The red line marks what will fit vs not fit.
<img src="https://CampSoftware.com/blog/postMedia/PageCards_0.jpg" alt=""></p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20250728-convert-reports-in-filemaker-to-xanadu-web-app</link>
<guid>https://campsoftware.com/blog/post.php?post=20250728-convert-reports-in-filemaker-to-xanadu-web-app</guid>
<pubDate>Mon, 28 Jul 2025 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Ollama Local AI]]></title>
<description><![CDATA[<h1>Ollama Local AI</h1>
<p>Date: 2024-04-04</p>
<p>Today I read that Opera Development Browser added local AI. I ended up deleting it after trying it since it was in a window within the browser. Afterwards, I tried LocalAI.app and a few others and found Ollama!</p>
<p>Local AI's are interesting in that if the internet is down, you still have access to it. In our <a href="https://campsoftware.com/products/xanadu.php">Xanadu Web Apps</a>, we use local libraries as much as possible so it's less likely for a resource to disappear. Nothing is worse than going to run your app and not be able to if a Google Font cannot be reached. There are some things that cannot be local like payment processing, address correction, etc.</p>
<!--more-->
<p>Ollama is interesting. <a href="https://ollama.com/download">Downloads are available</a> for Mac, Windows or Linux. I downloaded for Mac and as suggested on the <a href="https://github.com/ollama/ollama">github page</a>, I typed the following in a new Terminal window:</p>
<pre><code class="language-bash">ollama run llama2</code></pre>
<p>The command downloaded the llama2 model and I was able to chat with it! Pretty simple. They have a a <a href="https://ollama.com/library">large number of models available</a> but I was interesting in the coding models. I found they had codellama, so I tried:</p>
<pre><code class="language-bash">ollama run codellama</code></pre>
<p>There's actually <a href="https://ollama.com/library/codellama">many different versions of codellama</a>: 7b-instruct, 13b-instruct, 34b-instruct, 70b-instruct. That represents the number of parameters in billions and instuct is a conversational model. I tried 70b in another app and it was super slow. So I ended up actually installing 13b:</p>
<pre><code class="language-bash">ollama run codellama:7b-instruct</code></pre>
<p>At this point you can ask questions at the prompt like: "write a php function that outputs the fibonacci sequence".</p>
<p>Later I found you can ask questions in a few different ways via the command or via curl:</p>
<pre><code class="language-bash">ollama run codellama:13b-instruct "write a php function that outputs the fibonacci sequence"

curl http://localhost:11434/api/generate -d '{ "model": "codellama:13b-instruct", "prompt": "write a php function that outputs the fibonacci sequence", "stream": false }'

curl http://localhost:11434/api/chat -d '{ "model": "codellama:13b-instruct", "messages": [ { "role": "user", "content": "write a php function that outputs the fibonacci sequence" } ] }'</code></pre>
<p>Last thing... Checkout the commands that make it easy to list and delete models:</p>
<p><img src="https://CampSoftware.com/blog/postMedia/image_0.png" alt=""></p>
<p>I have a feeling that Ollama will be useful to add AI to our <a href="https://campsoftware.com/products/xanadu.php">Xanadu Web Apps</a>!!!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20240404-ollama-local-ai</link>
<guid>https://campsoftware.com/blog/post.php?post=20240404-ollama-local-ai</guid>
<pubDate>Thu, 04 Apr 2024 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Named Colors CSS Files]]></title>
<description><![CDATA[<h1>Named Colors CSS Files</h1>
<p>Date: 2024-03-30</p>
<p>Bootstrap rocks but sometimes you need more colors. Named Colors are a great solution. </p>
<p>Included are css files that can be included for Bootstrap like colors: </p>
<ul>
<li>color-bg-AliceBlue</li>
<li>color-border-AliceBlue</li>
<li>color-text-AliceBlue</li>
<li>color-text-bg-AliceBlue</li>
</ul>
<p>List of Named Colors with Color Swatches: <a href="https://www.w3.org/wiki/CSS/Properties/color/keywords">https://www.w3.org/wiki/CSS/Properties/color/keywords</a></p>
<!--more-->
<p>color-bg.css:</p>
<pre><code>.color-bg-AliceBlue { background-color: #F0F8FF; }
.color-bg-AntiqueWhite { background-color: #FAEBD7; }
.color-bg-Aqua { background-color: #00FFFF; }
.color-bg-Aquamarine { background-color: #7FFFD4; }
.color-bg-Azure { background-color: #F0FFFF; }
.color-bg-Beige { background-color: #F5F5DC; }
.color-bg-Bisque { background-color: #FFE4C4; }
.color-bg-Black { background-color: #000000; }</code></pre>
<p>color-border.css</p>
<pre><code>.color-border-AliceBlue { border-color: #F0F8FF; }
.color-border-AntiqueWhite { border-color: #FAEBD7; }
.color-border-Aqua { border-color: #00FFFF; }
.color-border-Aquamarine { border-color: #7FFFD4; }
.color-border-Azure { border-color: #F0FFFF; }
.color-border-Beige { border-color: #F5F5DC; }
.color-border-Bisque { border-color: #FFE4C4; }
.color-border-Black { border-color: #000000; }</code></pre>
<p>color-text.css</p>
<pre><code>.color-text-AliceBlue { color: #F0F8FF; }
.color-text-AntiqueWhite { color: #FAEBD7; }
.color-text-Aqua { color: #00FFFF; }
.color-text-Aquamarine { color: #7FFFD4; }
.color-text-Azure { color: #F0FFFF; }
.color-text-Beige { color: #F5F5DC; }
.color-text-Bisque { color: #FFE4C4; }
.color-text-Black { color: #000000; }</code></pre>
<p>color-text-bg.css</p>
<pre><code>.color-text-bg-AliceBlue { background-color: #F0F8FF; color: #000000; }
.color-text-bg-AntiqueWhite { background-color: #FAEBD7; color: #000000; }
.color-text-bg-Aqua { background-color: #00FFFF; color: #FFFFFF; }
.color-text-bg-Aquamarine { background-color: #7FFFD4; color: #000000; }
.color-text-bg-Azure { background-color: #F0FFFF; color: #000000; }
.color-text-bg-Beige { background-color: #F5F5DC; color: #000000; }
.color-text-bg-Bisque { background-color: #FFE4C4; color: #000000; }
.color-text-bg-Black { background-color: #000000; color: #FFFFFF; }</code></pre>
<p>On Github: <a href="https://github.com/campsoftware/namedColors-css">https://github.com/campsoftware/namedColors-css</a></p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20240330-named-colors-css-files</link>
<guid>https://campsoftware.com/blog/post.php?post=20240330-named-colors-css-files</guid>
<pubDate>Sat, 30 Mar 2024 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[PHP for Xojo Programmers - 0003 - URL Variables]]></title>
<description><![CDATA[<h1>PHP for Xojo Programmers - 0003 - URL Variables</h1>
<p>Date: 2021-05-10</p>
<p>Reposted from INN: <a href="https://ifnotnil.com/t/php-for-xojo-programmers-0003-variables-from-the-url/1733">https://ifnotnil.com/t/php-for-xojo-programmers-0003-variables-from-the-url/1733 </a></p>
<h5>URL Variables</h5>
<p>This time we'll work to add variables to the URL like "/products.php?item=widget" rather than create one page for every product. We could have 500 products and only need one PHP page and one HTML Template.</p>
<!--more-->
<p>When we talked about Includes we had three files "template.htm", "helloWorld.php", and "helloEarth.php". That worked well but now let's now use variables to replace the need for multiple PHP pages. </p>
<h5>Prep</h5>
<p>Start off by duplicating "helloEarth.php" and name the new file "products.php" so we have two files "template.htm" and "products.php":</p>
<p><strong>template.htm</strong></p>
<pre><code>&lt;html&gt;&lt;body&gt;
&lt;button type="button" onclick="&lt;?= $buttonJS ?&gt;"&gt;&lt;?= $buttonLabel ?&gt;&lt;/button&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>
<p><strong>products.php</strong></p>
<pre><code>&lt;?php
$buttonLabel = 'Hello';
$buttonJS = "alert('Hello Earth!');";
require_once( 'template.htm' );
?&gt;</code></pre>
<h4>PHP URL Variables</h4>
<p>There are two ways to pass variables to PHP. The first is passing name / value pairs in the URL like "/products.php?item=Widget&amp;price=10.50". The are called PHP GET parameters. To access the code and price, we can use <code>$_GET['item']</code> and <code>$_GET['price']</code>.</p>
<p>As you can imagine, this might not be a good idea as someone could just edit the URL and change the price! In a real world system you'd just pass the code and get the price from a database or another source.</p>
<p>Let's update the PHP.</p>
<p><strong>products.php</strong></p>
<pre><code>&lt;?php
$buttonLabel = 'Buy a ' . $_GET['item'] . ' now!';
$buttonJS = "alert( 'A " . $_GET['item'] ." costs $" . $_GET['price'] . ".' );";
require_once( 'template.htm' );
?&gt;</code></pre>
<h4>Test</h4>
<p>Now upload your files and test it! Or you can try loading this URL: <a href="https://campsoftware.com/labs/inn/0003/products.php?item=Widget&amp;price=10.50">https://campsoftware.com/labs/inn/0003/products.php?item=Widget&amp;price=10.50</a></p>
<p>Once you load the page, feel free to change the URL. Change the item and/or the price, remove both, or add another parameter.</p>
<h4>Trust but Verify</h4>
<p>While the code above will work, You should <strong>never</strong> just use the values passed because someone could try in inject code. That's a big topic. </p>
<p>We're running the code below at link above. It's just uses the PHP function htmlspecialchars to encode the these characters to for the PHP GET parameters.</p>
<pre><code>&amp; (ampersand) becomes &amp;
" (double quote) becomes "
' (single quote) becomes '
&lt; (less than) becomes &lt;
&gt; (greater than) becomes &gt;</code></pre>
<h4>Code with Encoded Parameters.</h4>
<pre><code>&lt;?php
$item = htmlspecialchars( $_GET['item'], ENT_QUOTES, 'UTF-8', false );
$price = htmlspecialchars( $_GET['price'], ENT_QUOTES, 'UTF-8', false );
$buttonLabel = 'Buy a ' . $item . ' now!';
$buttonJS = "alert( 'A " . $item ." costs $" . $price . ".' );";
require_once( 'template.htm' );
?&gt;</code></pre>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20210510-php-for-xojo-programmers-0003-url-variables</link>
<guid>https://campsoftware.com/blog/post.php?post=20210510-php-for-xojo-programmers-0003-url-variables</guid>
<pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[PHP for Xojo Programmers - 0002 - Includes]]></title>
<description><![CDATA[<h1>PHP for Xojo Programmers - 0002 - Includes</h1>
<p>Date: 2021-05-08</p>
<p>Reposted from INN: <a href="https://ifnotnil.com/t/php-for-xojo-programmers-0002-assets/1727">https://ifnotnil.com/t/php-for-xojo-programmers-0002-assets/1727</a></p>
<h5>Includes</h5>
<p>This time we'll talk about "includes". Includes are a way to pull in a file into the current file to reduce repetitive code or templates. Below we'll show how to make a simple template and use it.</p>
<!--more-->
<p>We use Includes in Xanadu which is open source: <a href="https://github.com/campsoftware/xanadu">https://github.com/campsoftware/xanadu</a></p>
<p>Here's Xanadu's template for almost every page. It'll look overwhelming, but broken down it's a simple template as well. <a href="https://github.com/campsoftware/xanadu/blob/master/templates/page-resp.php">https://github.com/campsoftware/xanadu/blob/master/templates/page-resp.php</a></p>
<h5>Previously...</h5>
<p>Last time we created a Hello World in an file called index.php.</p>
<pre><code>&lt;?php
$buttonLabel = 'Hello';
$buttonJS = "alert('Hello World!');";
?&gt;
&lt;html&gt;&lt;body&gt;
&lt;button type="button" onclick="&lt;?= $buttonJS ?&gt;"&gt;&lt;?= $buttonLabel ?&gt;&lt;/button&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>
<h5>Split File Into HTML File and a PHP File</h5>
<p>Let's assume that our HTML rocks and it does everything we want, for now. :slight_smile: Let's split index.php into two files, template.htm and helloWorld.php.</p>
<p>template.htm:</p>
<pre><code>&lt;html&gt;&lt;body&gt;
&lt;button type="button" onclick="&lt;?= $buttonJS ?&gt;"&gt;&lt;?= $buttonLabel ?&gt;&lt;/button&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>
<p>helloWorld.php</p>
<pre><code>&lt;?php
$buttonLabel = 'Hello';
$buttonJS = "alert('Hello World!');";
?&gt;</code></pre>
<p>Now we have two files but they are not working as a team yet. You can try loading them to see how they behave separated.</p>
<h5>Include HTML File into helloWorld.php</h5>
<p>Our two files won't work well until we Include the HTML into the PHP file.</p>
<p>helloWorld.php</p>
<pre><code>&lt;?php
$buttonLabel = 'Hello';
$buttonJS = "alert('Hello World!');";
require_once( 'template.htm' );
?&gt;</code></pre>
<p>Now our split HTML and PHP files are joined again using an Include from PHP. There are a few forms of Includes: include(), include_one(), require(), and require_once(). "Require" means the file must be there or it will produce an error. "Once" means only include it the first time, not every time it's asked for.</p>
<h5>Reuse HTML Template for helloEarth.php</h5>
<p>Duplicate the helloWorld.php file and name the duplicate file to helloEarth.php. Then change the value of $buttonJS from "Hello World" to "Hello Earth".</p>
<p>helloEarth.php</p>
<pre><code>&lt;?php
$buttonLabel = 'Hello';
$buttonJS = "alert('Hello Earth!');";
require_once( 'template.htm' );
?&gt;</code></pre>
<p>Now when you load:</p>
<ul>
<li>helloWorld.php you'll see a Hello button that says Hello World</li>
<li>helloEarth.php you'll see a Hello button that says Hello Earth</li>
</ul>
<p>You just created two PHP pages that both share a template.</p>
<h5>Lastly...</h5>
<p>Pretty easy, huh? Now think about a Web Store to show Products. All of the pages have the same format for the Title, Description, Photo, Comments, and such. This works because the same template but with different variable values.</p>
<p>I should say for a Products page you wouldn't want to create one PHP page for each product. Instead you might have products.php and products.htm. To see a Widget, you could load something like "/products.php?id=widget" or "/products/widget". I think that'll be our next topic.!</p>
<p>Next time you're using Xojo think about how you might do it in PHP. Keep in mind when I say PHP, I really mean using PHP to write HTML, CSS, and Javascript...</p>
<p><strong>Ask Questions</strong></p>
<p>Ask questions below. Some questions might end up being answered in another post. :) </p>
<p><strong>More</strong></p>
<p>CampSoftware Blog: <a href="https://campsoftware.com/blog/">https://campsoftware.com/blog/</a></p>
<p>Xanadu Info: <a href="https://campsoftware.com/products/xanadu.php">https://campsoftware.com/products/xanadu.php</a></p>
<p>Xanadu Download: <a href="https://github.com/campsoftware/xanadu">https://github.com/campsoftware/xanadu</a></p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20210508-php-for-xojo-programmers-0002-includes</link>
<guid>https://campsoftware.com/blog/post.php?post=20210508-php-for-xojo-programmers-0002-includes</guid>
<pubDate>Sat, 08 May 2021 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[PHP for Xojo Programmers - 0001 - Hello World]]></title>
<description><![CDATA[<h1>PHP for Xojo Programmers - 0001 - Hello World</h1>
<p>Date: 2021-05-07</p>
<p>Reposted from INN: <a href="https://ifnotnil.com/t/php-for-xojo-programmers-0001-hello-world/1726">https://ifnotnil.com/t/php-for-xojo-programmers-0001-hello-world/1726</a></p>
<p>This is the first post in a series about PHP from a Xojo perspective.</p>
<p>I'll be showing how I developed different parts in detail of Xanadu which is open source: <a href="https://github.com/campsoftware/xanadu">https://github.com/campsoftware/xanadu</a></p>
<!--more-->
<h5>Hello World!</h5>
<p>When I first used Xojo I did the same thing you probably did. I dragged out a button and when clicked it showed a message box that just said 'Hello World'.</p>
<p>Think about what Xojo might be doing to make this work. </p>
<ul>
<li>Define the page, even if blank</li>
<li>Define and place a button</li>
<li>Add a button onclick event that shows a dialog 'Hello World'.</li>
</ul>
<h5>Hello World - Without PHP</h5>
<p>Interestingly, you don't need PHP for this but you do need HTML, CSS, and Javascript. However, adding PHP brings in the power of programming. In my head, when I use Xojo I'm dragging buttons and adding code to write the HTML, CSS, and Javascript for me. PHP is what I now use to write HTML, CSS, and Javascript. Xojo tries hard to not expose the details but you can see a bit when folks share Javascript 'work arounds'. The key with PHP is to understand the details then write PHP code to mange the details.</p>
<p><strong>Define the page</strong></p>
<pre><code>&lt;html&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;</code></pre>
<p>That's it. We now have a blank page. The html and body tags are all you need at first. The files should be saved with an html extension like index.html or index.htm.</p>
<p><strong>Define and place a button</strong></p>
<pre><code>&lt;html&gt;&lt;body&gt;
&lt;button type="button"&gt;Hello&lt;/button&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>
<p>Buttons are just another tag, but have attributes or properties. Note [type="button"] and a button label [Hello]</p>
<p><strong>Add a button onclick event that shows a dialog 'Hello World'</strong></p>
<pre><code>&lt;html&gt;&lt;body&gt;
&lt;button type="button" onclick="alert('Hello World!');"&gt;Hello&lt;/button&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>
<p>Another Button attribute is onclick which runs the Javascript [alert('Hello World!');] The alert function shows a dialog with the text [Hello World!]</p>
<h5>Hello World - With PHP</h5>
<p>Now we can add some code to manipulate the HTML and Javascript.</p>
<p><strong>PHP Tags</strong></p>
<pre><code>&lt;?php
// This is a comment
?&gt;
&lt;html&gt;&lt;body&gt;
&lt;button type="button" onclick="alert('Hello World!');"&gt;Hello&lt;/button&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>
<p>Now we have a PHP tag with a comment, but it doesn't do anything yet. The file extension should be changed to php like index.php.</p>
<p><strong>Add a Variable</strong></p>
<pre><code>&lt;?php  
$message = 'Hello World';
?&gt;
&lt;html&gt;&lt;body&gt;
&lt;button type="button" onclick="alert('&lt;?= $message ?&gt;');"&gt;Hello&lt;/button&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>
<p>Not so bad right? First we define a variable at the top of the page wrapped in a PHP tag. Then in the Javascript alert function we add a special PHP tag that is a shortcut to echo the variable value. another way would be to use the longer PHP tag.</p>
<pre><code>&lt;?php echo $message; ?&gt;</code></pre>
<p><strong>Add a Variable with Javascript</strong></p>
<pre><code>&lt;?php
$buttonLabel = 'Hello';
$buttonJS = "alert('Hello World!');";
?&gt;
&lt;html&gt;&lt;body&gt;
&lt;button type="button" onclick="&lt;?= $buttonJS ?&gt;"&gt;&lt;?= $buttonLabel ?&gt;&lt;/button&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>
<p>Here we create and use a variable for the button label and the button onclick javascript. Notice that we used single quotes for the first variable and double quotes for the second variable. Quoting can get tricky with PHP quotes, HTML quotes and Javascript Quotes.</p>
<h5>Lastly...</h5>
<p>Next time you're using Xojo think about how you might do it in PHP. Keep in mind when I say PHP, I really mean using PHP to write HTML, CSS, and Javascript...</p>
<p><strong>Ask Questions</strong></p>
<p>Ask questions below. Some questions might end up being answered in another post. :) </p>
<p><strong>More</strong></p>
<p>CampSoftware Blog: <a href="https://campsoftware.com/blog/">https://campsoftware.com/blog/</a></p>
<p>Xanadu Info: <a href="https://campsoftware.com/products/xanadu.php">https://campsoftware.com/products/xanadu.php</a></p>
<p>Xanadu Download: <a href="https://github.com/campsoftware/xanadu">https://github.com/campsoftware/xanadu</a></p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20210507-php-for-xojo-programmers-0001-hello-world</link>
<guid>https://campsoftware.com/blog/post.php?post=20210507-php-for-xojo-programmers-0001-hello-world</guid>
<pubDate>Fri, 07 May 2021 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Code - Web Fav Icons]]></title>
<description><![CDATA[<h1>Code - Web Fav Icons</h1>
<p>Date: 2021-04-28</p>
<p><br>First create an image file for your icon that's large. It should be square and at least 512px by 512px. Going larger is good like 1024 by 1024 so you can regenerate the icons in the future as devices demand larger images.<br><br>There are many tools that can create icons for you where you just upload your image and get back some html and several image files in different formats.<br><br>One free service is <a href="https://iconifier.net" target="_blank"><a href="https://iconifier.net">https://iconifier.net</a></a>, shown below.<br><br><img class="imageStyle" alt="Iconifier" src="https://campsoftware.com/blog/postMedia/iconifier.png" width="452" height="515"><br><br>Along with the icons, you'll also receive the HTML to be placed between the HEAD tags. The HTML looks like this:<br><br><img class="imageStyle" alt="IconifierHTML" src="https://campsoftware.com/blog/postMedia/iconifierhtml.png" width="840" height="256"><br><br>Super easy!!!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20210428-code-web-fav-icons</link>
<guid>https://campsoftware.com/blog/post.php?post=20210428-code-web-fav-icons</guid>
<pubDate>Wed, 28 Apr 2021 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[HTML Fav Icons]]></title>
<description><![CDATA[<h1>HTML Fav Icons</h1>
<p>Date: 2021-04-28</p>
<p>Fav Icons make identifying Web Apps clear in Browsers, Bookmarks, and saving to the Home screen. Generating Icons is easy!</p>
<p>First create an image file for your icon that's large. It should be square and at least 512px by 512px. Going larger is good like 1024 by 1024 so you can regenerate the icons in the future as devices demand larger images.</p>
<!--more-->
<p>There are many tools that can create icons for you where you just upload your image and get back some html and several image files in different formats.</p>
<p>One free service is <a href="https://iconifier.net">https://iconifier.net</a>, shown below.</p>
<p><img src="https://CampSoftware.com/blog/postMedia/iconifier.png.jpeg" alt=""></p>
<p>Along with the icons, you'll also receive the HTML to be placed between the HEAD tags. The HTML looks like this:</p>
<p><img src="https://CampSoftware.com/blog/postMedia/iconifierhtml.png.jpeg" alt=""></p>
<p>Super easy!!!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20210428-html-fav-icons</link>
<guid>https://campsoftware.com/blog/post.php?post=20210428-html-fav-icons</guid>
<pubDate>Wed, 28 Apr 2021 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Xanadu PHP Web App on GitHub]]></title>
<description><![CDATA[<h1>Xanadu PHP Web App on GitHub</h1>
<p>Date: 2021-04-27</p>
<p><strong>NOTE</strong>: Since posting this, we've made Xanadu semi-private.</p>
<p>Xanadu is reincarnated as a Web App developed in PHP / HTML / CSS / Bootstrap / Javascript. If you haven't heard of Xanadu it's a framework for developing Database Driven Web Apps.</p>
<!--more-->
<p>It's been a long road, but Xanadu is a good place now. If you haven't heard about the <a href="https://campsoftware.com/blog/?id=software-development-disappointed-to-ecstatic">speed bumps we hit with FileMaker and Xojo, we posted a bit about it last year</a>. Not much has changed with FileMaker and Xojo since. FileMaker is still very expensive for Small Businesses as they focus on Enterprise. Xojo released Web 2.0 over the past year in pre-alpha quality. While Xojo has been fixing bugs it still has many show-stopping issues. One of the features I was most interested in, Responsive Containers, isn't included. Xojo said their bug tracker 'Feedback' would be available as a Web 2.0 app but hasn't been released. It's a shame. Xojo had a lot of promise, but quality is in the crapper and is slow to fix bugs. If you care to learn more, check out the Xojo forums at <a href="https://forum.xojo.com">https://forum.xojo.com</a>.</p>
<p>Moving Xanadu to PHP turned out to be an excellent decision. We have yet to run into a bug in PHP which is amazing after dropping Xojo. Web development hasn't changed much at its core. Essentially we write code that generates HTML / CSS / Bootstrap / Javascript etc. We're just now using PHP to generate that code.</p>
<p>We've decided to make Xanadu OPEN SOURCE on GitHub because we want to help others. If there's enough interest, we hope to build a community of developers. However, if you need some help or want someone else to develop your Web App, we'd love to help!</p>
<p>Xanadu Info can be found here: <a href="https://campsoftware.com/products/xanadu.php">https://campsoftware.com/products/xanadu.php</a> We add more info weekly and are working to use this page as our reference too!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20210427-xanadu-php-web-app-on-github</link>
<guid>https://campsoftware.com/blog/post.php?post=20210427-xanadu-php-web-app-on-github</guid>
<pubDate>Tue, 27 Apr 2021 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Xanadu Web App on GitHub]]></title>
<description><![CDATA[<h1>Xanadu Web App on GitHub</h1>
<p>Date: 2021-04-27</p>
<p><br>It's been a long road, but Xanadu is a good place now. If you haven't heard about the speed bumps we hit with FileMaker and Xojo, we <a href="https://campsoftware.com/blog/index_files/Software-Development-Disappointed-to-Ecstatic.php" target="_blank">posted a bit about it last year</a>. Not much has changed with FileMaker and Xojo since. FileMaker is still very expensive for Small Businesses as they focus on Enterprise. Xojo released Web 2.0 over the past year in pre-alpha quality. While Xojo has been fixing bugs it still has many show-stopping issues. One of the features I was most interested in, Responsive Containers, isn't included. Xojo said their bug tracker 'Feedback' would be available as a Web 2.0 app but hasn't been released. It's a shame. Xojo had a lot of promise, but quality is in the crapper and is slow to fix bugs. If you care to learn more, check out the Xojo forums at <a href="https://forum.xojo.com" target="_blank"><a href="https://forum.xojo.com">https://forum.xojo.com</a></a>.<br><br></p>
<!--more-->
<p>Moving Xanadu to PHP turned out to be an excellent decision. We have yet to run into a bug in PHP which is amazing after dropping Xojo. Web development hasn't changed much at its core. Essentially we write code that generates HTML / CSS / Bootstrap / Javascript etc. We're just now using PHP to generate that code.<br><br>We've decided to make Xanadu OPEN SOURCE on GitHub because we want to help others. If there's enough interest, we hope to build a community of developers. However, if you need some help or want someone else to develop your Web App, we'd love to help!<br><br>Xanadu Info can be found here: <a href="https://campsoftware.com/products/xanadu.php" target="_blank"><a href="https://campsoftware.com/products/xanadu.php">https://campsoftware.com/products/xanadu.php</a></a> We add more info weekly and are working to use this page as our reference too!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20210427-xanadu-web-app-on-github</link>
<guid>https://campsoftware.com/blog/post.php?post=20210427-xanadu-web-app-on-github</guid>
<pubDate>Tue, 27 Apr 2021 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Code - locationGet js]]></title>
<description><![CDATA[<h1>Code - locationGet js</h1>
<p>Date: 2020-08-13</p>
<p><br>- Uses an inline callback.<br>- Normalizes the output.<br>- Makes the async call feel like a sync call, but it's still async.<br>- This is a good template for similar async calls.<br><br>On Github: <a href="https://github.com/campsoftware/locationGet-js" target="_blank"><a href="https://github.com/campsoftware/locationGet-js">https://github.com/campsoftware/locationGet-js</a></a><br><br><br><img class="imageStyle" alt="Code - locationGet js" src="https://campsoftware.com/blog/postMedia/code---locationget-js.png" width="1746" height="1898"></p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20200813-code-locationget-js</link>
<guid>https://campsoftware.com/blog/post.php?post=20200813-code-locationget-js</guid>
<pubDate>Thu, 13 Aug 2020 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Code - semaphoreCheck php]]></title>
<description><![CDATA[<h1>Code - semaphoreCheck php</h1>
<p>Date: 2020-08-13</p>
<p><br>- Aborts if the semaphore cannot be retrieved.<br>- Aborts if the semaphore in use.<br>- The key must be an integer.<br><br>On Github: <a href="https://github.com/campsoftware/semaphoreCheck-php" target="_blank"><a href="https://github.com/campsoftware/semaphoreCheck-php">https://github.com/campsoftware/semaphoreCheck-php</a></a><br><br><img class="imageStyle" alt="Code - semaphoreCheck php" src="https://campsoftware.com/blog/postMedia/code---semaphorecheck-php.png" width="1370" height="788"></p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20200813-code-semaphorecheck-php</link>
<guid>https://campsoftware.com/blog/post.php?post=20200813-code-semaphorecheck-php</guid>
<pubDate>Thu, 13 Aug 2020 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[JS locationGet]]></title>
<description><![CDATA[<h1>JS locationGet</h1>
<p>Date: 2020-08-13</p>
<p>Retrieves the GPS coordinates of the Browser.</p>
<ul>
<li>Uses an inline callback.</li>
<li>Normalizes the output.</li>
<li>Makes the async call feel like a sync call, but it's still async.</li>
<li>This is a good template for similar async calls.</li>
</ul>
<p>On Github: <a href="https://github.com/campsoftware/locationGet-js">https://github.com/campsoftware/locationGet-js</a></p>
<!--more-->
<p><strong>Calling Example</strong></p>
<pre><code>xanLocationGet( function ( coords ) { alert( coords[`ErrorCode`] + `, ` + coords[`ErrorDesc`] + `, ` + coords[`Latitude`] + `, ` + coords[`Longitude`] + `, ` + coords[`Altitude`] );</code></pre>
<p><strong>Function</strong></p>
<pre><code>function xanLocationGet( pCallbackFunction ) {
    // Calling Example
    // locationGet( function ( coords ) { alert( coords['ErrorCode'] + '|' + coords['Latitude'] ); } );
    if ( navigator.geolocation ) {
        navigator.geolocation.getCurrentPosition(
            function ( position ) {
                let result = {};
                result[ 'ErrorCode' ] = "0";
                result[ 'ErrorDesc' ] = "OK";
                result[ 'Latitude' ] = position.coords.latitude;
                result[ 'Longitude' ] = position.coords.longitude;
                result[ 'Altitude' ] = position.coords.altitude;
                result[ 'Heading' ] = position.coords.heading;
                result[ 'Speed' ] = position.coords.speed;
                result[ 'Accuracy' ] = position.coords.accuracy;
                result[ 'AccuracyAltitude' ] = position.coords.altitudeAccuracy;
                pCallbackFunction( result );
            },
            function ( err ) {
                let result = {};
                result[ 'ErrorCode' ] = err.code;
                result[ 'ErrorDesc' ] += ''; // err.message;
                result[ 'ErrorDesc' ] += ( err.code === 1 ) ? "The acquisition of the geolocation information failed because the page didn't have the permission to do it." : "";
                result[ 'ErrorDesc' ] += ( err.code === 2 ) ? "The acquisition of the geolocation failed. Using Safari on Mac OS? Enable System Preferences &gt; Security and Privacy &gt; Privacy &gt; Location Services &gt; Safari." : "";
                result[ 'ErrorDesc' ] += ( err.code === 3 ) ? "The time allowed to acquire the geolocation, was reached before the information was obtained." : "";
                pCallbackFunction( result );
            },
            {
                enableHighAccuracy: true,
                timeout: 15000,
                maximumAge: 0
            }
        )
    } else {
        let result = {};
        result[ 'ErrorCode' ] = -1;
        result[ 'ErrorDesc' ] = "Browser does not support Geolocation";
        callbackFunction( result );
    }
}</code></pre>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20200813-js-locationget</link>
<guid>https://campsoftware.com/blog/post.php?post=20200813-js-locationget</guid>
<pubDate>Thu, 13 Aug 2020 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[PHP Semaphore Check and Release]]></title>
<description><![CDATA[<h1>PHP Semaphore Check and Release</h1>
<p>Date: 2020-08-13</p>
<p>Gets a semaphore and attempts to acquire to prevent code from running more than one time.</p>
<ul>
<li>Aborts if the semaphore cannot be retrieved.</li>
<li>Aborts if the semaphore in use.</li>
<li>The key must be an integer.</li>
</ul>
<p>On Github: <a href="https://github.com/campsoftware/semaphoreCheck-php">https://github.com/campsoftware/semaphoreCheck-php</a></p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20200813-php-semaphore-check-and-release</link>
<guid>https://campsoftware.com/blog/post.php?post=20200813-php-semaphore-check-and-release</guid>
<pubDate>Thu, 13 Aug 2020 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Software Development: Disappointed to Ecstatic]]></title>
<description><![CDATA[<h1>Software Development: Disappointed to Ecstatic</h1>
<p>Date: 2020-08-13</p>
<p>It's been a while since my last blog post. There's a reason for that. Software Development has been disappointing, but after five years, I'm ecstatic again!</p>
<p>Why? I think it has to do with greed along with a lack of communication. I'm just glad to see the pattern and have the technical ability to change development platforms.</p>
<!--more-->
<p>As developers, what do we need? That depends on the app. Some apps are hardware generic while other apps need specific hardware features. We primarily develop database apps where folks view/edit data, print to paper or PDF, and document management. There aren't many specific hardware feature we need other that being able to work offline when internet isn't available.</p>
<p>There are lots of app types: Desktop ( Mac, Windows, Linux ), Mobile ( iOS, iPadOS, Android ), and Web. I'm not including apps for watches, tvs as we focus mostly on business apps. That's a lot. Desktop has three, Mobile has two or three, Web has one. Thats SEVEN targets to develop to get your app in the hands of most users. However, for hardware generic apps, all you really need is a Web App. Web Apps run on just about anything with a browser so there's no need for target specific apps.</p>
<p>FileMaker: I've been using FileMaker since around 1992. FileMaker is very nice but has some issues that can be dealt with. FileMaker is like Excel in that each user must have a license to utilize FileMaker. FileMaker had Runtimes to allow developers who paid extra for FileMaker Pro Advanced to distribute a non-networkable version of FileMaker at no extra cost. That's been deprecated. Way back when around version 12, FileMaker was sold as 'pay and use it forever' and upgrade when you like. You can still purchase FileMaker that way but annual pricing is preferred. I could go into a ton of detail, but I'll save you from all that and provide one example instead. FileMaker Server has had the ability to create APIs for years and years called XML Web Publishing which was included. It's still in the product, but has been deprecated in favor of the Data API. That's great advancement, but now it's metered. So if you purchase FileMaker Server and set it up in your office, you'll have to pay extra to transmit your own data from your own server. It's nuts. This plus the other issues make continuing with FileMaker difficult.</p>
<p>Xojo: I've been using from 1998 and haven't renewed my license since September 2019. Xojo's pricing is amazing compared to FileMaker. Xojo Pro is $700/year to create Desktop, iOS, and Web Apps. It's less expensive if you only need one of those. Less than $1k per year for Xojo is a steal because none of your users need to pay Xojo ever. They don't even need to know you used Xojo. I haven't bothered to renew because Xojo hasn't shipped anything meaningful in quite a while. Xojo had some major missteps including the "New Framework", then API 2.0. I'll skip all the details, but long story short, Xojo Web 2.0 was announced around November 2017 and it's still not available, but is in alpha after all this time. Xojo has a Roadmap after lots of drama where you can get a long term idea where the product is going. That's great, but I'm sad that "Easy Database Connectivity" keeps dropping down on the list as new items are added. When I was using Xojo Web 1.0, I liked it, but I kept encountering bugs that I couldn't believe. I spent time writing bug reports where some would get fixed, but others would sit in purgatory. Xojo is NOT greedy which makes me wish them all the success in the world. They just need to ship. Right now Desktop apps are the only non-deprecated product as Web is being rewritten and iOS will be rewritten to be part of "Mobile" which will first ship with Android and iOS will be added. On top of all of that, Web 2.0, Android, and iOS 2.0 will all be brand new apps that will need lots of debugging. It just comes down to Xojo is way too chaotic right now.</p>
<p>It just stinks as FileMaker and Xojo could be game changes for development, but their respective issues prevent them from taking over the world.</p>
<p>In FileMaker we created a template called "Foundation" which we still use today. In Xojo we created three or four versions of "Xanadu" in different incarnations including using Aloe for Xojo by Tim Dietrich.</p>
<p>Xanadu is now being developed with PHP and I'm ecstatic about it. As I mentioned, all we really need is a Web App since we don't need to access any device specific hardware. Currently we're running PHP with NGINX on Amazon Lightsail which is very inexpensive and wicked fast! When people say they used PHP, it's a bit nebulous. In my head, we write PHP to write HTML, CSS, and Javascript. HTML and CSS are used to display information to the user while Javascript is used interact with the information. We use Aloe for PHP by Tim Dietrich. Aloe made getting started with PHP much easier as it handled the Web Request and Responses. In addition to Aloe, we also use Bootstrap, Barcodes, Flatpickr, FontAwesome, and a few other libraries.</p>
<p>If you have any question, get in touch!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20200813-software-development-disappointed-to-ecstatic</link>
<guid>https://campsoftware.com/blog/post.php?post=20200813-software-development-disappointed-to-ecstatic</guid>
<pubDate>Thu, 13 Aug 2020 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Amazon S3 Class for Xojo]]></title>
<description><![CDATA[<h1>Amazon S3 Class for Xojo</h1>
<p>Date: 2019-02-19</p>
<p><br>Using the S3 Class is easy. First initialize the class then start making calls! <br><br>Before getting into the calls, I'd like to have a big thank you to <a href="http://timdietrich.me" target="_blank">Tim Dietrich</a> and <a href="https://www.monkeybreadsoftware.de/xojo/" target="_blank">Christian Schmitz</a> for their example code which made putting the S3 Class together super easy. Once you have your Amazon account created, enabled S3 and add an S3 Bucket, I'd suggest using an App like CyberDuck to connect to S3 as a viewer to see the result of making calls to the S3 Class.<br><br></p>
<!--more-->
<p>Download: <a href="http://campsoftware.com/download/Xojo-S3-Class.zip" target="_blank">Xojo S3 Class</a><br><br><strong>Instantiate</strong><br><br>Once you drag the class to your project you can instantiate S3. Just set your Keys, update the Region, Domain, and set your Bucket name.<br><br><span style="font:12px Courier, mono; ">// Init S3<br>Dim theS3 As New S3( <em><br>"AccessKey", </em><br>"SecretKey", <em><br>"us-east-1", </em><br>"s3.us-east-1.amazonaws.com", <em><br>"Bucket" </em><br>)<br></span><br><strong>A Note About S3 Keys</strong><br><br>S3 uses Keys to reference files and folders. In the List Files example below, we're looking for what is in the "PhotoURL" folder which is in the UUID "F30BB9DD-DE9A-4F13-91B1-79BA4A776585" folder which is in the "Contacts" folder. This how we store files for a specific Contact Photo. In our database we have a Table called "Contacts" that has a record with that UUID and a column called "PhotoURL" which stores the key and the filename that was most recently updated which might look like "Contacts/F30BB9DD-DE9A-4F13-91B1-79BA4A776585/PhotoURL/PhotoOfHal.png". <a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingObjects.html" target="_blank">Amazon has a ton of information about S3</a>.<br><br><strong>List Files</strong><br><br>Get a Listing of Content, technically Files and Folders via a Key as XML. Amazon returns the Listings as XML.<br><br><span style="font:12px Courier, mono; ">// S3 List as XML<br>Dim theListXML As String<br>theListXML = theS3.ListGetXML( "Contacts/F30BB9DD-DE9A-4F13-91B1-79BA4A776585/PhotoURL/" )</span><br><br>Get a Listing of Content, technically Files and Folders via a Key as a Dictionary. Internally, this method calls ListGetXML and transforms the data into a Dictionary for easier processing.<br><br><span style="font:12px Courier, mono; ">// S3 List as Dictionary<br>Dim theListDict() as Dictionary<br>theListDict = theS3.ListGetDict( "Contacts/F30BB9DD-DE9A-4F13-91B1-79BA4A776585/PhotoURL/" )</span><br><br><strong>FolderItem Upload and Download</strong><br><br>Upload a FolderItem via a Key. To upload a file, you first define the folder Item and then pass the Key and FolderItem.<br><br><span style="font:12px Courier, mono; ">Dim theFolderItem as FolderItem = SpecialFolder.Desktop.Child("test-one.png")<br>Dim theFolderItemUpload as string // 200 is successful<br>theFolderItemUpload = theS3.FolderItemUpload( "Contacts/F30BB9DD-DE9A-4F13-91B1-79BA4A776585/PhotoURL/" + theFolderItem.Name, theFolderItem )</span><br><br>Download a FolderItem via a Key. To download a file, you first define the folder Item and then pass the Key and FolderItem.<br><br><span style="font:12px Courier, mono; ">Dim theFolderItem as FolderItem = SpecialFolder.Desktop.Child("test-one.png")<br>Dim theFolderItemUpload as string // 200 is successful<br>theFolderItemUpload = theS3.FolderItemDownload( "Contacts/F30BB9DD-DE9A-4F13-91B1-79BA4A776585/PhotoURL/" + theFolderItem.Name, theFolderItem )</span><br><br><strong>String Upload and Download</strong><br><br>Upload a String via a Key. Uploading a string is similar to uploading a FolderItem except that the string is held in memory rather than read from a file.<br><br><span style="font:12px Courier, mono; ">dim theString as string = "Hello World!"<br>dim theStringUpload as string // 200 is successful<br>theStringUpload = theS3.StringUpload( "Contacts/F30BB9DD-DE9A-4F13-91B1-79BA4A776585/PhotoURL/StringUpload.txt", theString )</span><br><br>Download a String via a Key. Downloading a string is similar to downloading a FolderItem except that the string is held in memory rather than written to a file.<br><br><span style="font:12px Courier, mono; ">dim theStringDownload as string // 200 is successful<br>theStringDownload = theS3.StringDownload( "Contacts/F30BB9DD-DE9A-4F13-91B1-79BA4A776585/PhotoURL/StringUpload.txt" )</span><br><br><strong>Delete</strong><br><br>Delete Content via a Key. When run, the file is deleted returning "204" representing "No Content" if successful. <br><br><span style="font:12px Courier, mono; ">dim theKeyDelete as string // 204 is successful<br>theKeyDelete = theS3.KeyDelete( "Contacts/F30BB9DD-DE9A-4F13-91B1-79BA4A776585/PhotoURL/StringUpload.txt" )</span><br><br><strong>Questions or Comments</strong><br><br>Post on <a href="https://forum.xojo.com/52546-blog-post-amazon-s3-class-for-xojo" target="_blank">this Xojo Forum Thread</a>. <img class="blog-image-smiley" src="https://campsoftware.com/blog/postMedia/rw_common/plugins/blog/smiley_smile.png" width="21" height="21" alt="Happy" border="0"> <br><br></p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20190219-amazon-s3-class-for-xojo</link>
<guid>https://campsoftware.com/blog/post.php?post=20190219-amazon-s3-class-for-xojo</guid>
<pubDate>Tue, 19 Feb 2019 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Pivot Tables in Xojo and FileMaker]]></title>
<description><![CDATA[<h1>Pivot Tables in Xojo and FileMaker</h1>
<p>Date: 2019-02-07</p>
<p><br>As you can see in the video below, you can easily visualize your data just by dragging the columns to change how the data is summarized. <br><br>This data is from our annual Boy Scout Mulch Sale which occurs for a few weeks in January and February, but I duplicated the data several times and randomized the dates so the sales would appear throughout the entire year. The video and the example files have sample data with 10,000 records.<br><br></p>
<!--more-->
<img class="imageStyle" alt="PivotTable" src="https://campsoftware.com/blog/postMedia/pivottable.gif" width="1280" height="800">
<p><br><br>When you add and remove columns and rows to the Pivot Table you can see it summarize the data with blinding speed. I've only seen a delay when adding unique data like an Invoice Date or Invoice Number which is unique unlike a Year, Month, or Product Name. That makes sense because the entire point of Pivot Tables is to show your raw data in different summarized views.<br><br>The Demo Files are designed to be easy to move into an existing Xojo App or FileMaker Database:<br><ul class="disc"><li>Xojo Web App Demo</li><li>Xojo Desktop App Demo</li><li>FileMaker Database Demo</li><li>Includes folder containing the required Javascript and CSS files</li></ul><br><a href="http://campsoftware.com/download/PivotTable2019-02-04.zip">Download the Demo Files</a><br><br>To get the Pivot Table into your Xojo App, first copy the PivotTable Class to your App. Then as shown on the PivotTablePage for Web Apps and PivotTableWindow for Desktop Apps, use the code on the PivotTableButton to apply your data to an HTMLViewer.<br><br>To get the Pivot Table into your FileMaker Database, first copy the PivotTableHTML Custom Function to your database. Then as shown on the FileMaker Layout, use the code in the WebViewer calculation to apply your data to a WebViewer.<br><br>The magic all happens within <a href="https://github.com/nicolaskruchten/pivottable" target="_blank">PivotTable.js</a>. I'd also like to thank <a href="https://www.soliantconsulting.com/blog/pivot-table-filemaker" target="_blank">Jeremy Brown for his Blog Post</a> where I first learned about PivotTable.js. Jeremy's download breaks down all the parts into separate fields and suggests a nice method to format the data.<br><br>You should know that our Demo Files load several Javascript and CSS include files from our website to make portability easier. We highly suggest that you use host these files on your own website which found in the 'Includes' folder. Just upload the folder to your website and change the url for each Javascript and CSS file to your own website links. The links can be found in the Xojo App PivotTable Class HTMLTemplate Constant and in the FileMaker Database PivotTableHTML Custom Function.<br><br>The PivotTableData is fomatted as a Javascript Array as shown below. Just format your data to match so that numbers are not quoted and strings are quoted. If you have data with double quotes you should escape them by adding a backslash before all double quotes. Escaping the double quotes turns them in into a simple string character rather than a defining the beginning or end of a string. Besides setting the PivotTable Title, all you need to do is set the Default Values, Default Columns, and Default Rows which defines how the Pivot table will appear on load.  <br><br>["Year","Month","Product","Quantity","Price","Total","Bag Count","Spread Service","Referral"],<br>[2014,"01 - January","Pine Mulch",1,47,47,9,"No","Friend"],<br>[2014,"01 - January","Pine Mulch",3,47,141,27,"No","Website"],<br>[2014,"01 - January","Pine Mulch",7,47,329,63,"Yes","Facebook"],<br>[2014,"01 - January","Cypress Mulch",7,47,329,63,"No","Facebook"],<br>[2014,"01 - January","Pine Mulch",4,47,188,36,"Yes","Website"],<br>[2014,"01 - January","Pine Mulch",2,47,94,18,"No","Facebook"]<br><br>Questions or Comments<br><br>Post on <a href="https://forum.xojo.com/52361-blog-post-pivot-tables-in-xojo-and-filemaker/" target="_blank">this Xojo Forum Thread</a>.</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20190207-pivot-tables-in-xojo-and-filemaker</link>
<guid>https://campsoftware.com/blog/post.php?post=20190207-pivot-tables-in-xojo-and-filemaker</guid>
<pubDate>Thu, 07 Feb 2019 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[Xojo Web - Logging to the Browser Console]]></title>
<description><![CDATA[<h1>Xojo Web - Logging to the Browser Console</h1>
<p>Date: 2019-01-31</p>
<p><br>In Chome on Mac, you can access the Javascript Console from the Menu Bar &gt; View &gt; Developer &gt; Javascript Console as shown below. Each Browser is different, but just search the web for your particular Browser. Once the Console is displayed you may already see messages or it could be blank if nothing has been sent to the Console.<br><br></p>
<!--more-->
<img class="imageStyle" alt="MacChromeJavascriptConsole" src="https://campsoftware.com/blog/postMedia/macchromejavascriptconsole.png" width="813" height="340">
<p><br><br>You can use Javascript to write to the Console using the Javascript "console.log" command as <a href="https://www.w3schools.com/jsref/met_console_log.asp" target="_blank">demonstrated at W3Schools</a>. It's really simple:<br><br><span style="font:12px Courier, mono; ">console.log("Hello world!");</span><br><br>In a Xojo Web App, you'd call the Javascript console.log command using the <a href="http://docs.xojo.com/ExecuteJavaScript" target="_blank">ExecuteJavascipt</a> command which is just as simple. However, as developers, we never want to repeat code that can be encapsulated. So, we created a Xojo Method that can be used to make Logging Messages to the Browser Console even easier. Here's how to Log some information in a Loop:<br><br><span style="font:12px Courier, mono; ">Dim i As Integer<br>For i = 1 To 10<br>   LogToBrowserConsoleLog( "Running Loop Number " + i.ToText )<br>Next</span><br><br>If you add that code to a Button.Action You should see this in the Browser Console:<br><br><img class="imageStyle" alt="XojoCodeResultInBrowserConsole" src="https://campsoftware.com/blog/postMedia/xojocoderesultinbrowserconsole.png" width="347" height="271"><br><br>The Xojo 'LogToBrowserConsoleLog' Method can be found below along with three helper methods that respectively return a Backslash, Single Quote, and a Double Quote. The three helper method could be removed and have the Chr functions pasted directly into the 'LogToBrowserConsoleLog' method, but we like having those separate since it makes code cleaner an easier to read.<br><br>The "LogToBrowserConsoleLog" Method accepts a string and then replaces each Single Quote and Double Quote with a preceding Backslash. Replacing the Quotes with a preceding Backslash is called 'escaping' which converts the Quotes into string characters rather than treating them as Quotes to define a string. W3Schools discusses <a href="https://www.w3schools.com/js/js_strings.asp" target="_blank">Javascript Special Characters</a> nicely. There are more characters that could be added to this like Carriage Returns and Tabs.<br><br><span style="font:12px Courier, mono; ">Public Function ChrBackslash() as string<br>   Return Chr(92)<br>End Function<br><br>Public Function ChrQuoteDouble() as string<br>   Return chr(34)<br>End Function<br><br>Public Function ChrQuoteSingle() as string<br>   Return chr(39)<br>End Function<br><br>Public Sub LogToBrowserConsoleLog(pMessage as string)<br>   // Escape Single and Double Quotes in the Message<br>   pMessage = ReplaceAll( pMessage, ChrQuoteSingle, ChrBackSlash + ChrQuoteSingle )<br>   pMessage = ReplaceAll( pMessage, ChrQuoteDouble, ChrBackSlash + ChrQuoteDouble )<br><br>   // Send the Message to the Web Console Log<br>   dim tempContainer as New WebContainer<br>   tempContainer.ExecuteJavaScript( "console.log('" + pMessage + "');" )<br>End Sub</span><br><br>If this is helpful, get in touch and let us know! <br><br>Thanks to <a href="https://twitter.com/tdietrich" target="_blank">Tim Dietrich</a> for suggesting that this was worthy of a blog post!</p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20190131-xojo-web-logging-to-the-browser-console</link>
<guid>https://campsoftware.com/blog/post.php?post=20190131-xojo-web-logging-to-the-browser-console</guid>
<pubDate>Thu, 31 Jan 2019 00:00:00 +0000</pubDate>
</item>
<item>
<title><![CDATA[SpeakerCue Released - A Confidence Monitor for Professional Producers and Presenters]]></title>
<description><![CDATA[<h1>SpeakerCue Released - A Confidence Monitor for Professional Producers and Presenters</h1>
<p>Date: 2018-06-12</p>
<div class="video-embed">
  <video controls preload="metadata" playsinline poster="/products/speaker-cue/SpeakerCue.mp4.png">
    <source src="/products/speaker-cue/SpeakerCue.mp4" type="video/mp4">
    Your browser does not support the video tag.
  </source></video>
</div>
<p></br>
</br>
"SpeakerCue is the only full-featured program countdown clock that integrates the ability to easily communicate with your speaker or moderator while they're on stage," said Bill Chapman, Executive Director/Producer at The Richmond Forum, America's largest non-profit public lecture series. "We've used SpeakerCue for speeches by President Barack Obama, actress Glenn Close, former U.S. Ambassador to the United Nations Samantha Power, and others. Now, I would never consider producing a program without it."</p>
<!--more-->
<img class="imageStyle" alt="stacks-image-fcd8881-1200x900" src="https://campsoftware.com/blog/postMedia/stacks-image-fcd8881-1200x900.jpg" width="780" height="585">
<p><br><br>The most important feature of SpeakerCue is that it provides a configurable clock, customizable in a variety of color and display options. The clock can be configured in hour/minutes or hour/minutes/seconds mode in the current time or as a countdown clock. Based on preference, it can also be set to flash or change color at a pre-set time to inform the speaker to start wrapping up their presentation or at other prearranged times.<br><br>SpeakerCue also features a one-way message transmitter that will allow an advisor to give speakers of all skill levels the instructions they need to make their presentations better in real time. SpeakerCue is an excellent way for a teacher to send basic instructions to a beginner speaker such as "Speak louder," "Slow down," "Look up," or "Smile." Seasoned professional producers can send their speaker more advanced cues such as, "Expand on that point," "Last question," or "Introduce the next
speaker."<br><br><img class="imageStyle" alt="SpeakerCue Producer Window" src="https://campsoftware.com/blog/postMedia/SpeakerCue-Producer-Window.png" width="912" height="654"><br><br><br>Computer Requirements:<br><em> Mac running OS 10.9.5 (Mavericks) or later<br></em> Windows running Windows 7, SP1 Platform Update (64-bit versions only) or later<br>* Also runs on Linux and Raspberry Pi<br><br>Visit SpeakerCue online to download a free demo. In demo mode, SpeakerCue will auto-quit after 10 minutes, but it can be relaunched as often as you would like. For more information, please contact Ardith Gumbert.<br><br>SpeakerCue was developed with Xojo.<br><img class="imageStyle" alt="Made with Xojo" src="https://campsoftware.com/blog/postMedia/made-with-xojo.png" width="295" height="115"></p>]]></description>
<link>https://campsoftware.com/blog/post.php?post=20180612-speakercue-released-a-confidence-monitor-for-professional-producers-and-presenters</link>
<guid>https://campsoftware.com/blog/post.php?post=20180612-speakercue-released-a-confidence-monitor-for-professional-producers-and-presenters</guid>
<pubDate>Tue, 12 Jun 2018 00:00:00 +0000</pubDate>
</item>
</channel>
</rss>
