tag:blogger.com,1999:blog-11630626317953974952024-03-07T20:15:35.756-08:00SV ExpertiseAnonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.comBlogger23125tag:blogger.com,1999:blog-1163062631795397495.post-16445358226119170142017-10-06T16:22:00.001-07:002017-10-06T16:22:04.251-07:00What The Internet Never Told You About Building A Login SystemThe Internet is replete with "how to build a login form" and "which password hashing algorithm to choose" articles but strangely silent on how to build a professional (well, maybe, semi-professional) login system.<div>
<br /></div>
<div>
Any login system has two sides: the client side and the server side. The client side is easier so we'll start there.</div>
<div>
<br /></div>
<div>
To login, a user should provide a username or email address and a password. For your system, well, which should it be: a username or an email address (or maybe a choice)?</div>
<div>
<br /></div>
<div>
Email addresses are more consistent and, chances are, you'll want to verify accounts by requiring a verified email address. On the other hand, usernames are shorter and have a little more flair though a user is far more likely to forget their username than their email address.</div>
<div>
<br /></div>
<div>
Many clients send the password in the clear to the server. But, when I researched the issue, I opted for a more sophisticated approach. It is:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">sha256('yoursite.com'+'user@emailaddress.com'+'password')</span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">sha256('yoursite.com'+'user'+'password')</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
SHA-256 is a rather insecure hashing algorithm. The point here is not to secure the password so much as invalidate an attacker's database of SHA-256 hashes and force him to generate a new one. An attacker might already have a huge database of SHA-256 password hashes. But, by adding the username or email address to the password string and then doing the SHA-256 hash, an attacker will have to have a much, much larger database of SHA-256 username-password hashes. Adding an arbitrary string, such as the name of your web site, to the username-password will force an attacker to generate a new database of SHA-256 hashes customized for your login.</div>
<div>
<br /></div>
<div>
This hash is sent from the client to the server as the user's "password". If an attacker steals your database and recovers the client-hashed password, they will be able to login to your site but the password won't work at any other site. The arbitrary string will ensure that the hashes are different. The attacker will be forced to recover the plain text password.</div>
<div>
<br /></div>
<div>
Having client-side SHA-256 hashes, even with usernames and arbitrary strings, isn't enough. You are protecting other sites (a little bit) from being vulnerable to broken into if your password database is stolen. But you are providing no protection for yourself (or your admin accounts) and SHA-256 is far too weak to be effective all on its own. Enter server side hashing.</div>
<div>
<br /></div>
<div>
Like I said earlier, the server side of a login system is more complicated than the client side of the login system. What server side hashing algorithm should you use?</div>
<div>
<br /></div>
<div>
MD5, SHA-256, PBKDF2, bcrypt, scrypt or Argon2?</div>
<div>
<br /></div>
<div>
Well, no. You shouldn't choose a hashing algorithm at all.</div>
<div>
<br /></div>
<div>
Hashing algorithms and hardware change over time and, since you are building professional system, meant to adapt over the years, your server should support multiple algorithms. Your login system, at least the server side, has three requirements:</div>
<div>
<ol>
<li>Hashing algorithms must be assignable per user, not one for the entire system</li>
<li>Each user's hashing algorithm has to be changeable into any other algorithm</li>
<li>Adding a new hashing algorithm should only require one function to be changed</li>
</ol>
<div>
No doubt, your database has a "password" column that contains the user's hashed password. Having a password in a single string is extraordinarily convenient. Rather than storing hashing algorithms in a separate column, it is best to store the hashing algorithm in the same column as the hashed password, as part of the same string. Ideally, you'd use a standard password string format.</div>
</div>
<div>
<br /></div>
<div>
As far as I'm aware, there is only one such format in wide use: that's the Linux /etc/shadow password format (a.k.a. the shadow password format). It looks like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">$algorithm$arguments$saltthenpasswordhash</span></div>
<div>
<br /></div>
<div>
The <i>algorithm</i> field indicates the algorithm:</div>
<div>
<br /></div>
<div>
1=MD5</div>
<div>
2=Blowfish</div>
<div>
5=SHA-256</div>
<div>
6=SHA-512</div>
<div>
2a,2b,2y=bcrypt</div>
<div>
pbkdf2=PBKDF2</div>
<div>
scrypt=scrypt</div>
<div>
argon2d=Argon2d</div>
<div>
argon2i=Argon2i</div>
<div>
<br /></div>
<div>
The <i>arguments</i> field might not exist or might contain algorithm arguments. bcrypt requires a work factor. Argon2 has 3 different arguments: iterations, memory and threads.</div>
<div>
<br /></div>
<div>
The <i>saltthenpasswordhash</i> is the salt with the hashed password for this user. Other articles (by other people) describe the reasons why each password should have its own salt and not a global salt. Nowadays, it is common for libraries to automatically generate a salt when creating new hashes so there is less reason for me to go over those arguments here. Since salts are generated along with the hash, it is easier to do it the right way and harder to do it the wrong way, with a global salt.</div>
<div>
<br /></div>
<div>
Storing all this information in a single string in the "password" column of your database using the standard /etc/shadow format will satisfy the first requirement that hashing algorithms be assignable per user.</div>
<div>
<br /></div>
<div>
For new users, I use a global variable to indicate the hashing algorithm that a new user should use so, when a new user is created, their password hash and their hashing algorithm (from the global variable at the time the account was created) is stored together.</div>
<div>
<br /></div>
<div>
When a user logs in, they use the hashing algorithm in their password string from the "password" column of the database to hash the SHA-256 client-side hash. Then, the hash from the login attempt is compared to the hash from the database, preferably using the special hash comparison function from the encryption library (to blunt timing attacks). If the hashes are not equal, the login attempt fails.</div>
<div>
<br /></div>
<div>
If the hashes are equal, the login attempt succeeds but you're not quite done yet. The user's current hashing algorithm is compared to the hashing algorithm in the global variable. If they are not the same, the client-side SHA-256 hash (which has already be validated to contain the correct password) is hashed using the global variable's hashing algorithm. The result, in shadow password format, is stored into the "password" column: the user has been upgraded on-the-fly to the latest algorithm.</div>
<div>
<br /></div>
<div>
The global variable provides the ability to "turn up" and "turn down" security by specifying different algorithms. When the global variable is changed into a stronger (slower) algorithm, new users and any user logging in will be upgraded to the new algorithm seamlessly. If you make a mistake and deploy an algorithm that bogs down your server, you can "turn down" security merely by changing that algorithm global variable. Existing users will undergo a one-time delay when they login using the too-slow algorithm and are downgraded to a faster algorithm.</div>
<div>
<br /></div>
<div>
Designing your login system such that a single function provides hashing new passwords, hashing a password with a given salt (for login checks) and comparing two hashes will allow you to deploy new hashing algorithms over the years by changing one function.</div>
<div>
<br /></div>
<div>
Rather than just sending passwords in the clear and hardcoding MD5 (oh, Yahoo!, will you never learn?!), a login system with client-side hashes and upgradable server-side hashing algorithms will provide you a basic framework for the future.</div>
<div>
<br /></div>
<div>
I don't claim these practices to be the best or even adequate. I'm not a security researcher and don't claim to be one. But SHA-256 client side hashes, the shadow password format and easy deployment of new server side hashing algorithms are important issues that are worth debating and considering. And, hopefully, will give you a place to start the conversation.</div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com1tag:blogger.com,1999:blog-1163062631795397495.post-59987393739338741292017-09-22T13:45:00.000-07:002017-09-22T14:37:59.414-07:00AngularJS to React/Redux and back againI've found AngularJS to have many analogues (corresponding pieces) to React/Redux. Let me show you.<br />
<br />
A Redux state almost exactly corresponds to an AngularJS $scope variable.<br />
<br />
In a Redux reducer file, you have an initial state that looks like this:<br />
<pre style="font-family: Menlo; font-size: 9pt;"><span style="color: navy; font-weight: bold;">const </span><span style="color: #458383;">initialState </span>= {
<span style="color: #660e7a; font-weight: bold;">inited</span>: <span style="color: navy; font-weight: bold;">false</span>,
<span style="font-size: 9pt;"> </span><span style="color: #660e7a; font-size: 9pt; font-weight: bold;">loading</span><span style="font-size: 9pt;">: </span><span style="color: navy; font-size: 9pt; font-weight: bold;">true</span><span style="font-size: 9pt;">,</span>
<span style="color: #660e7a; font-weight: bold;">user</span>: {
<span style="color: #660e7a; font-weight: bold;">first</span>: <span style="color: green; font-weight: bold;">''</span>,
<span style="color: #660e7a; font-weight: bold;">last</span>: <span style="color: green; font-weight: bold;">''</span>,
<span style="color: #660e7a; font-weight: bold;">email</span>: <span style="color: green; font-weight: bold;">''</span><span style="color: navy; font-weight: bold;">
</span><span style="color: navy; font-weight: bold;"> </span>},
<span style="color: #660e7a; font-weight: bold;">error</span>: <span style="color: green; font-weight: bold;">''</span>
}
</pre>
<pre style="font-family: Menlo; font-size: 9pt;"></pre>
This initial state almost directly corresponds to an AngularJS $scope initial state. If it doesn't, you can easily move the AngularJS variables that are outside the $scope variable into the $scope variable to match up exactly.<br />
<br />
Not every AngularJS controller has an "inited" variable but many do. In Redux, an "inited" variable is essential to signal that the Redux state has not been populated by data from the backend yet. An easy way to do this is to check the "inited" prop in the <span style="color: #7a7a43; font-family: "menlo"; font-size: 9pt;">componentDidMount()</span> method in the React component that requires it (or any React component that requires it) and, if it is false, load up the data and set the "inited" value (well, call an action to call a reducer to do it) to <span style="color: navy; font-family: "menlo"; font-size: 9pt; font-weight: bold;">true</span>. Even if you don't have an "inited" variable in your AngularJS $scope variables, it is easy to add one. The AngularJS controller function might be converted like this:<br />
<pre style="font-family: Menlo; font-size: 9pt;"><span style="color: #7a7a43;">componentDidMount</span>() {
<span style="color: navy; font-weight: bold;">if </span>(!<span style="color: navy; font-weight: bold;">this</span>.<span style="color: #660e7a; font-weight: bold;">props</span>.<span style="color: #660e7a; font-weight: bold;">inited</span>) {
<span style="color: grey; font-style: italic;">// do this only once</span>
<span style="color: navy; font-weight: bold;">this</span>.<span style="color: #660e7a; font-weight: bold;">props</span>.<span style="color: #7a7a43;">model</span>(<span style="color: green; font-weight: bold;">'inited'</span>, <span style="color: navy; font-weight: bold;">true</span>)
<span style="color: grey; font-style: italic;">// in AngularJS, this is the controller function</span>
getUser(<span style="color: navy; font-weight: bold;">this</span>.<span style="color: #660e7a; font-weight: bold;">props</span>)
}
}</pre>
Speaking of Redux reducers and so on (and since I brought it up in the code directly above), it is very convenient and useful to make a "model" action along the lines of an AngularJS ng-model. The "model" action will take a string indicating the value to change, e.g. <span style="font-family: "menlo"; font-size: 9pt;">props.</span><span style="color: #7a7a43; font-family: "menlo"; font-size: 9pt;">model</span><span style="font-family: "menlo"; font-size: 9pt;">(</span><span style="color: green; font-family: "menlo"; font-size: 9pt; font-weight: bold;">'user.first'</span><span style="font-family: "menlo"; font-size: 9pt;">, </span><span style="color: green; font-family: "menlo"; font-size: 9pt; font-weight: bold;">'John'</span><span style="font-family: "menlo"; font-size: 9pt;">)</span>, and ultimately create something that looks like this:<br />
<pre style="font-family: Menlo; font-size: 9pt;"><<span style="color: navy; font-weight: bold;">input </span><span style="color: blue; font-weight: bold;">type</span><span style="color: green; font-weight: bold;">="text" </span><span style="color: blue; font-weight: bold;">value</span><span style="color: green; font-weight: bold;">=</span>{ props.<span style="color: #660e7a; font-weight: bold;">user</span>.<span style="color: #660e7a; font-weight: bold;">first </span>}
<span style="color: blue; font-weight: bold;">onChange</span><span style="color: green; font-weight: bold;">=</span>{e => props.<span style="color: #7a7a43;">model</span>(<span style="color: green; font-weight: bold;">'user.first'</span>, e.<span style="color: #660e7a; font-weight: bold;">target</span>.<span style="color: #660e7a; font-weight: bold;">value</span>)}/></pre>
Often, I've found it easy to copy the HTML from my AngularJS templates (HTML) into the React render() method and fix them up. There are a three tricks that you have to remember:<br />
<ol>
<li>Do a global search and replace on "class=" to replace with "className=". Before you make this change, your React component may render but will not apply the CSS classes.</li>
<li>Search for each "style=" (e.g. style="background: blue") and convert each instance (e.g. style={ { background: 'blue' } }). (Don't forget to make it a JavaScript object by having the double curly braces.) . Really, if your AngularJS still contains "style" attributes, it's just better to convert them into CSS classes and "class" attributes rather than go through the hassle of converting them to React. Before you make this change, your React component will often render a blank screen; this is a big hint that you have unconverted "style" attributes.</li>
<li>Change each AngularJS template to have a single root element, not multiple root elements. Multiple root elements are allowed in AngularJS but there is no reason to use them and it is easy enough to just wrap it all in a div tag so it is easy to convert to React. React components (specifically, JSX and the render() method) require a single root element.</li>
</ol>
<div>
react-router-redux is a pretty convenient analogue to AngularJS router modules (either the built-in or ui-router).</div>
<div>
<br /></div>
<div>
Factories and services can be stripped of their AngularJS specifics and placed in your React application's modules folder. Usually, I place them in a subfolder of modules. Often, factories and services use the AngularJS $http service; you can convert these calls to use fetch (from the isomorphic-fetch npm module) or Axios (or superagent or request or whatever).<br />
<br />
AngularJS filters are essentially just functions; I just strip out the code and drop them into the modules folder itself. A call to a filter is easy to convert. In AngularJS, a filter looks like this:<br />
<pre style="font-family: Menlo; font-size: 9pt;">{{ <span style="color: green; font-weight: bold;">'mytitle'</span> | translate }}</pre>
In React, it becomes:<br />
<pre style="font-family: Menlo; font-size: 9pt;">{ <span style="font-style: italic;">translate</span>(<span style="color: green; font-weight: bold;">'mytitle'</span>) }
</pre>
In AngularJS, we have the "ng-if" and "ng-repeat" attributes which are used a lot in almost every AngularJS application to do control flow.<br />
<br />
The AngularJS "ng-if" attribute is used like this:<br />
<pre style="font-family: Menlo; font-size: 9pt;"><<span style="color: navy; font-weight: bold;">div </span><span style="color: blue; font-weight: bold;">ng-if=</span><span style="color: green; font-weight: bold;">"</span><span style="color: #660e7a; font-weight: bold;">showing</span><span style="color: green; font-weight: bold;">"</span>>
My title
</<span style="color: navy; font-weight: bold;">div</span>></pre>
In React, it becomes a conditional, like this:
<br />
<pre style="font-family: Menlo; font-size: 9pt;">{ props.<span style="color: #660e7a; font-weight: bold;">showing</span>? (
<<span style="color: navy; font-weight: bold;">div</span>>
My title
</<span style="color: navy; font-weight: bold;">div</span>>
) : <span style="color: green; font-weight: bold;">'' </span>}</pre>
An AngularJS "ng-repeat" attribute is a little more complicated. From this:<br />
<pre style="font-family: Menlo; font-size: 9pt;"><<span style="color: navy; font-weight: bold;">table</span>>
<<span style="color: navy; font-weight: bold;">thead</span>>
<<span style="color: navy; font-weight: bold;">tr</span>>
<<span style="color: navy; font-weight: bold;">th </span><span style="color: blue; font-weight: bold;">ng-repeat=</span><span style="color: green; font-weight: bold;">"</span><span style="color: #660e7a; font-style: italic; font-weight: bold;">column</span><span style="color: green; font-weight: bold;"> in </span><span style="color: #660e7a; font-weight: bold;">columns</span><span style="color: green; font-weight: bold;">"</span>>
{{ column }}
</<span style="color: navy; font-weight: bold;">th</span>>
</<span style="color: navy; font-weight: bold;">tr</span>>
</<span style="color: navy; font-weight: bold;">thead</span>>
</<span style="color: navy; font-weight: bold;">table</span>>
</pre>
To this in React:
<br />
<pre style="font-family: Menlo; font-size: 9pt;"><<span style="color: navy; font-weight: bold;">table</span>>
<<span style="color: navy; font-weight: bold;">thead</span>>
<<span style="color: navy; font-weight: bold;">tr</span>>
{ props.<span style="color: #660e7a; font-weight: bold;">columns</span>.<span style="color: #660e7a; font-weight: bold;">length</span>? props.<span style="color: #660e7a; font-weight: bold;">columns</span>
.<span style="color: #7a7a43;">map</span>((column, index) => (
<<span style="color: navy; font-weight: bold;">th </span><span style="color: blue; font-weight: bold;">key</span><span style="color: green; font-weight: bold;">=</span>{ index }>
{ column }
</<span style="color: navy; font-weight: bold;">th</span>>)
).<span style="color: #7a7a43;">reduce</span>((p, c) => [p, <span style="color: green; font-weight: bold;">''</span>, c]) : <span style="color: green; font-weight: bold;">''</span>}
</<span style="color: navy; font-weight: bold;">tr</span>>
</<span style="color: navy; font-weight: bold;">thead</span>>
</<span style="color: navy; font-weight: bold;">table</span>>
</pre>
Admittedly, this post is a little slapdash and not really enough to convert an entire AngularJS to React/Redux. But I think that you will agree that there are a lot of concepts that easily port from AngularJS to Redux with a little tweaks, here and there.
</div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-27817694294907136292017-09-20T16:00:00.000-07:002017-09-20T16:03:24.363-07:00What's wrong with Ember.js?Once you have React and Angular in your toolkit, should you concern yourself with Ember.js?<br />
<br />
Executive summary: No.<br />
<br />
React comes from Facebook; Angular comes from Google. If you live in the Silicon Valley like I do, Facebook and Google cast a long shadow. Many companies, large and small, "cargo cult" Facebook and Google; that is, they adopt technology blindly based on what Facebook and Google do, hoping that that imitating success will yield real success. Many startups adopt "hot" technologies to appeal to venture capitalists ("hey, we're just like Google!") and to garner attention from developers and the press.<br />
<br />
Right now (9/20/2017), React is in the cat bird seat. React developers command the extravagant salaries and most of the pursuit by recruiters. In contrast, AngularJS (1.x) is a workhorse: valued by many companies but also some willing to leave positions open rather than paying a premium and others paying a limited premium in developer salaries. Angular (2/4) is rarely seen: companies who adopted Angular (2/4) seem oddly out of touch, like they didn't get the memo, and seem to have little interest in paying salary premiums.<br />
<br />
Where is Ember.js?<br />
<br />
Ember.js is at LinkedIn, a respectable name to be sure, but hardly the influencer that Google and Facebook are. Ember.js is also seen at the occasional AI or hardware startup. No salary premium for Ember.js developers: those companies think that being an Ember.js developer shows that you are a free-thinking hippie that cares more about concepts than money.<br />
<br />
Ember.js gets an honorable mention by those job postings that read, "must know at least one of React, AngularJS, Ember.js or other JavaScript framework". Hey, that's something: Vue.js doesn't get this kind of respect. But we all know that, when a job posting reads like that, it really means, "Hell, we're don't know jack about any of these JavaScript frameworks so, if you've done any of it, it's all the same to us."<br />
<br />
Recently, I did a multi-day investigation and technical review on Ember.js and, while Ember.js seems technically competitive with React and Angular in some respects, there was no indication that they were ever going to emerge from their rather distant third place, open source, hippie-dippie "alternative" framework status. Having recently reviewed Backbone.js, I see a lot of intellectual inheritance from Backbone.js in Ember.js and, believe me, that's not a compliment. That smell of Backbone.js in Ember.js makes it feel ... old.<br />
<br />
I have to give the Ember.js team respect for hanging it there all this time. But, sorry, I will not be staking my future on Ember.js, either now or anytime soon. Nor will I be considering any Ember.js positions. And I'd advise others not to, either.<br />
<br />
But, hey, Ember.js, if, someday, you break through, give me a call. I'm in the book.Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com1tag:blogger.com,1999:blog-1163062631795397495.post-14300953803187184532017-09-19T11:39:00.001-07:002017-09-19T13:58:28.659-07:00What was Backbone.js and why did it die?<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-size: 14.6667px; font-weight: normal;">As AngularJS (Angular 1.x), Angular (Angular 4) and React rose, Backbone.js declined. What was Backbone.js? Why did it decline?</span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-size: 14.6667px; font-weight: normal;"><br /></span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial";"><span style="font-size: 14.6667px; white-space: pre-wrap;">Let's just dive in and look at some Backbone.js design and features.</span></span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">Backbone.View.extend(), Backbone.Model.extend(), Backbone.Router.extend(), etc.</span><br />
<span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;">Backbone.js is based on a Java-style class structure with classes, objects and inheritance. It makes a conscious choice to embrace this approach, instead of JavaScript prototypical inheritance. The </span><span style="font-family: "courier new" , "courier" , monospace; font-size: 14.6667px; white-space: pre-wrap;">extend()</span><span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;"> method creates a subclass of the "class" which it is invoked upon. While other frameworks adopt this approach as well (usually by upgrading to TypeScript or ES6) for their view classes, Backbone.js is rather dogmatic about it and, as we will soon see, it requires a lot more classes and a lot more objects whereas other frameworks were more limited and less, well, so darned adamant about creating all those things.</span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">_.bindAll() call in a Backbone.View constructor</span><br />
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">When a new Backbone.View class is created, its constructor often contains multiple calls to </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">_.bind()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> or a single call to </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">_.bindAll()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">. I have written about the function of </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">_.bind()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> before so I won't repeat that here. OK, OK, I'll repeat it a little. </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">_.bind()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> converts a method into a standalone function call. In Backbone.js, this is often required because event handlers only work with functions, not with methods. Backbone.js uses events extensively so the </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">_.bind()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> calls are necessary. Calls to </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">_.bind()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> and </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">_.bindAll()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> confuse many people and cause odd errors when not used so Backbone.js got a reputation as being confusing.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">React requires a </span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">bind()</span></span><span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> call in some but not all circumstances but it is not designed around events in the same way that Backbone.js is. The use of </span><span style="font-size: 14.6667px; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">_.bind()</span></span><span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;"> and </span><span style="font-size: 14.6667px; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">_.bindAll()</span></span><span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;"> isn't a deal killer but certainly doesn't help Backbone.js' reputation.</span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">render() method of a Backbone.View</span><br />
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">Every Backbone.View class has a </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">render()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> method, similar to React. The </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">render()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> method in Backbone.js is usually a string concatenator, something like an old-fashioned version of React, before JSX was widely adopted. In Backbone.js, it is additionally encouraged to call the </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">render()</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> methods of child Backbone.View objects, take the returned strings and insert them in the correct position in the parent Backbone.View string. All this string concatenation feels very awkward and old-fashioned.</span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">events property of a Backbone.View</span><br />
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">The </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">events</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> property looks something like this:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: 11pt; white-space: pre-wrap;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: 11pt; white-space: pre-wrap;">events: {</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;"> 'click button#add': 'addItem'</span></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">}</span></span><br />
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">It is a map with magic strings that makes nobody happy. The “click” refers to the </span><span style="color: black; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">onclick</span></span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> event; the “on” prefix is dropped. "button#add” is a jQuery selector. “addItem” is the method to invoke on the current object.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Backbone.js is the one framework that separated event handlers from the HTML. This approach provides no benefit to the developer and is somewhat inconvenient and buggy.</span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">Backbone.Model</span><br />
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;">Backbone.js separates views and data; Backbone.Model was the base class for data. Instead of working with plain old JSON objects like many other frameworks, Backbone.js expects JSON objects (from the server) to be deserialized into full blown Backbone.Model objects.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;">Backbone.js data objects support events and event handlers (as do view objects). The idea seems to be that, once JSON data is converted into full-blown Backbone.js objects, the view objects and data objects are stitched together with the right events.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"><br /></span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-size: 14.6667px; white-space: pre-wrap;"><span style="font-family: "arial";">The Backbone.js data objects is an alternative to AngularJS' </span><span style="font-family: "courier new" , "courier" , monospace;">$scope</span><span style="font-family: "arial";"> variable or React's </span><span style="font-family: "courier new" , "courier" , monospace;">props</span><span style="font-family: "arial";"> variable (argument). Backbone.js event mechanisms require the developer to orchestrate how data changes propagate to the view. In contrast, AngularJS' digest cycle and React's virtual DOM and diffing algorithms take care of this orchestration for the developer.</span></span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">Backbone.Collection</span><br />
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;">Backbone.Collection is the Backbone.Model version of an array. Instead of using ordinary JavaScript arrays, Backbone.js requires the developer to deserialize arrays into collections so that events and event handlers can be used to monitor data changes.</span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">Backbone.sync()</span><br />
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">Backbone.sync is an Backbone.js specific implementation of something like GraphQL but is incompatible with GraphQL.</span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> Its API is reminiscent of server side persistence libraries like Hibernate with methods like </span><span style="color: black; font-family: "courier new" , "courier" , monospace; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">fetch()</span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;"> and </span><span style="color: black; font-family: "courier new" , "courier" , monospace; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">save()</span><span style="color: black; font-family: "arial"; font-size: 11pt; vertical-align: baseline; white-space: pre-wrap;">. </span><span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"> Backbone.sync uses HTTP operations like GET, POST, PUT and DELETE to perform server CRUD operations using pre-determined URLs. A server needs to confirm to Backbone.sync's conventions in order to work with Backbone.sync.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;">GraphQL has attained some level of popularity due to its support from Facebook; Backbone.sync never has.</span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">Backbone.Router</span><br />
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;">Backbone.Router supports partial views. It seems OK.</span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">Underscore.js templates</span><br />
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;">Backbone.js supports HTML templates via Underscore.js.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">An Underscore.js HTML template looks like:</span><br />
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="font-size: 14.6667px; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;"><h1><%= title %></h1></span></span><br />
<span style="font-size: 14.6667px; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;"><% if (title === 'first') { %></span></span><br />
<span style="font-size: 14.6667px; white-space: pre-wrap;"><span style="font-family: "courier new" , "courier" , monospace;">This is the first title
<% } %></span></span><br />
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;">Backbone.js also supports other libraries like Mustache but these must be downloaded and integrated separately. </span><span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;">This presents a problem: you cannot re-use templates that were written in one library with a different library so templates were mostly not reusable.</span><br />
<span style="font-family: "arial"; font-size: 14.6667px; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial";"><span style="font-size: 14.6667px; white-space: pre-wrap;">Backbone.js </span></span><span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;">does not provide any support for dynamic data in the template. In AngularJS, Angular or React, if the data changes, the template (or JSX) is automatically re-rendered whereas, with Backbone.js templates, the developer must detect the change, invoke the template directly and re-render the updated template in the right place.</span><br />
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;">Also, Backbone.js does not provide any support for (reusable) components. In AngularJS, directives allow the developer to insert a component with HTML and behavior. Similarly, JSX in React allows a developer to insert a component with HTML and behavior.</span><br />
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Backbone's weakness in templates and lack of component functionality is one of its biggest weakness and an important reason why it is not popular.</span><br />
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; font-weight: 700; white-space: pre-wrap;">Overall</span><br />
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;"><br /></span>
<span style="font-family: "arial"; font-size: 11pt; white-space: pre-wrap;">Backbone.js is a very awkward framework, requiring much of the developer trying to use it.</span></div>
<div dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">It is also a very ineffective framework, lacking popular features while providing other features that were not that useful.</span><br />
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span>
<span style="background-color: transparent; color: black; font-family: "arial"; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Backbone.js is very imperative, manually connecting everything with events and event listeners, instead of declarative, changing JSX (in React) or changing HTML templates (in AngularJS) to invoke the event handler.</span></div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-87116954043900936102015-09-12T13:29:00.000-07:002015-09-12T13:29:52.487-07:00The Eh (Canadian) programming languageI know a guy who is Canadian and, just for fun, I wrote a programming language for him.<br />
<br />
The language is the Eh programming language. The syntax is similar to JavaScript, except that instead of a line ending with a semicolon, it ends with a comma, a space, the word "eh" and a question mark (?). For example:<br />
<pre class="brush: jscript">var src = 'script', eh?
var s = '', eh?
s += 'Hello', eh?
s += ' ', eh?
s += 'World', eh?
s += ' from ' + src, eh?
console.log(s), eh?
</pre>
This will print the following to the JavaScript console:<br />
<pre class="brush: plain">Hello World from script
</pre>
An Eh script can be included inline using the "text/eh" type:<br />
<pre class="brush: plain"><script type="text/eh">
console.log('Eh?'), eh?
</script>
</pre>
Or loaded from an external script file:
<br />
<pre class="brush: plain"><script src="script.js" type="text/eh"></script>
</pre>
Here's the source code for the Eh compiler (in JavaScript) named <span style="font-family: Courier New, Courier, monospace;">eh.js</span>:<br />
<pre class="brush: jscript">// eh compiler
window.onload = function() {
// execute eh code
function executeEh(src) {
eval(src.replace(/,\seh\?/g, ';'));
}
// load eh scripts
var scripts = document.getElementsByTagName('script');
for (var s=0; s < scripts.length; s++) {
if (scripts[s].type === 'text/eh') {
if (scripts[s].src) {
// execute external eh scripts
var extScript = new XMLHttpRequest();
extScript.open("GET", scripts[s].src, true);
extScript.send();
extScript.onreadystatechange = function() {
if ((extScript.readyState== 4)
&& (extScript.status == 200)) {
executeEh(extScript.responseText);
}
};
} else {
// execute inline eh scripts
executeEh(scripts[s].innerHTML);
}
}
}
}
</pre>
Eh supports all major JavaScript libraries and APIs.<br />
<br />
Here's a Plunker to demonstrate:
<a href="http://plnkr.co/edit/ASrEWq3uoZgi6TwolGLd">http://plnkr.co/edit/ASrEWq3uoZgi6TwolGLd</a><br />
<br />
If you make an Eh script, comment below and let me know!Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com1tag:blogger.com,1999:blog-1163062631795397495.post-23014547733074128152015-09-01T14:07:00.000-07:002015-09-01T14:08:05.789-07:00Put your Git branch in your Mac or Linux promptThere is a lot of examples out there but here's a basic way to show your Git branch in your profile.<br />
<br />
First, edit your .bash_profile.<br />
<div class="code">
$ vi .bash_profile
</div>
Press "i" to put vi in "insert mode". Then, add the following to your .bash_profile:<br />
<div class="code">
ps1_git_branch() {<br />
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'<br />
}<br />
export PS1="\w\$(ps1_git_branch)\$ "
</div>
After making this change and starting a new Terminal, the prompt will show the following in non-Git folders:<br />
<div class="code">
~/Documents $
</div>
In Git folders, the prompt will show the following (if you are on the "master" branch):<br />
<div class="code">
~/Documents/git/code (master)$
</div>
Very convenient.Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-19701028148783154772015-08-14T16:53:00.000-07:002015-08-15T12:48:03.653-07:00JavaScript private data using createPrivateThis()In JavaScript, all object properties are public.<br />
<br />
But, coming from Java and a long line of languages before it, I've seen how private properties can enforce better designs. Is there a convenient way to enforce private properties in JavaScript?<br />
<br />
Up to now, there were four proposed solutions:<br />
<ol>
<li>Imaginary privacy: Put an underscore (_) in front of all private properties to indicate that they are private.</li>
<li>Everything in the constructor: Use closure and attach all your functions to the <span style="font-family: Courier New, Courier, monospace;">this</span> object; don't use <span style="font-family: Courier New, Courier, monospace;">Object.prototype.func =</span> anymore.</li>
<li>Weakmaps: <a href="http://www.nczonline.net/blog/2014/01/21/private-instance-members-with-weakmaps-in-javascript/">http://www.nczonline.net/blog/2014/01/21/private-instance-members-with-weakmaps-in-javascript/</a></li>
<li>Everything public: Why care?</li>
</ol>
<div>
I am now proposing a fifth solution: <span style="font-family: Courier New, Courier, monospace;">createPrivateThis()</span>.</div>
<div>
<br /></div>
<div>
My objectives were simple:</div>
<div>
<ol>
<li>Provide Java-style public and private data in JavaScript.</li>
<li>Prevent read access to the private data from outside the object.</li>
<li>Allow functions to be created on the prototype object as normal.</li>
<li>Allow functions on the prototype object to access public and private data using the <span style="font-family: Courier New, Courier, monospace;">this</span> pointer.</li>
</ol>
<div>
Here's the code that I came up with:</div>
<pre class="brush: jscript">/**
* Wrap the prototype functions and put the wrappers on the
* object itself. A wrapper merges the object and private
* properties before the call, makes the call then unmerges
* the object and private properties.
*
* Normally, you create your private data in the constructor
* function and give distinct names to public and private
* data. Then, during function calls, you use the this
* pointer for BOTH public and private data. In ambiguous
* situations, the private data is operated on with the
* exception that adding/deleting data defaults to the
* (public) object.
*
* It is possible to create private functions and variables
* with the same name using the transitory getPrivateThis()
* method but this is NOT recommended and is an advanced use.
*
* @param {Object} obj The object that needs private data.
* @return {Object} The private data pointer (this_p).
*/
function createPrivateThis(obj) {
// use Underscore.js or our own mini version
var __ = (typeof _ === 'undefined')? {
isFunction: function(f) {
return f
& {}.toString.call(f) == '[object Function]';
},
has: function(o, k) {
return o.hasOwnProperty(k);
},
keys: function(o) {
return Object.keys(o);
},
clone: function(o) {
var k, o2 = {};
for (k in o) {
if (o.hasOwnProperty(k)) {
o2[k] = o[k];
}
}
return o2;
},
each: function(oa, f) {
var i, k;
if (oa.constructor === Array) {
for (i=0; i < oa.length; ++i) {
f(oa[i]);
}
} else {
for (k in oa) {
if (oa.hasOwnProperty(k)) {
f(oa[k], k);
}
}
}
},
extend: function() {
var a, arg, d = arguments[0];
for (a=1; a < arguments.length; ++a) {
arg = arguments[a];
for (var k in arg) {
if (arg.hasOwnProperty(k)) {
d[k] = arg[k];
}
}
}
return d;
},
union: function() {
var a = Array.prototype.concat.apply({}, arguments)
var b = [], a1, i;
while (a.length > 0) {
a1 = a.pop();
for (i=0; (i < b.length) && (a1 !== b[i]); ++i) ;
if (i === b.length) {
b.push(a1);
}
}
return b;
}
}: _;
var priv = {};
var getPriv = function() {
return priv;
};
// iterate over each function in the object prototype
__.each(obj.constructor.prototype, function(value, key) {
if (__.isFunction(value) && !__.has(obj, key)) {
// intercept the function on the object itself
obj[key] = function() {
var pre = __.clone(priv);
// combine the object and its private properties
var full = __.extend({}, obj, priv);
// add the getPrivateThis() access function
full.getPrivateThis = getPriv;
// invoke original function with combined object
var r = value.apply(full, arguments);
// remove the getPrivateThis() access function
delete full.getPrivateThis;
var objKeysToUpdate = {}, objKeysToDelete = {};
var privKeysToUpdate = {}, privKeysToDelete = {};
var allKeys = __.union(__.keys(pre), __.keys(priv),
__.keys(obj), __.keys(full));
// decide what to do with every key that we ever saw
__.each(allKeys, function(key) {
if (__.has(pre, key) !== __.has(priv, key)) {
// private property was added or deleted
// using getPrivateThis()
if (__.has(full, key)) {
// add/modify object property with the same name
objKeysToUpdate[key] = full[key];
} else if (!__.has(full, key) && __.has(obj, key)) {
// delete object property with the same name
objKeysToDelete[key] = 'delete';
}
} else if (__.has(full, key) && __.has(priv, key)) {
// modify private property
privKeysToUpdate[key] = full[key];
} else if (!__.has(full, key) && __.has(priv, key)) {
// delete private property
privKeysToDelete[key] = 'delete';
} else if (__.has(full, key) && !__.has(priv, key)) {
// add/modify object property
objKeysToUpdate[key] = full[key];
} else if (!__.has(full, key) && __.has(obj, key)) {
// delete object property
objKeysToDelete[key] = 'delete';
}
});
// do what we decided to do with the keys
__.each(objKeysToUpdate, function(value, key) {
obj[key] = value;
});
__.each(objKeysToDelete, function(value, key) {
delete obj[key];
});
__.each(privKeysToUpdate, function(value, key) {
priv[key] = value;
});
__.each(privKeysToDelete, function(value, key) {
delete priv[key];
});
return r;
};
}
});
return priv;
}
</pre>
<br />
The core idea is to create wrapper functions on the <span style="font-family: Courier New, Courier, monospace;">this</span> object which automatically intercept the calls to the functions defined on the prototype object. The automatically generated interception functions combine the <span style="font-family: Courier New, Courier, monospace;">this</span> and the private data, make a call to the prototype function, then uncombine the data back into the <span style="font-family: Courier New, Courier, monospace;">this</span> and the private data.<br />
<br />
In practice, <span style="font-family: Courier New, Courier, monospace;">createPrivateThis()</span> is used like this:</div>
<pre class="brush: jscript">function Animal() {
var this_p = createPrivateThis(this);
this_p.nickname = "NoName";
this.type = 'animal';
}
Animal.prototype.getPrivateNickname = function() {
// this.nickname is private but we can access it inside here
return this.nickname;
};
Animal.prototype.setPrivateNickname = function(nick) {
// this.nickname is private but we can access it inside here
this.nickname = nick;
};
Animal.prototype.getPublicType = function() {
return this.type;
};
Animal.prototype.setPublicType = function(type) {
this.type = type;
};
</pre>
<br />
The private property, <span style="font-family: Courier New, Courier, monospace;">nickname</span>, is not accessible outside the object. It is accessed via the <span style="font-family: Courier New, Courier, monospace;">this</span> pointer but it does not actually reside on the object. The private property actually exists as a local variable in the <span style="font-family: Courier New, Courier, monospace;">createPrivateThis()</span> function that is accessed via closure (only available inside the function).<br />
<br />Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-34251797890368472572015-08-13T16:54:00.001-07:002015-08-19T07:45:06.241-07:00Make Mac Minecraft work on Oracle JavaMy son likes to play Minecraft. I like to use Oracle Java 8 instead of the decrepit Apple Java (6) that Apple insists on. Can't Minecraft use Oracle Java 8?<br />
<br />
It can but it took me 6 months to figure out how to do it right. You can modify the Minecraft Mac application so it will work on whatever is installed, either Oracle Java 8 or Apple Java 6. You can even do it without starting a Terminal (but I'll tell you how to do it in Terminal, too).<br />
<br />
On MacOS X Yosemite:<br />
<br />
1. Make a copy of your Minecraft application for backup.<br />
<br />
2. Start Safari and go to this page:<br />
<br />
<a href="https://raw.githubusercontent.com/tofi86/universalJavaApplicationStub/master/src/universalJavaApplicationStub">https://raw.githubusercontent.com/tofi86/universalJavaApplicationStub/master/src/universalJavaApplicationStub</a><br />
<br />
This is a bash script from <a href="https://github.com/tofi86/universalJavaApplicationStub">https://github.com/tofi86/universalJavaApplicationStub</a> GitHub project.<br />
<br />
3. Choose the "File" menu, then select the "Save As..." menu item. Select the "Page Source" item in the "Format" dropdown list. Now, save it using the default name (i.e. universalJavaApplicationStub) on your Desktop.<br />
<br />
4. Go to the Applications folder, right click on the Minecraft application and select "Show Package Contents". A Finder window will open. Double-click on the <span style="font-family: Courier New, Courier, monospace;">Contents</span> folder to open it. <strike>Start Finder and choose the "Go" menu, then select the "Go to Folder..." menu item. Type "/Applications/Minecraft.app/Contents" and press the "Go" button. A Finder window will open.</strike><br />
<br />
5. Double-click on the <span style="font-family: Courier New, Courier, monospace;">MacOS</span> folder. If it is a Java application, you will probably see a single file named <span style="font-family: Courier New, Courier, monospace;">JavaApplicationStub</span> in the <span style="font-family: Courier New, Courier, monospace;">MacOS</span> folder.<br />
<br />
6. Drag the <span style="font-family: Courier New, Courier, monospace;">universalJavaApplicationStub</span> into the <span style="font-family: Courier New, Courier, monospace;">MacOS</span> folder. Now, there are two files in there.<br />
<br />
7. Right click on <span style="font-family: Courier New, Courier, monospace;">universalJavaApplicationStub</span> and select the "Get Info" menu item. Open the "Name & Extensions" item.<br />
<br />
8. If the "Hide extension" checkbox is enabled and checked, uncheck it.<br />
<br />
9. In the text box, delete the extension (probably ".txt") and close the "Get Info" box.<br />
<br />
10. If you get "Are you sure you want to remove the extension ".txt"?" prompt, press the "Remove" button. (The icon for <span style="font-family: Courier New, Courier, monospace;">universalJavaApplicationStub</span> will change to a green CRT terminal icon with the text, "exec", on it.) Leave the "MacOS" Finder window open.<br />
<br />
11. Oh, I lied. You do have to use Terminal. Open the Applications folder, open the Utilities and run Terminal application.<br />
<br />
12. Paste the following into Terminal and press the "Return" key to execute it:<br />
<br />
<div class="code">
chmod ugo+x /Applications/Minecraft.app/Contents/MacOS/universalJavaApplicationStub</div>
<br />
This command adds eXecute permissions to the file for User, Group and Other.<br />
<br />
If you don't get an error, it probably worked. If you get an error, you can give up on this process and, don't worry, the Minecraft application is undamaged. In either case, close the Terminal.<br />
<br />
13. Press the back button on the original Finder window to return to the <span style="font-family: Courier New, Courier, monospace;">Contents</span> folder.<br />
<br />
14. Right click on the <span style="font-family: Courier New, Courier, monospace;">Info.plist</span> file. Select the "Open With" menu, then choose the "Other..." menu item. Scroll down and select the "TextEdit" application. The TextEdit application should start.<br />
<br />
15. Edit and save the file:<br />
<br />
A. Insert "universal" in front of "JavaApplicationStub" so it reads <span style="font-family: Courier New, Courier, monospace;"><string>universal JavaApplicationStub</string></span>.<br />
<br />
B. Insert an "X" at <span style="font-family: Courier New, Courier, monospace;"><key>Java</key></span> to make <span style="font-family: Courier New, Courier, monospace;"><key>JavaX</key></span>.<br />
<br />
16. Go to the Applications folder and run Minecraft. It should work. You're done.<br />
<br />
If you want to perform this same process entirely in Terminal, start a Terminal and do this:<br />
<br />
<div class="code">
$ cp -r /Applications/Minecraft.app /Applications/Minecraft\ copy.app<br />
$ cd /Applications/Minecraft.app/Contents<br />
$ curl "https://raw.githubusercontent.com/tofi86/universalJavaApplicationStub/master/src/universalJavaApplicationStub" -o "MacOS/universalJavaApplicationStub"<br />
$ chmod ugo+x MacOS/universalJavaApplicationStub<br />
$ vi Info.plist<br />
Press i to insert text<br />
- <string>JavaApplicationStub</string><br />
+ <string>universalJavaApplicationStub</string><br />
- <key>Java</key><br />
+ <key>JavaX</key><br />
Press Esc, then type ZZ which will save</div>
<div style="-webkit-text-stroke-width: 0px; color: black; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; margin: 0px; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px;">
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
This technique should work for any Mac Java application, as long as you go to the correct .app folder.<br />
<br />
Don't forget that you have to a backslash (\) before any spaces in the name when using Terminal so <span style="font-family: Courier New, Courier, monospace;">Minecraft copy.app</span> becomes <span style="font-family: Courier New, Courier, monospace;">Minecraft\ copy.app</span>.<br />
<br />
If you need to troubleshoot, try running <span style="font-family: Courier New, Courier, monospace;">universalJavaApplicationStub</span> from the Terminal and see if it launches Minecraft or gives you error messages. If that works, try running the <span style="font-family: Courier New, Courier, monospace;">open</span> command so <span style="font-family: Courier New, Courier, monospace;">open /Applications/Minecraft.app</span> and see if that launches Minecraft or gives you error messages. The error messages can be somewhat cryptic but, with Google, perhaps you can figure out what the issue is.<br />
<br />
References:<br />
<a href="http://apple.stackexchange.com/questions/88110/make-minecraft-or-java-preferences-app-run-on-java-7">http://apple.stackexchange.com/questions/88110/make-minecraft-or-java-preferences-app-run-on-java-7</a><br />
<a href="http://gaming.stackexchange.com/questions/178178/making-minecraft-run-with-java-8-on-os-x-10-10">http://gaming.stackexchange.com/questions/178178/making-minecraft-run-with-java-8-on-os-x-10-10</a><br />
<a href="http://www.cgwerks.com/make-minecraft-work-mac-osx-yosemite-latest-java-8/">http://www.cgwerks.com/make-minecraft-work-mac-osx-yosemite-latest-java-8/</a><br />
<a href="https://gist.github.com/pudquick/7518753">https://gist.github.com/pudquick/7518753</a><br />
<a href="http://www.minecraftforum.net/forums/support/unmodified-minecraft-client/1858141-minecraft-x64-for-mac-with-java-7">http://www.minecraftforum.net/forums/support/unmodified-minecraft-client/1858141-minecraft-x64-for-mac-with-java-7</a><br />
<a href="https://bugs.mojang.com/browse/MCL-1049">https://bugs.mojang.com/browse/MCL-1049</a><br />
<a href="http://mosx.tumblr.com/post/64402950499/os-x-tip-execute-java-apps-like-minecraft-or">http://mosx.tumblr.com/post/64402950499/os-x-tip-execute-java-apps-like-minecraft-or</a><br />
<a href="http://stackoverflow.com/questions/14806709/application-is-using-java-6-from-apple-instead-of-java-7-from-oracle-on-mac-os-x">http://stackoverflow.com/questions/14806709/application-is-using-java-6-from-apple-instead-of-java-7-from-oracle-on-mac-os-x</a><br />
<a href="http://superuser.com/questions/490425/how-do-i-switch-between-java-7-and-java-6-on-os-x-10-8-2">http://superuser.com/questions/490425/how-do-i-switch-between-java-7-and-java-6-on-os-x-10-8-2</a><br />
<br />Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com4tag:blogger.com,1999:blog-1163062631795397495.post-40769310747525023772015-07-30T17:50:00.003-07:002015-07-30T17:52:53.369-07:00What does _.bind() do?The name, "bind", conjures fear. It's scary. Binding sounds mysterious and strange.<br />
<br />
Underscore.js has the <span style="font-family: Courier New, Courier, monospace;">_.bind()</span> function. What does it do?<br />
<br />
Let's say that you using the JavaScript <span style="font-family: Courier New, Courier, monospace;">setTimeout()</span> function to set the focus on a jQuery DOM element.<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">var el = $('#myinput');</span><br />
<span style="font-family: Courier New, Courier, monospace;">setTimeout(</span><span style="color: #38761d;"><b><span style="font-family: 'Courier New', Courier, monospace;">function() { </span><span style="font-family: 'Courier New', Courier, monospace;">el.focus(); </span><span style="font-family: 'Courier New', Courier, monospace;">}</span></b></span><span style="font-family: Courier New, Courier, monospace;">, 50);</span><br />
<br />
The <span style="font-family: Courier New, Courier, monospace;">setTimeout()</span> function takes a standalone function as its first argument.<br />
<br />
Now, consider this code:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">var el = $('#myinput');</span><br />
<span style="font-family: Courier New, Courier, monospace;">setTimeout(</span><span style="font-family: 'Courier New', Courier, monospace;"><span style="color: #38761d;"><b><strike>el.focus</strike></b></span></span><span style="font-family: 'Courier New', Courier, monospace;">, 50);</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
This code doesn't work. Why? Well, the <span style="font-family: Courier New, Courier, monospace;">focus()</span> call is an object method, not a standalone function. The <span style="font-family: Courier New, Courier, monospace;">setTimeout()</span> function expects a standalone function, not an object method.<br />
<br />
A standalone function and an object method aren't the same thing. If you pass an object method as an argument that expects a standalone function, it's not the same thing, it's wrong and it doesn't work.<br />
<br />
It's as if you passed a string as an argument to a function that expects a number. You passed the wrong kind of thing, even if the string contains a number. If you want it to work, you have to convert the string to the proper type before calling the function.<br />
<br />
So, how can you convert an object method call into a standalone function?<br />
<br />
One way is to create a standalone function that calls the object method as you did in the original code.<br />
<br />
A second way is to call the <span style="font-family: Courier New, Courier, monospace;">_.bind()</span> function.<br />
<br />
The <span style="font-family: Courier New, Courier, monospace;">_.bind()</span> function says, "Create a standalone function that invokes the method in the first argument, using the second argument as the <span style="font-family: Courier New, Courier, monospace;">this</span> object."<br />
<br />
So, instead of creating your own standalone function, the <span style="font-family: Courier New, Courier, monospace;">_.bind()</span> function will create one for you.<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">var el = $('#myinput');</span><br />
<span style="font-family: Courier New, Courier, monospace;">setTimeout(</span><span style="font-family: 'Courier New', Courier, monospace;"><span style="color: #38761d;"><b>_.bind(el.focus, el)</b></span></span><span style="font-family: 'Courier New', Courier, monospace;">, 50);</span><br />
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
The <span style="font-family: Courier New, Courier, monospace;">_.bind(el.focus, el)</span> code creates a standalone function that calls the <span style="font-family: Courier New, Courier, monospace;">el.focus()</span> method. You can think of it as converting a method (call) into a standalone function.<br />
<br />
The <span style="font-family: Courier New, Courier, monospace;">_.bind()</span> implementation is more complex but you can understand it by imagining that it is implemented like this:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">function bind(method, self) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return function() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> self.method(); // this doesn't work; it's just for clarity</span><br />
<span style="font-family: Courier New, Courier, monospace;"> };</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<br />
That's what <span style="font-family: Courier New, Courier, monospace;">_bind.()</span> does. It takes a method call and "converts" it into a standalone function.<br />
<br />
Once you grasp this, you will discover that <span style="font-family: Courier New, Courier, monospace;">_.bind()</span> has additional uses, such as converting a standalone function call with multiple arguments into a standalone function with no arguments.<br />
<br />
With this explanation, I hope that <span style="font-family: Courier New, Courier, monospace;">_.bind()</span> and binding is no longer mysterious and strange. And not scary.<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com2tag:blogger.com,1999:blog-1163062631795397495.post-66170685812462670192015-07-30T15:36:00.002-07:002015-07-30T15:39:55.378-07:00AngularJS dependency resolution and child controller creationSuppose that you have an AngularJS template (HTML with AngularJS tags, same thing) and an AngularJS controller and you want to execute them together:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">var elem = angular.element(angularTemplateHtml);</span><br />
<span style="font-family: Courier New, Courier, monospace;">var compileFunc = $compile(elem);</span><br />
<span style="font-family: Courier New, Courier, monospace;">$controller(controllerName, locals);</span><br />
<span style="font-family: Courier New, Courier, monospace;">elem = compileFunc($scope);</span><br />
<br />
This is how <span style="font-family: Courier New, Courier, monospace;">ngRoute</span> bootstraps a route.<br />
<br />
Suppose that the controller has dependencies. How are they resolved?<br />
<br />
Deep inside angular.js, there is an <span style="font-family: Courier New, Courier, monospace;">invoke()</span> function that looks like this:<br />
<br />
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">function invoke(fn, self, locals, serviceName) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> if (typeof locals === 'string') {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> serviceName = locals;</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> locals = null;</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> var args = [],</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> $inject = createInjector.$$annotate(fn, strictDi, serviceName),</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> length, i,</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> key;</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> for (i = 0, length = $inject.length; i < length; i++) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> key = $inject[i];</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> if (typeof key !== 'string') {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> throw $injectorMinErr('itkn',</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> 'Incorrect injection token! Expected service name as string, got {0}', key);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> args.push(</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> locals && locals.hasOwnProperty(key)</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> ? locals[key]</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> : getService(key, serviceName)</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> );</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> if (isArray(fn)) {</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> fn = fn[length];</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> // http://jsperf.com/angularjs-invoke-apply-vs-switch</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> // #5388</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;"> return fn.apply(self, args);</span></div>
<div class="p1">
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<div class="p1">
<br /></div>
<div class="p1">
AngularJS looks for dependencies in the <span style="font-family: Courier New, Courier, monospace;">locals</span> object and <span style="font-family: Courier New, Courier, monospace;">getService()</span>. If it doesn't find the dependency in either place, the dependency fails and you get an error.</div>
<div class="p1">
<br /></div>
<div class="p1">
It's interesting that you can bolt dependencies onto the <span style="font-family: Courier New, Courier, monospace;">locals</span> object. The <span style="font-family: Courier New, Courier, monospace;">locals</span> object is passed directly into the <span style="font-family: Courier New, Courier, monospace;">$controller()</span> function so, if you are calling <span style="font-family: Courier New, Courier, monospace;">$controller()</span> function directly, you can provide dependencies to the controller, even if those dependencies aren't AngularJS services.</div>
<div class="p1">
<br /></div>
<div class="p1">
The <span style="font-family: Courier New, Courier, monospace;">$controller()</span> function itself looks like this:</div>
<div class="p1">
<br /></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;">/**</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * @ngdoc service</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * @name $controller</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * @requires $injector</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> *</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * @param {Function|string} constructor If called with a function</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * then it's considered to be the </span></span><span style="font-family: 'Courier New', Courier, monospace;">controller constructor function.</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> * Otherwise it's considered to be a string which is used </span><span style="font-family: 'Courier New', Courier, monospace;">to</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> * retrieve the controller constructor using the following steps:</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> *</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * * check if a controller with given name is registered via </span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * `$controllerProvider`</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * * check if evaluating the string on the current scope returns</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * a constructor</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * * if $controllerProvider#allowGlobals, check</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * `window[constructor]` on the global </span></span><span style="font-family: 'Courier New', Courier, monospace;">`window` object (not</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> * recommended)</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> *</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * The string can use the `controller as property` syntax, where</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * the controller instance is published </span></span><span style="font-family: 'Courier New', Courier, monospace;">as the specified property</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> * on the `scope`; the `scope` must be injected into `locals` param</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> * for this </span><span style="font-family: 'Courier New', Courier, monospace;">to work correctly.</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> *</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * @param {Object} locals Injection locals for Controller.</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * @return {Object} Instance of given controller.</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> *</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * @description</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * `$controller` service is responsible for instantiating</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * controllers.</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> *</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * It's just a simple call to {@link auto.$injector $injector}, but</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> * extracted into </span></span><span style="font-family: 'Courier New', Courier, monospace;">a service, so that one can override this service</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> * with [BC version](https://gist.github.com/1649788).</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> */</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;">return function(expression, locals, later, ident) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // PRIVATE API:</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // param `later` --- indicates that the controller's constructor</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // is invoked at a later time. </span></span><span style="font-family: 'Courier New', Courier, monospace;">If true, $controller will</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> // allocate the object with the correct </span><span style="font-family: 'Courier New', Courier, monospace;">prototype chain, but</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> // will not invoke the controller until a returned </span><span style="font-family: 'Courier New', Courier, monospace;">callback is</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> // invoked.</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // param `ident` --- An optional label which overrides the label</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // parsed from the controller </span></span><span style="font-family: 'Courier New', Courier, monospace;">expression, if any.</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var instance, match, constructor, identifier;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> later = later === true;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (ident && isString(ident)) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> identifier = ident;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"></span><br /></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (isString(expression)) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> match = expression.match(CNTRL_REG);</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (!match) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> throw $controllerMinErr('ctrlfmt',</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> "Badly formed controller string '{0}'. " +</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> "Must match `__name__ as __id__` or `__name__`.",</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> expression);</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> constructor = match[1],</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> identifier = identifier || match[3];</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> expression = controllers.hasOwnProperty(constructor)</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> ? controllers[constructor]</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> : getter(locals.$scope, constructor, true) ||</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> (globals ? getter($window, constructor, true) : undefined);</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> assertArgFn(expression, constructor, true);</span></span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> }</span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"></span><br /></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (later) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // Instantiate controller later:</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // This machinery is used to create an instance of the object</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // before calling the </span></span><span style="font-family: 'Courier New', Courier, monospace;">controller's constructor itself.</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> //</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // This allows properties to be added to the controller before</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // the constructor is</span></span><span style="font-family: 'Courier New', Courier, monospace;">invoked. Primarily, this is used for</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> // isolate scope bindings in $compile.</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> //</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // This feature is not intended for use by applications, and is</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // thus not documented </span></span><span style="font-family: 'Courier New', Courier, monospace;">publicly.</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // Object creation: http://jsperf.com/create-constructor/2</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var controllerPrototype = (isArray(expression) ?</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> expression[expression.length - 1] : expression).prototype;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> instance = Object.create(controllerPrototype || null);</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"></span><br /></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (identifier) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> addIdentifier(locals, identifier, instance, constructor</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> || expression.name);</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"></span><br /></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var instantiate;</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> return instantiate = extend(function() {</span></span></div>
<div class="p1">
<span style="color: #38761d;"><span style="font-family: 'Courier New', Courier, monospace;"> var result = </span><b style="font-family: 'Courier New', Courier, monospace;">$injector.invoke(expression, instance, locals,</b></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"><b> constructor)</b>; // resolve dependencies</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> if (result !== instance && (isObject(result)</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> || isFunction(result))) {</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> instance = result;</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> if (identifier) {</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> // If result changed, re-assign controllerAs value to</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> // scope.</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> addIdentifier(locals, identifier, instance, constructor</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> || expression.name);</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> return instance;</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> }, {</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> instance: instance,</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> identifier: identifier</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #38761d; font-family: Courier New, Courier, monospace;"> });</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p2">
<span style="font-family: 'Courier New', Courier, monospace;"> instance = $injector.instantiate(expression, locals, constructor);</span></div>
<div class="p1">
<br /></div>
<div class="p1">
The <span style="font-family: Courier New, Courier, monospace;">$controller()</span> function's first argument is <span style="font-family: Courier New, Courier, monospace;">expression</span>. This is either a controller object or its a string with the controller's name. If it's a string with the controller's name, the <span style="font-family: Courier New, Courier, monospace;">$controller()</span> function immediately looks up the controller object and sets expression equal to the object.</div>
<div class="p1">
<br /></div>
<div class="p1">
Then, the <span style="font-family: Courier New, Courier, monospace;">$controller()</span> function chooses whether to create the object instance and return the constructor function to be called later (in green text) or to create the object instance and call the constructor immediately.</div>
<div class="p1">
<br /></div>
<div class="p1">
In our example, the constructor is called immediately.</div>
<div class="p1">
<br /></div>
<div class="p1">
But what happens if <span style="font-family: Courier New, Courier, monospace;">angularTemplateHtml</span> contains additional controllers that are created by using the <span style="font-family: Courier New, Courier, monospace;">ng-controller</span> attribute?</div>
<div class="p1">
<br /></div>
<div class="p1">
<span class="s1">When <span style="font-family: Courier New, Courier, monospace;">$compile()</span> function is invoked, it crawls through the </span><span style="font-family: 'Courier New', Courier, monospace;">angularTemplateHtml</span> DOM tree using functions named <span style="font-family: Courier New, Courier, monospace;">nodeLinkFn()</span>, <span style="font-family: Courier New, Courier, monospace;">childLinkFn()</span> and <span style="font-family: Courier New, Courier, monospace;">compositeLinkFn()</span> to find and instantiate AngularJS constructs, like controllers.</div>
<div class="p1">
<br /></div>
<div class="p1">
The <span style="font-family: Courier New, Courier, monospace;">compileFunc()</span> function is created and returned by the <span style="font-family: Courier New, Courier, monospace;">$compile()</span> call.</div>
<div class="p1">
<br /></div>
<div class="p1">
Inside AngularJS, the <span style="font-family: Courier New, Courier, monospace;">compileFunc()</span> function looks like this:</div>
<div class="p1">
<br /></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;">return function publicLinkFn(scope, cloneConnectFn, options) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> assertArg(scope, 'scope');</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"></span><br /></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> options = options || {};</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var parentBoundTranscludeFn = options.parentBoundTranscludeFn,</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> transcludeControllers = options.transcludeControllers,</span></span></div>
<div class="p1">
</div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> futureParentElement = options.futureParentElement;</span></span></div>
<div class="p1">
<span class="s1"><br /></span></div>
<div class="p1">
AngularJS crawls the <span style="font-family: Courier New, Courier, monospace;">angularTemplateHtml</span> DOM tree during the <span style="font-family: Courier New, Courier, monospace;">$compile()</span> function and, when it finds a <span style="font-family: Courier New, Courier, monospace;">ng-controller</span> attribute, it invokes the <span style="font-family: Courier New, Courier, monospace;">setupControllers()</span> function.</div>
<div class="p1">
<br /></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;">function setupControllers($element, attrs, transcludeFn,</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> controllerDirectives, isolateScope, scope) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var elementControllers = createMap();</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> for (var controllerKey in controllerDirectives) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var directive = controllerDirectives[controllerKey];</span></span></div>
<div class="p1">
<span class="s1"><span style="color: #741b47; font-family: Courier New, Courier, monospace;"><b> var locals = {</b></span></span></div>
<div class="p1">
<span class="s1"><span style="color: #741b47; font-family: Courier New, Courier, monospace;"><b> $scope: directive === newIsolateScopeDirective</b></span></span></div>
<div class="p1">
<span class="s1"><span style="color: #741b47; font-family: Courier New, Courier, monospace;"><b> || directive.$$isolateScope ? isolateScope : scope,</b></span></span></div>
<div class="p1">
<span class="s1"><span style="color: #741b47; font-family: Courier New, Courier, monospace;"><b> $element: $element,</b></span></span></div>
<div class="p1">
<span class="s1"><span style="color: #741b47; font-family: Courier New, Courier, monospace;"><b> $attrs: attrs,</b></span></span></div>
<div class="p1">
<span class="s1"><span style="color: #741b47; font-family: Courier New, Courier, monospace;"><b> $transclude: transcludeFn</b></span></span></div>
<div class="p1">
<span class="s1"><span style="color: #741b47; font-family: Courier New, Courier, monospace;"><b> };</b></span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var controller = directive.controller;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (controller == '@') {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> controller = attrs[directive.name];</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<br />
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var controllerInstance = $controller(controller, locals, <b><span style="color: #e69138;">true</span></b>,</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> directive.controllerAs);</span></span></div>
<div class="p1">
<span class="s1"><br /></span></div>
<div class="p1">
Notice the bold purple text where the <span style="font-family: Courier New, Courier, monospace;">locals</span> object is created by AngularJS when it finds an <span style="font-family: Courier New, Courier, monospace;">ng-controller</span> attribute. While a child controller can access its parent's <span style="font-family: Courier New, Courier, monospace;">$scope</span> for various purposes, a child controller's <span style="font-family: Courier New, Courier, monospace;">locals</span> object is hardcoded, unavailable and unlinked to the parent controller's <span style="font-family: Courier New, Courier, monospace;">locals</span> object! So, a parent controller cannot resolve dependencies for a child controller created by AngularJS. Wouldn't it be nice if a parent controller could use its <span style="font-family: Courier New, Courier, monospace;">locals</span> object to provide dependency resolution and control over instantiation of its child controllers? But, it doesn't. Maybe next version.</div>
<div class="p1">
<br /></div>
<div class="p1">
Notice the orange highlighted <span style="font-family: Courier New, Courier, monospace;">true</span> argument inside the <span style="font-family: Courier New, Courier, monospace;">setupControllers()</span> function. The <span style="font-family: Courier New, Courier, monospace;">true</span> argument allocates the controller instance but does not invoke the constructor immediately. It returns the constructor to be invoked later.</div>
<div class="p1">
<br /></div>
<div class="p1">
AngularJS allocates controller instances during the <span style="font-family: Courier New, Courier, monospace;">$compile()</span> call (in our example) but waits and invokes constructors later during the <span style="font-family: Courier New, Courier, monospace;">compileFunc()</span> call (in our example).</div>
<div class="p1">
<br /></div>
<div class="p1">
During the <span style="font-family: Courier New, Courier, monospace;">compileFunc()</span> call, the child controller constructor functions are invoked in this code:</div>
<div class="p1">
<br /></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;">if (elementControllers) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // Initialize bindToController bindings for new/isolate scopes</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var scopeDirective = newIsolateScopeDirective</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> || newScopeDirective;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var bindings;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var controllerForBindings;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (scopeDirective && elementControllers[scopeDirective.name]) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> bindings = scopeDirective.$$bindings.bindToController;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> controller = elementControllers[scopeDirective.name];</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"></span><br /></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (controller && controller.identifier && bindings) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> controllerForBindings = controller;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> thisLinkFn.$$destroyBindings = </span></span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> initializeDirectiveBindings(scope, attrs,</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> controller.instance, </span><span style="font-family: 'Courier New', Courier, monospace;">bindings, scopeDirective);</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> for (i in elementControllers) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> controller = elementControllers[i];</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> var controllerResult = <b><span style="color: #990000;">controller()</span></b>;</span></span></div>
<div class="p2">
<span style="font-family: Courier New, Courier, monospace;"><span class="s1"></span><br /></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (controllerResult !== controller.instance) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // If the controller constructor has a return value,</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // overwrite the instance </span></span><span style="font-family: 'Courier New', Courier, monospace;">from setupControllers and update</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;"> //the element data</span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> controller.instance = controllerResult;</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> $element.data('$' + i + 'Controller', controllerResult);</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> if (controller === controllerForBindings) {</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> // Remove and re-install bindToController bindings</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> thisLinkFn.$$destroyBindings();</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> thisLinkFn.$$destroyBindings =</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> initializeDirectiveBindings(scope, attrs,</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> controllerResult, bindings, scopeDirective);</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;"> }</span></span></div>
<div class="p1">
</div>
<div class="p1">
<span class="s1"><span style="font-family: Courier New, Courier, monospace;">}</span></span></div>
<div class="p1">
<span class="s1"><br /></span></div>
<div class="p1">
<span class="s1">Notice the bold red text shows where the child controller construction functions are invoked during the <span style="font-family: Courier New, Courier, monospace;">compileFunc()</span> call.</span></div>
<div class="p1">
<span class="s1"><br /></span></div>
<div class="p1">
<span class="s1">This shows the lifecycle of controller objects, both directly created controllers and controllers created by AngularJS itself.</span></div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-66497551319624862162015-07-27T12:49:00.000-07:002015-07-30T13:26:34.050-07:00AngularJS $formattersSuppose you have a new directive that declares a tag like:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"><input type="array"></span><br />
<br />
In AngularJS v1.4.1, when you add an "input" directive, AngularJS decides how its going to store your data. Will your data be stored as a string (in a text node), in a JavaScript object (hanging off a DOM node) or what?<br />
<br />
In the case above, is the data stored as a string like "[1, 2, 3, 4]" in a text node or is it stored as a JavaScript array like ctrl.array = [1, 2, 3, 4]?<br />
<br />
It decides by running through a whole list of known (to AngularJS) input types and, if it does not know about a new, unexpected input type, it decides that it must be stored as a string.<br />
<br />
The known types are:<br />
<ul>
<li>text</li>
<li>date ('yyyy-MM-dd')</li>
<li>datetime-local ('yyyy-MM-ddTHH:mm:ss.sss')</li>
<li>time ('HH:mm:ss.sss')</li>
<li>week ('yyyy-Www')</li>
<li>month ('yyyy-MM')</li>
<li>number</li>
<li>url</li>
<li>email</li>
<li>radio</li>
<li>checkbox</li>
<li>hidden</li>
<li>button</li>
<li>submit</li>
<li>reset</li>
<li>file</li>
</ul>
<div>
If you default to being stored as a string, it adds this function to the $formatters object on the caller's instantiated directive.</div>
<div>
<br />
<span style="font-family: Courier New, Courier, monospace;">function stringBasedInputType(ctrl) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> ctrl.$formatters.push(function(value) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return ctrl.$isEmpty(value) ? value : value.toString();</span><br />
<span style="font-family: Courier New, Courier, monospace;"> });</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<br />
As you can see, if the object doesn't have a toString() method, it will call the default toString() method for all JavaScript objects. If that happens, the user's data may be "stored" as "[object Object]". Oh, no!</div>
<div>
<br /></div>
<div>
To avoid this, make sure that you empty or change the $formatters array so it is appropriate:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">angular</span><br />
<span style="font-family: Courier New, Courier, monospace;"> .directive('input', function(...) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> restrict: 'E',</span><br />
<span style="font-family: Courier New, Courier, monospace;"> require: '?ngModel',</span><br />
<span style="font-family: Courier New, Courier, monospace;"> link: function($scope, $element, $attributes, ngModel) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> ngModel.$formatters = [];</span><br />
<span style="font-family: Courier New, Courier, monospace;"> ...</span><br />
<br />
That way, you can get the unformatted object and handle it appropriately.<br />
<br /></div>
<div>
</div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-81981542093923904132014-08-22T16:53:00.004-07:002015-07-30T15:37:09.099-07:00QUnit tests on AngularJS directivesRecently, I had an little homework assignment to create a "signup" web application using Angular.js. You can see the app <a href="https://github.com/ajaximrpg/signup" target="_blank">here</a>.<br />
<br />
As part of that assignment, I created an Angular directive to validate the data in the "password" and "verification" text inputs. Here's what the Angular directive looked like:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">// match password and verification</span><br />
<span style="font-family: Courier New, Courier, monospace;">app.directive('match', [function () {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> require: 'ngModel',</span><br />
<span style="font-family: Courier New, Courier, monospace;"> link: function(scope, elem, attrs, ctrl) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> scope.$watch('['+attrs.ngModel+', '+attrs.match+']',</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> function(value){</span><br />
<span style="font-family: Courier New, Courier, monospace;"> ctrl.$setValidity('match', value[0] === value[1]);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }, true);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"> };</span><br />
<span style="font-family: Courier New, Courier, monospace;">}]);</span><br />
<div>
<br /></div>
<div>
But I wanted to use QUnit to test this directive. I spent a ton of time, trying a zillion things, which didn't work because I was a newbie to both Angular and QUnit (and using them together). But, finally, I found the correct combination.</div>
<br />
<span style="font-family: Courier New, Courier, monospace;">// create test bed</span><br />
<span style="font-family: Courier New, Courier, monospace;">var injector = angular.injector(['ng', 'ngMock', 'signupApp']);</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">var init = {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> setup: function() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> this.$scope = injector.get('$rootScope').$new();</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;">};</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">module('tests', init);</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">// test the 'match' directive</span><br />
<span style="font-family: Courier New, Courier, monospace;">QUnit.test('match', function() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> var html = '<form id="myform" name="signupController" ng-controller="signupController"><input id="password" ng-model="password" match="verification"></input><input id="verification" ng-model="verification" match="password"></input></form>';</span><br />
<span style="font-family: Courier New, Courier, monospace;"> var $compile = injector.get('$compile');</span><br />
<span style="font-family: Courier New, Courier, monospace;"> var element = $compile(html)(this.$scope);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> this.$scope.password = 'passw0rd';</span><br />
<span style="font-family: Courier New, Courier, monospace;"> this.$scope.verification = 'passw0rd';</span><br />
<span style="font-family: Courier New, Courier, monospace;"> this.$scope.$apply();</span><br />
<span style="font-family: Courier New, Courier, monospace;"> ok(element.scope().signupController.$valid, '$valid is false');</span><br />
<span style="font-family: Courier New, Courier, monospace;"> this.$scope.password = 'passw0rd';</span><br />
<span style="font-family: Courier New, Courier, monospace;"> this.$scope.verification = 'passw0rd2';</span><br />
<span style="font-family: Courier New, Courier, monospace;"> this.$scope.$apply();</span><br />
<span style="font-family: Courier New, Courier, monospace;"> ok(!element.scope().signupController.$valid, '$valid is true when it should be false');</span><br />
<span style="font-family: Courier New, Courier, monospace;">});</span><br />
<div>
<br /></div>
<div>
My first mistake was that it took me a long time to figure out that I needed angular-mocks.js to create a fully functional test bed. The ngMock module is needed to add lots of support to the Angular test harness. While I could get a simple test harness running without angular-mocks.js, testing that required the controller (and probably ngModel) required angular-mocks.js.</div>
<div>
<br /></div>
<div>
My second mistake was that it took me a long time to realize that the Angular scope and the Angular controller are different. (Well, duh.) Finally, I discovered that, if I added a "name" attribute to the controller element, a property giving access to the controller would be available in the scope.</div>
<div>
<br /></div>
<div>
This may be obvious to Angular experts but I spent a ton of time traveling around on Google and I never found any posts that were on point.</div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-41010690318247270932014-07-03T13:28:00.002-07:002014-07-03T13:39:14.498-07:00Events Are More Flexible Than HTTPWhen you first use <span style="font-family: Courier New, Courier, monospace;">$.ajax()</span> in jQuery, it is easy and tempting to set up a HTTP request/response style communication mechanism between your client and your server. Your client sends a HTTP request, it provides a handler to receive the HTTP response and the handler does something with the result. That seems fine ...<br />
<br />
... except it breaks down pretty easily. If the server takes too long, the HTTP connection times out and the response is lost. And, when it breaks down, you hack on it, adding polling and/or subscription mechanisms. Then it breaks down some more and you hack on it some more.<br />
<br />
Events are always better.<br />
<br />
With events, the client sends and receives events (e.g. a JSON object).<br />
<br />
Events are self-contained. They do not require any external information. For example, the event should not have a different meaning if it is called on one URI (e.g. /users) versus another URI (e.g. /groups). If it does, the event should be updated with that new information to keep the event self-contained. So, in our example, maybe an "domain" key is added to the event which is assigned a value of "users" or "groups". After modifying the event, the event would again be self-contained and the URI that it was sent to can be forgotten.<br />
<br />
An event should also be disconnected from the communications mechanism: it should not matter if an event was received via a HTTP request (or HTTP response), short-polling, long-polling, JSONP, Socket.IO or even some strange new datagram mechanism (which would be session-less and not allow responses). Different communication transports should be easy to substitute.<br />
<br />
Events work in "fire and forget" mode. Once an event is fired, it is gone. The sender does not concern itself whether the event is received or not; it lets the event delivery mechanism do its work and deliver the event with no further interaction.<br />
<br />
But what if your code expects a response?<br />
<br />
The event handler should receive the event, process it and then send a response event back to the original sender. A response event is <u>new</u> event that is created by the receiver and sent to the original sender with a reference to the original event. Usually, the events contain a unique event number which the response event can reference. The response is still a new event; it just references another event.<br />
<br />
With HTTP, an assumption is made that every request has its own response. This assumption may not be valid when you are using long-polling instead of HTTP. With events, however, the assumption is removed; events can be sent and response events can be sent back using some other mechanism or at a much later date. HTTP relies on the response being sent back in the same HTTP connection as the HTTP request was sent. Events do not.<br />
<br />
Events can be built on top of HTTP requests/responses. Events are sent as part of the GET query string or the POST data and any pending events can be returned in the HTTP response body. The difference is that the events returned do not need to correspond to the events sent. Arbitrary events can be sent and arbitrary events can be received in the same HTTP connection with no relationship implied between them.<br />
<br />
Inevitably, it seems that most systems move towards an event system (or suffer through an ever growing number of hacks to add flexibility to HTTP request/response designs).<br />
<br />
If you want my advice, consider starting any new code with events, rather than muddling through with an HTTP request/response system and converting it later.Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-30557881569241431662014-01-03T16:23:00.000-08:002014-01-03T16:23:30.179-08:00Super simple JSON and MySQLI invented <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> (available for Node.js and PHP at <a href="http://github.com/ajaximrpg/jsonhib">http://github.com/ajaximrpg/jsonhib</a> ) to provide a "good enough" solution to reading and writing JSON data to a MySQL database.<br />
<br />
Most developers say this is impossible.<br />
<br />
But, to read JSON from a MySQL database, <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> has a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">readRows()</span> method that takes two arguments: a table name and a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">WHERE</span> clause. It returns a JSON array of JSON objects where each object represents a MySQL row. The columns of each MySQL row become JSON property names; the values of each MySQL row become property values. The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">WHERE</span> clause only serves to narrow the number of JSON objects returned.<br />
<br />
In Node.js, it looks like this:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// assume 'mytable' is a MySQL table with these columns: id, name</span><br />
<span class="Apple-style-span" style="color: #274e13; font-family: 'Courier New', Courier, monospace;">jh.readRows('mytable', 'WHERE id > 0', function(s) {</span><br />
<span class="Apple-style-span" style="color: #274e13; font-family: 'Courier New', Courier, monospace;"> var str = s;</span><br />
<span class="Apple-style-span" style="color: #274e13; font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// str='[{"id": 1, "name": "bob"}, {"id": 2, "name": "fred"}]'</span><br />
<div>
<br /></div>
<div>
Most developers will object that the objects are out of order. "There's no sorting or ordering," they say, "the objects come out in random order."</div>
<div>
<br /></div>
<div>
They sure do. Sometimes, the client doesn't care so it doesn't matter. But, if it does matter and an additional integer column can be added to the table (a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">sort_column</span> column), <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> can be directed to use this hidden column to maintain the order of rows. And, if the table can't be modified, <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> relies on the caller to reorder the JSON array if he wishes.</div>
<div>
<br /></div>
<div>
Another objection is that MySQL databases require a schema and JSON objects can have arbitrary properties. "The only choice is to put all your JSON objects in one table and serialize the JSON to a single MySQL text column," they say.</div>
<div>
<br /></div>
<div>
Uh, is that how you do it in client? Just have all your JSON objects crammed into a single humongous array? Mix your customer JSON objects with your sales order JSON objects and your permissions JSON objects? Of course not! You assign arrays of similar JSON objects to different variables. In this case, similar JSON objects are assigned to specific MySQL tables. Your client doesn't add customer JSON objects to the permissions variable; don't add customer data to the MySQL permissions table.</div>
<div>
<br /></div>
<div>
Also, don't all customers have a name? Isn't their name always a string, not a floating point value? Yes, JSON objects can have arbitrary properties but they always have a lot of properties that are expected and required and are of a specific type. <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> relies on the caller to know that, by default, JSON properties that do not fit into the MySQL schema will be discarded. Avoid extra properties or store them somewhere else. If that is not desirable and an additional string column can be added to the table (a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">json_column</span> column), <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> can be directed to use this hidden column to keep track of JSON data that doesn't fit into the MySQL schema. This column doesn't keep all the JSON data; it just keeps the JSON properties that don't have a corresponding dedicated MySQL column or have a MySQL column of the wrong type.</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> really is just an object-relational mapping (ORM) layer for JSON and MySQL. When reading, <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> queries MySQL rows and sensibly maps MySQL columns to JSON properties and, if available, uses the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">sort_column</span> and <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">json_column</span> columns to fix up the mismatches between how MySQL works and how JSON works.</div>
<div>
<br /></div>
<div>
Besides reading, <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> can also insert, delete and update JSON objects. JSON arrays (i.e. MySQL tables) can also be reordered using the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">moveRow()</span> method.</div>
<div>
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// insert a row</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jh.insertRow('mytable', '', -1, '{"id": 1, "name": "bob"}',</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function() {</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// delete a row</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jh.deleteRow('mytable', '', 0,</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function() {</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// update a row</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jh.updateRow('mytable', 'WHERE id=1', 0, '{"id": 1, "name": "eric"}',</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function() {</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// move a row (huh?)</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jh.moveRow('mytable', 'WHERE id=1', 0, 1,</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function() {</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span></div>
</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> has a lot of nice attributes. Other solutions require new SQL syntax provided by specially modified MySQL database software, plugins or new versions but <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span> works with any MySQL version and any MySQL data. Existing applications and processes that use the MySQL database work without modification and can work with data that is inserted, updated and deleted using <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">jsonhib</span>.</div>
<div>
<br /></div>
<div>
Impossible? No, not impossible. JSON can be stored in MySQL.</div>
<div>
<br /></div>
<br />
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-67299392548887894382014-01-03T11:18:00.000-08:002014-01-03T11:18:30.022-08:00Rewrite a CSS stylesheet using PHPI've been writing a user-generated content system in PHP where users can upload CSS (and HTML) that will be shown in a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">DIV</span>. Of course, I wanted to restrict their CSS to only be applied inside the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">DIV</span> and not style the surrounding content (either accidentally or maliciously).<br />
<br />
The first step was easy: create a class and assign it to the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">DIV</span>.<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><div class="user_content"></span><br />
<br />
But how to restrict a CSS stylesheet with a bunch of random rules to that <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">DIV</span>?<br />
<br />
Given something like this:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">.block input, table td.money {</span><br />
...<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
How do I turn it into this?<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">.user_content .block input, .</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">user_content</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">table td.money</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {</span><br />
...<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
A long search finally ended when Stack Overflow revealed a way to insert my <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">user_content</span> class before every rule using a fancy regular expression ... but it broke down on comments with commas in them. It was a long search because it was very difficult to figure out what search terms to use and a real bummer that the solution broke down on something as common as comments.<br />
<br />
So, I developed this code to handle block out the commas in comments, apply the fancy regular expression from Stack Overflow and then put the commas back.<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function rewriteCss($css, $cssClazz) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> while (preg_match('/(\/\*.+?),(.+?\*\/)/', $</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">css</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">) === 1) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> $contents = preg_replace(</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> '/(\/\*.+?),(.+?\*\/)/', '$1:comma:$2', $</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">css</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> $</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">css</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> = preg_replace(</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> '/([^\r\n,{}]+)(,\s*(?=[^}]*{)|\s*{)/',</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> '.'.$</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">cssClazz</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">.' $1$2', $</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">css</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> while (preg_match('/(\/\*.+?):comma:(.+?\*\/)/', $</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">css</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">) === 1) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> $</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">css</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> = preg_replace(</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> '/(\/\*.+?):comma:(.+?\*\/)/', '$1,$2', $</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">css</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return $css;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
That did the trick. Each comma in a comment is blocked out by replacing it with <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">:comma:</span>. Then, a complicated <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">preg_replace()</span> call is made with the fancy regular expression from Stack Overflow. Finally, the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">:comma:</span> in comments are restored to being literal commas.<br />
<br />
Super.Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-23468508409098618212013-10-24T16:42:00.002-07:002013-10-24T16:46:01.420-07:00jQuery Tabs One Tab BugI needed to add tabs to my jQuery so, naturally, I selected jQuery Tabs. It worked great!<br />
<br />
Except for a gnarly jQuery bug. If I had only one tab, the tab background (e.g. the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ol</span>/<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ul</span> element) would be one pixel shorter than if I had two or more tabs. Also, with only one tab, there was an "off by one" bug where the one and only tab would descend by extra one pixel below the tab background. This "off by one" effect wasn't that noticeable, especially depending on the various colors involved, which is why it may have slipped by the jQuery Tab developers.<br />
<br />
The visual effect would be that, when the user took some action that would add a second tab, the tab background would grow by one pixel and the "off by one" effect would be fixed for all tabs.<br />
<br />
I spent a good number of hours hunting for the problem before finding it in <i>jquery.ui.tabs.css</i>. It contained a rule like this:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">.ui-tabs .ui-tabs-nav li.ui-tabs-active {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> margin-bottom: -1px;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> padding-bottom: 1px;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<div>
<br /></div>
<div>
The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">margin-bottom</span> style with a negative margin was the issue. The tab background calculated its height according to the height of its child tab (e.g. the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">li</span> element). But, apparently, if negative margins are used, the height of the child element is reduced by the negative margin for parent calculation purpose. For example, if a child element is 15 pixels high and has a margin of -2, the parent sees the child element as 13 pixels high.</div>
<div>
<br /></div>
<div>
The negative margin is only applied to the active tab. If there is only one tab, the tab background uses only that tab for calculation purposes and is one pixel too short. If a second tab is added, then, presto!, there is at least one inactive tab. Inactive tabs don't get the CSS rule that applies the negative margin so the "negative margin discount" isn't applied. The tab background grows by one pixel so it can contain the inactive tab and solves the issue for all tabs ... at least until there is one tab (which will be the active tab) again.</div>
<div>
<br /></div>
<div>
I confirmed the issue in two ways. First, I changed the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">margin-bottom</span> from -1 pixel to -10 pixels and saw the tab background shrink by 9 pixels. But, of course, when a second tab was added, the height of the tab background jumped by 9 pixels. Second, I created two tabs and then used the Inspector in my browser to hack in the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ui-tabs-active</span> CSS class to both tabs such that there were seemingly two active tabs and zero inactive tabs and was rewarded to see the tab background shrink.</div>
<div>
<br /></div>
<div>
But how to fix?</div>
<div>
<br /></div>
<div>
I tried hacking jQuery Tab JavaScript and modifying the CSS rule above. Sometimes, I fixed it but then I'd break something else. Finally, I realized that I needed a way to use the negative margin CSS rule only when there were two or more tabs (which worked already) and then apply a different CSS rule when there was one and only one tab.</div>
<div>
<br /></div>
<div>
CSS3 to the rescue! As it turns out, there is a way to create a CSS rule that is only applied when an element contains only one child.</div>
<div>
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">.ui-tabs .ui-tabs-nav li:first-child:nth-last-child(1) {</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> margin-bottom: 0px;</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> position: relative;</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> top: 1px;</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div>
</div>
<div>
<br /></div>
<div>
Rather than changing the CSS rule in <i>jquery.ui.tabs.css</i>, I modify it with my own CSS rule in my own stylesheet that is only applied when there is one child. My own rule resets <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">margin-bottom</span> from -1 to 0 and then uses <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">position: relative</span> and <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">top: 1px</span> to accomplish the same "border overlap" effect that negative margins were trying to achieve.</div>
<div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-15628864689332828842013-09-18T15:42:00.001-07:002013-09-18T15:42:25.332-07:00Fetch a Single Backbone.js Object via REST<br />
In a previous post, I described how to fetch a collection using Backbone.js RESTful Persistence. But, this morning, I was a little befuddled as to how to fetch a single object without fetching the entire collection.<br />
<br />
The trick is to create a new model with the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">id</span> attribute and add an options object to the model constructor with the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">urlRoot</span> property. <br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><!doctype html></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><html lang="en"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><head></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <meta charset="utf-8"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <title></title></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <meta name="description" content=""></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></head></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><body onload="onload();"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <script src="jquery-1.10.1.js"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <script src="underscore.js"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <script src="backbone.js"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <script language="JavaScript"> function onload() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// create a data model</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var Person = Backbone.Model.extend({</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> defaults: {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> name: '',</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> age: 1</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// create a data model collection</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var PersonCollection = Backbone.Collection.extend({</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> model: Person,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> url: 'backbone.php'</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var oneOff = new Person(<span class="Apple-style-span" style="color: #6aa84f;">{id: 138}, {urlRoot: 'backbone.php'}</span>);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">oneOff.fetch({</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> success: function(model) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> console.log(JSON.stringify(oneOff.toJSON()));</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">} </script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></body></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></html></span><br />
<div>
<br /></div>
<div>
But, of course, this required an improvement to PHP code in the "Backbone.js REST with PHP" post. Until now, a GET request always returned the entire collection from <i>backbone.php</i> but I had to update it to check the request URI and, if an object was specified, to find and <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">print</span> that object only.</div>
<div>
<br /></div>
<div>
I've made over a dozen changes to the "Backbone.js REST with PHP" post since I originally published it; I just keep stumbling across new features and quirks which seem to only be covered in bits and pieces all over the Internet.</div>
<div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-35253921762753680042013-09-11T11:17:00.000-07:002013-09-11T14:28:45.712-07:00Finally, A Simple Backbone.js Router!The Internet provides plenty of Backbone.js Router examples but not a simple, straightforward sample that can easily be demonstrated without a web server.<br />
<br />
A good example needs to be complete, show a generic implementation that will be often used verbatim and can be easily extended and that will highlight concerns. The following example shows how to implement a Backbone.js Router to access different pages while using a single HTML page and highlights when the page variables will be reset.<br />
<br />
Here's some code that you can copy-and-paste into a file named <i>router.html</i>:<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><!doctype html></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><html lang="en"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><head></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <meta charset="utf-8"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <title>Backbone.js Router Example</title></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <meta name="description" content=""></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></head></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><body></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><div id="page"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Welcome to Backbone.js routers!</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></div></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><input type="button" value="About"</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> onclick="<span class="Apple-style-span" style="color: #6aa84f;">pageRouter.navigate('about', {trigger: true});</span>"></input></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><input type="button" value="Page 1"</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> onclick="<span class="Apple-style-span" style="color: #6aa84f;">pageRouter.navigate('page/1', {trigger: true});</span>"></input></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><input type="button" value="Page 2"</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> onclick="<span class="Apple-style-span" style="color: #6aa84f;">pageRouter.navigate('page/2', {trigger: true});</span>"></input></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><input type="button" value="Page 3"</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> onclick="<span class="Apple-style-span" style="color: #6aa84f;">pageRouter.navigate('page/3', {trigger: true});</span>"></input></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><script src="jquery-1.10.1.js"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><script src="underscore.js"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><script src="backbone.js"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><script language="JavaScript"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var visited = 1;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var PageRouter = Backbone.Router.extend({</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> routes: {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> 'about': 'routeToAbout',</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> 'page/:num': 'routeToPage'</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">// one way to define a route handler</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> routeToAbout: function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> $('#page').html('About, visited '+visited+' pages');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ++visited;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// create a router instance</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var pageRouter = new PageRouter();</span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;">// a second way to define a route handler</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">pageRouter.on('route:routeToPage', function(num) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> $('#page').html('Page '+num+', visited '+visited+' pages');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ++visited;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;">Backbone.history.start();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></body></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></html></span></div>
<br />
<div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Assuming that you have the Backbone.js infrastructure (i.e. <i>jquery-1.10.1.js</i>, <i>underscore.js</i> and <i>backbone.js</i>), you can load this into any popular browser without a web server. For example, if you are using Windows and place your files in the C:\ folder, you can type the following directly into the location bar:</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
file:///C:/router.html</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
If you want to visit one of the routes, you can press the "About", "Page 1", "Page 2" or "Page 3" buttons on the web page. Or, you can directly visit the routes by copy-and-pasting the following URLs into the location bar:</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
file:///C:/router.html#about</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
file:///C:/router.html#page/1</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
file:///C:/router.html#page/2</div>
<div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
file:///C:/router.html#page/3</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Notice how the global variable, <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">visited</span>, is maintained (usually) or reset (when the page is reloaded). It is critical that your design take into account the circumstances under which page variables are reset.</div>
</div>
<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Some other examples neglect to include a call to <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Backbone.history.start()</span> and, without it, the page does nothing. A confusing oversight!</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Hopefully, this example lets you learn about Backbone.js Routers in a straightforward way without pausing to manipulate a web server.</div>
</div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com2tag:blogger.com,1999:blog-1163062631795397495.post-37911295510749613462013-09-06T11:55:00.000-07:002014-01-15T12:21:33.348-08:00Backbone.js REST with PHP<br />
Why isn't there a simple example where Backbone.js RESTful Persistence uses PHP?<br />
<br />
Backbone.js has Models and Collections which can be, in theory, easily connected to a REST backend so that adds, deletes and changes on the client are mirrored on the server.<br />
<br />
The following <i>sync.html</i> file is a simple demo (or, at least, as simple as I could make it!) that uses a single PHP page, <i>backbone.php</i>, as a server.<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><!doctype html></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><html lang="en"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><head></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <meta charset="utf-8"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <title></title></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <meta name="description" content=""></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></head></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><body onload="onload();"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <script src="jquery-1.10.1.js"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <script src="underscore.js"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <script src="backbone.js"></script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <script language="JavaScript"> function onload() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: #6aa84f;">// optional since these are the defaults</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Backbone.emulateHTTP = false;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Backbone.emulateJSON = false;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;">// create a data model</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var Person = Backbone.Model.extend({</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> defaults: {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> name: '',</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> age: 1</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;">// create a data model collection</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var PersonCollection = Backbone.Collection.extend({</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> model: Person,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> url: 'backbone.php'</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// store a collection first for nice demo</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var people = new PersonCollection([</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> new Person({"name": "Bob", "age": 20}),</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> new Person({"name": "Bill", "age": 25}),</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> new Person({"name": "John", "age": 30})</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">]);</span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;">// jQuery promise deals with async issues</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var promise = $.Deferred();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">people.forEach(function(person) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> promise.then(function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> console.log('saving '+person.get('name'));</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> person.save();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">promise.then(function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">// fetch the collection via HTTP GET</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> people = new PersonCollection();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> people.fetch({ success: function(collection) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">// pretty print the people who were fetched</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> console.log(JSON.stringify(collection.toJSON()));</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // another promise</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var promise = $.Deferred();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">// change Bob's age and store him via HTTP PUT</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> collection.filter(function(person) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return person.get('name') === 'Bob';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }).forEach(function(person) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> promise.then(function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> console.log('changing Bob\'s age');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> collection.get(person.id).set('age', 40).save();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">// change Bill's age and store him via HTTP PUT</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> collection.filter(function(person) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return person.get('name') === 'Bill';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }).forEach(function(person) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> promise.then(function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> console.log('changing Bill\'s age');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> person.set('age', 45).save();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">// change Bill's properties via HTTP PATCH</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> collection.filter(function(person) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return person.get('name') === 'Bill';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }).forEach(function(person) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> promise.then(function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> console.log('patching Bill');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> person.save({name: 'Billy', age: 55} , {patch: true});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: #6aa84f;">// create Tim via HTTP POST</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> promise.then(function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> console.log('creating Tim');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> collection.create({</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> name: 'Tim', age: 50</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }, {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> wait: true,</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> success: function(resp) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">// delete John via HTTP DELETE</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> collection.filter(function(person) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return person.get('name') === 'John';</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }).forEach(function(person) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> console.log('destroying John');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> person.destroy({ success: function(resp) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: #6aa84f;">// pretty print the people in the collection</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> console.log(JSON.stringify(collection.toJSON()));</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> promise.resolve();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">promise.resolve();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">} </script></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></body></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></html></span><br />
<br />
This page creates the initial collection, then executes a series of modifications, then shows the final collection. The modifications are as follows: (1) Bob's age is changed from 20 to 40; (2) Bill's age is changed from 25 to 45; (3) Bill's name is patched to "Billy" and his age is patched to 55; (4) Tim is created and added; and (5) John is deleted from the collection.<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">saving Bob</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">saving Bill</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">saving John</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">[{"id":41,"name":"Bill","age":25},{"id":978,"name":"Bob","age":20},{"id":726,"name":"John","age":30}]</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">changing Bob's age</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">changing Bill's age</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">patching Bill</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">creating Tim</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">destroying John</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">[{"id":41,"name":"Billy","age":55},{"id":978,"name":"Bob","age":40},{"name":"Tim","age":50,"id":689}]</span> <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<br />
It works by first creating a Backbone.js model called <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Person</span>.<br />
<br />
It then creates a Backbone.js collection called <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">PersonCollection</span> which, like a model, is a "kind" of object, not an object instance. By default, the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">url</span> property assigns <i>backbone.php</i> as the server URL that will handle Backbone.js RESTful Persistence.<br />
<br />
Then, a collection instance with model instances is created and stored in the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">people</span> variable.<br />
<br />
So far, no REST calls have been made. Backbone.js REST calls are asynchronous so, to avoid race conditions, it is helpful to use a feature like the jQuery promise APIs. The promise APIs allow the developer to make REST calls (or other function calls) to only let the next REST call start only after the previous REST call has finished. The three <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Person</span> objects in the collection are stored as promises so they can be saved to the backend in order.<br />
<br />
A final promise is added to execute the remaining operations. The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">people</span> variable is replaced with an empty collection and the previously saved models will be load into the collection using Backbone.js <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">fetch()</span> API as a test. A new <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">promise</span> variable is created and promises are made to change Bob's and Bill's ages. Finally, Tim is created, John is destroyed/deleted and the models left in both the client-side (i.e. the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">collection</span> variable) and the server-side (i.e. <i>store.txt</i>) are the same.<br />
<br />
The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">resolve()</span> function on the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">promise</span> variable kicks off the actual execution of the promises.<br />
<br />
Although I had hoped to avoid the complication of using the jQuery promise APIs in this example, the asynchronous nature of REST APIs make it necessary, even in this simple case.<br />
<br />
The only backend is a <i>backbone.php</i> page. It usually works but it has to use some hacks and tricks which aren't kosher. It uses a single <i>store.txt</i> file for a data store in the service of clarity but at the expense of reliability; a database would be better. It also might not work on every PHP server. That said ...<br />
<br />
... it will give you a good idea how Backbone.js RESTful Persistence works.<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><?php</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">$lock = fopen('log.txt', 'a+');<br />
$floc = flock($lock, LOCK_EX);<br />
fwrite($lock, $_SERVER['REQUEST_METHOD']." ");<br />
fwrite($lock, $_SERVER['REQUEST_URI']." lock\r\n");<br />
if (!$floc) {<br />
fwrite($lock, "flock() failed\r\n");<br />
}<br />
if ($_SERVER['REQUEST_METHOD'] == 'GET') {<br />
// see if fetching a single object<br />
$id = explode('/', $_SERVER['REQUEST_URI']);<br />
$id = array_pop($id);<br />
if (strpos(strrev($id), strrev('.php')) === 0) {<br />
<span class="Apple-style-span" style="color: #6aa84f;">// select (fetch) the collection</span><br />
$json = file_get_contents('store.txt');<br />
if ($json === false) {<br />
$json = "[\n]";<br />
}<br />
fwrite($lock, $json."\r\n");<br />
<span class="Apple-style-span" style="color: #6aa84f;">print $json;</span><br />
} else {<br />
<span class="Apple-style-span" style="color: #6aa84f;">// select (fetch) the object</span><br />
fwrite($lock, "GET $id\r\n");<br />
if ((string)(int)$id != $id) {<br />
$id = '"'.$id.'"';<br />
}<br />
// get the item from store.txt<br />
$json = file_get_contents('store.txt');<br />
if ($json === false) {<br />
fwrite($lock, "[\n]\r\n");<br />
fwrite($lock, "{}\r\n");<br />
print "{}";<br />
} else {<br />
fwrite($lock, $json."\r\n");<br />
$found = false;<br />
$json = explode("\n", $json);<br />
$json = array_slice($json, 1, -1);<br />
for ($p=0; $p < count($json); ++$p) {<br />
if (strpos($json[$p], '"id":'.$id) !== false) {<br />
$person = rtrim($json[$p], ', ');<br />
$found = true;<br />
break;<br />
}<br />
}<br />
if ($found) {<br />
fwrite($lock, $person."\r\n");<br />
<span class="Apple-style-span" style="color: #6aa84f;">print $person;</span><br />
} else {<br />
fwrite($lock, "{}\r\n");<br />
print "{}";<br />
}<br />
}<br />
}<br />
} else {<br />
// set flags for actions and options<br />
$emulateHTTP = false;<br />
$emulateJSON = false;<br />
$insert = false;<br />
$update = ($_SERVER['REQUEST_METHOD'] == 'PUT');<br />
$patch = ($_SERVER['REQUEST_METHOD'] == 'PATCH');<br />
$delete = ($_SERVER['REQUEST_METHOD'] == 'DELETE');<br />
if ($update || $patch) {<br />
// get the new value<br />
$person = file_get_contents('php://input');<br />
// don't trust $_SERVER['CONTENT_TYPE']<br />
if ((substr($person, 0, 1) != '{')<br />
&& (substr($person, 0, 1) != '[')) {<br />
$emulateJSON = true;<br />
}<br />
if ($emulateJSON) {<br />
$person = urldecode($person);<br />
$person = explode('=', $person);<br />
$person = $person[1];<br />
}<br />
} elseif ($_SERVER['REQUEST_METHOD'] == 'POST') {<br />
// get emulated action and value<br />
$emulateHTTP = true;<br />
if (isset($_POST['_method'])) {<br />
if ($_POST['_method'] === 'PUT') {<br />
$update = true;<br />
$person = $_POST['model'];<br />
} elseif ($_POST['_method'] === 'PATCH') {<br />
$patch = true;<br />
$person = $_POST['model'];<br />
} elseif ($_POST['_method'] === 'DELETE') {<br />
$delete = true;<br />
}<br />
} elseif (isset($_POST['model'])) {<br />
$person = urldecode($_POST['model']);<br />
$insert = true;<br />
} else {<br />
$person = file_get_contents('php://input');<br />
if ($person === '') {<br />
$delete = true;<br />
} else {<br />
$id = explode('/', $_SERVER['REQUEST_URI']);<br />
$id = array_pop($id);<br />
if (is_numeric($id)) {<br />
if (preg_match('/"id":(.*?)[,}]/', $person) === 1) {<br />
$update = true;<br />
}<br />
$patch = !$update;<br />
} else {<br />
$insert = true;<br />
$emulateHTTP = false;<br />
}<br />
}<br />
}<br />
}<br />
if ($delete) {<br />
<span class="Apple-style-span" style="color: #6aa84f;">// delete the object from store.txt</span><br />
$id = explode('/', $_SERVER['REQUEST_URI']);<br />
$id = array_pop($id);<br />
fwrite($lock, "DELETE $id\r\n");<br />
if ((string)(int)$id != $id) {<br />
$id = '"'.$id.'"';<br />
}<br />
// remove the item in store.txt<br />
$json = file_get_contents('store.txt');<br />
if ($json === false) {<br />
fwrite($lock, "[\n]\r\n");<br />
fwrite($lock, "[\n]\r\n");<br />
} else {<br />
fwrite($lock, $json."\r\n");<br />
$found = false;<br />
$json = explode("\n", $json);<br />
$json = array_slice($json, 1, -1);<br />
for ($p=0; $p < count($json); ++$p) {<br />
if (strpos($json[$p], '"id":'.$id) !== false) {<br />
$person = rtrim($json[$p], ', ');<br />
array_splice($json, $p, 1);<br />
array_push($json, rtrim(array_pop($json), ', '));<br />
$found = true;<br />
break;<br />
}<br />
}<br />
if ($found) {<br />
$json = implode("\n", $json);<br />
$json = "[\n".$json."\n]";<br />
file_put_contents('store.txt', $json);<br />
<span class="Apple-style-span" style="color: #6aa84f;">print $person;</span><br />
}<br />
fwrite($lock, $json."\r\n");<br />
}<br />
} else {<br />
<span class="Apple-style-span" style="color: #6aa84f;">// insert (create) or update the item in store.txt</span><br />
$verb = $update? 'PUT ': 'PATCH ';<br />
fwrite($lock, ($insert? 'POST ': $verb)."$person\r\n");<br />
if ($insert) {<br />
$id = rand(0, 999);<br />
$person = '{"id":'.$id.','.substr($person, 1);<br />
} elseif ($patch) {<br />
$id = explode('/', $_SERVER['REQUEST_URI']);<br />
$id = array_pop($id);<br />
$person = '{"id":'.$id.','.substr($person, 1);<br />
// PATCH is broken but use JSON to merge $person<br />
// with the value in store.txt<br />
}<br />
$json = file_get_contents('store.txt');<br />
if ($json === false) {<br />
fwrite($lock, "[\n]\r\n");<br />
$json = "[\n".$person."\n]";<br />
} else {<br />
fwrite($lock, $json."\r\n");<br />
preg_match('/"id":(.*?)[,}]/', $person, $id);<br />
$id = $id[1];<br />
$found = false;<br />
$json = explode("\n", $json);<br />
$json = array_slice($json, 1, -1);<br />
<span class="Apple-style-span" style="color: #6aa84f;">// replace the item with the new value</span><br />
if ($update || $patch) {<br />
for ($p=0; $p < count($json); ++$p) {<br />
if (strpos($json[$p], '"id":'.$id) !== false) {<br />
$last = ($p == count($json) - 1);<br />
$json[$p] = $person.($last? '': ',');<br />
$found = true;<br />
}<br />
}<br />
}<br />
$json = implode("\n", $json);<br />
// insert the new value<br />
if (!$found) {<br />
$json .= ",\n".$person;<br />
}<br />
$json = "[\n".$json."\n]";<br />
}<br />
fwrite($lock, $json."\r\n");<br />
file_put_contents('store.txt', $json);<br />
<span class="Apple-style-span" style="color: #6aa84f;">print $person;</span><br />
}<br />
} <br />
fwrite($lock, $_SERVER['REQUEST_METHOD']." unlock\r\n");<br />
flock($lock, LOCK_UN);<br />
fclose($lock);</span><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">?></span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
When the <i>sync.html</i> is loaded, it uses <i>backbone.php</i> to synchronize the models from the client-side to the server-side via REST. By setting the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">url</span> parameter on the Backbone.js Collection in <i>sync.html</i>, Backbone.js RESTful Persistence is activated and directed to <i>backbone.php</i> on the server.</div>
<div>
<br /></div>
<div>
The PHP <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">REQUEST_METHOD</span> is used to separate the HTTP actions into GET, PUT, PATCH, POST and DELETE. The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">people.fetch()</span> call in <i>sync.html</i> corresponds to the GET action in <i>backbone.php</i>. The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">person.save()</span> call in <i>sync.html</i> corresponds to the PUT or PATCH actions in <i>backbone.php</i>. The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">collection.create()</span> call in <i>sync.html</i> corresponds to the POST action in <i>backbone.php</i>. The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">person.destroy()</span> call in <i>sync.html</i> corresponds to the DELETE action in <i>backbone.php</i>.</div>
<div>
<br /></div>
<div>
The implementations of GET, PUT, PATCH, POST and DELETE all manipulate a <i>store.txt</i> file on the server that stores JSON data for the collection. It is complicated in places to avoid reliance on a JSON library in PHP but the end result is the same: adding, changing and deleting objects from a JSON array which is stored in <i>store.txt</i>.</div>
<div>
<br /></div>
<div>
I wish that the PHP <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">flock()</span> function calls were not necessary but they are a solid but not wholly successful way to handle the race conditions that can occur on PHP platforms. Race conditions can occur when several REST calls are executed simultaneously. Backbone.js does not wait from one REST action to finish before executing another one and, to keep <i>sync.html</i> simple, I've ignored this on the client and used PHP <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">flock()</span> to control this on the server. The <i>log.txt</i> file, which is used with PHP <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">flock()</span>, also serves as a log.</div>
<div>
<br /></div>
<div>
So, finally, here's something to get you started with Backbone.js and PHP.</div>
<div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-36128230686006909712013-08-22T14:24:00.000-07:002013-09-11T11:41:54.775-07:00Backbone.js and Two Dumb IdiomsI've been learning <a href="http://backbonejs.org/" target="_blank">Backbone.js</a> which is MVC (or MV*) for client-side JavaScript.<br />
<br />
In Backbone.js, you define a model (the "M") by doing this:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var Model1 = Backbone.Model.extend({</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<br />
You create an instance of a model by doing this:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var model1 = new Model1();</span><br />
<br />
To define a Backbone.js view (the "V"), you can do something like this:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var View1 = Backbone.View.extend({</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> events: {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> click: function(e) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> alert('Clicked!');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">});</span><br />
<br />
To create an instance of the view, you can do this:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a id="a1" href="#"><span>My Link</span></a></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">...</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var view1 = new View1({el: '#a1'});</span><br />
<br />
Now, here comes Dumb Idiom #1. You can do this:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">alert(view1.$el.css('display'));</span><br />
<br />
Say what? What's <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">view1.$el</span>? It's a dumb shorthand that saves two characters. It means <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">$(view1.el)</span> in regular old jQuery but instead of typing those two parentheses, you can use a shortcut that only works in Backbone.js and easily throws everybody, including yourself, for a loop.<br />
<br />
But, wait, there's more. Here comes Dumb Idiom #2. Instead of using jQuery <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">find()</span>, you can do this:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">alert(view1.$('span').html());</span><br />
<br />
What is this? In Backbone.js but not standard jQuery, you can use <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">view1.$('span')</span> as a shorthand for <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">$(view1.el).find('span')</span>. We save a few more characters here (i.e. nine) but pay for it with the need to memorize yet another idiom.<br />
<br />
Backbone.js, stick to your knitting! You aren't jQuery and shouldn't be inventing unnecessary and non-standard idioms for jQuery.Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com1tag:blogger.com,1999:blog-1163062631795397495.post-2310053615647247742013-07-10T12:05:00.000-07:002013-09-11T11:42:08.479-07:00More Simple JavaScript InheritanceI like John Resig's <a href="http://ejohn.org/blog/simple-javascript-inheritance/">Simple JavaScript Inheritance</a> ... except that I hate having to refactor my JavaScript code to use it.<br />
<br />
As John suggests, suppose that I have some code:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function Person(isDancing){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.dancing = isDancing;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Person.prototype.dance = function(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return this.dancing;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">};</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function Ninja(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.dancing = false;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Ninja.prototype.dance = function(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return this.dancing;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">};</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Ninja.prototype.swingSword = function(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return true;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">};</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
That's normal JavaScript object code. Now, I decide to use John Resig's "Simple JavaScript Inheritance". John Resig suggests that I rewrite my code like this:<br />
<br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">var Person = Class.extend({</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> init: function(isDancing){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.dancing = isDancing;</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> dance: function(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return this.dancing;</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">});</span><br />
<br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">var Ninja = Person.extend({</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> init: function(){</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> this._super( false );</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> dance: function(){</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> // Call the inherited version of dance()</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> return this._super();</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> swingSword: function(){</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return true;</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">});</span><br />
<div>
<br /></div>
<div>
Changes are in <span class="Apple-style-span" style="color: red;">red</span>. The implementations stay the same but the code syntax is different. It's a bit of a hassle if you have little bit of code and, for a lot of code, it's even more of a hassle.</div>
<div>
<br /></div>
<div>
Why can't I reuse my code as-is with only a few modifications? Something like this:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function Person(isDancing){</span></div>
<div>
<span class="Apple-style-span" style="font-size: small; line-height: normal;">
</span>
<div>
<span class="Apple-style-span" style="font-size: small; line-height: normal;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.dancing = isDancing;</span></span></div>
<span class="Apple-style-span" style="font-size: small; line-height: normal;">
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Person.prototype.dance = function(){</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return this.dancing;</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">};</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">Person = Class.extend(Person);</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function Ninja(){</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: red;">this._super( false );</span></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Ninja.prototype.dance = function(){</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: red;">// Call the inherited version of dance()</span></span></div>
<div>
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> return this._super();</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">};</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Ninja.prototype.swingSword = function(){</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return true;</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">};</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;">Ninja = Person.extend(Ninja);</span></div>
</span><br />
<div>
<br /></div>
Instead of rewriting the code into a new code syntax, I only add an <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">extend()</span> call at the end of each new JavaScript class. Once I've done that, if I want, I can call <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">this._super()</span> where ever I need to.<span class="Apple-style-span" style="font-size: small; line-height: normal;"></span><br />
<div>
<span class="Apple-style-span" style="font-size: small; line-height: normal;"><span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"><br /></span></span></div>
<span class="Apple-style-span" style="font-size: small; line-height: normal;">
</span><br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
I make this possible using a slight modification to John Resig's script. By checking to see if the argument passed to <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">extend()</span> is a function, instead of an object, the script can handle both syntaxes: John Resig's original syntax and the traditional JavaScript object syntax.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">/* Simple JavaScript Inheritance</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> * By John Resig http://ejohn.org/</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> * Init-by-function modification</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> * By Daniel Howard http://www.svexpertise.com/</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> * MIT Licensed.</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> */</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">// Inspired by base2 and Prototype</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">(function(){</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // The base Class implementation (does nothing)</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.Class = function(){};</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Create a new Class that inherits from this class</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Class.extend = function(prop) {</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var _super = this.prototype;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> if ( typeof prop == 'function' ) {</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> prop.prototype.init = prop;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> for (var name in prop)</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> prop.prototype[name] = prop[name];</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> prop = prop.prototype;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="color: red; font-family: 'Courier New', Courier, monospace;"> }</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Instantiate a base class (but only create the instance,</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // don't run the init constructor)</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> initializing = true;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var prototype = new this();</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> initializing = false;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Copy the properties over onto the new prototype</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> for (var name in prop) {</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Check if we're overwriting an existing function</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> prototype[name] = typeof prop[name] == "function" &&</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> typeof _super[name] == "function" && fnTest.test(prop[name]) ?</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> (function(name, fn){</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return function() {</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var tmp = this._super;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Add a new ._super() method that is the same method</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // but on the super-class</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this._super = _super[name];</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // The method only need to be bound temporarily, so we</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // remove it when we're done executing</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var ret = fn.apply(this, arguments); </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this._super = tmp;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return ret;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> };</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })(name, prop[name]) :</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> prop[name];</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // The dummy class constructor</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function Class() {</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // All construction is actually done in the init method</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if ( !initializing && this.init )</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.init.apply(this, arguments);</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Populate our constructed prototype object</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Class.prototype = prototype;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Enforce the constructor to be what we expect</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Class.prototype.constructor = Class;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // And make this class extendable</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Class.extend = arguments.callee;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return Class;</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> };</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">})();</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
When a function is detected, instead of an object, the function is first saved to the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">init</span> property. Then, all enumerable function properties (which will behave pretty much like static variables in classical class-based systems) are saved to the function's prototype object. Finally, and here's the magic pixie dust, the function's prototype object is used as the argument which the rest of the code generates the JavaScript class from. John Resig's code behaves the same but the function's prototype object becomes "the class object" instead of being directly passed as the original argument.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Person = Class.extend(Person);</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">...</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Ninja = Person.extend(Ninja);</span></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">extend()</span> calls above take the JavaScript constructor functions and return the new JavaScript class. By assigning the return value to the function (variable), the new JavaScript class replaces the traditional JavaScript object creation function.</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
With this modification and technique, you can write traditional JavaScript object code and, if you later want to use John Resig's "Simple JavaScript Inheritance", you can add it with ease.</div>
</div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-88321589828814253372013-06-03T16:52:00.002-07:002013-06-04T11:10:41.929-07:00Effective XML by Elliotte Rusty Harold (2004)<br />
I finished reading Effective XML by Elliotte Rusty Harold over the weekend. It's an old book, almost 10 years old, published in 2004. Still, it is interesting and I wanted to summarize the book for myself and anybody else who is interested.<br />
<br />
It is divided into 4 parts: Syntax, Structure, Semantics and Implementation. Total, there are 50 suggestions to improving your XML.<br />
<br />
<h2>
Syntax</h2>
<h3>
#1: Include an XML declaration.</h3>
Like “<?xml version="1.0" encoding="utf-8" standalone="yes" ?>” for example.<br />
<h3>
#2: Mark up with ASCII if possible.</h3>
Don’t use Chinese characters as tag names.<br />
<h3>
#3: Stay with XML 1.0.</h3>
XML 1.1 makes several inadvisable things possible, like tag names in obscure, non-alphabetic languages.<br />
<h3>
#4: Use standard entity references.</h3>
Prefer named references (e.g. &Ecaron;) to character references (e.g. &#x011B;). Don’t invent your own names if somebody else already has.<br />
<h3>
#5: Comment DTDs liberally.</h3>
DTDs are hard to understand. Use lots of comments so they can be understood.<br />
<h3>
#6: Name elements with camel case.</h3>
CamelCase instead of camel-case is easier to map to variables.<br />
<h3>
#7: Parameterize DTDs.</h3>
You can build all kinds of flexibility into DTDs using parameters, including conditionals and changing namespaces.<br />
<h3>
#8: Modularize DTDs.</h3>
You can split DTDs into multiple files for more flexibility.<br />
<h3>
#9: Distinguish text from markup.</h3>
The title isn’t very good but what he’s saying is that, once you delimit XML tags and use them as text, they aren’t accessible to the parser. They are just text.<br />
<h3>
#10: White space matters.</h3>
White space, such as newlines and indentation, will be included in a text node when it’s read in. In other cases, like DTDs, it is irrelevant.<br />
<br />
<h2>
Structure</h2>
<h3>
#11: Make structure explicit through markup.</h3>
Avoid mini-formats where the developer has to parse a text node or attribute value to separate it further. Use tags instead of embedding spaces or commas in text or values.<br />
<h3>
#12: Store metadata in attributes.</h3>
The content/text should be the data itself; attributes should give metadata about the data. The content/text should contain what people normally want to see with attributes hiding away less important info.<br />
<h3>
#13: Remember mixed content.</h3>
A single sentence isn’t necessarily a single text node. It might be multiple text nodes, separated by child tags enclosing certain fragments. Don’t assume that the XML is flat or expect a rigid schema.<br />
<h3>
#14: Allow all XML syntax.</h3>
Don’t invent an XML format or XML parser that forbids XML processing directives, comments or other standard XML features.<br />
<h3>
#15: Build on top of structures, not syntax.</h3>
Don’t try to differentiate between things that the XML parser says are the same. Don’t write applications that do something different if it is a named reference vs a character reference. Or do something different if it is an empty element versus an element with the empty string.<br />
<h3>
#16: Prefer URLs to unparsed entities and notations.</h3>
DTDs can be used to define unparsed entities and notations but it’s better to avoid them and just stick the value right in the XML itself.<br />
<h3>
#17: Use processing instructions for process-specific content.</h3>
XML processing instructions, like “<?xml-stylesheet ?>”, are not returned by parsers as easily but have their uses, especially for data that cuts across parent-child relationships. Use them where appropriate.<br />
<h3>
#18: Include all information in the instance document.</h3>
Avoid using DTD and other XML features that modify the XML data from outside, such as default attributes. The XML should contain all the data, even if the parser doesn’t read the DTD or other linked documents.<br />
<h3>
#19: Encode binary data using quoted printable and/or Base64.</h3>
Binary data can be encoded and inserted in XML. If you need it, do that.<br />
<h3>
#20: Use namespaces for modularity and flexibility.</h3>
Namespaces look like URLs but they are just IDs. Choose and use a namespace for the data. Don’t avoid it just because you don’t understand it or don’t care.<br />
<h3>
#21: Rely on namespace URIs, not prefixes.</h3>
Don’t use the “svg:” prefix without setting it to “http://www.w3.org/2000/svg”. Don’t use namespaces without defining the namespace URI.<br />
<h3>
#22: Don’t use namespace prefixes in element content and attribute values.</h3>
It is very confusing when XML prefixes are used other places than as a tag name. Instead, require the full namespace URL to be used, maybe as an attribute that modifies the unprefixed tag name. For example, instead of <element xmlns:ex=”…” type=”ex:year”>, do <element type=”year” typens=”…”>.<br />
<h3>
#23: Reuse XHTML for generic narrative content.</h3>
Use XHTML to content that is paragraphs of text instead of inventing your own schema or restricting the content to unformatted text.<br />
<h3>
#24: Choose the right schema language for the job.</h3>
You have a choice between DTDs and XML Schema. There is also RELAX NG, Schematron or even using Java.<br />
<h3>
#25: Pretend there’s no such thing as the PSVI.</h3>
PSVI is XML data annotated with its schema information produced by advanced XML parsers. Some libraries can read the PSVI and produces a memory objects automatically from XML data, like Hibernate does for SQL databases. PSVI is a nice theory but not practical.<br />
<h3>
#26: Version documents, schemas and stylesheets.</h3>
Add version numbers to XML and its related documents because it will change over time. You can use dates or major/minor versions. Don’t assume that your data, schemas and stylesheets will never need revision.<br />
<h3>
#27: Mark up according to meaning.</h3>
Put XML tags around things according to what they are, not just how they are formatted. For example, italics has several different uses so be more specific with the XML tag.<br />
<br />
<h2>
Semantics</h2>
<h3>
#28: Use only what you need.</h3>
XML has lots of parts: XML 1.0, well-formedness, DTDs, Namespaces, XPath, Schemas, XLinks (Simple and Extended), XPointers, XInclude, Infoset, PSVI, XML 1.1, Namespaces 1.1, SVG, MathML, RDF, OWL, CSS, XSLT, XSL-FO, XQuery and so on. Use what you need. Don’t feel that you have to understand and use it all.<br />
<h3>
#29: Always user a parser.</h3>
Don’t try to write your own XML parser using regular expressions or something. Use an off-the-shelf XML parser.<br />
<h3>
#30: Layer functionality.</h3>
Feel free to process XML is whatever order works best for you. Feel free to do validation before and/or after other processing. Creating a processing chain that gets you to your final result.<br />
<h3>
#31: Program to standard APIs.</h3>
Write code so it is easy to swap in a new parser.<br />
<h3>
#32: Choose SAX for computer efficiency.</h3>
SAX is an event-based, streaming parser. Efficiency isn’t usually needed so you normally don’t need SAX.<br />
<h3>
#33: Choose DOM for standards support.</h3>
DOM is a solid standard with lots of implementations. Many developers understand it. It is weird in some places, though.<br />
<h3>
#34: Read the complete DTD.</h3>
If standalone is set to “no” in the XML declaration, the DTD is required. Skipping a required DTD may result in parsing errors. Be flexible in accepting XML data by reading DTDs.<br />
<h3>
#35: Navigate with XPath.</h3>
Doing “//name” with XPath is easier and less error prone than crawling the DOM tree using getChildNode(). It’s hard to write getChildNode() code that doesn’t rely on tag parent-child relationships, tag order, number of tags and other variations in XML data.<br />
<h3>
#36: Serialize XML with XML.</h3>
Don’t convert XML into an opaque binary format for no reason. Leave XML as XML.<br />
<h3>
#37: Validate inside your program with schemas.</h3>
Validate XML data using schemas rather than just breaking/crashing. The point of validation is to detect invalid data.<br />
<br />
<h2>
Implementation</h2>
<h3>
#38: Write in Unicode.</h3>
Use UTF-8. ASCII is a subset of UTF-8. If using Japanese, Chinese or similar languages, use UTF-16. Don’t use obsolete ASCII formats. Do Unicode correctly with normalization and sorting.<br />
<h3>
#39: Parameterize XSLT stylesheets.</h3>
Use xsl:variable (like a constant) and xsl:param to make it easy to change fonts, sizes and other stuff in XSLT.<br />
<h3>
#40: Avoid vendor lock-in.</h3>
Avoid tools that have binary XML formats, unclear tag names, proprietary XML parsers and proprietary APIs.<br />
<h3>
#41: Hang on to your relational database.</h3>
XML does not replace SQL databases but you can use XML with them.<br />
<h3>
#42: Document namespaces with RDDL.</h3>
Namespaces are just IDs but people still try to use them as URLs. RDDL is an XML schema for a web page that is posted at a namespace “URL” that can provide resources, natures and purposes (such as DTDs) that might be useful.<br />
<h3>
#43: Preprocess XSLT on the server side.</h3>
For speed and consistency, preprocess and cache XSLT transformations on the server side using web server plugins.<br />
<h3>
#44: Serve XML+CSS to the client.</h3>
Browser clients can style using XSLT. CSS can be applied conditionally, depending on the display type.<br />
<h3>
#45: Pick the correct MIME media type.</h3>
Have your web server serve up application/xml instead of text/xml. Use more official, more accurate mime types, like application/xml+svg, if appropriate.<br />
<h3>
#46: Tidy up your HTML.</h3>
Converting HTML to XHTML will uncover bugs which are worth fixing. Do validation and fix any bugs that are found. Really old browsers don’t support some XHTML constructs.<br />
<h3>
#47: Catalog common resources.</h3>
XML Catalogs, used with parsers, allow you locally cache remote resources like DTDs and schemas. Instead of getting the file from a remote site, the request is redirected to the local machine.<br />
<h3>
#48: Verify documents with XML digital signatures.</h3>
You probably don’t need it but there is a standard for doing digital signatures in XML.<br />
<h3>
#49: Hide confidential data with XML encryption.</h3>
You probably don’t need it but there is a standard for doing encryption in XML.<br />
<h3>
#50: Compress if space is a problem.</h3>
XML doesn’t waste that much space but, if needed, you can compress it.<br />
<br />Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0tag:blogger.com,1999:blog-1163062631795397495.post-69801434399427526982013-05-28T15:19:00.001-07:002013-09-11T11:38:07.827-07:00XXHTMLDo you know what XML is? An example of XML is:<br />
<div>
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><?xml version="1.0" encoding="utf-8" standalone="yes" ?></span></div>
</div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><person></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <name type="common">Bob</name></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <name type="scientific">Homo Sapiens</name></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <intelligence>Average</intelligence></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></person></span></div>
<div>
<br /></div>
<div>
XML is a standard data format. It looks like HTML but you can make up your own tags and attribute names.</div>
<div>
<br /></div>
<div>
If you put the XML above into a file named <i>bob.xml</i> and load it into a browser like Firefox, you get a nice view into the XML data, laid out in a tree. The browser shows this as a courtesy. It is only useful to the programmer as an informational tool; the tree display isn't used in programs, web sites or end users.</div>
<div>
<br /></div>
<div>
If you rename <i>bob.xml</i> to <i>bob.html</i> and load <i>bob.html</i> into a browser, it might be blank or it might be an unformatted jumble of text.</div>
<div>
<br /></div>
<div>
To display the XML in HTML, you can use XSL.</div>
<div>
<br /></div>
<div>
First, you need to add a reference to the XSL file from the XML file:</div>
<div>
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><?xml-stylesheet type="text/xsl" href="reader.xsl" ?></span></div>
</div>
<div>
<br /></div>
<div>
Now, the XML file looks like this:</div>
<div>
<br /></div>
<div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><?xml version="1.0" encoding="utf-8" standalone="yes" ?></span></div>
</div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><?xml-stylesheet type="text/xsl" href="reader.xsl" ?></span></div>
</div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<div style="font-family: Times;">
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><person></span></span></div>
</div>
<div style="font-family: Times;">
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <name type="common">Bob</name></span></span></div>
</div>
<div style="font-family: Times;">
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <name type="scientific">Homo Sapiens</name></span></span></div>
</div>
<div style="font-family: Times;">
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <intelligence>Average</intelligence></span></span></div>
</div>
<div style="font-family: Times;">
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></person></span></span></div>
</div>
<div style="font-family: Times;">
</div>
</div>
</div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
Next, you need to write a XSL stylesheet. A partial version of the <i>reader.xsl</i> file might look like:</div>
<div>
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><?xml version="1.0" encoding="utf-8"?> </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><!DOCTYPE xsl:stylesheet [</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <!ENTITY nbsp "&#160;"></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">]></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://example.com/namespace" exclude-result-prefixes="str"></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><xsl:output method="html" encoding="iso-8859-1" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><xsl:template match="/"></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><html></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><head></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><title>People</title></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></head></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><body></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><xsl:for-each select="//person"></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ...</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <xsl:value-of select="text()" /></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ...</span></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <xsl:choose></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <xsl:when test="position() mod 2 = 1"></span></div>
</div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ...</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></xsl:for-each></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></body></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></html></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></xsl:template></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></xsl:stylesheet></span></div>
</div>
<div>
<br /></div>
<div>
Ugh. All that trouble, just to get some simple HTML. Not to mention that ordinary HTML hackers aren't likely to understand your XML and they surely won't understand your XSL. Plus, when you select "View Source" from the menus in a browser, many browsers show only the original XML and don't show the XSL or the final HTML that is shown in a browser.</div>
<div>
<br /></div>
<div>
Why does it have to be so hard? Why can't renaming <i>bob.xml</i> to <i>bob.html</i> just work, at least in some simple way?</div>
<div>
<br /></div>
<div>
I propose a simple standard: XXHTML. This stands for "XML friendly XHTML".</div>
<div>
<br /></div>
<div>
Instead of using custom XML tags, XXHTML use XHTML like this:</div>
<div>
<br /></div>
<div>
<div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><?xml version="1.0" encoding="utf-8" standalone="yes" ?></span></div>
</div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><?xml-stylesheet type="text/xsl" href="reader.xsl" ?></span></div>
</div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><html></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><head></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><title>People</title></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><style></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">span {</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> display: block;</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></head></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><body></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <div class="person"></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="name" type="common">Bob</span></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="name" type="scientific">Homo Sapiens</span></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="intelligence">Average</span></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </div></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></body></span></div>
</div>
</div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></html></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
Rather than use custom tags, like <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">person</span>, let's use standard XHTML tags, like <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">span</span>, but encode them such that a standard XHTML property, like <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">class</span>, encapsulates custom XML tag name. We can do that in such a way that it is easy to pick out all the information using XML and XPath in XSL but still have it be normal looking HTML.</div>
<div>
<br /></div>
<div>
By doing this, renaming <i>bob.xml</i> to <i>bob.html</i> is actually useful and makes sense to HTML hackers. But it also provides all the same functionality in XML and XSL.</div>
<div>
<br /></div>
<div>
XXHTML is a win-win.</div>
<div>
<br /></div>
<div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/10968880956025044019noreply@blogger.com0