I'm not referring to the clothes you wear, rather the conventions you use in your coding. In fact it's less about style and more following a set of style rules - so closer to being fashionable. There are some examples of style guides out there and they range from the conservative like Google's style guide to the more extreme such as NPM's
I'm not going to tell you what style out there is best, instead I'm only going to tell you that when you're working on previous code or another project you should follow their guidelines. It's definitely easier on the eyes when jumping between files and the code looks the same. In fact go have a look at the JQuery source or any other large open source project with a lot of contributors and see if you can spot which parts are written by different people.
If you're writing for yourself or starting off with a new team have a look at different styles and see which is easiest to understand, even try writing a bit in each and see which flows more naturally for you. It's quite objective which style can be best and a good programmer should be able to adjust between the formats relatively easy.
Monday, April 30, 2012
Wednesday, April 25, 2012
optimizes often used templates
In the todomvc posts I've been doing the todo creates it's dom from a string:
But we can do one better. The htmlToDocumentFragment works by setting the string as the innerHTML of a div then grabbing the dom underneath it. A better option would be to set the DOM structure once and then use a deep clone to get the structure for other todos. So how should it look?
What we do is the first time the file is opened we set a class variable on mvc.todocontrol and then we can just do a deep clone of that making things faster which could be very useful if we start to have thousands of todo items on the page.
todomvc.todocontrol.prototype.createDom = function() {
this.el = goog.dom.htmlToDocumentFragment('<li>' +
'<div class="view">' +
'<input class="toggle" type="checkbox">' +
'<label>' + this.getModel().get('text') + '</label>' +
'<a class="destroy"></a>' +
'</div>' +
'<input class="edit" type="text" value="Create a TodoMVC template">' +
'</li>');
this.setElementInternal(this.el);
};
But we can do one better. The htmlToDocumentFragment works by setting the string as the innerHTML of a div then grabbing the dom underneath it. A better option would be to set the DOM structure once and then use a deep clone to get the structure for other todos. So how should it look?
todomvc.todocontrol.dom = goog.dom.htmlToDocumentFragment('<li>' +
'<div class="view">' +
'<input class="toggle" type="checkbox">' +
'<label>' + this.getModel().get('text') + '</label>' +
'<a class="destroy"></a>' +
'</div>' +
'<input class="edit" type="text" value="Create a TodoMVC template">' +
'</li>');
todomvc.todocontrol.prototype.createDom = function() {
this.el = todomvc.todocontrol.dom.cloneNode(true);
this.setElementInternal(this.el);
};
What we do is the first time the file is opened we set a class variable on mvc.todocontrol and then we can just do a deep clone of that making things faster which could be very useful if we start to have thousands of todo items on the page.
PlastronJS by example pt6
this post we're going to move code in to the todomvc template.
First download the todomvc template from https://github.com/addyosmani/todomvc (it should be in a folder called template) and copy everything across to the folder. Next we'll move the contents of /lib to /js/libs.
now some editing. I edited the index.html file to add a class name the same as id to "toggle-all" as I want to use the mvc.Control to handle those events and at the moment it only takes a classname to clarify the element, and one to the main input as "todo-entry" for the same reason.
Next we edit the part at the bottom where it asks for our scripts so we have this:
<script src="js/libs/closure-library/closure/goog/base.js"></script>
<script src="deps.js"></script>
<script src="js/app.js"></script>
<script>
todomvc.main();
</script>
I then copy and pasted our main.js in to app.'s and because it's in a closer I gave the window a reference to todomvc:
you might also notice that above I changed the todolistcontrol to decorate rather than render. You do this when there is already a DOM structure that the control should use. And here is the list control:
What I've done is add a schema to the list. The schema lists a new attribute on the list called completed. It has a get which tells me what I want back and model: true which tells the schema that the get uses the list of models and so changes should fire when the models change. I can then get a list of completed models any time by using .get('completed') but more importantly I can listen for changes on 'completed'.
We no longer need the createDom as it's already there for us so we move on to enterDocument.
The keyup event doesn't change except to tell the new models where they should render.
The next two clicks are for the check all and clear completed. They both run through the models in the list and one will set their complete value the other will tell the model to dispose itself.
The next is on a model change and will hide the footer and list if need be, I have similar lines of code to make it run the first time (in case in the future I want to already have notes displayed).
The last one binds to the 'completed' key I set in the schema and will update the items left when the completed number changes.
Now on to the todo:
here I change the createDom to use the HTML in the index.html (and I remove it from the index.html). I have four listeners setup, two are listening on the view and two on the model. So I listen on the view for the check box or the dispose button and I set the complete attribute or dispose of the model. I also listen to the complete attribute and set the views class and toggle check and lastly I listen for dispose on the model and dispose of my control.
Last thing to do is run the depswriter with the new file and directories:
js/libs/closure-library/closure/bin/calcdeps.py --dep js/libs/closure-library --input js/app.js --path js/libs/plastronjs --path js/libs/plastronjs/sync --path js/ --output_mode deps > deps.js
and that's it!
I have removed the functionality to edit a note but that shouldn't be too tricky. Next thing we need to do is put in comments for the functions and see how it all compiles.
N.B: download latest PlastronJS - there are a few fixes
First download the todomvc template from https://github.com/addyosmani/todomvc (it should be in a folder called template) and copy everything across to the folder. Next we'll move the contents of /lib to /js/libs.
now some editing. I edited the index.html file to add a class name the same as id to "toggle-all" as I want to use the mvc.Control to handle those events and at the moment it only takes a classname to clarify the element, and one to the main input as "todo-entry" for the same reason.
Next we edit the part at the bottom where it asks for our scripts so we have this:
<script src="js/libs/closure-library/closure/goog/base.js"></script>
<script src="deps.js"></script>
<script src="js/app.js"></script>
<script>
todomvc.main();
</script>
I then copy and pasted our main.js in to app.'s and because it's in a closer I gave the window a reference to todomvc:
/* [MIT licensed](http://en.wikipedia.org/wiki/MIT_License) (c) [You](http://addyosmani.github.com/todomvc/) */ goog.provide('todomvc.main'); goog.require('mvc.Collection'); goog.require('todomvc.listcontrol'); goog.require('goog.dom'); (function() { // Your starting point. Enjoy the ride! todomvc.main = function() { var todolist = new mvc.Collection(); var todolistControl = new todomvc.listcontrol(todolist); todolistControl.decorate(goog.dom.getElement('todoapp')); window['a'] = todolist; window['b'] = todolistControl; }; window.todomvc = todomvc; })();
you might also notice that above I changed the todolistcontrol to decorate rather than render. You do this when there is already a DOM structure that the control should use. And here is the list control:
goog.provide('todomvc.listcontrol'); goog.require('mvc.Control'); goog.require('todomvc.todocontrol'); goog.require('goog.dom'); todomvc.listcontrol = function(model) { goog.base(this, model); this.getModel().setSchema({ 'completed': { get: function() { return this.getModels(function(mod) { return mod.get('complete'); }); }, models: true } }); }; goog.inherits(todomvc.listcontrol, mvc.Control); todomvc.listcontrol.prototype.enterDocument = function() { goog.base(this, 'enterDocument'); this.on('keyup', function(e) { // on return if (e.keyCode != 13) return; // create new model var text = (this.getEls('input')[0]).value; var newModel = this.getModel().newModel({'text': text}); //create new model control var newModelControl = new todomvc.todocontrol(newModel); newModelControl.render(goog.dom.getElement('todo-list')); }, 'todo-entry'); this.click(function(e) { goog.array.forEach(this.getModel().get('completed'), function(model) { model.dispose(); }); }, 'clear-completed'); this.click(function(e) { goog.array.forEach(this.getModel().getModels(), function(mod) { mod.set('complete', e.target.checked); }); }, 'toggle-all'); this.getModel().modelChange(function() { goog.dom.getElement('main').style.display = this.getModels().length ? 'block' : 'none'; goog.dom.getElementsByTagNameAndClass('footer')[0].style.display = this.getModels().length ? 'block' : 'none'; }); goog.dom.getElement('main').style.display = this.getModel().getModels().length ? 'block' : 'none'; goog.dom.getElementsByTagNameAndClass('footer')[0].style.display = this.getModel().getModels().length ? 'block' : 'none'; this.getModel().bind('completed', function(mods) { goog.dom.setTextContent(goog.dom.getElement('todo-count'), (this.getModel().getModels().length - mods.length) + ' items left'); }, this); };
What I've done is add a schema to the list. The schema lists a new attribute on the list called completed. It has a get which tells me what I want back and model: true which tells the schema that the get uses the list of models and so changes should fire when the models change. I can then get a list of completed models any time by using .get('completed') but more importantly I can listen for changes on 'completed'.
We no longer need the createDom as it's already there for us so we move on to enterDocument.
The keyup event doesn't change except to tell the new models where they should render.
The next two clicks are for the check all and clear completed. They both run through the models in the list and one will set their complete value the other will tell the model to dispose itself.
The next is on a model change and will hide the footer and list if need be, I have similar lines of code to make it run the first time (in case in the future I want to already have notes displayed).
The last one binds to the 'completed' key I set in the schema and will update the items left when the completed number changes.
Now on to the todo:
goog.provide('todomvc.todocontrol'); goog.require('mvc.Control'); todomvc.todocontrol = function(model) { goog.base(this, model); this.editable = false; }; goog.inherits(todomvc.todocontrol, mvc.Control); todomvc.todocontrol.prototype.createDom = function() { this.el = goog.dom.htmlToDocumentFragment('<li>' + '<div class="view">' + '<input class="toggle" type="checkbox">' + '<label>' + this.getModel().get('text') + '</label>' + '<a class="destroy"></a>' + '</div>' + '<input class="edit" type="text" value="Create a TodoMVC template">' + '</li>'); this.setElementInternal(this.el); }; todomvc.todocontrol.prototype.enterDocument = function() { this.click(function(e) { if (e.target.checked) { this.getModel().set('complete', true); } else { this.getModel().set('complete', false); } }, 'toggle'); this.getModel().bind('complete', function(complete) { if (complete) { goog.dom.classes.add(this.getElement(), 'done'); } else { goog.dom.classes.remove(this.getElement(), 'done'); } this.getEls('.toggle')[0].checked = complete; }, this); this.getModel().bindUnload(function() { this.dispose(); }, this); this.click(function(e) { e.preventDefault(); this.getModel().dispose(); return false; }, "destroy") };
here I change the createDom to use the HTML in the index.html (and I remove it from the index.html). I have four listeners setup, two are listening on the view and two on the model. So I listen on the view for the check box or the dispose button and I set the complete attribute or dispose of the model. I also listen to the complete attribute and set the views class and toggle check and lastly I listen for dispose on the model and dispose of my control.
Last thing to do is run the depswriter with the new file and directories:
js/libs/closure-library/closure/bin/calcdeps.py --dep js/libs/closure-library --input js/app.js --path js/libs/plastronjs --path js/libs/plastronjs/sync --path js/ --output_mode deps > deps.js
and that's it!
I have removed the functionality to edit a note but that shouldn't be too tricky. Next thing we need to do is put in comments for the functions and see how it all compiles.
N.B: download latest PlastronJS - there are a few fixes
Thursday, April 19, 2012
Javascript Gentleman Rule #1
A Gentleman never goes past 80 characters
A simple one, and one I used to break myself a lot. It's cool writing bit long lines which do everything, after all you're writing less lines of code right?
Then I found that at my new work place my boss liked to check the commits in an 80 character width vim instance. I know we all have wide screens now but think about it for a second, if we split long lines up then all that whitespace to the right of short lines are just wasted space. Splitting a line at 80 columns means we waste less screen space, are compatible with other people's viewing habits and can also give us a rough guideline as to how much information we should be putting on to each line. Possibly the biggest thing we gain from this though is that because we now know the absolute width of each file we can have several editor windows (or a spit view) on a screen and know exactly how wide that window should be without having to scroll to the right.
Luckily most editors have an in built option to put a line at the 80 character mark, or wrap the text at that point to allow you to easily see when you've gone past it. Vim hasn't got anything directly in, but just do a quick google search and there are solutions to highlight the column or highlight text going past it.
All the major lint tools should also have options, and good old Closure Linter definitely does.
This does raise a question though - what should we do if a line is too long? how do we split it up? Hopefully you can deal with that in style guides but a good first rule is to make a new line at the last comma. I'll go in to indentation in another rule
Wednesday, April 18, 2012
PlastronJS By Example pt5
now we'll put in some more functionality which will include the count of todos the ability to check a todo as complete and also remove a todo.
first the list control we want to add in a count:
what I've done is add in a div at the end and then put in a listener on modelChange which will fire if any models are added or removed (or are changed so that they are sorted differently) and then just display the number of notes. Now on to the todo:
Here I've added in a checkbox and a delete div and given them click functions. On clicking the checkbox it looks to see if the element is checked and changes the style of the text. It also sets 'complete' on the model or unsets it.
the delete will call dispose on the model which in turn will let the collection know it is disposed so the listener we put on to the collection will fire and update the count. We then dispose our control which will remove itself from the dom and clean up any event listeners it has setup.
first the list control we want to add in a count:
goog.provide('todomvc.listcontrol'); goog.require('mvc.Control'); goog.require('todomvc.todocontrol'); todomvc.listcontrol = function(model) { goog.base(this, model); }; goog.inherits(todomvc.listcontrol, mvc.Control); todomvc.listcontrol.prototype.createDom = function() { this.el = goog.dom.htmlToDocumentFragment("<div>" + "<div>Todo</div>" + "<div><input type='text' class='todoform'/></div>" + "<div class='todolist'></div>" + "<div class='count'></div></div>"); console.log(this.el); this.setElementInternal(this.el); }; todomvc.listcontrol.prototype.enterDocument = function() { goog.base(this, 'enterDocument'); this.on('keyup', function(e) { // on return if (e.keyCode != 13) return; // create new model var text = (this.getEls('input')[0]).value; var newModel = this.getModel().newModel({'text': text}); //create new model control var newModelControl = new todomvc.todocontrol(newModel); this.addChild(newModelControl); newModelControl.render(this.getEls('.todolist')[0]); }, 'todoform'); this.getModel().modelChange(function() { goog.dom.setTextContent(this.getEls('.count')[0], this.getModel().getLength() + ' notes'); }, this); };
what I've done is add in a div at the end and then put in a listener on modelChange which will fire if any models are added or removed (or are changed so that they are sorted differently) and then just display the number of notes. Now on to the todo:
goog.provide('todomvc.todocontrol'); goog.require('mvc.Control'); todomvc.todocontrol = function(model) { goog.base(this, model); this.editable = false; }; goog.inherits(todomvc.todocontrol, mvc.Control); todomvc.todocontrol.prototype.createDom = function() { this.el = goog.dom.htmlToDocumentFragment("<div>" + "<input type='checkbox' class='complete'/>" + "<div class='todoedit'></div>" + "<div class='delete'>X</div>" + "</div>"); console.log(this.el); this.setElementInternal(this.el); }; todomvc.todocontrol.prototype.makeEditable = function() { this.getEls('.todoedit')[0].innerHTML = "<input type='text' value='"+this.getModel().get('text')+"'/>"; this.editable = true; }; todomvc.todocontrol.prototype.makeUneditable = function() { this.getEls('.todoedit')[0].innerHTML = this.getModel().get('text'); this.editable = false; }; todomvc.todocontrol.prototype.enterDocument = function() { this.makeUneditable(); this.on('keyup', function(e) { this.getModel().set('text', this.getEls('input')[1].value); }); this.on('focusout', function() { this.makeUneditable(); }); this.click(function() { if(!this.editable) this.makeEditable(); }, "todoedit"); this.click(function(e) { if(e.target.checked) { this.getEls(".todoedit")[0].style.textDecoration = 'line-through'; this.getModel().set('complete', true); } else { this.getEls(".todoedit")[0].style.textDecoration = 'none'; this.getModel().unset('complete'); } }, "complete"); this.click(function(e) { this.getModel().dispose(); this.dispose(); }, "delete") };
Here I've added in a checkbox and a delete div and given them click functions. On clicking the checkbox it looks to see if the element is checked and changes the style of the text. It also sets 'complete' on the model or unsets it.
the delete will call dispose on the model which in turn will let the collection know it is disposed so the listener we put on to the collection will fire and update the count. We then dispose our control which will remove itself from the dom and clean up any event listeners it has setup.
Javascript IDE
I've spent most of my time using textmate and lately been on the lookout for a better editor. I tried out Webstorm for a bit, but it felt like overkill and was frustrating to look at and work with so I decided to give macvim a go, and for a while I thought I had found my perfect editor.
That has now all changed. My new IDE is Sublime. I stumbled upon it and saw it's minimap feature and wondered if it could be done in macvim, but looks like it can't (at least not yet and not really well done). It could be done in emacs and I was thinking I might try emacs - but why not give Sublime a spin first?
It was love at first sight. It's just beautiful and it all works with very little setup. The configs are done in a file rather than in a preferences screen and are well documented and if you delve in to the app folder then you see that the setup for the languages are actually textmate files so you can use those syntax files (so I grabbed the syntax for soy files from a tmBundle).
You can also choose to get some vim keymappings, but I found the mappings out of the box to be really good (although there are some that aren't in the menus that you figure out) and more similar to what I was used to in textmate.
The cmd+shift+p is great for running and installing bundles after you get the package manager and it should be fairly easy to get all the things you need.
It's $59 to buy but free to try so give it a go. It took me about 30 minutes before I reached for my credit card. I've got a few packages in there now and looking forward to installing some more tomorrow.
That has now all changed. My new IDE is Sublime. I stumbled upon it and saw it's minimap feature and wondered if it could be done in macvim, but looks like it can't (at least not yet and not really well done). It could be done in emacs and I was thinking I might try emacs - but why not give Sublime a spin first?
It was love at first sight. It's just beautiful and it all works with very little setup. The configs are done in a file rather than in a preferences screen and are well documented and if you delve in to the app folder then you see that the setup for the languages are actually textmate files so you can use those syntax files (so I grabbed the syntax for soy files from a tmBundle).
You can also choose to get some vim keymappings, but I found the mappings out of the box to be really good (although there are some that aren't in the menus that you figure out) and more similar to what I was used to in textmate.
The cmd+shift+p is great for running and installing bundles after you get the package manager and it should be fairly easy to get all the things you need.
It's $59 to buy but free to try so give it a go. It took me about 30 minutes before I reached for my credit card. I've got a few packages in there now and looking forward to installing some more tomorrow.
Monday, April 16, 2012
the Javascript Gentleman introduction
This is something I've been thinking about for a while. A javascript programmer is often referred to as a ninja or rockstar. They sound exciting, but what makes someone a ninja, and why would they want to be one? Programming is unlike a lot of other fields, we are actively encouraged to work in groups and share our code yet we're seen as loners at the same time as we sit in front of a screen for hours on end.
What I would like to see is a more mature community where js professionals write their code as it doesn't belong to them. The code we write will be picked up and viewed by other people and it is for them we really should be writing, not ourselves.
I believe there is a set of rules that can be followed and a great start has already been put down with Javscript: The Good Parts. If anything I believe the book doesn't go far enough. The book is a great read and essential for anyone who wants to work on javascript but only deals with specific parts of the language, what it misses are things like code style, naming conventions and just how to organize your code.
As a start everyone should at least read Google's style guide which is a good way to start thinking about code style. I won't tell you how you should style your code - it is something that should be agreed upon by a team but I will go in to what I do to try and make my code more accessible to future programmers.
That is the major point - we should code for others. This is one of the reasons I wrote the JQuery-like interface for the closure library. I know that there is not that much exposure to the closure library so I want future programmers to work with something they feel comfortable with.
I'm going to make some future posts that lays down rules for the Javascript Gentleman which I hope will help anyone who wants to work in a team or write a library for others to use, but for now I'll leave you with this thought - a javascript ninja tries to do cool things by bending the language to his whim, a javascript gentleman however knows how to get the same result by playing within the intended spirit of the language, and in that is the difference. It's not cool to be a gentleman but in the end you'll be respected more and have a greater impact.
What I would like to see is a more mature community where js professionals write their code as it doesn't belong to them. The code we write will be picked up and viewed by other people and it is for them we really should be writing, not ourselves.
I believe there is a set of rules that can be followed and a great start has already been put down with Javscript: The Good Parts. If anything I believe the book doesn't go far enough. The book is a great read and essential for anyone who wants to work on javascript but only deals with specific parts of the language, what it misses are things like code style, naming conventions and just how to organize your code.
As a start everyone should at least read Google's style guide which is a good way to start thinking about code style. I won't tell you how you should style your code - it is something that should be agreed upon by a team but I will go in to what I do to try and make my code more accessible to future programmers.
That is the major point - we should code for others. This is one of the reasons I wrote the JQuery-like interface for the closure library. I know that there is not that much exposure to the closure library so I want future programmers to work with something they feel comfortable with.
I'm going to make some future posts that lays down rules for the Javascript Gentleman which I hope will help anyone who wants to work in a team or write a library for others to use, but for now I'll leave you with this thought - a javascript ninja tries to do cool things by bending the language to his whim, a javascript gentleman however knows how to get the same result by playing within the intended spirit of the language, and in that is the difference. It's not cool to be a gentleman but in the end you'll be respected more and have a greater impact.
PlastronJS by example pt4
today we're going to display the todo and allow some editing.
First I'll create the control for a todo item:
It should look fairly similar to our list control. It has a createDom method where I'm putting in the div and an enterDocument where I setup all the listeners. I've also created makeEditable and makeUneditable functions which will put in an input which will listen to key up events to change the models text.
I've also put in a focusout event (notice it's not blur because blur events don't bubble so won't reach the control's listener) to go back to uneditable mode. Now we need to add this to our list control:
first thing I did was add in the goog.require at the top. Since this is a new file we'll have to add it to out deps.js with the command we used in the first post:
lib/closure-library/closure/bin/calcdeps.py --dep lib/closure-library --input js/main.js --path lib/plastronjs --path lib/plastronjs/sync --path js/ --output_mode deps > deps.js
I also added in the goog.base() for enterDocument. This is because I'm adding the controls as a child and goog.ui.Component does some things with it's enterDocument to setup relationships between itself and child components.
I also changed the form to just listen to a keyup as this is what is being done on todomvc's website (and debugging forms can be a pain as the browser will submit the form even if you break in the javascript).
and then the part at the bottom I setup the control, pass it the new model (mvc.Collection#newModel returns the model that was created and added) added it as a child of the control and then rendered it in to a div that I put in to hold the children.
save those changes and run then. You should be able to see that you can create new notes, click them and edit them. The only thing we use to display the test is a call to get('text') so we can see that the changes are being saved to the model.
First I'll create the control for a todo item:
goog.provide('todomvc.todocontrol'); goog.require('mvc.Control'); todomvc.todocontrol = function(model) { goog.base(this, model); this.editable = false; }; goog.inherits(todomvc.todocontrol, mvc.Control); todomvc.todocontrol.prototype.createDom = function() { this.el = goog.dom.htmlToDocumentFragment("<div>" + "<div class='todoedit'></div>" + "</div>"); console.log(this.el); this.setElementInternal(this.el); }; todomvc.todocontrol.prototype.makeEditable = function() { this.getEls('.todoedit')[0].innerHTML = "<input type='text' value='" + this.getModel().get('text') + "'/>"; this.editable = true; }; todomvc.todocontrol.prototype.makeUneditable = function() { this.getEls('.todoedit')[0].innerHTML = this.getModel().get('text'); this.editable = false; }; todomvc.todocontrol.prototype.enterDocument = function() { this.makeUneditable(); this.on('keyup', function(e) { this.getModel().set('text', this.getEls('input')[1].value); }); this.on('focusout', function() { this.makeUneditable(); }); this.click(function() { if(!this.editable) this.makeEditable(); }); };
It should look fairly similar to our list control. It has a createDom method where I'm putting in the div and an enterDocument where I setup all the listeners. I've also created makeEditable and makeUneditable functions which will put in an input which will listen to key up events to change the models text.
I've also put in a focusout event (notice it's not blur because blur events don't bubble so won't reach the control's listener) to go back to uneditable mode. Now we need to add this to our list control:
goog.provide('todomvc.listcontrol'); goog.require('mvc.Control'); goog.require('todomvc.todocontrol'); todomvc.listcontrol = function(model) { goog.base(this, model); }; goog.inherits(todomvc.listcontrol, mvc.Control); todomvc.listcontrol.prototype.createDom = function() { this.el = goog.dom.htmlToDocumentFragment("<div>" + "<div>Todo</div>" + "<div><input type='text' class='todoform'/></div>" + "<div class='todolist'></div></div>"); console.log(this.el); this.setElementInternal(this.el); }; todomvc.listcontrol.prototype.enterDocument = function() { goog.base(this, 'enterDocument'); this.on('keyup', function(e) { // on return if (e.keyCode != 13) return; // create new model var text = (this.getEls('input')[0]).value; var newModel = this.getModel().newModel({'text': text}); //create new model control var newModelControl = new todomvc.todocontrol(newModel); this.addChild(newModelControl); newModelControl.render(this.getEls('.todolist')[0]); }, 'todoform'); };
first thing I did was add in the goog.require at the top. Since this is a new file we'll have to add it to out deps.js with the command we used in the first post:
lib/closure-library/closure/bin/calcdeps.py --dep lib/closure-library --input js/main.js --path lib/plastronjs --path lib/plastronjs/sync --path js/ --output_mode deps > deps.js
I also added in the goog.base() for enterDocument. This is because I'm adding the controls as a child and goog.ui.Component does some things with it's enterDocument to setup relationships between itself and child components.
I also changed the form to just listen to a keyup as this is what is being done on todomvc's website (and debugging forms can be a pain as the browser will submit the form even if you break in the javascript).
and then the part at the bottom I setup the control, pass it the new model (mvc.Collection#newModel returns the model that was created and added) added it as a child of the control and then rendered it in to a div that I put in to hold the children.
save those changes and run then. You should be able to see that you can create new notes, click them and edit them. The only thing we use to display the test is a call to get('text') so we can see that the changes are being saved to the model.
Friday, April 13, 2012
closure javascript syntax for vim
here is the javascript.vim file that I'm using. I found that I was missing the highlighting on methods that doesn't happen when you write in the form:
myApp.prototype.method = function() {}
so I made additions to highlight and also popped in inheritDoc as a recognised doctype while I was at it. Here is the javascript.vim that should go in ~/.vim/syntax/ or if you're using janus ~/.vim/janus/vim/langs/javascript/syntax
" Vim syntax file
" Language: JavaScript
" Maintainer: Yi Zhao (ZHAOYI) <zzlinux AT hotmail DOT com>
" Last Change By: Marc Harter
" Last Change: February 18, 2011
" Version: 0.7.9
" Changes: Updates JSDoc syntax
"
" TODO:
" - Add the HTML syntax inside the JSDoc
if !exists("main_syntax")
if version < 600
syntax clear
elseif exists("b:current_syntax")
finish
endif
let main_syntax = 'javascript'
endif
"" Drop fold if it is set but VIM doesn't support it.
let b:javascript_fold='true'
if version < 600 " Don't support the old version
unlet! b:javascript_fold
endif
"" dollar sign is permittd anywhere in an identifier
setlocal iskeyword+=$
syntax sync fromstart
syntax match javaScriptKlass "^[a-zA-Z0-9.]*\(\.prototype\)\@="
syntax match javaScriptMethod "\(\.prototype\.\)\@<=[0-9a-zA-Z_]*"
"" JavaScript comments
syntax keyword javaScriptCommentTodo TODO FIXME XXX TBD contained
syntax region javaScriptLineComment start=+\/\/+ end=+$+ keepend contains=javaScriptCommentTodo,@Spell
syntax region javaScriptEnvComment start="\%^#!" end="$" display
syntax region javaScriptLineComment start=+^\s*\/\/+ skip=+\n\s*\/\/+ end=+$+ keepend contains=javaScriptCommentTodo,@Spell fold
syntax region javaScriptCvsTag start="\$\cid:" end="\$" oneline contained
syntax region javaScriptComment start="/\*" end="\*/" contains=javaScriptCommentTodo,javaScriptCvsTag,@Spell fold
"" JSDoc / JSDoc Toolkit
if !exists("javascript_ignore_javaScriptdoc")
syntax case ignore
"" syntax coloring for javadoc comments (HTML)
"syntax include @javaHtml <sfile>:p:h/html.vim
"unlet b:current_syntax
syntax region javaScriptDocComment matchgroup=javaScriptComment start="/\*\*\s*" end="\*/" contains=javaScriptDocTags,javaScriptCommentTodo,javaScriptCvsTag,@javaScriptHtml,@Spell fold
" tags containing a param
syntax match javaScriptDocTags contained "@\(augments\|base\|borrows\|class\|constructs\|default\|exception\|exports\|extends\|file\|member\|memberOf\|module\|name\|namespace\|optional\|requires\|title\|throws\|version\|inheritDoc\)\>" nextgroup=javaScriptDocParam skipwhite
" tags containing type and param
syntax match javaScriptDocTags contained "@\(argument\|param\|property\)\>" nextgroup=javaScriptDocType skipwhite
" tags containing type but no param
syntax match javaScriptDocTags contained "@\(type\|return\|returns\)\>" nextgroup=javaScriptDocTypeNoParam skipwhite
" tags containing references
syntax match javaScriptDocTags contained "@\(lends\|link\|see\)\>" nextgroup=javaScriptDocSeeTag skipwhite
" other tags (no extra syntax)
syntax match javaScriptDocTags contained "@\(access\|addon\|alias\|author\|beta\|constant\|constructor\|copyright\|deprecated\|description\|event\|example\|exec\|field\|fileOverview\|fileoverview\|function\|global\|ignore\|inner\|license\|overview\|private\|protected\|project\|public\|readonly\|since\|static\)\>"
syntax region javaScriptDocType start="{" end="}" oneline contained nextgroup=javaScriptDocParam skipwhite
syntax match javaScriptDocType contained "\%(#\|\"\|\w\|\.\|:\|\/\)\+" nextgroup=javaScriptDocParam skipwhite
syntax region javaScriptDocTypeNoParam start="{" end="}" oneline contained
syntax match javaScriptDocTypeNoParam contained "\%(#\|\"\|\w\|\.\|:\|\/\)\+"
syntax match javaScriptDocParam contained "\%(#\|\"\|{\|}\|\w\|\.\|:\|\/\)\+"
syntax region javaScriptDocSeeTag contained matchgroup=javaScriptDocSeeTag start="{" end="}" contains=javaScriptDocTags
syntax case match
endif "" JSDoc end
syntax case match
"" Syntax in the JavaScript code
syntax match javaScriptSpecial "\\\d\d\d\|\\x\x\{2\}\|\\u\x\{4\}\|\\."
syntax region javaScriptStringD start=+"+ skip=+\\\\\|\\$"+ end=+"+ contains=javaScriptSpecial,@htmlPreproc
syntax region javaScriptStringS start=+'+ skip=+\\\\\|\\$'+ end=+'+ contains=javaScriptSpecial,@htmlPreproc
syntax region javaScriptRegexpCharClass start=+\[+ end=+\]+ contained
syntax region javaScriptRegexpString start=+\(\(\(return\|case\)\s\+\)\@<=\|\(\([)\]"']\|\d\|\w\)\s*\)\@<!\)/\(\*\|/\)\@!+ skip=+\\\\\|\\/+ end=+/[gimy]\{,4}+ contains=javaScriptSpecial,javaScriptRegexpCharClass,@htmlPreproc oneline
syntax match javaScriptNumber /\<-\=\d\+L\=\>\|\<0[xX]\x\+\>/
syntax match javaScriptFloat /\<-\=\%(\d\+\.\d\+\|\d\+\.\|\.\d\+\)\%([eE][+-]\=\d\+\)\=\>/
syntax match javaScriptLabel /\<\w\+\(\s*:\)\@=/
"" JavaScript Prototype
syntax keyword javaScriptPrototype prototype
"" Program Keywords
syntax keyword javaScriptSource import export
syntax keyword javaScriptType const undefined var void yield
syntax keyword javaScriptOperator delete new in instanceof let typeof
syntax keyword javaScriptBoolean true false
syntax keyword javaScriptNull null
syntax keyword javaScriptThis this
"" Statement Keywords
syntax keyword javaScriptConditional if else
syntax keyword javaScriptRepeat do while for
syntax keyword javaScriptBranch break continue switch case default return
syntax keyword javaScriptStatement try catch throw with finally
syntax keyword javaScriptGlobalObjects Array Boolean Date Function Infinity JavaArray JavaClass JavaObject JavaPackage Math Number NaN Object Packages RegExp String Undefined java netscape sun
syntax keyword javaScriptExceptions Error EvalError RangeError ReferenceError SyntaxError TypeError URIError
syntax keyword javaScriptFutureKeys abstract enum int short boolean export interface static byte extends long super char final native synchronized class float package throws goto private transient debugger implements protected volatile double import public
"" DOM/HTML/CSS specified things
" DOM2 Objects
syntax keyword javaScriptGlobalObjects DOMImplementation DocumentFragment Document Node NodeList NamedNodeMap CharacterData Attr Element Text Comment CDATASection DocumentType Notation Entity EntityReference ProcessingInstruction
syntax keyword javaScriptExceptions DOMException
" DOM2 CONSTANT
syntax keyword javaScriptDomErrNo INDEX_SIZE_ERR DOMSTRING_SIZE_ERR HIERARCHY_REQUEST_ERR WRONG_DOCUMENT_ERR INVALID_CHARACTER_ERR NO_DATA_ALLOWED_ERR NO_MODIFICATION_ALLOWED_ERR NOT_FOUND_ERR NOT_SUPPORTED_ERR INUSE_ATTRIBUTE_ERR INVALID_STATE_ERR SYNTAX_ERR INVALID_MODIFICATION_ERR NAMESPACE_ERR INVALID_ACCESS_ERR
syntax keyword javaScriptDomNodeConsts ELEMENT_NODE ATTRIBUTE_NODE TEXT_NODE CDATA_SECTION_NODE ENTITY_REFERENCE_NODE ENTITY_NODE PROCESSING_INSTRUCTION_NODE COMMENT_NODE DOCUMENT_NODE DOCUMENT_TYPE_NODE DOCUMENT_FRAGMENT_NODE NOTATION_NODE
" HTML events and internal variables
syntax case ignore
syntax keyword javaScriptHtmlEvents onblur onclick oncontextmenu ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup onresize
syntax case match
" Follow stuff should be highligh within a special context
" While it can't be handled with context depended with Regex based highlight
" So, turn it off by default
if exists("javascript_enable_domhtmlcss")
" DOM2 things
syntax match javaScriptDomElemAttrs contained /\%(nodeName\|nodeValue\|nodeType\|parentNode\|childNodes\|firstChild\|lastChild\|previousSibling\|nextSibling\|attributes\|ownerDocument\|namespaceURI\|prefix\|localName\|tagName\)\>/
syntax match javaScriptDomElemFuncs contained /\%(insertBefore\|replaceChild\|removeChild\|appendChild\|hasChildNodes\|cloneNode\|normalize\|isSupported\|hasAttributes\|getAttribute\|setAttribute\|removeAttribute\|getAttributeNode\|setAttributeNode\|removeAttributeNode\|getElementsByTagName\|getAttributeNS\|setAttributeNS\|removeAttributeNS\|getAttributeNodeNS\|setAttributeNodeNS\|getElementsByTagNameNS\|hasAttribute\|hasAttributeNS\)\>/ nextgroup=javaScriptParen skipwhite
" HTML things
syntax match javaScriptHtmlElemAttrs contained /\%(className\|clientHeight\|clientLeft\|clientTop\|clientWidth\|dir\|id\|innerHTML\|lang\|length\|offsetHeight\|offsetLeft\|offsetParent\|offsetTop\|offsetWidth\|scrollHeight\|scrollLeft\|scrollTop\|scrollWidth\|style\|tabIndex\|title\)\>/
syntax match javaScriptHtmlElemFuncs contained /\%(blur\|click\|focus\|scrollIntoView\|addEventListener\|dispatchEvent\|removeEventListener\|item\)\>/ nextgroup=javaScriptParen skipwhite
" CSS Styles in JavaScript
syntax keyword javaScriptCssStyles contained color font fontFamily fontSize fontSizeAdjust fontStretch fontStyle fontVariant fontWeight letterSpacing lineBreak lineHeight quotes rubyAlign rubyOverhang rubyPosition
syntax keyword javaScriptCssStyles contained textAlign textAlignLast textAutospace textDecoration textIndent textJustify textJustifyTrim textKashidaSpace textOverflowW6 textShadow textTransform textUnderlinePosition
syntax keyword javaScriptCssStyles contained unicodeBidi whiteSpace wordBreak wordSpacing wordWrap writingMode
syntax keyword javaScriptCssStyles contained bottom height left position right top width zIndex
syntax keyword javaScriptCssStyles contained border borderBottom borderLeft borderRight borderTop borderBottomColor borderLeftColor borderTopColor borderBottomStyle borderLeftStyle borderRightStyle borderTopStyle borderBottomWidth borderLeftWidth borderRightWidth borderTopWidth borderColor borderStyle borderWidth borderCollapse borderSpacing captionSide emptyCells tableLayout
syntax keyword javaScriptCssStyles contained margin marginBottom marginLeft marginRight marginTop outline outlineColor outlineStyle outlineWidth padding paddingBottom paddingLeft paddingRight paddingTop
syntax keyword javaScriptCssStyles contained listStyle listStyleImage listStylePosition listStyleType
syntax keyword javaScriptCssStyles contained background backgroundAttachment backgroundColor backgroundImage gackgroundPosition backgroundPositionX backgroundPositionY backgroundRepeat
syntax keyword javaScriptCssStyles contained clear clip clipBottom clipLeft clipRight clipTop content counterIncrement counterReset cssFloat cursor direction display filter layoutGrid layoutGridChar layoutGridLine layoutGridMode layoutGridType
syntax keyword javaScriptCssStyles contained marks maxHeight maxWidth minHeight minWidth opacity MozOpacity overflow overflowX overflowY verticalAlign visibility zoom cssText
syntax keyword javaScriptCssStyles contained scrollbar3dLightColor scrollbarArrowColor scrollbarBaseColor scrollbarDarkShadowColor scrollbarFaceColor scrollbarHighlightColor scrollbarShadowColor scrollbarTrackColor
" Highlight ways
syntax match javaScriptDotNotation "\." nextgroup=javaScriptPrototype,javaScriptDomElemAttrs,javaScriptDomElemFuncs,javaScriptHtmlElemAttrs,javaScriptHtmlElemFuncs
syntax match javaScriptDotNotation "\.style\." nextgroup=javaScriptCssStyles
endif "DOM/HTML/CSS
"" end DOM/HTML/CSS specified things
"" Code blocks
" there is a name collision with javaScriptExpression in html.vim, hence the use of the '2' here
syntax cluster javaScriptExpression2 contains=javaScriptComment,javaScriptLineComment,javaScriptDocComment,javaScriptStringD,javaScriptStringS,javaScriptRegexpString,javaScriptNumber,javaScriptFloat,javaScriptSource,javaScriptThis,javaScriptType,javaScriptOperator,javaScriptBoolean,javaScriptNull,javaScriptFunction,javaScriptGlobalObjects,javaScriptExceptions,javaScriptFutureKeys,javaScriptDomErrNo,javaScriptDomNodeConsts,javaScriptHtmlEvents,javaScriptDotNotation,javaScriptBracket,javaScriptParen,javaScriptBlock,javaScriptParenError
syntax cluster javaScriptAll contains=@javaScriptExpression2,javaScriptLabel,javaScriptConditional,javaScriptRepeat,javaScriptBranch,javaScriptStatement,javaScriptTernaryIf
syntax region javaScriptBracket matchgroup=javaScriptBracket transparent start="\[" end="\]" contains=@javaScriptAll,javaScriptParensErrB,javaScriptParensErrC,javaScriptBracket,javaScriptParen,javaScriptBlock,@htmlPreproc
syntax region javaScriptParen matchgroup=javaScriptParen transparent start="(" end=")" contains=@javaScriptAll,javaScriptParensErrA,javaScriptParensErrC,javaScriptParen,javaScriptBracket,javaScriptBlock,@htmlPreproc
syntax region javaScriptBlock matchgroup=javaScriptBlock transparent start="{" end="}" contains=@javaScriptAll,javaScriptParensErrA,javaScriptParensErrB,javaScriptParen,javaScriptBracket,javaScriptBlock,@htmlPreproc
syntax region javaScriptTernaryIf matchgroup=javaScriptTernaryIfOperator start=+?+ end=+:+ contains=@javaScriptExpression2
"" catch errors caused by wrong parenthesis
syntax match javaScriptParensError ")\|}\|\]"
syntax match javaScriptParensErrA contained "\]"
syntax match javaScriptParensErrB contained ")"
syntax match javaScriptParensErrC contained "}"
if main_syntax == "javascript"
syntax sync clear
syntax sync ccomment javaScriptComment minlines=200
syntax sync match javaScriptHighlight grouphere javaScriptBlock /{/
endif
"" Fold control
if exists("b:javascript_fold")
syntax match javaScriptFunction /\<function\>/ nextgroup=javaScriptFuncName skipwhite
syntax match javaScriptOpAssign /=\@<!=/ nextgroup=javaScriptFuncBlock skipwhite skipempty
syntax region javaScriptFuncName contained matchgroup=javaScriptFuncName start=/\%(\$\|\w\)*\s*(/ end=/)/ contains=javaScriptLineComment,javaScriptComment nextgroup=javaScriptFuncBlock skipwhite skipempty
syntax region javaScriptFuncBlock contained matchgroup=javaScriptFuncBlock start="{" end="}" contains=@javaScriptAll,javaScriptParensErrA,javaScriptParensErrB,javaScriptParen,javaScriptBracket,javaScriptBlock fold
else
syntax keyword javaScriptFunction function
endif
" Define the default highlighting.
" For version 5.7 and earlier: only when not done already
" For version 5.8 and later: only when an item doesn't have highlighting yet
if version >= 508 || !exists("did_javascript_syn_inits")
if version < 508
let did_javascript_syn_inits = 1
command -nargs=+ HiLink hi link <args>
else
command -nargs=+ HiLink hi def link <args>
endif
HiLink javaScriptKlass Comment
HiLink javaScriptMethod PreProc
HiLink javaScriptComment Comment
HiLink javaScriptLineComment Comment
HiLink javaScriptEnvComment PreProc
HiLink javaScriptDocComment Comment
HiLink javaScriptCommentTodo Todo
HiLink javaScriptCvsTag Function
HiLink javaScriptDocTags Special
HiLink javaScriptDocSeeTag Function
HiLink javaScriptDocType Type
HiLink javaScriptDocTypeNoParam Type
HiLink javaScriptDocParam Label
HiLink javaScriptStringS String
HiLink javaScriptStringD String
HiLink javaScriptTernaryIfOperator Conditional
HiLink javaScriptRegexpString String
HiLink javaScriptRegexpCharClass Character
HiLink javaScriptCharacter Character
HiLink javaScriptPrototype Type
HiLink javaScriptConditional Conditional
HiLink javaScriptBranch Conditional
HiLink javaScriptRepeat Repeat
HiLink javaScriptStatement Statement
HiLink javaScriptFunction Function
HiLink javaScriptError Error
HiLink javaScriptParensError Error
HiLink javaScriptParensErrA Error
HiLink javaScriptParensErrB Error
HiLink javaScriptParensErrC Error
HiLink javaScriptOperator Operator
HiLink javaScriptType Type
HiLink javaScriptThis Type
HiLink javaScriptNull Type
HiLink javaScriptNumber Number
HiLink javaScriptFloat Number
HiLink javaScriptBoolean Boolean
HiLink javaScriptLabel Label
HiLink javaScriptSpecial Special
HiLink javaScriptSource Special
HiLink javaScriptGlobalObjects Special
HiLink javaScriptExceptions Special
HiLink javaScriptDomErrNo Constant
HiLink javaScriptDomNodeConsts Constant
HiLink javaScriptDomElemAttrs Label
HiLink javaScriptDomElemFuncs PreProc
HiLink javaScriptHtmlEvents Special
HiLink javaScriptHtmlElemAttrs Label
HiLink javaScriptHtmlElemFuncs PreProc
HiLink javaScriptCssStyles Label
delcommand HiLink
endif
" Define the htmlJavaScript for HTML syntax html.vim
"syntax clear htmlJavaScript
"syntax clear javaScriptExpression
syntax cluster htmlJavaScript contains=@javaScriptAll,javaScriptBracket,javaScriptParen,javaScriptBlock,javaScriptParenError
syntax cluster javaScriptExpression contains=@javaScriptAll,javaScriptBracket,javaScriptParen,javaScriptBlock,javaScriptParenError,@htmlPreproc
" Vim's default html.vim highlights all javascript as 'Special'
hi! def link javaScript NONE
let b:current_syntax = "javascript"
if main_syntax == 'javascript'
unlet main_syntax
endif
" vim: ts=4
myApp.prototype.method = function() {}
so I made additions to highlight and also popped in inheritDoc as a recognised doctype while I was at it. Here is the javascript.vim that should go in ~/.vim/syntax/ or if you're using janus ~/.vim/janus/vim/langs/javascript/syntax
" Vim syntax file
" Language: JavaScript
" Maintainer: Yi Zhao (ZHAOYI) <zzlinux AT hotmail DOT com>
" Last Change By: Marc Harter
" Last Change: February 18, 2011
" Version: 0.7.9
" Changes: Updates JSDoc syntax
"
" TODO:
" - Add the HTML syntax inside the JSDoc
if !exists("main_syntax")
if version < 600
syntax clear
elseif exists("b:current_syntax")
finish
endif
let main_syntax = 'javascript'
endif
"" Drop fold if it is set but VIM doesn't support it.
let b:javascript_fold='true'
if version < 600 " Don't support the old version
unlet! b:javascript_fold
endif
"" dollar sign is permittd anywhere in an identifier
setlocal iskeyword+=$
syntax sync fromstart
syntax match javaScriptKlass "^[a-zA-Z0-9.]*\(\.prototype\)\@="
syntax match javaScriptMethod "\(\.prototype\.\)\@<=[0-9a-zA-Z_]*"
"" JavaScript comments
syntax keyword javaScriptCommentTodo TODO FIXME XXX TBD contained
syntax region javaScriptLineComment start=+\/\/+ end=+$+ keepend contains=javaScriptCommentTodo,@Spell
syntax region javaScriptEnvComment start="\%^#!" end="$" display
syntax region javaScriptLineComment start=+^\s*\/\/+ skip=+\n\s*\/\/+ end=+$+ keepend contains=javaScriptCommentTodo,@Spell fold
syntax region javaScriptCvsTag start="\$\cid:" end="\$" oneline contained
syntax region javaScriptComment start="/\*" end="\*/" contains=javaScriptCommentTodo,javaScriptCvsTag,@Spell fold
"" JSDoc / JSDoc Toolkit
if !exists("javascript_ignore_javaScriptdoc")
syntax case ignore
"" syntax coloring for javadoc comments (HTML)
"syntax include @javaHtml <sfile>:p:h/html.vim
"unlet b:current_syntax
syntax region javaScriptDocComment matchgroup=javaScriptComment start="/\*\*\s*" end="\*/" contains=javaScriptDocTags,javaScriptCommentTodo,javaScriptCvsTag,@javaScriptHtml,@Spell fold
" tags containing a param
syntax match javaScriptDocTags contained "@\(augments\|base\|borrows\|class\|constructs\|default\|exception\|exports\|extends\|file\|member\|memberOf\|module\|name\|namespace\|optional\|requires\|title\|throws\|version\|inheritDoc\)\>" nextgroup=javaScriptDocParam skipwhite
" tags containing type and param
syntax match javaScriptDocTags contained "@\(argument\|param\|property\)\>" nextgroup=javaScriptDocType skipwhite
" tags containing type but no param
syntax match javaScriptDocTags contained "@\(type\|return\|returns\)\>" nextgroup=javaScriptDocTypeNoParam skipwhite
" tags containing references
syntax match javaScriptDocTags contained "@\(lends\|link\|see\)\>" nextgroup=javaScriptDocSeeTag skipwhite
" other tags (no extra syntax)
syntax match javaScriptDocTags contained "@\(access\|addon\|alias\|author\|beta\|constant\|constructor\|copyright\|deprecated\|description\|event\|example\|exec\|field\|fileOverview\|fileoverview\|function\|global\|ignore\|inner\|license\|overview\|private\|protected\|project\|public\|readonly\|since\|static\)\>"
syntax region javaScriptDocType start="{" end="}" oneline contained nextgroup=javaScriptDocParam skipwhite
syntax match javaScriptDocType contained "\%(#\|\"\|\w\|\.\|:\|\/\)\+" nextgroup=javaScriptDocParam skipwhite
syntax region javaScriptDocTypeNoParam start="{" end="}" oneline contained
syntax match javaScriptDocTypeNoParam contained "\%(#\|\"\|\w\|\.\|:\|\/\)\+"
syntax match javaScriptDocParam contained "\%(#\|\"\|{\|}\|\w\|\.\|:\|\/\)\+"
syntax region javaScriptDocSeeTag contained matchgroup=javaScriptDocSeeTag start="{" end="}" contains=javaScriptDocTags
syntax case match
endif "" JSDoc end
syntax case match
"" Syntax in the JavaScript code
syntax match javaScriptSpecial "\\\d\d\d\|\\x\x\{2\}\|\\u\x\{4\}\|\\."
syntax region javaScriptStringD start=+"+ skip=+\\\\\|\\$"+ end=+"+ contains=javaScriptSpecial,@htmlPreproc
syntax region javaScriptStringS start=+'+ skip=+\\\\\|\\$'+ end=+'+ contains=javaScriptSpecial,@htmlPreproc
syntax region javaScriptRegexpCharClass start=+\[+ end=+\]+ contained
syntax region javaScriptRegexpString start=+\(\(\(return\|case\)\s\+\)\@<=\|\(\([)\]"']\|\d\|\w\)\s*\)\@<!\)/\(\*\|/\)\@!+ skip=+\\\\\|\\/+ end=+/[gimy]\{,4}+ contains=javaScriptSpecial,javaScriptRegexpCharClass,@htmlPreproc oneline
syntax match javaScriptNumber /\<-\=\d\+L\=\>\|\<0[xX]\x\+\>/
syntax match javaScriptFloat /\<-\=\%(\d\+\.\d\+\|\d\+\.\|\.\d\+\)\%([eE][+-]\=\d\+\)\=\>/
syntax match javaScriptLabel /\<\w\+\(\s*:\)\@=/
"" JavaScript Prototype
syntax keyword javaScriptPrototype prototype
"" Program Keywords
syntax keyword javaScriptSource import export
syntax keyword javaScriptType const undefined var void yield
syntax keyword javaScriptOperator delete new in instanceof let typeof
syntax keyword javaScriptBoolean true false
syntax keyword javaScriptNull null
syntax keyword javaScriptThis this
"" Statement Keywords
syntax keyword javaScriptConditional if else
syntax keyword javaScriptRepeat do while for
syntax keyword javaScriptBranch break continue switch case default return
syntax keyword javaScriptStatement try catch throw with finally
syntax keyword javaScriptGlobalObjects Array Boolean Date Function Infinity JavaArray JavaClass JavaObject JavaPackage Math Number NaN Object Packages RegExp String Undefined java netscape sun
syntax keyword javaScriptExceptions Error EvalError RangeError ReferenceError SyntaxError TypeError URIError
syntax keyword javaScriptFutureKeys abstract enum int short boolean export interface static byte extends long super char final native synchronized class float package throws goto private transient debugger implements protected volatile double import public
"" DOM/HTML/CSS specified things
" DOM2 Objects
syntax keyword javaScriptGlobalObjects DOMImplementation DocumentFragment Document Node NodeList NamedNodeMap CharacterData Attr Element Text Comment CDATASection DocumentType Notation Entity EntityReference ProcessingInstruction
syntax keyword javaScriptExceptions DOMException
" DOM2 CONSTANT
syntax keyword javaScriptDomErrNo INDEX_SIZE_ERR DOMSTRING_SIZE_ERR HIERARCHY_REQUEST_ERR WRONG_DOCUMENT_ERR INVALID_CHARACTER_ERR NO_DATA_ALLOWED_ERR NO_MODIFICATION_ALLOWED_ERR NOT_FOUND_ERR NOT_SUPPORTED_ERR INUSE_ATTRIBUTE_ERR INVALID_STATE_ERR SYNTAX_ERR INVALID_MODIFICATION_ERR NAMESPACE_ERR INVALID_ACCESS_ERR
syntax keyword javaScriptDomNodeConsts ELEMENT_NODE ATTRIBUTE_NODE TEXT_NODE CDATA_SECTION_NODE ENTITY_REFERENCE_NODE ENTITY_NODE PROCESSING_INSTRUCTION_NODE COMMENT_NODE DOCUMENT_NODE DOCUMENT_TYPE_NODE DOCUMENT_FRAGMENT_NODE NOTATION_NODE
" HTML events and internal variables
syntax case ignore
syntax keyword javaScriptHtmlEvents onblur onclick oncontextmenu ondblclick onfocus onkeydown onkeypress onkeyup onmousedown onmousemove onmouseout onmouseover onmouseup onresize
syntax case match
" Follow stuff should be highligh within a special context
" While it can't be handled with context depended with Regex based highlight
" So, turn it off by default
if exists("javascript_enable_domhtmlcss")
" DOM2 things
syntax match javaScriptDomElemAttrs contained /\%(nodeName\|nodeValue\|nodeType\|parentNode\|childNodes\|firstChild\|lastChild\|previousSibling\|nextSibling\|attributes\|ownerDocument\|namespaceURI\|prefix\|localName\|tagName\)\>/
syntax match javaScriptDomElemFuncs contained /\%(insertBefore\|replaceChild\|removeChild\|appendChild\|hasChildNodes\|cloneNode\|normalize\|isSupported\|hasAttributes\|getAttribute\|setAttribute\|removeAttribute\|getAttributeNode\|setAttributeNode\|removeAttributeNode\|getElementsByTagName\|getAttributeNS\|setAttributeNS\|removeAttributeNS\|getAttributeNodeNS\|setAttributeNodeNS\|getElementsByTagNameNS\|hasAttribute\|hasAttributeNS\)\>/ nextgroup=javaScriptParen skipwhite
" HTML things
syntax match javaScriptHtmlElemAttrs contained /\%(className\|clientHeight\|clientLeft\|clientTop\|clientWidth\|dir\|id\|innerHTML\|lang\|length\|offsetHeight\|offsetLeft\|offsetParent\|offsetTop\|offsetWidth\|scrollHeight\|scrollLeft\|scrollTop\|scrollWidth\|style\|tabIndex\|title\)\>/
syntax match javaScriptHtmlElemFuncs contained /\%(blur\|click\|focus\|scrollIntoView\|addEventListener\|dispatchEvent\|removeEventListener\|item\)\>/ nextgroup=javaScriptParen skipwhite
" CSS Styles in JavaScript
syntax keyword javaScriptCssStyles contained color font fontFamily fontSize fontSizeAdjust fontStretch fontStyle fontVariant fontWeight letterSpacing lineBreak lineHeight quotes rubyAlign rubyOverhang rubyPosition
syntax keyword javaScriptCssStyles contained textAlign textAlignLast textAutospace textDecoration textIndent textJustify textJustifyTrim textKashidaSpace textOverflowW6 textShadow textTransform textUnderlinePosition
syntax keyword javaScriptCssStyles contained unicodeBidi whiteSpace wordBreak wordSpacing wordWrap writingMode
syntax keyword javaScriptCssStyles contained bottom height left position right top width zIndex
syntax keyword javaScriptCssStyles contained border borderBottom borderLeft borderRight borderTop borderBottomColor borderLeftColor borderTopColor borderBottomStyle borderLeftStyle borderRightStyle borderTopStyle borderBottomWidth borderLeftWidth borderRightWidth borderTopWidth borderColor borderStyle borderWidth borderCollapse borderSpacing captionSide emptyCells tableLayout
syntax keyword javaScriptCssStyles contained margin marginBottom marginLeft marginRight marginTop outline outlineColor outlineStyle outlineWidth padding paddingBottom paddingLeft paddingRight paddingTop
syntax keyword javaScriptCssStyles contained listStyle listStyleImage listStylePosition listStyleType
syntax keyword javaScriptCssStyles contained background backgroundAttachment backgroundColor backgroundImage gackgroundPosition backgroundPositionX backgroundPositionY backgroundRepeat
syntax keyword javaScriptCssStyles contained clear clip clipBottom clipLeft clipRight clipTop content counterIncrement counterReset cssFloat cursor direction display filter layoutGrid layoutGridChar layoutGridLine layoutGridMode layoutGridType
syntax keyword javaScriptCssStyles contained marks maxHeight maxWidth minHeight minWidth opacity MozOpacity overflow overflowX overflowY verticalAlign visibility zoom cssText
syntax keyword javaScriptCssStyles contained scrollbar3dLightColor scrollbarArrowColor scrollbarBaseColor scrollbarDarkShadowColor scrollbarFaceColor scrollbarHighlightColor scrollbarShadowColor scrollbarTrackColor
" Highlight ways
syntax match javaScriptDotNotation "\." nextgroup=javaScriptPrototype,javaScriptDomElemAttrs,javaScriptDomElemFuncs,javaScriptHtmlElemAttrs,javaScriptHtmlElemFuncs
syntax match javaScriptDotNotation "\.style\." nextgroup=javaScriptCssStyles
endif "DOM/HTML/CSS
"" end DOM/HTML/CSS specified things
"" Code blocks
" there is a name collision with javaScriptExpression in html.vim, hence the use of the '2' here
syntax cluster javaScriptExpression2 contains=javaScriptComment,javaScriptLineComment,javaScriptDocComment,javaScriptStringD,javaScriptStringS,javaScriptRegexpString,javaScriptNumber,javaScriptFloat,javaScriptSource,javaScriptThis,javaScriptType,javaScriptOperator,javaScriptBoolean,javaScriptNull,javaScriptFunction,javaScriptGlobalObjects,javaScriptExceptions,javaScriptFutureKeys,javaScriptDomErrNo,javaScriptDomNodeConsts,javaScriptHtmlEvents,javaScriptDotNotation,javaScriptBracket,javaScriptParen,javaScriptBlock,javaScriptParenError
syntax cluster javaScriptAll contains=@javaScriptExpression2,javaScriptLabel,javaScriptConditional,javaScriptRepeat,javaScriptBranch,javaScriptStatement,javaScriptTernaryIf
syntax region javaScriptBracket matchgroup=javaScriptBracket transparent start="\[" end="\]" contains=@javaScriptAll,javaScriptParensErrB,javaScriptParensErrC,javaScriptBracket,javaScriptParen,javaScriptBlock,@htmlPreproc
syntax region javaScriptParen matchgroup=javaScriptParen transparent start="(" end=")" contains=@javaScriptAll,javaScriptParensErrA,javaScriptParensErrC,javaScriptParen,javaScriptBracket,javaScriptBlock,@htmlPreproc
syntax region javaScriptBlock matchgroup=javaScriptBlock transparent start="{" end="}" contains=@javaScriptAll,javaScriptParensErrA,javaScriptParensErrB,javaScriptParen,javaScriptBracket,javaScriptBlock,@htmlPreproc
syntax region javaScriptTernaryIf matchgroup=javaScriptTernaryIfOperator start=+?+ end=+:+ contains=@javaScriptExpression2
"" catch errors caused by wrong parenthesis
syntax match javaScriptParensError ")\|}\|\]"
syntax match javaScriptParensErrA contained "\]"
syntax match javaScriptParensErrB contained ")"
syntax match javaScriptParensErrC contained "}"
if main_syntax == "javascript"
syntax sync clear
syntax sync ccomment javaScriptComment minlines=200
syntax sync match javaScriptHighlight grouphere javaScriptBlock /{/
endif
"" Fold control
if exists("b:javascript_fold")
syntax match javaScriptFunction /\<function\>/ nextgroup=javaScriptFuncName skipwhite
syntax match javaScriptOpAssign /=\@<!=/ nextgroup=javaScriptFuncBlock skipwhite skipempty
syntax region javaScriptFuncName contained matchgroup=javaScriptFuncName start=/\%(\$\|\w\)*\s*(/ end=/)/ contains=javaScriptLineComment,javaScriptComment nextgroup=javaScriptFuncBlock skipwhite skipempty
syntax region javaScriptFuncBlock contained matchgroup=javaScriptFuncBlock start="{" end="}" contains=@javaScriptAll,javaScriptParensErrA,javaScriptParensErrB,javaScriptParen,javaScriptBracket,javaScriptBlock fold
else
syntax keyword javaScriptFunction function
endif
" Define the default highlighting.
" For version 5.7 and earlier: only when not done already
" For version 5.8 and later: only when an item doesn't have highlighting yet
if version >= 508 || !exists("did_javascript_syn_inits")
if version < 508
let did_javascript_syn_inits = 1
command -nargs=+ HiLink hi link <args>
else
command -nargs=+ HiLink hi def link <args>
endif
HiLink javaScriptKlass Comment
HiLink javaScriptMethod PreProc
HiLink javaScriptComment Comment
HiLink javaScriptLineComment Comment
HiLink javaScriptEnvComment PreProc
HiLink javaScriptDocComment Comment
HiLink javaScriptCommentTodo Todo
HiLink javaScriptCvsTag Function
HiLink javaScriptDocTags Special
HiLink javaScriptDocSeeTag Function
HiLink javaScriptDocType Type
HiLink javaScriptDocTypeNoParam Type
HiLink javaScriptDocParam Label
HiLink javaScriptStringS String
HiLink javaScriptStringD String
HiLink javaScriptTernaryIfOperator Conditional
HiLink javaScriptRegexpString String
HiLink javaScriptRegexpCharClass Character
HiLink javaScriptCharacter Character
HiLink javaScriptPrototype Type
HiLink javaScriptConditional Conditional
HiLink javaScriptBranch Conditional
HiLink javaScriptRepeat Repeat
HiLink javaScriptStatement Statement
HiLink javaScriptFunction Function
HiLink javaScriptError Error
HiLink javaScriptParensError Error
HiLink javaScriptParensErrA Error
HiLink javaScriptParensErrB Error
HiLink javaScriptParensErrC Error
HiLink javaScriptOperator Operator
HiLink javaScriptType Type
HiLink javaScriptThis Type
HiLink javaScriptNull Type
HiLink javaScriptNumber Number
HiLink javaScriptFloat Number
HiLink javaScriptBoolean Boolean
HiLink javaScriptLabel Label
HiLink javaScriptSpecial Special
HiLink javaScriptSource Special
HiLink javaScriptGlobalObjects Special
HiLink javaScriptExceptions Special
HiLink javaScriptDomErrNo Constant
HiLink javaScriptDomNodeConsts Constant
HiLink javaScriptDomElemAttrs Label
HiLink javaScriptDomElemFuncs PreProc
HiLink javaScriptHtmlEvents Special
HiLink javaScriptHtmlElemAttrs Label
HiLink javaScriptHtmlElemFuncs PreProc
HiLink javaScriptCssStyles Label
delcommand HiLink
endif
" Define the htmlJavaScript for HTML syntax html.vim
"syntax clear htmlJavaScript
"syntax clear javaScriptExpression
syntax cluster htmlJavaScript contains=@javaScriptAll,javaScriptBracket,javaScriptParen,javaScriptBlock,javaScriptParenError
syntax cluster javaScriptExpression contains=@javaScriptAll,javaScriptBracket,javaScriptParen,javaScriptBlock,javaScriptParenError,@htmlPreproc
" Vim's default html.vim highlights all javascript as 'Special'
hi! def link javaScript NONE
let b:current_syntax = "javascript"
if main_syntax == 'javascript'
unlet main_syntax
endif
" vim: ts=4
Thursday, April 12, 2012
PlastronJS by example pt3
Now we're going to hook up the form we built to crete new todo models.
First thing we're going to do is modify our main.js to make it easier to access the todolist in our console (plus fix up an error from last post).
The only real change to the above is that I've added window['a']= todolist at the end. This will be removed later, but I like having easy access to specific variables in the console when I'm developing. This will allow me to call the todolist with just a.method.
Next up is the Control:
What this has done is add in the enterDocument function. Usually we should call goog.base() on this as well as it inherits from goog.ui.component and does some things we want to keep (like figuring out that it is attached to a document so child components will render). We put the setup in here because this is when we are first guaranteed that the DOM structure we create in createDom is in the document
From here I've call .on which is a method that we use for event handling. I've passed in the event to listen for and a function to handle it. There are other optional parameters I could pass such as a classname to only target one form and then a handler, but the function is automagically bound to the control and there is only one form so I don't need to.
The function stops the event and returns false to prevent the default submit, then it grabs the text from the first input under our control. I'm then getting the controls model and using the collection's newModel method which will put a new mvc.Model at the top and passing in the text and that's it.
try opening up the html file, typing something in to the field and pressing enter. You can then open up your javascript console and see the new model by entering in:
Next post we'll create a new control for todos and have them display
First thing we're going to do is modify our main.js to make it easier to access the todolist in our console (plus fix up an error from last post).
goog.provide('todomvc.main'); goog.require('mvc.Collection'); goog.require('todomvc.listcontrol'); todomvc.main = function() { var todolist = new mvc.Collection(); var todolistControl = new todomvc.listcontrol(todolist); todolistControl.createDom(); todolistControl.render(document.body); window['a'] = todolist; };
The only real change to the above is that I've added window['a']= todolist at the end. This will be removed later, but I like having easy access to specific variables in the console when I'm developing. This will allow me to call the todolist with just a.method.
Next up is the Control:
Whatgoog.provide('todomvc.listcontrol'); goog.require('mvc.Control'); todomvc.listcontrol = function(model) { goog.base(this, model); }; goog.inherits(todomvc.listcontrol, mvc.Control); todomvc.listcontrol.prototype.createDom = function() { this.el = goog.dom.htmlToDocumentFragment("<div>" + "<div>Todo</div>" + "<div><form><input type='text' /></form></div>" + "</div>"); console.log(this.el); this.setElementInternal(this.el); }; todomvc.listcontrol.prototype.enterDocument = function() { this.on('submit', function(e) { e.preventDefault(); e.stopPropagation(); var text = (this.getEls('input')[0]).value; this.getModel().newModel({'text': text}); return false; }); };
What this has done is add in the enterDocument function. Usually we should call goog.base() on this as well as it inherits from goog.ui.component and does some things we want to keep (like figuring out that it is attached to a document so child components will render). We put the setup in here because this is when we are first guaranteed that the DOM structure we create in createDom is in the document
From here I've call .on which is a method that we use for event handling. I've passed in the event to listen for and a function to handle it. There are other optional parameters I could pass such as a classname to only target one form and then a handler, but the function is automagically bound to the control and there is only one form so I don't need to.
The function stops the event and returns false to prevent the default submit, then it grabs the text from the first input under our control. I'm then getting the controls model and using the collection's newModel method which will put a new mvc.Model at the top and passing in the text and that's it.
try opening up the html file, typing something in to the field and pressing enter. You can then open up your javascript console and see the new model by entering in:
a.at(0); // returns your new model a.at(0).get('text'); // returns the text in your new model which should be what you typed in
Next post we'll create a new control for todos and have them display
Tuesday, April 10, 2012
PlastronJS By example pt2
what we're going to do is setup a new todo list and put in the start of the display.
So first we'll change the HTML to just call the main function with:
<script>
todomvc.main();
</script>
Next we'll create a new file under our js folder and call it list control.js - this will be the C in our MVC for the todo list. The contents of our file should look like this:
The first line is for dependancies and tells calcdeps what class the file provides. We also need to require mvc.Control because we'll be inheriting from it.
next comes the constructor function which calls good.base(this) which tells it to call the constructor function of the class it inherits from.
goog.inherits sets up the inheritance chain.
The createDom is from good.ui.Component and is what is used to setup the DOM for the control. For now I've just put in the template as a string and used the good.dom.htmlToDocumentFragment to create the elements and then set the top element as the controls internal element.
Now we need to change min.'s to use this control:
We need to require our list control and pass it a model (in this case our todo list collection). We then create the Dom and finally render it in to the document.
That should be enough to get us the title and the input box. Next we'll setup the input box to create todo models.
So first we'll change the HTML to just call the main function with:
<script>
todomvc.main();
</script>
Next we'll create a new file under our js folder and call it list control.js - this will be the C in our MVC for the todo list. The contents of our file should look like this:
goog.provide('todomvc.listcontrol'); goog.require('goog.dom'); goog.require('mvc.Control'); todomvc.listcontrol = function(model) { goog.base(this, model); }; goog.inherits(todomvc.listcontrol, mvc.Control); todomvc.listcontrol.prototype.createDom = function() { this.el = goog.dom.htmlToDocumentFragment("<div>" + "<div>Todo</div>" + "<div><form><input type='text' /></form></div>" + "</div>"); console.log(this.el); this.setElementInternal(this.el); };
The first line is for dependancies and tells calcdeps what class the file provides. We also need to require mvc.Control because we'll be inheriting from it.
next comes the constructor function which calls good.base(this) which tells it to call the constructor function of the class it inherits from.
goog.inherits sets up the inheritance chain.
The createDom is from good.ui.Component and is what is used to setup the DOM for the control. For now I've just put in the template as a string and used the good.dom.htmlToDocumentFragment to create the elements and then set the top element as the controls internal element.
Now we need to change min.'s to use this control:
goog.provide('todomvc.main'); goog.require('mvc.Collection'); goog.require('todomvc.listcontrol'); todomvc.main = function() { var todolist = new mvc.Collection(); var todolistControl = new todomvc.listcontrol(this.todolist); todolistControl.createDom(); todolistControl.render(document.body); };
We need to require our list control and pass it a model (in this case our todo list collection). We then create the Dom and finally render it in to the document.
That should be enough to get us the title and the input box. Next we'll setup the input box to create todo models.
Monday, April 9, 2012
PlastronJS by example pt.1
We're going to use PlastronJS to make a todo app that could go on TodoMVC
Setup
first thing we need to setup a directory where we can put the application and the library.
I've created a folder called todomvc with two folders underneath it, lib and js. First thing I need to do is put in the closure library. You can get the closure library from here: http://code.google.com/p/closure-library/downloads/list
Next I need to put in PlastronJS which I can get from here: https://github.com/rhysbrettbowen/PlastronJS
Put those two under the lib folder.
Now under the js folder create a file called min.'s and insert this code:
goog.provide('todomvc.main'); goog.require('mvc.Model'); todomvc.main = function() { this.a = mvc.Model.create(); };
All that it's doing is providing the todo.mvc function, requiring our mvc.Model and giving us a function to create a model.
Now we need some HTML to display, pop this in main.html which you can create under the root:
<!doctype html>
<html>
<head>
<title>Example: TodoMVC</title>
</head>
<body>
<div id="hello"></div>
<script src="lib/closure-library/closure/goog/base.js"></script>
<script src="deps.js"></script>
<script src="js/main.js"></script>
</body>
</html>
last we need to create the deps.js which is a dependency map of the js files we need to use. Head over to your command line and put in:
lib/closure-library/closure/bin/calcdeps.py --dep lib/closure-library --input js/main.js --path lib/plastronjs --path lib/plastronjs/sync --path js/ --output_mode deps > deps.js
Now we need some HTML to display, pop this in main.html which you can create under the root:
<!doctype html>
<html>
<head>
<title>Example: TodoMVC</title>
</head>
<body>
<div id="hello"></div>
<script src="lib/closure-library/closure/goog/base.js"></script>
<script src="deps.js"></script>
<script src="js/main.js"></script>
</body>
</html>
last we need to create the deps.js which is a dependency map of the js files we need to use. Head over to your command line and put in:
lib/closure-library/closure/bin/calcdeps.py --dep lib/closure-library --input js/main.js --path lib/plastronjs --path lib/plastronjs/sync --path js/ --output_mode deps > deps.js
Run it
That should be all you need to start. You can open the page in your browser and you won't see much. Open up your javascript console and put in
You can then play around with the model through a.a
Next post we'll got through creating the models for the todo items and how to use them with sync
var a = new todomvc.main();
You can then play around with the model through a.a
a.a.set('a',1); a.a.get('a') // 1 b = a.getBinder('a'); b(); // 1 b(2); a.a.get('a') // 2
Next post we'll got through creating the models for the todo items and how to use them with sync
Friday, April 6, 2012
PlastronJS
For an MVC I'm going to be using PlastronJS.
It's a shameful plug because I wrote it. It should be familiar if you've used anything like backbone, spine, angular, ember or any number of MVC frameworks out there. So why did I write another one? A few reasons.
You'll also notice that the functions are bound to the model, so you can just call 'this'.
There is also compare functionality in the schema. A model decides what has changed by comparing the current attribute and a saved attribute from the last change. When saving an attribute it does an unsafe recursive clone (so no recursive references in the model's data please) so the object will not be equal. Primitive types and arrays of primitive types are checked for equality, otherwise you'll probably want to define a comparison function.
There are already a couple of functions under mvc.Model.Compare you can use. RECURSIVE goes through the object/array recursively and checks that everything matches. There is also a STRING and a SERIALZE that compares the .toSring() and JSON.serialze() of the object against each other. You may notice I didn't need to give a cmp for 'star' and that's because event though it is under an object it's getter will only return a primitive type.
If you want to learn more about Schemas in PlastronJS head over to https://github.com/rhysbrettbowen/PlastronJS and go through the README and the source code (which should be commented thoroughly). Hopefully you'll be able to define an interface for your complex models which will relieve a lot of the stress when working with them in more complex applications.
It's a shameful plug because I wrote it. It should be familiar if you've used anything like backbone, spine, angular, ember or any number of MVC frameworks out there. So why did I write another one? A few reasons.
- There isn't a framework that integrates well with the closure compiler
- Most frameworks have dependancies on jquery and not closure-library
- The frameworks are fairly lightweight
- I was bored
PlastronJS is slightly different to what you've been used to. At it's core it works the same way - there are models that communicate to a data layer through sync and you can bind functions to changes in the models data.
So what is different? Probably the best reason to use PlastronJS are the schemas. A schema can be put on a model to provide you with a different interface for accessing the data. For instance at Catch we hold information about notes under their 'annotations'. If I want to star a note I would have to get the annotations object, change the 'catch:starred' attribute and then set the 'catch:starred' attribute. I'd also have to listen to changes on the annotations object, so any other changes to that would fire the function.
SCHEMA
A schema allows the setting and getting of custom attributes and helps you describe them based on attributes that already exist in the model. For instance I can create a star attribute like this:
// schema object var schema = { 'star': { get: function(annotations) { return annotations && annotations['catch:starred']; }, set: function(star) { var ann = this.get('annotations'); goog.object.extend(ann, {'catch:starred': star}); this.set('annotations', ann, true); }, require: ['annotations'] } };
The require attribute tells us what attributes from the model we need to get/set star. The get function will receive the get() value of our requires array. So if we required two elements we would have those two as inputs to the get function. The get function can also throw errors for validation and they will be caught by the model's error function.
When setting the value we'll usually pass the optional "silent" parameter if using the set() with other attributes. This is because the original set() call for star is the one we want to decide whether to fire a change event. You could also just set it on the actually data in the model like this:
When setting the value we'll usually pass the optional "silent" parameter if using the set() with other attributes. This is because the original set() call for star is the one we want to decide whether to fire a change event. You could also just set it on the actually data in the model like this:
this.attr_['annotations'] = ann;
You'll also notice that the functions are bound to the model, so you can just call 'this'.
There is also compare functionality in the schema. A model decides what has changed by comparing the current attribute and a saved attribute from the last change. When saving an attribute it does an unsafe recursive clone (so no recursive references in the model's data please) so the object will not be equal. Primitive types and arrays of primitive types are checked for equality, otherwise you'll probably want to define a comparison function.
// schema object var schema = { 'annotations': { cmp: mvc.Model.Compare.RECURSIVE } };
There are already a couple of functions under mvc.Model.Compare you can use. RECURSIVE goes through the object/array recursively and checks that everything matches. There is also a STRING and a SERIALZE that compares the .toSring() and JSON.serialze() of the object against each other. You may notice I didn't need to give a cmp for 'star' and that's because event though it is under an object it's getter will only return a primitive type.
If you want to learn more about Schemas in PlastronJS head over to https://github.com/rhysbrettbowen/PlastronJS and go through the README and the source code (which should be commented thoroughly). Hopefully you'll be able to define an interface for your complex models which will relieve a lot of the stress when working with them in more complex applications.
The project file structure
The first thing you have to decide with a project is how to structure it. I like to use an MVC framework and so this is how I usually end up structuring my projects:
myApp/
js/
api/
app/
control/
models/
schema/
sync/
ui/
widgets/
lib/
mvc/
style/
template/
strings/
myApp/
js/
api/
app/
control/
models/
schema/
sync/
ui/
widgets/
lib/
mvc/
style/
template/
strings/
- The JS folder holds all your javascript files.
- The api folder holds communication with servers
- the app holds your main.js and other files that the rest of your program can access (like a mediator)
- the control holds the controls for your model
- the models will hold your models
- the schema holds the definition of your models (transforms on model data e.g. getters and setters)
- sync holds the models communication with the data layer
- ui holds javascript for general page UI (like page layout)
- widgets holds reusable elements like dialog boxes
- lib holds 3rd party js libraries like an mvc framework or the closure library
- style holds all the CSS/GSS/SASS etc
- template holds you HTML templates (soy files)
- strings hold translatable strings to be injected in the DOM.
This is an example and may not make much sense for your project or may be tweaked but is a good guide to start off with and you can change your structure if your needs are different.
Thursday, April 5, 2012
How to start closure
I keep running over in my head just how far in to closure I should get. There is already a tutorial and the Closure: Definitive Guide are great starts. These will be enough to get you up and running for a simple application that runs locally (or on a local server if you went down the plovr route).
Once you have followed the tutorial and got everything setup you'll find your first problem will be XHR. If you are building a site that consumes XHR requests from another domain then running your page off a local server should suffice, the issue comes when you are trying to consume json from your own domain. Since you'll be running your page from localhost (or whatever you're using) you'll run in to cross domain issues.
The fix is pretty simple, use a proxy server. You're going to have to build one though, so what is the best way? Well we know javascript so node.js is probably a good option.
Building a proxy server in node.js is a bit beyond what this blog is about and it would be terrible of me to take you away from the experience of learning about node.js. Instead I'm just going to let you know the principle and let you go and find your own tutorial.
The idea is that you want to run the server on your localhost, read the requests as it comes in and if it should match something local then read that file and return it. Otherwise you want to copy the headers over to a new request and pipe the response back.
At catch we even decide whether to look at the uncompiled version of the code for development by putting a debug=1 in the query string parameters.
Once you have followed the tutorial and got everything setup you'll find your first problem will be XHR. If you are building a site that consumes XHR requests from another domain then running your page off a local server should suffice, the issue comes when you are trying to consume json from your own domain. Since you'll be running your page from localhost (or whatever you're using) you'll run in to cross domain issues.
The fix is pretty simple, use a proxy server. You're going to have to build one though, so what is the best way? Well we know javascript so node.js is probably a good option.
Building a proxy server in node.js is a bit beyond what this blog is about and it would be terrible of me to take you away from the experience of learning about node.js. Instead I'm just going to let you know the principle and let you go and find your own tutorial.
The idea is that you want to run the server on your localhost, read the requests as it comes in and if it should match something local then read that file and return it. Otherwise you want to copy the headers over to a new request and pipe the response back.
At catch we even decide whether to look at the uncompiled version of the code for development by putting a debug=1 in the query string parameters.
Tuesday, April 3, 2012
The Closure toolset
The Closure set of tools are a set of five components that help in creating web applications. You can check out their page to see the five different components. What we're going to look at here though is what they address.
An interesting alternative for projects where you don't want to go down the path of using a subset of javascript is UglifyJS
Closure Compiler
The Closure Compiler is perhaps the most interesting part when you first hear about the closure toolset. The compiler will take your javascript and minify it. There are already minifiers but the compiler actually parses your code and makes it better by doing things like code inlining. This means that your code will also run faster. The compiler will also do some great things like type checking to pick up errors that could turn around and bite you later. There are some rules though when you use the compiler which is effectively like using a subset of the language. Perhaps the biggest difference is that you can't mix bracket and dot notation (i.e. person.name != person['name']).An interesting alternative for projects where you don't want to go down the path of using a subset of javascript is UglifyJS
Closure Library
The Closure Library is built to work well with the compiler. It may look huge to begin with but most of that is comments and when you build your project you only include the code you use as opposed to other libraries where you need to include either the entire file or entire modules on the page. The closure library will allow you to do DOM manipulation, ajax, gives you UI components and also takes care of dependancy management through the reps_writer - a python script that goes through and figures out what order javascript needs to be loaded on to the page.
There are many alternatives which you can use instead. Thes include JQuery & JQueryUI, YUI, Sencha (ExtJS) and Dojo.
The library also gives a testing framework which is similar to other test frameworks like QUnit, mocha or jasmine.
Closure Templates
The Closure Templates, or Soy work with both javascript and java which allows them to be used on the client and server side. You can include some logic in them and extend their functionality through java plugins.
Closure Linter
The Closure Linter checks your code style to make sure it is always readable by other people in your group.
Linters are well known for hurting people's feelings - so be sure to run them often, you can even get JSHint as a plugin for Textmate.
Closure Stylesheets
The latest recent addition to the closure toolset is Closure Stylesheets. This allows you to to write CSS in a more programmatical way. One of the large benefits with Closure stylesheets is the minification and class renaming which can save even more bytes.
Alternatives are LESS and SASS and compass. You may still want a CSS minifier to use afterwards and you can use Yahoo's
So it's a pretty complete set of tools - the only thing that is really missing is a way to structure your code (and perhaps an IDE). Next blog article I'm going to take you through creating a local application which will be followed by posts about building and serving your application on a server. In those I will introduce you to Plovr and talk about creating a node proxy script so you can test pages "live".
I'd also recommend you pick up the Closure Definitive Guide, which you can also get on Amazon for your kindle.
Monday, April 2, 2012
Where we are
Javascript is very much in the maturing stage now. It has grown up from a way to place novelty snippets on pages to be the glue for large complex applications. Because of this javascript programmers should become familiar with development patterns and learn from more mature language. This is happening with the explosion of MVC frameworks and testing suites that are becoming available.
Testing is going to become increasingly important. This is because the size of codebases and the number of people who are working on them are increasing. As more people touch the core of an application it's important to have tests there so they don't break it.
MVC frameworks are a proven method of structuring an application. They allow for modularity which we should all strive for. By breaking our applications down to smaller applications we can untangle the mess of spaghetti code which leads to more readable code, more re-usable code and bugs that are easier to find.
There is a lot of information that is available on the internet about different frameworks and high level concepts for large applications. What I'm going to do is introduce you to these concepts by example using Google's closure tools.
Next post I'll give a brief introduction to what a large application needs, what the Closure tools can cover and other alternatives.
Testing is going to become increasingly important. This is because the size of codebases and the number of people who are working on them are increasing. As more people touch the core of an application it's important to have tests there so they don't break it.
MVC frameworks are a proven method of structuring an application. They allow for modularity which we should all strive for. By breaking our applications down to smaller applications we can untangle the mess of spaghetti code which leads to more readable code, more re-usable code and bugs that are easier to find.
There is a lot of information that is available on the internet about different frameworks and high level concepts for large applications. What I'm going to do is introduce you to these concepts by example using Google's closure tools.
Next post I'll give a brief introduction to what a large application needs, what the Closure tools can cover and other alternatives.
Sunday, April 1, 2012
Where we were
Javascript has come a long way from being a toy language where kids would copy snippets off other sites to add something cool to their website. It has become the glue that holds the internet together giving it that behavioral layer which allows us to interact with the content and each other.
Rather than be a history lesson I'd like to look at a couple of things that has made javascript what it is today. If you do want a history lesson and to go in to more detail then I'd heartily recommend watching the Crockford on JS series.
Rather than be a history lesson I'd like to look at a couple of things that has made javascript what it is today. If you do want a history lesson and to go in to more detail then I'd heartily recommend watching the Crockford on JS series.
Web 2.0
One of the main contributors to the language and it's use evolving is AJAX. Those four letters took the idea that a webpage was a single piece of content and turned it on it's head allowing programmers to bring in new content to the page. The first letter is perhaps the most important, asynchronous.
Being asynchronous the sites could change and become a whole new page without ever navigating, new content could be brought in and so we could do more with the page that we were on. This also meant that we could process the information that was sent to us and move the logic from the server side to the client side. Because of this we needed two new advances.
The first was how to send the information. The last letter stood for XML. This was a cumbersome way of giving data which was tricky to traverse and had a lot of repetition. Luckily there was a data format that we were already familiar with JSON.
The second breakthrough was the speed. Browsers began to realize that speeding up javascript execution allowed for better experiences in the browser. Google came out with chrome to push those boundaries. Javascript performance is still increasing and is extending what is possible inside the browser.
With these improvements javascript is no longer the toy language where anyone who has copied a snippet can claim to know javascript. The language is now something to be taken seriously and so we need to take our development approach seriously. In the next installment we'll touch upon some of the things that a modern day javascript programmer should know
Subscribe to:
Posts (Atom)