├── content ├── scripts │ ├── util │ │ ├── underscore.js │ │ ├── loadcss.js │ │ ├── module-activator.js │ │ ├── content-builder.js │ │ ├── cookie.js │ │ └── json.js │ ├── main.js │ ├── backbone │ │ └── todo │ │ │ ├── main.js │ │ │ ├── templates │ │ │ ├── todo.js │ │ │ └── stats.js │ │ │ ├── models │ │ │ └── todo.js │ │ │ ├── collections │ │ │ └── todoList.js │ │ │ ├── views │ │ │ ├── todoView.js │ │ │ └── appView.js │ │ │ └── localStorage.js │ └── lib │ │ ├── jquery.alphanumeric.js │ │ ├── selectivizr.js │ │ ├── jquery.tmpl.js │ │ ├── modernizr-1.6.min.js │ │ ├── backbone-min.js │ │ ├── nwmatcher-1.2.3-min.js │ │ ├── knockout.js │ │ ├── underscore.js │ │ └── backbone.js ├── images │ └── destroy.png └── styles │ └── todos.css ├── readme.md └── index.html /content/scripts/util/underscore.js: -------------------------------------------------------------------------------- 1 | define(["../lib/underscore.js"], function () { 2 | return _; 3 | }); -------------------------------------------------------------------------------- /content/images/destroy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rauhryan/Backbone_RequireJS/HEAD/content/images/destroy.png -------------------------------------------------------------------------------- /content/scripts/main.js: -------------------------------------------------------------------------------- 1 | require({ 2 | paths: { 3 | underscore: "util/underscore-wrapper" 4 | } 5 | }, 6 | ['util/content-builder', 'util/module-activator'], function (builder, activator) { 7 | activator.execute(); 8 | builder.execute(); 9 | }); -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Backbone.js + RequireJS 2 | ----------------------- 3 | 4 | This is just the todo example application used in the Backbone.js documentation refactored 5 | to use requireJS. 6 | 7 | The easiest way to get it running is probably use 8 | 9 | > gem install simpleserve 10 | 11 | 12 | > simpleserve 13 | 14 | -------------------------------------------------------------------------------- /content/scripts/backbone/todo/main.js: -------------------------------------------------------------------------------- 1 | define(["./views/appView", 2 | "util/loadCss", 3 | "/content/scripts/lib/backbone.js", 4 | "/content/scripts/lib/underscore.js", 5 | "/content/scripts/lib/jquery.tmpl.js"], 6 | function (AppView, loadCss) { 7 | //loadCss("todos"); 8 | var app = new AppView(); 9 | return {}; 10 | }); 11 | -------------------------------------------------------------------------------- /content/scripts/backbone/todo/templates/todo.js: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 | -------------------------------------------------------------------------------- /content/scripts/backbone/todo/templates/stats.js: -------------------------------------------------------------------------------- 1 | {{if (total) }} 2 | 3 | ${ remaining } 4 | ${ remaining == 1 ? 'item' : 'items' } left. 5 | 6 | {{/if}} 7 | {{if (done) }} 8 | 9 | 10 | Clear ${ done } 11 | completed ${ done == 1 ? 'item' : 'items' } 12 | 13 | 14 | {{/if}} 15 | -------------------------------------------------------------------------------- /content/scripts/backbone/todo/models/todo.js: -------------------------------------------------------------------------------- 1 | define([], function(){ 2 | var Todo = Backbone.Model.extend({ 3 | defaults : { 4 | content: "empty todo ...", 5 | done: false 6 | }, 7 | initialize: function (){ 8 | if(!this.get("content")){ 9 | this.set({"content":this.defaults.content}); 10 | } 11 | }, 12 | toggle : function () { 13 | this.save({done: !this.get('done')}); 14 | }, 15 | clear: function () { 16 | this.destroy(); 17 | this.view.remove(); 18 | } 19 | 20 | }); 21 | return Todo; 22 | }); 23 | -------------------------------------------------------------------------------- /content/scripts/backbone/todo/collections/todoList.js: -------------------------------------------------------------------------------- 1 | define([ "../models/todo","../localStorage"], function(Todo){ 2 | var TodoList = Backbone.Collection.extend({ 3 | model : Todo, 4 | localStorage: new Store("todos"), 5 | done: function(){ 6 | return this.filter(function(todo){ return todo.get('done'); }); 7 | }, 8 | remaining: function(){ 9 | return this.without.apply(this, this.done()); 10 | }, 11 | nextOrder : function() { 12 | if(!this.length) return 1; 13 | return this.last().get('order') + 1; 14 | }, 15 | comparator: function(todo){ 16 | return todo.get('order'); 17 | } 18 | 19 | 20 | 21 | }); 22 | 23 | return new TodoList; 24 | }); 25 | -------------------------------------------------------------------------------- /content/scripts/util/loadcss.js: -------------------------------------------------------------------------------- 1 | define(['util/underscore'], function (_) { 2 | return function loadCss(cssFile, element) { 3 | var path = "", 4 | folder = "/content/styles", 5 | extension = ".css", 6 | href = '/' + (path ? path + '/' : '') + folder + '/' + cssFile + extension; 7 | 8 | if (document.createStyleSheet) { 9 | document.createStyleSheet(href); 10 | } 11 | else { 12 | var link = $('', { 13 | rel: 'stylesheet', 14 | type: 'text/css', 15 | href: href 16 | }); 17 | link.appendTo('head'); 18 | if (element) { 19 | setTimeout(function () { 20 | element.css('visibility', 'visible'); 21 | }, 100); 22 | } 23 | } 24 | }; 25 | }); -------------------------------------------------------------------------------- /content/scripts/util/module-activator.js: -------------------------------------------------------------------------------- 1 | define(['util/loadcss'], function (loadcss) { 2 | var exports = {}; 3 | exports.execute = function (element) { 4 | $("[data-module-cssonly]", element) 5 | .each(function () { 6 | var item = $(this), 7 | css = item.data("module-cssonly"); 8 | loadcss(css, item); 9 | }); 10 | $("[data-module]", element) 11 | .each(function () { 12 | var item = $(this), 13 | module = item.data("module"), 14 | parameters = item.data("module-parameters"); 15 | 16 | require([module], function (mod) { 17 | if (mod.css) { 18 | loadcss(mod.css, item); 19 | } else { 20 | item.css('visibility', 'visible'); 21 | } 22 | if (mod.init) { 23 | mod.init(item, parameters); 24 | } 25 | }); 26 | }); 27 | }; 28 | return exports; 29 | }); -------------------------------------------------------------------------------- /content/scripts/lib/jquery.alphanumeric.js: -------------------------------------------------------------------------------- 1 | //http://itgroup.com.ph/alphanumeric/ 2 | eval(function (p, a, c, k, e, d) { e = function (c) { return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36)) }; if (!''.replace(/^/, String)) { while (c--) { d[e(c)] = k[c] || e(c) } k = [function (e) { return d[e] } ]; e = function () { return '\\w+' }; c = 1 }; while (c--) { if (k[c]) { p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]) } } return p } ('(2($){$.c.f=2(p){p=$.d({g:"!@#$%^&*()+=[]\\\\\\\';,/{}|\\":<>?~`.- ",4:"",9:""},p);7 3.b(2(){5(p.G)p.4+="Q";5(p.w)p.4+="n";s=p.9.z(\'\');x(i=0;i 2 | 3 | 4 | 5 | 6 | 7 | Home | TODO 8 | 9 | 10 | 11 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 |
34 |

Todos

35 |
36 | 37 |
38 | 39 |
40 | 41 | 42 |
43 | 44 |
45 |
    46 |
    47 | 48 |
    49 | 50 |
    51 | 52 |
    53 | 54 | 58 | 59 |
    60 | Created by 61 |
    62 | Jérôme Gravel-Niquet 63 |
    64 | 65 | 66 | -------------------------------------------------------------------------------- /content/scripts/backbone/todo/views/todoView.js: -------------------------------------------------------------------------------- 1 | define(["text!../templates/todo.js"], function (templateText) { 2 | var TodoView = Backbone.View.extend({ 3 | tagName:"li", 4 | template: function(data, options){ return $.tmpl(templateText, data, options)}, 5 | events: { 6 | "click .check" : "toggleDone", 7 | "dblclick div.todo-content" : "edit", 8 | "click span.todo-destroy" : "clear", 9 | "keypress .todo-input" : "updateOnEnter" 10 | }, 11 | initialize : function(){ 12 | _.bindAll(this, 'render', this.render); 13 | this.model.bind('change', this.render); 14 | this.model.view = this; 15 | }, 16 | 17 | render : function(){ 18 | $(this.el).html(this.template(this.model.toJSON())); 19 | this.setContent(); 20 | return this; 21 | }, 22 | // To avoid XSS (not that it would be harmful in this particular app), 23 | // we use `jQuery.text` to set the contents of the todo item. 24 | setContent: function() { 25 | var content = this.model.get('content'); 26 | this.$('.todo-content').text(content); 27 | this.input = this.$('.todo-input'); 28 | this.input.bind('blur', this.close); 29 | this.input.val(content); 30 | }, 31 | // Toggle the `"done"` state of the model. 32 | toggleDone: function() { 33 | this.model.toggle(); 34 | }, 35 | 36 | // Switch this view into `"editing"` mode, displaying the input field. 37 | edit: function() { 38 | $(this.el).addClass("editing"); 39 | this.input.focus(); 40 | }, 41 | 42 | // Close the `"editing"` mode, saving changes to the todo. 43 | close: function() { 44 | this.model.save({content: this.input.val()}); 45 | $(this.el).removeClass("editing"); 46 | }, 47 | 48 | // If you hit `enter`, we're through editing the item. 49 | updateOnEnter: function(e) { 50 | if (e.keyCode == 13) this.close(); 51 | }, 52 | 53 | // Remove this view from the DOM. 54 | remove: function() { 55 | $(this.el).remove(); 56 | }, 57 | 58 | // Remove the item, destroy the model. 59 | clear: function() { 60 | this.model.clear(); 61 | } 62 | 63 | 64 | }); 65 | return TodoView; 66 | }); 67 | -------------------------------------------------------------------------------- /content/scripts/backbone/todo/views/appView.js: -------------------------------------------------------------------------------- 1 | define(["text!../templates/stats.js","../collections/todoList","./todoView"], function(statsTemplate, Todos,TodoView){ 2 | var AppView = Backbone.View.extend({ 3 | el : $("#todoapp"), 4 | statsTemplate: function(data, options){return $.tmpl(statsTemplate, data, options);}, 5 | events: { 6 | "keypress #new-todo":"createOnEnter", 7 | "keyup #new-todo" : "showTooltip", 8 | "click .todo-clear a": "clearCompleted" 9 | }, 10 | initialize: function(){ 11 | _.bindAll(this, 'addOne', 'addAll', 'render'); 12 | this.input = this.$("#new-todo"); 13 | 14 | Todos.bind('add', this.addOne); 15 | Todos.bind('refresh', this.addAll); 16 | Todos.bind('all', this.render); 17 | 18 | Todos.fetch(); 19 | }, 20 | render: function(){ 21 | var done = Todos.done().length; 22 | this.$('#todo-stats').html(this.statsTemplate({ 23 | total: Todos.length, 24 | done: Todos.done().length, 25 | remaining: Todos.remaining().length 26 | })) 27 | }, 28 | addOne : function(todo){ 29 | var view = new TodoView({model:todo}); 30 | this.$("#todo-list").append(view.render().el); 31 | }, 32 | addAll : function() { 33 | Todos.each(this.addOne); 34 | }, 35 | newAttributes : function(){ 36 | return { 37 | content: this.input.val(), 38 | order: Todos.nextOrder(), 39 | done: false 40 | }; 41 | }, 42 | createOnEnter: function(e) { 43 | if (e.keyCode != 13) return; 44 | Todos.create(this.newAttributes()); 45 | this.input.val(''); 46 | }, 47 | clearCompleted: function() { 48 | _.each(Todos.done(), function(todo){ todo.clear(); }); 49 | return false; 50 | }, 51 | showTooltip: function(e) { 52 | var tooltip = this.$(".ui-tooltip-top"); 53 | var val = this.input.val(); 54 | tooltip.fadeOut(); 55 | if (this.tooltipTimeout) clearTimeout(this.tooltipTimeout); 56 | if (val == '' || val == this.input.attr('placeholder')) return; 57 | var show = function(){ tooltip.show().fadeIn(); }; 58 | this.tooltipTimeout = _.delay(show, 1000); 59 | } 60 | 61 | }); 62 | return AppView; 63 | 64 | }); 65 | 66 | -------------------------------------------------------------------------------- /content/scripts/util/content-builder.js: -------------------------------------------------------------------------------- 1 | define(['./underscore', './cookie', './module-activator'], function (_, cookie, activator) { 2 | var exports = {}, 3 | replaceContent = function (element, content) { 4 | element[0].innerHTML = content; 5 | element.css('visibility', 'visible'); 6 | }, 7 | authorizedCookieKey = 'authorized'; 8 | 9 | exports.execute = function (element) { 10 | $('div[data-dynamic-uri], div[data-auth-uri]', element) 11 | .css('visibility', 'hidden') 12 | .each(function () { 13 | var element = $(this), 14 | load = element.data('dynamic-load'), 15 | authUri = element.data('auth-uri'), 16 | dynamicUri = element.data('dynamic-uri'), 17 | authCookie = cookie(authorizedCookieKey), 18 | uri = (authUri && authCookie) ? authUri : dynamicUri, 19 | match = /\{\w+\}/.exec(uri), 20 | val; 21 | 22 | if (load) { 23 | replaceContent(element, 'Loading'); 24 | } 25 | 26 | /* temporary alerts to help us get usage right */ 27 | if (element.data("dynamic-auth")) { 28 | alert("Switch data-dynamic-auth to use data-auth-uri"); 29 | return; 30 | } 31 | 32 | /* checks to return early and make content visible */ 33 | if (authUri && !authCookie && !dynamicUri) { 34 | element.css('visibility', 'visible'); 35 | return; 36 | } 37 | 38 | if (match) { 39 | val = cookie(match[0].slice(1, match.length - 2)); 40 | val = val || ""; 41 | uri = uri.replace(match, val); 42 | } 43 | 44 | $.ajax({ 45 | url: uri, 46 | success: function (resp) { 47 | replaceContent(element, resp); 48 | activator.execute(element); 49 | exports.execute(element); 50 | }, 51 | error: function (xhr) { 52 | var authCookieChk = cookie(authorizedCookieKey); 53 | if (xhr.status === 403 && authCookieChk) { 54 | cookie(authorizedCookieKey, null, { path: "/" }); 55 | location.reload(); 56 | } 57 | element.css('visibility', 'visible'); 58 | } 59 | }); 60 | }); 61 | }; 62 | 63 | return exports; 64 | }); -------------------------------------------------------------------------------- /content/scripts/backbone/todo/localStorage.js: -------------------------------------------------------------------------------- 1 | // A simple module to replace `Backbone.sync` with *localStorage*-based 2 | // persistence. Models are given GUIDS, and saved into a JSON object. Simple 3 | // as that. 4 | 5 | // Generate four random hex digits. 6 | function S4() { 7 | return (((1+Math.random())*0x10000)|0).toString(16).substring(1); 8 | }; 9 | 10 | // Generate a pseudo-GUID by concatenating random hexadecimal. 11 | function guid() { 12 | return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); 13 | }; 14 | 15 | // Our Store is represented by a single JS object in *localStorage*. Create it 16 | // with a meaningful name, like the name you'd give a table. 17 | var Store = function(name) { 18 | this.name = name; 19 | var store = localStorage.getItem(this.name); 20 | this.data = (store && JSON.parse(store)) || {}; 21 | }; 22 | 23 | _.extend(Store.prototype, { 24 | 25 | // Save the current state of the **Store** to *localStorage*. 26 | save: function() { 27 | localStorage.setItem(this.name, JSON.stringify(this.data)); 28 | }, 29 | 30 | // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already 31 | // have an id of it's own. 32 | create: function(model) { 33 | if (!model.id) model.id = model.attributes.id = guid(); 34 | this.data[model.id] = model; 35 | this.save(); 36 | return model; 37 | }, 38 | 39 | // Update a model by replacing its copy in `this.data`. 40 | update: function(model) { 41 | this.data[model.id] = model; 42 | this.save(); 43 | return model; 44 | }, 45 | 46 | // Retrieve a model from `this.data` by id. 47 | find: function(model) { 48 | return this.data[model.id]; 49 | }, 50 | 51 | // Return the array of all models currently in storage. 52 | findAll: function() { 53 | return _.values(this.data); 54 | }, 55 | 56 | // Delete a model from `this.data`, returning it. 57 | destroy: function(model) { 58 | delete this.data[model.id]; 59 | this.save(); 60 | return model; 61 | } 62 | 63 | }); 64 | 65 | // Override `Backbone.sync` to use delegate to the model or collection's 66 | // *localStorage* property, which should be an instance of `Store`. 67 | Backbone.sync = function(method, model, options) { 68 | 69 | var resp; 70 | var store = model.localStorage || model.collection.localStorage; 71 | 72 | switch (method) { 73 | case "read": resp = model.id ? store.find(model) : store.findAll(); break; 74 | case "create": resp = store.create(model); break; 75 | case "update": resp = store.update(model); break; 76 | case "delete": resp = store.destroy(model); break; 77 | } 78 | 79 | if (resp) { 80 | options.success(resp); 81 | } else { 82 | options.error("Record not found"); 83 | } 84 | }; -------------------------------------------------------------------------------- /content/scripts/lib/selectivizr.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * selectivizr v1.0.1 - (c) Keith Clark, freely distributable under the terms of the MIT license. 3 | * selectivizr.com 4 | */ 5 | var k=true,p=false;(function(A){function N(a){return a.replace(O,q).replace(P,function(b,e,c){b=c.split(",");c=0;for(var g=b.length;c0){d=f;var x;i=h.substring(0,i).replace(U,o);if(i==o||i.charAt(i.length-1)==w)i+="*";try{x=y(i)}catch(ha){}if(x){i=0;for(m=x.length;i-1)a=a.substring(0,f);if(a.charAt(0)==":")switch(a.slice(1)){case "root":b=function(d){return c?d!=H:d==H};break;case "target":if(s==8){b=function(d){function l(){var m=location.hash,j=m.slice(1);return c?m==""||d.id!=j:m!=""&&d.id==j}t(A,"hashchange",function(){u(d,e,l())});return l()};break}return p;case "checked":b=function(d){X.test(d.type)&&t(d,"propertychange",function(){event.propertyName=="checked"&&u(d,e,d.checked!==c)});return d.checked!==c};break;case "disabled":c=!c;case "enabled":b=function(d){if(Y.test(d.tagName)){t(d,"propertychange",function(){event.propertyName=="$disabled"&&u(d,e,d.a===c)});z.push(d);d.a=d.disabled;return d.disabled===c}return a==":enabled"?c:!c};break;case "focus":g="focus";h="blur";case "hover":if(!g){g="mouseenter";h="mouseleave"}b=function(d){t(d,c?h:g,function(){u(d,e,k)});t(d,c?g:h,function(){u(d,e,p)});return c};break;default:if(!Z.test(a))return p}return{className:e,b:b}}function G(a){return I+"-"+(s==6&&$?aa++:a.replace(ba,function(b){return b.charCodeAt(0)}))}function Q(a){return a.replace(J,q).replace(ca,w)}function u(a,b,e){var c=a.className;b=E(c,b,e);if(b!=c){a.className=b;a.parentNode.className+=o}}function E(a,b,e){var c=RegExp("(^|\\s)"+b+"(\\s|$)"),g=c.test(a);return e?g?a:a+w+b:g?a.replace(c,q).replace(J,q):a}function t(a,b,e){a.attachEvent("on"+b,e)}function D(a,b){if(/^https?:\/\//i.test(a))return b.substring(0,b.indexOf("/",8))==a.substring(0,a.indexOf("/",8))?a:null;if(a.charAt(0)=="/")return b.substring(0,b.indexOf("/",8))+a;var e=b.split("?")[0];if(a.charAt(0)!="?"&&e.charAt(e.length-1)!="/")e=e.substring(0,e.lastIndexOf("/")+1);return e+a}function K(a){if(a){v.open("GET",a,p);v.send();return(v.status==200?v.responseText:o).replace(da,o).replace(ea,function(b,e,c,g,h){return K(D(c||h,a))}).replace(fa,function(b,e,c){e=e||"";return" url("+e+D(c,a)+e+") "})}return o}function ga(){var a,b;a=n.getElementsByTagName("BASE");for(var e=a.length>0?a[0].href:n.location.href,c=0;c0&&setInterval(function(){for(var g=0,h=z.length;g8||!v)){var L={NW:"*.Dom.select",DOMAssistant:"*.$",Prototype:"$$",YAHOO:"*.util.Selector.query",MooTools:"$$",Sizzle:"*",jQuery:"*",dojo:"*.query"},y,z=[],aa=0,$=k,I="slvzr",M=I+"DOMReady",da=/(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)\s*/g,ea=/@import\s*(?:(?:(?:url\(\s*(['"]?)(.*)\1)\s*\))|(?:(['"])(.*)\3))[^;]*;/g,fa=/\burl\(\s*(["']?)([^"')]+)\1\s*\)/g,Z=/^:(empty|(first|last|only|nth(-last)?)-(child|of-type))$/,O=/:(:first-(?:line|letter))/g,P=/(^|})\s*([^\{]*?[\[:][^{]+)/g,T=/([ +~>])|(:[a-z-]+(?:\(.*?\)+)?)|(\[.*?\])/g,U=/(:not\()?:(hover|enabled|disabled|focus|checked|target|active|visited|first-line|first-letter)\)?/g,ba=/[^\w-]/g,Y=/^(INPUT|SELECT|TEXTAREA|BUTTON)$/,X=/^(checkbox|radio)$/,F=s>6?/[\$\^*]=(['"])\1/:null,R=/([(\[+~])\s+/g,S=/\s+([)\]+~])/g,ca=/\s+/g,J=/^\s*((?:[\S\s]*\S)?)\s*$/,o="",w=" ",q="$1";n.write("