├── .gitignore ├── .gitmodules ├── Gruntfile.js ├── build ├── changelog.ejs ├── tasks │ ├── bannerize.js │ ├── beautify.js │ ├── build.js │ ├── changelog.js │ ├── docco.js │ ├── testify.js │ └── updateversion.js └── underscore.js ├── changelog.md ├── js ├── js.bat ├── license.md ├── package.json ├── readme.md ├── scripts ├── getjmvc └── getjmvc.bat ├── site ├── blog.html ├── builder.html ├── code.html ├── contribute.html ├── docs.html ├── docs.js ├── follow.html ├── fonts │ ├── museo_slab_500-webfont.eot │ ├── museo_slab_500-webfont.svg │ ├── museo_slab_500-webfont.ttf │ ├── museo_slab_500-webfont.woff │ └── museoslab.css ├── images │ ├── add_tag_example.png │ ├── backgrounds │ │ ├── body.png │ │ ├── bottom.png │ │ ├── calendar.png │ │ ├── download │ │ │ ├── documentjs.png │ │ │ ├── funcunit.png │ │ │ ├── javascriptmvc.png │ │ │ ├── jquerymx.png │ │ │ ├── stealjs.png │ │ │ └── syn.png │ │ ├── greenbox.png │ │ ├── link_hover.png │ │ ├── navigation.png │ │ └── quote.png │ ├── coverage_commandline.png │ ├── coverage_file.png │ ├── coverage_report.png │ ├── crm_doc_demo_1.png │ ├── crm_doc_demo_2.png │ ├── follow_twitter.png │ ├── fork.png │ ├── funcunit.png │ ├── funcunit_medium.png │ ├── funcunit_small.png │ ├── funcunitfolder.png │ ├── funcunithtml.png │ ├── header.png │ ├── iepopups.png │ ├── iesecurity.png │ ├── logo.png │ ├── page_type_example.png │ ├── parent_tag_example.png │ ├── phui.png │ ├── plugin_tag_example.png │ ├── return_tag_example.png │ ├── rss.png │ ├── tag_tag_example.png │ ├── team │ │ ├── brian.png │ │ ├── joe.png │ │ ├── justin.png │ │ ├── lee.png │ │ ├── matt.png │ │ ├── max.png │ │ ├── michael.png │ │ └── trey.png │ ├── test_cookbook_example.png │ ├── test_tag_example.png │ └── test_tag_test_example.png ├── pages │ ├── developingjmvc.md │ ├── developingwithgit.md │ ├── folders.md │ ├── help.md │ ├── learn.js │ └── why.js ├── scripts │ ├── build.html │ ├── build.js │ ├── clean.js │ ├── compress.js │ ├── crawl.js │ ├── doc.html │ └── doc.js ├── site.js ├── static │ └── styles │ │ └── config.less ├── stylesheets │ ├── application.css │ ├── builder.css │ ├── reset.css │ └── site.less ├── summary.ejs ├── templates │ └── layout.mustache └── views │ └── blog.ejs ├── stealconfig.js ├── test ├── run.js ├── scripts │ ├── big_test.js │ └── getting_started_test.js └── test.js └── tutorials ├── ajaxy ├── ajaxy.html ├── ajaxy.md └── fixtures │ ├── articles.html │ ├── images.html │ └── videos.html ├── cms.png ├── done.md ├── examples.md ├── examples ├── contacts.md ├── srchr.md ├── todo.html └── todo.md ├── funcunit.md ├── getstarted ├── Cookbook.png ├── Docs.png ├── Welcome.png ├── building.md ├── creating.md ├── documenting.md ├── getstarted.md ├── selenium-run.png └── testing.md ├── images ├── .DS_Store ├── app_organization.png ├── app_scaffold.png ├── contacts_design.png ├── contacts_preview.jpg ├── contacts_preview.png ├── contacts_widgets.jpg ├── coverage1.png ├── coverage2.png ├── diagram.gif ├── diagram.png ├── diagram_search.png ├── diagram_tabs.png ├── eoa_diagram1.jpg ├── eoa_diagram2.jpg ├── inputs_outputs.jpg ├── playermx.png ├── playermx_overview.png ├── playermx_play.png ├── playermx_position.png ├── tabs.png ├── todo_arch.png └── todos.png ├── installing.md ├── jquerypp.md ├── mvc.md ├── organizing.md ├── rapidstart ├── rapidstart.md ├── test.html ├── todos.ejs ├── todos.html ├── todos.js └── todos_test.js ├── rootfolder.md ├── services.md └── tutorials.md /.gitignore: -------------------------------------------------------------------------------- 1 | docs/* 2 | .tmp* 3 | cookbook* 4 | site/docs/* 5 | scripts/key 6 | .DS_Store 7 | html/ 8 | selenium.log 9 | packages/* 10 | tmp* 11 | node_modules 12 | npm-debug.log 13 | backbone 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "can"] 2 | path = can 3 | url = git://github.com/bitovi/canjs.git 4 | [submodule "documentjs"] 5 | path = documentjs 6 | url = git://github.com/bitovi/documentjs.git 7 | [submodule "jquerypp"] 8 | path = jquerypp 9 | url = https://github.com/bitovi/jquerypp.git 10 | [submodule "jmvc"] 11 | path = jmvc 12 | url = git://github.com/bitovi/jmvc-generators 13 | [submodule "funcunit"] 14 | path = funcunit 15 | url = git://github.com/bitovi/legacy-funcunit.git 16 | [submodule "steal"] 17 | path = steal 18 | url = git://github.com/bitovi/legacy-steal.git 19 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | grunt.loadNpmTasks('grunt-contrib-connect'); 4 | 5 | grunt.initConfig({ 6 | connect: { 7 | server: { 8 | options: { 9 | port: 8000, 10 | base: '.', 11 | keepalive: true 12 | } 13 | } 14 | } 15 | }) 16 | 17 | grunt.registerTask("server", "connect:server") 18 | }; -------------------------------------------------------------------------------- /build/changelog.ejs: -------------------------------------------------------------------------------- 1 | __<%= version %>__ ( <%= date.toDateString().substring(4) %> ) 2 | <% for(var i = 0; i < issues.length; i++) { %> 3 | - change: [<%= issues[i].title %>](<%= issues[i].url %>)<% } %> 4 | 5 | -------------------------------------------------------------------------------- /build/tasks/bannerize.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | // A grunt task that writes the banner to every file 4 | module.exports = function (grunt) { 5 | grunt.registerMultiTask('bannerize', 'Adds the banner to a set of files', function () { 6 | var options = grunt.config.process(['bannerize', this.target]); 7 | var banner = this.data.banner; 8 | 9 | grunt.file.expand(this.data.files).forEach(function (file) { 10 | var outFile = options.out ? path.join(options.out, path.basename(file)) : file; 11 | 12 | grunt.log.writeln('Adding banner to ' + file); 13 | grunt.file.write(outFile, banner + grunt.file.read(file)); 14 | }); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /build/tasks/beautify.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-beautify.git 3 | * https://github.com/pix/grunt-beautify 4 | * 5 | * Copyright (c) 2012 Camille Moncelier 6 | * Licensed under the MIT license. 7 | */ 8 | var beautifier = require('js-beautify'); 9 | 10 | module.exports = function (grunt) { 11 | 12 | // Please see the grunt documentation for more information regarding task and 13 | // helper creation: https://github.com/cowboy/grunt/blob/master/docs/toc.md 14 | // ========================================================================== 15 | // TASKS 16 | // ==========================================================================a 17 | var default_options = { 18 | indentSize : 2 19 | }; 20 | 21 | grunt.registerMultiTask('beautify', 'Javascript beautifier', function () { 22 | var options = null; 23 | var tmp = grunt.config(['beautifier', this.target, 'options']); 24 | if (typeof tmp === 'object') { 25 | grunt.verbose.writeln('Using "' + this.target + '" beautifier options.'); 26 | options = tmp; 27 | } else { 28 | tmp = grunt.config('beautifier.options'); 29 | if (typeof tmp === 'object') { 30 | grunt.verbose.writeln('Using master beautifier options.'); 31 | options = tmp; 32 | } else { 33 | grunt.verbose.writeln('Using beautifier default options.'); 34 | options = default_options; 35 | } 36 | } 37 | 38 | // Beautify specified files. 39 | var excludes = grunt.config(['beautifier', this.target, 'exclude']); 40 | 41 | grunt.file.expand(this.filesSrc).filter(function (file) { 42 | if(/\.min\./.test(file)) { 43 | grunt.log.writeln('Not beautifying ' + file); 44 | return false; 45 | } 46 | 47 | for (var i = 0; i < excludes.length; i++) { 48 | if (excludes[i].test(file)) { 49 | grunt.log.writeln('Not beautifying ' + file); 50 | return false; 51 | } 52 | } 53 | return true; 54 | }).forEach(function (filepath) { 55 | grunt.log.writeln('Beautifying ' + filepath); 56 | var result = beautifier(grunt.file.read(filepath), options); 57 | grunt.file.write(filepath, result); 58 | }); 59 | 60 | }); 61 | 62 | }; -------------------------------------------------------------------------------- /build/tasks/build.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var jsDir = path.join( __dirname, "../.."); 3 | 4 | module.exports = function( grunt ) { 5 | grunt.registerMultiTask('build', 'Runs build files.', function() { 6 | var done = this.async(); 7 | var target = this.target; 8 | 9 | var options = grunt.config.process(['build', target]); 10 | var args = [this.data.src, this.data.out || 'dist/', this.data.version || 'edge']; 11 | var libraries = Array.isArray(this.data.libraries) ? this.data.libraries : []; 12 | 13 | args.push.apply(args, libraries); 14 | 15 | grunt.verbose.writeflags(this.data, 'Options'); 16 | grunt.log.writeln('Running ./js ' + args.join(' ')); 17 | 18 | grunt.util.spawn({ 19 | cmd : "./js", 20 | args : args, 21 | opts : { 22 | cwd: jsDir 23 | } 24 | }, function(error, result, code) { 25 | grunt.log.writeln('Done building'); 26 | done(); 27 | }); 28 | 29 | grunt.log.write("Building " + this.data.src + " with Steal...\n"); 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /build/tasks/changelog.js: -------------------------------------------------------------------------------- 1 | var https = require('https'), 2 | querystring = require('querystring'), 3 | ejs = require('ejs'); 4 | 5 | module.exports = function(grunt) { 6 | 7 | grunt.registerMultiTask('changelog', 'Updates changelog.md based on GitHub milestones', function() { 8 | var done = this.async(), 9 | 10 | params = querystring.stringify({ 11 | milestone: this.data.milestone, 12 | state: 'closed', 13 | per_page: 100 14 | }), 15 | 16 | path = '/repos/' + this.data.user + '/' + this.data.repo + '/issues?' + params, 17 | 18 | buffer = '', 19 | self = this; 20 | 21 | var write = function() { 22 | var issues = JSON.parse(buffer), 23 | log = ''; 24 | 25 | if(grunt.file.exists('changelog.md')) { 26 | log = grunt.file.read('changelog.md'); 27 | }; 28 | 29 | ejs.renderFile(__dirname + '/../changelog.ejs', { 30 | version: self.data.version, 31 | date: new Date(Date.now()), 32 | issues: issues 33 | }, function(e, template) { 34 | 35 | if(e) { 36 | done(e); 37 | } 38 | 39 | grunt.file.write('changelog.md', template + log); 40 | done(); 41 | 42 | }); 43 | }, 44 | 45 | req = https.request({ 46 | hostname: 'api.github.com', 47 | path: path 48 | }, function(res) { 49 | 50 | res.on('data', function(data) { 51 | buffer += data; 52 | }); 53 | 54 | res.on('end', write); 55 | }); 56 | 57 | req.end(); 58 | 59 | req.on('error', function(e) { 60 | done(e); 61 | }); 62 | 63 | }); 64 | 65 | } -------------------------------------------------------------------------------- /build/tasks/docco.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Grunt Docco task. Based on https://github.com/DavidSouther/grunt-docco 3 | */ 4 | var docco = require('docco'); 5 | 6 | module.exports = function(grunt) { 7 | grunt.registerMultiTask('docco', 'Docco processor.', function() { 8 | var _ = grunt.util._; 9 | var done = this.async(); 10 | var options = this.options(); 11 | var src = grunt.file.expand(this.data.files).filter(function(file) { 12 | return !_.some(options.exclude, function(exclude) { 13 | return exclude.test(file); 14 | }); 15 | }); 16 | 17 | docco.document( _.extend({ args: src }, this.data.docco, options.docco), function(err, result, code){ 18 | grunt.log.writeln("Doccoed [" + src.join(", ") + "]; " + 19 | err ? err : "(No errors)" + "\n" + result + " " + code); 20 | done(); 21 | }); 22 | }); 23 | } -------------------------------------------------------------------------------- /build/tasks/testify.js: -------------------------------------------------------------------------------- 1 | var ejs = require('ejs'); 2 | var beautify = require('js-beautify'); 3 | 4 | module.exports = function(grunt) { 5 | var _ = grunt.util._; 6 | 7 | grunt.registerMultiTask('testify', 'Generates test runners', function() { 8 | var done = this.async(); 9 | var data = this.data; 10 | var template = grunt.file.read(this.data.template); 11 | var transform = this.data.transform || {}; 12 | var modules = this.data.builder.modules; 13 | var configurations = this.data.builder.configurations; 14 | 15 | _.each(configurations, function(config, configurationName) { 16 | var options = { 17 | configuration: config, 18 | modules: [], 19 | tests: [], 20 | root: data.root, 21 | '_': _ 22 | }; 23 | 24 | _.each(modules, function(definition, key) { 25 | if(!definition.configurations || definition.configurations.indexOf(configurationName) !== -1) { 26 | var name = key.substr(key.lastIndexOf('/') + 1); 27 | var mod = transform.module ? transform.module(definition, key) : key; 28 | var test = transform.test ? transform.test(definition, key) : (key + '/' + name + '_test.js'); 29 | 30 | mod && options.modules.push(mod); 31 | test && options.tests.push(test); 32 | } 33 | }); 34 | 35 | _.extend(config.steal, { 36 | root: data.root 37 | }); 38 | 39 | if(transform && transform.options) { 40 | _.extend(options, transform.options.call(config, configurationName)); 41 | } 42 | 43 | var lib = '\n'+ 44 | beautify.html(ejs.render(template, options), { 45 | "wrap_line_length": 70 46 | }); 47 | 48 | grunt.log.writeln('Generating ' + data.out + configurationName + '.html'); 49 | grunt.file.write(data.out + configurationName + '.html', lib); 50 | }); 51 | 52 | done(); 53 | }); 54 | }; -------------------------------------------------------------------------------- /build/tasks/updateversion.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | // A grunt task that writes the banner to every file 4 | module.exports = function (grunt) { 5 | grunt.registerMultiTask('updateversion', 'Replaces given symbol with current version', function () { 6 | var options = grunt.config.process(['updateversion', this.target]); 7 | var version = this.data.version; 8 | var symbol = this.data.symbol; 9 | 10 | grunt.file.expand(this.data.files).forEach(function (file) { 11 | var outFile = options.out ? path.join(options.out, path.basename(file)) : file; 12 | var fileContents = grunt.file.read(file).replace(symbol, version); 13 | 14 | if(grunt.file.read(file).match(symbol)) { 15 | grunt.log.writeln('Updating version in ' + file); 16 | grunt.file.write(outFile, fileContents.replace(symbol, version)); 17 | } 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ## 3.3 2 | 3 | CanJS is now stolen and is the prefered way of doing things. $.Class, $.Model, $.View, $.Controller, $.route, $.Observe are all 4 | now can.NAME. To maintain large amounts of backwards compatability, these things still exist. However, 5 | using can is now encouraged. Also, can has things as plugins that were included by default previously. 6 | 7 | 8 | ### Class 9 | 10 | - Removed .Class access to instance's constructor function, use .constructor instead 11 | - Removed callback, use .proxy 12 | 13 | ### Controller 14 | 15 | - `delegate` and `bind` are replaced with `on` 16 | 17 | ### Observe 18 | 19 | - $.O is removed 20 | - attrs is now just attr 21 | - sort is removed (temporarily) 22 | 23 | ### Model 24 | 25 | - model's implemented ajax methods do not take success / error, they produce methods that do 26 | - model.update(attrs, success, error) is removed, use model.attr(attrs).save(success, error) 27 | - model list does not get updated events, listen for change 28 | - error message handlers get `handler(ev, attr, errorName)` 29 | - `this` in save's success handler `save(succes, error)` is now the deferred. 30 | - There are no default fixtures for models anymore. 31 | - there is no error handler on attributes. Use an event handler. 32 | - Abstracted element helper `.models` to be applicable to `can.Observe` and renamed to `.instances` 33 | 34 | ### View 35 | 36 | - Returns a documentFragment 37 | - All types are moved into can 38 | - Helpers are removed 39 | - plugin is gone, use <%= (el)-> el.pluginName(args) %> 40 | - callback functions execute before the fragment is inserted into the DOM 41 | 42 | ### Fixture 43 | 44 | - fixture.make returns a store 45 | 46 | ## 3.2.2 47 | 48 | ### Model 49 | 50 | - Removed this.publish 51 | 52 | 53 | ## 3.2.1 (10/18/11) 54 | 55 | ### Steal 56 | 57 | - Fixed a bug in steal/html that made it unusable 58 | 59 | ### FuncUnit 60 | 61 | - Fixed a bug in PhantomJS steal.browser 62 | 63 | ## 3.2 (10/15/11) 64 | 65 | ### JavaScriptMVC 66 | 67 | - Updated Getting Started Guide 68 | - Added tutorials for FuncUnit, jQueryMX, and StealJS 69 | - Added examples for Contacts, PlayerMX, Todo, and Srchr apps. 70 | - Added Organizing your App, Searchable Ajax Apps, Migrating to 3.1, and Ajax Service Guidelines tutorial 71 | 72 | ### StealJS 73 | 74 | - js accepts `-e` to exit on error 75 | - steal works asynchronously 76 | - steal uses suffix as type (using steal.type) 77 | - removed steal.plugins, steal.less, steal.css, etc. 78 | - added steal.parse 79 | - fixed bug with steal.dev not handling nested parenthesis 80 | - added steal.html and steal.html.crawl 81 | - IE loads more than 32 styles 82 | - added steal.browser 83 | - steal.get can follow steals and install dependencies 84 | - added steal.loaded and steal.has 85 | 86 | ### jQueryMX 87 | 88 | - Better distance calculation on drag-drop 89 | - $.Range fixes for IE 90 | - Added $.Observe and $.route 91 | - fixtures handle 0 based ids 92 | - CoffeeScript generator 93 | - Moved string helpers to lang/string 94 | - Added $.Object helpers 95 | - $.fixture can intercept a request and handle templated urls. 96 | - Updated generators to insert steal requests auto-magically 97 | - FormParams leaves values as strings by default. 98 | - dimensions works when not provided an element 99 | - upgraded to jQuery 1.6.4 100 | 101 | #### View 102 | 103 | - EJS escapes content by default. Use <%== to not escape. 104 | - Bugs fixed jQuery modify helpers when not passing html. 105 | - EJS filenames show up in firebug on the filesystem. 106 | 107 | 108 | #### Controller 109 | 110 | - Removed Document Controllers 111 | - pluginName works right 112 | - Controller's can bind on constructors or other functions. 113 | - plugin helper code happens in setup 114 | - update rebinds event handlers 115 | 116 | #### Model 117 | 118 | - added beta $.Model.Store 119 | - Removed associations, added convert 120 | - removed wrap and wrapMany in favor of model and models. 121 | - Model.List creates updated events instead of update events. 122 | - Model uses static update and destroy for ajax events. 123 | 124 | ### FuncUnit 125 | 126 | - 'inherits' from jQuery via .sub() 127 | - Uses steal.browser so PhantomJS and browsers can work 128 | - Faster Page Opening 129 | - Uses latest QUnit 130 | 131 | 132 | ### Syn 133 | 134 | - rightclick works better 135 | 136 | ### DocumentJS 137 | 138 | - caches content in localStorage 139 | - better breadcumb 140 | - handles .md files 141 | 142 | 143 | ## 3.1 (5/17/2011) 144 | 145 | ### JavaScriptMVC 146 | 147 | - Added getjmvc script 148 | - Added install script for windows 149 | - Added new init page with framework overview 150 | - Added error level (-e) support to the js.bat (Windows) and ./js (Mac, Linux) 151 | 152 | ### jQueryMX 153 | 154 | - jQuery upgraded to 1.6.1 155 | - .val method supports Views. EX: $('input').val('view_name', {}); 156 | - Added range plugin 157 | - Added deparam plugin - Takes a string of name value pairs and returns a Object literal that represents those params. 158 | 159 | #### Model 160 | 161 | - Deferreds and Converter Support. 162 | - Added VERB support to parameterized CRUD urls. EX: update: "POST /recipe/update/{id}.json" 163 | - Global model events. EX: Recipe.bind('update', func). 164 | - Attribute update event. EX: recipe.bind('updated.attr', func); 165 | - Model.list upgraded to handle findAll, findOne 166 | - AJAX converters are renamed: wrap -> model and wrapMany -> models 167 | - Added dataType optional param to the ajax function 168 | - Added filters to Fixtures 169 | - Models and Fixtures support create, delete, and update model encapsulation. 170 | 171 | #### Events 172 | 173 | - Added swipe, swipeleft and swiperight events 174 | - Swipe left and swipe right added to jQuery.event.special for autobinding with controller 175 | - Hover can set leave. EX: $('.elem').bind('hoverleave', func) 176 | - Hover only runs one Mouseenter / Mouseleave per selector at a time 177 | - Added support for HTML5 history API 178 | - Drag and drop allows adding drops after drag has started 179 | - Drag doesn't select text anymore 180 | - Limit and step take center param 181 | - Limit can limit center of drag 182 | - Added pause and resume for events 183 | 184 | #### Controller 185 | 186 | - Added object binding to parameterized controller events. EX: "{window} load". 187 | 188 | #### View 189 | 190 | - Deferreds support 191 | - Better warning when templates don't exist 192 | 193 | #### Funcunit 194 | 195 | - Changes to the repeat API. 196 | - Added eval. 197 | - Added examples. 198 | 199 | #### Syn 200 | 201 | - Syn adjusts scrolling for drag / move positions not in the page. 202 | - Syn loads in Rhino, and documentation can be generated. 203 | - Syn works under Env.js. -------------------------------------------------------------------------------- /js: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This script checks for arguments, if they don't exist it opens the Rhino dialog 3 | # if arguments do exist, it loads the script in the first argument and passes the other arguments to the script 4 | # ie: ./js steal/script/controller Todo 5 | 6 | if [ $# -eq 0 ] 7 | then 8 | java -cp steal/rhino/js.jar:funcunit/java/selenium-java-client-driver.jar org.mozilla.javascript.tools.shell.Main 9 | exit 127 10 | fi 11 | CP=funcunit/java/selenium-java-client-driver.jar:steal/rhino/js.jar 12 | 13 | export ERRORLEV=0 14 | if [ $1 = "-e" ] 15 | then 16 | ERRORLEV=1 17 | shift 18 | fi 19 | 20 | if [ $1 = "-h" -o $1 = "-?" -o $1 = "--help" ] 21 | then 22 | echo Load a command line Rhino JavaScript environment or run JavaScript script files in Rhino. 23 | echo Available commands: 24 | echo -e "./js\t\t\t\tOpens a command line JavaScript environment" 25 | echo -e "./js -d\t\t\t\tOpens the Rhino debugger" 26 | echo -e "./js [FILE]\t\t\tRuns FILE in the Rhino environment" 27 | echo -e "" 28 | echo -e "JavaScriptMVC script usage:" 29 | echo -e "./js steal/generate/app [NAME]\t\tCreates a new JavaScriptMVC application" 30 | echo -e "./js steal/generate/page [APP] [PAGE]\tGenerates a page for the application" 31 | echo -e "./js steal/generate/controller [NAME]\tGenerates a Controller file" 32 | echo -e "./js steal/generate/model [TYPE] [NAME]\tGenerates a Model file" 33 | echo -e "./js apps/[NAME]/compress.js\t\tCompress your application and generate documentation" 34 | exit 127 35 | fi 36 | 37 | if [ $1 = "-d" ] 38 | then 39 | java -classpath steal/rhino/js.jar:steal/rhino/selenium-java-client-driver.jar org.mozilla.javascript.tools.debugger.Main 40 | exit 127 41 | fi 42 | 43 | ARGS=[ 44 | for arg 45 | do 46 | if [ $arg != $1 ] 47 | then 48 | ARGS=$ARGS"'$arg'", 49 | fi 50 | done 51 | ARGS=$ARGS] 52 | java -Xmx512m -Xss1024k -cp $CP org.mozilla.javascript.tools.shell.Main -e _args=$ARGS -opt -1 -e 'load('"'"$1"'"')' 53 | 54 | if [ $ERRORLEV = "1" -a $? = "1" ] 55 | then 56 | 57 | exit 1 58 | fi 59 | -------------------------------------------------------------------------------- /js.bat: -------------------------------------------------------------------------------- 1 | :: This script checks for arguments, if they don't exist it opens the Rhino dialog 2 | :: if arguments do exist, it loads the script in the first argument and passes the other arguments to the script 3 | :: ie: js jmvc\script\controller Todo 4 | @echo off 5 | SETLOCAL ENABLEDELAYEDEXPANSION 6 | if "%1"=="" ( 7 | java -cp steal\rhino\js.jar org.mozilla.javascript.tools.shell.Main 8 | GOTO END 9 | ) 10 | if "%1"=="-h" GOTO PRINT_HELP 11 | if "%1"=="-?" GOTO PRINT_HELP 12 | if "%1"=="--help" GOTO PRINT_HELP 13 | 14 | if "%1"=="-d" ( 15 | java -classpath funcunit/java/selenium-java-client-driver.jar;steal/rhino/js.jar org.mozilla.javascript.tools.debugger.Main 16 | GOTO END 17 | ) 18 | SET CP=funcunit/java/selenium-java-client-driver.jar;steal\rhino\js.jar 19 | SET ERRORLEV=0 20 | if "%1"=="-e" ( 21 | SET ERRORLEV=1 22 | SHIFT /1 23 | ) 24 | SET ARGS=[ 25 | SET FILENAME=%1 26 | SET FILENAME=%FILENAME:\=/% 27 | ::haven't seen any way to loop through all args yet, so for now this goes through arg 2-7 28 | ::dos sucks and for some reason this structure doesn't respect the shift, so we branch 29 | if "%ERRORLEV%"=="1" ( 30 | for /f "tokens=3,4,5,6,7,8 delims= " %%a in ("%*") do SET ARGS=!ARGS!'%%a','%%b','%%c','%%d','%%e','%%f' 31 | ) ELSE ( 32 | for /f "tokens=2,3,4,5,6,7 delims= " %%a in ("%*") do SET ARGS=!ARGS!'%%a','%%b','%%c','%%d','%%e','%%f' 33 | ) 34 | ::remove the empty args 35 | :: for %%a in (",''=") do ( call set ARGS=%%ARGS:%%~a%% ) 36 | SET ARGS=%ARGS:,''=% 37 | ::remove the spaces 38 | :: for /f "tokens=1*" %%A in ("%ARGS%") do SET ARGS=%%A 39 | SET ARGS=%ARGS: =% 40 | SET ARGS=%ARGS%] 41 | set ARGS=%ARGS:\=/% 42 | java -Xmx512m -Xss1024k -cp %CP% org.mozilla.javascript.tools.shell.Main -opt -1 -e _args=%ARGS% -e load('%FILENAME%') 43 | 44 | if "%ERRORLEV%"=="1" ( 45 | if errorlevel 1 exit 1 46 | ) 47 | 48 | GOTO END 49 | 50 | :PRINT_HELP 51 | echo Load a command line Rhino JavaScript environment or run JavaScript script files in Rhino. 52 | echo Available commands: 53 | echo js Opens a command line JavaScript environment 54 | echo js -d Opens the Rhino debugger 55 | echo js -selenium Starts selenium server 56 | echo js [FILE] Runs FILE in the Rhino environment 57 | 58 | echo JavaScriptMVC script usage: 59 | echo js steal/generate/app [NAME] Creates a new JavaScriptMVC application 60 | echo js steal/generate/page [APP] [PAGE] Generates a page for the application 61 | echo js steal/generate/controller [NAME] Generates a Controller file 62 | echo js steal/generate/model [TYPE] [NAME] Generates a Model file 63 | echo js apps/[NAME]/compress.js Compress your application and generate documentation 64 | 65 | :END 66 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Copyright (C) 2012 by [Bitovi](http://bitovi.com) 4 | 5 | The MIT license: 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JavaScriptMVC", 3 | "description": "A full stack JavaScript application framework containing CanJS, StealJS, FuncUnit, jQuery++, and DocumentJS.", 4 | "version": "3.3.0pre", 5 | "author": { 6 | "name": "Bitovi", 7 | "email": "contact@bitovi.com", 8 | "web": "http://bitovi.com/" 9 | }, 10 | "devDependencies": { 11 | "grunt": "0.4.0", 12 | "grunt-closure-tools": "0.7.7", 13 | "http-server": "0.5.1", 14 | "docco": "0.6.2", 15 | "js-beautify": "1.2.0", 16 | "github": ">= 0.1.7", 17 | "commander": "*", 18 | "tafa-misc-util": "*", 19 | "lodash" : "0.1.0", 20 | "grunt-contrib-connect": "0.1.2", 21 | "ejs": "0.8.3" 22 | }, 23 | "homepage": "http://javascriptmvc.com/", 24 | "repository": { 25 | "type": "git", 26 | "url": "git@github.com:jupiterjs/javascriptmvc.git" 27 | }, 28 | "licenses": [ 29 | { 30 | "type": "MIT", 31 | "url": "http://opensource.org/licenses/mit-license.php" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | @page javascriptmvc JavaScriptMVC 2 | 3 |
4 |
5 |
6 |
7 |
8 |
9 |

JavaScriptMVC Documentation

10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 | JavaScriptMVC (JMVC) is a MIT licensed, client-side, JavaScript framework that 18 | builds maintainable, error-free, lightweight 19 | applications as quick as possible. It packs best-of-breed 20 | libraries and tools that are guaranteed to work together. It 21 | supports every browser that jQuery supports. 22 | 23 | If you are new to the framework, this page followed by 24 | the [tutorials] is the best place to start. 25 | 26 | JMVC's goodies are broken down into four sub-projects: 27 | 28 | - [canjs CanJS] - A client side MVC Framework 29 | - [jquerypp jQuery++] - A collection of useful DOM helpers and special events for jQuery 30 | - [stealjs StealJS] - JavaScript and CSS dependency management and build tools 31 | - [FuncUnit FuncUnit] - Functional and unit testing framework 32 | - [DocumentJS DocumentJS] - Documentation engine 33 | 34 | The remainder of this page highlights each sub-project. Click 35 | the the project links on the left for a more in-depth overview 36 | of the sub-project. 37 | 38 | ## CanJS 39 | 40 | [canjs CanJS] is a JavaScript framework that makes 41 | building rich web applications easy and the MVC of 42 | JavaScriptMVC. The library is extremely lightweight 43 | (at only 11k minified and compressed) and full featured. 44 | 45 | Everything you want and need to know about CanJS 46 | [http://canjs.us can be found here.] 47 | 48 | ## jQuery++ 49 | 50 | [jquerypp jQuery++] is a collection of useful jQuery libraries that provide the 51 | missing functionality necessary to implement and organize large-scale 52 | jQuery applications. It provides low-level utilities for things that 53 | jQuery doesn’t support. 54 | 55 | The best way to get started is the [jQuery++ overview](http://jquerypp.com). 56 | 57 | ## StealJS 58 | 59 | [stealjs StealJS] is a "code manager" that keeps code beautiful and organized 60 | while developing and FAST for users in production. It's a collection of 61 | command-line and browser-based utilities enabling you to: 62 | 63 | - [steal load] JS, CSS, LESS, and CoffeeScript files and build them into a single production file. 64 | - [steal.generate generate] an application file/folder structure, complete with test, build and documentation scripts. 65 | - [steal.clean clean and JSLint] your code. 66 | - make your Ajax app [steal.html crawlable]. 67 | - log [steal.dev messages] in development that get removed in production builds. 68 | 69 | [stealjs StealJS] is a stand-alone tool that can be used without the rest of JavaScriptMVC. 70 | 71 | ## FuncUnit 72 | 73 | [FuncUnit FuncUnit] is a web application testing framework that provides automated unit and 74 | functional testing. Tests are written and debugged in the browser with 75 | FuncUnit's short, terse, jQuery-like API. The same tests can be instantly 76 | automated, run by Envjs or Selenium. 77 | 78 | FuncUnit also supports extremely accurate [Syn event simulation] on practically every browser and 79 | system. 80 | 81 | ## DocumentJS 82 | 83 | [DocumentJS DocumentJS] provides powerful JavaScript documenting 84 | capabilities. This whole website is built with it! DocumentJS can document practically 85 | anything. It's extensible. And with Markdown support, it's easy to document your code. 86 | -------------------------------------------------------------------------------- /scripts/getjmvc: -------------------------------------------------------------------------------- 1 | # 2 | # Use this script to install JMVC from github or your own fork. If its already installed, 3 | # it will get latest for all the submodules. Assumes your project uses git. 4 | # 5 | # Options: 6 | # -u username (default is bitovi) 7 | # -b branch (default is master) 8 | # -s source url (default is https://github.com) 9 | # -p install path (default is current directory) 10 | # 11 | # Usage: 12 | # Default usage. This will install from https://bitovi@github.com/bitovi/steal.git 13 | # ./getjmvc 14 | # 15 | # Use your own forked submodule repositories. This will install from https://github.com/mycompany/steal.git 16 | # ./getjmvc -u mycompany 17 | # 18 | # Install to your own path. You can specify the path where the submodules are installed, like to public/ 19 | # ./getjmvc -p public/ 20 | # 21 | # Install a different branch (used to install the 2.0 branches for steal, canjs, jquerypp, and funcunit). 22 | # ./getjmvc -b 2.0 23 | # 24 | # Install from a different repository (not github) or from ssh instead of http (if you have write access). 25 | # ./getjmvc -s git@github.com 26 | # ./getjmvc -s http://mygitrepo.com 27 | # 28 | # Update code. If you installed somewhere besides the current directory, specify a -p. This will update code in each submodule. 29 | # ./getjmvc 30 | # ./getjmvc -p public/ 31 | 32 | 33 | USERNAME=bitovi 34 | BRANCH=master 35 | SRC=https://github.com 36 | INSTALLPATH= 37 | 38 | while getopts ":u:b:s:p:" opt; do 39 | case $opt in 40 | u) 41 | USERNAME=$OPTARG 42 | ;; 43 | b) 44 | BRANCH=$OPTARG 45 | ;; 46 | s) 47 | SRC=$OPTARG 48 | ;; 49 | p) 50 | INSTALLPATH=$OPTARG 51 | ;; 52 | \?) 53 | echo "Invalid option: -$OPTARG" >&2 54 | exit 1 55 | ;; 56 | esac 57 | done 58 | 59 | # if steal/steal.js exists, refresh everything 60 | if [ -f ${INSTALLPATH}steal/steal.js ] 61 | then 62 | cd ${INSTALLPATH}steal 63 | git pull origin master 64 | cd ../jquerypp 65 | git pull origin master 66 | cd ../documentjs 67 | git pull origin master 68 | cd ../funcunit 69 | git pull origin master 70 | cd syn 71 | git pull origin master 72 | exit 127 73 | fi 74 | 75 | # if its http, should look like this: https://github.com/bitovi/canjs.git 76 | if [[ $SRC =~ :// ]]; then 77 | FULLSRC=$SRC/$USERNAME 78 | # else it should look like this: git@github.com:bitovi/canjs.git 79 | else 80 | FULLSRC=$SRC:$USERNAME 81 | fi 82 | 83 | git submodule add $FULLSRC/legacy-steal.git ${INSTALLPATH}steal 84 | git submodule add $FULLSRC/canjs.git ${INSTALLPATH}can 85 | git submodule add $FULLSRC/jquerypp.git ${INSTALLPATH}jquerypp 86 | git submodule add $FULLSRC/documentjs.git ${INSTALLPATH}documentjs 87 | git submodule add $FULLSRC/legacy-funcunit.git ${INSTALLPATH}funcunit 88 | git submodule add $FULLSRC/jmvc-generators.git ${INSTALLPATH}jmvc 89 | git submodule init 90 | git submodule update 91 | cd ${INSTALLPATH}steal 92 | git checkout $BRANCH 93 | cd ../jmvc 94 | git checkout $BRANCH 95 | cd ../can 96 | git checkout $BRANCH 97 | cd ../jquerypp 98 | git checkout $BRANCH 99 | cd ../documentjs 100 | git checkout legacy 101 | cd ../funcunit 102 | git checkout $BRANCH 103 | git submodule init 104 | git submodule update 105 | cd syn 106 | git checkout $BRANCH 107 | cd ../../ 108 | ./steal/js steal/make.js 109 | chmod 777 js 110 | -------------------------------------------------------------------------------- /scripts/getjmvc.bat: -------------------------------------------------------------------------------- 1 | :: Use this script to install JMVC from github or your own fork. If its already installed, 2 | :: it will get latest for all the submodules. Assumes your project uses git. 3 | :: 4 | :: Options: 5 | :: -u username (default is bitovi) 6 | :: -b branch (default is master) 7 | :: -s source url (default is https://github.com) 8 | :: -p install path (default is current directory) 9 | :: 10 | :: Usage: 11 | :: Default usage. This will install from https://bitovi@github.com/bitovi/steal.git 12 | :: ./getjmvc 13 | :: 14 | :: Use your own forked submodule repositories. This will install from 15 | :: https://github.com/bitovi/steal.git 16 | :: ./getjmvc -u cengage 17 | :: 18 | :: Install to your own path. You can specify the path where the submodules are installed, 19 | :: like to public/ 20 | :: ./getjmvc -p public/ 21 | :: 22 | :: Install a different branch (used to install the 2.0 branches for steal, canjs, jquerypp, and funcunit). 23 | :: ./getjmvc -b 2.0 24 | :: 25 | :: Install from a different repository (not github) or from ssh instead of http (if you have 26 | :: write access). 27 | :: ./getjmvc -s git@github.com 28 | :: ./getjmvc -s http://mygitrepo.com 29 | :: 30 | :: Update code. If you installed somewhere besides the current directory, specify a -p. 31 | :: ./getjmvc 32 | :: ./getjmvc -p public/ 33 | 34 | @echo off 35 | set USERNAME=bitovi 36 | set BRANCH=master 37 | set SRC=https://github.com 38 | set INSTALLPATH=./ 39 | 40 | :GETOPTS 41 | if /I "%1"=="-u" ( 42 | set USERNAME=%2 43 | shift 44 | shift 45 | goto :GETOPTS 46 | ) 47 | if /I "%1"=="-b" ( 48 | set BRANCH=%2 49 | shift 50 | shift 51 | goto :GETOPTS 52 | ) 53 | if /I "%1"=="-s" ( 54 | set SRC=%2 55 | shift 56 | shift 57 | goto :GETOPTS 58 | ) 59 | if /I "%1"=="-p" ( 60 | set INSTALLPATH=%2 61 | shift 62 | shift 63 | goto :GETOPTS 64 | ) 65 | 66 | set USERNAME=%USERNAME: =% 67 | set BRANCH=%BRANCH: =% 68 | set SRC=%SRC: =% 69 | set INSTALLPATH=%INSTALLPATH: =% 70 | 71 | :: if steal/steal.js exists, refresh everything 72 | IF EXIST %INSTALLPATH%steal/steal.js GOTO UPDATE 73 | 74 | 75 | :: CAN'T DO REGEX IN DOS, SO USE THIS HACK INSTEAD 76 | set WITHOUTHTTP=%SRC:http=% 77 | 78 | if "%WITHOUTHTTP%"=="%SRC%" ( 79 | set FULLSRC=%SRC%:%USERNAME% 80 | GOTO :INSTALL 81 | ) 82 | set FULLSRC=%SRC%/%USERNAME% 83 | 84 | :INSTALL 85 | call git submodule add %FULLSRC%/legacy-steal.git %INSTALLPATH%steal 86 | call git submodule add %FULLSRC%/canjs.git %INSTALLPATH%canjs 87 | call git submodule add %FULLSRC%/jquerypp.git %INSTALLPATH%jquerypp 88 | call git submodule add %FULLSRC%/documentjs.git %INSTALLPATH%documentjs 89 | call git submodule add %FULLSRC%/legacy-funcunit.git %INSTALLPATH%funcunit 90 | call git submodule init 91 | call git submodule update 92 | cd %INSTALLPATH%steal 93 | call git checkout %BRANCH% 94 | cd ..\jquery 95 | call git checkout %BRANCH% 96 | cd ..\documentjs 97 | call git checkout legacy 98 | cd ..\funcunit 99 | call git checkout %BRANCH% 100 | call git submodule init 101 | call git submodule update 102 | cd syn 103 | call git checkout %BRANCH% 104 | cd ..\..\ 105 | call steal\js steal/make.js 106 | goto :END 107 | 108 | :UPDATE 109 | cd %INSTALLPATH%steal 110 | call git pull origin master 111 | cd ..\jquery 112 | call git pull origin master 113 | cd ..\documentjs 114 | call git pull origin master 115 | cd ..\funcunit 116 | call git pull origin master 117 | cd syn 118 | call git pull origin master 119 | cd ..\..\ 120 | goto :END 121 | 122 | :END 123 | -------------------------------------------------------------------------------- /site/blog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JavaScriptMVC Blog 5 | 6 | 7 | 8 | 9 |
10 | 17 | 27 |
28 |
29 |

30 | JavaScriptMVC Blog 31 | Rss 32 |

33 |
    34 |
35 |
36 | 43 |
44 |
45 | 69 |
70 |
71 | © Bitovi - JavaScript Training and Support 72 |
73 | 74 | 75 | 76 | 80 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /site/builder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JavaScriptMVC Download 5 | 6 | 7 | 8 | 9 |
10 | 17 | 27 |
28 |
29 |

Download JavascriptMVC

30 |

31 | Download JavaScriptMVC3.2.1 Production 32 | The integrated framework. Everything you need to sling mad $(). 33 |

34 |
35 |

Or download only what you need

36 |

37 | Download FuncUnit3.2.1 Production 38 | Web Application Testing. Learn more. 39 |

40 |
41 |

42 | Download StealJS3.2.1 Production 43 | Manage dependencies, build fast pages, etc. Learn more. 44 |

45 |
46 |

47 | Download DocumentJS3.2.1 Production 48 | Powerfully easy documentation engine. Learn more. 49 |

50 |
51 |

52 | Download Syn1.0 Production 53 | Syn is used to simulate user actions. It creates synthetic events and performs their default behaviors. Learn more. 54 |

55 |
56 |
57 | 58 |
jQuery MVC Extensions. Learn More
59 |
60 |
61 |
62 |
63 | 70 |
71 |
72 | 96 |
97 |
98 | © Bitovi - JavaScript Training and Support 99 |
100 | 101 | 102 | 103 | 107 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /site/code.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JavaScriptMVC Code 5 | 6 | 7 | 8 | 9 |
10 | 17 | 27 |
28 |
29 |

Get JavaScriptMVC

30 |

There are two ways to get JavaScriptMVC:

31 |
    32 |
  1. Download it
  2. 33 |
  3. Git it from GitHub
  4. 34 |
35 |

We prefer you use Git. It's a great way to share changes with us and the community.

36 |

Installing from Github

37 |

Download the JavaScriptMVC Install script for Mac 38 | or Windows.

39 | 40 |

Put this script where you want to install JavaScriptMVC's submodules (Steal, jQueryMX, FuncUnit, and DocumentJS).

41 | 42 |

Type getjmvc in a command promp (or ./getjmvc for Mac users) and it will install each submodule. This script 43 | assumes your project is using a git repository.

44 | 45 |

Run it again later to update each submodule. Script options and more details can be found here.

46 | 47 |

If you just want a certain repo, or you like doing things the hard way, here's how to get each project yourself:

48 |
    49 |
  • 50 | JavaScriptMVC 51 | git clone http://github.com/jupiterjs/javascriptmvc.git
    52 | git submodule init
    53 | git submodule update
    54 | cd into each submodule and do git checkout master
    55 |
  • 56 |
  • 57 | Steal 58 | git clone https://github.com/jupiterjs/steal.git 59 |
  • 60 |
  • 61 | jQueryMX 62 | git clone https://github.com/jupiterjs/jquerymx.git jquery 63 |
  • 64 |
  • 65 | Syn 66 | git clone https://github.com/jupiterjs/syn.git 67 |
  • 68 |
  • 69 | FuncUnit 70 | git clone https://github.com/jupiterjs/funcunit.git
    71 | git submodule init
    72 | git submodule update
    73 | cd syn
    74 | git checkout master
    75 |
  • 76 |
  • 77 | DocumentJS 78 | git clone https://github.com/jupiterjs/documentjs.git 79 |
  • 80 |
81 |

For more information on how to develop with Git check out Developing with Git.

82 |
83 | 90 |
91 |
92 | 116 |
117 |
118 | © Bitovi - JavaScript Training and Support 119 |
120 | 121 | 122 | 123 | 127 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /site/contribute.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JavaScriptMVC Contribute 5 | 6 | 7 | 8 | 9 |
10 | 17 | 27 |
28 |
29 |

Contributing Code

30 |

The best way to contribute code is to develop with git and make a pull requests 31 | from your fork.

32 |

If you want to make contributions to the framework as a whole, read Developing JavaScriptMVC.

33 |

Ideas, Feedback, Problems

34 |

Know how to make JavaScriptMVC better or you don't like something? 35 | Let us know in one of JavascriptMVC's Forums.

36 |

Submitting Bugs

37 |

Bugs can be submitted to:

38 | 44 |

Hard Core Contributors

45 |

Grab a beer with the JavaScriptMVC team:

46 |

Jedi

47 |
    48 |
  • 49 | Justin 50 |

    Justin Meyer

    51 |

    Justin is a founder, and lead developer of JavaScriptMVC. During the day, he is the CEO of Jupiter Consulting, a JavaScript consulting company. When not working on JavaScriptMVC, he's cheating with his other favorite framework - Ruby on Rails.

    52 |
    53 |
  • 54 |
  • 55 | Brian 56 |

    Brian Moschel

    57 |

    Brian is a founder of JavaScriptMVC and Jupiter Consulting in Chicago. He wrote the original version of JavaScriptMVC and does writing for the site. He also has a dog named Ajax. Attempts to teach him programming have been fruitless thus far.

    58 |
    59 |
  • 60 |
  • 61 | Michael 62 |

    Michael Mayer

    63 |

    Michael loves to bring order to the chaos of Web development. He is specialized in keeping deadlines and puts his life at risk to successfully complete projects. His home base is in Berlin, Germany.

    64 |
    65 |
  • 66 |
67 |

Padawan

68 |
    69 |
  • 70 | Matt 71 |

    M@ McCray

    72 |

    A sith apprentice who corrected jQuery.Model.save and used the dark side to fix Controller::form_params.

    73 |
    74 |
  • 75 |
  • 76 | Joe 77 |

    Joe Tortuga

    78 |

    Fixed problems with nested views.

    79 |
    80 |
  • 81 |
  • 82 | Max 83 |

    Max C.H. Hwang

    84 |

    Helped views correctly evaluate Ajax responses.

    85 |
    86 |
  • 87 |
88 |

Blue Ghost Jedi

89 |
    90 |
  • 91 | Trey 92 |

    Trey Kennedy

    93 |

    Trey Kennedy has been an embedded C/C++ software engineer for over 10 years and a part-time web applications developer for the last 3 years. He is sick and tired of generating HTML and JavaScript using other languages and is ready to evangelize the world in the way of native JavaScript development - or maybe just his company.

    94 |
    95 |
  • 96 |
  • 97 | Lee 98 |

    Lee Henson

    99 |

    Located at the secret Music Glue skunk works in London Town, plotting the downfall of the music industry fat cats (and sharpening his ice pick).

    100 |
    101 |
  • 102 |
103 |
104 | 111 |
112 |
113 | 137 |
138 |
139 | © Bitovi - JavaScript Training and Support 140 |
141 | 142 | 143 | 144 | 148 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /site/docs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DoneJS 5 | 6 | 7 | 21 | 22 | 23 |
24 |
25 |
26 |
27 | 28 |
29 |
30 | 41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 | 51 |
52 |
53 | 54 |
55 |
56 |
57 |
58 |
59 |
60 | © Bitovi - JavaScript Training and Support 61 | 62 |
63 | 78 | 81 | 82 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /site/docs.js: -------------------------------------------------------------------------------- 1 | steal('can', 2 | 'funcunit', 3 | 'documentjs', 4 | 'jquery/build/lib.js') 5 | 6 | steal('steal','steal/generate', 7 | 'steal/build', 8 | 'steal/build/pluginify', 9 | 'steal/coffee', 10 | 'steal/less', 11 | 'steal/clean', 12 | 'steal/parse', 13 | 'steal/html/crawl') 14 | 15 | steal('can/construct', 16 | 'can/construct/proxy', 17 | 'can/control', 18 | 'can/control/route', 19 | 'can/control/plugin', 20 | 'can/model', 21 | 'can/observe', 22 | 'can/observe/attributes', 23 | 'can/observe/backup', 24 | 'can/observe/delegate', 25 | 'can/observe/validations', 26 | 'can/route', 27 | 'can/view/ejs', 28 | 'can/util/fixture', 29 | 'can/view/modifiers', 30 | 'can/view/mustache') 31 | 32 | steal('can/util/func.js') 33 | 34 | // .then('canui') 35 | -------------------------------------------------------------------------------- /site/follow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JavaScriptMVC Community 5 | 6 | 7 | 8 | 9 |
10 | 17 | 27 |
28 |
29 |

Follow JavaScriptMVC

30 |

Twitter

31 |

Follow @javascriptmvc on Twitter for daily useful tips.

32 |

Blog

33 |

Read JavaScriptMVC's Blog for articles, techniques and ideas on maintainable JavaScript

34 |

Community page

35 |

Visit our community page to find links, articles, plugins and examples shared by our community

36 |

Forum

37 |

Discuss ideas to make the framework better or problems you are having on JavaScriptMVC's Forum.

38 |
39 | 46 |
47 |
48 | 72 |
73 |
74 | © Bitovi - JavaScript Training and Support 75 |
76 | 77 | 78 | 79 | 83 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /site/fonts/museo_slab_500-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/fonts/museo_slab_500-webfont.eot -------------------------------------------------------------------------------- /site/fonts/museo_slab_500-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/fonts/museo_slab_500-webfont.ttf -------------------------------------------------------------------------------- /site/fonts/museo_slab_500-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/fonts/museo_slab_500-webfont.woff -------------------------------------------------------------------------------- /site/fonts/museoslab.css: -------------------------------------------------------------------------------- 1 | /* Generated by Font Squirrel (http://www.fontsquirrel.com) on January 4, 2011 */ 2 | 3 | 4 | 5 | @font-face { 6 | font-family: 'MuseoSlab500'; 7 | src: url('museo_slab_500-webfont.eot'); 8 | src: local('☺'), url('museo_slab_500-webfont.woff') format('woff'), url('museo_slab_500-webfont.ttf') format('truetype'), url('museo_slab_500-webfont.svg#webfonth7ZuKjlS') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /site/images/add_tag_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/add_tag_example.png -------------------------------------------------------------------------------- /site/images/backgrounds/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/body.png -------------------------------------------------------------------------------- /site/images/backgrounds/bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/bottom.png -------------------------------------------------------------------------------- /site/images/backgrounds/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/calendar.png -------------------------------------------------------------------------------- /site/images/backgrounds/download/documentjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/download/documentjs.png -------------------------------------------------------------------------------- /site/images/backgrounds/download/funcunit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/download/funcunit.png -------------------------------------------------------------------------------- /site/images/backgrounds/download/javascriptmvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/download/javascriptmvc.png -------------------------------------------------------------------------------- /site/images/backgrounds/download/jquerymx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/download/jquerymx.png -------------------------------------------------------------------------------- /site/images/backgrounds/download/stealjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/download/stealjs.png -------------------------------------------------------------------------------- /site/images/backgrounds/download/syn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/download/syn.png -------------------------------------------------------------------------------- /site/images/backgrounds/greenbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/greenbox.png -------------------------------------------------------------------------------- /site/images/backgrounds/link_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/link_hover.png -------------------------------------------------------------------------------- /site/images/backgrounds/navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/navigation.png -------------------------------------------------------------------------------- /site/images/backgrounds/quote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/backgrounds/quote.png -------------------------------------------------------------------------------- /site/images/coverage_commandline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/coverage_commandline.png -------------------------------------------------------------------------------- /site/images/coverage_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/coverage_file.png -------------------------------------------------------------------------------- /site/images/coverage_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/coverage_report.png -------------------------------------------------------------------------------- /site/images/crm_doc_demo_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/crm_doc_demo_1.png -------------------------------------------------------------------------------- /site/images/crm_doc_demo_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/crm_doc_demo_2.png -------------------------------------------------------------------------------- /site/images/follow_twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/follow_twitter.png -------------------------------------------------------------------------------- /site/images/fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/fork.png -------------------------------------------------------------------------------- /site/images/funcunit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/funcunit.png -------------------------------------------------------------------------------- /site/images/funcunit_medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/funcunit_medium.png -------------------------------------------------------------------------------- /site/images/funcunit_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/funcunit_small.png -------------------------------------------------------------------------------- /site/images/funcunitfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/funcunitfolder.png -------------------------------------------------------------------------------- /site/images/funcunithtml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/funcunithtml.png -------------------------------------------------------------------------------- /site/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/header.png -------------------------------------------------------------------------------- /site/images/iepopups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/iepopups.png -------------------------------------------------------------------------------- /site/images/iesecurity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/iesecurity.png -------------------------------------------------------------------------------- /site/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/logo.png -------------------------------------------------------------------------------- /site/images/page_type_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/page_type_example.png -------------------------------------------------------------------------------- /site/images/parent_tag_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/parent_tag_example.png -------------------------------------------------------------------------------- /site/images/phui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/phui.png -------------------------------------------------------------------------------- /site/images/plugin_tag_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/plugin_tag_example.png -------------------------------------------------------------------------------- /site/images/return_tag_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/return_tag_example.png -------------------------------------------------------------------------------- /site/images/rss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/rss.png -------------------------------------------------------------------------------- /site/images/tag_tag_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/tag_tag_example.png -------------------------------------------------------------------------------- /site/images/team/brian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/team/brian.png -------------------------------------------------------------------------------- /site/images/team/joe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/team/joe.png -------------------------------------------------------------------------------- /site/images/team/justin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/team/justin.png -------------------------------------------------------------------------------- /site/images/team/lee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/team/lee.png -------------------------------------------------------------------------------- /site/images/team/matt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/team/matt.png -------------------------------------------------------------------------------- /site/images/team/max.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/team/max.png -------------------------------------------------------------------------------- /site/images/team/michael.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/team/michael.png -------------------------------------------------------------------------------- /site/images/team/trey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/team/trey.png -------------------------------------------------------------------------------- /site/images/test_cookbook_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/test_cookbook_example.png -------------------------------------------------------------------------------- /site/images/test_tag_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/test_tag_example.png -------------------------------------------------------------------------------- /site/images/test_tag_test_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/site/images/test_tag_test_example.png -------------------------------------------------------------------------------- /site/pages/developingjmvc.md: -------------------------------------------------------------------------------- 1 | @page developingjmvc Developing JavaScriptMVC 2 | 3 | Awesome, you want to contribute back some code to JavaScriptMVC, or build your own 4 | download. This article 5 | explains how. 6 | 7 | JavaScriptMVC is comprised of several projects, each with 8 | it's own repository: 9 | 10 | - [http://github.com/bitovi/steal] 11 | - [http://github.com/bitovi/canjs] 12 | - [http://github.com/bitovi/jquerypp] 13 | - [http://github.com/bitovi/documentjs] 14 | - [http://github.com/bitovi/funcunit] 15 | 16 | These are collected in the javascriptmvc repository: 17 | 18 | - [http://github.com/bitovi/javascriptmvc] 19 | 20 | Read how to get, test, and build each project in JavaScriptMVC: 21 | 22 | ## 1. Get 23 | 24 | In Github, fork the repo you want to make changes to. Then clone 25 | the javascriptmvc repo and install the submodules like: 26 | 27 | @codestart 28 | git clone git@github.com:bitovi/javascriptmvc 29 | @codeend 30 | 31 | Now, open the javascriptmvc folder's .gitmodule file and change the url of the submodule(s) 32 | you have forked. For example, you might change: 33 | 34 | @codestart 35 | url = git://github.com/bitovi/canjs.git 36 | @codeend 37 | to 38 | @codestart 39 | url = git://github.com/justinbmeyer/canjs.git 40 | @codeend 41 | 42 | Now run: 43 | 44 | 45 | @codestart 46 | cd javascriptmvc 47 | git submodule init 48 | git submodule update 49 | @codeend 50 | Finally, you might need cd into each submodule and 51 | run 52 | @codestart 53 | git checkout 54 | @codeend 55 | 56 | Now make your changes! 57 | 58 | ## 2. Test 59 | 60 | To test FuncUnit, Steal, CanJS, and jQuery++ combined open 61 | javascriptmvc/test.html in 62 | every supported browser and run: 63 | 64 | @codestart 65 | ./js test/run.js 66 | @codeend 67 | 68 | To test just the invidual projects, do the following: 69 | 70 | #### StealJS 71 | 72 | Open /steal/test/qunit.html 73 | in a browser. 74 | 75 | Run: 76 | 77 | @codestart 78 | ./js steal/test/run.js 79 | @codeend 80 | 81 | #### FuncUnit 82 | 83 | Open /funcunit/funcunit.html 84 | and /funcunit/qunit.html in every browser. 85 | 86 | Run: 87 | 88 | @codestart 89 | funcunit/envjs funcunit/funcunit.html 90 | @codeend 91 | 92 | #### CanJS 93 | 94 | Open /canjs/qunit.html 95 | in every browser. 96 | 97 | Run: 98 | 99 | @codestart 100 | ./js canjs/test/run.js 101 | @codeend 102 | 103 | #### jQuery++ 104 | 105 | Open /jquerypp/qunit.html 106 | in every browser. 107 | 108 | Run: 109 | 110 | @codestart 111 | ./js jquerypp/test/run.js 112 | @codeend 113 | 114 | 115 | ## 3. Build 116 | 117 | Coming soon, but most projects have a build.js and so does framework. 118 | 119 | ## 4. Building the Docs 120 | 121 | Run: 122 | @codestart 123 | js jmvc\scripts\doc.js 124 | @codeend 125 | 126 | If you have problems, you might need to create a jmvc/docs folder. 127 | Pages like this one are found in jmvc/pages/. 128 | 129 | ## 4. Deploying the Docs 130 | 131 | Run: 132 | @codestart 133 | ruby scripts\deploy.rb 134 | @codeend 135 | 136 | First you need to add our EC2 private key in the scripts folder, named key. If you want to 137 | deploy, talk to Brian to get access to this key. 138 | -------------------------------------------------------------------------------- /site/pages/developingwithgit.md: -------------------------------------------------------------------------------- 1 | @page developwithgit Developing With Git 2 | 3 | Thank you so much for developing with git. You're making 4 | the world better for yourself and others. With GIT we can see any change 5 | you make to JavaScriptMVC and share them with other people. 6 | 7 | Before we get started, we're assuming you: 8 | 9 | - know git 10 | - have a github account setup 11 | - have a project that is already using git 12 | 13 | If you don't, you might find the following resources helpful: 14 | 15 | - [http://git.or.cz/course/svn.html Git - SVN Crash Course] 16 | - [http://github.com/ Github] - create an account here 17 | - [http://help.github.com/msysgit-key-setup/ SSH Key Setup] 18 | 19 | ## Git-ing JavaScriptMVC 20 | 21 | JavaScriptMVC is comprised of 7 sub projects: 22 | 23 | - [http://github.com/bitovi/steal] 24 | - [http://github.com/bitovi/canjs] 25 | - [http://github.com/bitovi/jquerypp] 26 | - [http://github.com/bitovi/documentjs] 27 | - [http://github.com/bitovi/funcunit] 28 | - [http://github.com/bitovi/syn] 29 | - [https://github.com/bitovi/jmvc-generators] 30 | 31 | We're going to fork each of these projects and add them as submodules to your 32 | master git project. 33 | 34 | #### Forking 35 | 36 | Assuming you have a github account set up, and are signed in, 37 | click each of the github links and click 38 | the fork button (in the upper right of the page). 39 | 40 | @image site/images/fork.png 41 | 42 | > TIP: If you're working for a company, you should create company forks and give 43 | employees access to the company forks. This will keep everyone using the 44 | same version. 45 | 46 | #### Installing with a script 47 | 48 | To make the next several steps easier, we've made scripts for [https://github.com/bitovi/javascriptmvc/raw/master/scripts/getjmvc Mac] 49 | and [https://github.com/bitovi/javascriptmvc/raw/master/scripts/getjmvc.bat Windows] users that automate adding your repos and setting them up. 50 | 51 | _Note: For the Mac script, be sure to set permissions to run locally._ 52 | @codestart text 53 | chmod 755 getjmvc 54 | @codeend 55 | 56 | Use this script to install JMVC from github or your own fork. If its already installed, it will get latest for all the submodules. 57 | Assumes your project uses git. 58 | 59 | ##### Options 60 | 61 | - -u username (default is bitovi) 62 | - -b branch (default is master) 63 | - -s source url (default is https://github.com) 64 | - -p install path (default is current directory) 65 | 66 | ##### Usage 67 | 68 | Windows users can ignore the ./ in the path: 69 | 70 | Default usage. This will install from https://bitovi@github.com:bitovi/steal.git 71 | @codestart text 72 | ./getjmvc 73 | @codeend 74 | 75 | Use your own forked submodule repositories. This will install from https://github.com/mycompany/steal.git 76 | @codestart text 77 | ./getjmvc -u mycompany 78 | @codeend 79 | 80 | Install to your own path. You can specify the path where the submodules are installed, like to public/ 81 | @codestart text 82 | ./getjmvc -p public/ 83 | @codeend 84 | 85 | Install a different branch (used to install the 2.0 branches for steal, canjs, jquerypp, and funcunit). 86 | @codestart text 87 | ./getjmvc -b 2.0 88 | @codeend 89 | 90 | Install from a different repository (not github) or from ssh instead of http (if you have write access). 91 | @codestart text 92 | ./getjmvc -s git@github.com 93 | ./getjmvc -s http://mygitrepo.com 94 | @codeend 95 | 96 | Update code. If you installed somewhere besides the current directory, specify a -p. This will update code in each submodule. 97 | @codestart text 98 | ./getjmvc 99 | ./getjmvc -p public/ 100 | @codeend 101 | 102 | Note: This script installs steal, documentjs, canjs, jquerypp, and funcunit, and it downloads syn from whatever is in funcunit/.gitmodules. 103 | If you wish to fork your own syn and use that fork, you have to change your funcunit/.gitmodules to point to the right location manually. 104 | Check it in and future installs will work fine. 105 | 106 | If you just want a certain repo, or you like doing things the hard way, here's how to get each project yourself: 107 | 108 | #### Adding a submodule 109 | 110 | Now add the first four forked repositories as submodules 111 | to your project like: 112 | 113 | @codestart text 114 | git submodule add git@github.com:_YOU_/steal.git public/steal 115 | git submodule add git@github.com:_YOU_/canjs.git public/canjs 116 | git submodule add git@github.com:_YOU_/jquerypp.git public/jquerypp 117 | git submodule add git@github.com:_YOU_/documentjs.git public/documentjs 118 | git submodule add git@github.com:_YOU_/funcunit.git public/funcunit 119 | git submodule add git@github.com:_YOU_/jmvc-generators.git public/jmvc 120 | @codeend 121 | 122 | _Note_: Learn a little more about submodules [here](http://johnleach.co.uk/words/archives/2008/10/12/323/git-submodules-in-n-easy-steps Submodules). 123 | 124 | There are 3 important things to notice: 125 | 126 | 1. Change _YOU_ with your github username. 127 | 128 | 2. Add the submodules in a public folder, where the server hosts static content. 129 | 130 | 3. Copy the javascriptmvc repository into a jquery folder. 131 | 132 | Next, you have to install and update the submodules. Run: 133 | 134 | @codestart 135 | git submodule init 136 | git submodule update 137 | @codeend 138 | 139 | You may also have to change to each directory and checkout the master branch: 140 | 141 | @codestart 142 | cd steal 143 | git checkout master 144 | @codeend 145 | 146 | #### Installing Syn 147 | 148 | Syn is a submodule of the funcunit project. To add your fork to funcunit, 149 | first you have to change the submodule to point to your fork 150 | (because it points to the bitovi fork). To do this, open funcunit/.gitmodules. You'll see: 151 | 152 | @codestart text 153 | [submodule "syn"] 154 | path = syn 155 | url = git@github.com:bitovi/syn.git 156 | update = merge 157 | @codeend 158 | 159 | Change the URL to your own fork, like: 160 | 161 | @codestart text 162 | url = git@github.com:_YOU_/syn.git 163 | @codeend 164 | 165 | Now install syn, like the other submodules: 166 | 167 | @codestart 168 | cd funcunit 169 | git submodule init 170 | git submodule update 171 | cd syn 172 | git checkout master 173 | @codeend 174 | 175 | Finally, you just have to move the 'js' commands out of steal for convienence: 176 | 177 | @codestart text 178 | [WINDOWS] > steal\js steal\make.js 179 | 180 | [Lin/Mac] > ./steal/js steal/make.js 181 | @codeend 182 | 183 | Yes, that was more annoying then just downloading it, but you're making the 184 | world a better place for yourself and for others. 185 | 186 | -------------------------------------------------------------------------------- /site/pages/folders.md: -------------------------------------------------------------------------------- 1 | /** 2 | @page folders Folder and File Organization 3 | 4 | This hasn't been filled out yet. Ping us and we 5 | will. For now, this is a place holder. 6 | 7 | Here's what it should dicuss: 8 | 9 | - The app folder and file structure 10 | - The plugin file structure 11 | - a plugins folder 12 | - How apps and plugins should work together 13 | - Maybe sharing your plugins folder 14 | 15 | Here's some text from the old docs that 16 | might be useful. 17 | 18 |
19 |
cookbook.js 20 |
The application file, 21 | load plugins and other JavaScript files. 22 |
cookbook.html 23 |
A page that loads your application. 24 | 25 |
funcunit.html 26 |
A page that runs your functional tests. 27 | 28 |
qunit.html 29 |
A page that runs your qunit tests. 30 | 31 |
test/ 32 |
A folder for your qunit and funcunit tests. 33 | 34 |
docs/ 35 |
A folder for your documentation files. 36 | 37 |
scripts/ 38 |
Scripts to document and compress your application. 39 | 40 | 41 |
controllers/ 42 |
A folder for code that manages events. 43 |
models/ 44 |
A folder code that manages Ajax requests. 45 |
view/ 46 |
A folder for client side templates 47 |
resources/ 48 |
A folder for 3rd party plugins and scripts. 49 |
fixtures/ 50 |
A folder for simulated ajax responses (So you don't have to wait on the slow poke backenders). 51 | 52 |
53 | */ -------------------------------------------------------------------------------- /site/pages/help.md: -------------------------------------------------------------------------------- 1 | @page help Get Help 2 | 3 |

Getting Help

4 | There are a number of ways to get help: 5 |

Premium Support and Training

6 | 7 | 8 | 9 |

10 | 11 | [http://jupiterit.com Jupiter], the company that maintains JavaScriptMVC, 12 | provides premium [http://jupiterit.com/support.html support] and 13 | [http://jupiterit.com/training.html training] 14 | for JavaScriptMVC at affordable prices. This is the best way to rapidly progress on 15 | your project. 16 | 17 |

18 |

Google Group

19 | 20 | 21 | 22 | 23 | Ask questions on [http://groups.google.com/group/javascriptmvc JavaScriptMVC's Google Group]. 24 | 25 | 26 |

Search the Documentation

27 | 28 | JMVC's documentation is searchable. Type in the input on the top right. 29 | -------------------------------------------------------------------------------- /site/pages/learn.js: -------------------------------------------------------------------------------- 1 | /* 2 | @page learn 3. Learn 3 | 4 | 5 | JavaScriptMVC contains pretty much everything 6 | you need to develop, test, and maintain a 7 | JavaScript application. Instead of learning 8 | an API, learning JavaScriptMVC is more 9 | about learning HOW to build an application. 10 | 11 | ## The Basics 12 | 13 | 14 | Watch 15 | 2.0 Video 16 | Before you do anything, watch 17 | the 2.0 Video. 18 | It's a 12 min brain dump that will highlight most of JMVC's features. 19 | 20 |
21 | 22 | 23 | You might be asking yourself a frequently asked question: 24 | 25 | 26 | #### Who should use JMVC? 27 | 28 | JMVC is designed for large, single-page JavaScript applications 29 | that require lots of custom code (something like [http://gmail.com GMail]). 30 | It fits between low-level libraries like jQuery and widget libraries like 31 | jQueryUI. 32 | 33 | If you need to organize, test, maintain, or compress a JavaScript 34 | application, JavaScriptMVC will help. 35 | 36 | #### How does JMVC fit into my project? 37 | 38 | JMVC is based around the principles of Service Oriented Architecture (SOA) and 39 | Thin Server Architecture (TSA). This means your server 40 | produces raw (preferably REST) services and never sends data in HTML. 41 | 42 | Read a [http://blog.javascriptmvc.com/?p=68 1.5 article] how it looks 43 | from within a rails application: 44 | 45 | 46 | 47 | For information on the benefits of TSA, watch [http://www.youtube.com/watch?v=XMkIZZ7dBng Practical Thin Server Architecture]. 48 | 49 | #### Does JMVC work with a Java/PHP/Rails/etc backend? 50 | 51 | Yes, JMVC will will work with any backend service. It 52 | prefers to consume JSON Rest services, but it's flexible 53 | enough to work from 54 | anything. 55 | 56 | #### Do you have any example code? 57 | 58 | We are trying to get move public source available, but for now check out: 59 | 60 | - Srchr - demo app 61 | - MXUI - jQueryMX UI widgets. 62 | 63 | 64 | #### How does JMVC compare to other JS Frameworks? 65 | 66 | JMVC has the gamut of features to support the most complex JS applications. 67 | But it's most important feature, and its most unique, 68 | is its event delegation support organized 69 | via [jQuery.Controller controllers]. If you haven't used controllers to organize event handling in 70 | JavaScript, you haven't really programmed JavaScript. 71 | 72 | 73 | ## Model View Controller 74 | 75 | There are only 4 things you will ever do with JavaScript! JMVC breaks these down into the 76 | Model-View-Controller architecture. 77 | 78 | 79 | - Respond to events -> [jQuery.Controller Controller] 80 | - Get data and manipulate services (Ajax) -> [jQuery.Model Model] Static functions 81 | - Wrap service data with domain specific information -> [jQuery.Model Model] Prototype functions 82 | - Update the page -> [jQuery.Controller Controller] and [jQuery.View View] 83 | 84 | 85 | Here's how that flow looks: 86 | 87 | 88 | 89 | Think how this would work with the google auto-suggest. 90 | 91 | 92 | 93 | - Respond to typing "JavaScriptMVC" -> [jQuery.Controller Controller]. 94 | - Get search suggestions -> [jQuery.Model Model] Static functions. 95 | - Wrap search data -> [jQuery.Model Model] Prototype functions. Not really important here! 96 | - Draw suggestions -> [jQuery.Controller Controller] and [jQuery.Controller View]. 97 | 98 | 99 | ## Development Tools? 100 | 101 | JavaScriptMVC supplies a host of JS tools including: 102 | 103 | - [generators Code generators] 104 | - [steal Dependancy management] 105 | - [FuncUnit Testing] 106 | - [steal.build Compression] 107 | - [DocumentJS Documentation] 108 | 109 | ## How do I get help? 110 | 111 | Write on our [http://forum.javascriptmvc.com/ forum]. 112 | 113 | ## How do I report errors, or contribute code? 114 | 115 | Submit patches or errors in [https://github.com/jupiterjs github]. 116 | 117 | */ 118 | 119 | //break -------------------------------------------------------------------------------- /site/pages/why.js: -------------------------------------------------------------------------------- 1 | /* 2 | @page why 0. Why JavaScriptMVC 3 | 4 | 5 | So you've read through the list of features and you're still not 6 | convinced JavaScriptMVC is right for you. If you're looking for 7 | a little (extremely biased) advice, you've come to the 8 | right place. 9 | 10 | ## Who Should Use JavaScriptMVC? 11 | 12 | JavaScriptMVC is designed to enhance jQuery development 13 | for medium to large projects. You should care about 14 | code quality, performance, and maintainability. 15 | 16 | If you don't care about these things, or think jQuery is enough 17 | for any project, 18 | you don't know what you're doing, and you will 19 | embarrass the project by using it. Leave now. 20 | 21 | If you do care, here's how JavaScriptMVC helps you: 22 | 23 |

24 | JavaScriptMVC makes everything you should be doing, as easy as possible! 25 |

26 | 27 | Here's a few things you should be doing: 28 | 29 | - Testing (especially automatic and functional testing) 30 | - Documenting 31 | - Breaking up code into logically organized files 32 | - Compressing and concatenating your JavaScript files 33 | - Using and organizing client side templates 34 | - Making plugins that clean themselves up, are internally organized, and extendable. 35 | - Error reporting 36 | 37 | All of these things are hard or impossible to do right with jQuery alone. 38 | 39 | You can add your own automated testing library - 40 | QUnit isn't automated, it's difficult to write Selenium tests. 41 | 42 | You can add your own documentation engine - JSDoc, make sure you keep track of every file! 43 | 44 | You can add your own way of loading and compressing scripts - RequireJS. 45 | 46 | You can use other client side template libraries - jquery-tmpl, but you won't be able to compress them into your build or put them in external files as easily. 47 | 48 | You can be careful to structure your jQuery plugins so they can be easily removed from an element, remove all event handlers, and provide some mechanism for extending or overwriting your plugin. 49 | 50 | You can devise your own way of doing error reporting. 51 | 52 | ### OR ... 53 | 54 | You can download JavaScriptMVC and run: 55 | 56 | @codestart text 57 | js steal/generate/app APPNAME 58 | @codeend 59 | 60 | and get all of these things for free. 61 | 62 | JavaScriptMVC's greatest strength is it's integration. 63 | Everything you should be doing is available immediately. 64 | 65 | ## Ease of Adoption 66 | 67 | Despite the huge amount of features, JavaScriptMVC is 68 | easy to learn. 69 | 70 | Every component includes: 71 | 72 | - thorough documentation 73 | - demo examples 74 | - test pages 75 | - a write-up on JavaScriptMVC's blog. 76 | 77 | We are extremely active on the forums, with essentially 78 | zero unanswered questions. 79 | 80 | We've released a number of mini apps that are built the 81 | JavaScriptMVC way. 82 | 83 | Jupiter Consulting provides JavaScriptMVC training, support, 84 | and consulting services. 85 | 86 | 87 | 88 | */ 89 | 90 | 91 | //break -------------------------------------------------------------------------------- /site/scripts/build.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | site Build Page 6 | 7 | 8 |

site Build Page

9 |

This is a dummy page that loads your app so steal can 10 | get all the files. 11 |

12 |

If you built your app 13 | to depend on HTML in the page before DOMContent loaded or 14 | onload, you can add the HTML here, or you can change the 15 | build.js script to point to a better html file. 16 |

17 | 20 | 21 | -------------------------------------------------------------------------------- /site/scripts/build.js: -------------------------------------------------------------------------------- 1 | //steal/js jmvc/site/scripts/compress.js 2 | 3 | load("steal/rhino/rhino.js"); 4 | steal('steal/build','steal/build/scripts','steal/build/styles',function(){ 5 | steal.build('site/scripts/build.html',{to: 'site'}); 6 | }); 7 | -------------------------------------------------------------------------------- /site/scripts/clean.js: -------------------------------------------------------------------------------- 1 | //steal/js jmvc/site2/scripts/compress.js 2 | 3 | load("steal/rhino/rhino.js"); 4 | steal.plugins('steal/clean',function(){ 5 | steal.clean('jmvc/site2/site2.html',{indent_size: 1, indent_char: '\t'}); 6 | }); 7 | -------------------------------------------------------------------------------- /site/scripts/compress.js: -------------------------------------------------------------------------------- 1 | //js jmvc/compress.js 2 | 3 | load("steal/compress/compress.js") 4 | var compress = new steal.Compress(['jmvc/jmvc.html','jmvc']); -------------------------------------------------------------------------------- /site/scripts/crawl.js: -------------------------------------------------------------------------------- 1 | load('steal/rhino/rhino.js') 2 | 3 | steal('steal/html/crawl', function(){ 4 | steal.html.crawl("site/docs.html#!can.Control", 5 | { 6 | out: 'html', 7 | browser: 'phantomjs' 8 | }) 9 | }) -------------------------------------------------------------------------------- /site/scripts/doc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DoneJS 5 | 11 | 12 | 13 |

Welcome to JavaScriptMVC 3.3!

14 | 17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /site/scripts/doc.js: -------------------------------------------------------------------------------- 1 | load('steal/rhino/rhino.js'); 2 | 3 | steal("documentjs", function(DocumentJS){ 4 | DocumentJS('site/scripts/doc.html',{ 5 | markdown : [ 'readme.md', 'site', 'tutorials', 'steal', 'jquerypp', 'can', 'funcunit', 'jmvc'], 6 | out : 'docs', 7 | parent: 'javascriptmvc', 8 | "static": "site/static", 9 | "templates": "site/templates" 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /site/site.js: -------------------------------------------------------------------------------- 1 | steal.loadedProductionCSS = true; 2 | 3 | steal('steal/less') 4 | .then('site/stylesheets/site.less') 5 | .then('jquery/controller', 'jquery/view/ejs') 6 | .then('//site/views/blog.ejs', function(){ 7 | 8 | jQuery.Controller.extend('Feed', 9 | /* @Static */ 10 | { 11 | }, 12 | /* @Prototype */ 13 | { 14 | date_template: '{day}, {date} {month} {year}', 15 | days : ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], 16 | months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 17 | linkify : function(text){ 18 | var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; 19 | return text.replace(exp,"$1"); 20 | }, 21 | // gets passed strings like "Jan" 22 | getMonth: function(month_string){ 23 | for(var i=0; i

{tweet}

{date}
", 50 | init : function(){ 51 | var twitterUrl = 'http://twitter.com/status/user_timeline/javascriptmvc.json?count=30'; 52 | $.ajax({ 53 | url: twitterUrl, 54 | dataType: 'jsonp', 55 | success: this.proxy('insertTwitterFeed') 56 | }); 57 | }, 58 | insertTwitterFeed : function(data){ 59 | var tweets = []; 60 | for(var i = 0, ii = data.length; i < ii; i++){ //Filter out direct replies 61 | var tweet = data[i]; 62 | tweet.date = this.getDate(tweet.created_at) 63 | if(tweet.text.charAt(0) != '@' && tweets.length < 6){ 64 | var formattedDate = this.formatDate(tweet.date); 65 | tweets.push(can.sub(this.template, {tweet: this.linkify(tweet.text), date: formattedDate})); 66 | } 67 | } 68 | this.element.html(''); 69 | }, 70 | linkify : function(text){ 71 | var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; 72 | return text.replace(exp,"$1"); 73 | }, 74 | // receives a date like "Thu Jan 13 05:33:12 +0000 2011" 75 | getDate : function(date_string){ 76 | var dateMatch = date_string.match(/\w+\s+(\w+)\s+(\d+).*\s+(\d+)$/), 77 | date = new Date( 78 | parseInt(dateMatch[3], 10), 79 | this.getMonth(dateMatch[1]), 80 | parseInt(dateMatch[2], 10) 81 | ) 82 | return date; 83 | } 84 | }); 85 | 86 | Feed.extend('ForumFeed', 87 | /* @Static */ 88 | { 89 | loadData : function(data){ 90 | $('#forum-feed').data("controls")[0].insertForumFeed(data.value.items); 91 | } 92 | }, 93 | /* @Prototype */ 94 | { 95 | template : '
  • {title}{author} - {date}
  • ', 96 | init : function(){ 97 | var forumUrl = 'http://pipes.yahoo.com/pipes/pipe.run?_id=a6c6d9c200823b78d02087c6ea16b9ad&_render=json&_callback=ForumFeed.loadData'; 98 | $.ajax({ 99 | url: forumUrl, 100 | dataType: 'script' 101 | }); 102 | }, 103 | insertForumFeed : function(data){ 104 | var html = [], titles = [], title, d; 105 | for(var i = 0, ii = data.length; i < 6 && i < data.length; i++){ 106 | title = data[i].title.replace(/^Re\s\:\s/, ""); 107 | if($.inArray(title, titles) == -1){ 108 | titles.push(title); 109 | d = { 110 | title : title, 111 | author : data[i]['dc:creator'], 112 | date : this.formatDate(data[i].pubDate), 113 | href : data[i].link 114 | } 115 | html.push(can.sub(this.template, d)); 116 | } 117 | } 118 | if(html.length > 0){ 119 | this.element.find('ul').html(html.join('')); 120 | } 121 | }, 122 | // "Thu, 13 Jan 2011 11:59:47 -0800" - forum 123 | // receives a date like "Thu Jan 13 05:33:12 +0000 2011" 124 | // receives a date like "Thu, 13 Jan 2011 11:59:47 -0800" - forum 125 | getDate : function(date_string){ 126 | var dateMatch = date_string.match(/\w+\s+(\d+)\s+(\w+)\s+(\d)+/), 127 | date = new Date( 128 | parseInt(dateMatch[3], 10), 129 | this.getMonth(dateMatch[2]), 130 | parseInt(dateMatch[1], 10) 131 | ) 132 | return date; 133 | } 134 | }); 135 | 136 | Feed.extend('BlogFeed', 137 | /* @Static */ 138 | { 139 | loadData : function(data){ 140 | $('#blog').data("controls")[0].insertBlogFeed(data.responseData.feed.entries); 141 | } 142 | }, 143 | /* @Prototype */ 144 | { 145 | init : function(){ 146 | var forumUrl = 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://bitovi.com/blog/tag/javascriptmvc.rss&v=1.0&callback=BlogFeed.loadData&num=10' 147 | $.ajax({ 148 | url: forumUrl, 149 | dataType: 'script' 150 | }); 151 | }, 152 | formatDate : function(date_string){ 153 | var parts = date_string.match(/\/blog\/(\d+)\/(\d+)/); 154 | return { 155 | // TODO the date doesn't come back in this feed, add it 156 | date: parseInt(parts[2], 10)+1, 157 | month: parseInt(parts[2], 10), 158 | year: parseInt(parts[1], 10) 159 | }; 160 | }, 161 | insertBlogFeed : function(data){ 162 | var html = [], d, date, li, url; 163 | for(var i = 0, ii = data.length; i < 10 && i < data.length; i++){ 164 | date = this.formatDate(data[i].link); 165 | d = { 166 | title : data[i].title, 167 | month : date.month, 168 | date : date.date, 169 | year : date.year, 170 | body : data[i].contentSnippet, 171 | url : data[i].link 172 | } 173 | li = $.View("//site/views/blog.ejs", d); 174 | this.element.append(li); 175 | } 176 | } 177 | }); 178 | 179 | $('#twitter-feed').twitter_feed(); 180 | $('#forum-feed').forum_feed(); 181 | $('#blog').blog_feed(); 182 | 183 | }); -------------------------------------------------------------------------------- /site/static/styles/config.less: -------------------------------------------------------------------------------- 1 | @logo: "../img/javascriptmvc-logo.svg"; 2 | @logoFooter: "../img/javascriptmvc-logo-grey.svg"; 3 | @logoWidth: 260px; 4 | @pageBackground: url(../img/bkg-dots.png) repeat #fff; 5 | 6 | @colorHeader: #444444; 7 | @colorLinks: #1f54c6; 8 | @colorCode: #f9f7df; 9 | @colorNav: #066248; 10 | @colorSignature: #066248; 11 | @colorParamsReturns: #ece7e7; 12 | @fontColorParamsReturns: #000000; 13 | @colorTags: #999999; -------------------------------------------------------------------------------- /site/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Verdana; 3 | font-size: 10pt; } 4 | -------------------------------------------------------------------------------- /site/stylesheets/builder.css: -------------------------------------------------------------------------------- 1 | /* Generated by Font Squirrel (http://www.fontsquirrel.com) on January 4, 2011 */ 2 | 3 | @font-face { 4 | font-family: 'MuseoSlab500'; 5 | src: url('..//fonts/museo_slab_500-webfont.eot'); 6 | src: local('☺'), url('..//fonts/museo_slab_500-webfont.woff') format('woff'), url('..//fonts/museo_slab_500-webfont.ttf') format('truetype'), url('..//fonts/museo_slab_500-webfont.svg#webfonth7ZuKjlS') format('svg'); 7 | font-weight: normal; 8 | font-style: normal; 9 | } 10 | 11 | html { 12 | overflow:hidden; 13 | } 14 | 15 | body { 16 | font-family: "Lucida Grande","Lucida Sans Unicode",Verdana,sans-serif; 17 | } 18 | #plugins .section .section-desc h2, h3, h4 { 19 | font-family: MuseoSlab500; 20 | font-size: 22px; 21 | margin: 20px 0 10px; 22 | padding-bottom: 10px; 23 | border-bottom: 1px dotted #1e1f1c; 24 | color: #1e1f1c; 25 | text-shadow: 0px 1px white; 26 | } 27 | #plugins .section .section-desc h3, h4 { 28 | border: none; 29 | font-size: 18px; 30 | margin: 10px 0; 31 | padding-bottom: 0; 32 | } 33 | #plugins .section .section-desc p { 34 | color: #1e1f1c; 35 | margin-bottom: 10px; 36 | line-height: 18px; 37 | font-size: 11px; 38 | } 39 | #plugins { 40 | border: 1px solid #bd4d00; 41 | -webkit-border-bottom-right-radius: 5px; 42 | -webkit-border-bottom-left-radius: 5px; 43 | -moz-border-radius-bottomright: 5px; 44 | -moz-border-radius-bottomleft: 5px; 45 | border-bottom-right-radius: 5px; 46 | border-bottom-left-radius: 5px; 47 | } 48 | #plugins .download-builder { 49 | height: 52px; 50 | background: #bd4d00; 51 | z-index: 100; 52 | margin-top: -1px; 53 | } 54 | #plugins .download-builder input { 55 | float: right; 56 | margin: 10px; 57 | border: none; 58 | height: 32px; 59 | width: 120px; 60 | background: url(..//images/download_button.png); 61 | cursor: pointer; 62 | font-family: MuseoSlab500; 63 | font-size: 16px; 64 | text-shadow: 0px 1px white; 65 | color: Black; 66 | line-height:normal; 67 | } 68 | #plugins .download-builder input:hover { 69 | color: White; 70 | } 71 | #plugins .section { 72 | border-bottom: 1px dotted #999; 73 | clear: both; 74 | overflow: auto; 75 | padding: 15px; 76 | font-size: 11px; 77 | height:100%; 78 | } 79 | #plugins .section .section-desc { 80 | float: left; 81 | width: 148px; 82 | } 83 | #plugins .section .plugin { 84 | float: right; 85 | width: 450px; 86 | margin: 5px 0; 87 | padding-top: 1px; 88 | min-height:30px; 89 | } 90 | 91 | #plugins .section .plugin input{ 92 | float: left; 93 | margin: -1px 10px 0 0; 94 | height: 19px; 95 | width: 19px; 96 | padding:0px; 97 | } 98 | #plugins .section .plugin label { 99 | display: block; 100 | float: left; 101 | width: 200px; 102 | line-height: 14px; 103 | font-weight: bold; 104 | font-size: 11px; 105 | margin-left:0; 106 | color: Black; 107 | } 108 | #plugins .section .plugin .desc { 109 | float: left; 110 | width: 200px; 111 | margin-left: 10px; 112 | font-size: 11px; 113 | } 114 | #plugins h3 { 115 | margin: 0; 116 | } -------------------------------------------------------------------------------- /site/stylesheets/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ */ 2 | /* v1.0 | 20080212 */ 3 | 4 | html, body, div, span, applet, object, iframe, 5 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 6 | a, abbr, acronym, address, big, cite, code, 7 | del, dfn, em, font, img, ins, kbd, q, s, samp, 8 | small, strike, strong, sub, sup, tt, var, 9 | b, u, i, center, 10 | dl, dt, dd, ol, ul, li, 11 | fieldset, form, label, legend, 12 | table, caption, tbody, tfoot, thead, tr, th, td { 13 | margin: 0; 14 | padding: 0; 15 | border: 0; 16 | outline: 0; 17 | font-size: 100%; 18 | vertical-align: baseline; 19 | background: transparent; 20 | } 21 | body { 22 | line-height: 1; 23 | } 24 | ol, ul { 25 | list-style: none; 26 | } 27 | blockquote, q { 28 | quotes: none; 29 | } 30 | blockquote:before, blockquote:after, 31 | q:before, q:after { 32 | content: ''; 33 | content: none; 34 | } 35 | 36 | /* remember to highlight inserts somehow! */ 37 | ins { 38 | text-decoration: none; 39 | } 40 | del { 41 | text-decoration: line-through; 42 | } 43 | 44 | /* tables still need 'cellspacing="0"' in the markup */ 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /site/summary.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DoneJS 5 | 6 | 7 | 21 | 22 | 23 |
    24 |
    25 |
    26 |
    27 | 28 |
    29 |
    30 | 41 |
    42 |
    43 | 44 |
    45 |
    46 |
    47 |
    48 | 51 |
    52 |
    53 | <%= this.indexPage ? this.indexPage.real_comment : "Add a page named 'index' to see something here." %> 54 |
    55 |
    56 |
    57 |
    58 |
    59 |
    60 | © Bitovi - JavaScript Training and Support 61 | 62 |
    63 | 78 | 81 | 82 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /site/templates/layout.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | JavaScriptMVC {{#if title}}- {{title}} {{else}} {{#if name}}- {{name}}{{/if}}{{/if}} 21 | 22 | 23 | 24 | 30 | 33 | 34 | 35 | 36 |
    37 | 73 |
    74 | 75 | {{{content}}} 76 | 77 | 82 | 96 |

    {{staticLocation}}

    97 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /site/views/blog.ejs: -------------------------------------------------------------------------------- 1 |
  • 2 |
    3 |
    <%=month%>
    4 |
    <%=date%>
    5 |
    <%=year%>
    6 |
    7 |

    <%==title%>

    8 | <%==body%> 9 | Read rest of the entry » 10 |
  • -------------------------------------------------------------------------------- /stealconfig.js: -------------------------------------------------------------------------------- 1 | steal.config({ 2 | map: { 3 | "*": { 4 | "jquery/jquery.js" : "jquery", 5 | "can/util/util.js": "can/util/jquery/jquery.js", 6 | "jquery/": "jquerypp/" 7 | } 8 | }, 9 | paths: { 10 | "jquery/": "jquerypp/", 11 | "jquery": "can/lib/jquery.1.9.1.js", 12 | "mootools/mootools.js" : "can/lib/mootools-core-1.4.5.js", 13 | "dojo/dojo.js" : "can/util/dojo/dojo-1.8.1.js", 14 | "yui/yui.js" : "can/lib/yui-3.7.3.js", 15 | "zepto/zepto.js" : "can/lib/zepto.1.0rc1.js" 16 | }, 17 | shim : { 18 | jquery: { 19 | exports: "jQuery" 20 | } 21 | }, 22 | ext: { 23 | js: "js", 24 | css: "css", 25 | less: "steal/less/less.js", 26 | coffee: "steal/coffee/coffee.js", 27 | ejs: "can/view/ejs/ejs.js", 28 | mustache: "can/view/mustache/mustache.js" 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /test/run.js: -------------------------------------------------------------------------------- 1 | // loads each project's command line tests 2 | 3 | STEALPRINT = false; 4 | 5 | load("steal/test/run.js"); 6 | 7 | // funcunit here ... 8 | 9 | load("funcunit/test/run.js"); 10 | 11 | load("jquery/test/run.js"); 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/scripts/big_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This test loads every test in JavaScriptMVC 3 | */ 4 | load('test/scripts/getting_started_test.js') 5 | 6 | print("========================== generators =============================") 7 | load('steal/generate/test/run.js'); 8 | 9 | 10 | print("========================== compression ============================") 11 | load('steal/build/test/run.js'); 12 | load('jquery/view/test/compression/run.js'); 13 | 14 | 15 | print("========================== unit/functional ============================") 16 | load('steal/rhino/rhino.js'); 17 | load('funcunit/loader.js'); 18 | FuncUnit.load('test.html'); 19 | 20 | -------------------------------------------------------------------------------- /test/scripts/getting_started_test.js: -------------------------------------------------------------------------------- 1 | print("========================== jquery/generate/scaffold ============================") 2 | 3 | load('steal/rhino/rhino.js'); 4 | load('steal/test/test.js'); 5 | 6 | (function(rhinoSteal){ 7 | 8 | _args = ['cookbook']; 9 | load('jquery/generate/app'); 10 | _args = ['Cookbook.Models.Recipe']; 11 | load('jquery/generate/scaffold'); 12 | 13 | load('steal/rhino/rhino.js'); 14 | cookbookContent = readFile('cookbook/cookbook.js'). 15 | replace(".models()", ".models('recipe')"). 16 | replace(".controllers()", ".controllers('recipe')"); 17 | new steal.File('cookbook/cookbook.js').save( cookbookContent ); 18 | 19 | qunitContent = readFile('cookbook/test/qunit/qunit.js'). 20 | replace(".then(\"cookbook_test\")", ".then(\"recipe_test\")"); 21 | new steal.File('cookbook/test/qunit/qunit.js').save( qunitContent ); 22 | 23 | funcunitContent = readFile('cookbook/test/funcunit/funcunit.js'). 24 | replace(".then(\"cookbook_test\")", ".then(\"recipe_controller_test\")"); 25 | new steal.File('cookbook/test/funcunit/funcunit.js').save( funcunitContent ); 26 | 27 | //now see if unit and functional run 28 | print("-- Run unit tests for cookbook --"); 29 | load('funcunit/loader.js'); 30 | FuncUnit.load('cookbook/qunit.html'); 31 | 32 | print("-- Run functional tests for cookbook --"); 33 | load('steal/rhino/rhino.js'); 34 | load('funcunit/loader.js'); 35 | FuncUnit.load('cookbook/funcunit.html'); 36 | 37 | print("-- Compress cookbook --"); 38 | load("cookbook/scripts/build.js") 39 | 40 | print("-- Generate docs --"); 41 | _args = ['cookbook/cookbook.html'] 42 | load("documentjs/documentjs.js"); 43 | DocumentJS('cookbook/cookbook.html'); 44 | 45 | load('steal/rhino/rhino.js'); 46 | cookbookPage = readFile('cookbook/cookbook.html'). 47 | replace("steal.js", "steal.production.js"); 48 | new steal.File('cookbook/cookbook.html').save( cookbookPage ); 49 | 50 | print("== complete !!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); 51 | 52 | //print("-- cleanup --"); 53 | steal.File("cookbook").removeDir(); 54 | 55 | })(steal); 56 | 57 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | //we probably have to have this only describing where the tests are 2 | steal('funcunit') 3 | .then('jquery/test/qunit') 4 | .then('funcunit/syn/test/qunit') 5 | .then('funcunit/test/funcunit') 6 | .then(function(){ 7 | FuncUnit.jQuery("").appendTo(document.body) 8 | }) 9 | .then('mxui/test.js') -------------------------------------------------------------------------------- /tutorials/ajaxy/ajaxy.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | app 6 | 7 | 8 | 9 | 10 |
    11 | 12 | Videos 13 | Articles 14 | Images 15 |
    16 | 17 |
    18 | 19 | 20 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /tutorials/ajaxy/ajaxy.md: -------------------------------------------------------------------------------- 1 | @page ajaxy Searchable Ajax Apps 2 | @parent tutorials 8 3 | 4 | This tutorial walks you through building a simple widget 5 | that listens for changes in the browser location hash 6 | and updates the content of the page. It demonstrates how to make 7 | a site Google crawlable and searchable. 8 | 9 | ## The App 10 | 11 | We'll make a mini app that updates the contents of page with an 12 | Ajax request when a user clicks on a navigation link. Then, we'll make this searchable 13 | with the ajaxy/scripts/crawl.js script. 14 | 15 | @demo tutorials/ajaxy/ajaxy.html 500 16 | 17 | The crawl script generates html pages that Google can use as a representation 18 | of the content of an Ajax application. Read Google's documentation on its 19 | [Ajax crawling API](https://developers.google.com/webmasters/ajax-crawling/docs/getting-started) 20 | before continuing this tutorial. 21 | 22 | ## Setup 23 | 24 | [installing Download and install] the latest version of JavaScriptMVC. 25 | 26 | After installing JavaScriptMVC, open a command line to 27 | the [steal.config.root steal.config.root] folder (where you unzipped 28 | JavaScriptMVC). 29 | 30 | 31 | We'll use the application generator to generate an application 32 | skeleton folder. Run: 33 | 34 | [WINDOWS] > js jmvc/generate/app ajaxy 35 | [Lin/Mac] > ./js jmvc/generate/app ajaxy 36 | 37 | ## The Code 38 | 39 | In the generated ajaxy folder, you'll find ajaxy.html 40 | and ajaxy.js. We'll add a content area 41 | and few links to 42 | ajaxy.html. When we click on links, 43 | we'll make ajaxy.js load content into 44 | the content area. 45 | 46 | Change ajaxy.html so it looks like: 47 | 48 | @codestart xml 49 | <!DOCTYPE HTML> 50 | <html lang="en"> 51 | <head> 52 | <title>Ajaxy</title> 53 | <meta name="fragment" content="!"> 54 | </head> 55 | <body> 56 | <a href='#!videos'>Videos</a> 57 | <a href='#!articles'>Articles</a> 58 | <a href='#!images'>Images</a> 59 | <div id='content'></div> 60 | <script type='text/javascript' 61 | src='../steal/steal.js?ajaxy,development'> 62 | </script> 63 | </body> 64 | </html> 65 | @codeend 66 | 67 | Notice that the page includes a <meta name="fragment" content="!"> 68 | tag. This tells to Google to process ajaxy.html as having Ajax content. 69 | 70 | Next, add some content to show when these links are clicked. Put the following content 71 | in each file: 72 | 73 | __ajaxy/fixtures/articles.html__ 74 | 75 | @codestart xml 76 | <h1>Articles</h1> 77 | <p>Some articles.</p> 78 | @codeend 79 | 80 | __ajaxy/fixtures/images.html__ 81 | 82 | @codestart xml 83 | <h1>Images</h1> 84 | <p>Some images.</p> 85 | @codeend 86 | 87 | __ajaxy/fixtures/videos.html__ 88 | 89 | @codestart xml 90 | <h1>Videos</h1> 91 | <p>Some videos.</p> 92 | @codeend 93 | 94 | Finally, change ajaxy.js to look like: 95 | 96 | steal('jquery', 97 | 'can/construct/proxy', 98 | 'can/control', 99 | 'can/route', 100 | 'steal/html', 101 | function($, can){ 102 | 103 | var Ajaxy = can.Control({ 104 | "{route} change" : function(route, ev){ 105 | this.updateContent(route.page) 106 | }, 107 | updateContent : function(hash){ 108 | // postpone reading the html 109 | steal.html.wait(); 110 | 111 | $.get("fixtures/" + hash + ".html", {}, this.proxy('replaceContent'), "text") 112 | }, 113 | replaceContent : function(html){ 114 | this.element.html(html); 115 | 116 | // indicate the html is ready to be crawled 117 | steal.html.ready(); 118 | } 119 | }) 120 | 121 | new Ajaxy('#content', { route: can.route(":page", { page: "videos" }) }); 122 | 123 | }); 124 | 125 | When a route ("{route} change") event occurs, Ajaxy 126 | uses the route.page value to make a 127 | request ($.get) 128 | for content in thefixtures folder. For more information 129 | on routing, visit [can.route]. 130 | 131 | When the content is retrieved, it replaces the element's 132 | html (this.element.html(...)). 133 | 134 | Ajaxy also calls updateContent to load content when 135 | the page loads initially. 136 | 137 | ## Crawling and scraping 138 | 139 | To crawl your site and generate google-searchable html, run: 140 | 141 | @codestart none 142 | [WINDOWS] > js ajaxy\scripts\crawl.js 143 | [Lin/Mac] > ./js ajaxy/scripts/crawl.js 144 | @codeend 145 | 146 | This script peforms the following actions: 147 | 148 | 1. Opens a page in a headless browser. 149 | 2. Waits until its content is ready. 150 | 3. Scrapes its contents. 151 | 4. Writes the contents to a file. 152 | 5. Adds any links in the page that start with #! to be indexed 153 | 6. Changes the url hash to the next index-able page 154 | 7. Goto #2 and repeats until all pages have been loaded 155 | 156 | 157 | ## Pausing the html scraping. 158 | 159 | By default, the contents are scraped immediately after the page's scripts have loaded or 160 | the route has changed. The Ajax request for content 161 | happens asynchronously so we have to tell [steal.html] to wait to scrape the content. 162 | 163 | To do this, Ajaxy calls: 164 | 165 | steal.html.wait(); 166 | 167 | before the Ajax request. And when the page is ready, Ajaxy calls: 168 | 169 | steal.html.ready(); 170 | 171 | ## Getting Google To Crawl Your Site 172 | 173 | If you haven't already, read up on 174 | Google's [Ajax crawling API.](https://developers.google.com/webmasters/ajax-crawling/docs/getting-started) 175 | 176 | When google wants to crawl your site, it will send a 177 | request to your page with \_escaped\_fragment=. 178 | 179 | When your server sees this param, redirect google to the generated html page. For example, when the Google Spider requests http://mysite.com?\_escaped\_fragment=val, this is its attempt to crawl http://mysite.com#!val. You should redirect this request to http://mysite.com/html/val.html. 180 | 181 | Yes, it's that easy! 182 | 183 | ## Phantom for Advanced Pages 184 | 185 | By default the crawl script uses EnvJS to open your page and build a static snapshot. For some pages, EnvJS won't be powerful enough to accurately simulate everything. If your page experiences errors, you can use PhantomJS (headless Webkit) to generate snapshots instead, which may work better. 186 | 187 | To turn on Phantom: 188 | 189 | 1. Install it using the install instructions [funcunit.phantomjs here] 190 | 1. Open scripts/crawl.js and change the second parameter of steal.html.crawl to an options object with a browser option, like this: 191 | 192 | @codestart 193 | steal('steal/html', function(){ 194 | steal.html.crawl("ajaxy/ajaxy.html", 195 | { 196 | out: 'ajaxy/out', 197 | browser: 'phantomjs' 198 | }) 199 | }) 200 | @codeend -------------------------------------------------------------------------------- /tutorials/ajaxy/fixtures/articles.html: -------------------------------------------------------------------------------- 1 |

    Articles

    2 |

    Some articles.

    -------------------------------------------------------------------------------- /tutorials/ajaxy/fixtures/images.html: -------------------------------------------------------------------------------- 1 |

    Images

    2 |

    Some images.

    -------------------------------------------------------------------------------- /tutorials/ajaxy/fixtures/videos.html: -------------------------------------------------------------------------------- 1 |

    Videos

    2 |

    Some videos.

    -------------------------------------------------------------------------------- /tutorials/cms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/cms.png -------------------------------------------------------------------------------- /tutorials/done.md: -------------------------------------------------------------------------------- 1 | @page done Upgrading to 3.3 2 | @parent tutorials 11 3 | 4 | JavaScriptMVC 3.3 introduces a lot of new features to build large and responsive applications. As such, there are a few changes from 3.2 and this guide walks through the API differences between the versions. 5 | 6 | ## CanJS 7 | 8 | [canjs Can] has replaced the previous MVC internals of JavaScript MVC, but provides backwards compatability to jQuery MX. jQuery MX $.Class, $.Model, and $.Controller will not be supported in future versions, and we urge you to switch to using can. 9 | 10 | ## Steal 11 | 12 | [stealjs Steal] in 3.3 has support for AMD modules. Steal will still load resources as 3.2, however will also now follow the pattern for dependencies represented by a string id. 13 | 14 | So, as a simple example: 15 | 16 | steal('fooResource', function(foo) {}); 17 | 18 | In the above example, "foo" refers to the module returned by fooResource/fooResource.js. 19 | 20 | This leads to the gotcha when defining "$" in the previous syntax. So code that used to look like: 21 | 22 | steal('jquery', 'someResource') 23 | .then('someController', function($) {}); 24 | 25 | The above will not work as expected, because "$" will refer to a module returned by "someController" or undefined. In 3.3, a simple change will fix this issue for jQuery users: 26 | 27 | steal('jquery', 'someResource') 28 | .then('someController', function() {}); 29 | 30 | ## $.Class.prototype.callback 31 | 32 | "callback" has been deprecated and is now "proxy" to match jQuery's API. 33 | 34 | 3.2: 35 | 36 | $.Controller('foo', {}, { 37 | init: function() { 38 | $.get('/resource', this.callback('render')); 39 | }, 40 | 41 | render: function() {} 42 | }); 43 | 44 | 3.3: 45 | 46 | $.Controller('foo', {}, { 47 | init: function() { 48 | $.get('/resource', this.proxy('render')); 49 | }, 50 | 51 | render: function() {} 52 | }); 53 | 54 | ## $.View 55 | 56 | $.View() now returns a document fragment to aid with faster rendering within your application. However, when nesting templates, you'll need the actual html string returned by $.View.render as opposed to the document fragment. 57 | 58 | Within your EJS templates in 3.2: 59 | 60 |
    61 | <%== $.View('//sometemplate.ejs', {}) %> 62 |
    63 | 64 | 3.3: 65 | 66 |
    67 | <%== $.View.render('//sometemplate.ejs', {}) %> 68 |
    -------------------------------------------------------------------------------- /tutorials/examples.md: -------------------------------------------------------------------------------- 1 | @page examples Examples 2 | @parent javascriptmvc 6 3 | @description Example application to learn from. 4 | 5 | Here is the list of the example JavaScriptMVC applications. They cover specific JavaScriptMVC features and are ordered by complexity. If you need an introduction to the JavaScriptMVC framework it is best to read them in order. 6 | 7 | ## [todo TodoMVC JavaScriptMVC] - [open application ⇗](tutorials/examples/todo.html) 8 | 9 | In this guide, we're going to be installing and walking through the simplest [JavaScriptMVC](http://javascriptmvc.com/) 10 | application imaginable — a TODO list manager. 11 | 12 | This article covers: 13 | 14 | - Separation of application logic from user interface 15 | - Using model lists to manage collection of models 16 | 17 | ## [srchr Srchr] - [open application ⇗](srchr/srchr.html) 18 | 19 | Srchr searches multiple services (like Flickr, Upcoming, and Twitter) and saves the results between page requests. 20 | 21 | This article covers: 22 | 23 | - The ideas behind JavaScriptMVC 24 | - How JavaScriptMVC enables code separation 25 | - Event oriented architecture 26 | 27 | ## [contacts Contacts] - [open application ⇗](contacts) 28 | 29 | The contacts example is a lightweight application that allows users to add and organize their friend's contact information. 30 | 31 | This article covers: 32 | 33 | - Installing and running the application 34 | - The application's structure and organization 35 | - How the application's widgets were designed 36 | - The anatomy of the application's widgets 37 | - How we glued the application's widgets together using event-oriented-architecture 38 | -------------------------------------------------------------------------------- /tutorials/examples/contacts.md: -------------------------------------------------------------------------------- 1 | @page contacts Contacts 2 | @parent examples 3 3 | 4 | In this article we will walk through installing and the ins-and-outs of Contacts. Contacts is a lightweight application that allows users to add and organize their friends' contact information. 5 | 6 | This tutorial describes: 7 | 8 | - Installing and running the application 9 | - The application's structure and organization 10 | - Dividing the application into modular widgets 11 | - Tieing the widgets together 12 | 13 | @image ../tutorials/images/contacts_preview.png 14 | 15 | Let's get started! 16 | 17 | ## Setup 18 | 19 | The application source is hosted by [GitHub](https://github.com/bitovi/contacts). You can download the application on github using the following commands: 20 | 21 | $ git clone https://github.com/bitovi/contacts 22 | $ cd contacts 23 | $ git submodule update --init 24 | 25 | To run the application, open _contacts.html_ with your browser. We will be using [can.fixture fixtures] to simulate the AJAX requests so running it from a server isn’t necessary. 26 | 27 | This will run the application in development mode. If you want to build and run the application in production, in the command line run: 28 | 29 | $ ./js contacts/scripts/build.js 30 | 31 | then change the script tag in `contacts.html` to be in production mode: 32 | 33 | 34 | 35 | Additionally, the app can be found on [Github Pages](http://bitovi.github.io/contacts/) if you do not want to set it up. 36 | 37 | ## Folder Structure 38 | 39 | The application resides in the `contacts` folder. Steal, CanJS, and CanUI folders sit perpendicular to this for reuse in other projects. The directory structure should mirror below. 40 | 41 | [top-level] 42 | /can 43 | /steal 44 | /funcunit 45 | /contacts 46 | /form 47 | /scripts 48 | /test 49 | /models 50 | /fixtures 51 | /views 52 | /less 53 | funcunit.html 54 | qunit.html 55 | contacts.js 56 | ... 57 | contacts.html 58 | stealconfig.js 59 | 60 | The contacts folder contains: 61 | 62 | - `models` AJAX end-point definitions and helpers 63 | - `views` EJS/Mustache can.view templates 64 | - `fixtures` simulated AJAX response 65 | - `less` LESS stylesheet such as [Boostrap](http://twitter.github.io/bootstrap/) 3.0 and `contacts.less` 66 | - `contacts/form` child components of contacts 67 | 68 | Along with runners and scripts for building and tests. 69 | 70 | ## Division of Modules 71 | 72 | The secret to building large applications is NEVER build large applications. Understanding how to divide and isolate modules in the application is the first step towards maintainable architecture. 73 | 74 | The goal for dividing your application should be to create modules that are isolated. 75 | 76 | Isolated modules are: 77 | 78 | - Limited to one specific purpose, for example showing a list of data 79 | - Rarely reference other modules and never their parents 80 | - Have a simple generic API making them easy to swap out 81 | 82 | Isolated modules are easily testable because they have a small, well defined scope. Each piece can be worked on in parallel because the code is divided. Reuse is easier because the modules are not coupled to each other. 83 | 84 | ### Dividing Contacts 85 | 86 | The contacts app has 3 lists that filter the grid of contacts. You can create additional categories and contacts by clicking the 'new' icon. 87 | 88 | This application can be divided up into a few widgets: 89 | 90 | * List - accepts a generic data source and layout, renders and updates the list. 91 | * Grid - accepts a generic data source, renders a grid. 92 | * Form - create a new instance from a data source. 93 | 94 | Heres a visual representation of how this app is broken up into modules. 95 | 96 | @image ../tutorials/images/contacts_design.png 97 | 98 | ## Tying it all together 99 | 100 | `contacts/contacts.js` will be where the application starts: loading each module, initializing them, and gluing them together. 101 | 102 | In the `init` method, we initalize all the base objects and inject the base view. 103 | 104 | init: function(){ 105 | // initalize the lists and objects 106 | this.categoryList = new Models.Category.List; 107 | this.locationList = new Models.Location.List; 108 | this.companyList = new Models.Company.List; 109 | this.contactsList = new Models.Contact.List; 110 | this.edited = new Observe; 111 | this.total = can.compute(0); 112 | this.isLoading = can.compute(function(loading){ 113 | loading ? loadingCounter++ : loadingCounter--; 114 | return loading > 0; 115 | }); 116 | 117 | // Draw the view, setup helpers and partials 118 | this.element.html(initView({ ... }); 119 | 120 | // Initalize each Form category 121 | can.each(['location', 'category', 122 | 'company', 'contact'], function(formType){ 123 | new Form(this.element.find('#' + formType), { 124 | edited : this.edited, 125 | model : Models[can.capitalize(formType)], 126 | list : this[formType + 'List'] 127 | }); 128 | }.bind(this)); 129 | 130 | this.setupScroll(); 131 | this.loadFilters(); 132 | this.loadContacts(); 133 | } 134 | 135 | From this point on, the application uses live-binding to update the lists/views based on the filter/offset. 136 | 137 | {{#contacts}} 138 | {{>contact}} 139 | {{/contacts}} 140 | 141 | As the `contacts` list changes, the view automatically updates to reflect the new list. 142 | 143 | ## Wrapup 144 | 145 | In this article, we explored: 146 | 147 | - Installing and running the application 148 | - The application's structure and organization 149 | - Dividing the application into modular widgets 150 | - Tieing the widgets together 151 | 152 | If you're interested in other examples, check out the other application examples. -------------------------------------------------------------------------------- /tutorials/examples/srchr.md: -------------------------------------------------------------------------------- 1 | @page srchr Srchr 2 | @parent examples 0 3 | 4 | Srchr searches several data sources for content and 5 | displays it to the user. See it in 6 | action [here](http://javascriptmvc.com/srchr/srchr.html). This article 7 | covers how to install Srchr. To understand how Srchr works and many 8 | of the core concepts behind JavaScriptMVC, please watch: 9 | 10 | - [Part 1 - MVC architecture and the observer pattern](http://www.youtube.com/watch?v=NZi5Ru4KVug) 11 | - [Part 2 - Development process](http://www.youtube.com/watch?v=yFxDY5SQQp4) 12 | 13 | 14 | ## Installing Srchr 15 | 16 | 17 | Install the Srchr app by cloning the git repo: 18 | 19 | > git clone git://github.com/bitovi/srchr srchr 20 | > cd srchr 21 | > git submodule update --init --recursive 22 | 23 | Once you get the application you should have a structure similar to below 24 | 25 | /srchr [top-level directory] 26 | /can 27 | /documentjs 28 | /steal 29 | /funcunit 30 | /srchr 31 | /history 32 | /models 33 | /scripts 34 | /search 35 | /search_result 36 | /tabs 37 | /templates 38 | /test 39 | test.html 40 | srchr.less 41 | srchr.js 42 | srchr.html 43 | ... 44 | 45 | Srchr is now ready to be used. To run the Srchr application simply open _srchr/index.html_ in your browser. 46 | 47 | -------------------------------------------------------------------------------- /tutorials/examples/todo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CanJS • TodoMVC 7 | 8 | 9 |
    10 |
    11 | 16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tutorials/funcunit.md: -------------------------------------------------------------------------------- 1 | @page funcunit.getstarted Get Started with FuncUnit 2 | @parent tutorials 6 3 | 4 | @body 5 | 6 | In this guide, we'll use [FuncUnit] to write functional tests for the jQuery UI 7 | autocomplete widget. We'll go over: 8 | 9 | * Running a test in browser 10 | * Writing a test 11 | * Debugging a broken test 12 | * Running tests via Selenium 13 | * Running tests via PhantomJS 14 | 15 | ## Running Autocomplete Tests 16 | 17 | Open _funcunit/test/autosuggest/autosuggest.html_ in a browser. Type "J" in the input. You'll see the following: 18 | 19 | @image funcunit/pages/images/autosuggest.png 20 | 21 | 22 | This page is a simple demo app, using [jQueryUI autocomplete](http://jqueryui.com/demos/autocomplete/). It 23 | shows results when you start typing, then you can click a result (or use mouse navigation) to populate the input. 24 | 25 | There is a test already written. Open funcunit/test/autosuggest/autosuggest_test.js in your IDE: 26 | 27 | @codestart 28 | module("autosuggest",{ 29 | setup: function() { 30 | S.open('autosuggest.html') 31 | } 32 | }); 33 | 34 | test("results appear",function(){ 35 | S('input').visible().click().type("Java") 36 | 37 | // wait until we have some results 38 | S('.ui-menu-item').visible(function(){ 39 | equal( S('.ui-menu-item').size(), 2, "there are 2 results") 40 | }) 41 | }); 42 | @codeend 43 | 44 | As you can probably tell, the [funcunit.finding S method] is an alias for jQuery (*). This test: 45 | 46 | 1. Opens autosuggest.html 47 | 1. Grabs the input element, clicks it, and types "Java" 48 | 1. Grabs the element that is populated with results, waits for it to be visible 49 | 1. Asserts that there are 2 results shown 50 | 51 | (*) Actually its a [http://api.jquery.com/jQuery.sub/ copy] of jQuery that performs queries in 52 | the application window by default, and sometimes caches its selector to run asynchronously. 53 | 54 | To run this test, open funcunit/test/autosuggest/funcunit.html in any browser (turn off your popup blocker). The test will open and run. The results are shown in the QUnit page: 55 | 56 | @image funcunit/pages/images/qunit.png 57 | 58 | 59 | ## Writing an Autocomplete Test 60 | 61 | Next we'll add a test for selecting a result with the keyboard. FuncUnit's [apifuncunit API] consists of: 62 | 63 | * [funcunit.finding The S Method] - Perform a query in the application window 64 | * [funcunit.actions Actions] - Simulate user actions like [FuncUnit.prototype.click click], [FuncUnit.prototype.type type], [FuncUnit.prototype.drag drag] 65 | * [funcunit.waits Waits] - Wait for a condition in your page to be met. Fail the test if the condition isn't met before a timeout. 66 | * [funcunit.getters Assertions & getters] - Synchronously check a condition in your page. 67 | 68 | The setup and assertion methods are part of the [http://docs.jquery.com/Qunit QUnit] API. 69 | 70 | Our test should do the following: 71 | 72 | 1. Type "JavaS" in the input. 73 | 1. Wait for a result to be visible. 74 | 1. Select the input and press the down and enter keys to select the first item. 75 | 1. Wait for the input to show "JavaScript". 76 | 77 | Add the following under the first test: 78 | 79 | @codestart 80 | test("keyboard navigation",function(){ 81 | S('input').visible().click().type("JavaS") 82 | 83 | S('.ui-menu-item').visible() 84 | S('input').type('[down][enter]') 85 | .val("JavaScript") 86 | }); 87 | @codeend 88 | 89 | A few important notes about this test: 90 | 91 | 1. We have no assertions. This is ok. Most FuncUnit tests don't need them. If the wait conditions aren't met before a timeout, the test will fail. If the test completes, this feature is working. 92 | 1. The click, visible, and val methods are actually doing asynchronous things. FuncUnit lets you write tests with this linear syntax by queueing the actual methods and running them one by one. This is to prevent your tests from being an unreadable mess of nested functions like: 93 | 94 | @codestart 95 | S('.input').visible(function(){ 96 | S('.input').click(function(){ 97 | S('input').type("JavaS") 98 | }) 99 | }) 100 | @codeend 101 | 102 | Reload the funcunit.html page and see your new test run and pass. 103 | 104 | ## Debugging tests 105 | 106 | Now change .val("JavaScript") to .text("C#"). Reload the page and watch it timeout and fail. 107 | 108 | @image funcunit/pages/images/broken.png 109 | 110 | 111 | In this case, the error message shown is a good indication for why the test is broken. But often we need 112 | more visibility to debug a test. 113 | 114 | Your first debugging instinct might be "Let's add a breakpoint!". But, as noted, this 115 | code is running asynchronously. When .val() runs, its adding a method to 116 | FuncUnit.queue, not actually doing the check. When its this wait condition's turn to 117 | run, $("input").val() === "JavaScript" is checked repeatedly until its true or a timeout is reached. 118 | 119 | We can replace the string value with a checker function and use console.log to see what's going on. When 120 | previous queued methods finish, this function will run on repeat. Change that line to: 121 | 122 | @codestart 123 | .val(function(val){ 124 | console.log(val, this) 125 | if(val === "C#") return true; 126 | }); 127 | @codeend 128 | 129 | "this" in your wait method is the element that .text is being run against. The console will show the following: 130 | 131 | @image funcunit/pages/images/console.png 132 | 133 | 134 | Using this technique, you can inspect the state of your app at various points throughout the test. Undo 135 | this breaking change before moving on to the next part. 136 | 137 | ## Running in Selenium 138 | 139 | Next we'll run this same test via the browser automation tool Selenium. Open a 140 | command prompt to the JMVC directory and run the following: 141 | 142 | @codestart 143 | ./js funcunit/run selenium funcunit/test/autosuggest/funcunit.html 144 | @codeend 145 | 146 | On windows, just use "js" instead of ./js. This will open the test page in 147 | Firefox, run the same test, and report the results on the command line: 148 | 149 | @image funcunit/pages/images/commandline.png 150 | 151 | 152 | You can configure this step to run in any browser via the [integrations settings.js file]. 153 | 154 | ## Running in PhantomJS 155 | 156 | Running in Selenium is great, but physically opening a browser can be too slow for quick 157 | regression testing. [http://www.phantomjs.org/ PhantomJS] is a headless version of WebKit, which can run the same 158 | tests from the commandline much faster without opening any visual browser windows. To run 159 | this step, first you must [funcunit.phantomjs PhantomJS]. Then run: 160 | 161 | @codestart 162 | ./js funcunit/open/phantomjs funcunit/test/autosuggest/funcunit.html 163 | @codeend 164 | 165 | Phantom opens your page, runs the same test, and reports results on the commandline. 166 | This step can be easily integrated in your build process via [funcunit.jenkins Jenkins] or [funcunit.maven Maven]. 167 | 168 | ## Conclusion 169 | 170 | Hopefully, this guide illustrates how FuncUnit provides the holy grail of testing: easy, familiar syntax, in browser running for 171 | easy debugging, and simple automation. 172 | 173 | FuncUnit will transform your development lifecycle, give your developers confidence, and improve quality. 174 | 175 | 176 | That's it! If you want to learn more, read about FuncUnit's [FuncUnit API] and [funcunit.integrations integrations] 177 | or check out some [funcunit.demos demos]. -------------------------------------------------------------------------------- /tutorials/getstarted/Cookbook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/getstarted/Cookbook.png -------------------------------------------------------------------------------- /tutorials/getstarted/Docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/getstarted/Docs.png -------------------------------------------------------------------------------- /tutorials/getstarted/Welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/getstarted/Welcome.png -------------------------------------------------------------------------------- /tutorials/getstarted/building.md: -------------------------------------------------------------------------------- 1 | @page building.cookbook Building Cookbook 2 | @parent getstarted 2 3 | 4 | There is a large overhead associated with 5 | downloading many JavaScript and CSS files. [stealjs StealJS] 6 | can build your app into a single minified JS and CSS file 7 | for faster download. 8 | 9 |
    10 | It can also break your app into cache-able minified parts 11 | for more advanced performance techniques. 12 |
    13 | 14 | ## Build Script 15 | 16 | To build your application, run the following command from a console: 17 | 18 | > ./js cookbook/scripts/build.js 19 | Building to cookbook/ 20 | ... 21 | Building cookbook/production.js 22 | cookbook/production.css 23 | 24 | Verify that production.js was created by checking your `cookbook` folder. 25 | 26 | ## Switch to Production Mode 27 | 28 | Switch to production mode by changing the script tag to include steal.production.js: 29 | 30 | 33 | 34 | ## Reload and verify 35 | 36 | Reload your page. Only two JavaScript files will load: steal.production.js and production.js. 37 | Not bad considering 28 files are loaded in development mode. 38 | 39 | When you're ready, learn how to [cookbook.documenting Document Cookbook] -------------------------------------------------------------------------------- /tutorials/getstarted/documenting.md: -------------------------------------------------------------------------------- 1 | @page cookbook.documenting Documenting Cookbook 2 | @parent getstarted 3 3 | 4 | @body 5 | 6 | Documentation is a critical step in creating maintainable code. 7 | It's often burdensome on developers and 8 | becomes neglected. JavaScriptMVC's integrates [DocumentJS] to make 9 | it easy to document your code. 10 | 11 | ## Generating Documentation 12 | 13 | Create the docs by running: 14 | 15 | > ./js cookbook/scripts/docs.js 16 | 17 | 18 | ## Viewing Documentation 19 | 20 | Open __cookbook/docs/index.html__ and you'll find something like: 21 | 22 | @image ../tutorials/getstarted/Docs.png 23 | 24 | ## Writing Documentation 25 | 26 | The generated app comes with very minimal docs. But, it 27 | gives you a great place to 28 | start. Open __cookbook/cookbook.md__. This is the top level 29 | page for the cookbook application. Notice that it's markdown! 30 | 31 | The syntax for documentation is very similar to JavaDoc. However, there are some 32 | important differences. Consult the [DocumentJS DocumentJS's documentation] 33 | for more information. 34 | 35 | ## Next steps 36 | 37 | In the context of this trivial application, you've 38 | been exposed to major features of JavaScriptMVC: 39 | 40 | - code separation 41 | - testing 42 | - building 43 | - documentation 44 | 45 | This is pretty cool! Look at how simply you went from 46 | nothing to a compressed, tested, and documented application. 47 | 48 | -------------------------------------------------------------------------------- /tutorials/getstarted/getstarted.md: -------------------------------------------------------------------------------- 1 | @page getstarted Get Started with JMVC 2 | @parent tutorials 2 3 | 4 | This guide introduces the most important aspects of JavaScriptMVC (JMVC) by 5 | creating a simple cookbook application. 6 | 7 | ## Basics 8 | 9 | Before jumping in, there are some things you should know: 10 | 11 | ### Purpose 12 | 13 | Use JavaScriptMVC to develop client-side JavaScript apps. JMVC was 14 | created by [http://bitovi.com Bitovi], a JavaScript consulting 15 | company, to create quality, maintainable apps in the shortest 16 | amount of time. Since that time, JMVC has undergone 7 primary production releases 17 | with over 100 outside contributors. 18 | 19 | Unlike most JavaScript projects, JMVC is a 20 | true __framework__. It supplies best-of-bread solutions for things like: 21 | 22 | - DOM manipulation 23 | - MVC Architecture 24 | - Testing 25 | - Dependency management 26 | - Documentation 27 | 28 | It tightly integrates these solutions so they 29 | work together seemlessly. With repeatable development 30 | patterns, JMVC provides __direction to development__ making it easy 31 | for teams to work together more 32 | effectively. 33 | 34 | 35 | ### Sub Projects 36 | 37 | JavaScriptMVC is comprised of 5 sub projects: 38 | 39 | - [canjs CanJS] - A client side MVC framework 40 | - [jquerypp jQuery++] - A collection of useful DOM helpers and special events for jQuery 41 | - [stealjs StealJS] - A code manager: dependency management, code cleaning, building, etc. 42 | - [DocumentJS DocumentJS] - A documentation engine 43 | - [FuncUnit FuncUnit] - A web testing framework 44 | 45 | ### Plugins 46 | 47 | Sub-projects are futher broken down into plugins. Just [steal] the ones you need. Plugins load 48 | their own dependencies and won't load duplicate files. It looks like: 49 | 50 | steal('can/control', function( Control ) { 51 | Control // -> the Control API 52 | ... 53 | }); 54 | 55 | 56 | > _P.S. `steal('can/control')` adds `can/control/control.js` to your project._ 57 | 58 | 59 | ## License 60 | 61 | JavaScriptMVC is MIT with the following exceptions: 62 | 63 | - [Rhino](http://www.mozilla.org/rhino/) - JS command line ([MPL 1.1](http://www.mozilla.org/MPL/)) 64 | - [Selenium](http://seleniumhq.org/) - Browser Automation ([Apache 2](http://www.apache.org/licenses/LICENSE-2.0)) 65 | 66 | These exceptions, although permissive licenses themselves, are not linked in your final production build. 67 | 68 | ## Installing JavaScriptMVC 69 | 70 | Before continuing, make sure you have [installing installed JavaScriptMVC]. Once you 71 | have installed JavaScriptMVC, continue to [cookbook.creating Creating Cookbook]. 72 | -------------------------------------------------------------------------------- /tutorials/getstarted/selenium-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/getstarted/selenium-run.png -------------------------------------------------------------------------------- /tutorials/getstarted/testing.md: -------------------------------------------------------------------------------- 1 | @page cookbook.testing Testing Cookbook 2 | @parent getstarted 1 3 | 4 | @body 5 | 6 | JavaScriptMVC puts a tremendous emphasis on 7 | testing. It uses [FuncUnit] to easily write 8 | tests that can be run in the browser or automated. FuncUnit 9 | integrates: 10 | 11 | - QUnit - Assertions and testing structure 12 | - Syn - Synthetic user events like clicking and typing 13 | - Selenium / PhantomJS - Browser automation 14 | 15 | When you scaffolded recipe, it created tests and test pages for you. This 16 | guide will show you how to: 17 | 18 | - Run tests. 19 | - Understand the unit tests. 20 | - Run functional tests. 21 | - Understand the functional tests. 22 | - Test isTasty functionality. 23 | 24 | ## Run Tests 25 | 26 | Open `cookbook/cookbook_test.js`. You'll notice it steals tests 27 | for the model and controls. There 28 | are also tests that verify the 29 | original "Welcome to JavaScriptMVC" text that we removed. __Remove__ the 30 | extraneous test so `cookbook_test.js` just looks like this: 31 | 32 | @codestart 33 | steal( 34 | 'funcunit', 35 | './models/recipe_test.js', 36 | 'cookbook/recipe/create/create_test.js', 37 | 'cookbook/recipe/list/list_test.js', 38 | function (S) { 39 | 40 | // this tests the assembly 41 | module("cookbook", { 42 | setup : function () { 43 | S.open("//cookbook/index.html"); 44 | } 45 | }); 46 | 47 | test("creating a recipes adds it to the list ", function () { 48 | 49 | S("[name=name]").type("Ice Water"); 50 | S("[name=description]") 51 | .type("Pour water in a glass. Add ice cubes."); 52 | 53 | S("[type=submit]").click(); 54 | 55 | S("h3:contains(Ice Water)").exists(); 56 | S("p:contains(Pour water in a glass. Add ice cubes.)").exists() 57 | }); 58 | }); 59 | @codeend 60 | 61 | To run all of __cookbook's__ tests, open 62 | `cookbook/test.html` in a browser. 63 | 64 | To run those same tests with [funcunit.selenium Selenium], first you must set up a 65 | local server, like Apache, running at the javascriptmvc root. Make sure you can 66 | open your test page from this server, at a URL like http://localhost/javascriptmvc/cookbook/test.html. 67 | 68 | Then run: 69 | 70 | > ./js funcunit/open/selenium http://localhost/javascriptmvc/cookbook/test.html 71 | 72 | You should see something like: 73 | 74 | @image ../tutorials/getstarted/selenium-run.png 75 | 76 |
    77 | If Selenium is unable to open your browsers, it's likely you have them in an 78 | unusual location. Read the Other Browsers section in [funcunit.selenium Selenium] 79 | docs for information on how to configure browsers so selenium can find them. 80 |
    81 | 82 | Continue to [building.cookbook Building Cookbook] or continuen reading to learn how 83 | this code works. 84 | 85 | ## Tiered testing 86 | 87 | If an application should be built of small, isolated modules that are glued together, its tests should reflect that. 88 | 89 | Cookbook's modules are each designed to be built and tested independently. For example, the `cookbook/recipe/create` module has its own tests and test page. Open `cookbook/recipe/create/test.html` 90 | and it will run the tests in `cookbook/recipe/create/create_test.js`. 91 | 92 | To test the "glue", `cookbook_test.js` loads all modules' tests 93 | and provides an integration test, verifying the application as a whole works as expected. 94 | 95 | The following goes through: 96 | 97 | - cookbook/models/recipe_test.js 98 | - cookbook/recipe/create/create_test.js 99 | - cookbook/recipe/list/list_test.js 100 | - cookbook/cookbook_test.js 101 | 102 | ## recipe_test.js 103 | 104 | `cookbook/models/recipe_test.js` unit tests the 105 | `cookbook/models/recipe`, module which is aliased as Recipe. It starts 106 | by loading the `Recipe` model, QUnit, and the fixtures 107 | used to simulate the server: 108 | 109 | steal( "./recipe.js", 110 | "funcunit/qunit", 111 | "cookbook/models/fixtures", 112 | function( Recipe ){ 113 | 114 | Next it specifies which module the following tests belong to: 115 | 116 | module("cookbook/models/recipe"); 117 | 118 | Then, it defines a `findAll` test: 119 | 120 | test("findAll", function(){ 121 | expect(4); 122 | stop(); 123 | Recipe.findAll({}, function(recipes){ 124 | ok(recipes) 125 | ok(recipes.length) 126 | ok(recipes[0].name) 127 | ok(recipes[0].description) 128 | start(); 129 | }); 130 | }); 131 | 132 | The `findAll` test calls `Recipe.findAll` and 133 | attempts to verify that it returns recipes with 134 | a name and description. 135 | 136 | Because `Recipe.findAll` is asynchronous, this 137 | test calls QUnit's `stop` and `start` methods 138 | to signal when the test is complete. 139 | 140 | `recipe_test.js` goes on to test the remainder of 141 | `Recipe`'s CRUD methods: create, update, destroy. 142 | 143 | ## create_test.js 144 | 145 | `cookbook/recipe/create/create_test.js` tests 146 | the `cookbook/recipe/create` module aliased as 147 | RecipeCreate. It starts by loading funcunit, the 148 | RecipeCreate control, the Recipe model and 149 | the recipeStore fixture: 150 | 151 | steal('funcunit', 152 | './create.js', 153 | 'cookbook/models/recipe.js', 154 | 'cookbook/models/fixtures', 155 | function (S, RecipeCreate, Recipe, recipeStore ) { 156 | 157 | Next, it defines the module, with setup and teardown 158 | code that runs before and after every test: 159 | 160 | module("cookbook/recipe/create", { 161 | setup: function(){ 162 | $("#qunit-test-area") 163 | .append("
    "); 164 | new RecipeCreate("#create"); 165 | }, 166 | teardown: function(){ 167 | $("#qunit-test-area").empty(); 168 | recipeStore.reset(); 169 | } 170 | }); 171 | 172 | `setup` creates a _form_ element and creates a new `RecipeCreate` instance 173 | 174 | on it. `teardown` removes the element and [can.fixture.store.reset resets] the 175 | `recipeStore` to contain the original set of recipes. 176 | 177 | `create_test.js` tests that RecipeCreate can create a recipe: 178 | 179 | test("create recipes", function () { 180 | ... 181 | }); 182 | 183 | We are going to create an __Ice Water__ recipe, so we 184 | listen to a recipe being created and check it's contents like: 185 | 186 | stop(); 187 | Recipe.bind("created",function(ev, recipe){ 188 | ok(true, "Ice Water added"); 189 | 190 | equals(recipe.name, 191 | "Ice Water", 192 | "name set correctly"); 193 | 194 | equals(recipe.description, 195 | "Pour water in a glass. Add ice cubes.", 196 | "description set correctly" ); 197 | 198 | start(); 199 | Recipe.unbind("created",arguments.callee); 200 | }) 201 | 202 | As this test is asynchronous, it calls QUnit's stop and start. After 203 | listening to Recipes being created, the test creates 204 | a recipe by simulating a user filling in the recipe form and clicking submit: 205 | 206 | S("[name=name]").type("Ice Water"); 207 | S("[name=description]").type("Pour water in a glass. "+ 208 | "Add ice cubes."); 209 | 210 | S("[type=submit]").click(); 211 | 212 | Then, it verifies the submit button's value is "Creating": 213 | 214 | S("[type=submit]").val("Creating...", 215 | "button text changed while created"); 216 | 217 | Finally, when the value is changed back to "Create", the 218 | test checks that the form has been reset: 219 | 220 | S("[type=submit]").val("Create", function(){ 221 | ok(true, "button text changed back after create" ); 222 | equals(S("[name=name]").val(), "", "form reset"); 223 | equals(S("[name=description]").val(), "", "form reset"); 224 | }); 225 | 226 | ## list_test.js 227 | 228 | `cookbook/recipe/list/list_test.js` tests the `cookbook/recipe/list` 229 | module aliased as RecipeList. It starts by loading funcunit, the 230 | RecipeList control, the Recipe model and 231 | the recipeStore fixture: 232 | 233 | steal('funcunit', 234 | './list.js', 235 | 'cookbook/models/recipe.js', 236 | 'cookbook/models/fixtures', 237 | function (S, RecipeCreate, Recipe, recipeStore ) { 238 | 239 | Next, it defines the module it is testing, with setup and teardown 240 | code that runs before and after every test: 241 | 242 | module("cookbook/recipe/list", { 243 | setup: function(){ 244 | $("#qunit-test-area").append("
    "); 245 | this.list = new RecipeList("#recipes"); 246 | }, 247 | teardown: function(){ 248 | $("#qunit-test-area").empty(); 249 | recipeStore.reset(); 250 | } 251 | }); 252 | 253 | `setup` creates a _div_ element and creates a new `RecipeList` 254 | instance. That list will be accessible within each test as `this.list`. 255 | `teardown` removes the element and [can.fixture.store.reset resets] 256 | the `recipeStore` to contain the original set of recipes. 257 | 258 | Then, `list_test.js` tests that RecipeList displays all 259 | the recipes that are loaded on the server: 260 | 261 | test("lists all recipes", function(){ 262 | stop(); 263 | 264 | Recipe.findAll({}, function(recipes){ 265 | 266 | S(".recipe").size(recipes.length,function(){ 267 | ok(true, "All recipes listed"); 268 | 269 | start(); 270 | }) 271 | }) 272 | }); 273 | 274 | And it tests that created recipes are added to the list of recipes 275 | by creating a recipe and making sure a corresponding element shows 276 | up on the page: 277 | 278 | test("lists created recipes", function(){ 279 | 280 | new Recipe({ 281 | name: "Grilled Cheese", 282 | description: "grill cheese in bread" 283 | }).save(); 284 | 285 | S('h3:contains(Grilled Cheese X)') 286 | .exists("Lists created recipe"); 287 | }) 288 | 289 | To test deleting a recipe, `list_test.js` creates a recipe then 290 | clicks its destroy link and makes sure the element has been removed: 291 | 292 | test("delete recipes", function(){ 293 | new Recipe({ 294 | name: "Ice Water", 295 | description: "mix ice and water" 296 | }).save(); 297 | 298 | // wait until grilled cheese has been added 299 | S('h3:contains(Ice Water X)').exists(); 300 | 301 | S.confirm(true); 302 | S('h3:last a').click(); 303 | 304 | S('h3:contains(Ice Water)') 305 | .missing("Grilled Cheese Removed"); 306 | 307 | }); 308 | 309 | ## cookbook_test.js 310 | 311 | `cookbook/cookbook_test.js` loads all other tests 312 | and tests the `cookbook` module. It starts 313 | by loading FuncUnit and all the other tests: 314 | 315 | steal( 316 | 'funcunit', 317 | './models/recipe_test.js', 318 | 'cookbook/recipe/create/create_test.js', 319 | 'cookbook/recipe/list/list_test.js', 320 | function (S) { 321 | 322 | Next it defines which module it's testing: 323 | 324 | module("cookbook", { 325 | setup : function () { 326 | S.open("//cookbook/index.html"); 327 | } 328 | }); 329 | 330 | `setup` uses FuncUnit to open the application's page. Any 331 | FuncUnit commands, for example `S("h1").text()`, will 332 | operate within that page instead of the 333 | testing window. This is ideal for integration and functional tests. 334 | 335 | `cookbook_test.js` then tests if the page contains 336 | JavaScriptMVC's welcome text: 337 | 338 | test("welcome test", function () { 339 | equals( S("h1").text(), 340 | "Welcome to JavaScriptMVC!", 341 | "welcome text" ); 342 | }); 343 | 344 | Finally, it tests the integration between RecipeCreate and 345 | RecipeList by creating a recipe and making sure it is 346 | listed on the page: 347 | 348 | test("creating a recipes adds it to the list ", function () { 349 | 350 | S("[name=name]").type("Ice Water"); 351 | S("[name=description]").type("Pour water in a glass. "+ 352 | "Add ice cubes."); 353 | 354 | S("[type=submit]").click(); 355 | 356 | S("h3:contains(Ice Water)").exists(); 357 | S("p:contains(Pour water in a glass. Add ice cubes.)") 358 | .exists() 359 | }); 360 | 361 | 362 | Continue to [building.cookbook Building Cookbook]. -------------------------------------------------------------------------------- /tutorials/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/.DS_Store -------------------------------------------------------------------------------- /tutorials/images/app_organization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/app_organization.png -------------------------------------------------------------------------------- /tutorials/images/app_scaffold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/app_scaffold.png -------------------------------------------------------------------------------- /tutorials/images/contacts_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/contacts_design.png -------------------------------------------------------------------------------- /tutorials/images/contacts_preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/contacts_preview.jpg -------------------------------------------------------------------------------- /tutorials/images/contacts_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/contacts_preview.png -------------------------------------------------------------------------------- /tutorials/images/contacts_widgets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/contacts_widgets.jpg -------------------------------------------------------------------------------- /tutorials/images/coverage1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/coverage1.png -------------------------------------------------------------------------------- /tutorials/images/coverage2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/coverage2.png -------------------------------------------------------------------------------- /tutorials/images/diagram.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/diagram.gif -------------------------------------------------------------------------------- /tutorials/images/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/diagram.png -------------------------------------------------------------------------------- /tutorials/images/diagram_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/diagram_search.png -------------------------------------------------------------------------------- /tutorials/images/diagram_tabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/diagram_tabs.png -------------------------------------------------------------------------------- /tutorials/images/eoa_diagram1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/eoa_diagram1.jpg -------------------------------------------------------------------------------- /tutorials/images/eoa_diagram2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/eoa_diagram2.jpg -------------------------------------------------------------------------------- /tutorials/images/inputs_outputs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/inputs_outputs.jpg -------------------------------------------------------------------------------- /tutorials/images/playermx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/playermx.png -------------------------------------------------------------------------------- /tutorials/images/playermx_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/playermx_overview.png -------------------------------------------------------------------------------- /tutorials/images/playermx_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/playermx_play.png -------------------------------------------------------------------------------- /tutorials/images/playermx_position.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/playermx_position.png -------------------------------------------------------------------------------- /tutorials/images/tabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/tabs.png -------------------------------------------------------------------------------- /tutorials/images/todo_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/todo_arch.png -------------------------------------------------------------------------------- /tutorials/images/todos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitovi/javascriptmvc/c13c2eb7de8dcc6887e47d7a1ce2f117d19febf8/tutorials/images/todos.png -------------------------------------------------------------------------------- /tutorials/installing.md: -------------------------------------------------------------------------------- 1 | @page installing Installing JavaScriptMVC 2 | @parent tutorials 0 3 | 4 | ## Requirements 5 | 6 | JavaScriptMVC requires [Java JRE 1.6](http://www.oracle.com/technetwork/java/javase/downloads/java-se-jdk-7-download-432154.html) or greater for: 7 | 8 | - Compression (Google Closure) 9 | - Running [FuncUnit](http://www.funcunit.com/) tests with [Selenium](http://seleniumhq.org/) 10 | - Easy updating 11 | - Code Generators 12 | 13 | But your backend server can be written in any language. 14 | Download the latest [Java JRE here](http://www.java.com/en/download/index.jsp). 15 | 16 | ## Getting JavaScriptMVC 17 | 18 | There are 2 ways to get JavaScriptMVC: 19 | 20 | - [Downloading](http://javascriptmvc.com/builder.html) 21 | - [developwithgit Installing JavaScriptMVC with Git] 22 | 23 | ## Downloading 24 | 25 | [Download](http://javascriptmvc.com/builder.html) the latest JavaScriptMVC. 26 | Unzip the folder on your file system or web server. 27 | If you are using this on a webserver, 28 | unzip in a public folder where the server hosts static content. 29 | 30 | > TIP: Unzip these files as 31 | high in your apps folder structure as possible (i.e. don't 32 | put them under a javascriptmvc folder in your public directory). 33 | 34 | ## Installing JavaScriptMVC with Git. 35 | 36 | JavaScriptMVC is comprised of 7 sub projects: 37 | 38 | - [https://github.com/bitovi/legacy-steal](http://github.com/bitovi/legacy-steal) 39 | - [https://github.com/bitovi/canjs](https://github.com/bitovi/canjs) 40 | - [https://github.com/bitovi/canui](https://github.com/bitovi/canui) 41 | - [https://github.com/bitovi/jquerypp](https://github.com/bitovi/jquerypp) 42 | - [https://github.com/bitovi/documentjs](http://github.com/bitovi/documentjs) 43 | - [https://github.com/bitovi/legacy-funcunit](http://github.com/bitovi/legacy-funcunit) 44 | - [https://github.com/bitovi/jmvc-generators](https://github.com/bitovi/jmvc-generators) 45 | 46 | You want to fork each project and add it as a submodule to your project 47 | in a public folder (where your server keeps static content). 48 | If these words mean nothing to you, or you'd like more 49 | explanation, you might want to read 50 | [developwithgit Developing With Git]. 51 | 52 | Forking the repos looks like: 53 | 54 | @codestart text 55 | git submodule add git@github.com:_YOU_/legacy-steal.git public/steal 56 | git submodule add git@github.com:_YOU_/canjs.git public/can 57 | git submodule add git@github.com:_YOU_/canui.git public/canui 58 | git submodule add git@github.com:_YOU_/jquerypp.git public/jquerypp 59 | git submodule add git@github.com:_YOU_/documentjs.git public/documentjs 60 | git submodule add git@github.com:_YOU_/legacy-funcunit.git public/funcunit 61 | git submodule add git@github.com:_YOU_/jmvc-generators.git public/jmvc 62 | @codeend 63 | 64 | Notice that CanJS is in can folder and 65 | jQuery++ is in the jquerypp folder. 66 | 67 | After installing the repository, run: 68 | 69 | @codestart 70 | [WINDOWS] > steal\js steal\make.js 71 | 72 | [Lin/Mac] > ./steal/js steal/make.js 73 | @codeend 74 | 75 | ## Verifing the install 76 | 77 | In your public (or static) folder, you should have something that looks like: 78 | 79 | @codestart 80 | static 81 | \documentjs - DocumentJS library 82 | \funcunit - FuncUnit testing library 83 | \canjs - CanJS MVC Framework 84 | \canui - Widgets built on CanJS and jQuery++ 85 | \jquery - jQuery's missing utils and special events 86 | \steal - Compression and build system 87 | \js.bat - Windows Rhino shortcut 88 | \js - Mac/Linux Rhino shortcut 89 | @codeend 90 | 91 | 92 | Open a command line to that folder and run: 93 | 94 | @codestart 95 | [WINDOWS] > js 96 | 97 | [Lin/Mac] > ./js 98 | @codeend 99 | 100 | This starts the [Rhino JS engine](http://www.mozilla.org/rhino/). Type quit() to exit. 101 | 102 | -------------------------------------------------------------------------------- /tutorials/jquerypp.md: -------------------------------------------------------------------------------- 1 | @page tutorials.jquerypp Get Started with jQuery++ 2 | @parent tutorials 4 3 | 4 | jQuery++ is a collection of useful jQuery libraries that provide the 5 | missing functionality necessary to implement and organize large-scale 6 | jQuery applications. It provides low-level utilities for things that 7 | jQuery doesn’t support. 8 | 9 | You can find out everything you need to know about jQuery++ 10 | [at its site.](http://jquerypp.com) jQuery++ is included with 11 | JavaScriptMVC by default in the jquery folder in the root 12 | of your project. -------------------------------------------------------------------------------- /tutorials/mvc.md: -------------------------------------------------------------------------------- 1 | @page mvc Get Started with CanJS 2 | @parent tutorials 3 3 | 4 | [canjs CanJS] is a JavaScript framework that makes 5 | building rich web applications easy and the MVC of 6 | JavaScriptMVC. The library is extremely lightweight 7 | (at only 8.5k minified and compressed) and full featured. 8 | 9 | Everything you want and need to know about CanJS 10 | [ can be found here.](http://canjs.us) You will find documentation 11 | on how to use CanJS, examples of CanJS in action, as well as 12 | a customizable download builder. 13 | 14 | For the purposes of JavaScript MVC, you will not need to download 15 | CanJS, as it is already included in the framework, located in the 16 | can folder in the root of your project. -------------------------------------------------------------------------------- /tutorials/organizing.md: -------------------------------------------------------------------------------- 1 | @page organizing Organizing Your App 2 | @parent tutorials 7 3 | 4 | @body 5 | 6 | The secret to building large apps is to NEVER build 7 | large apps. Break up your applications into small 8 | pieces. Then assemble those testable, bite-sized pieces 9 | into your big application. 10 | 11 | JavaScriptMVC 3.X is built with this pattern in 12 | mind. As opposed to a single flat 'scripts' folder, 13 | JavaScriptMVC breaks up your app into 14 | manageable, isolated modules. This tutorial discusses 15 | the reasons for doing this and patterns for doing it. 16 | 17 | ## Why 18 | 19 | Traditionally JavaScript, CSS and static resources were seen as second-class 20 | citizens when compared to server code. JavaScript was put in a single 21 | flat 'scripts' folder that looked like: 22 | 23 | button.js 24 | jquery.ui.calendar.js 25 | contactmanager.js 26 | tabs.js 27 | jquery.js 28 | nav.js 29 | resizer.js 30 | \test 31 | button_test.js 32 | contactmanager.js 33 | tabs_test.js 34 | nav_test.js 35 | 36 | This was OK for a limited amount of JavaScript; however; client code 37 | increasingly represents a larger percentage of an 38 | app's codebase. What works for 10 files does not work for 100. 39 | 40 | Complicating matters, an individual JavaScript file might have dependencies on 41 | non-JavaScript resources. A menu might need 42 | a specific stylesheet, images, or [can.view client side template]. 43 | 44 | Spreading these dependencies across images, styles, and template folders 45 | makes it more difficult to know what depends on what. Over the lifetime 46 | of an an application, it makes it more likely you'll be loading 47 | resources that are not needed. 48 | 49 | ### The Fix 50 | 51 | JavaScriptMVC gives each resource you author it's own folder. Typically, 52 | the folder will hold the resource, its demo page, test page, 53 | test script, and any other files specific to that resource. 54 | 55 | For example, a tabs folder might look like: 56 | 57 | \tabs 58 | tabs.js - the code for a tabs widget 59 | tabs.html - a demo page 60 | funcunit.html - a test page 61 | tabs_test.js - test code 62 | tabs.css - css for the tab 63 | 64 | The idea is that we can work on tabs.js in complete isolation. 65 | 66 | ## How 67 | 68 | Before we discuss best practices for organizing your application, a little 69 | throat clearing ... 70 | 71 | > Every app is different. Providing a single folder structure for 72 | all applications is impossible. However, there are several useful 73 | patterns that when understood can keep your 74 | application under control. JavaScriptMVC is extremely flexible so use your best judgement! 75 | 76 | This guide walks you through starting with a small-ish example app and where you would add 77 | features over time. Before the example, it's good to know some JavaScript terminology: 78 | 79 | 80 | ### App and Library Folders 81 | 82 | In general, a JavaScriptMVC application is divided into two root folders: an app folder and 83 | library folder. The app folder code typically 'steals' and configures 'library' code. 84 | 85 | #### Application Folder 86 | 87 | The application (or app) folder houses code specific to a particular 88 | application. The code in this folder is very unlikely to be 89 | used in other places. The folder name reflects the name of the application 90 | being built. 91 | 92 | Create an application folder structure with: 93 | 94 | js jmvc\generate\app cms 95 | 96 | 97 | #### Library Folders 98 | 99 | A library folder is for general code that 100 | can be reused across several applications. It is the perfect place for 101 | reusable controls like a tabs widget. Typically folder names reflect 102 | the name of the organization building the controls. 103 | 104 | ### Module Types 105 | 106 | An application is comprised of various modules. JavaScriptMVC's code generators can 107 | be used to create . 108 | 109 | __Model__ - A model represents a set of services. Typically, models exist within 110 | an application folder's `models` directory and are used to request 111 | data. 112 | 113 | Generate a model like: 114 | 115 | js jmvc\generate\model cms\models\image 116 | 117 | __Control__ - A [can.Control] can be a traditional view (a tabs widget) or 118 | a traditional controller (coordinates model and view). Reusable controls are 119 | added to library folders. Controls specific 120 | to an application should be put in a folder within an application folder. 121 | 122 | Generate a controller like: 123 | 124 | js jmvc\generate\control bitovi\tabs 125 | 126 | 127 | __Plugin__ - A plugin is a low-level reusable module such as a special event or dom extension. 128 | It does not typically have a visible component. These should be added to library folders. 129 | 130 | js jmvc\generate\plugin bitovi\range 131 | 132 | 133 | ## Example Application 134 | 135 | The example is a content management system that organizes 'videos', 'images', and 136 | 'articles' under a tabbed layout. For each content type, the user needs 137 | to be able to edit a selected item of that type. 138 | 139 | @image tutorials/cms.png 140 | 141 | 142 | 143 | If the application's name is __cms__ and it is built by __Bitovi__, a basic version's 144 | folder structure might look like: 145 | 146 | 147 | \cms 148 | \models - models for the CMS 149 | \views - views to configure the grid 150 | cms.js 151 | \bitovi 152 | \tabs - a basic tabs widget 153 | \edit - binds a form to edit a model instance 154 | \grid - a configurable grid 155 | \views 156 | 157 | 158 | This basic version assumes that we can configure the grid and edit widget 159 | enough to produces the desired functionality. In this case, 160 | cms/cms.js might look like: 161 | 162 | // load dependencies 163 | steal('bitovi/tabs', 164 | 'bitovi/grid', 165 | 'bitovi/create', 166 | './models/image.js', 167 | './models/video.js', 168 | './models/article.js', 169 | function( 170 | Tabs, Grid, Create, 171 | Image, Video, Article 172 | ){ 173 | 174 | // add tabs to the page 175 | var tabs = new Tabs('#tabs'); 176 | 177 | // Configure the video grid 178 | var videos = new Grid($videos, { 179 | model: Cms.Models.Video, 180 | view: "//cms/views/videos.ejs" 181 | }), 182 | videoEdit = new Edit('#videoEdit') 183 | 184 | // listen for when a video is selected 185 | videos.element.on('selected','li', 186 | function(ev, video){ 187 | // update the edit form with the selected 188 | // video's attributes 189 | videoEdit.update(video); 190 | } 191 | ); 192 | 193 | // Do the same for images and articles 194 | var images = new Bitovi.Grid('#images', { 195 | model: Cms.Models.Image, 196 | view: "//cms/views/images.ejs" 197 | }), 198 | imageEdit = new Edit('#imageEdit'); 199 | 200 | images.element.on('selected','li', 201 | function(ev, image){ 202 | imageEdit.update(video); 203 | } 204 | ); 205 | 206 | var articles = new Grid('#articles', { 207 | model: Article, 208 | view: "//cms/views/article.ejs" 209 | }), 210 | articleEdit = new Edit('#articleEdit'); 211 | 212 | articles.element.on('selected','li', 213 | function(ev, article){ 214 | articleEdit.update(video); 215 | } 216 | ); 217 | 218 | }) 219 | 220 | Notice that the cms.js configures the grid and edit widgets with 221 | the cms folder's models and views. This represents an ideal separation between 222 | app specific code and reusable widgets. However, it's extremely rare that 223 | widgets are able to provide all the functionality an app needs to meet its 224 | requirements. 225 | 226 | ### More complexity 227 | 228 | Eventually, you won't be able to configure abstract widgets to satisfy 229 | the requirements of your application. For example, you might need to 230 | add specific functionality around listing and editing videos (such as a thumbnail editor). 231 | 232 | This is application specific functionality and belongs 233 | in the application folder. We'll encapsulate it in a controller [can.Control] for each type: 234 | 235 | \cms 236 | \articles - the articles tab 237 | \images - the images tab 238 | \videos - the videos tab 239 | \models 240 | \views 241 | cms.js 242 | \bitovi 243 | \thumbnail 244 | \tabs 245 | \edit 246 | \grid 247 | \views 248 | 249 | cms/cms.js now looks like: 250 | 251 | steal('cms/articles', 252 | 'cms/images', 253 | 'cms/videos', 254 | 'bitovi/tabs', 255 | function( 256 | Articles, Images, Videos, Tabs 257 | ){ 258 | 259 | new Tabs('#tabs'); 260 | 261 | // add the video grid 262 | new Videos('#videos'); 263 | 264 | // Do the same for images and articles 265 | new Images('#images'); 266 | new Articles('#articles'); 267 | 268 | }) 269 | 270 | cms/articles/articles.js might look like: 271 | 272 | steal('can', 273 | 'bitovi/grid', 274 | 'bitovi/edit', 275 | './init.ejs', 276 | './article.ejs', 277 | 'bitovi/models/article.js', 278 | function(can, 279 | Grid, Edit, 280 | initEJS, articleEJS, 281 | Article){ 282 | 283 | return can.Control({ 284 | 285 | init : function(){ 286 | // draw the html for the tab 287 | this.element.html(initEJS({})); 288 | 289 | // configure the grid 290 | new Grid(this.find('.grid'), { 291 | model: Article, 292 | view: articleEJS 293 | }) 294 | 295 | this.editor = new Edit(".edit") 296 | }, 297 | 298 | // when the grid triggers a select event 299 | "li select" : function(el, ev, article){ 300 | this.editor.update(article) 301 | } 302 | }); 303 | 304 | }); 305 | 306 | ### Adding leaves to the tree 307 | 308 | In the previous example, we moved most of the code in cms/cms.js into 309 | an articles, images, and videos plugin. Each of these plugins should 310 | work independently from each other, have it's own tests and demo page. 311 | 312 | Communication between these high-level 313 | controls should be configured in cms/cms.js. 314 | 315 | Essentially, as your needs become more specific, you are encouraged to 316 | nest plugins within each other. 317 | 318 | In this example, after separating out each type into it's own plugin, you might 319 | want to split the type into edit and grid controls. The resulting 320 | folder structure would look like: 321 | 322 | \cms 323 | \articles 324 | \grid 325 | \edit 326 | \images 327 | \grid 328 | \edit 329 | \videos 330 | \grid 331 | \edit 332 | \models 333 | \views 334 | cms.js 335 | \bitovi 336 | \thumbnail 337 | \tabs 338 | \edit 339 | \grid 340 | 341 | 342 | `cms/articles/articles.js` would look the same, except it would 343 | change __Grid__ and __Edit__ to point to `cms/articles/grid` and 344 | `cms/articles/edit`: 345 | 346 | 347 | steal('can', 348 | 'cms/articles/grid', 349 | 'cms/articles/edit', 350 | './init.ejs', 351 | './article.ejs', 352 | 'bitovi/models/article.js', 353 | function(can, 354 | Grid, Edit, 355 | initEJS, articleEJS, 356 | Article){ 357 | ... 358 | }) 359 | 360 | JavaScriptMVC encourages you to organize your application folder as a tree. 361 | The leaves of the tree are micro-controls that perform a specific task (such as 362 | allowing the editing of videos). 363 | 364 | Higher-order controls (`cms/articles/articles.js`) combine leaves and other nodes 365 | into more complex functionality. The root of the application is the application file 366 | (`cms/cms.js`). It combines and configures all high-level widgets. 367 | 368 | Communication between modules is done with the observer 369 | pattern ([can.compute] or [can.Observe]) or with events. 370 | 371 | With events, low-level controls use `$.fn.trigger` to send messages 'up' to higher-order 372 | controls. Higher-order controls typically call methods on lower-level controls 373 | 374 | The Articles control listening to a 'select' event produced by 375 | Grid and creating (or updating) the Edit control 376 | is a great example of this. 377 | 378 | The situation where this breaks down is usually when a 'state' needs to be shared and communicated 379 | across several controls. [can.compute] and [can.Observe] are useful 380 | for this situation. 381 | 382 | ## Conclusion 383 | 384 | This is an extremely abstract article, but hopefully illustrates a few 385 | important trends of JavaScriptMVC organization: 386 | 387 | - Put code specific to an app in the application folder. 388 | - Put reusable plugins, widgets, and other code into library folders. 389 | - Fill out the tree. 390 | -------------------------------------------------------------------------------- /tutorials/rapidstart/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tutorials/rapidstart/todos.ejs: -------------------------------------------------------------------------------- 1 | <% this.each(function(todo){ %> 2 |
  • el.data('todo', todo) %> class='todo'> 3 | <%= todo.attr('name') %> 4 | X 5 |
  • 6 | <% }) %> -------------------------------------------------------------------------------- /tutorials/rapidstart/todos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /tutorials/rapidstart/todos.js: -------------------------------------------------------------------------------- 1 | steal('can', 2 | './todos.ejs', 3 | 'can/util/fixture', 4 | function(can, todosEJS){ 5 | 6 | Todo = can.Model({ 7 | findAll : "GET /todos", 8 | findOne : "GET /todos/{id}", 9 | create : "POST /todos", 10 | update : "PUT /todos/{id}", 11 | destroy : "DELETE /todos/{id}" 12 | }, 13 | {}); 14 | 15 | // our list of todos 16 | var TODOS = [ 17 | {id: 1, name: "wake up"}, 18 | {id: 2, name: "take out trash"}, 19 | {id: 3, name: "do dishes"} 20 | ]; 21 | can.fixture({ 22 | // findAll 23 | "GET /todos": function(){ 24 | return TODOS 25 | }, 26 | // findOne 27 | "GET /todos/{id}": function(orig){ 28 | return TODOS[(+orig.data.id)-1]; 29 | }, 30 | // create 31 | "POST /todos": function(request){ 32 | TODOS.push(request.data); 33 | return {id: TODOS.length} 34 | }, 35 | // update 36 | "PUT /todos/{id}": function(){ 37 | return {}; 38 | }, 39 | // destroy 40 | "DELETE /todos/{id}": function(){ 41 | return {}; 42 | } 43 | }); 44 | 45 | // THE CONTROLLERS 46 | Todos = can.Control({ 47 | init: function( element ){ 48 | Todo.findAll({}, function(todos){ 49 | element.html( todosEJS( todos ) ); 50 | }); 51 | }, 52 | "li click": function(li){ 53 | li.trigger('selected', li.data('todo') ); 54 | }, 55 | "li .destroy click": function(el, ev){ 56 | // get the li element that has the model 57 | var li = el.closest('li'); 58 | 59 | // get the model and destroy it 60 | li.data('todo').destroy(); 61 | } 62 | }) 63 | 64 | Editor = can.Control({ 65 | todo: function(todo){ 66 | this.options.todo = todo; 67 | this.on(); 68 | this.setName(); 69 | }, 70 | // a helper that sets the value of the input 71 | // to the todo's name 72 | setName: function(){ 73 | this.element.val(this.options.todo.name); 74 | }, 75 | // listen for changes in the todo 76 | // and update the input 77 | "{todo} updated" : function(){ 78 | this.setName(); 79 | }, 80 | // when the input changes 81 | // update the todo instance 82 | "change" : function(){ 83 | var todo = this.options.todo 84 | todo.attr('name',this.element.val() ) 85 | todo.save(); 86 | } 87 | }); 88 | 89 | Routing = can.Control({ 90 | init : function(){ 91 | this.editor = new Editor("#editor") 92 | new Todos("#todos"); 93 | }, 94 | // the index page 95 | "route" : function(){ 96 | $("#editor").hide(); 97 | }, 98 | "todos/:id route" : function(data){ 99 | $("#editor").show(); 100 | Todo.findOne(data, $.proxy(function(todo){ 101 | this.editor.todo(todo); 102 | }, this)) 103 | }, 104 | ".todo selected" : function(el, ev, todo){ 105 | can.route.attr('id',todo.id); 106 | } 107 | }); 108 | 109 | // create routing controller 110 | new Routing(document.body); 111 | 112 | 113 | 114 | }) -------------------------------------------------------------------------------- /tutorials/rapidstart/todos_test.js: -------------------------------------------------------------------------------- 1 | steal('funcunit', function(S){ 2 | 3 | module('todos', { 4 | setup: function(){ 5 | S.open("//tutorials/rapidstart/todos.html"); 6 | } 7 | }) 8 | 9 | test('edit first todo', function(){ 10 | S(".todo:first").click(); 11 | S("#editor").val("wake up", "First Todo added correctly"); 12 | }) 13 | 14 | }) -------------------------------------------------------------------------------- /tutorials/rootfolder.md: -------------------------------------------------------------------------------- 1 | @page rootfolder Root Folder 2 | 3 | The root folder is the folder where JavaScriptMVC is installed. This is the parent 4 | folder of the `steal`, `can`, etc folder. For example: 5 | 6 | ROOT/ 7 | can/ 8 | jquery/ 9 | steal/ 10 | documentjs/ 11 | 12 | Typically, the root folder should be a public folder that serves static content. It's often named something 13 | like `static` or `public` depending on what server and system setup you have. 14 | 15 | By default, `steal(moduleId)`, references files from the root folder. The following loads 16 | `ROOT/foo/bar.js`: 17 | 18 | steal('foo/bar.js') 19 | 20 | Paths that begin with `"//"` also reference the root folder. The following 21 | loads `ROOT/views/bar.ejs 22 | 23 | $('#foo').html('//views/bar.ejs',{}) 24 | -------------------------------------------------------------------------------- /tutorials/services.md: -------------------------------------------------------------------------------- 1 | @page services Ajax Service Guidelines 2 | @parent tutorials 9 3 | 4 | JavaScriptMVC's flexibility allows it to 5 | be used with almost any service layer. However, 6 | this guide details suggests a service layer design 7 | that minimizes the amount of extra work to get JavaScriptMVC running. 8 | 9 | In general, the service layer should be as 10 | thin as possible and reflect the Database 11 | queries and results the server must make 12 | to get the data. This keeps things flexible 13 | from the client's perspective. 14 | 15 | ## JSON Rest Part 1 16 | 17 | The best over-all service layer can be described as JSON REST. 18 | 19 | JSON is used as the data received and sometimes sent to the server. 20 | 21 | [REST](http://en.wikipedia.org/wiki/Representational_state_transfer Representational State Transfer) is 22 | where there are resource urls that are 23 | modified with GET POST PUT and DELETE methods. 24 | 25 | A brief example is a service API for messages. The server might expose the 26 | following METHOD and URLS: 27 | 28 | GET /messages - gets an array of messages from the server. 29 | GET /messages/{id} - gets a single message from the server. 30 | POST /messages - creates a message from the server. 31 | PUT /messages/{id} - updates a message from the server. 32 | DELETE /messages/{id} - destroys a message from the server. 33 | 34 | ## Query String Params 35 | 36 | Before going into detail about what each 37 | METHOD URL does, it's worth 38 | quickly describing how parameters are 39 | passed to the query string. [can.Model] passes 40 | parameters to your framework of choices ajax handler 41 | and that gets converted by [can.param]. 42 | For example, if we wanted 43 | all messages for a given user, 44 | sorted first by date, then by the users's name, 45 | we might call something like: 46 | 47 | $.get('/messages',{ 48 | userId: 5, 49 | order: ['createdAt ASC','user.name ASC'] 50 | }) 51 | 52 | Which produces: 53 | 54 | @codestart text 55 | GET /messages? 56 | userId=5& 57 | order%5B%5D=createdAt+ASC& 58 | order%5B%5D=user.name+ASC 59 | @codeend 60 | 61 | Lets walk through each REST service example. 62 | 63 | ## GET /messages 64 | 65 | A request to GET /messages should return 66 | all message records visible to the 67 | requesting user. The data should look like: 68 | 69 | { 70 | "data": [ 71 | { 72 | "id" : 1, 73 | "fromUserId": 921, 74 | "text": "Hello World", 75 | "createdAt" : 1024324214123 76 | }, 77 | { 78 | "id" : 2, 79 | "fromUserId": 923, 80 | "text": "Goodnight World", 81 | "createdAt" : 23524365346543 82 | }, 83 | ... 84 | ], 85 | "count": 100 86 | } 87 | 88 | Where: 89 | 90 | - data - has an array of objects (in this case 100), 91 | each object contains the data for a single message. 92 | - count - lists the number of items that 93 | would be returned if a limit was not used. In this 94 | case, no limit was used so count matches the number of items. 95 | 96 | 97 | GET /messages will typically take 98 | arguments passed in as name=value parameters 99 | in the query string like: 100 | 101 | > GET /messages?limit=10&offset=20&order[]=createdAt+DESC 102 | 103 | Common name / values are: 104 | 105 | - limit - the total number of items to return 106 | - offset - the position in the 'total' set to start returning items 107 | - order - an array of 'NAME SORTORDER' pairs 108 | 109 | 110 | You'll notice that these properties can effectively be sent straight away to a DB query. 111 | 112 | #### Relationships 113 | 114 | Often, you want to get all data for a particular item. For example, 115 | all messages from user 52. Instead of requesting something like: 116 | 117 | > GET /users/52/messages 118 | 119 | A request should be made to: 120 | 121 | > GET /messages?fromUserId=52 122 | 123 | The service should limit messages to only those where fromUserId = 52. 124 | 125 | ### Related Data 126 | 127 | Another common problem is when, for 128 | performance reasons, you want the 'joined' 129 | data for a particular field. For example, 130 | when getting messages, you might want to also 131 | want to get the user data from fromUserId. 132 | 133 | In this case, we encourage the use 134 | of an 'include' option which 135 | signifies including additional data like: 136 | 137 | > GET /messages?include[]=fromUser 138 | 139 | The fromUser data will be added to each message object like: 140 | 141 | { 142 | "data": [ 143 | { 144 | "id" : 1, 145 | "fromUserId": 921, 146 | "text": "Hello World", 147 | "createdAt" : 1024324214123, 148 | "fromUser": { 149 | "id" : 921, 150 | "name" : "Justin Meyer" 151 | } 152 | }, 153 | ... 154 | ], 155 | "count": 100 156 | } 157 | 158 | ## GET /messages/{id} 159 | 160 | Gets a single item from the server. It should return just the JSON data for the object like: 161 | 162 | ->{ 163 | "id" : 1, 164 | "fromUserId": 921, 165 | "text": "Hello World", 166 | "createdAt" : 1024324214123 167 | } 168 | 169 | 170 | ## POST /messages 171 | 172 | Creates a message on the server. Typically, 173 | the body of this request is JSON data that 174 | looks exactly like the data from a GET request, 175 | but without the id property or any properties 176 | the server might add. For example, I can 177 | create a message by sending: 178 | 179 | POST /messages 180 | { 181 | "fromUserId": 921, 182 | "text": "A new message" 183 | } 184 | 185 | The server is going to add the id and createdAt property and should return those as JSON in the response: 186 | 187 | ->{ 188 | "id": 22, 189 | "createdAt": 1224324214123 190 | } 191 | 192 | ## PUT /messages/{id} 193 | 194 | This updates a resource. Similar to POST, 195 | the body should be JSON that matches what 196 | the data from a GET request looks 197 | like. However, only fields that are changing 198 | are necessary to send. For example, we we 199 | want to update the text of a message: 200 | 201 | PUT /messages/22 202 | { 203 | 'text': "An updated EVIL message" 204 | } 205 | 206 | The response should have any fields that were 207 | modified or adjusted on the server. For 208 | example, the server might filter the word 209 | "EVIL" out of messages and be updating some 210 | 'updatedAt' property. It should return: 211 | 212 | ->{ 213 | 'text' : "An updated message", 214 | 'updatedAt' : 123254356346241 215 | } 216 | 217 | If no filtering or modifying of other changes 218 | happened, the server can just return 219 | an empty object: {}. 220 | 221 | ## DELETE /messages/{id} 222 | 223 | Destroys a resource from the server. 224 | 225 | ## Sending Dates 226 | 227 | The best way of sending dates is an integer representing the Julian date like: 228 | 229 | createdAt: 12313123133423 230 | 231 | Where we can convert that easily to a JavaScript date like 232 | 233 | new Date(123123133423) 234 | 235 | ## CUD Multiple Items with a Single Request 236 | 237 | Often, you want to create, update, or delete items with a single request. This is 238 | most often done with [can.Model.List]. 239 | 240 | 241 | ## Handling Errors 242 | 243 | When an error happens, make sure your server sends back the 244 | proper HTTP status code. The response body should be a JSON object with 245 | property names mapped to an array of errors: 246 | 247 | { 248 | email : ["Formatting is incorrect","No email is provided"] 249 | } 250 | 251 | -------------------------------------------------------------------------------- /tutorials/tutorials.md: -------------------------------------------------------------------------------- 1 | @page tutorials Tutorials 2 | @parent javascriptmvc 5 3 | 4 | @description Tutorials on JavaScriptMVC. 5 | 6 | This is where your learning starts. Go through these tutorials to learn JavaScriptMVC basics. Also check out these [examples example apps] 7 | 8 | ### [installing Installing JavaScriptMVC] 9 | 10 | Learn how to install JavaScriptMVC. This is a prerequisite 11 | for most of the other tutorials. 12 | 13 | ### [rapidstart Rapid Start] 14 | 15 | Want to take JMVC for a test-drive? Start here. 16 | 17 | ### [getstarted Get Started with JavaScriptMVC] 18 | 19 | Build, minify, test, and document a basic recipe application. This tutorial 20 | covers the major components of JavaScriptMVC. 21 | 22 | ### [mvc Get started with CanJS] 23 | 24 | A walkthrough of the MVC parts of JavaScript MVC. This is a must read if you 25 | want to understand how they work together. 26 | 27 | ### [tutorials.jquerypp Get started with jQuery++] 28 | 29 | jQuery++ is a collection of useful jQuery libraries that provide the missing 30 | functionality necessary to implement and organize large-scale jQuery applications. 31 | 32 | ### [funcunit.getstarted Get Started with FuncUnit] 33 | 34 | Uses [FuncUnit] to write functional tests for the jQuery UI 35 | autocomplete widget. 36 | 37 | ### [organizing Organizing Your App] 38 | 39 | From a small to large projects, 40 | covers the best way of organizing a JavaScriptMVC application and 41 | scaling it to meet future needs. 42 | 43 | ### [ajaxy Searchable Ajax Apps] 44 | 45 | Build a simple widget 46 | that loads content with ajax. This walkthrough demonstrates how to make 47 | a site Google crawlable and searchable. 48 | 49 | ### [services Ajax Service Guidelines] 50 | 51 | This guide details suggests a service layer design that minimizes the amount of extra work to get JavaScriptMVC running. 52 | 53 | ### [migrate Migrating from 3.0 and 3.1] 54 | 55 | This guide outlines the things you have to look at when upgrading from version 3.0 or 3.1. 56 | 57 | ### [done Migrating from 3.2 to 3.3] 58 | 59 | This guide outlines the API changes for moving a project from version 3.2. 60 | --------------------------------------------------------------------------------