camen design http://camendesign.com/ code is art en-gb http://camendesign.com/design/cameneye-ink(c)kroccamen.png camen design http://camendesign.com/ Fri, 20 Jun 2014 17:41:49 +0000 Fri, 20 Jun 2014 17:41:49 +0000 Etho http://camendesign.com/etho http://camendesign.com/photo/etho Sun, 14 Jul 2013 11:17:00 +0000 <h1>Etho</h1> <p> Through the life-changing experience of an intense 16-hour labour we are able to announce to you the birth of our son Etho Samuel on the 2<sup>nd</sup> of July 2013. We are all doing well and just beginning to get a grasp on the change of routine this brings upon us. </p> <figure> <a href="http://camendesign.com/photo/etho/67943045-6.jpg" type="image/jpeg"> <img src="http://camendesign.com/photo/etho/67943045-6_preview.jpg" alt="" width="640" height="970" /> </a> </figure> <figure> <a href="http://camendesign.com/photo/etho/67943045-7.jpg" type="image/jpeg"> <img src="http://camendesign.com/photo/etho/67943045-7_preview.jpg" alt="" width="640" height="965" /> </a> </figure> <figure> <a href="http://camendesign.com/photo/etho/67943045-1.jpg" type="image/jpeg"> <img src="http://camendesign.com/photo/etho/67943045-1_preview.jpg" alt="" width="640" height="965" /> </a> </figure> <figure> <a href="http://camendesign.com/photo/etho/67943045-8.jpg" type="image/jpeg"> <img src="http://camendesign.com/photo/etho/67943045-8_preview.jpg" alt="" width="640" height="965" /> </a> </figure> <figure> <a href="http://camendesign.com/photo/etho/67943045-4.jpg" type="image/jpeg"> <img src="http://camendesign.com/photo/etho/67943045-4_preview.jpg" alt="" width="640" height="424" /> </a> </figure> <figure> <a href="http://camendesign.com/photo/etho/67943045-9.jpg" type="image/jpeg"> <img src="http://camendesign.com/photo/etho/67943045-9_preview.jpg" alt="" width="640" height="970" /> </a> </figure> <p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p> kroc-fund-drive http://camendesign.com/kroc-fund-drive http://camendesign.com/blog/kroc-fund-drive Sun, 12 May 2013 17:40:00 +0000 <blockquote> <p> Howdy, folks! Kroc has decided to let me take over Camen Design for today — not literally, mind you — and has given me the opportunity to borrow his podium for this blog entry. </p> </blockquote> <blockquote> <p> My name is Clint Franklin. I am known on <a href="http://forum.camendesign.com" rel="external">the forums</a> here as “theraje” (no caps, please). Way, way, WAY back in mid-2011, ol' Kroc chirps at me on <abbr title="instant messenger">IM</abbr>, saying he had a surprise for me. Not being one who is particularly fond of surprises, I inquired about the nature of this surprise. If I recall correctly, he told me to wait a week while he put on some finishing touches. I think it all boils down to him teasing me. </p><p> Well, after a week had passed, he said he was finished, and wanted to send me a video. Now, I know what you’re thinking — “Big deal. I just go to YouTube and watch videos all day long.” </p><p> But, as I'm sure a few of you will recall, at that time, I was using the very best Internet connection I could afford in my area. 56k dial-up. Not only that, but 56K dial-up that would, on its best days, max out at 28.8K. Even a “standard” video would be impossible to view via YouTube. In fact, poor Kroc had to virtually obliterate the video quality, to the point where things could be vaguely recognizable. He then had to reduce the picture size down to some ridiculous dimension. Think along the lines of: Barely takes up all the screen space on an average cell phone manufactured in 2003. </p><p> I think the final “preview” video was around 2 megabytes. Of course, it took about 15 minutes to download this 2-meg file. Yes — 15 minutes to download 2 measly megabytes. And that was pretty decent, for me. And this was less than two years ago. </p><p> When I saw the video, I just watched, slack-jawed, as I saw Kroc’s work. Long story short, it described my situation (and included plenty of amusing, yet home-hitting, visuals) in a way people could understand. Because, folks, I tell you what — you just have no clue how precious data is until you have to wait 20+ minutes for a 3-minute MP3 song to download onto your computer! Or when you can’t browse 90% of the Web because of server timeouts and JavaScript-heavy site architectures. </p><p> The real shocker was that this video (in higher quality, of course — if Kroc had released the “preview” version publicly, the world would have gouged its eyes out) was part of a campaign to encourage people to donate money so that I could afford the only means of acquiring anything remotely similar to broadband (which would have been satellite Internet — 3G was completely unavailable in my house). </p><p> The reason Kroc wanted to gather up the money was because I had no access to anything faster than 28.8k dial-up, except for satellite — and not only does it <em>START</em> at $60 a month… you are forced to sign a two-year contract. If you wish to cancel before the two years expires, you are required to pay an early termination fee (several hundred dollars!). </p><p> I, being legally disabled and unable to work (and even if I <em>COULD</em> work, I lived out in the boonies, and the nearest grocery store took half an hour to drive to), was certainly unable to afford such. That said, I never was one who liked to ask people for things (I hated the idea of having to apply for disability in the first place, but that changed when it became obvious that I had no other choice). I value self-sufficiency. So, initially, I was a bit perturbed. No… I was sick to my stomach. I didn’t have the heart, at the time, to say “no” to Kroc on this. He put <em>WAY</em> too much work into it. He was <em>WAY</em> too excited about it. </p><p> So, even with the knowledge that not only did I dislike the idea, but also that people would get the urge to assume things (which they did — calling me a bum, telling me to get a job, and so on, not knowing my situation), and that even if people did find it in their heart to help, that it would not necessarily work out (satellite Internet performance and reliability is very, very dubious). And if that were to happen, I would have wasted those people’s charity. I don’t know if I could have dealt with that. </p><p> But, I caved. Kroc wanted so badly for me to have the chance to do the things I love — working on programming projects, artwork, and so on — that I couldn’t say no. Plus, the idea of me having the opportunity to work again, doing contract work online, made me feel as though I could at some point become productive and not be at the mercy of a government check. </p> <aside> This video is available only in WebM format. It may not play on some devices. If in doubt, view this page with <a href="http://getfirefox.com" rel="external">Mozilla Firefox</a> </aside> <video preload="none" src="http://camendesign.com/blog/kroc-fund-drive/help_clint_trimmed.webm" width="600" height="336" poster="/blog/kroc-fund-drive/poster.png" controls> <img src="http://camendesign.com/blog/kroc-fund-drive/poster.png" alt="Help Clint" width="600" height="336" /> </video> <p> In the end, <a href="http://camendesign.com/help_clint">we collected over 50% of our goal of $2000</a> (the total cost of covering the contract period). We <a href="http://camendesign.com/an_update">stopped short</a> because after catching a lot of flak from those people I mentioned early (the ones who like to <em>ASS</em>-ume things about which they have no understanding), I had basically decided to commit suicide. The world and everyone in it wanted me to suffer, but I'd rather just kill myself and get it over with, I thought. </p><p> I regained my composure a few months later, but still went to bed, every night, saying the same prayer to a god I didn’t even believe existed: “God, please let me go to sleep now, and just never let me wake up again.” </p><p> To wrap things up, I spent the following few months in and out of hospitals (both medical and mental), hoping that my life would just end. Then, out of nowhere, something very strange happened. I won’t go into details — some call it a “revelation” from God himself… others call it a severe psychotic breakdown… all I can tell you, definitively, is that something changed in me. I regained the will to live. </p><p> After my “psychotic breakdown,” I was put into group therapy. While there, one of the other patients told me about an apartment complex that is set up for people like me who are disabled — think of it as a retirement community, not for retirees, but for the disabled. </p><p> Long story short, a former tenant caused problems at that complex, and the building manager (who had met with me previously) told me she had an opening, and wanted me to move in. That was in October of 2012. </p><p> The money donated to me for the campaign Kroc started allowed me to not only pay the deposits for the rent and electricity (which added up to several hundred dollars, and would have kept me out if I were unable to pay them), but to accomplish the original goal — I now live in an area that has access to DSL Internet service! So long, 28.8 <abbr title="Kilobits per second">Kbps</abbr>… hello 12.0 <abbr title="Megabits per second">Mbps</abbr>!!! Goodbye, 6 minutes to download 1 megabyte… Good to see ya, 1 megabyte per second!!!!!! </p><p> And now, of course, my life has improved by an order of magnitude from before. The people who donated did not merely help me get on broadband. They gave me a place to stay, an opportunity to get the help I need (medically, psychologically, etc.), to find work (it no longer costs $30 in gas to take a trip to the nearest business!!!), to attend church (I was an atheist for roughly 8 years, until my “revelation” from God gave me the strength to get out of bed again), to form my first real romantic relationship (wedding bells may not be far off), and to help others and be blessed by the people who are now a part of my life, and would not have been otherwise. Those of you who donated have helped far, far more people than just little old me. </p><p> I may have been upset with Kroc starting all of this fundraising jazz in the first place, but now, all I can feel is immense gratitude to him and all of the people who helped — whether it was by donating money, moral support, a shoulder upon which to cry… </p><p> Now that I'm getting all sentimental, I'm just going to say that I'm incredibly blessed to have a God that heard my cries, and had all the right people in place who gave me the strength to push myself forward, to the point where I could waste three dozen paragraphs explaining what could have been said in two simple words: </p><p> Thank you. </p><p> Yours Truly, </p> <cite>Clint Franklin, aka. “theraje”</cite> </blockquote> <p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p> Browser Vendors, You Have _Failed_ Us http://camendesign.com/failed http://camendesign.com/blog/failed Wed, 20 Mar 2013 08:54:00 +0000 <section> <h1>Browser Vendors, You Have <em>Failed</em> Us</h1> <p> <strong>Browser vendors have unilaterally lost sight of real problems that plague real users.</strong><br /> I am so angry right now. The thing that causes me so much anger is not that Google are shutting down their RSS reader product, it’s that the browser vendors sat back and allowed all the power over content to go into the hands of Facebook and now Google. </p><p> I wrote <a href="http://camendesign.com/rss_is_dying"><em>two years ago</em></a> that browser vendors were failing to focus on the real problem that: users don’t want, and find it slow and painful, to trudge around their daily round of websites and that the web browser should be exposing an automatic, intelligent and ultimately <em>useful</em> interface to RSS. </p><p> Why can I not do something about this? Becuase <em>creating another RSS web service to ‘replace’ Google Reader does not solve the underlying problem</em>, that is:—personal aggregation of content should not be dependnt on a business model. </p><p> Google never offered a pay option. Reader had to supliment other strategies. RSS does not benefit Google+, whose aim is to centralise all the content including the ability to process and access it. </p><p> RSS is a browser feature, a browser’s responsibility. It is a personalised view upon the web, just as bookmarks and add-ons are. It makes managing daily content much easier, just as bookmarks and add-ons do. The browser—as the personal portal on to the web—is best positioned to aggregate the web for the user. It knows the user’s browsing habits better than even the most evil of evil advertisers. The browser vendors could make the web faster, easier, less cluttered and junky than any web sevice ever could. And it could do all of that with the barest minimum of privacy-intrusion, without a single ad and without a conflicting business model. </p><p> Because browser vendors failed to see personalised aggregation as a benefit to regular users, these regular users have taken to Facebook as their personalised aggregation service, taking the power over content away from the browser vendors. </p><p> Facebook will one day soon be a major technology player rivalling Google, Amazon and even Apple. Facebook are already big, yes, but for now they’re ‘just a website’. When they control the technology landscape because they control access to the content then things turn very bad for users. Mozilla, prepare to be side-lined by a ‘website’. </p><p> That brings me to the second big thing browser vendors have failed users with: e-mail. </p><p> It is 2013 and there is still no way to set Firefox as the <em>default e-mail client</em> in Windows. There is no way to make it so that when a user right-clicks a file and goes <samp>Send To &gt; Mail Recipient</samp> that Firefox will launch with the user’s preferred web-mail. </p><p> This is no end of headaches for my customers. They are constantly slipping up because they do not discern the difference between actions that invoke the native mail client and the fact they are using web-mail. Functions they expect to work simply don’t, such as using the e-mail toolbar button in Windows Explorer. Most of my customers have unsent e-mails in the outbox of Outlook Express or Windows Mail because the native client was launched instead of their web-mail. Attaching files is an unbelievable pain because of this. Customers have to be carefully taught that no, you don’t go to the picture first, you write the e-mail first, then click attach and then have to understand the concept of a different view upon the file-system and that the Pictures folder in there, is actually the same as the Pictures folder their muscle-memory is programmed to use from the Start Menu. </p><p> E-mail sucks because browser vendors don’t care about real problems that plague real users every day. </p><p> If I were Mozilla I would integrate e-mail back into Firefox. The browser already knows the user’s web-mail user names and passwords. It could automatically configure everything. The user could have a clean interface to their e-mail without having to go through painful web-mail clients. Attachments would be easier. E-mail links in web pages <strong>would actually work</strong>. Native OS e-mail actions would actually <strong>do the right thing for once</strong>. Users would actually benefit more from this than redesigning the f*cking shape of the tabs or removing the RSS button from the toolbar. </p><p> Two years have passed. The boat has been missed. Facebook and Google control the content, how it is accessed, how it is processed. There is no way back. You have failed us, browser vendors. </p> </section> <p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p> Making the Ugly Elegant: Templating With DOM http://camendesign.com/dom_templating http://camendesign.com/code/dom_templating Thu, 07 Mar 2013 19:38:00 +0000 <section> <h1>Making the Ugly Elegant: Templating With DOM</h1> <ol> <li><a href="#how">How It Works</a></li> <li><a href="#code">The Code</a></li> <li><a href="#caveats">Caveats</a></li> <li> <a href="#api">The API</a> <ol> <li><a href="#api-new">Instantiation</a></li> <li><a href="#api-shorthand">Shorthand XPath Syntax</a></li> <li><a href="#api-html"><code>(string)</code> Output</a></li> <li> <a href="#api-repeat"><code>repeat</code></a> <ol> <li><a href="#api-next"><code>next</code></a></li> </ol> </li> <li><a href="#api-setvalue"><code>setValue</code></a></li> <li><a href="#api-set"><code>set</code></a></li> <li><a href="#api-addclass"><code>addClass</code></a></li> <li><a href="#api-remove"><code>remove</code></a></li> </ol> </li> <li><a href="#history">History</a></li> </ol> <aside> <strong>Update</strong> v16: Much better handling of HTML/XML-input and output </aside> <p> <strong>Templating is easy to do in any particular way, but doing it <em>right</em> is hard.</strong> I can’t count how many hip new template engines have popped up in just the last few years alone. I’m about to add one to the pile, but it is certainly not ‘hip’. It is however the closest I have ever gotten to the fabled golden fleece of “100% separation”. Unlike most other forms of templating, this <em>really</em> doesn’t mix logic and HTML, nor does it try to mask the blatent logic (“if this, then this”) by renaming ‘logic’ or using a <samp>{{special syntax}}</samp>. </p><p> What we’re going to do is this: take a static (and I mean static) HTML page, load it into the <abbr>DOM</abbr> as an XML tree and then use the PHP as your logic, removing bits of the template not needed and changing the text about. </p><p> I got this idea from this blog post: <a href="http://www.workingsoftware.com.au/page/Your_templating_engine_sucks_and_everything_you_have_ever_written_is_spaghetti_code_yes_you" rel="external"><cite>Your templating engine sucks and everything you have ever written is spaghetti code (yes, you)</cite></a>. The article itself is long, agressive, rambling and fails to demonstrate the principle concretely. I simply ignored all the text and focused on the core principle that was being noted: instead of embedding some form of code in the HTML (even if it’s just evolved search/replace syntax), just load the HTML into <abbr>DOM</abbr> and minpulate there so that the HTML itself is <em>ignorant</em> of the templating. </p><p> The reason why this is not just the same as a <samp>{{special-syntax}}</samp> is that we are <em>not mixing two different languages, syntaxes or programming models in one HTML file</em>. If you change your templating engine, it’s still HTML. If you change your logic, it’s still HTML. Special syntaxes invent another language to intermix with HTML and thus add <em>programatic concepts</em> to a <em>declartive syntax</em>—which is not clean separation no matter what you name it. </p> <blockquote> <p> Ever since the ’Web was invented there has been a transluscent, yet intransient divisor between those developers who understand the fundamental <a href="http://camendesign.com/html_lasts">difference</a> between a declerative markup syntax and a programming language, and those who don’t. Some learn to see this difference, others simply ignore it and believe that it is a swell idea to tie structured data to a structured program that will bit rot one thousand times quicker than the data will. If you are trying to replace HTML or CSS with JavaScript, you are doing it wrong and have just signed a maintenance contract from hell, with yourself, for yours and your data’s life. </p> <cite><a href="http://camendesign.com/dont_wanna">Kroc Camen—I Don’t Want to Do This Any More</a></cite> </blockquote> <p> By doing it this way, the HTML file itself can be designed independently of the software, and that whoever does the HTML doesn’t have to know PHP. You could change the whole server language and it wouldn’t change the template one bit. More importantly you can actually view the whole look of the template in the browser without running the software. The reason I’m adopting this templating approach for <a href="http://camendesign.com/nononsense_forum">NoNonsense Forum</a> is to make it easier for anybody to modify the look of their forum without having to learn PHP, and hopefully encourage more contribution from all skill levels. </p><p> It took a few revisions, two weeks and a lot of head-wracking to beat the <abbr>DOM</abbr> into something elegant, but here it is, NoNonsense Templating: </p> <h2 id="how">How It Works</h2> <p> The first thing to wrap your head around is that <abbr>DOM</abbr> templating works on the principle of mostly taking away rather than adding. Logic-wise this is more difficult to get used to than you would think; you will be used to adding data according to logic rather than “if this, then remove the thing that it is not”. </p><p> Firstly your template should be a static HTML page that contains all of the content and ‘possibilities’ of your output, where by we will remove what is not relevant to the page. For example: </p> <pre><code>&lt;p id="login" class="logged-out"&gt; You are not logged in. &lt;/p&gt; &lt;p id="login" class="logged-in"&gt; You are logged in as &lt;b class="username"&gt;Bob&lt;/b&gt; &lt;/p&gt;</code></pre> <p> In the PHP we can modify the HTML this way: </p><p> <small>(Please note that templates you load must be valid XML and have a single root node—e.g. “<code>&lt;html&gt;</code>”—in order to work, the examples in this article omit this for simplicity. See <a href="#xml">XML caveats</a> for more details)</small> </p> <pre><code>//load the template and provide an interface $template = new DOMTemplate (file_get_contents ('test.html')); //lets imagine the user is logged in, remove the logged-out section and set the username $template-&gt;remove ('.logged-out'); $template-&gt;setValue ('.username', 'Alice');</code></pre> <p> The command “<code>remove ('.logged-out')</code>” finds all elements that have a class of “<samp>logged-out</samp>” and deletes them (You can also refer to IDs using ‘<samp>#id</samp>’). </p><p> The <code>setValue</code> method sets the text-content of an element, removing anything that was within. By replacing element content it means that you can provide dummy text to test the look and feel of your template, and it will be replaced with the real data. No more staring at <code>{{NAME_GOES_HERE}}</code>! </p><p> Behind the scenes “<samp>.logged-out</samp>” becomes the full XPath “<code>.//*[contains(@class,"logged-out")]</code>”. The shorthand syntax also supports specifying a required element type and/or an attribute to target, e.g: </p> <pre><code>$template-&gt;setValue ('a.my-button@href', '/some_url');</code></pre> <p> You can also use full <a href="http://www.w3schools.com/xpath/xpath_syntax.asp" rel="external">XPath syntax</a>: </p> <pre><code>//if using HTTPS, change the Google search box to use HTTPS too if (@$_SERVER['HTTPS'] == 'on') $template-&gt;setValue ( '//form[@action="http://google.com/search"]/@action', 'https://encrypted.google.com/search' );</code></pre> <p> Looping is always a sore point in templating. How do you take a chunk and repeat it down the page without having to define a ton of logic in your templates? </p><p> Looping with the <abbr>DOM</abbr> is shockingly elegant! </p> <pre><code>$item = $template-&gt;repeat ('.list-item'); foreach ($data as $value) { $item-&gt;setValue ('.item-name', $value); $item-&gt;next (); }</code></pre> <p> The <code>repeat</code> method takes an element (via shorthand/XPath) to be used as the repeating template and copies it, then you just set and remove elements from the repeating template as if it were its own template. Once you’ve templated that iteration you call the <code>next</code> method and the HTML is added after the previous element, then the template repeater resets itself back to the original HTML so you can template it again! </p><p> Once you’ve made all your changes to the template, just retrieve the final HTML and output. </p> <pre><code>die ($template);</code></pre> <p> See <a href="#api">the API</a> for details of all the functions. </p> <h2 id="code">The Code</h2> <ul> <li><a href="http://camendesign.com/code/dom_templating/domtemplate.php">View source code</a></li> <li><a href="https://github.com/Kroc/DOMTemplate" rel="external">View source code on GitHub</a></li> <li><a href="http://forum.camendesign.com" rel="external">Discuss this article in the forum</a> (no registration required)</li> </ul> <p> If you would like to see a real-world use of this templating system with a ton of examples you can draw from real, practical code you can examine the source code of my forum system called <a href="http://camendesign.com/nononsense_forum">NoNonsene Forum</a> here: </p> <ul> <li><a href="https://github.com/Kroc/NoNonsenseForum/blob/master/index.php" rel="external">The PHP logic</a></li> <li><a href="https://github.com/Kroc/NoNonsenseForum/blob/master/themes/greyscale/index.html" rel="external">The HTML template</a></li> </ul> <p> If you don’t like the idea of targetting classes or IDs in your HTML, have a look at v4 of DOMTemplate that finds elements according to <samp>data-template</samp> attributes. </p> <ul> <li><a href="http://camendesign.com/code/dom_templating/domtemplate_v4.rem">The old v4 article</a></li> <li><a href="http://camendesign.com/code/dom_templating/domtemplate_v4.php">The old v4 code</a></li> </ul> <h2 id="caveats">Caveats</h2> <dl> <dt id="whitespace">Whitespace handling is good, but not perfect</dt> <dd> <p> In the case of repeating an element the whitespace within is kept, but the whitespace outside the element is not. This is not a major problem, it just means that the closing and opening tags of your lists will be paired (e.g. “<code>…&lt;/li&gt;&lt;li&gt;…</code>”). </p><p> The biggest issue is that when elements are removed, the whitespace around them remains, meaning that you get a number of blank lines in the output HTML where the elements used to be. There’s no direct way of handling this other than perhaps using a search/replace to remove blank lines in the HTML after it’s been templated. </p><p> One benefit of using the <abbr>DOM</abbr> however is that if you want minify the HTML a little, you can just add “<code>$this-&gt;DOMDocument-&gt;preserveWhiteSpace = false;</code>” to the constructor function of <code>DOMTemplate</code> and the markup will be returned as a big blob with few line-breaks. </p><p> If you add “<code>$this-&gt;DOMDocument-&gt;formatOutput = true;</code>” instead, the markup will be ‘tidied’ for you, re-nesting the elements neatly in an easy to read fashion. </p> </dd> <dt id="xml">XML woes</dt> <dd> <p> DOMTemplate stores and manipulates the template internally as strict XML. Thankfully, since <abbr title="version ">v</abbr>16, DOMTemplate automatically converts your source HTML to XML on loading and converts from XML to HTML on output, thus alieviating most of the input-strictness problems with earlier versions. There is however still a few caveats to remember: </p> <ul> <li> <p>HTML must be valid</p> </li><li> <p> The automatic conversion of HTML named-entities (invalid in XML) into Unicode is still not comprehensive. 248 of the most common are covered, but a total of <a href="http://whatwg.org/specs/web-apps/current-work/multipage/named-character-references.html" rel="external">over 2100 exist</a>. DOMTemplate may in a future version cover all 2100+ named entities, but until then ensure that your HTML source does not use any named-entities outside of the 248 recognised by DOMTemplate </p> </li><li> <p> HTML that you load either through <a href="#api-new"><code>DOMTemplate</code></a> or apply to the template using <samp>setValue</samp> <em>must</em> have only one root node. <abbr title="That is,">I.e.</abbr> a list of elements can not be used unless wrapped by an element. </p> </li> </ul> </dd> </dl> <h2 id="api">The API</h2> <h3 id="api-new">Instantiation</h3> <p> Provide the HTML to load as a string when instantiating the template class. It must be valid and have only one root element (e.g. <code>&lt;html&gt;</code>). </p> <pre><code>$template = new DOMTemplate (file_get_contents ('index.html'));</code></pre> <p> If you are loading an XHTML document, or any XML file with a default namespace (e.g. <code>&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;</code>), you <em>must</em> specify a prefix (any will do) and the namespace URL like so: </p> <pre><code>$template = new DOMTemplate ('index.html', 'html', 'http://www.w3.org/1999/xhtml');</code></pre> <p> All XPath queries you make with this template <em>must</em> prefix element names with the namespace, including for the <a href="#api-shorthand">shorthand</a>: </p> <pre><code>$template-&gt;setValue ('//html:title', 'Hello World'); //XPath $template-&gt;setValue ('html:a#my-button@href, 'http://google.co.uk'); //shorthand</code></pre> <p> This bizzare requirement is a limitation in the design of XPath itself. </p> <h3 id="api-shorthand">Shorthand XPath Syntax</h3> <ul> <li> <p> All of the methods that accept a query (<a href="#api-setvalue"><code>setValue</code></a>, <a href="#api-set"><code>set</code></a>, <a href="#api-repeat"><code>addClass</code></a>, <a href="#api-repeat"><code>remove</code></a> &amp; <a href="#api-repeat"><code>repeat</code></a>) use a shorthand-syntax where you only need to provide the class (“<samp>.class</samp>”) or ID (“<samp>#id</samp>”) you want to target and the full <a href="http://www.w3schools.com/xpath/xpath_syntax.asp" rel="external">XPath query</a> is built for you. E.g. <code>`.my-button`</code> </p> </li><li> <p>An element type can be provided: <code>`a#my-button`</code></p> </li><li> <p> An attribute name can be provided which will be the target of the <a href="#api-setvalue"><code>setValue</code></a>, <a href="#api-set"><code>set</code></a> and <a href="#api-repeat"><code>remove</code></a> methods: <code>`a#my-button@href`</code> </p> </li><li> <p> You can test attributes for values (the element will be selected, not the attribute):<br /> <code>`label@for="submit"`</code> </p> </li><li> <p>You can specify the index of an element to select: <code>`li[1]`</code></p> </li><li> <p>You can select child elements: <code>`#list/li/a`</code></p> </li><li> <p>You can also just use full XPath query, as-is: <code>`/html/head/title`</code></p> </li><li> <p> You can provide multiple targets by separating the queries with commas, e.g: <code>`.header, .body, .footer`</code><br /> You can intermix shorthand and full XPath like this. </p> </li> </ul> <h3 id="api-html"><code>(string)</code> Output</h3> <p> To get the HTML out of the template, cast the template class object to a string, <abbr title="for example">e.g.</abbr>: </p> <pre><code>$template = new DOMTemplate ('&lt;span&gt;test&lt;/span&gt;'); echo $template;</code></pre> <p> In instances where the intended type is ambiguous, use PHP’s casting syntax to force a string conversion: </p> <pre><code>$html = (string) $template;</code></pre> <h3 id="api-repeat"><code>repeat</code></h3> <p> <code>repeat (string $query)</code> </p><p> Takes a <a href="#api-shorthand">shorthand XPath query</a> and returns a <code>DOMTemplateRepeaterArray</code> object instantiated with the element(s) selected in the query. This object supports the <a href="#api-set"><code>set</code></a>, <a href="#api-setvalue"><code>setValue</code></a>, <a href="#api-addclass"><code>addClass</code></a> &amp; <a href="#api-remove"><code>remove</code></a> methods, in addition to the following method: </p> <h4 id="api-next"><code>next</code></h4> <p> Takes the current HTML content of the elements within <code>DOMTemplateRepeaterArray</code> object and appends it as a sibling to the previously repeated template (<abbr title="that is,">i.e</abbr> either the element(s) you instantiated the repeater with, or the element(s) that were added by the previous call to the <code>next</code> method), then resets its HTML content back to the original HTML it had when it was created. </p><p> In simple terms, it adds the templated HTML to end of a list and then resets it back to the original HTML, to be used again. In practical terms, like this: </p> <pre><code>$item = $template-&gt;repeat ('.list-item'); foreach ($data as $value) { $item-&gt;setValue ('.item-name', $value); $item-&gt;next (); }</code></pre> <h3 id="api-setvalue"><code>setValue</code></h3> <p> <code>setValue (string $query, string $value, [bool $asHTML=false])</code> </p><p> Replaces the content of all elements matched with the <a href="#api-shorthand">shorthand XPath query</a> with the given value. The string value is HTML-encoded (unless you give <code>`asHTML`</code> as true), so any HTML in the value will appear as-is, rather than be rendered as HTML. This method intelligently sets the value to elements, attributes and classes according to the XPath used. See <a href="#api-addclass"><samp>addClass</samp></a> for details on HTML class behaviour. </p> <pre><code>$template-&gt;setValue ('#name', 'Kroc');</code></pre> <h3 id="api-set"><code>set</code></h3> <p> <code>set (array $queries, [bool $asHTML=false])</code> </p><p> Allows you to write code in a more compact way by specifiying an array of <a href="#api-shorthand">shorthand XPath queries</a> and their associated value to set. </p> <pre><code>$template-&gt;set (array ( '#name' =&gt; 'Kroc', '#site' =&gt; 'http://camendesign.com' ));</code></pre> <h3 id="api-addclass"><code>addClass</code></h3> <p> <code>addClass (string $class)</code> </p><p> Adds the specificed HTML class name to every element matched with the <a href="#api-shorthand">shorthand XPath query</a>. If an element already has a class attribute, mutliple class names will be separated by spaces when the new class is added. </p> <pre><code>$template-&gt;addClass ('#section', 'open');</code></pre> <h3 id="api-remove"><code>remove</code></h3> <p> <code>remove (string $query | array $queries)</code> </p><p> Deletes all the elements (and their children) matched with the <a href="#api-shorthand">shorthand XPath query</a>. </p> <pre><code>$template-&gt;remove ('.secret-stuff');</code></pre> <p> Also accepts an array in the format of “<code>'xpath' =&gt; true|false</code>”.<br /> If the value is false, the XPath will be skipped. This allows you to write compact removal code by not having to write “<code>if (x) $template-&gt;remove ('y');</code>” several times in a row, e.g: </p> <pre><code>$template-&gt;remove (array ( '.section-1' =&gt; $section == 1, '.section-2' =&gt; $section == 2, ⋮ ));</code></pre> <p> For a good example of this style of writing, see <a href="https://github.com/Kroc/NoNonsenseForum/blob/master/index.php" rel="external">the code</a> for <a href="http://camendesign.com/nononsense_forum">NoNonsense Forum</a>. </p><p> In addition to this behaviour, you can also remove classNames from a class attribute, whilst retaining any other class names present by specifying the className to remove in the value, when tragetting a class attribute with the XPath, thusly: </p> <pre><code>$template-&gt;remove (array ('a@class' =&gt; 'undesired'));</code></pre> <h2 id="history">History</h2> <ul> <li><strong>v16</strong> Filtering of HTML on input and output, removing the strict-XML requirement for source text. The <code>`html`</code> method was removed in favour of casting the class to a <code>String</code></li> <li><strong>v15</strong> Throw an exception for invalid XPath queries or HTML</li> <li><strong>v14</strong> XPaths are cached for speed</li> <li><strong>v13</strong> Multiple namespace support</li> <li><strong>v12</strong> Ability to remove classNames using <code>`remove`</code> method</li> <li><strong>v11</strong> Changed instantiation to use a string instead of a filename</li> <li><strong>v10</strong> <code>`repeat`</code> now works simultaneously with multiple elements instead of just one</li> <li><strong>v9</strong> Greatly improved shorthand XPath syntax adding index matching, child matching &amp; attribute testing</li> <li><strong>v8</strong> Changed <code>`setValue`</code> to intelligently apply to elements, attributes or classes, with a parameter to include HTML as-is (<code>`setHTML`</code> was removed)</li> <li><strong>v7</strong> XML prolog is kept if already present and UTF-8 characters are no longer hex-encoded</li> <li><strong>v6</strong> XML namespace support. Also, template repeating now appends as a sibling, not as the last child of the parent (removes the need for a superfluous parent element).</li> <li><strong>v5</strong> New shorthand XPath syntax for classes and IDs instead of <code>`data-template`</code> attributes</li> <li><strong>v4</strong> Added multiple XPath targets</li> <li><strong>v3</strong> Added method chaining</li> <li><strong>v2</strong> Added HTML entity decoding</li> <li><strong>v1</strong> Initial release</li> </ul> </section> <p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p> A Summary http://camendesign.com/a-summary http://camendesign.com/blog/a-summary Fri, 22 Feb 2013 10:42:00 +0000 <section> <h1>A Summary</h1> <p> <strong>It is with much intrepidation (for life has become very uncertain) and joy (for life is no longer dull)</strong> that I can announce the news of our child, a son, due to us in early July. </p><p> Life, ever since meeting the love of it, has been upside down and changing at a relentless pace. First we met, and that was a big, unexpected change. Then I <a href="http://camendesign.com/4eva">asked her to marry me</a>. In three months we were married, and lived together—no small change for myself, which I found to be effortlessly natural (though obviously challanging). It’s only because of the news of our child that we have had to directly tackle the physical piles of stuff still not yet disseminated into everyday life. We find that having not even gotten settled in our first home we have to up-sticks and down-size (where there is no spare room to pile things) to be able to better focus ourselves on our child. </p><p> For this though I am very grateful as a blessing from God; meeting my wife has made me address many, many personal, internal burdens which have reflected—on this blog—through me giving up a great many things physical (<a href="http://camendesign.com/disentanglement">the MacBook Pro</a>), <a href="http://camendesign.com/dont_wanna">spiritual</a> and <a href="http://camendesign.com/passion_process">somewhere in between</a> (software projects), whilst the birth of my son forces me to address my many needs outside of the computer world. </p><p> This is where we get to the point. It will not have gone without your notice that I have neither been very active, or reliable for the last year. My more immediate needs in the real world overtake me and though even when there is the adequate time to devote to the website and other matters, I have neither the strength or will. </p><p> There is much I want to do, but it will have to wait for a few years whilst I care for my family. </p><p> I don’t expect to do much blogging here on the main site (my system isn’t working well for me under my new circumstances and it needs a complete overhaul). You can best be kept up to date with my happenings and thoughts through the system that does work well for my circumstances, <a href="http://forum.camendesign.com" rel="external">my forum</a>. </p><p> Thank you to all for your patience, understanding, generoisty and care. </p><p> Camen Design will return to <ins>ir</ins>regular service in a future iteration some day. What it will be is only a hazy idea right now, but I do know for certain that I will do my own things in my own way and demonstrate to others that they can do the same. </p> </section> <p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p> NoNonsense Forum http://camendesign.com/nononsense_forum http://camendesign.com/code/nononsense_forum Mon, 24 Dec 2012 13:55:00 +0000 <section> <h1>NoNonsense Forum</h1> <ol> <li><a href="#features">Features</a></li> <li><a href="#limits">Limitations</a></li> <li><a href="#help">Help Out</a></li> </ol> <aside> <strong>Update:</strong> v23 released: title lines in posts now self-link </aside> <p> <strong>NoNonsense Forum is a free, open source, PHP-based simple discussion forum.</strong> It favours removing barriers to conversation rather than massaging egos. </p><p> It is ideal if you want a discussion platform you can throw up in seconds, is quick and easy to administer, simple to use and works great as a private forum for a team working together. </p> <ul> <li><a href="http://forum.camendesign.com" rel="external">See it in action</a></li> <li><a href="https://github.com/Kroc/NoNonsenseForum/archive/master.zip" rel="external" type="application/zip">Download</a></li> <li><a href="https://github.com/Kroc/NoNonsenseForum/blob/master/HISTORY.txt" rel="external" type="text/plain">Change log</a></li> <li><a href="https://github.com/Kroc/NoNonsenseForum" rel="external">Source code</a> (GitHub)</li> <li><a href="http://camendesign.com/code/forums">Early history &amp; design goals</a></li> <li><a href="http://camendesign.com/worthing-digital">Watch a video about the design philosophy behind NoNonsense Forum</a></li> </ul> <h2 id="features">Features</h2> <dl> <dt>No Database</dt> <dd> All data is just RSS. Starting a new thread creates a new RSS feed. Replying adds an item to the feed. </dd> <dt>No hoops to jump through</dt> <dd> <p> I just want to talk, I don’t want to go through registration and e-mail confirmation and logging in each time. </p><p> NoNonsense Forum asks you for a name and password when you post. This name and password forms a unique token, protecting the name from other people using it. Every time you want to post, just use the same name and password to keep the same name. </p> <figure> <a href="http://camendesign.com/code/nononsense_forum/add.png" type="image/png"> <img src="http://camendesign.com/code/nononsense_forum/add.jpg" alt="Add new thread input form in NoNonsense Forum" width="600" height="387" /> </a> <figcaption>Add new thread input form — user name and password act as a unique token</figcaption> </figure> <p> This is not the same as registration because you can have as many different names as you want, you don’t have to pre-register a name and password, and you don’t have to go through e-mail confirmation and a login screen before you can do anything. </p><p> If your web browser remembers your name and password and fills it in for you, then all you do is type your message and submit. Nothing gets in your way. </p> </dd> <dt>Minimal Markup</dt> <dd> <p> I wouldn’t have written NoNonsense Forum if I believed that other forum software was doing it right, so likewise I did not opt for normal <a href="http://en.wikipedia.org/wiki/BBCode" rel="external">bbcode</a>. There is no preview function, and I believe that having one just adds hurdles to people posting. What comes out should be exactly what you expect. There should be no black-box that you fear will screw up your text in ways you don’t understand. </p><p> Want to insert a blockquote? There’s no quote button, just copy and paste and wrap in speech marks. </p> <pre>"How now brown cow?" Who knew pink moo?</pre> <p> Gives: </p> <figure> <a href="code/nononsense_forum/quote.png" type="image/png"> <img src="http://camendesign.com/code/nononsense_forum/quote.jpg" alt="Screenshot of styled blockquote" width="600" height="105" /> </a> </figure> <p> What’s more, the output blockquote includes quotes so that if you copy and paste this, the quote is maintained. Even when nested. </p><p> Pre-formatted text / monospace / source-code is also supported in simple fashion: </p> <pre>% CSS pre {overflow: auto; margin: 20px 0 0;} pre, code {font: 12.5px/20px Monaco, Consolas, monospace, monospace;} %</pre> <p> You can write anything after the first percent sign, it’s useful as a title or specifying the programming language. </p> <figure> <a href="code/nononsense_forum/code.png" type="image/png"> <img src="http://camendesign.com/code/nononsense_forum/code.jpg" alt="Screenshot of styled code block" width="600" height="149" /> </a> </figure> <p> Again, this just copy + pastes back into the text area and will output the same thing. </p> </dd> <dt>Mods and Rockers</dt> <dd> <p> There is no “edit” ability. Instead users can append to their existing posts. This allows them to note any corrections without the potential of changing facts, post-facto. </p> <figure> <a href="http://camendesign.com/code/nononsense_forum/append.png" type="image/png"> <img src="http://camendesign.com/code/nononsense_forum/append.jpg" alt="Append" width="600" height="149" /> </a> </figure> <p> Appending and deleting is done by clicking the relevant button on the post and providing the same name and password as was used to create that post. </p> <img src="http://camendesign.com/code/nononsense_forum/delete.png" alt="Delete button" width="260" height="41" /> <p> Despite no database, the forum does have the capability for moderators. Add names to a ‘mods.txt’ and they will be given the ability to append / delete any post or thread. Moderators can be set a global level, or only within sub-forums by creating a ‘mods.txt’ in sub-forums. (A sub-forum is just a folder) </p> <img src="http://camendesign.com/code/nononsense_forum/mods.png" alt="List of moderators" width="400" height="60" /> <p> A sign-in is provided for moderators (via HTTP auth), they will have to suffer the woes of logging-in <samp>:)</samp> </p> </dd> <dt>Access control</dt> <dd> <p> You can set a forum or its sub-forums to be locked according to two different policies: </p> <dl> <dt>Threads:</dt> <dd> Only moderators or members can start new threads, but anybody can reply. This could be used as, for example, a kind of blog / news system where by only site staff can post the news, but the public can comment on it. </dd> <dt>Posts:</dt> <dd> A read-only forum. Only moderators or members can start threads and replies, the public can read only. This would be typical of an archive or announcements sub-forum. </dd> </dl> <figure> <a href="http://camendesign.com/code/nononsense_forum/locks.png" type="image/png"> <img src="http://camendesign.com/code/nononsense_forum/locks.jpg" alt="Screenshot of different types of sub-forums" width="600" height="169" /> </a> </figure> <p> Just create a ‘locked.txt’ in the forum / sub-forum with the desired mode “<samp>threads</samp>”, “<samp>posts</samp>” or “<samp>private</samp>” written within. </p><p> Create a “members.txt” file with the names of users you want to be able to post / access the locked forum. Moderators (“mods.txt”) will always be able to post in any locked forum as well as use usual moderator capabilities. </p> </dd> <dt>Wide Browser Support</dt> <dd> <p> There shouldn’t be unnecessary hurdles added by browsers. NoNonsense Forum works out of the box on: </p> <ul> <li>IE6, 7, 8, 9+</li> <li>Firefox 3+</li> <li>Chrome, Safari</li> <li>Opera 9+</li> <li>Text-based browsers</li> </ul> <p> And is mobile-optimised too! </p> <img src="http://camendesign.com/code/nononsense_forum/iphone_thumb.png" alt="Screenshot of NoNonsense Forum on an iPhone" width="600" height="319" /> <p> Now with Opera Speed Dial support: </p> <img src="http://camendesign.com/code/nononsense_forum/opera_thumb.png" alt="Screenshot of Opera Speed Dial page showing off NoNonsense Forum integration" width="600" height="488" /> <p> How many forums do you know do this?—Instantly see if there’s new posts / replies each time you open your browser! </p> </dd> <dt>Customisable, Hackable</dt> <dd> <p> NoNonsense Forum provides many routes for customisation, like a basic config file for preferences, an ‘about.html’ file to add a description / extra HTML to each forum (without having to modify the template), a custom CSS file and <a href="http://camendesign.com/dom_templating">templates that are 100% HTML</a> so that you don’t have to know PHP or learn a <samp>{{special syntax}}</samp> to modify the look of your forum. </p><p> The <a href="https://github.com/Kroc/NoNonsenseForum" rel="external">PHP source code</a> is heavily commented and explained and free for you to learn from, modify and share. </p> </dd> </dl> <h2 id="limits">Limitations</h2> <dl> <dt>Scale</dt> <dd> <p> NoNonsense Forum does not scale well. It is not trying to solve that problem. It is designed so that you can throw up a forum in seconds and have something working with no configuration and nothing to manage. If your forum grows beyond practical limits, switch to a more complete forum package like <a href="http://phpbb.com" rel="external">phpBB</a>. </p><p> You might want to run a NoNonsense Forum on a private server or intranet, where scale won’t matter and NNF will be far less hassle than other softwares. </p> </dd> <dt>Abuse</dt> <dd> <p> I don’t believe that regular users should be treated the same as spammers, trolls and trouble-makers. This is why there is no <abbr>CAPTCHA</abbr>. No other forum software seems to have learnt that spam bots and regular users are not on the same level and don’t need the same “solutions”. Forums have registration, e-mail confirmation, <abbr>CAPTCHA</abbr>s and all sorts of hurdles and <strong>still</strong> get spammed. </p><p> My forum has none of these hurdles and doesn’t get bot-spammed <!-- ;) --> (not one for over a year). For the moment it is safe simply because it is unique and different and I’ve used some fresh tricks to confuse spam-bots. In the future they might catch up and I’ll have to adapt, but I won’t add more hurdles for users. </p><p> However, regardless of spam bots, because there is no ‘username = e-mail’ login, there’s nothing preventing one person using a different name every time and spamming the forum with posts. That is not a software problem, it’s an administration problem and you should ban the user’s IP using your server software / config panel / htaccess. </p><p> You could always use the access controls to protect the forum, or disable new users once you have the users you want. NoNonsense Forum is ideal in situations where you need a quick to throw-up, easy to administer private forum, rather than a large scale public forum with thousands of users (though I think it could technically handle it) </p><p> Again, should NNF no longer solve your problems it’s time to upgrade to a big software package with all the bells and whistles. </p> </dd> </dl> <h2 id="help">Help Out</h2> <p> Please try it out. </p><p> You should read the <a href="http://camendesign.com/forums#goals">goals of the project</a> to get an idea of what sort of features suits the flavour of NoNonsense Forum and what will simply be impossible given the design! </p><p> The most effective way to “get things done” is to <a href="https://github.com/Kroc/NoNonsenseForum/issues" rel="external">add an issue on GitHub</a>, you can also discuss NoNonsense Forum on—obviously—<a href="http://forum.camendesign.com" rel="external">the forum</a>. </p> </section> <p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p> rainbow-landing http://camendesign.com/rainbow-landing http://camendesign.com/photo/rainbow-landing Wed, 31 Oct 2012 08:02:00 +0000 <figure> <a href="http://camendesign.com/photo/rainbow-landing/DSC00257.jpg" type="image/jpeg"> <img src="http://camendesign.com/photo/rainbow-landing/DSC00257_preview.jpg" alt="" width="640" height="853" /> </a> </figure> <p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p> Transliteration http://camendesign.com/transliteration http://camendesign.com/code/transliteration Sat, 15 Sep 2012 08:43:00 +0000 <section> <h1>Transliteration</h1> <aside> <strong>Update:</strong> Reverted o-umlaut conversion to “<code>ö-&gt;o</code>” based on <a href="http://forum.camendesign.com/language_specific_transliteration_re_transliteration" rel="external">this discussion</a>. </aside> <p> <strong>Transliteration is when you replace individual characters in a piece of text with alternative characters</strong>, commonly used on the web to replace accented characters <ins>“<code>ẚèìòù</code>”</ins> with their ASCII equivilents <ins>“<code>aeiou</code>”</ins> to use as filenames or URLs. </p><p> This is my take on the subject that uses different levels of fallback to gaurantee a result whatever your environment: It transliterates better than any example snippet you have seen out there as will be explained in the code and afterwards. </p> <pre><code>//safeTransliterate v3, copyright (cc-by 3.0) Kroc Camen &lt;camendesign.com&gt; //generate a safe (a-z0-9_) string, for use as filenames or URLs, from an arbitrary string function safeTransliterate ($text) { //if available, this function uses PHP5.4's transliterate, which is capable of converting arabic, hebrew, greek, //chinese, japanese and more into ASCII! however, we use our manual (and crude) fallback *first* instead because //we will take the liberty of transliterating some things into more readable ASCII-friendly forms, //e.g. "100℃" &gt; "100degc" instead of "100oc" /* manual transliteration list: -------------------------------------------------------------------------------------------------------------- */ /* this list is supposed to be practical, not comprehensive, representing: 1. the most common accents and special letters that get typed, and 2. the most practical transliterations for readability; given that I know nothing of other languages, I will need your assistance to improve this list, mail kroc@camendesign.com with help and suggestions. this data was produced with the help of: http://www.unicode.org/charts/normalization/ http://www.yuiblog.com/sandbox/yui/3.3.0pr3/api/text-data-accentfold.js.html http://www.utf8-chartable.de/ */ static $translit = array ( 'a' =&gt; '/[ÀÁÂẦẤẪẨÃĀĂẰẮẴȦẲǠẢÅÅǺǍȀȂẠẬẶḀĄẚàáâầấẫẩãāăằắẵẳȧǡảåǻǎȁȃạậặḁą]/u', 'b' =&gt; '/[ḂḄḆḃḅḇ]/u', 'c' =&gt; '/[ÇĆĈĊČḈçćĉċčḉ]/u', 'd' =&gt; '/[ÐĎḊḌḎḐḒďḋḍḏḑḓð]/u', 'e' =&gt; '/[ÈËĒĔĖĘĚȄȆȨḔḖḘḚḜẸẺẼẾỀỂỄỆèëēĕėęěȅȇȩḕḗḙḛḝẹẻẽếềểễệ]/u', 'f' =&gt; '/[Ḟḟ]/u', 'g' =&gt; '/[ĜĞĠĢǦǴḠĝğġģǧǵḡ]/u', 'h' =&gt; '/[ĤȞḢḤḦḨḪĥȟḣḥḧḩḫẖ]/u', 'i' =&gt; '/[ÌÏĨĪĬĮİǏȈȊḬḮỈỊiìïĩīĭįǐȉȋḭḯỉị]/u', 'j' =&gt; '/[Ĵĵǰ]/u', 'k' =&gt; '/[ĶǨḰḲḴKķǩḱḳḵ]/u', 'l' =&gt; '/[ĹĻĽĿḶḸḺḼĺļľŀḷḹḻḽ]/u', 'm' =&gt; '/[ḾṀṂḿṁṃ]/u', 'n' =&gt; '/[ÑŃŅŇǸṄṆṈṊñńņňǹṅṇṉṋ]/u', 'o' =&gt; '/[ÒÖŌŎŐƠǑǪǬȌȎȪȬȮȰṌṎṐṒỌỎỐỒỔỖỘỚỜỞỠỢØǾòöōŏőơǒǫǭȍȏȫȭȯȱṍṏṑṓọỏốồổỗộớờởỡợøǿ]/u', 'p' =&gt; '/[ṔṖṕṗ]/u', 'r' =&gt; '/[ŔŖŘȐȒṘṚṜṞŕŗřȑȓṙṛṝṟ]/u', 's' =&gt; '/[ŚŜŞŠȘṠṢṤṦṨſśŝşšșṡṣṥṧṩ]/u', 'ss' =&gt; '/[ß]/u', 't' =&gt; '/[ŢŤȚṪṬṮṰţťțṫṭṯṱẗ]/u', 'th' =&gt; '/[Þþ]/u', 'u' =&gt; '/[ÙŨŪŬŮŰŲƯǓȔȖṲṴṶṸṺỤỦỨỪỬỮỰùũūŭůűųưǔȕȗṳṵṷṹṻụủứừửữựµ]/u', 'v' =&gt; '/[ṼṾṽṿ]/u', 'w' =&gt; '/[ŴẀẂẄẆẈŵẁẃẅẇẉẘ]/u', 'x' =&gt; '/[ẊẌẋẍ×]/u', 'y' =&gt; '/[ÝŶŸȲẎỲỴỶỸýÿŷȳẏẙỳỵỷỹ]/u', 'z' =&gt; '/[ŹŻŽẐẒẔźżžẑẓẕ]/u', //combined letters and ligatures: 'ae' =&gt; '/[ÄǞÆǼǢäǟæǽǣ]/u', 'oe' =&gt; '/[Œœ]/u', 'dz' =&gt; '/[DŽDžDZDzdždz]/u', 'ff' =&gt; '/[ff]/u', 'fi' =&gt; '/[ffifi]/u', 'ffl' =&gt; '/[fflfl]/u', 'ij' =&gt; '/[IJij]/u', 'lj' =&gt; '/[LJLjlj]/u', 'nj' =&gt; '/[NJNjnj]/u', 'st' =&gt; '/[ſtst]/u', 'ue' =&gt; '/[ÜǕǗǙǛüǖǘǚǜ]/u', //currencies: 'eur' =&gt; '/[€]/u', 'cents' =&gt; '/[¢]/u', 'lira' =&gt; '/[₤]/u', 'dollars' =&gt; '/[$]/u', 'won' =&gt; '/[₩]/u', 'rs' =&gt; '/[₨]/u', 'yen' =&gt; '/[¥]/u', 'pounds' =&gt; '/[£]/u', 'pts' =&gt; '/[₧]/u', //misc: 'degc' =&gt; '/[℃]/u', 'degf' =&gt; '/[℉]/u', 'no' =&gt; '/[№]/u', 'tm' =&gt; '/[™]/u' ); //do the manual transliteration first $text = preg_replace (array_values ($translit), array_keys ($translit), $text); //flatten the text down to just a-z0-9 and dash, with underscores instead of spaces $text = preg_replace ( //remove punctuation //replace non a-z //deduplicate //trim underscores from start &amp; end array ('/\p{P}/u', '/[^_a-z0-9-]/i', '/_{2,}/', '/^_|_$/'), array ('', '_', '_', ''), //attempt transliteration with PHP5.4's transliteration engine (best): //(this method can handle near anything, including converting chinese and arabic letters to ASCII. // requires the 'intl' extension to be enabled) function_exists ('transliterator_transliterate') ? transliterator_transliterate ( //split unicode accents and symbols, e.g. "Å" &gt; "A°": 'NFKD; '. //convert everything to the Latin charset e.g. "ま" &gt; "ma": //(splitting the unicode before transliterating catches some complex cases, // such as: "㏳" &gt;NFKD&gt; "20日" &gt;Latin&gt; "20ri") 'Latin; '. //because the Latin unicode table still contains a large number of non-pure-A-Z glyphs (e.g. "œ"), //convert what remains to an even stricter set of characters, the US-ASCII set: //(we must do this because "Latin/US-ASCII" alone is not able to transliterate non-Latin characters // such as "ま". this two-stage method also means we catch awkward characters such as: // "㏀" &gt;Latin&gt; "kΩ" &gt;Latin/US-ASCII&gt; "kO") 'Latin/US-ASCII; '. //remove the now stand-alone diacritics from the string '[:Nonspacing Mark:] Remove; '. //change everything to lowercase; anything non A-Z 0-9 that remains will be removed by //the letter stripping above 'Lower', $text) //attempt transliteration with iconv: &lt;php.net/manual/en/function.iconv.php&gt; : strtolower (function_exists ('iconv') ? str_replace (array ("'", '"', '`', '^', '~'), '', strtolower ( //note: results of this are different depending on iconv version, // sometimes the diacritics are written to the side e.g. "ñ" = "~n", which are removed iconv ('UTF-8', 'US-ASCII//IGNORE//TRANSLIT', $text) )) : $text) ); //old iconv versions and certain inputs may cause a nullstring. don't allow a blank response return !$text ? '_' : $text; }</code></pre> <p> <small><a href="https://gist.github.com/3551351" rel="external">Edit this code</a> on GitHub &bull; You may do anything with this code as long as you leave credit in the code</small> <br /><br /> There is no ‘guaranteed available’ method with which to transliterate effectively in PHP; the functions that can do so vary by PHP version and are not likely to always be installed and enabled on every PHP server out there. The only built-in function <a href="http://uk.php.net/manual/en/function.strtr.php" rel="external"><samp>strtr</samp></a> guaranteed to be present only replaces one character with another and doesn’t handle the common need to expand one character to multiple, such as converting “<code>ß</code>” to “<code>ss</code>”. </p><p> There are a number of libraries and functions out there for transliteration. Those that are comprehensive are massive and therefore total overkill for a small project and often have unhelpful licences, those that are small and compact are usually very incomplete and rely upon a single method that might not be available to you. </p><p> My method uses fallbacks so as to guarantee a result and improves upon other methods out there: </p> <dl> <dt>Better unicode normalisation</dt> <dd> All the transliterations examples I have seen out there that use PHP5.4′s transliterator make the mistake of copy-pasting the example normalisation string given on the website. The results are simply wrong. My code uses a unicode normalisation method that I have worked out that has not been used anywhere else I have seen. It handles thousands of cases that no other library/function does because it uses two-stages of transliteration, first from any script to Latin-1 and then from Latin-1 to US-ASCII. This means that characters such as “<code>㏀</code>” become “<code>ko</code>” where else this would fail on other code I have seen because it would transliterate “<code>㏀</code>” to “<code>kΩ</code>” and then remove the “<code>Ω</code>” for being non-ASCII </dd> <dt>Readability</dt> <dd> Since the transliteration will be used for filenames or URLs, readability is important, so it’s better to convert some things to more meaningful words such as “<code>¥</code>” to “<code>yen</code>” instead of just “<code>y</code>”. In the example above, it would be trivial for the function to convert “<code>㏀</code>” to “<code>k-ohm</code>” but I chose not to since the single character “<code>㏀</code>” is almost never used on the `Web </dd> </dl> <hr /> <p> <strong>I don’t know anything about other languages and writing systems</strong>, so if there’s something amiss in my code, please let me know <a href="mailto:kroc@camendesign.com">via email</a>, <a href="http://forum.camendesign.com" rel="external">the forums</a> or by editing the <a href="https://gist.github.com/3551351" rel="external">GitHub gist</a> </p> </section> <p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p> Announcing PhotoDemon: A Fast, Free, Open-Source Image Processor http://camendesign.com/photodemon http://camendesign.com/link/photodemon Sat, 11 Aug 2012 15:42:00 +0000 <h1>Announcing PhotoDemon:<br />A Fast, Free, Open-Source Image Processor</h1> <a href="http://www.tannerhelland.com/4183/photodemon-image-processor-vb6/" rel="external">tannerhelland.com/4183/photodemon-image-processor-vb6/</a> <p> Myself, and my good friend Tanner Helland grew up in <a href="http://camendesign.com/kroc_writes">a scene</a> that revolved around <a href="https://en.wikipedia.org/wiki/Visual_basic" rel="external">Visual Basic 6</a>. </p><p> Much hated by “real programmers” for not being a “real language”, Visual Basic 6 struck a chord with the common populous unlike anything since. For a very business-orientated product, a huge scene grew up around teens experimenting with the language. </p><p> For all the hate it got, my choice in using it was one of the wisest I made. Software I wrote over 10 years ago still works now and will still work throughout the lifespan of Windows 8. Those who adopted <a href="https://en.wikipedia.org/wiki/.NET_Framework" rel="external">.NET</a> could not say the same and have been dragged along Microsoft’s eclectic upgrade-train, with every new idea thrown in along the way by a directionless Microsoft failing to practice anything they preach (.NET is the future! Silverlight is the future! C# is the future! HTML5 is the future!) </p><p> So, it is of deep personal joy for me to <a href="http://www.tannerhelland.com/4183/photodemon-image-processor-vb6/" rel="external">link you to</a> some 12 year old software which has only just been released! </p><p> I have known PhotoDemon for a long time. Tanner has been sending me copies for over a decade. The wolrd may have changed rapidly around me in those years—Apple went from dead to the <abbr title="number ">No.</abbr>1 company in the world and Microsoft lost their grip on the ’Web—but PhotoDemon has matured slowly, like a fine wine. </p><p> Application design in general has changed fads immensely since the days of PhotoDemon <ins>Tanner has included some nice old screenshots showing just how immensely!</ins> and though what Tanner started out with couldn’t be further removed from the “apps” of today, the recent changes in UI leading up to this release have been most profound. In 2003 I sent Tanner a massive 20-page e-mail detailing everything I could criticise on the UI-front. In those nine years my design skills have changed irrevocably so I understand how making PhotoDemon look so good today <ins>for, let’s remind ourselves, a VB6 program</ins> is such an achievement for Tanner who never admitted to knowing much about UI. </p><p> So, <a href="http://www.tannerhelland.com/4183/photodemon-image-processor-vb6/" rel="external">go check out</a> Tanner’s announcement, read the history and see the screenshots of a program 12 years in the making! </p><p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p> The Word to End All Words http://camendesign.com/end-of-words http://camendesign.com/poem/end-of-words Wed, 01 Aug 2012 12:45:00 +0000 <h1>The Word to End All Words</h1> <p> Like a black monolith you stand.<br /> Thy fearful symmetry. </p><p> Upon you is engraved a single word; a magic word,<br /> More powerful than the utterance of <a href="https://en.wikipedia.org/wiki/White_Witch" rel="external">Jadis</a>, the queen of <a href="https://en.wikipedia.org/wiki/Charn" rel="external">Charn</a>,<br /> who ended all life in her world by the <a href="https://en.wikipedia.org/wiki/Deplorable_Word" rel="external">Deplorable Word</a>. </p><p> It is a word they do not understand.<br /> From birth they have not known it.<br /> It has not been taught, and neither has it been searched for. </p><p> By your word kingdoms have been flattened,<br /> Dynasties felled, endless wars begun.<br /> Lives torn apart and thrown asunder. </p><p> They do not know how to comprehend it.<br /> It confuses them, frustrates them.<br /> They bash against you, enraged at your silence.<br /> </p><p> <q>Why do you not respond!?</q> </p><p> But you do not respond.<br /> Your silence causes all their machinations to fall apart;<br /> The noise their livelihoods have come to depend upon. </p><p> When your silence has drowned out their noise,<br /> Then peace and quiet will ring out everywhere.<br /> For you are the word to end all words: No. </p><p> <small><a href="http://forum.camendesign.com/" rel="external">Discuss this in the forum</a></small> </p>