├── .gitignore ├── CHANGELOG.md ├── README.md ├── docpad.coffee ├── generate_production.sh ├── package-lock.json ├── package.json ├── plugins └── sproutdown │ ├── package.json │ └── src │ └── sproutdown.plugin.coffee └── src ├── documents ├── adding_unit_test.html.md.sd ├── animate.html.md.sd ├── build_tools.html.md.sd ├── chance.html.md.sd ├── commit_code.html.md.sd ├── connect_server.html.md.sd ├── contribute.html.md.sd ├── core_concepts_kvo.html.md.sd ├── core_concepts_sc_object.html.md.sd ├── credits.html.md.sd ├── data_source.html.md.sd ├── documentation_guidelines.html.md.sd ├── enumerables.html.md.sd ├── fixtures.html.md.sd ├── getting_started.html.md.sd ├── getting_started_2.html.md.sd ├── getting_started_3.html.md.sd ├── index.html ├── no_store.html.md.sd ├── record_lifecycle.html.md.sd ├── records.html.md.sd ├── run_loop.html.md.sd ├── running_unit_tests.html.md.sd ├── style_guide.html.md.sd ├── test.html.md.sd ├── test2.html.md.sd ├── testing_guidelines.html.md.sd ├── theming_app.html.md.sd ├── todos_tdd.html.md.sd ├── unit_test_framework.html.md.sd ├── views.html.md.sd └── writing_unit_tests.html.md.sd ├── files ├── images │ ├── animate │ │ ├── ease_in_graph.png │ │ ├── ease_in_out_graph.png │ │ ├── ease_out_graph.png │ │ ├── linear_graph.png │ │ └── xyz.png │ ├── credits │ │ ├── dtorres.jpg │ │ ├── eocean.jpg │ │ ├── fkugler.jpg │ │ ├── gdonaldson.jpg │ │ ├── jzimdars.jpg │ │ ├── mball.jpg │ │ ├── mikeatkins.jpg │ │ ├── pbergstrom.jpg │ │ ├── psarnacki.jpg │ │ ├── pwagenet.jpg │ │ ├── ssmith.jpg │ │ ├── tdale.jpg │ │ ├── tfangio.jpg │ │ ├── tkeating.jpg │ │ ├── unknown.jpg │ │ ├── vimtarnasan.jpg │ │ └── ykatz.jpg │ ├── footer │ │ ├── dot.png │ │ ├── facebook.png │ │ ├── google.png │ │ ├── html5_tech.png │ │ ├── sc_logo_medium.png │ │ ├── top.png │ │ └── twitter.png │ ├── getting_started │ │ └── todos_three_screen_capture.png │ ├── graphics │ │ ├── chapters.png │ │ ├── check.png │ │ ├── code.png │ │ ├── containergradient.png │ │ ├── contribute.png │ │ ├── credits.png │ │ ├── guide-button.png │ │ ├── guidegradient.png │ │ ├── guides.png │ │ ├── notes.png │ │ ├── paperandpencil.png │ │ ├── pin.png │ │ ├── sidebar-gradient.png │ │ ├── starperson.png │ │ ├── tab-code.png │ │ ├── tab-notes.png │ │ ├── tab-pin.png │ │ ├── tab-warning.png │ │ └── warning.png │ ├── header │ │ ├── glow.png │ │ ├── logo.png │ │ ├── pixels.png │ │ └── search.png │ ├── html_based │ │ ├── bindings.graffle │ │ └── bindings.png │ ├── records │ │ ├── busy_substates.png │ │ ├── destroyed_substates.png │ │ ├── diagrams.graffle │ │ ├── ready_substates.png │ │ └── record_anatomy.png │ ├── run_loop_diagram.jpg │ ├── testing │ │ ├── sample_sc_test_screenshot.png │ │ ├── screenshot_1st_test.png │ │ ├── unit_test_1.png │ │ ├── unit_test_2.png │ │ ├── unit_test_3.png │ │ └── unit_test_4.png │ └── theming │ │ ├── button-active.png │ │ ├── button.png │ │ ├── button_slice.jpg │ │ └── webinspector-myapp.png ├── javascripts │ └── jquery.min.js └── stylesheets │ ├── guides.css │ ├── highlight-github.css │ ├── main.css │ ├── overrides.print.css │ ├── overrides.style.css │ ├── print.css │ └── reset.css └── layouts ├── credits_layout.html.eco ├── default.html.eco └── home.html.eco /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .bundle 3 | .docpad.db 4 | node_modules 5 | out 6 | *.swp 7 | *.swo 8 | .avatar_cache.json 9 | /.idea 10 | .docpad.db 11 | *.log 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## core_concepts.textile 4 | 5 | * January 12, 2011: initial partial version by "Peter Wagenet":credits.html#pwagenet 6 | * January 19, 2011: further updates by "Peter Wagenet":credits.html#pwagenet 7 | * January 20, 2011: corrections to "The init Method" and "The Run Loop" by "Peter Wagenet":credits.html#pwagenet 8 | * January 24, 2011: added section on "Bindings and Chained Property Paths" by "Peter Wagenet":credits.html#pwagenet 9 | * March 2, 2011: fixed paragraph formmatting by "Topher Fangio":credits.html#topherfangio 10 | * March 2, 2011: fixed grammar, clarified phrasing, added examples to the Observer section by "Jason Gignac":credits.html#jgignac 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SproutCore Guides 2 | ================= 3 | 4 | These are the official guides for the [SproutCore](http://www.sproutcore.com) framework. You can see them live at the [SproutCore Guides 5 | homepage](http://guides.sproutcore.com). 6 | 7 | This document contains a brief overview of how to contribute changes and additioins to the SproutCore Guides. For an in-depth explanation, see the 8 | [Contributing Guide](http://guides.sproutcore.com/contribute.html). 9 | 10 | ## Getting the Source Code 11 | 12 | 1. [Fork](https://help.github.com/articles/fork-a-repo) the [SproutCore Guides repository](https://github.com/sproutcore/guides) on [GitHub](http://github.com). 13 | 14 | 2. Clone the fork on to your local machine. 15 | ```bash 16 | $ git clone git://github.com//guides.git 17 | ``` 18 | 19 | 3. Add the main SproutCore Guides repository as a remote so you can fetch updates. 20 | ```bash 21 | $ cd guides 22 | $ git remote add upstream git://github.com/sproutcore/guides.git 23 | ``` 24 | 25 | ## Installing Prerequisites 26 | 27 | The SproutCore guides use the [Node](http://nodejs.org/)-powered [DocPad](http://docpad.org/) framework. In order to contribute to the guides, 28 | you will first need to install Node from [http://nodejs.org/](http://nodejs.org/). 29 | 30 | Next, install the docpad framework (note: you may need to use `sudo` for this): 31 | 32 | ```bash 33 | $ npm install -g docpad 34 | ``` 35 | 36 | Finally, change into the guides directory and install the necessary development dependencies: 37 | 38 | ```bash 39 | $ cd ~/my/dev/directory/sproutcore/guides 40 | $ npm install 41 | ``` 42 | 43 | You should see a plethora of output similar to the following: 44 | 45 | ... 46 | npm http 304 https://registry.npmjs.org/keypress 47 | npm http 304 https://registry.npmjs.org/readable-stream 48 | npm http 304 https://registry.npmjs.org/underscore 49 | npm http 304 https://registry.npmjs.org/qs/0.6.5 50 | npm http 304 https://registry.npmjs.org/commander/0.6.1 51 | npm http 304 https://registry.npmjs.org/bal-util 52 | npm http 304 https://registry.npmjs.org/range-parser/0.0.4 53 | ... 54 | 55 | ## Before Making Changes 56 | 57 | Before you make any changes, you'll want to pull in any upstream changes and create a new topic branch for your changes. 58 | 59 | ```bash 60 | $ git checkout master 61 | $ git pull 62 | $ git checkout -b 63 | ``` 64 | 65 | ## Create the Avatar Cache 66 | 67 | Our sproutdown plugin will attempt to cache the avatars so that we do not hit GitHub more than necessary. Run the following 68 | command to initialize the file. 69 | 70 | ```bash 71 | echo '{}' > plugins/sproutdown/src/.avatar_cache.json 72 | ``` 73 | 74 | ## Creating or Modifying a Guide 75 | 76 | Guides reside in the `src` directory and are written in a modified version of the [Markdown](http://daringfireball.net/projects/markdown/) markup 77 | language. The best way to familiarize yourself with how to use Markdown is to check out the other guides. 78 | 79 | Docpad makes it easy to see what the guides on your local machine will look like when they're published to 80 | [guides.sproutcore.com](http://guides.sproutcore.com). To preview your changes, run the following command from the `guides` directory: 81 | 82 | ```bash 83 | $ docpad run 84 | ``` 85 | 86 | Now point a web browser to [localhost:9778](http://localhost:9778). This preview will keep itself updated as you make changes to the guides; all you have to do is refresh 87 | your browser window to see your latest changes. 88 | 89 | ## Submitting Your Changes 90 | 91 | You can get your changes and additions included by creating a [pull request](https://help.github.com/articles/using-pull-requests) on GitHub using the 92 | topic branch you created above. 93 | 94 | ## Deploying a Release 95 | 96 | In order to deploy a release, you'll need to have the 97 | [sproutcore-guides.github.com](https://github.com/sproutcore-guides/sproutcore-guides.github.com) repository checked out 98 | into a local directory. All you need to do is use DocPad to generate the guides, copy that to the checked out live repo 99 | and then commit and push. It should looks something like this: 100 | 101 | ./generate_production.sh 102 | cd ../sproutcore-guides.github.com 103 | git status 104 | git commit -am "New release of guides..." 105 | git push 106 | 107 | ## More Information 108 | 109 | For more information on the SproutCore Guides, including a more in depth look at committing your additions and changes, see the [contributing 110 | guide](http://guides.sproutcore.com/contribute.html). 111 | 112 | If you have any questions, the team can be reached at [@sproutcore](http://twitter.com/#!/sproutcore) on Twitter, in the 113 | [#sproutcore](irc://irc.freenode.net/sproutcore) IRC channel on [Freenode](http://freenode.net/), or at the [SproutCore Google 114 | Group](http://groups.google.com/group/sproutcore). 115 | -------------------------------------------------------------------------------- /docpad.coffee: -------------------------------------------------------------------------------- 1 | # DocPad Configuration File 2 | # http://docpad.org/docs/config 3 | 4 | # Define the DocPad Configuration 5 | docpadConfig = { 6 | # ... 7 | } 8 | 9 | # Export the DocPad Configuration 10 | module.exports = docpadConfig -------------------------------------------------------------------------------- /generate_production.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf out/ && rm .docpad.db && docpad generate -e production && rsync -av out/ ../sproutcore-guides.github.com 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sproutcore-guides", 3 | "description": "The Docpad version of the SproutCore guides.", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/sproutcore/guides" 7 | }, 8 | "version": "0.1.0", 9 | "engines": { 10 | "node": "0.10", 11 | "npm": "1.2" 12 | }, 13 | "dependencies": { 14 | "docpad": "~6", 15 | "docpad-plugin-eco": "~2.3.0", 16 | "docpad-plugin-marked": "~2.5.0", 17 | "docpad-plugin-highlightjs": "~2.6.0" 18 | }, 19 | "main": "node_modules/docpad/bin/docpad-server", 20 | "scripts": { 21 | "start": "node_modules/docpad/bin/docpad-server" 22 | }, 23 | "devDependencies": {} 24 | } 25 | -------------------------------------------------------------------------------- /plugins/sproutdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docpad-plugin-sproutdown", 3 | "version": "2.0.0", 4 | "description": "DocPad plugin that adds the ability to render SproutCore specific Markdown to standard Markdown.", 5 | "homepage": "https://github.com/sproutcore/docpad-plugin-sproutdown", 6 | "keywords": [ 7 | "docpad", 8 | "docpad-plugin", 9 | "markdown", 10 | "sproutdown" 11 | ], 12 | "author": "Copyright Topher Fangio (https://github.com/topherfangio)", 13 | "maintainers": [ 14 | "Topher Fangio (https://github.com/topherfangio)" 15 | ], 16 | "contributors": [ 17 | "Topher Fangio (https://github.com/topherfangio)" 18 | ], 19 | "bugs": { 20 | "url": "https://github.com/sproutcore/docpad-plugin-sproutdown/issues" 21 | }, 22 | "repository" : { 23 | "type": "git", 24 | "url": "https://github.com/sproutcore/docpad-plugin-sproutdown.git" 25 | }, 26 | "engines" : { 27 | "node": ">=0.8", 28 | "docpad": ">=6" 29 | }, 30 | "main": "./src/sproutdown.plugin.coffee" 31 | } 32 | -------------------------------------------------------------------------------- /plugins/sproutdown/src/sproutdown.plugin.coffee: -------------------------------------------------------------------------------- 1 | fs = require('fs') 2 | https = require('https') 3 | 4 | # Create a cache so that we don't hit the GitHub API rate limit 5 | avatar_cache = {} 6 | try 7 | avatar_cache = require(__dirname + '/.avatar_cache.json') 8 | catch err 9 | console.log(" *** Couldn't load cache file: ", err) 10 | 11 | console.log(' *** Avatar cache at start is: ', avatar_cache) 12 | 13 | # Reset the cache if it's been more than an hour since it was saved 14 | now = new Date() 15 | if now.getTime() > ['timestamp'] + 1000 * 3600 16 | console.log(" *** Avatar cache is older than 1 hour; regenerating...") 17 | avatar_cache = {} 18 | 19 | # Export Plugin 20 | module.exports = (BasePlugin) -> 21 | # Define Plugin 22 | class SproutdownPlugin extends BasePlugin 23 | # Plugin Name 24 | name: 'sproutdown' 25 | 26 | # 27 | # Render our special code 28 | # 29 | render: (opts) -> 30 | # 31 | # Prepare 32 | # 33 | {inExtension,outExtension,file} = opts 34 | 35 | # 36 | # Ensure we are parsing a sproutdown file 37 | # 38 | if inExtension in ['sd', 'sproutdown'] and outExtension in ['md', null] 39 | 40 | # 41 | # Replace & save all code blocks so we can re-replace them at the end 42 | # 43 | code_blocks = [] 44 | opts.content = opts.content.replace(/```(\s|.)*?```/g, (a)-> 45 | code_blocks.push(a) 46 | "CODE_REPLACEMENT" 47 | ) 48 | 49 | # 50 | # Substitute '+something+' for 'something' 51 | # 52 | opts.content = opts.content.replace(/\+(.*?)\+/g, "$1") 53 | 54 | # 55 | # Surround anything following 'NOTE:' (and two newlines) with a "
...
" block 56 | # 57 | opts.content = opts.content.replace(/NOTE(:| )((.|\s)*?\n\n)/g, "\n\n

$2

\n\n") 58 | 59 | # 60 | # Surround anything following 'INFO:' or 'TIP:' (and two newlines) with a "
...
" block 61 | # 62 | opts.content = opts.content.replace(/(INFO|TIP)(:| )((.|\s)*?\n\n)/g, "\n\n

$3

\n\n") 63 | 64 | # 65 | # Surround anything following 'WARNING:' (and two newlines) with a "
...
" block 66 | # 67 | opts.content = opts.content.replace(/WARNING(:| )((.|\s)*?\n\n)/g, "\n\n

$2

\n\n") 68 | 69 | # 70 | # Add super-basic support for Markdown tables 71 | # 72 | opts.content = opts.content.replace(/\n\|/g, "\n\n
") 73 | opts.content = opts.content.replace(/\|\n/g, "
\n\n") 74 | opts.content = opts.content.replace(/[ ]?\|[ ]?/g, "") 75 | 76 | # Merge any tables connected only by white-space 77 | opts.content = opts.content.replace(/<\/table>\s*/g, "\n") 78 | 79 | # Check for table headers 80 | opts.content = opts.content.replace(/") 81 | 82 | # 83 | # Add numbers to the headers and handle table of contents (h3/h4 only) 84 | # 85 | h3_count = h4_count = h5_count = h6_count = 1 86 | toc = [] 87 | opts.content = opts.content.replace(/(###)+?(.*?\n)/g, (match, p1, p2, offset, total_string) -> 88 | heading = p2.replace(/^#*/, '') 89 | dasherized_heading = heading.replace(/[^a-zA-Z0-9]/g, '-') 90 | if match.match(/^###[^#]/) # Found an h3 91 | h4_count = h5_count = 1 92 | numbered_heading = "\n\n

#{h3_count++} - #{heading}

\n\n" 93 | toc.push({ title: heading, subheadings: [] }) 94 | return numbered_heading 95 | 96 | if match.match(/^####[^#]/) # Found an h4 97 | h5_count = 1 98 | numbered_heading = "\n\n

#{h3_count - 1}.#{h4_count++} - #{heading}

\n\n" 99 | toc[toc.length - 1]['subheadings'].push(heading) 100 | return numbered_heading 101 | 102 | if match.match(/^#####[^#]/) # Found an h5 103 | h6_count = 1 104 | return "\n\n
#{h3_count - 1}.#{h4_count - 1}.#{h5_count++} - #{heading}
\n\n" 105 | 106 | if match.match(/^######[^#]/) # Found an h6 107 | return "\n\n
#{h3_count - 1}.#{h4_count - 1}.#{h5_count - 1}.#{h6_count++} - #{heading}
\n\n" 108 | 109 | 110 | return match 111 | ) 112 | 113 | # 114 | # Re-replace all code blocks 115 | # 116 | (opts.content = opts.content.replace("CODE_REPLACEMENT", "\n\n" + block + "\n\n")) for block in code_blocks 117 | 118 | # 119 | # Drop the table of contents into the document for use by the layout 120 | # 121 | opts.templateData.document.table_of_contents = toc 122 | 123 | return 124 | 125 | # 126 | # Render document-level changes 127 | # 128 | renderDocument: (opts) -> 129 | # 130 | # Wrap code blocks in a code container for styling 131 | # 132 | opts.content = opts.content.replace(/
<\/pre>/g, "
") 134 | opts.content = opts.content.replace(/#(.*)filename[:=] ?(.*)/g, (match, p1, p2, offset, total_string)-> 135 | return "
" + p2 + "
" 136 | ) 137 | 138 | opts.content = opts.content.replace(/
/g, "
") 139 | opts.content = opts.content.replace(/<\/code><\/div><\/div><\/pre>/g, "
") 140 | 141 | # 142 | # Setup helpers for generating the index dropdown 143 | # 144 | opts.templateData.document.printIndexDropdowns = (document)-> 145 | buffer = "
" 146 | buffer += document.printIndexDropdownSection(document, 'Start Here') 147 | buffer += document.printIndexDropdownSection(document, 'Views') 148 | buffer += document.printIndexDropdownSection(document, 'Models') 149 | buffer += document.printIndexDropdownSection(document, 'Theming') 150 | buffer += "
" 151 | 152 | buffer += "
" 153 | buffer += document.printIndexDropdownSection(document, 'Testing') 154 | buffer += document.printIndexDropdownSection(document, 'Extras') 155 | buffer += document.printIndexDropdownSection(document, 'Contributing to SproutCore') 156 | buffer += document.printIndexDropdownSection(document, 'Thanks') 157 | buffer += "
" 158 | 159 | buffer 160 | 161 | opts.templateData.document.printIndexDropdownSection = (document, section)-> 162 | buffer = ["
#{section}
"] 163 | sorted = document.index_dropdown.sort((a,b) -> 164 | if (a.order == b.order) then 0 else (if (a.order < b.order) then -1 else 1) 165 | ) 166 | sorted.forEach((guide)-> 167 | if section == guide.category 168 | buffer.push "
#{guide['title']}
" 169 | ) 170 | buffer.join("\n") 171 | 172 | # 173 | # Setup a helper for sorting 174 | # 175 | opts.templateData.document.sortBy = (array, keys) -> 176 | keys = [keys] unless keys instanceof Array 177 | array.sort((a,b) -> 178 | ret = 0 179 | index = 0 180 | 181 | while ret == 0 and index < keys.length 182 | valA = a[keys[index]] 183 | valB = b[keys[index]] 184 | 185 | if typeof valA == 'string' and typeof valB == 'string' 186 | ret = if valA.toUpperCase() >= valB.toUpperCase() then 1 else -1 187 | else 188 | ret = if valA >= valB then 1 else -1 189 | 190 | index++ 191 | ret 192 | ) 193 | 194 | 195 | # 196 | # Setup a helper for getting the users avatar 197 | # 198 | opts.templateData.document.avatarFor = (githubUsername) -> 199 | if avatar_cache[githubUsername] 200 | return avatar_cache[githubUsername] 201 | else 202 | console.log(' *** Requesting avatar from ', "https://api.github.com/users/#{githubUsername}") 203 | 204 | # Set the cache to the unknown image so that we don't make a request 205 | # in the middle of another request 206 | avatar_cache[githubUsername] = "/images/credits/unknown.jpg" 207 | 208 | https.request({ host: "api.github.com", path: "/users/#{githubUsername}", headers: { 'user-agent': 'sproutcore' } }, (res) -> 209 | str = '' 210 | 211 | res.on('data', (chunk)-> 212 | str += chunk 213 | ) 214 | 215 | res.on('end', ()-> 216 | json = JSON.parse(str) 217 | 218 | if (!json) 219 | console.log(" *** ERROR: Could not parse JSON: ", str) 220 | avatar_cache[githubUsername] = json['avatar_url'] 221 | avatar_cache['timestamp'] = (new Date()).getTime() 222 | 223 | console.log(" *** Avatar found for " + githubUsername + ": " + json['avatar_url']) 224 | 225 | fs.writeFile(__dirname + '/.avatar_cache.json', JSON.stringify(avatar_cache, null, 2), (err) -> 226 | if (err) 227 | console.log(" *** ERROR: Could not save cache file: ", err) 228 | ) 229 | ) 230 | ).end() 231 | 232 | return "/images/credits/unknown.jpg" 233 | 234 | 235 | # 236 | # Generate an object for the index dropdown 237 | # 238 | if not opts.templateData.document.index_dropdown 239 | opts.templateData.document.index_dropdown = [] 240 | docs = opts.templateData.getCollection('documents') 241 | env = opts.templateData.getEnvironment() 242 | 243 | docs.forEach((doc) -> 244 | if doc.get('outExtension') == 'html' 245 | title = doc.meta.get('sc_title') 246 | 247 | if doc.meta.get('sc_draft') 248 | if env == 'production' 249 | return # Don't show draft documents in production 250 | else 251 | title = "DRAFT - " + title 252 | 253 | opts.templateData.document.index_dropdown.push({ category: doc.meta.get('sc_category'), order: doc.meta.get('sc_order'), title: title, url: "/#{doc.get('outFilename')}" }) 254 | ) 255 | return 256 | -------------------------------------------------------------------------------- /src/documents/adding_unit_test.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Testing 4 | sc_order: 20 5 | sc_title: Adding a Unit Test 6 | sc_description: This guide covers unit test file generation and organization. After reading this guide, you will be able to: 7 | sc_list: 8 | - Automatically generate a unit test file that will be placed in the correct location. 9 | - Manually create a unit test file from a template. 10 | - Manually create unit test files. 11 | - Arrange your unit test files. 12 | --- 13 | 14 | NOTE: The way of interacting with the build tools has changed. Most of the generators are not included, which 15 | means that these files and folders need to be created by hand. Check out the Getting Started guides for 16 | which generators can be used. This guide will be updated as soon as possible to reflect these changes. 17 | 18 | ### Creating a Directory for Your Unit Test Files 19 | 20 | When you first create your SproutCore application, directories are 21 | automatically created for you. 22 | 23 | ```bash 24 | $ sc-init MyApp 25 | ~ Created directory at my_app 26 | ~ Created file at my_app/Buildfile 27 | ~ Created file at my_app/README 28 | ~ Created directory at apps 29 | ~ Created directory at apps/my_app 30 | ~ Created directory at apps/my_app/resources 31 | ~ Created file at apps/my_app/resources/main_page.js 32 | ~ Created file at apps/my_app/resources/loading.rhtml 33 | ~ Created file at apps/my_app/core.js 34 | ~ Created file at apps/my_app/main.js 35 | Your new SproutCore project is ready! 36 | ``` 37 | By convention, SproutCore assumes that all unit tests files are located in the +tests+ 38 | sub-directory for your application (or framework). 39 | 40 | `my_app/apps/my_app/tests` 41 | 42 | Hence, create the +tests+ directory if it is missing. 43 | 44 | Unlike like most folders, SproutCore assumes that each file in the tests 45 | directory that ends in +.js+ or +.rhtml+ is a unit test. You can load each 46 | of these unit tests individually or as a group from the built-in test runner. 47 | 48 | NOTE: The files in the +tests+ directory are omitted from your production deployment. 49 | 50 | ### Creating a Unit Test File 51 | 52 | SproutCore unit test cases are coded and saved into unit test files under the +tests+ 53 | directory. 54 | 55 | It is good practice to group related unit test cases into a single unit test 56 | file. For example, all test cases for a +login+ controller class should be 57 | saved in a unit test file called +login.js+. This is way, when a change is 58 | made to the login controller class, we can run all the unit test cases in +login.js+ 59 | to verify. 60 | 61 | In addition, it is good practice to group related unit test files into a 62 | sub-directory under the +tests+ directory. For example, unit test files for 63 | controllers should be saved in the +tests/controller+ directory. In this way, 64 | if a change is made to all controllers, we run the unit test cases in all unit 65 | test files in the +controller+ directory to verify. 66 | 67 | There are three ways to create your unit test files. 68 | 69 | #### Automatically Creating Unit Test Files 70 | 71 | When you use the +sc-gen+ tool to add new source files to your application, it 72 | will automatically add a corresponding test file added into the +tests+ 73 | directory. For example, 74 | 75 | ```bash 76 | $ sc-gen controller MyApp.LoginController 77 | ~ Created directory at controllers 78 | ~ Created file at controllers/login.js 79 | ~ Created directory at tests 80 | ~ Created directory at tests/controllers 81 | ~ Created file at tests/controllers/login.js # <== Generated unit test file 82 | 83 | Your controller is now ready to use! 84 | ``` 85 | 86 | Notice the creation of +tests/controllers/login.js+. 87 | 88 | Conveniently, the generated unit test file is built from a template: 89 | 90 | ```javascript 91 | # filename: apps/my_app/test/controllers/login.js 92 | // ================================================== 93 | // Project: MyApp.loginController Unit Test 94 | // Copyright: ©2011 My Company, Inc. 95 | // ================================================== 96 | /*globals MyApp module test ok equals same stop start */ 97 | 98 | module("MyApp.loginController"); 99 | 100 | // TODO: Replace with real unit test for MyApp.loginController 101 | test("test description", function() { 102 | var expected = "test"; 103 | var result = "test"; 104 | equals(result, expected, "test should equal test"); 105 | }); 106 | ``` 107 | 108 | #### Generating Unit Test Files 109 | 110 | You can also use the +sc-gen+ tool to create your unit test file by itself: 111 | 112 | ```bash 113 | $ sc-gen test TARGET_NAME PATH/TO/TEST[.js] 114 | ``` 115 | 116 | For example, if you wanted to add a unit test file named +views/login/render.js+ 117 | to your application, you would: 118 | 119 | ```bash 120 | $ sc-gen test MyApp views/login/render 121 | ~ Created file at tests/views/login/render.js 122 | 123 | Your unit test has been generated and is now ready to be configured! 124 | ``` 125 | 126 | +render.js+ contains a sample unit test case to help you get started. 127 | 128 | ```javascript 129 | # filename: apps/my_app/tests/views/login/render.js 130 | // ================================================== 131 | // Project: MyApp Unit Test 132 | // Copyright: ©2011 My Company, Inc. 133 | // ================================================== 134 | /*globals MyApp module test ok equals same stop start */ 135 | 136 | module("MyApp"); 137 | 138 | // TODO: Replace with real unit test 139 | test("test description", function() { 140 | var expected = "test"; 141 | var result = "test"; 142 | equals(result, expected, "test should equal test"); 143 | }); 144 | ``` 145 | 146 | #### Manually Creating Unit Test Files 147 | 148 | If you cannot or would prefer not to use the +sc-gen+ tool, you can always add 149 | a unit test manually. 150 | 151 | Just create a new +.js+ file in your +tests+ directory. It will be 152 | automatically run by the test runner when appropriate. 153 | 154 | 155 | ### How to Arrange Your Unit Tests 156 | 157 | Unit tests work best if you arrange them into files and directories based on 158 | how you might want to run them together. Often, it is a good practice is to 159 | create a directory for each class you add to your app and one unit test file 160 | per method you want to test inside that directory. 161 | 162 | For example, the unit tests for SC.Object might look something like: 163 | 164 | ```bash 165 | tests/system/object/create.js 166 | tests/system/object/extend.js 167 | tests/system/object/kindOf.js 168 | ... 169 | ``` 170 | 171 | This way you can easily run all of the unit tests for a particular method, all 172 | of the unit tests for the SC.Object class itself or all of the unit tests for 173 | classes defined in the "system" directory of the target. 174 | 175 | 176 | ### Moving On 177 | 178 | Once you've added a unit test file, now you need to write some unit test code. 179 | Learn more by writing your first unit test. 180 | 181 | On to [Writing Unit Test Cases »](/writing_unit_tests.html) 182 | 183 | ### Changelog 184 | 185 | * February 15, 2011: initial version by [Scott Smith](credits.html#oldfartdeveloper) and [Vibul Imtarnasan](credits.html#veebs) 186 | * March 2, 2011: added filenames, fixed link in credits, changed ++ tags to ++ tags by [Topher Fangio](credits.html#topherfangio) 187 | * July 4, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 188 | -------------------------------------------------------------------------------- /src/documents/animate.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_draft: true 4 | sc_category: Views 5 | sc_order: 10 6 | sc_title: Animation 7 | sc_short_description: Core Concepts 8 | sc_description: The guide covers some of the core concepts of animations in SproutCore. By referring to this guide, you will be able to: 9 | sc_list: 10 | - Gain a basic understanding of SC.View#animate 11 | - Become familiar with the types of animations, how to set their duration, and understand the timing of the effect. 12 | - Take the information provided and apply it to an existing application. 13 | - See real world examples for animating buttons on a toolbar, animating views on touch enabled devices, and creating a 3d plane on a 2d view. 14 | --- 15 | 16 | _TODO: This needs more substance and less fluff. This also needs to be more 17 | guide-like and less API-like._ 18 | 19 | ### Introduction 20 | 21 | Animations play an important role when engineering an application. They allow 22 | a stagnant set of states and views to become fluid with movement. When used 23 | tactfully, these visual effects can be used to create seamless transitions 24 | between views, provide 3D space to a flat view or object, or help draw 25 | attention to a specific part of your application. The possibilities are 26 | endless with +SC.View#animate+. 27 | 28 | #### What is SC.View#animate? 29 | 30 | +SC.View#animate+ is a set of features and functionality that makes it easy to 31 | build compelling user interfaces. It animates the properties of a view. You'll 32 | need to think about three things when applying an animation: The type of 33 | animation you'd like to apply, the duration in seconds needed to complete the 34 | animation, and how smoothly the animations move between frames. 35 | 36 | #### What are the features of SC.View#animate? 37 | 38 | +SC.View#animate+ uses CSS3 animations and javascript as a fallback when 39 | needed. Animations can be combined with multiple types, synchronized and 40 | executed at once or executed concurrently. When using +SC.View#animate+, the 41 | property of a view animates and changes without redrawing the view. Therefore, 42 | the views properties change during the animation. 43 | 44 | WARNING: The JavaScript fallback has not yet been implemented. 45 | 46 | #### What are the benefits of SC.View#animate? 47 | 48 | Using +SC.View#animate+ has many benefits. By using CSS-only timing functions 49 | of the animations before falling back to JavaScript, allows JavaScript 50 | performance to not be impacted. When JavaScript handles the timing functions 51 | it has to do some semi-heavy calculations each frame. Using CSS-only timing 52 | functions gives a graceful degradation of sorts. This approach allows your 53 | application to be cross-browser compliant and have optimized performance. 54 | 55 | Furthermore, combing multiple types of animations on one view allows you to 56 | step outside of the preset effects and enables you to create custom effects. 57 | While synchronizing multiple views to animate at once enables your application 58 | to become cinematic or a presentation of sorts. 59 | 60 | And last, when used with a callback method your animations can become 61 | concurrent and fluid. We will explore the aforementioned features and benefits 62 | of +SC.View#animate+ through practical, real-world scenarios towards the end of 63 | this guide. But before we do so lets have a detailed look at +SC.View#animate+. 64 | 65 | ### A detailed look 66 | 67 | To start with we will explore the syntax for +SC.View#animate+. Below is a 68 | basic animation that fades a views opacity from +1.0+ down to +0.0+. The 69 | duration for how long it takes for the animation to take place is 1.5 seconds 70 | and the timing is eased in then out. 71 | 72 | ```javascript 73 | myApp.myView.animate('opacity', 0.0, { 74 | duration:1.5, 75 | timing:'ease-in-out' 76 | }); 77 | ``` 78 | 79 | #### Type of animation effects 80 | 81 | After you set which view you would like to animate, the first parameter passed 82 | is the type of animation effect you would like to use. The following is a list 83 | of what animation effects you can use: 84 | 85 | ##### +opacity+ 86 | 87 | Opacity has a value of +0.0 - 1.0+. The value assigned is that of a 88 | percentage. Therefore, +0.6+ is setting a views opacity to +60%+ transparency. 89 | Opacity is a great way to switch between two views that are stacked on each 90 | other. 91 | 92 | ##### +scale+ 93 | 94 | Scale has a value of +0.0 - infinite+. The value assigned is that of a 95 | percentage. Therefore, +2.0+ is setting a views height and width 200% larger 96 | then its defined properties in your view. Scale allows you to create bounce 97 | effects and explore the 3d plane through the z axis. 98 | 99 | ##### +top+ / +bottom+ / +left+ / +right+ / +height+ / +width+ 100 | 101 | +top+, +bottom+, +left+, +right+, +height+, and +width+ changes the value of 102 | each property in your layout. In order to use +top+, +bottom+, +left+, and/or 103 | +right+, the view in which you are animating must be assigned those values. 104 | The value assign is that of a pixel. Therefore, if you have a view with a 105 | value of +'left:10'+ assign to the left property and within your animation you 106 | adjust the properties value to +'left:100'+ then the view will glide to the 107 | newly assigned +left+ value. 108 | 109 | ##### +rotateX+ / +rotateY+ / +rotateZ+ 110 | 111 | +rotateX+, +rotateY+, and +rotateZ+ allows you to change a views position 112 | within a 3d plane. Below is a graph showing how X, Y, and Z interact with a 3d 113 | plane. 114 | 115 |
![XYZ Rotation](images/animate/xyz.png)
116 | 117 | #### Duration of an animation 118 | 119 | The second parameter passed is pretty straight forward and is the duration of 120 | the animations effect. It has a value of seconds. 121 | 122 | #### Timing of an animation 123 | 124 | The third parameter passed relates to the animations timing. More 125 | specifically, it allows for animations to change their speed over its duration. 126 | The values assigned to timing is preset and determined based on what timing 127 | option you pass. 128 | 129 | ##### +linear+ 130 | 131 | The +linear+ animation timing function provides and straight-line interpolation 132 | between the starting value and the final animated value assigned to your view. 133 | 134 |
![Linear Graph](images/animate/linear_graph.png)
135 | 136 | ##### +ease-in+ 137 | 138 | The +ease-in+ animation timing function allows the animation to start slow, get 139 | faster and faster as the animation plays out, and finally become constant 140 | again. However, the constant state occurs faster than a basic curve would be. 141 | 142 | +ease-in+ starts with a shallow slope during the start of the animation. 143 | During this part of the animation the animated value does not change much, but 144 | as time goes on, the animated value starts to change more quickly until the 145 | final value is achieved. This timing function provides the user with a subtle 146 | cue and allows the user time to adjust or acknowledge the fact that something 147 | is changing. Furthermore, with the animations speed dramatically increasing 148 | towards the end, the visual cue places an emphasis on the achieved state of the 149 | animated property value. 150 | 151 |
![Ease In Graph](images/animate/ease_in_graph.png)
152 | 153 | ##### +ease-out+ 154 | 155 | The +ease-out+ animation is the opposite of ease-in. Instead of starting slow 156 | and speeding up, the +ease-out+ timing starts off fast and slows down till the 157 | animated value is reached. This timing function provides the user with a cue 158 | that draws their attention to the initial change and gives an emphasis to the 159 | start of the animation and less to the ending. 160 | 161 |
![Ease Out Graph](images/animate/ease_out_graph.png)
162 | 163 | ##### +ease-in-out+ 164 | 165 | The +ease-in-out+ timing function starts off slowly, accelerates in the middle, 166 | and then slows down again towards the end. This animation, much like its name 167 | describes, is a combination between +ease-in+ and +ease-out+. The cue provided 168 | with +ease-in-out+ gives more emphasis on the middle of the animation and less 169 | towards the beginning and ending. 170 | 171 |
![Ease In Out Graph](images/animate/ease_in_out_graph.png)
172 | 173 | ### Using +SC.View#animate+: 174 | 175 | #### Adding animations to an existing application 176 | 177 | _TODO: Here we can have a developer grab an existing application from git and 178 | add the animations to the app. By having the developer use an existing 179 | application allows us to walk a developer through with minimal distraction and 180 | adds an emphasis to the topic at hand... animations._ 181 | 182 | #### Example animation one: animating buttons on a navigation menu 183 | 184 | _TODO: Here we will focus only on the animations part of our application. The 185 | code example shown in the guides will only be the animations._ 186 | 187 | #### Example animation two: animating views for touch enabled devices 188 | 189 | _TODO: We would like to show how to use animations with touch enabled devices, 190 | such as a drill down listview for mobile devices or for a tablets navigation 191 | menu and navigating through views with a swipe on a tablet._ 192 | 193 | #### Example animation three: using animation in a 3d plane 194 | 195 | _TODO: Here we can have some fun and introduce the developer to animating on a 196 | 3d plane. This part of animations is great for creating splash pages, and 197 | ads._ 198 | 199 | 200 | ### Changelog 201 | 202 | * March 22, 2011: initial version bi [Chad Eubanks](credits.html#ChadEubanks) 203 | and [Kyle Carriedo](credits.html#kcarriedo) 204 | * March 22, 2011: some cleanup by "Peter Wagenet":credits.html#wagenet 205 | * July 4, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 206 | -------------------------------------------------------------------------------- /src/documents/commit_code.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Contributing to SproutCore 4 | sc_order: 20 5 | sc_title: Committer Guidelines 6 | sc_description: So, you're ready to push some code into the public repo? Here's a list of things you can do to make sure your code gets past the review process. 7 | --- 8 | 9 | ### Conform to SC Style Guides 10 | 11 | Yeah, this is pretty obvious. If you haven't already go [read them](/style_guide.html). 12 | 13 | ### Must Pass JS Hint 14 | 15 | Javascript can be beautiful and annoying all at the same time. [JSHint](http://jshint.com/) is a 16 | god send and helps check for common mistakes like: 17 | 18 | * Undefined global vars 19 | * use of +==+ and +!=+ instead of +===+ and +!==+ 20 | * Extra or missing commas 21 | 22 | The following JS lint config can be used in combination with the [SproutCore 23 | Textmate bundle](https://github.com/sproutit/sproutcore-tmbundle). 24 | 25 | Install it at +/SproutCore.tmbundle/Support/bin/prefs.js+ 26 | 27 | ```javascript 28 | var JSLINT_PREFS = { 29 | adsafe : false, // if ADsafe should be enforced 30 | bitwise : false, // if bitwise operators should not be allowed 31 | browser : true, // if the standard browser globals should be predefined 32 | cap : false, // if upper case HTML should be allowed 33 | debug : true, // if debugger statements should be allowed 34 | eqeqeq : true, // if === should be required 35 | evil : false, // if eval should be allowed 36 | forin : true, // if for in statements must filter 37 | fragment : false, // if HTML fragments should be allowed 38 | laxbreak : true, // if line breaks should not be checked 39 | nomen : false, // if names should be checked 40 | on : false, // if HTML event handlers should be allowed 41 | passfail : false, // if the scan should stop on first error 42 | plusplus : false, // if increment/decrement should not be allowed 43 | regexp : true, // if the . should not be allowed in regexp literals 44 | rhino : false, // if the Rhino environment globals should be predefined 45 | undef : true, // if variables should be declared before used 46 | safe : false, // if use of some browser features should be restricted 47 | sidebar : false, // if the System object should be predefined 48 | sub : true, // if all forms of subscript notation are tolerated 49 | white : false, // if strict whitespace rules apply 50 | widget : false, // if the Yahoo Widgets globals should be predefined 51 | indent: 2, 52 | predef: ["SC", "require", "sc_super", "YES", "NO", "static_url", "sc_static", "sc_resource", "sc_require", "module", "test", "equals", "same", "ok", "CoreTest", "SproutCore", "$", "jQuery", "start", "stop", "expect", "htmlbody"] 53 | }; 54 | ``` 55 | 56 | TODO: we should have a command line tool that is included in the build tools so 57 | there is a consistent standard and so all the cool kids using VI have an 58 | option. 59 | 60 | ### Unit Tests 61 | 62 | #### Don't break anyone else's tests 63 | 64 | On stable builds, all of the unit tests should pass. On development branches, 65 | if there are broken tests because of a major project that someone on the Core 66 | Team is doing, you can ignore those failures. 67 | 68 | #### Write good tests 69 | 70 | Writing good unit tests is something often pontificated on by seasoned 71 | developers, so rather than waste time with a long essay I'll just show some 72 | examples of good and bad unit tests: 73 | 74 | ##### View Unit Tests 75 | 76 | ```javascript 77 | test("basic", function() { 78 | var view = pane.view('basic'); 79 | ok(!view.$().hasClass('disabled'), 'should not have disabled class'); 80 | ok(!view.$().hasClass('sel'), 'should not have sel class'); 81 | 82 | var input = view.$(); 83 | equals(input.attr('aria-checked'), 'false', 'input should not be checked'); 84 | 85 | var label = view.$('span.label'); 86 | equals(label.text(), 'Hello World', 'should have label'); 87 | }); 88 | ``` 89 | 90 | The above test is great because it verifies that the *rendered DOM* is correct. 91 | 92 | ```javascript 93 | test("basic", function() { 94 | var view = pane.view('basic'); 95 | equals(view.get('title'), 'Hello World', 'should have label'); 96 | }); 97 | ``` 98 | 99 | This test sucks because it only verifies that you can set a property on an object. 100 | 101 | TIP: For more information see [Writing Unit Tests](writing_unit_tests.html). 102 | 103 | #### Running Unit Tests 104 | 105 | You can run the unit tests by using the test runner app http://localhost:4020/sproutcore/tests 106 | or by calling them manually: 107 | 108 | * All Tests: 109 | 110 | http://localhost:4020/<framework_or_app_name>/en/current/tests.html 111 | 112 | 113 | * Specific Tests: 114 | 115 | http://localhost:4020/<framework_or_app_name>/en/current/tests/path/to/your/tests.html 116 | 117 | 118 | For example: 119 | 120 | * All of SproutCore's datastore tests: 121 | 122 | http://localhost:4020/sproutcore/datastore/en/current/tests.html 123 | 124 | 125 | * Just the tests in the store directory: 126 | 127 | http://localhost:4020/sproutcore/datastore/en/current/tests/system/store.html 128 | 129 | 130 | * Just the find tests: 131 | 132 | http://localhost:4020/sproutcore/datastore/en/current/tests/system/store/find.html 133 | 134 | TIP: For more information see [Running Unit Tests](running_unit_tests.html). 135 | 136 | ### Consistency 137 | 138 | All code must be consistent with patterns found in the Application and within the Frameworks. example: MVC, 139 | Visitor, Statecharts don't add special functionality without good reason! 140 | 141 | ### Performance 142 | 143 | Avoid things that are known to be slow: 144 | 145 | * +.observes+ 146 | * nested run loops 147 | * looping 148 | * missing +.cacheable()+ 149 | * code must be prove-ably fast with +SC.Benchmark+ 150 | * keep in mind code size in kb, number of statements (Internet Explorer!), doing too much on init 151 | 152 | ### Maintainability 153 | 154 | * Don't override private APIs 155 | * Be consistent and predictable 156 | * Make your classes extensible for other devs! (comment and modularize) 157 | * Use good OO practices 158 | * Reduce coupling between layers (parent classes should not know about their children -> looking at you SC.View) 159 | * Refactor, don't *hack* -> **make code better not worse!** 160 | 161 | ### Changelog 162 | 163 | * March 3, 2011: initial version by [Mike Ball](credits.html#onkis) 164 | * March 3, 2011: minor adjustments by [Peter Wagenet](credits.html#wagenet) 165 | * March 3, 2011: minor grammatical/formatting changes by [Topher Fangio](credits.html#topherfangio) 166 | * July 24, 2013: migration to DocPad by [Topher Fangio](credits.html#topherfangio) 167 | * February 5, 2014: fix formatting issue dealing with dollar signs followed by a single quote by [Topher Fangio](credits.html#topherfangio) 168 | -------------------------------------------------------------------------------- /src/documents/connect_server.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_draft: true 4 | sc_category: Extras 5 | sc_order: 30 6 | sc_title: Connecting to a Server 7 | sc_description: There are a number of options for connecting your SproutCore application to a server. Here we give an overview of the options that you have. 8 | --- 9 | 10 | WARNING: This is a Draft Copy, and although the information in here is 11 | correct, it may be incomplete in places. 12 | 13 | ### When you have a light app, with limited server needs 14 | 15 | SproutCore includes a powerful model layer, called the datastore. SproutCore 16 | has special code in it's controller and view layers to make the datastore the 17 | most efficient source of data. 18 | 19 | Nevertheless, you don't have to use the datastore in your application. Like 20 | most high-quality frameworks, the datastore is an optional framework in 21 | SproutCore (though it is "on" by default). 22 | 23 | If you come from a jQuery (or other JS framework), you're probably used to 24 | making your own AJAX calls and handling the response yourself. Toolkits like 25 | [BackBone](http://backbonejs.org/) work like this. If you 26 | have a small app, and feel more comfortable with this approach: 27 | 28 | Follow along with the [Using a server without the store](no_store.html) Guide. 29 | 30 | ### Using the Store 31 | 32 | However, if you are going to deal with more than a handful of data, or want to 33 | take advantage of relationships between data, then managing it all on your own 34 | quickly gets messy. One of SproutCore's strongest features is it's Data Store. 35 | The Store can hold all of your data in memory allowing you to search through 36 | it with [Queries](records.html#using-queries). It is also the central place 37 | for your data, allowing you to bind many different views to the same record 38 | and have changes propagated easily. 39 | 40 | Connecting your store to a server can be one of the most daunting tasks in 41 | creating your app: 42 | 43 | * How do I know when to send a message to the server? 44 | * Do I have to rewrite my server? 45 | 46 | There are some different choices depending on what setup you already have (if 47 | any). 48 | 49 | #### When you have an existing, server-based MVC app 50 | 51 | If you are coming to SproutCore with a server side app that you already have 52 | running, the fastest way of getting going is to contact your server and update 53 | the store to match based on the response. For example, if you want to update a 54 | record in the store, you would ask the server to update the record and when the 55 | update is successful, you would update the store locally. 56 | 57 | This is possible because SproutCore's datastore framework allows for nested 58 | stores. With a nested store, edits are "buffered" in the nested store. You 59 | would send the changes in the nested store to the server, and when it 60 | responds, update the base store and clear (or destroy) the nested store. 61 | 62 | This makes your "base" store a read-only copy of the server. You can push new 63 | data from the server into the base store at any time, update existing data, or 64 | delete data. Any *changes* to data are made in a nested store where they can 65 | easily be aborted or cleared. 66 | 67 | This puts the server 100% in control of the "base" store. There are techniques 68 | to keep your base store easily up to date, and to refresh parts of the store 69 | when needed. Very sophisticated apps can be built with this approach, and it 70 | is the most appropriate way to build a SproutCore application if you already 71 | have an existing server-based MVC app. 72 | 73 | _We will have a guide for this soon._ 74 | 75 | The downside to this approach is that your app will have to wait on responses 76 | from the server, meaning that you don't have a truly responsive user 77 | experience. See the next section if this is a concern. 78 | 79 | #### When you are building a new SproutCore app from scratch, without an existing server 80 | 81 | The final approach puts a SproutCore datastore in 100% control. 82 | 83 | This method of connecting to the server is to use SproutCore's default method 84 | of a DataSource. The Store is designed to interact with the DataSource, 85 | letting it know when things have changed. When using a data source you can 86 | treat your Store like a local copy of your server. You can ask for records, 87 | save records or create records directly in the store, and your dataSource will 88 | then take care of propogating these changes to your server. 89 | 90 | This method will give you the most interactive setup, allowing your user to 91 | keep clicking, and not have to wait on your servers responses before moving 92 | on. If your going for the best user experience in an app, this is the way to 93 | go. 94 | 95 | Have a look at the [Connecting With a DataSource](data_source.html) Guide. 96 | 97 | ### Changelog 98 | 99 | * March 2, 2011: initial version by [Geoffrey Donaldson](credits.html#geoffreyd) and [Erich Ocean](credits.html#erichocean) 100 | * March 22, 2011: minor spelling corrections by [Topher Fangio](credits.html#topherfangio) 101 | * September 4, 2013: converted to Markdown format for DocPad guides by [deeDude](credits.html#deeDude) 102 | * September 11, 2013: minor spelling/link corrections and update of Changelog by [Topher Fangio](credits.html#topherfangio) 103 | -------------------------------------------------------------------------------- /src/documents/core_concepts_sc_object.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Start Here 4 | sc_order: 40 5 | sc_title: Classes and SC.Object 6 | sc_description: The guide covers some of the core concepts of object-oriented programming with the SproutCore framework. By referring to this guide, you will be able to: 7 | sc_list: 8 | - Understand how Classes work, including SC.Object. 9 | - Write your own subclasses based on SC.Object. 10 | - Create mixins to share functionality between Classes. 11 | - Add Class properties and functions 12 | --- 13 | 14 | 15 | ### SC.Object 16 | 17 | SproutCore uses a traditional class-based [object-oriented 18 | programming](http://en.wikipedia.org/wiki/Object-oriented_programming) style 19 | based on the +SC.Object+ Class. In this style, SC.Object becomes 20 | the base for all "Classes" in SproutCore and provides powerful extensions to 21 | regular JavaScript objects. These extensions include computed properties, 22 | automatic bindings, observers and more, which will be described in detail below. 23 | 24 | #### Creating an SC.Object Instance 25 | 26 | Creation of an SC.Object instance is straightforward. 27 | 28 | ```javascript 29 | var obj = SC.Object.create() 30 | ``` 31 | 32 | In most cases you will want to create your SC.Object with some pre- 33 | set properties. You accomplish this by providing the properties in a hash. The 34 | following example creates a new SC.Object to represent a person. 35 | 36 | ```javascript 37 | // Create an SC.Object to represent a person. 38 | var person = SC.Object.create({ 39 | firstName: 'Peter', 40 | lastName: 'Wagenet' 41 | }); 42 | 43 | // Access our object's properties 44 | person.get('firstName'); // 'Peter' 45 | person.get('lastName'); // 'Wagenet' 46 | ``` 47 | 48 | NOTE: The +get()+ and +set()+ functions allow for Key-Value observing which is 49 | described in more detail below. For now, trust that they are important and you 50 | should make a habit of always using them to access properties on SC.Object and 51 | its subclasses. 52 | 53 | #### Creating an SC.Object Subclass 54 | 55 | In many cases you will want to create subclasses of SC.Object 56 | rather than direct instances. In the above example, we created a new 57 | SC.Object to represent a person, but it often makes more sense to 58 | define a unique Person Class, and to create instances of it. Doing 59 | so allows us to define default properties on the Class and to include related 60 | helper functions. To do this, we simply call +extend()+ on 61 | SC.Object as the next example shows. 62 | 63 | ```javascript 64 | MyApp.Person = SC.Object.extend({ 65 | firstName: null, 66 | lastName: null, 67 | permissions: 'none', 68 | 69 | fullName: function() { 70 | return [this.get('firstName'), this.get('lastName')].compact().join(' '); 71 | } 72 | }); 73 | ``` 74 | 75 | After defining the Person Class, we can now create new 76 | Person objects and each will contain the properties 77 | firstName, lastName and permissions as 78 | well as the simple fullName() function. The following example 79 | shows this. 80 | 81 | ```javascript 82 | var person = MyApp.Person.create({ 83 | firstName: 'Peter', 84 | lastName: 'Wagenet' 85 | }); 86 | 87 | person.fullName(); // 'Peter Wagenet' 88 | person.get('permissions'); // 'none' 89 | ``` 90 | 91 | ##### Calling superclass methods with sc_super 92 | 93 | In some cases when you subclass a Class you will want to augment a method of 94 | the parent Class without completely overriding it. In this case, SproutCore 95 | provides the +sc_super+ method which calls the original function. For those 96 | familiar with Ruby's super method, sc_super is very 97 | similar. Here is an example of a subclass of Person that extends 98 | and uses the superclass's fullName() method. 99 | 100 | ```javascript 101 | // Define a new subclass of MyApp.Person 102 | MyApp.FormalPerson = MyApp.Person.extend({ 103 | title: null, 104 | 105 | fullName: function() { 106 | // Joins `title` with the result of `fullName()` on the superclass. 107 | return [this.get('title'), sc_super()].compact().join(' '); 108 | } 109 | }); 110 | 111 | // Create a MyApp.FormalPerson object 112 | var person = MyApp.FormalPerson.create({ 113 | title: 'Mr.', 114 | firstName: 'Peter', 115 | lastName: 'Wagenet' 116 | }); 117 | 118 | person.fullName(); // Mr. Peter Wagenet 119 | person.get('permissions'); // 'none' 120 | ``` 121 | 122 | NOTE: +sc_super+ is one of the rare exceptions in SproutCore in that it is not 123 | actually a JavaScript function. It's just a pre-processor directive that gets 124 | replaced with arguments.callee.base.apply(this, arguments) by the 125 | Build Tools. 126 | 127 | ##### The init method 128 | 129 | Whenever an instance of an SC.Object is created, its +init()+ 130 | method is called first. This function can be overridden when you need to 131 | perform additional setup each time one of your objects is created. The next 132 | example uses init() to ensure that a calculation is performed 133 | each time a new MyApp.Calculation object is created. 134 | 135 | ```javascript 136 | MyApp.Calculation = SC.Object.extend({ 137 | input: null, 138 | result: null, 139 | 140 | _calculate: function() { 141 | // Do expensive calculation 142 | this.set('result', this.get('input') * 2); 143 | }, 144 | 145 | init: function() { 146 | sc_super(); // Warning: Always invoke SC.Object's init() method! 147 | this._calculate(); 148 | } 149 | }); 150 | 151 | var calc = MyApp.Calculation.create({ input: 5 }); 152 | calc.get('result'); // 10 153 | ``` 154 | 155 | WARNING: When overriding the init() method it is important that 156 | you call sc_super at the start of your custom init() 157 | method. This will ensure that all internal object initialization takes place 158 | as expected. 159 | 160 | ##### Using Mixins 161 | 162 | Mixins are an easy way to extend multiple classes, that don't share an 163 | inheritance tree, with similar functionality. Mixins are simply a hash with a 164 | series of properties that will be added to the objects you create. The next 165 | example defines the MyApp.Introducable mixin, which can then be 166 | shared between many different Classes. 167 | 168 | ```javascript 169 | MyApp.Introducable = { 170 | sayHello: function() { 171 | return 'Hello, my name is ' + this.name; 172 | } 173 | }; 174 | ``` 175 | 176 | To add one or more mixins to a Class, add them as parameters to extend. For 177 | example, 178 | 179 | ```javascript 180 | MyApp.Person = SC.Object.extend(MyApp.Introducable, { name: ''; }); 181 | 182 | var person = MyApp.Person.create({ name: 'Bob' }); 183 | person.sayHello(); // Hello, my name is Bob 184 | ``` 185 | 186 | As you may have guessed, SC.Object.extend just takes a _series of 187 | hashes_ and mixes them all in to a newly created class. 188 | 189 | ##### Class methods 190 | 191 | You can also define Class methods on your custom Classes. If you need to add a 192 | single Class method to a Class, you can do it in the traditional JavaScript 193 | manner. For example, the following adds a quickCreate() helper 194 | function to MyApp.Person. 195 | 196 | ```javascript 197 | MyApp.Person.quickCreate = function(firstName, lastName){ 198 | return MyApp.Person.create({ 199 | firstName: firstName, 200 | lastName: lastName 201 | }); 202 | }; 203 | 204 | var person = MyApp.Person.quickCreate('Peter', 'Wagenet'); 205 | person.fullName(); // Peter Wagenet 206 | ``` 207 | 208 | However, sometimes you may want to add a series of Class methods and 209 | properties. In this case you can use the +mixin()+ method. 210 | 211 | ```javascript 212 | MyApp.Person.mixin({ 213 | quickCreate: function(firstName, lastName){ 214 | return MyApp.Person.create({ 215 | firstName: firstName, 216 | lastName: lastName 217 | }); 218 | }), 219 | }); 220 | 221 | person = MyApp.Person.quickCreate('Mary', 'Shelley'); 222 | person.fullName(); // Mary Shelley 223 | ``` 224 | 225 | Note that the mixin() method is different from the Mixins 226 | described above. The mixin() method will add the properties and 227 | methods within the block as _Class properties and methods_. Mixins themselves 228 | add _instance variables and methods_. 229 | 230 | ### Changelog 231 | 232 | * January 12, 2011: initial partial version by [Peter Wagenet](credits.html#wagenet) 233 | * January 19, 2011: further updates by [Peter Wagenet](credits.html#wagenet) 234 | * January 20, 2011: corrections to "The +init+ Method" and "The Run Loop" by [Peter Wagenet](credits.html#wagenet) 235 | * July 19, 2013: added "Changelog" by [Topher Fangio](credits.html#topherfangio) 236 | * August 27, 2013: converted to Markdown format for DocPad guides by [deeDude](credits.html#deeDude) 237 | -------------------------------------------------------------------------------- /src/documents/credits.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: credits_layout 3 | sc_category: Thanks 4 | sc_order: 10 5 | sc_title: Credits 6 | sc_short_description: The People That Make It Happen 7 | sc_credits: 8 | - name: Documentation Team 9 | authors: 10 | - fname: Topher 11 | lname: Fangio 12 | github: topherfangio 13 | - name: Contributors 14 | authors: 15 | - fname: Mike 16 | lname: Atkins 17 | github: apechimp 18 | - fname: Peter 19 | lname: Wagenet 20 | github: wagenet 21 | - fname: Yehuda 22 | lname: Katz 23 | github: wycats 24 | - fname: Mike 25 | lname: Ball 26 | github: onkis 27 | - fname: Peter 28 | lname: Bergström 29 | github: pbergstr 30 | - fname: Kyle 31 | lname: Carriedo 32 | github: kcarriedo 33 | - fname: Tom 34 | lname: Dale 35 | github: tomdale 36 | - fname: Geoffrey 37 | lname: Donaldson 38 | github: geoffreyd 39 | - fname: Chad 40 | lname: Eubanks 41 | github: ChadEubanks 42 | - fname: Jason 43 | lname: Gignac 44 | github: jasonpgignac 45 | - fname: Vibul 46 | lname: Imtarnasan 47 | github: veebs 48 | - fname: Alex 49 | lname: Iskander 50 | github: ialexi 51 | - fname: Tyler 52 | lname: Keating 53 | github: publickeating 54 | - fname: Florian 55 | lname: Kugler 56 | github: dkduck 57 | - fname: Shawn 58 | lname: Morel 59 | github: strangemonad 60 | - fname: Erich 61 | lname: Ocean 62 | github: erichocean 63 | - fname: Piotr 64 | lname: Sarnacki 65 | github: drogus 66 | - fname: Scott 67 | lname: Smith 68 | github: oldfartdeveloper 69 | - fname: Majd 70 | lname: Taby 71 | github: jtaby 72 | - fname: Devin 73 | lname: Torres 74 | github: devinus 75 | - fname: Gregory 76 | lname: Moeck 77 | github: gmoeck 78 | - fname: Eric 79 | lname: Theise 80 | github: erictheise 81 | - fname: deeDude 82 | lname: 83 | github: deeDude 84 | - fname: William 85 | lname: Estoque 86 | github: westoque 87 | - fname: Jeff 88 | lname: Pittman 89 | github: geojeff 90 | - fname: Maurits 91 | lname: Lamers 92 | github: mauritslamers 93 | --- 94 | -------------------------------------------------------------------------------- /src/documents/documentation_guidelines.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Contributing to SproutCore 4 | sc_order: 30 5 | sc_title: Documentation Guidelines 6 | sc_description: This guide will walk you through writing documentation for your SproutCore classes in a format that is friendly to jsdoc-toolkit, the library used for generating documentation from code. 7 | --- 8 | 9 | ### Overview 10 | 11 | SproutCore uses the [jsdoc-toolkit](http://code.google.com/p/jsdoc-toolkit/) to 12 | generate its documentation. As such, your SproutCore classes should follow 13 | strict code-documenting formatting guidelines in order to properly generate 14 | your HTML documentation. This document outlines the proper format you should 15 | use. 16 | 17 | ### Classes 18 | 19 | Classes must always start with an +@class+ directive, followed by optional the 20 | class description. You can use 21 | [Markdown](http://daringfireball.net/projects/markdown/) to style your 22 | description (add headings, paragraphs, code, etc.) 23 | 24 | JsDoc Toolkit also supports tags in your comments to add metadata about your 25 | class. For example, you can specify who the author of the class is, what 26 | classes it inherits from, what classes are related, and what version of the 27 | frameworks it’s been available since. 28 | 29 | #### Common and Supported Markdown Syntax 30 | 31 | * For code samples, make sure you indent the code block further than the other 32 | text 33 | * For headers, add three dashes (+---+) for 2nd level headers, and three 34 | (+===+) for 1st level headers. These go on the subsequent line 35 | * For list, prefix each line with a dash (+-+) 36 | 37 | ```javascript 38 | #filename="Sample Class Documentation" 39 | /** 40 | @class 41 | 42 | Class Description 43 | ====== 44 | 45 | Overview 46 | ---------- 47 | 48 | This is a sample description. Here’s some code: 49 | 50 | SC.Store.find(...) 51 | 52 | And here’s a list: 53 | 54 | - Eggs 55 | - Butter 56 | 57 | @deprecated This class has been deprecated. Please use SC.NewRequest 58 | @see Firebug Logging Reference 59 | @extends SC.Object 60 | @author Majd Taby 61 | @since SproutCore 1.0 62 | */ 63 | 64 | SC.Request = SC.Object.extend( 65 | /** @scope SC.Request.prototype */{ 66 | ... 67 | }); 68 | ``` 69 | 70 | The +@scope+ definition is mandatory, and it defines what class/namespace the 71 | proceeding code documents. 72 | 73 | You can define multiple +@see+/+extends+/+author+ directives for each class, as 74 | you see fit. 75 | 76 | WARN: You cannot put any javascript between your documentation and the class 77 | it’s documenting. Otherwise, JsDoc will get confused by what you’re trying to 78 | document. 79 | 80 | #### Instance Methods 81 | 82 | Documenting methods follows the same general structure as a class, but with a 83 | different set of required/optional directives. You start off by describing the 84 | method’s functionality, followed by the parameters, and then the return types. 85 | 86 | It’s important to keep in mind that the jsdoc toolkit does not parse javascript 87 | code. This means that you need to put the name of the parameters in the 88 | documentation, and it’s your responsibility to keep them up to date as you 89 | update the method. 90 | 91 | In the example below, the response property is marked as optional by wrapping 92 | the name in square brackets. 93 | 94 | ```javascript 95 | #filename="Sample Instance Method Documentation" 96 | /** 97 | Method description. 98 | 99 | @param {SC.Request} request A copy of the request, frozen. 100 | @param {SC.Response} [response] An optional parameter 101 | @param {SC.Response} [foobar] A sample parameter 102 | 103 | @throws {SC.Exception} SC.RequestNotValidException 104 | @returns {Boolean} Yes on success, NO on failure. 105 | */ 106 | didSend: function(request, response) {}, 107 | ``` 108 | 109 | #### Class Methods 110 | 111 | Class methods are usually implemented by mixing-in extra methods to an existing 112 | class. For these methods/properties to show up in the same location in the 113 | generated output, you need to add a +@scope+ declaration just as you did with 114 | the class. 115 | 116 | 117 | ```javascript 118 | #filename="Sample Class Method Documentation" 119 | /** 120 | @class 121 | 122 | ... 123 | */ 124 | SC.Store = SC.Object.extend(/** @scope SC.Store.prototype */{...}); 125 | 126 | SC.Store.mixin(/** @scope SC.Store.prototype */{...}) 127 | ``` 128 | 129 | #### Properties 130 | 131 | Properties are arguably the simplest entity to document. You simply need to 132 | write a description of what the property represents, and what type it is. You 133 | can optionally provide a default value. 134 | 135 | ```javascript 136 | #filename="Sample Property Documentation" 137 | /** 138 | Description 139 | 140 | @default YES 141 | @type Boolean 142 | */ 143 | isAsynchronous: YES, 144 | ``` 145 | 146 | NOTE: Constants are documented using +@constant+ but they don’t need a default 147 | value. 148 | 149 | #### Computed Properties 150 | 151 | Computed Properties are documented just like regular properties but they add 152 | the +@field+ property. 153 | 154 | ```javascript 155 | #filename="Sample Computed Property Documentation" 156 | /** 157 | Description 158 | 159 | @field 160 | @default YES 161 | @type Boolean 162 | */ 163 | isAsynchronous: function(){... 164 | ``` 165 | 166 | ### Testing and viewing your documentation during development 167 | 168 | If, during development, you would like to test your documentation and see how 169 | it looks once it's generated, then you can use the SproutCore TextMate bundle 170 | available on "github":https://github.com/sproutcore/sproutcore-tmbundle which 171 | includes a Command to generate documentation for the current file you are 172 | viewing. To activate it, go to a jsdoc-documented javascript file, and click 173 | CMD + CTRL + G. 174 | 175 | NOTE: Generating documentation in TextMate requires the Web Sharing feature 176 | of Mac OS X to be enabled since it uses apache to display your files. 177 | 178 | #### References 179 | 180 | * [JSDoc Tag Reference](http://code.google.com/p/jsdoc-toolkit/wiki/TagReference) 181 | 182 | ### Changelog 183 | 184 | * March 22, 2011: initial version by [Majd Taby](credits.html#jtaby) 185 | * August 14, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 186 | -------------------------------------------------------------------------------- /src/documents/enumerables.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Extras 4 | sc_order: 30 5 | sc_title: Enumerables 6 | sc_short_description: The SproutCore Enumerable API 7 | sc_description: This guide covers how to work with enumerable SproutCore objects, like Arrays, Sets, ArrayControllers, and RecordArrays. When you are done with this guide, you should be able to: 8 | sc_list: 9 | - Identify the SproutCore enumerable objects. 10 | - Fluently work with enumerable objects using the Enumerable interface. 11 | --- 12 | 13 | ### What Are Enumerables? 14 | 15 | In SproutCore, an Enumerable is any object that contains a number of child 16 | objects, and which allows you to work with those children using the Enumerable 17 | interface. The most basic Enumerable is the built-in JavaScript Array. 18 | 19 | For instance, all Enumerables support the standard +forEach+ method: 20 | 21 | ```javascript 22 | [1,2,3].forEach(function(item) { 23 | console.log(item); 24 | }); 25 | ``` 26 | 27 | In general, Enumerable methods, like +forEach+, take an optional second 28 | parameter, which will become the value of +this+ in the callback function: 29 | 30 | ```javascript 31 | var array = [1,2,3]; 32 | 33 | array.forEach(function(item) { 34 | console.log(item, this.indexOf(item)); 35 | }, array) 36 | ``` 37 | 38 | Among other reasons, you will find this useful when using another Enumerable 39 | method as a callback to +forEach+: 40 | 41 | ```javascript 42 | var array = [1,2,3]; 43 | 44 | array.forEach(array.removeObject, array); 45 | ``` 46 | 47 | NOTE: This second parameter helps work around a limitation of JavaScript which 48 | sets the value of +this+ to +window+ in methods used this way. 49 | 50 | ### Enumerables in SproutCore 51 | 52 | In general, SproutCore objects that represent lists implement the Enumerable 53 | interface. Some examples: 54 | 55 | * [Array](http://docs.sproutcore.com/#doc=Array): SproutCore extends the 56 | native JavaScript Array with the Enumerable interface. 57 | * [SC.ArrayController](http://docs.sproutcore.com/#doc=SC.ArrayController): A 58 | construct that wraps a native Array and adds additional functionality for 59 | the view layer. 60 | * [SC.SparseArray](http://docs.sproutcore.com/#doc=SC.SparseArray): An 61 | Array-like object that will incrementally load in ranges as they are needed 62 | by the view layer. 63 | * [SC.RecordArray](http://docs.sproutcore.com/#doc=SC.RecordArray): An 64 | Array-like object that represents a list of Record objects. RecordArray's 65 | are "live", and the Data Store can update them when new data comes in from 66 | the backend. 67 | * [SC.Set](http://docs.sproutcore.com/#doc=SC.Set): An object that can quickly 68 | answer whether it includes an object. 69 | 70 | ### The Enumerable Interface 71 | 72 | #### Parameters 73 | 74 | The callbacks to Enumerable methods take three arguments: 75 | 76 | * *item*: the item for the current iteration. 77 | * *index*: an Integer, counting up from 0. 78 | * *self*: the Enumerable itself. 79 | 80 | #### Enumeration 81 | 82 | To enumerate all the values of an enumerable object, use the +forEach+ method: 83 | 84 | ```javascript 85 | enumerable.forEach(function(item, index, self) { 86 | console.log(item); 87 | }); 88 | ``` 89 | 90 | To invoke some method on each element of an enumerable object, use the +invoke+ 91 | method: 92 | 93 | ```javascript 94 | Person = SC.Object.extend({ 95 | sayHello: function() { 96 | console.log("Hello from " + this.get('name')); 97 | } 98 | }); 99 | 100 | var people = [ 101 | Person.create({ name: "Juan" }), 102 | Person.create({ name: "Charles" }), 103 | Person.create({ name: "Majd" }) 104 | ] 105 | 106 | people.invoke('sayHello'); 107 | 108 | // Hello from Juan 109 | // Hello from Charles 110 | // Hello from Majd 111 | ``` 112 | 113 | #### First and Last 114 | 115 | You can get the first or last object from an Enumerable by 116 | getting +firstObject+ or +lastObject+. 117 | 118 | ```javascript 119 | [1,2,3].get('firstObject') // 1 120 | [1,2,3].get('lastObject') // 3 121 | ``` 122 | 123 | #### Converting to Array 124 | 125 | This one is simple. To convert an Enumerable into an Array, simply call 126 | its +toArray+ method. 127 | 128 | #### Transforming 129 | 130 | You can transform an Enumerable into a derived Array by using the +map+ method: 131 | 132 | ```javascript 133 | ['Goodbye', 'cruel', 'world'].map(function(item, index, self) { 134 | return item + "!"; 135 | }); 136 | 137 | // returns ["Goodbye!", "cruel!", "world!"] 138 | ``` 139 | 140 | #### Setting and Getting on Each Object 141 | 142 | A very common use of +forEach+ and +map+ is to get (or set) a property on each 143 | element. You can use the +getEach+ and +setEach+ methods to accomplish these 144 | goals. 145 | 146 | ```javascript 147 | var arr = [SC.Object.create(), SC.Object.create()]; 148 | 149 | // we now have an Array containing two SC.Objects 150 | 151 | arr.setEach('name', 'unknown'); 152 | arr.getEach('name') // ['unknown', 'unknown'] 153 | ``` 154 | 155 | #### Filtering 156 | 157 | Another common task to perform on an Enumerable is to take the Enumerable as 158 | input, and return an Array after sorting or filtering it based on some 159 | criteria. 160 | 161 | For arbitrary filtering, use the (you guessed it) +filter+ method. The filter 162 | method expects the callback to return +true+ if SproutCore should include it in 163 | the final Array, and +false+ or +undefined+ if SproutCore should not. 164 | 165 | ```javascript 166 | var arr = [1,2,3,4,5]; 167 | 168 | arr.filter(function(item, index, self) { 169 | if (item < 4) { return true; } 170 | 171 | // Note, this could also be written as: 172 | return item < 4; 173 | }) 174 | 175 | // returns [1,2,3] 176 | ``` 177 | 178 | When working with a collection of SproutCore objects, you will often want to 179 | filter a set of objects based upon the value of some property. 180 | The +filterProperty+ method provides a shortcut. 181 | 182 | ```javascript 183 | Todo = SC.Object.extend({ 184 | title: null, 185 | isDone: false 186 | }); 187 | 188 | todos = [ 189 | Todo.create({ title: 'Write code', isDone: true }), 190 | Todo.create({ title: 'Go to sleep' }) 191 | ]; 192 | 193 | todos.filterProperty('isDone', true); 194 | 195 | // returns an Array containing just the first item 196 | ``` 197 | 198 | If you want to return just the first matched value, rather than an Array 199 | containing all of the matched values, you can use +find+ and +findProperty+, 200 | which work just like +filter+ and +filterProperty+, but return only one item. 201 | 202 | #### Sorting 203 | 204 | You can sort an Enumerable based on the value of some property or list of 205 | properties using +sortProperty+. If you pass in multiple properties, SproutCore 206 | will sort items with the same value for the first property by the value of the 207 | second parameter, and so on. 208 | 209 | ```javascript 210 | var todos = [ 211 | Todo.create({ title: 'Write code', isDone: true }), 212 | Todo.create({ title: 'Go to sleep' }), 213 | Todo.create({ title: 'Eat lunch', isDone: true }) 214 | ]; 215 | 216 | todos.sortProperty('isDone', 'title'); 217 | 218 | // returns an Array containing 219 | // * Go to sleep 220 | // * Eat lunch 221 | // * Write code 222 | ``` 223 | 224 | Internally, the +sortProperty+ method uses +SC.compare+, which uses 225 | SproutCore's comparable semantics. You can override the default comparison 226 | behavior for a custom object by using 227 | the +[SC.Comparable](http://docs.sproutcore.com/#doc=SC.Comparable)+ mixin. 228 | 229 | #### Aggregate Information (All or Any) 230 | 231 | If you want to find out whether every item in an Enumerable matches some 232 | condition, you can use the +every+ method: 233 | 234 | ```javascript 235 | Person = SC.Object.extend({ 236 | name: null, 237 | isHappy: false 238 | }); 239 | 240 | var people = [ 241 | Person.create({ name: 'Yehuda', isHappy: true }), 242 | Person.create({ name: 'Majd', isHappy: false }) 243 | ]; 244 | 245 | people.every(function(person, index, self) { 246 | if(person.get('isHappy')) { return true; } 247 | }); 248 | 249 | // returns false 250 | ``` 251 | 252 | If you want to find out whether at least one item in an Enumerable matches some 253 | conditions, you can use the +some+ method: 254 | 255 | ```javascript 256 | people.some(function(person, index, self) { 257 | if(person.get('isHappy')) { return true; } 258 | }); 259 | 260 | // returns true 261 | ``` 262 | 263 | Just like the filtering methods, the +every+ and +some+ methods have 264 | analogous +everyProperty+ and +someProperty+ methods. 265 | 266 | ```javascript 267 | people.everyProperty('isHappy', true) // false 268 | people.someProperty('isHappy', true) // true 269 | ``` 270 | 271 | ### Changelog 272 | 273 | * May 3, 2011: initial version by [Yehuda Katz](credits.html#wycats) 274 | * August 14, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 275 | * August 14, 2013: minor formatting and example updates by [Topher Fangio](credits.html#topherfangio) 276 | -------------------------------------------------------------------------------- /src/documents/fixtures.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Models 4 | sc_order: 20 5 | sc_title: Using Fixtures 6 | sc_description: This guide covers the use of fixtures in your application. By referring to this guide, you will be able to: 7 | sc_list: 8 | - Understand the purpose of fixtures. 9 | - Write your own fixtures. 10 | - Set up relationships within fixtures. 11 | - Use the fixtures data source. 12 | --- 13 | 14 | ### The Purpose of Fixtures 15 | 16 | Fixtures are a quick and easy way to preload your application with sample data 17 | without having to write a custom data source. This is especially useful in the 18 | early stages of development as it allows you to focus on the general structure 19 | without having to be concerned with your application's backend. 20 | 21 | #### How Fixtures Work 22 | 23 | Fixtures are defined as an array of hashes with each hash containing the 24 | attributes and values for each instance you wish to create. 25 | 26 | A sample fixture looks something like: 27 | 28 | ```javascript 29 | # filename: apps/app/fixtures/my_model.js 30 | App.MyModel.FIXTURES = [ 31 | { 32 | guid: 1, 33 | firstName: "Michael", 34 | lastName: "Scott" 35 | }, 36 | 37 | { 38 | guid: 2, 39 | firstName: "Dwight", 40 | lastName: "Schrute" 41 | } 42 | ]; 43 | ``` 44 | 45 | #### Defining Fixtures 46 | 47 | SproutCore looks for fixtures assigned specifically to a model (i.e. +App.MyModel.FIXTURES+ on the above example). By convention, fixtures are defined 48 | in the +app/fixtures+ folder of your application (i.e. +app/fixtures/my_model.js+). By default fixtures are not included in production builds. 49 | 50 | ### Writing Your Own Fixtures 51 | 52 | Writing fixtures is relatively straightforward. Each item representing a record gets its own hash in the format of +propertyName: value+. If your 53 | model looks like: 54 | 55 | ```javascript 56 | # filename: apps/app/models/article.js 57 | App.Article = SC.Record.extend({ 58 | title: SC.Record.attr(String), 59 | body: SC.Record.attr(String) 60 | }); 61 | ``` 62 | 63 | Your fixture will look like: 64 | 65 | ```javascript 66 | # filename: apps/app/fixtures/article.js 67 | App.Article.FIXTURES = [ 68 | { 69 | guid: 1, 70 | title: "Writing a SproutCore app", 71 | body: "Writing a SproutCore app is fun and exciting!" 72 | }, 73 | 74 | { 75 | guid: 2, 76 | title: "Working with Fixtures", 77 | body: "Using fixtures can make it easier to get your app started." } 78 | ]; 79 | ``` 80 | 81 | It is also possible to specify attributes that are not explicity defined in your model, though explicit declaration is encouraged. 82 | 83 | WARNING: You must specify a value for the +primaryKey+ in your fixtures. 84 | 85 | #### Defining Relationships 86 | 87 | Since relationships are only specified with the foreign key it is quite easy to set them up in your fixtures. If you have model declaration, like the 88 | following, where an employee belongs to a company and a company has many employees: 89 | 90 | ```javascript 91 | # filename: apps/app/models/employee.js 92 | App.Employee = SC.Record.extend({ 93 | firstName: SC.Record.attr(String), 94 | lastName: SC.Record.attr(String), 95 | company: SC.Record.toOne("App.Company", { 96 | inverse: "employees", 97 | isMaster: NO 98 | }) 99 | }); 100 | 101 | App.Company = SC.Record.extend({ 102 | name: SC.Record.attr(String), 103 | employees: SC.Record.toMany("App.Employee", { 104 | inverse: "company", 105 | isMaster: YES 106 | }) 107 | }); 108 | ``` 109 | 110 | You would set up your fixtures like: 111 | 112 | ```javascript 113 | # filename: apps/app/fixtures/employee.js 114 | App.Employee.FIXTURES = [ 115 | { 116 | guid: 1, 117 | firstName: "John", 118 | lastName: "Smith", 119 | company: 1 // SomeCompany, Inc. 120 | }, 121 | 122 | { 123 | guid: 2, 124 | firstName: "Sally", 125 | lastName: "Washington", 126 | company: 1 // SomeCompany, Inc. 127 | }, 128 | 129 | { 130 | guid: 3, 131 | firstName: "Bernard", 132 | lastName: "Peterson", 133 | company: 2 // AnotherCompany, Inc. 134 | } 135 | ]; 136 | 137 | App.Company.FIXTURES = [ 138 | { guid: 1, name: "SomeComany, Inc.", employees: [1,2] }, 139 | { guid: 2, name: "AnotherCompany, Inc.", employees: [3] } 140 | ]; 141 | ``` 142 | 143 | When the fixtures are loaded into your application the relationships automatically take over and point to the appropriate records. 144 | 145 | WARNING: Unlike a traditional relational database it is necessary to specify both sides of the relationship. 146 | 147 | TIP: When defining the 'many' side of the fixture record, be certain to include the [ ] around a single value because SproutCore is expecting an array 148 | of values. 149 | 150 | #### Defining Non-String Values 151 | 152 | The above examples have referred mostly to string values in the fixture. As most data is either passed into the application via 153 | [JSON](http://www.json.org) or [XML](http://www.w3.org/XML/), +SC.Record+ is capable of transforming data from a string into the appropriate object 154 | type. See +SC.RecordAttribute.registerTransform+ for more information. 155 | 156 | ### Hooking Up Your Fixtures 157 | 158 | SproutCore comes with a built-in data source for your fixtures. To use the fixtures data source you merely need to set the following in your 159 | application's +core.js+. 160 | 161 | ```javascript 162 | # filename: in apps/app/core.js 163 | App = SC.Application.create({ 164 | store: SC.Store.create().from(SC.Record.fixtures) 165 | }); 166 | ``` 167 | 168 | You can extend the built-in fixture data source to simulate real world performance (important in managing user expectations). 169 | 170 | ```bash 171 | $ sc-gen data-source App.AppsFixturesDataSource SC.FixturesDataSource 172 | ``` 173 | 174 | Delete the boilerplate code in the fixture data source so your code looks like: 175 | 176 | ```javascript 177 | # filename: apps/app/data_sources/apps_fixtures.js 178 | App.AppsFixturesDataSource = SC.FixturesDataSource.extend({ 179 | simulateRemoteResponse: YES, 180 | 181 | latency: 500 // 500 ms latency 182 | }); 183 | ``` 184 | 185 | This simulates a remote response with 500 milliseconds round trip latency. Tweak the value to match the performance of your network and servers. 186 | 187 | Next, point your store to the new fixtures data source. 188 | 189 | ```javascript 190 | # filename: in apps/app/core.js 191 | App = SC.Application.create({ 192 | store: SC.Store.create().from('App.AppsFixturesDataSource') 193 | }); 194 | ``` 195 | 196 | TIP: Notice the quotes around the data source name? +App.AppsFixturesDataSource+ doesn't exist until its instantiated during the loading process. 197 | 198 | And that's it! Your fixtures should now be available to your data store! 199 | 200 | ### Changelog 201 | 202 | * January 11, 2011: initial version by [Peter Wagenet](credits.html#wagenet) 203 | * March 2, 2011: added filenames and fixed code formatting by [Topher Fangio](credits.html#topherfangio) 204 | * July 17, 2011: minor changes by [William Estoque](credits.html#westoque) 205 | * October 23, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 206 | * January 17, 2019: removed reference to sc-gen by [Maurits Lamers](credits.html#mauritslamers) 207 | -------------------------------------------------------------------------------- /src/documents/getting_started.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Start Here 4 | sc_order: 10 5 | sc_title: Getting Started: Part 1 6 | sc_description: Welcome to SproutCore! This guide will get you up-and-running as quickly as possible with SproutCore. For this individual guide, you will: 7 | 8 | sc_list: 9 | - Install SproutCore on your machine. 10 | - Use the SproutCore tools to generate a basic starting application. 11 | - Run the SproutCore development server to test your application. 12 | - Modify the application to ensure that changes are visible. 13 | --- 14 | 15 | 16 | ### Introduction 17 | 18 | The introductory "Getting Started" guides pages are step-by-step treatments of concepts needed to make a full 19 | SproutCore application. 20 | 21 | This first "Getting Started" guide will ensure that you have the latest version of Sproutcore installed and 22 | configured properly. If you run into any issues or bumps along the way, please reach out on the 23 | [mailing list](http://groups.google.com/group/sproutcore), find us on IRC at #sproutcore or leave a message on [Gitter](https://gitter.im/sproutcore/sproutcore)! 24 | 25 | ### Installation 26 | 27 | To install the SproutCore development environment you need to have [NodeJS](http://nodejs.org/) and NPM installed. 28 | The latest version of NodeJS currently supported by this environment is 9.x. 29 | 30 | Please visit the [Installation Page](http://sproutcore.com/install/) and follow the guide for your platform. 31 | 32 | Great! Now that SproutCore is installed, we can move forward and get our first app up-and-running! 33 | 34 | ### Creating A New SproutCore Application 35 | 36 | When you are first learning any new language or framework, it is generally a good idea to create a separate 37 | directory to store all of your code. So, in some area of your hard drive where you develop software, make a 38 | sproutcore directory. 39 | 40 | ```bash 41 | $ mkdir -p ~/development/sproutcore 42 | ``` 43 | 44 | NOTE: The dollar sign ($) in the code above and below represents the command line prompt and should not 45 | be typed when executing the commands. Lines without a prompt generally show you the output of the previous 46 | command. 47 | 48 | This directory can serve as a place for all things SproutCore. Change directory to `~/development/sproutcore`, then 49 | create a SproutCore project: 50 | 51 | ```bash 52 | $ cd ~/development/sproutcore 53 | $ sproutcore init getting_started 54 | Your project has been successfully created! 55 | $ cd getting_started 56 | ``` 57 | 58 | Now you have a getting_started project directory, ~/development/sproutcore/getting_started. We will build 59 | several SproutCore apps in this project space, but for now, we'll just create a basic "Hello World" style application 60 | to ensure that everything is installed and working properly. 61 | 62 | Create the app in the getting_started project directory: 63 | 64 | ```bash 65 | $ sproutcore gen app TodosOne 66 | App TodosOne generated 67 | ``` 68 | 69 | Your directory structure should now look like the following: 70 | 71 | 72 | * **getting_started/** 73 | * **sc_config** 74 | * **apps/** 75 | * **TodosOne/** 76 | * **sc_config** 77 | * **core.js** 78 | * **main.js** 79 | * **theme.js** 80 | * **statechart.js** 81 | * **controllers/** 82 | * **fixtures/** 83 | * **views/** 84 | * **states/** 85 | * **ready_state.js** 86 | * **resources/** 87 | * **_theme.css** 88 | * **loading.ejs** 89 | * **main_page.js** 90 | * **main_page.css** 91 | 92 | ### Explanation 93 | 94 | When you are building a web application in SproutCore, it is common to have 95 | a multitude of mini-applications in order to separate out the logic into 96 | manageable parts. For instance, Apple's iCloud application has apps for 97 | e-mail, contacts and finding your friends. 98 | 99 | This is why there is an **apps/** directory inside of the getting_started project directory. It 100 | allows you to easily separate distinct applications. 101 | 102 | Inside the **apps/TodosOne** directory, you will find the sc_config for the TodosOne application. 103 | Just like the project (getting_started) has a **sc_config**, each application or 104 | framework that you create should also have a **sc_config** which specifies it's 105 | requirements as well as any build-options that you may want to change, such as 106 | whether or not to minify and compact the code. 107 | 108 | The **core.js** file is the first file that gets loaded, and is where the actual 109 | application is defined. This is also where you usually define any global 110 | constants that the application might use (which should be very few), or any 111 | configuration that external libraries might require. 112 | 113 | The **main.js** file is usually very basic. It starts the application. The default 114 | assumes you are using the Statechart framework (which we will discuss in Part 2). 115 | **main.js** simply initializes the statechart, which you will see if you open up **main.js** right now. 116 | 117 | The resources folder contains things like images, stylesheets and views. 118 | 119 | Inside the resources folder, the **_theme.css** file defines that directory's 120 | CSS theme. This allows you to define different themes for each directory. 121 | 122 | The **resources/loading.ejs** file defines the HTML that your users see before 123 | SproutCore has fully loaded and handed control over to the application. For 124 | now, it displays a simple loading message. 125 | 126 | Lastly, the **resources/main_page.js** file describes the actual user interface of the 127 | application. Open this file now and take a look. Depending upon how complex 128 | the app becomes, you may decide to have just the mainPage and mainPane, or 129 | you may decide you want to break it out a bit more by having a loadingPane, 130 | loggedInPane, loggedOutPane, etc. Most applications have at least a few 131 | panes, and possibly a few pages. 132 | 133 | ### Installing the SproutCore framework 134 | 135 | ```bash 136 | $ sproutcore install sproutcore frameworks 137 | ``` 138 | 139 | This installs the SproutCore framework inside the frameworks folder of your 140 | project. 141 | 142 | ### Viewing Your Application 143 | 144 | So, we have our SproutCore TodosOne application built, and we understand the basic 145 | layout of the files. Let's see what it looks like in the browser! 146 | 147 | Inside the project directory type the following command: 148 | 149 | ```bash 150 | $ sproutcore serve 151 | ``` 152 | 153 | NOTE: If you are attempting to run multiple copies of **sproutcore serve** at the same 154 | time, you will probably see an error about the port already being in use. If 155 | so, just re-run the command and add --port 4030 so the second instance will not use 156 | the default 4020 port. 157 | 158 | Using your favorite web browser, visit 159 | http://localhost:4020/TodosOne. If all goes 160 | well, you should see "Welcome to Sproutcore!" in the middle of the screen. 161 | 162 | ### Making Changes 163 | 164 | Open up resources/main_page.js and change the "Welcome to SproutCore!" 165 | line to say "SproutCore is Awesome!". Save the file and refresh the page in 166 | your web browser. If the message updates properly, you're ready to move on to 167 | [Getting Started: Part 2](/getting_started_2.html) of the tutorial. 168 | 169 | ### Troubleshooting 170 | 171 | As with any unfamiliar framework, you may hit a few snags on the way. Below 172 | we list some of the common pitfalls that users have discovered, with 173 | solutions for getting past them. 174 | 175 | * *"Browser says 'No matching target'"* - This is generally caused if you 176 | misspelled the name of the application in the URL. Make sure that you properly 177 | typed it, and that you used underscores instead of spaces. For example: 178 | http://localhost/TodosOne. 179 | 180 | * *"ERROR: missing } after property list ..."* - This error is usually thrown 181 | if you added a property somewhere, but forgot to put a comma at the end. 182 | Look at the line in the error, and then look above it to see if there is 183 | a comma missing. 184 | 185 | ### Changelog 186 | 187 | * March 1, 2011: initial version by [Tom Dale](credits.html#tomdale and [Yehuda Katz](credits.html#wycats) 188 | * March 2, 2011: fixed formmating and added paths to filenames by [Topher Fangio](credits.html#topherfangio) 189 | and [Peter Wagenet](credits.html#wagenet) 190 | * March 22, 2011: cleaned up demo based on new features by [Yehuda Katz](credits.html#wycats) 191 | * April 11, 2011: consistently use view classes and extend, update to reflect better Handlebars integration 192 | by [Yehuda Katz](credits.html#wycats) and [Tom Dale](credits.html#tomdale) 193 | * May 6, 2011: clarifications, minor inconsistency fixes, updated CSS for older browsers, plus new mobile section 194 | by [Tyler Keating](credits.html#publickeating) 195 | * May 9, 2011: update for recent changes in SproutCore 1.6 by [Tom Dale](credits.html#tomdale) and [Yehuda Katz](credits.html#wycats) 196 | * March 6, 2012: rewrite for SproutCore 1.8 by the 1.8 release sprint team, including the following who did 197 | much work on this task: [Tim Evans](credits.html#tce), [Topher Fangio](credits.html#topherfangio) and [Jeff Pittman](credits.html#geojeff) 198 | * March 12, 2012: added new error and fixed italics in the troubleshooting section by [Topher Fangio](credits.html#topherfangio) 199 | * July 30, 2013: converted to Markdown format for DocPad guides by [Eric Theise](credits.html#erictheise) 200 | * July 30, 2013: fix issues with formatting and updated Changelog by [Topher Fangio](credits.html#topherfangio) 201 | * January 17, 2019: Change to NPM build tools. -------------------------------------------------------------------------------- /src/documents/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | sc_title: Getting Started 4 | --- 5 |
6 |

Start Here

7 | 8 |
9 |
Getting Started: Part 1
10 |
11 |

Learn how to get your first SproutCore app up-and-running.

12 |
13 |
Getting Started: Part 2
14 |
15 |

Spring forward with the information you learned in Part 1 and learn about statecharts and views.

16 |
17 |
Getting Started: Part 3
18 |
19 |

Expand upon your knowledge from the first two guides, and build a full-scale Todos application.

20 |
21 |
Classes and SC.Object
22 |
23 |

In this guide, we will touch on SC.Object, the basis for SproutCore's class-like object-oriented programming structure.

24 |
25 |
Computed Properties, Observers and Bindings
26 |
27 |

In this guide, we will touch on KVO in SproutCore.

28 |
29 |
30 |

Views

31 | 32 |
33 |
Core View Concepts
34 |
35 |

In this guide, we will cover the basics of SproutCore views, and show you how to customize the built-in SproutCore views for your own use.

36 |
37 |
38 |

Models

39 | 40 |
41 |
SproutCore Records
42 |
43 |

The SproutCore data framework is a full-featured ORM-like framework for working with data in SproutCore. Learn about the SproutCore data framework and how you can use it to interact with your data sources.

44 |
45 |
Using Fixtures
46 |
47 |

In order to facilitate rapid development, SproutCore provides a facility for using pre-set fixture data as your data source. This guide covers how to set up your fixtures, and how to replace them with your real server once you're ready to get going.

48 |
49 |
50 |

Theming

51 | 52 |
53 |
Theming Your App
54 |
55 |

In this guide, we'll cover how to change the style of SproutCore controls in your app. As an example, we'll re-theme an SC.ButtonView.

56 |
57 |
58 |

Testing

59 | 60 |
61 |
Unit Testing
62 |
63 |

SproutCore has an easy-to-learn unit test facility that can be used to drive SproutCore test-driven development. This guide describes the basic SproutCore test commands.

64 |
65 |
Adding a Unit Test
66 |
67 |

Adding a unit test automatically or manually.

68 |
69 |
Writing Unit Tests
70 |
71 |

Describes how to write unit tests using module and test functions

72 |
73 |
Running Unit Tests
74 |
75 |

How tests are loaded and executed by the test runner.

76 |
77 |
SproutCore Development Using TDD
78 |
79 |

Show implementing the TODOs project using TDD.

80 |
81 |
82 |

Extras

83 | 84 |
85 |
Build Tools
86 |
87 |

Discover the many performance advantages and development benefits of SproutCore's build tools.

88 |
89 |
The Run Loop
90 |
91 |

An indepth look at SproutCore's Run Loop for an advanced understanding of the framework.

92 |
93 |
Enumerables
94 |
95 |

To simplify working with various kinds of lists, SproutCore provides a unified Enumerable interface. This guide explores which SproutCore objects are Enumerable, and how to use the Enumerable API.

96 |
97 |
98 |

Contributing to SproutCore

99 | 100 |
101 |
Javascript Style Guide
102 |
103 |

This guide covers the style of SproutCore framework code.

104 |
105 |
Committer Guidelines
106 |
107 |

A detailed checklist of the do's and don'ts of SC framework code.

108 |
109 |
Documentation Guidelines
110 |
111 |

Writing and generating jsdoc documentation.

112 |
113 |
114 |
115 | 116 | 117 |
118 |

Get Involved

119 | 120 |

121 | 122 | Contribute New Guides
123 | SproutCore Guides are a community effort; contribute your own! 124 |

125 | 126 |

127 | 128 | Author Credits
129 | Meet the team responsible for putting the SproutCore Guides together. 130 |

131 | 132 |

Icon Legend

133 | 134 |

135 | 136 | The warning icon means the content you're looking at isn't quite done yet. 137 |

138 | 139 |

140 | 141 | Notes are bits of important side info you want to make sure you pay attention to. 142 |

143 | 144 |

145 | 146 | Pins are for supplementary info tidbits you might want; background, fun facts, etc. 147 |

148 | 149 |

150 | 151 | This icon tells you you're looking at code. Which you likely knew... It's still pretty :p 152 |

153 | 154 |
155 | -------------------------------------------------------------------------------- /src/documents/no_store.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_draft: true 4 | sc_category: Models 5 | sc_order: 50 6 | sc_title: No DataStore 7 | sc_description: SproutCore includes a powerful model layer, called the datastore. SproutCore has special code in it's controller and view layers to make the datastore the most efficient source of data. 8 | --- 9 | 10 | ### MVC -- The model layer. 11 | 12 | WARNING: This is a Draft Copy, and although the information in here is correct, 13 | it may be incomplete in places. 14 | 15 | Nevertheless, you don't have to use the datastore in your application. Like 16 | most high-quality frameworks, the datastore is an optional framework in 17 | SproutCore (though it is "on" by default). 18 | 19 | ### How To Use SproutCore Without a Model Layer 20 | 21 | This is how you do what backbone.js does in SproutCore. It does not use 22 | SproutCore's datastore. 23 | 24 | See http://www.jamesyu.org/2011/01/27/cloudedit-a-backbone-js-tutorial-by-example/ 25 | for an example. 26 | 27 | ```javascript 28 | #filename="apps/my_app/core.js" 29 | var MyApp = SC.Application.create({ 30 | 31 | newTask: function() { 32 | // optional: clear the values in the fields 33 | MyApp.newTaskPane.append(); 34 | }, 35 | 36 | createTask: function() { 37 | // 1. Fetch the values from the fields in MyApp.newTaskPane. 38 | var name = MyApp.newTaskPane.get('nameField'), 39 | description = MyApp.newTaskPane.get('descriptionField'); 40 | 41 | // 2. Send the values to the remote "create" method 42 | // (disable interaction) 43 | SC.Request.postUrl('/tasks') 44 | .header({ 'Accept': 'application/json' }).json() 45 | .notify(this, '_didCreateTask').send(); 46 | }, 47 | 48 | // 3. When the server responds, create the task and add update 49 | // MyApp.tasksController. 50 | _didCreateTask: function(response, store, storeKey) { 51 | if (SC.ok(response)) { 52 | var hash = response.get('body'); 53 | MyApp.Task.create(hash); 54 | } 55 | 56 | // 4. Show the MyApp.editTaskPane 57 | MyApp.editTaskPane.append(); 58 | }, 59 | 60 | updateTask: function() { 61 | // 1. Get the values from MyApp.selectedTaskController 62 | var task = MyApp.selectedTaskController.get('content'); 63 | 64 | // 2. Contact the server and send the updated properties 65 | // for the task (disable interaction) 66 | // 3. When the server responds, refresh the task with then 67 | // new properties. 68 | MyApp.editTaskPane.append(); 69 | }, 70 | 71 | deleteTask: function() { 72 | // 1. Search MyApp.tasksController for the task with id = `id`. 73 | var task = MyApp.selectedTaskController.get('content'); 74 | 75 | // 2. Contact the server and ask it delete the task with 76 | // id == `id` (disable interaction) 77 | // 3. When the server responds, delete the task and remove 78 | // it from the list. 79 | MyApp.listTasksPane.append(); 80 | }, 81 | 82 | showTaskList: function() { 83 | // 1. Use SC.Response ajax to contact your server. 84 | // You can hardcode the url in here: 85 | SC.Request.getUrl('/tasks').json() 86 | .notify(this, '_listDidRespond').send(); 87 | MyApp.listTasksPane.append(); 88 | } 89 | 90 | _listDidRespond: function(res) { 91 | // 2. When you get the response, instatiate MyApp.Task objects 92 | // and add them to a new array 93 | var newArray = []; 94 | var jsonHashesArray = res.get('body'); 95 | jsonHashesArray.forEach(function(hash) { 96 | var task = MyApp.Task.create(hash); 97 | newArray.push(task); 98 | }); 99 | 100 | // 3. Set the new array as the "content" 101 | MyApp.tasksController.set('content', newArray); 102 | } 103 | 104 | }); 105 | 106 | // 107 | // Define our model class. 108 | // 109 | MyApp.Task = SC.Object.extend({ 110 | 111 | toJSON: function() { 112 | var hash = {}, key, val; 113 | 114 | for (key in this) { 115 | if (!this.hasOwnKey(key)) continue; 116 | 117 | val = this[key]; 118 | if (val.isProperty) val = this.get('val'); 119 | hash[key] = this.get(key); 120 | } 121 | } 122 | 123 | }); // gives us the "create" function. 124 | 125 | // 126 | // Make our Task list available to views. 127 | // 128 | MyApp.tasksController = SC.ArrayController.create({ 129 | 130 | content: [], 131 | allowsMultipleSelection: NO, 132 | 133 | }); 134 | 135 | // 136 | // Keeps track of the selected task so we can bind to it in 137 | //our views. 138 | // 139 | MyApp.selectedTaskController = SC.ObjectController.create({ 140 | 141 | contentBinding: SC.Binding 142 | .single('MyApp.tasksController.selection') 143 | 144 | )}; 145 | 146 | // 147 | // Create a pane to display our task list. 148 | // 149 | MyApp.listTasksPane = SC.MainPane.create({ 150 | 151 | contentView: SC.View.create({ 152 | 153 | childViews: 'list new'.w(), 154 | 155 | list: SC.ListView.create({ 156 | layout: { centerX:0, top:50, width:300, bottom:100 }, 157 | 158 | contentBinding: 'MyApp.tasksController.arrangedObjects', 159 | selectionBinding: 'MyApp.tasksController.selection' 160 | 161 | actsOnSelect: YES, 162 | action: 'editTask', 163 | target: MyApp 164 | }), 165 | 166 | new: SC.ButtonView.create({ 167 | layout: { centerX:60, height:54, width:90, bottom:50 }, 168 | title: "New Task", 169 | action: 'newTask', 170 | target: MyApp 171 | }) 172 | }) 173 | }); 174 | 175 | // 176 | // Create a new task pane. 177 | // 178 | MyApp.newTaskPane = SC.MainPane.create({ 179 | 180 | nameField: SC.outlet('contentView.form.name'), 181 | descriptionField: SC.outlet('contentView.form.description'), 182 | 183 | contentView: SC.View.create({ 184 | 185 | childViews: 'form new'.w(), 186 | 187 | form: SC.View.create({ 188 | layout: { centerX:0, top:50, width:300, bottom:100 }, 189 | 190 | childViews: 'name description'.w(), 191 | 192 | name: SC.TextFieldView.create({ 193 | layout: { top:10, left:10, right:10, height:80 }, 194 | hint: "Enter a task name" 195 | }), 196 | 197 | description: SC.TextFieldView.create({ 198 | layout: { top: 100, left: 10, right: 10, bottom: 10 }, 199 | allowsMultipleLines: YES 200 | }) 201 | 202 | }), 203 | 204 | new: SC.ButtonView.create({ 205 | layout: { centerX:60, height:54, width:90, bottom:50 }, 206 | title: "Create", 207 | action: 'createTask', 208 | target: MyApp 209 | }) 210 | }) 211 | }); 212 | 213 | // 214 | // Create an edit task pane. 215 | // 216 | MyApp.editTaskPane = SC.MainPane.create({ 217 | 218 | contentView: SC.View.create({ 219 | 220 | childViews: 'form update delete'.w(), 221 | 222 | form: SC.View.create({ 223 | layout: { centerX:0, top:50, width:300, bottom:100 }, 224 | 225 | childViews: 'name description'.w(), 226 | 227 | name: SC.TextFieldView.create({ 228 | layout: { top:10, left:10, right:10, height:80 }, 229 | valueBinding: 'MyApp.selectedTaskController.name' 230 | }), 231 | 232 | description: SC.TextFieldView.create({ 233 | layout: { top: 100, left: 10, right: 10, bottom: 10 }, 234 | allowsMultipleLines: YES, 235 | valueBinding: 'MyApp.selectedTaskController.description' 236 | }) 237 | 238 | }), 239 | 240 | update: SC.ButtonView.create({ 241 | layout: { right:10, height:54, width:90, bottom:50 }, 242 | title: "Update", 243 | action: 'updateTask', 244 | target: MyApp 245 | }), 246 | 247 | delete: SC.ButtonView.create({ 248 | layout: { right:120, height:54, width:90, bottom:50 }, 249 | title: "Delete", 250 | action: 'deleteTask', 251 | target: MyApp 252 | }) 253 | }) 254 | }); 255 | 256 | main() { 257 | MyApp.showTaskList(); // Puts the list on the screen. 258 | }; 259 | ``` 260 | 261 | 262 | ### Changelog 263 | 264 | * March 2, 2011: initial version by [Erich Ocean](credits.html#erichocean) and 265 | [Geoffrey Donaldson](credits.html#geoffreyd) 266 | * March 2, 2011: cleanup by [Peter Wagenet](credits.html#wagenet) 267 | * March 7, 2011: small bug fixes and filename addition by 268 | [Topher Fangio](credits.html#topherfangio) 269 | * August 14, 2013: converted to Markdown format for DocPad guides by 270 | [Topher Fangio](credits.html#topherfangio) 271 | -------------------------------------------------------------------------------- /src/documents/run_loop.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Extras 4 | sc_order: 20 5 | sc_title: The Run Loop 6 | sc_description: The guide covers SproutCore's Run Loop. By referring to this guide, you will be able to: 7 | sc_list: 8 | - Understand the Run Loop 9 | - Know when to manually trigger a Run Loop (rare). 10 | - Know how to use invokeOnce(), invokeLast() and invokeNext() to adjust the execution flow of code. 11 | --- 12 | 13 | ### The Run Loop 14 | 15 | The Run Loop coordinates all the events within your application, which includes 16 | primarily flushing bindings when observers update. This helps make sure that 17 | events stay synchronized and run at the proper times. One of the main ways this 18 | will affect your application is that bindings do not propagate unless a Run 19 | Loop runs. Also, Views will only render updates once per Run Loop, thus 20 | avoiding unnecessarily touching the DOM. This keeps display updates extremely 21 | fast even if many properties are changing per Run Loop. 22 | 23 | SproutCore manages the Run Loop for you, automatically starting a Run Loop when 24 | it receives any browser events or user input. The only case in which you will 25 | have to manage the Run Loop from within your app is _if you have a callback 26 | from an external library that is not managed by SproutCore_. In this 27 | circumstance, you will want to trigger a new Run Loop by wrapping the code in 28 | +SC.run()+. This will create a new Run Loop for that bit of code and will make 29 | sure your application continues to update as expected. 30 | 31 | Here is an example of triggering a Run Loop when a Web Worker message event is 32 | fired. 33 | 34 | ```javascript 35 | worker = new Worker(path); 36 | 37 | worker.onmessage = function(event) { 38 | SC.run(function() { 39 | // Do something 40 | }); 41 | }; 42 | ``` 43 | 44 | NOTE: See +SC.Event.addEvent()+ for a SproutCore compatible way to add 45 | the `message` event listener in the example above. 46 | 47 | #### Using the Run Loop in Unit Tests 48 | 49 | When working with unit tests, you will also have situations where you need to 50 | force bindings and observers to update so that you can check for the correct 51 | results. In these cases, it is completely legitimate to manually invoke the Run 52 | Loop. Normally user events would trigger the Run Loop, but since your tests are 53 | automated, there are no user events taking place. 54 | 55 | You may also find that when you are working in your browser's JavaScript 56 | console that, when you set a variable, your application's interface doesn't 57 | update as you expect. This may be caused by a Run Loop not firing (since you 58 | aren't interacting directly with the application no events are being 59 | triggered). In this case you should try invoking the Run Loop manually or just 60 | move your mouse over the application to trigger a new event. 61 | 62 | WARNING: Remember, you rarely need to manage the Run Loop manually from within 63 | your application. If you aren't certain of why you are forcing a new Run Loop, 64 | then you may be doing something wrong. 65 | 66 | ### The Run Loop Methods: invokeLast(), invokeOnce(), invokeNext() 67 | 68 | SC.RunLoop provides three methods for greater control over the execution of 69 | code within a Run Loop: +invokeOnce()+, +invokeLast()+ and +invokeNext()+. All 70 | methods take the same parameters, a +target+ and a +method+ and, depending on 71 | which function is used, the method is called on the target at a specific point 72 | in the Run Loop. 73 | 74 | The following describes the execution order: 75 | 76 | * functions passed to +invokeOnce()+ are run at the beginning of the current Run Loop, 77 | * functions passed to +invokeLast()+ are run at the end of the current Run Loop, and 78 | * functions passed to +invokeNext()+ are run at the very beginning of the _next_ Run Loop. 79 | 80 | INFO: You will likely never call these methods on SC.RunLoop itself, since 81 | there are matching convenience methods defined on SC.Object (i.e. in any 82 | subclass of SC.Object you can call +this.invokeLater(method)+). 83 | 84 | To best illustrate the different stages of the Run Loop, see the following 85 | diagram. 86 | 87 | ![Run Loop Diagram](images/run_loop_diagram.jpg) 88 | 89 | You will notice the recursive "More changes?" stages. These exist because each 90 | time a wave of changes is propagated, it may cause several additional observers 91 | to fire and more code to be run. The Run Loop keeps flushing these waves of 92 | changes until everything is stable, thus demonstrating another key benefit of 93 | using Run Loops; that at the end of each Run Loop and before the next event, 94 | the application's state is guaranteed to be stabilized. 95 | 96 | Here are some typical usage scenarios for each of the Run Loop methods: 97 | 98 | * If you have a function that could be called from multiple places, but you 99 | only want it to execute once no matter how many times it was referenced, 100 | use +invokeOnce()+. 101 | * If you have a function that you want to run after all bindings have flushed 102 | and after views have updated, use +invokeLast()+. 103 | * If you have a function that you want to defer entirely, such as a View paint, 104 | use +invokeNext()+. 105 | 106 | Therefore, as the previous usage scenarios suggest, you will not typically find 107 | the need to use any of these methods. The most common exception is that you 108 | may occasionally use invokeLast() to ensure that your Views have rendered 109 | before doing some operation on dependent on their rendered output, such as an 110 | animation. 111 | 112 | NOTE: If you add the same +target+ and +method+ pair multiple times to any of 113 | these functions, it will still only be executed once. 114 | 115 | ### Changelog 116 | 117 | * August 22, 2012: initial version by [Public Keating](credits.html#publickeating) 118 | * August 14, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 119 | * August 14, 2013: minor formatting changes by [Topher Fangio](credits.html#topherfangio) 120 | -------------------------------------------------------------------------------- /src/documents/running_unit_tests.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Testing 4 | sc_order: 40 5 | sc_title: Running Unit Tests 6 | sc_description: After reading this guide, you will be able to: 7 | sc_list: 8 | - Understand how unit test files are loaded and executed 9 | - Use the GUI Test Runner 10 | - Manually run unit test(s) 11 | --- 12 | 13 | ### How Unit Test Cases Are Loaded and Executed 14 | 15 | When you run a unit test, SproutCore retrieves and runs the source unit test 16 | JavaScript and renders the results as an HTML page. 17 | 18 | SproutCore does this in the following sequence: 19 | 20 | 1. Load the application including its dependencies. 21 | 1. Load the unit tests. 22 | 1. Instead of calling the main application's +main()+ function, runs all or 23 | some of the unit tests. 24 | 1. Like most javascript test frameworks, SproutCore renders the results in a 25 | nicely-formatted HTML page. 26 | 27 | The advantage is that your unit tests are run in an environment that is very 28 | close to the actual combination of code and resources that will be available to 29 | your application when it runs. 30 | 31 | It also means that if you write code that runs on page load BEFORE the +main()+ 32 | method is called, it could be executed even in test mode. Make sure that any 33 | code you write to run at early page startup can deal with being loaded in test 34 | mode as well. 35 | 36 | 37 | ### Using the SproutCore Test Runner 38 | 39 | WARNING: The new Abbot tools do not yet support the test runner. Until they 40 | do, you should follow the 41 | [Running Selected Unit Tests](running_unit_tests.html#running-selected-unit-tests) section below. 42 | 43 | #### Starting the Test Runner 44 | 45 | The easiest way to run unit tests is to simply visit the built-in unit test 46 | runner by starting your browser and navigating to http://localhost:4020, then 47 | selecting +tests+ from the application list as shown. 48 | 49 | Test Runner 50 | 51 | Your unit tests for your application(s) will be listed under +APPS+. The unit 52 | tests for SproutCore itself are listed under +SPROUTCORE+. 53 | 54 | For quicker access to this page, bookmark http://localhost:4020/sproutcore/tests. 55 | 56 | #### Selecting and Running Tests 57 | 58 | Suppose your application is named "my_app". To see a list of your unit tests, 59 | in the left hand column under +APPS+, double-click "my_app": 60 | 61 | Test Runner Unit Test Files 62 | 63 | Run all the unit test cases within a file by clicking on it. 64 | 65 | NOTE: The "+Using Continuous Integration+" checkbox and the "+Run Tests+" 66 | button are under construction. 67 | 68 | When the test run is complete, the results are displayed as follows: 69 | 70 | Test Runner Unit Test Results 71 | 72 | 73 | ### Running Selected Unit Tests 74 | 75 | Sometimes you may want to run unit tests manually outside of the test runner. 76 | For example, perhaps you have a continuous integration environment and would 77 | like to use Selenium to evaluate the results of running your unit tests. 78 | 79 | All unit tests for an application can be found at an easily computed URL. 80 | 81 | The computed URLs can run all tests on all applications or a subset of them as 82 | detailed below. 83 | 84 | 85 | #### Running an Individual Unit Test File 86 | 87 | Any unit test file can be run from a browser using a URL in the following 88 | format: 89 | 90 | http://localhost:4020/APP_NAME/LANGUAGE/current/tests/PATH/TO/TEST.html@ 91 | 92 | where: 93 | 94 | * +APP_NAME+ is your application name. 95 | * +LANGUAGE+ is the language you want to run tests for. (Usually 'en') 96 | * +PATH/TO/TEST+ is the subpath from the +PROJECT_FOLDER/apps/APP_NAME/tests+ 97 | folder. It should include the body of the javascript file name but 98 | * not the +.js+ extension. 99 | 100 | As an example, suppose you have a unit test file located at: 101 | 102 | PROJECT_HOME/apps/my_app/tests/unit/sample.js 103 | 104 | To run this test, browse: 105 | 106 | http://localhost:4020/my_app/en/current/tests/unit/sample.html 107 | 108 | #### Running All Unit Tests in a Directory 109 | 110 | If you would like to run all of the unit tests in a directory, you can use the 111 | same method described as above, but instead name the directory with an .html 112 | extension. For example, to run all of the unit tests in the +tests/unit+ 113 | directory, you could visit: 114 | 115 | http://localhost:4020/my_app/en/current/tests/unit.html 116 | 117 | Note that the HTML file loaded at this URL will contain all of your unit tests 118 | files concatenated together in a single HTML page. If your tests are not 119 | properly isolated, they may interfere with one another when loaded this way, 120 | though this is rare. 121 | 122 | #### Running All Unit Tests in an Application 123 | 124 | If you would like to run all of the unit tests in your application, you can do 125 | that too. Instead of naming a directory, just visit a URL with the format 126 | described above but ending in tests.html. For example, to load all of the unit 127 | tests at once for your contact application you could visit: 128 | 129 | http://localhost:4020/my_app/en/current/tests.html 130 | 131 | Note that, like the combined directory tests above, this global test file will 132 | contain all of your unit test files concatenated together in a single HTML 133 | page. If your tests are not properly isolated, they may interfere with one 134 | another, causing the unit tests to pass when loaded individually but fail when 135 | they are loaded together. This situation is rare, but you should be careful to 136 | write your test files so that they will not clobber one another at load time. 137 | 138 | #### Loading Tests from Nested Targets 139 | 140 | If you use nested targets, you can load tests from them as well. Just name 141 | your nested target like you name it in a config file. For example, the 142 | sproutcore framework contains a nested framework called "runtime". You could 143 | load all of the unit tests for the sproutcore/runtime framework by visiting: 144 | 145 | http://localhost:4020/sproutcore/runtime/en/current/tests.html 146 | 147 | ### Moving On 148 | 149 | This concludes the main programming guide for working with Unit Tests in 150 | SproutCore applications. You're now ready to change the world with awesome 151 | unit tests! 152 | 153 | On to the [SproutCore Testing Guidelines »](/testing_guidelines.html) 154 | 155 | ### Changelog 156 | 157 | * February 15, 2011: initial version by [Scott Smith](credits.html#oldfartdeveloper) and [Vibul Imtarnasan](credits.html#veebs) 158 | * February 15, 2011: minor revisions by [Peter Wagenet](credits.html#wagenet) 159 | * March 2, 2011: minor revisions by [Topher Fangio](credits.html#topherfangio) 160 | * March 2, 2011: minor revisions by [Vibul Imtarnasan](credits.html#veebs) 161 | * August 14, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 162 | -------------------------------------------------------------------------------- /src/documents/test.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_draft: true 4 | sc_category: Invisible 5 | sc_order: 10 6 | sc_title: Test Page 7 | sc_short_description: Making Sure it Works 8 | sc_description: This is a sample guide designed to test: 9 | sc_list: 10 | - Proper translation of +something+ to something 11 | - Automatic heading numbering 12 | - Generation of Table of Contents 13 | - Code blocks with filenames 14 | - Provide NOTE/INFO/WARNING shortcuts 15 | --- 16 | 17 | ### I'm the first heading! 18 | 19 | This is the first paragraph. It's nice and short. 20 | 21 | ``` bash 22 | $ some bash code here 23 | ~ Yup...I think this worked... 24 | ``` 25 | 26 | Another paragraph goes here. I could type for a fairly long time and make some really long sentences 27 | and keep it going until the end of all days, but I think I shall defer until a better time. That makes 28 | much more +sense+. 29 | 30 | `some/one/line/code/here` 31 | 32 | Here is some more paragraph text. 33 | 34 | #### I'm a subheading. 35 | 36 | I'm even more text. 37 | 38 | WARNING: Woah! Watch out for me. 39 | 40 | ##### I'm a sub-subheading. 41 | 42 | And here is some more text. 43 | 44 | ### I am the second heading. I'm not so great. 45 | 46 | NOTE: I'm a pretty cool note! 47 | 48 | Woot, we got more text coming right at you! 49 | 50 | #### Another subheading. 51 | 52 | With some more text. 53 | 54 | INFO: I'm just here to tell you about something. 55 | 56 | And here is some fancy code below: 57 | 58 | ``` ruby 59 | # filename: /some/ruby/file.rb 60 | a = 1 + 2 61 | b = a / 3 62 | c = a * b 63 | 64 | puts a, b, c 65 | ``` 66 | 67 | ##### Another sub-subheading. 68 | 69 | With even more text/code. 70 | 71 | ``` javascript 72 | # filename: /i/am/some/javascript3.js 73 | { 74 | here: { 75 | comes: "the bride", 76 | 1: "more time", 77 | "something else": [3, 'b', { fu: 'bar' }] 78 | } 79 | } 80 | ``` 81 | 82 | ##### Yet one more sub-subheading. 83 | 84 | With the last bit of text. 85 | -------------------------------------------------------------------------------- /src/documents/test2.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_draft: true 4 | sc_category: Invisible 5 | sc_order: 20 6 | sc_title: Test Page 2 7 | sc_short_description: Testing the Index 8 | sc_description: This is a sample guide designed to test: 9 | sc_list: 10 | - Proper index creation 11 | --- 12 | -------------------------------------------------------------------------------- /src/documents/testing_guidelines.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_draft: true 4 | sc_category: Testing 5 | sc_order: 60 6 | sc_title: Testing Guidelines 7 | sc_description: This guide covers using test-driven development (TDD) techniques to develop SproutCore applications. After reading this guide, you will be able to: 8 | sc_list: 9 | - Write tests for methods to be implemented and use them to help you implement them. 10 | - Write tests to verify that your observers fire when you expect them to. 11 | - Write tests that verify your bindings fire when you expect them to and how to incorporate +SC.RunLoop+ so that they will function. 12 | - TODO - more 13 | --- 14 | 15 | WARNING: This guide is under construction. 16 | 17 | ### Test-drive SproutCore Method Implementation 18 | 19 | ### Test-drive Observer Implementation 20 | 21 | ### Test-drive Bindings Implementation 22 | 23 | ### Next 24 | 25 | Let's reinvent the *todos* project using TDD techniques. 26 | 27 | On to [SproutCore Development Using TDD »](/todos_tdd.html) 28 | 29 | ### Changelog 30 | 31 | * February 15, 2011: initial placeholder by [Scott Smith](credits.html#oldfartdeveloper) and [Vibul Imtarnasan](credits.html#veebs) 32 | * August 14, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 33 | -------------------------------------------------------------------------------- /src/documents/unit_test_framework.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Testing 4 | sc_order: 10 5 | sc_title: Unit Testing 6 | sc_short_description: About Unit Testing 7 | sc_description: This guide covers the SproutCore unit test API and some ideas on how to use it. After reading this guide, you will: 8 | sc_list: 9 | - Know how to create your unit tests and where to locate them. 10 | - How to run your tests. 11 | - How to get the most out of unit testing in SproutCore. 12 | --- 13 | 14 | ### Overview 15 | 16 | Rigorous unit testing is a key principle to modern agile development. Unit tests not only make it easier for other developers to understand and work 17 | with your code, it makes it easier for you to maintain your own code as well. 18 | 19 | In the past, unit testing JavaScript has been a difficult process, primarily because the infrastructure needed to run unit tests required a lot of 20 | manual setup. 21 | 22 | SproutCore makes unit testing far easier by bundling an integrated testing framework as well as a test runner right into the build tools. The testing 23 | framework is based on [jQuery's QUint](http://docs.jquery.com/QUnit). 24 | 25 | ### How it Works 26 | 27 | #### Where Unit Test Files Are Stored 28 | 29 | Unit test files are stored in a directory, +tests+, under your SproutCore application subfolder. For example, if your application is 30 | named +hello_world+, then the +tests+ folder will have this project path: +hello_world/apps/hello_world/tests+. 31 | 32 | If this folder doesn't already exist for you, then add it at this time. 33 | 34 | #### Adding Unit Test Files 35 | 36 | When you create a code file using +sc-gen+, it will automatically generate the skeleton for a unit test that you can enhance to test your code file. 37 | Then simply open the test file and write some tests. 38 | 39 | To add a unit test file manually, simply create it inside the +tests+ directory. 40 | 41 | See [Adding a Unit Test](/adding_unit_test.html) for more information on this process. 42 | 43 | #### Writing Unit Test Cases 44 | 45 | Once you've added a unit test file, write your unit test cases using the QUnit testing API. A simple unit test looks like this: 46 | 47 | ```javascript 48 | module("HelloWorld") ; 49 | test("value of 'string' is 'Hello World', function() { 50 | ok(HelloWorld.get('string'), 'Hello World', "has preset string"); 51 | }); 52 | ``` 53 | 54 | See [Writing a Unit Test Case](/writing_unit_tests.html) for more information on this process. 55 | 56 | #### Running Unit Test Cases 57 | 58 | The test runner is activated when you launch +sc_server+. 59 | 60 | You run all the tests by simply browsing to [http://localhost:4020/sproutcore/tests](http://localhost:4020/sproutcore/tests). 61 | 62 | You can also test a subset of unit tests; see [Running Unit Test Cases](/running_unit_test.html) for more information. 63 | 64 | ### Getting the Most Out of Unit Testing 65 | 66 | To get the best results out of your test running, you should get into the habit of adding unit tests for every major function in your application. It 67 | is not necessary to add unit tests for every single method you write, but you should make sure your unit tests cover the major pieces of your app that 68 | are stitched together with bindings. This is especially important for any complicated engines or for components with a large number of moving parts 69 | since they tend to break easily. 70 | 71 | Assuming you have good unit tests, you should also make sure everyone in your team runs all of the unit tests at least once before committing any 72 | changes to your source repository. Try to never commit source changes that leave tests failing. This way unit tests will remain a useful check for 73 | every developer on the project to ensure they have not broken another piece of the code. 74 | 75 | In order to achieve the best test coverage, you need to run your unit tests in every target web browser you intend to run your application in. For 76 | example, when we write SproutCore code, we will load the test runner in IE6, IE7, IE8, Safari, Chrome, and Firefox. Since running unit tests this 77 | many times can become very labor intensive if you try to do it before every checkin, usually the best thing to do is to run your unit tests before 78 | every checkin in only one or two browsers such as Firefox and Safari. Periodically you should set milestones in your project development where you 79 | will run through the unit tests on all browsers and fix anything that breaks unit tests for continuing with your development. 80 | 81 | Above all, the most important thing about unit testing is to write and run your unit tests regularly. Unit tests are only useful if you maintain 82 | them. If you write too much code that is not covered by unit tests, they will not prevent enough errors for people on your team to use them 83 | regularly. Likewise, if you let your unit tests failing for too long, developers will look at test failures as "noise" and tend to ignore them. 84 | 85 | ### Moving On 86 | 87 | Now that you know the basics of how to use unit tests, it's time to start adding unit tests to your project. 88 | 89 | See [Adding Unit Test Files »](/adding_unit_test.html) 90 | 91 | ### Changelog 92 | 93 | * February 15, 2011: initial version by [Scott Smith](credits.html#oldfartdeveloper) and [Vibul Imtarnasan](credits.html#veebs) 94 | * March 2, 2011: paragraph formamtting by [Topher Fangio](credits.html#topherfangio) 95 | * August 7, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 96 | -------------------------------------------------------------------------------- /src/documents/writing_unit_tests.html.md.sd: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | sc_category: Testing 4 | sc_order: 30 5 | sc_title: Writing Unit Tests 6 | sc_description: After reading this guide, you will be able to: 7 | sc_list: 8 | - Use modules to group your unit tests together to share setup and teardown code. 9 | - Use assertions to test values and report results. 10 | - Use start and stop to synchronize wtih an asynchronous event such as a timer or callback. 11 | - Specify HTML in the case where you need to test against HTML. 12 | --- 13 | 14 | ### Overview 15 | 16 | Once you've [added a unit test file](/adding_unit_test.html), writing your unit test cases is easy. 17 | 18 | A unit test file is made up of modules. A module is a collection of unit test cases that run together. An individual unit test case performs some 19 | verification on your code. 20 | 21 | ### Defining a Module 22 | 23 | To define a module, call the +module()+ method. For example, in the [previous guide](/adding_unit_test.html), a module called +MyApp.loginController+ 24 | is created. 25 | 26 | ```javascript 27 | module("MyApp.loginController"); 28 | ``` 29 | 30 | Unit test cases defined after calling +module()+ will automatically be grouped into that module until the next +module()+ call or the end of the file. 31 | 32 | ```javascript 33 | module("MyApp.loginController.module1"); 34 | test ("test1 for MyApp.loginController module 1", ...); 35 | test ("test2 for MyApp.loginController module 1", ...); 36 | 37 | module("MyApp.loginController.module2"); 38 | test ("test1 for MyApp.loginController module 2", ...); 39 | test ("test2 for MyApp.loginController module 2", ...); 40 | ``` 41 | 42 | Within the +module+, you may optionally implement +setup()+ and +teardown()+ callbacks to run before and after each unit test case for that module. 43 | Use these methods to setup and teardown any common testing infrastructure. 44 | 45 | For example, if you are writing a group of tests to work on a +Contact+ record, you might define your module like so: 46 | 47 | ```javascript 48 | var contact ; 49 | module("Test Contact record", { 50 | setup: function() { 51 | contact = MyApp.Contact.create({ firstName: "John", lastName: "Doe" }); 52 | }, 53 | teardown: function() { 54 | contact = null; // reset 55 | } 56 | }); 57 | 58 | // .. unit tests go here 59 | ``` 60 | 61 | Note that any common setup you define should be placed into global variables (such as the +contact+ variable above). Since unit tests are the last 62 | thing loaded in the page, it is OK to create these variables globally instead of isolating everything into namespaces like you should with all other 63 | code. 64 | 65 | ### Defining a Unit Test Case 66 | 67 | Once you've defined a module, you can add individual unit test cases. A unit test case is defined using the +test()+ method. A test should have a 68 | description followed by the function you want to execute to perform the test. 69 | 70 | ```javascript 71 | test("test description", function() { 72 | var expected = "test"; 73 | var result = "test"; 74 | equals(result, expected, "test should equal test"); 75 | }); 76 | ``` 77 | 78 | For example, if you were writing tests for a +Contact+ model object, you might add a test like this: 79 | 80 | ```javascript 81 | test("fullName property returns combined firstName and lastName", function() { 82 | // set preconditions 83 | contact.set('firstName', 'John'); 84 | contact.set('lastName', 'Doe'); 85 | 86 | // verify 87 | equals(contact.get('fullName'), 'John Doe'); 88 | }); 89 | ``` 90 | 91 | NOTE: Use the test description to describe the _business rule_ you are testing; you (and developers that follow you) will be thankful that you did. 92 | 93 | ### Assertions 94 | 95 | Unit test case functions may contain any valid JavaScript. To actually record the results of your test, however, you can use one of the built-in 96 | *assertions*. 97 | 98 | Assertions are methods that test a value and then report the result. If the assertion fails, the test runner will report a failure. If the assertion 99 | passes, then your test is allowed to continue. 100 | 101 | #### ok() 102 | 103 | The most basic assertion you can make is +ok()+. This method accepts a boolean value and an optional description string. The assertion passes if the 104 | value is true and fails otherwise. For example, here is how you would assert that a test value is greater than 10: 105 | 106 | ```javascript 107 | ok(contacts.get('length') > 10, "should be more than 10 contacts"); 108 | ``` 109 | 110 | To make your tests more understandable, you should always include a description when you use +ok()+ assertions. Otherwise, the test runner will 111 | simply print the value that was returned and the value that was expected. 112 | 113 | #### equals() 114 | 115 | The other assertion you will commonly use is +equals()+. This method accepts two values and an optional description. It passes if the two values are 116 | equal and fails otherwise. The advantage of using +equals+ over +ok(a == b, "a equals b")+ is that, if the test fails, then the test runner will 117 | display both the expected value and the actual value; this can be valuable when debugging your code. 118 | 119 | For example, here is how you would test the fullName property of a contact: 120 | 121 | ```javascript 122 | equals(contact.get('fullName'), 'John Doe', 'fullName = John Doe'); 123 | ``` 124 | 125 | Note: this method is equivalent to +equal()+ in the latest QUnit API. 126 | 127 | #### Other assertions 128 | 129 | There are several other assertion types that you can learn about from the [QUnit pages](http://docs.jquery.com/QUnit#API_documentation), such as 130 | +same()+ or +expects()+. Most of these assertions are not as generally useful as +ok()+ and +equals()+. However, you can use them if you wish. 131 | 132 | 133 | 134 | ### Asynchronous Unit Tests Cases 135 | 136 | Sometimes, a unit test case needs to stop and wait for task(s) to complete. This is particularly true when testing code that involves loading data 137 | from a server or using timers. In these scenarios, you can use the +stop()+ and +start()+ methods to control your test execution. 138 | 139 | The +stop()+ method temporarily pauses your unit test case from running. This means that when your test function returns, instead of calling the 140 | module's +teardown()+ method (if defined) and running the next unit test case, the test runner will just exit. 141 | 142 | After the test runner has been stopped, you can resume it by calling +start()+. To invoke +start()+, you will need to setup a callback or timer. 143 | 144 | For example, the unit test case below verifies if a particular file loads from the server. It first issues a +stop()+ to the test runner for 1 145 | second. If +start()+ is not called within 1 second, the test will fail. It then uses the callback in +SC.Request.getUrl()+ to call +start()+. 146 | 147 | ```javascript 148 | test("load file from server", function() { 149 | // setup a timeout in case of failure 150 | stop(1000); 151 | 152 | var req = SC.Request.getUrl("/foo.json").notify(function() { 153 | // verify request loaded OK 154 | ok(SC.typeOf(req.get('response')) !== SC.T_ERROR, "response should not be an error"); 155 | // resume executing tests 156 | start(); 157 | }); 158 | }); 159 | ``` 160 | 161 | Here's a more complex example with assertions. 162 | 163 | ```javascript 164 | test('Create User', function() { 165 | // Pause the test runner. If start() is not called within 2 seconds, fail the test. 166 | stop(2000); 167 | 168 | var user = MyApp.store.createRecord(MyApp.UserRecord, { 169 | username: 'SpongeBob', 170 | department: 'Accounts', 171 | status: 'Active', 172 | isAdmin: NO, 173 | lastLoggedInDate: '2010-05-05T10:20:30Z' 174 | }); 175 | 176 | equal(user.get('username'), 'SpongeBob', 'user name'); 177 | equal(user.get('department'), 'Accounts', 'department'); 178 | equal(user.get('userStatus'), 'Active', 'user status'); 179 | equal(user.get('isAdmin'), NO, 'user is Admin'); 180 | var d1 = user.get('lastLoggedInDate').toISO8601(); 181 | var d2 = '2010-05-05T10:20:30+00:00'; 182 | equal(d1, d2, 'user last logged in'); 183 | 184 | // Status should be ready new because record is new and not committed to server 185 | ok(user.get('status') === SC.Record.READY_NEW, 'Status is READY_NEW'); 186 | 187 | // Commit changes 188 | MyApp.store.commitRecords(); 189 | 190 | // Give our store 1 second to commit records to the remote server 191 | setTimeout(checkCreate, 1000); 192 | }); 193 | 194 | function checkCreate() { 195 | // Should be to find record now 196 | var query = SC.Query.local(MyApp.UserRecord, { 197 | conditions: 'username = {name}', 198 | name: 'SpongeBob' 199 | }); 200 | 201 | var users = MyApp.store.find(query); 202 | equal(users.get('length'), 1, 'SpongeBob record should be searchable by query'); 203 | 204 | // Status should now be 'clean' since it's been saved 205 | equal(user.get('status'), SC.Record.READY_CLEAN, 'Status is READY_CLEAN'); 206 | 207 | // Can get our record by primary key 208 | var user2 = MyApp.store.find(MyApp.UserRecord, user.get('id')); 209 | equal(user2.get('username'), 'SpongeBob', 'user name'); 210 | 211 | // Resume the test runner again 212 | start(); 213 | } 214 | ``` 215 | 216 | ### Writing HTML 217 | 218 | Normally you will not need to add HTML to your page when you write tests since you will be testing functionality provided by your application and all 219 | the HTML you need will already exist. 220 | 221 | If you do need to write out some sample HTML on the page to work against when testing, however, you can add this HTML using the method +htmlbody()+. 222 | This method should be the first thing called in your page, even before you start a new module. 223 | 224 | To use this method, just pass a string containing the HTML you want to add. The HTML will be appended to the main body tag. 225 | 226 | ```javascript 227 | // adds the h1 tag to the end of the body. 228 | htmlbody("

") ; 229 | ``` 230 | 231 | ### Moving On 232 | 233 | Now that you have written a unit test case or two, it's time to run them. The next section will tell you everything you need to know about how unit 234 | tests are loaded and executed by the test runner. 235 | 236 | On to [Running Unit Tests »](/running_unit_tests.html) 237 | 238 | ### Changelog 239 | 240 | * February 15, 2011: initial version by [Scott Smith](credits.html#oldfartdeveloper) and [Vibul Imtarnasan](credits.html#veebs) 241 | * March 2, 2011: minor paragraph formatting changes by [Topher Fangio](credits.html#topherfangio) 242 | * August 7, 2013: converted to Markdown format for DocPad guides by [Topher Fangio](credits.html#topherfangio) 243 | -------------------------------------------------------------------------------- /src/files/images/animate/ease_in_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/animate/ease_in_graph.png -------------------------------------------------------------------------------- /src/files/images/animate/ease_in_out_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/animate/ease_in_out_graph.png -------------------------------------------------------------------------------- /src/files/images/animate/ease_out_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/animate/ease_out_graph.png -------------------------------------------------------------------------------- /src/files/images/animate/linear_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/animate/linear_graph.png -------------------------------------------------------------------------------- /src/files/images/animate/xyz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/animate/xyz.png -------------------------------------------------------------------------------- /src/files/images/credits/dtorres.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/dtorres.jpg -------------------------------------------------------------------------------- /src/files/images/credits/eocean.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/eocean.jpg -------------------------------------------------------------------------------- /src/files/images/credits/fkugler.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/fkugler.jpg -------------------------------------------------------------------------------- /src/files/images/credits/gdonaldson.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/gdonaldson.jpg -------------------------------------------------------------------------------- /src/files/images/credits/jzimdars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/jzimdars.jpg -------------------------------------------------------------------------------- /src/files/images/credits/mball.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/mball.jpg -------------------------------------------------------------------------------- /src/files/images/credits/mikeatkins.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/mikeatkins.jpg -------------------------------------------------------------------------------- /src/files/images/credits/pbergstrom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/pbergstrom.jpg -------------------------------------------------------------------------------- /src/files/images/credits/psarnacki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/psarnacki.jpg -------------------------------------------------------------------------------- /src/files/images/credits/pwagenet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/pwagenet.jpg -------------------------------------------------------------------------------- /src/files/images/credits/ssmith.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/ssmith.jpg -------------------------------------------------------------------------------- /src/files/images/credits/tdale.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/tdale.jpg -------------------------------------------------------------------------------- /src/files/images/credits/tfangio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/tfangio.jpg -------------------------------------------------------------------------------- /src/files/images/credits/tkeating.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/tkeating.jpg -------------------------------------------------------------------------------- /src/files/images/credits/unknown.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/unknown.jpg -------------------------------------------------------------------------------- /src/files/images/credits/vimtarnasan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/vimtarnasan.jpg -------------------------------------------------------------------------------- /src/files/images/credits/ykatz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/credits/ykatz.jpg -------------------------------------------------------------------------------- /src/files/images/footer/dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/footer/dot.png -------------------------------------------------------------------------------- /src/files/images/footer/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/footer/facebook.png -------------------------------------------------------------------------------- /src/files/images/footer/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/footer/google.png -------------------------------------------------------------------------------- /src/files/images/footer/html5_tech.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/footer/html5_tech.png -------------------------------------------------------------------------------- /src/files/images/footer/sc_logo_medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/footer/sc_logo_medium.png -------------------------------------------------------------------------------- /src/files/images/footer/top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/footer/top.png -------------------------------------------------------------------------------- /src/files/images/footer/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/footer/twitter.png -------------------------------------------------------------------------------- /src/files/images/getting_started/todos_three_screen_capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/getting_started/todos_three_screen_capture.png -------------------------------------------------------------------------------- /src/files/images/graphics/chapters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/chapters.png -------------------------------------------------------------------------------- /src/files/images/graphics/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/check.png -------------------------------------------------------------------------------- /src/files/images/graphics/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/code.png -------------------------------------------------------------------------------- /src/files/images/graphics/containergradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/containergradient.png -------------------------------------------------------------------------------- /src/files/images/graphics/contribute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/contribute.png -------------------------------------------------------------------------------- /src/files/images/graphics/credits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/credits.png -------------------------------------------------------------------------------- /src/files/images/graphics/guide-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/guide-button.png -------------------------------------------------------------------------------- /src/files/images/graphics/guidegradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/guidegradient.png -------------------------------------------------------------------------------- /src/files/images/graphics/guides.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/guides.png -------------------------------------------------------------------------------- /src/files/images/graphics/notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/notes.png -------------------------------------------------------------------------------- /src/files/images/graphics/paperandpencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/paperandpencil.png -------------------------------------------------------------------------------- /src/files/images/graphics/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/pin.png -------------------------------------------------------------------------------- /src/files/images/graphics/sidebar-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/sidebar-gradient.png -------------------------------------------------------------------------------- /src/files/images/graphics/starperson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/starperson.png -------------------------------------------------------------------------------- /src/files/images/graphics/tab-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/tab-code.png -------------------------------------------------------------------------------- /src/files/images/graphics/tab-notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/tab-notes.png -------------------------------------------------------------------------------- /src/files/images/graphics/tab-pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/tab-pin.png -------------------------------------------------------------------------------- /src/files/images/graphics/tab-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/tab-warning.png -------------------------------------------------------------------------------- /src/files/images/graphics/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/graphics/warning.png -------------------------------------------------------------------------------- /src/files/images/header/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/header/glow.png -------------------------------------------------------------------------------- /src/files/images/header/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/header/logo.png -------------------------------------------------------------------------------- /src/files/images/header/pixels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/header/pixels.png -------------------------------------------------------------------------------- /src/files/images/header/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/header/search.png -------------------------------------------------------------------------------- /src/files/images/html_based/bindings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/html_based/bindings.png -------------------------------------------------------------------------------- /src/files/images/records/busy_substates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/records/busy_substates.png -------------------------------------------------------------------------------- /src/files/images/records/destroyed_substates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/records/destroyed_substates.png -------------------------------------------------------------------------------- /src/files/images/records/ready_substates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/records/ready_substates.png -------------------------------------------------------------------------------- /src/files/images/records/record_anatomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/records/record_anatomy.png -------------------------------------------------------------------------------- /src/files/images/run_loop_diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/run_loop_diagram.jpg -------------------------------------------------------------------------------- /src/files/images/testing/sample_sc_test_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/testing/sample_sc_test_screenshot.png -------------------------------------------------------------------------------- /src/files/images/testing/screenshot_1st_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/testing/screenshot_1st_test.png -------------------------------------------------------------------------------- /src/files/images/testing/unit_test_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/testing/unit_test_1.png -------------------------------------------------------------------------------- /src/files/images/testing/unit_test_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/testing/unit_test_2.png -------------------------------------------------------------------------------- /src/files/images/testing/unit_test_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/testing/unit_test_3.png -------------------------------------------------------------------------------- /src/files/images/testing/unit_test_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/testing/unit_test_4.png -------------------------------------------------------------------------------- /src/files/images/theming/button-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/theming/button-active.png -------------------------------------------------------------------------------- /src/files/images/theming/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/theming/button.png -------------------------------------------------------------------------------- /src/files/images/theming/button_slice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/theming/button_slice.jpg -------------------------------------------------------------------------------- /src/files/images/theming/webinspector-myapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/images/theming/webinspector-myapp.png -------------------------------------------------------------------------------- /src/files/stylesheets/guides.css: -------------------------------------------------------------------------------- 1 | /* Reset 2 | --------------------------------------------- */ 3 | 4 | html, body, div, span, object, iframe, 5 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 6 | a, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, 7 | small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, figcaption, figure, 11 | footer, header, hgroup, menu, nav, section, summary, 12 | time, mark, audio, video { 13 | margin: 0; 14 | padding: 0; 15 | border: 0; 16 | font-size: 100%; 17 | font: inherit; 18 | vertical-align: baseline; 19 | } 20 | 21 | article, aside, details, figcaption, figure, 22 | footer, header, hgroup, menu, nav, section { 23 | display: block; 24 | } 25 | 26 | ol, ul { 27 | list-style: none; 28 | } 29 | 30 | blockquote, q { 31 | quotes: none; 32 | } 33 | 34 | blockquote:before, blockquote:after, 35 | q:before, q:after { 36 | content: ""; 37 | content: none; 38 | } 39 | 40 | table { 41 | border-collapse: collapse; 42 | border-spacing: 0; 43 | } 44 | 45 | 46 | /* Base 47 | --------------------------------------------- */ 48 | 49 | html { 50 | overflow-y: scroll; 51 | } 52 | 53 | body { 54 | color: #444; 55 | font: 12px/1.5 "Helvetica Neue", Arial, sans-serif; 56 | } 57 | 58 | .container { 59 | width: 960px; 60 | margin: 0 auto; 61 | position: relative; 62 | } 63 | 64 | /* Inputs */ 65 | 66 | input, 67 | textarea, 68 | select, 69 | button { 70 | font: 12px "Helvetica Neue", Arial, sans-serif; 71 | } 72 | 73 | /* Links */ 74 | 75 | a:link, 76 | a:visited { 77 | text-decoration: none; 78 | } 79 | 80 | 81 | /* Banner 82 | --------------------------------------------- */ 83 | 84 | [role="banner"] { 85 | width: 100%; 86 | position: absolute; 87 | overflow: hidden; 88 | border-bottom: 1px solid #000; 89 | border-bottom: 1px solid rgba(0, 0, 0, 0.2); 90 | background: #000; 91 | background: rgba(0, 0, 0, 0.3); 92 | -webkit-box-shadow: 0 1px 9px rgba(0, 0, 0, 0.3), inset 0 -1px 0 rgba(255, 255, 255, 0.2); 93 | -moz-box-shadow: 0 1px 9px rgba(0, 0, 0, 0.3), inset 0 -1px 0 rgba(255, 255, 255, 0.2); 94 | box-shadow: 0 1px 9px rgba(0, 0, 0, 0.3), inset 0 -1px 0 rgba(255, 255, 255, 0.2); 95 | font-size: 90%; 96 | } 97 | 98 | #logo, 99 | [role="banner"] nav, 100 | [role="banner"] form { 101 | float: left; 102 | } 103 | 104 | /* Logo */ 105 | 106 | #logo { 107 | margin-top: 11px; 108 | } 109 | 110 | /* Navigation */ 111 | 112 | [role="banner"] nav { 113 | width: 600px; 114 | text-align: center; 115 | } 116 | 117 | [role="banner"] nav li { 118 | display: inline-block; 119 | padding: 16px 15px; 120 | color: #fff; 121 | font-weight: bold; 122 | text-transform: uppercase; 123 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75); 124 | } 125 | 126 | [role="banner"] nav li.active { 127 | background: url(../images/header/glow.png) no-repeat 50% 0; 128 | } 129 | 130 | [role="banner"] nav li.disabled { 131 | opacity: 0.25; 132 | } 133 | 134 | [role="banner"] nav a:link, 135 | [role="banner"] nav a:visited { 136 | color: #fff; 137 | } 138 | 139 | [role="banner"] nav a:hover, 140 | [role="banner"] nav .active a { 141 | color: #beff9d; 142 | } 143 | 144 | /* Search */ 145 | 146 | [role="banner"] input { 147 | width: 165px; 148 | height: 14px; 149 | margin-top: 13px; 150 | padding: 5px 8px 5px 27px; 151 | border: 0; 152 | color: #eee; 153 | background: #000 url(../images/header/search.png) no-repeat 8px 6px; 154 | background: rgba(0, 0, 0, 0.19) url(../images/header/search.png) no-repeat 8px 6px; 155 | -webkit-border-radius: 12px; 156 | -moz-border-radius: 12px; 157 | border-radius: 12px; 158 | -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.24), inset 0 1px 6px rgba(0, 0, 0, 0.5); 159 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.24), inset 0 1px 6px rgba(0, 0, 0, 0.5); 160 | box-shadow: 0 1px 0 rgba(255, 255, 255, 0.24), inset 0 1px 6px rgba(0, 0, 0, 0.5); 161 | } 162 | 163 | [role="banner"] input:focus { 164 | outline: 0; 165 | background-color: #000; 166 | background-color: rgba(0, 0, 0, 0.07); 167 | } 168 | 169 | 170 | /* Footer 171 | --------------------------------------------- */ 172 | 173 | body > footer { 174 | padding: 40px 0; 175 | color: #e9e9e9; 176 | font-size: 14px; 177 | text-align: left; 178 | text-shadow: 0 1px 0 rgba(0, 0, 0, 0.8); 179 | background: #343434 url(../images/footer/dot.png); 180 | -webkit-box-shadow: inset 0 1px 10px rgba(0, 0, 0, 0.6); 181 | -moz-box-shadow: inset 0 1px 10px rgba(0, 0, 0, 0.6); 182 | box-shadow: inset 0 1px 10px rgba(0, 0, 0, 0.6); 183 | overflow: hidden; 184 | } 185 | 186 | /* Columns */ 187 | 188 | body > footer .col { 189 | float: left; 190 | } 191 | 192 | body > footer .col:nth-child(1) { 193 | width: 305px; 194 | } 195 | 196 | body > footer .col:nth-child(2) { 197 | width: 200px; 198 | margin: 0 85px; 199 | } 200 | 201 | body > footer .col:nth-child(3) { 202 | width: 285px; 203 | } 204 | 205 | /* Logo */ 206 | 207 | body > footer .col:nth-child(1) > a { 208 | display: inline-block; 209 | margin-bottom: 24px; 210 | } 211 | 212 | /* Headers */ 213 | 214 | body > footer h1 { 215 | margin-bottom: 1em; 216 | font-weight: bold; 217 | } 218 | 219 | /* Nav */ 220 | 221 | body > footer a:link, 222 | body > footer a:visited { 223 | color: #a1bed5; 224 | font-size: inherit; 225 | } 226 | 227 | body > footer a:hover { 228 | color: #6c8191; 229 | } 230 | 231 | body > footer ul { 232 | overflow: hidden; 233 | margin-bottom: 24px; 234 | } 235 | 236 | body > footer li { 237 | float: left; 238 | } 239 | 240 | body > footer nav li { 241 | width: 100px; 242 | } 243 | 244 | body > footer nav > a img { 245 | display: block; 246 | } 247 | 248 | body > footer .social li:nth-child(2) { 249 | margin: 0 8px; 250 | } 251 | 252 | body > footer .social li:nth-child(4) { 253 | width: 90px; 254 | margin: -1px 0 0 5px; 255 | line-height: 1.2; 256 | } 257 | 258 | /* Newsletter */ 259 | 260 | #newsletter.button.secondary { 261 | display: inline-block; 262 | padding: 10px 15px; 263 | border: 1px solid #000; 264 | color: #fff; 265 | font-weight: bold; 266 | font-size: 11px; 267 | text-transform: uppercase; 268 | text-shadow: 0 1px 0 #000; 269 | opacity: 0.5; 270 | -webkit-border-radius: 4px; 271 | -moz-border-radius: 4px; 272 | border-radius: 4px; 273 | -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.14); 274 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.14); 275 | box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.14); 276 | background: #242424; 277 | background: -moz-linear-gradient(top, #3b3b3b, #2f2f2f 50%, #242424 51%, #161616); 278 | background: -ms-linear-gradient(top, #3b3b3b, #2f2f2f 50%, #242424 51%, #161616); 279 | background: -o-linear-gradient(top, #3b3b3b, #2f2f2f 50%, #242424 51%, #161616); 280 | background: -webkit-gradient(linear, left top, left bottom, from(#3b3b3b), color-stop(50%, #2f2f2f), color-stop(51%, #242424), to(#161616)); 281 | background: -webkit-linear-gradient(top, #3b3b3b, #2f2f2f 50%, #242424 51%, #161616); 282 | background: linear-gradient(top, #3b3b3b, #2f2f2f 50%, #242424 51%, #161616); 283 | } 284 | 285 | #newsletter.button.secondary:hover { 286 | background: #333; 287 | background: -moz-linear-gradient(top, #333, #161616); 288 | background: -ms-linear-gradient(top, #333, #161616); 289 | background: -o-linear-gradient(top, #333, #161616); 290 | background: -webkit-gradient(linear, left top, left bottom, from(#333), to(#161616)); 291 | background: -webkit-linear-gradient(top, #333, #161616); 292 | background: linear-gradient(top, #333, #161616); 293 | } 294 | 295 | #newsletter.button.secondary:active { 296 | background: #161616; 297 | background: -moz-linear-gradient(top, #161616, #333); 298 | background: -ms-linear-gradient(top, #161616, #333); 299 | background: -o-linear-gradient(top, #161616, #333); 300 | background: -webkit-gradient(linear, left top, left bottom, from(#161616), to(#333)); 301 | background: -webkit-linear-gradient(top, #161616, #333); 302 | background: linear-gradient(top, #161616, #333); 303 | } 304 | 305 | /* Back to top */ 306 | 307 | body > footer .top:link, 308 | body > footer .top:visited { 309 | position: absolute; 310 | top: -40px; 311 | right: 0; 312 | padding: 0 20px 0 43px; 313 | border: 1px solid #2a2a2a; 314 | border: 1px solid rgba(0, 0, 0, 0.33); 315 | border-top: 0; 316 | color: #ececec; 317 | font-weight: bold; 318 | font-size: 9px; 319 | line-height: 30px; 320 | text-transform: uppercase; 321 | background: #222 url(../images/footer/top.png) no-repeat 20px 7px; 322 | background-color: rgba(0, 0, 0, 0.2); 323 | -webkit-border-radius: 0 0 4px 4px; 324 | -moz-border-radius: 0 0 4px 4px; 325 | border-radius: 0 0 4px 4px; 326 | -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); 327 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); 328 | box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); 329 | } 330 | 331 | body > footer .top:hover { 332 | border-color: #151515; 333 | border-color: rgba(0, 0, 0, 0.6); 334 | background-color: #1f1f1f; 335 | background-color: rgba(0, 0, 0, 0.4); 336 | } -------------------------------------------------------------------------------- /src/files/stylesheets/highlight-github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | pre code { 8 | display: block; padding: 0.5em; 9 | color: #333; 10 | background: #f8f8ff 11 | } 12 | 13 | pre .comment, 14 | pre .template_comment, 15 | pre .diff .header, 16 | pre .javadoc { 17 | color: #998; 18 | font-style: italic 19 | } 20 | 21 | pre .keyword, 22 | pre .css .rule .keyword, 23 | pre .winutils, 24 | pre .javascript .title, 25 | pre .nginx .title, 26 | pre .subst, 27 | pre .request, 28 | pre .status { 29 | color: #333; 30 | font-weight: bold 31 | } 32 | 33 | pre .number, 34 | pre .hexcolor, 35 | pre .ruby .constant { 36 | color: #099; 37 | } 38 | 39 | pre .string, 40 | pre .tag .value, 41 | pre .phpdoc, 42 | pre .tex .formula { 43 | color: #d14 44 | } 45 | 46 | pre .title, 47 | pre .id, 48 | pre .coffeescript .params, 49 | pre .scss .preprocessor { 50 | color: #900; 51 | font-weight: bold 52 | } 53 | 54 | pre .javascript .title, 55 | pre .lisp .title, 56 | pre .clojure .title, 57 | pre .subst { 58 | font-weight: normal 59 | } 60 | 61 | pre .class .title, 62 | pre .haskell .type, 63 | pre .vhdl .literal, 64 | pre .tex .command { 65 | color: #458; 66 | font-weight: bold 67 | } 68 | 69 | pre .tag, 70 | pre .tag .title, 71 | pre .rules .property, 72 | pre .django .tag .keyword { 73 | color: #000080; 74 | font-weight: normal 75 | } 76 | 77 | pre .attribute, 78 | pre .variable, 79 | pre .lisp .body { 80 | color: #008080 81 | } 82 | 83 | pre .regexp { 84 | color: #009926 85 | } 86 | 87 | pre .class { 88 | color: #458; 89 | font-weight: bold 90 | } 91 | 92 | pre .symbol, 93 | pre .ruby .symbol .string, 94 | pre .lisp .keyword, 95 | pre .tex .special, 96 | pre .prompt { 97 | color: #990073 98 | } 99 | 100 | pre .built_in, 101 | pre .lisp .title, 102 | pre .clojure .built_in { 103 | color: #0086b3 104 | } 105 | 106 | pre .preprocessor, 107 | pre .pi, 108 | pre .doctype, 109 | pre .shebang, 110 | pre .cdata { 111 | color: #999; 112 | font-weight: bold 113 | } 114 | 115 | pre .deletion { 116 | background: #fdd 117 | } 118 | 119 | pre .addition { 120 | background: #dfd 121 | } 122 | 123 | pre .diff .change { 124 | background: #0086b3 125 | } 126 | 127 | pre .chunk { 128 | color: #aaa 129 | } 130 | -------------------------------------------------------------------------------- /src/files/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | /* General 2 | --------------------------------------- */ 3 | 4 | .left {float: left; margin-right: 1em;} 5 | .right {float: right; margin-left: 1em;} 6 | .small {font-size: smaller;} 7 | .large {font-size: larger;} 8 | .hide {display: none;} 9 | 10 | li ul, li ol { margin:0 1.5em; } 11 | ul, ol { margin: 0 1.5em 1.5em 1.5em; } 12 | 13 | ul { list-style-type: disc; } 14 | ol { list-style-type: decimal; } 15 | 16 | dl { margin: 0 0 1.5em 0; } 17 | dl dt { font-weight: bold; } 18 | dd { margin-left: 1.5em;} 19 | 20 | pre,code { margin: 1.5em 0; overflow: auto; color: #222;} 21 | pre,code,tt,ins { 22 | font-size: 1em; 23 | font-family: "Anonymous Pro", "Inconsolata", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; 24 | line-height: 1.5; 25 | } 26 | 27 | abbr, acronym { border-bottom: 1px dotted #666; } 28 | address { margin: 0 0 1.5em; font-style: italic; } 29 | del { color:#666; } 30 | 31 | blockquote { margin: 1.5em; color: #666; font-style: italic; } 32 | strong { font-weight: bold; } 33 | em, dfn { font-style: italic; } 34 | dfn { font-weight: bold; } 35 | sup, sub { line-height: 0; } 36 | p {margin: 0 0 1.5em;} 37 | 38 | label { font-weight: bold; } 39 | fieldset { padding:1.4em; margin: 0 0 1.5em 0; border: 1px solid #ccc; } 40 | legend { font-weight: bold; font-size:1.2em; } 41 | 42 | input.text, input.title, 43 | textarea, select { 44 | margin:0.5em 0; 45 | border:1px solid #bbb; 46 | } 47 | 48 | table { 49 | margin: 0 0 1.5em; 50 | border: 2px solid #CCC; 51 | background: #FFF; 52 | border-collapse: collapse; 53 | } 54 | 55 | table th, table td { 56 | padding: 0.25em 1em; 57 | border: 1px solid #CCC; 58 | border-collapse: collapse; 59 | } 60 | 61 | table th { 62 | border-bottom: 2px solid #CCC; 63 | background: #EEE; 64 | font-weight: bold; 65 | padding: 0.5em 1em; 66 | } 67 | 68 | 69 | /* Structure and Layout 70 | --------------------------------------- */ 71 | 72 | body { 73 | text-align: center; 74 | font-family: Helvetica, Arial, sans-serif; 75 | font-size: 87.5%; 76 | line-height: 1.5em; 77 | background: #222; 78 | color: #999; 79 | } 80 | 81 | .wrapper { 82 | text-align: left; 83 | margin: 0 auto; 84 | width: 69em; 85 | } 86 | 87 | #topNav { 88 | padding: 1em 0; 89 | color: #565656; 90 | } 91 | 92 | #header { 93 | background: #c52f24 url(/images/header_tile.gif) repeat-x; 94 | color: #FFF; 95 | padding: 1.5em 0; 96 | position: relative; 97 | z-index: 99; 98 | } 99 | 100 | #feature { 101 | background: #d5e9f6 url(/images/feature_tile.gif) repeat-x; 102 | color: #333; 103 | padding: 0.5em 0 1.5em; 104 | } 105 | 106 | #container { 107 | background: #FFF; 108 | color: #333; 109 | padding: 0.5em 0 1.5em 0; 110 | } 111 | 112 | #mainCol { 113 | width: 45em; 114 | margin-left: 2em; 115 | } 116 | 117 | #subCol { 118 | position: absolute; 119 | z-index: 0; 120 | top: 0; 121 | right: 0; 122 | background: #FFF; 123 | padding: 1em 1.5em 1em 1.25em; 124 | width: 17em; 125 | font-size: 0.9285em; 126 | line-height: 1.3846em; 127 | } 128 | 129 | #extraCol {display: none;} 130 | 131 | #footer { 132 | padding: 2em 0; 133 | background: url(/images/footer_tile.gif) repeat-x; 134 | } 135 | #footer .wrapper { 136 | padding-left: 2em; 137 | width: 67em; 138 | } 139 | 140 | #header .wrapper, #topNav .wrapper, #feature .wrapper {padding-left: 1em; width: 68em;} 141 | #feature .wrapper {width: 45em; padding-right: 23em; position: relative; z-index: 0;} 142 | 143 | /* Links 144 | --------------------------------------- */ 145 | 146 | a, a:link, a:visited { 147 | color: #ee3f3f; 148 | text-decoration: underline; 149 | } 150 | 151 | #mainCol a, #subCol a, #feature a {color: #980905;} 152 | 153 | 154 | /* Navigation 155 | --------------------------------------- */ 156 | 157 | .nav {margin: 0; padding: 0;} 158 | .nav li {display: inline; list-style: none;} 159 | 160 | #header .nav { 161 | float: right; 162 | margin-top: 1.5em; 163 | font-size: 1.2857em; 164 | width: 30em; 165 | } 166 | 167 | #header .nav li {margin: 0 0 0 0.5em;} 168 | #header .nav a {color: #FFF; text-decoration: none;} 169 | #header .nav a:hover {text-decoration: underline;} 170 | 171 | #header .nav .index { 172 | padding: 0.5em 1.5em; 173 | border-radius: 1em; 174 | -webkit-border-radius: 1em; 175 | -moz-border-radius: 1em; 176 | background: #980905; 177 | position: relative; 178 | } 179 | 180 | #header .nav .index a { 181 | background: #980905 no-repeat right top; 182 | position: relative; 183 | z-index: 15; 184 | padding-bottom: 0.125em; 185 | } 186 | #header .nav .index:hover a, #header .nav .index a:hover {background-position: right -81px;} 187 | 188 | #guides { 189 | width: 27em; 190 | display: block; 191 | background: #980905; 192 | border-radius: 1em; 193 | -webkit-border-radius: 1em; 194 | -moz-border-radius: 1em; 195 | -webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25); 196 | -moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em; 197 | color: #f1938c; 198 | padding: 1.5em 2em; 199 | position: absolute; 200 | z-index: 10; 201 | top: -0.25em; 202 | right: 0; 203 | padding-top: 2em; 204 | } 205 | 206 | #guides dt, #guides dd { 207 | font-weight: normal; 208 | font-size: 0.722em; 209 | margin: 0; 210 | padding: 0; 211 | } 212 | #guides dt {padding:0; margin: 0.5em 0 0;} 213 | #guides a {color: #FFF; background: none !important;} 214 | #guides .L, #guides .R {float: left; width: 50%; margin: 0; padding: 0;} 215 | #guides .R {float: right;} 216 | #guides hr { 217 | display: block; 218 | border: none; 219 | height: 1px; 220 | color: #f1938c; 221 | background: #f1938c; 222 | } 223 | 224 | /* Headings 225 | --------------------------------------- */ 226 | 227 | h1 { 228 | font-size: 2.5em; 229 | line-height: 1em; 230 | margin: 0.6em 0 .2em; 231 | font-weight: bold; 232 | } 233 | 234 | h2 { 235 | font-size: 2.1428em; 236 | line-height: 1em; 237 | margin: 0.7em 0 .2333em; 238 | font-weight: bold; 239 | } 240 | 241 | h3 { 242 | font-size: 1.7142em; 243 | line-height: 1.286em; 244 | margin: 0.875em 0 0.2916em; 245 | font-weight: bold; 246 | } 247 | 248 | h4 { 249 | font-size: 1.2857em; 250 | line-height: 1.2em; 251 | margin: 1.6667em 0 .3887em; 252 | font-weight: bold; 253 | } 254 | 255 | h5 { 256 | font-size: 1em; 257 | line-height: 1.5em; 258 | margin: 1em 0 .5em; 259 | font-weight: bold; 260 | } 261 | 262 | h6 { 263 | font-size: 1em; 264 | line-height: 1.5em; 265 | margin: 1em 0 .5em !important; 266 | font-weight: bold !important; 267 | } 268 | 269 | .section { 270 | padding-bottom: 0.25em; 271 | border-bottom: 1px solid #999; 272 | } 273 | 274 | /* Content 275 | --------------------------------------- */ 276 | 277 | .pic { 278 | margin: 0 2em 2em 0; 279 | } 280 | 281 | #topNav strong {color: #999; margin-right: 0.5em;} 282 | #topNav strong a {color: #FFF;} 283 | 284 | #header h1 { 285 | float: left; 286 | background: url(/images/rails_guides_logo.gif) no-repeat; 287 | width: 297px; 288 | text-indent: -9999em; 289 | margin: 0; 290 | padding: 0; 291 | } 292 | 293 | #header h1 a { 294 | text-decoration: none; 295 | display: block; 296 | height: 77px; 297 | } 298 | 299 | #feature p { 300 | font-size: 1.2857em; 301 | margin-bottom: 0.75em; 302 | } 303 | 304 | #feature ul {margin-left: 0;} 305 | #feature ul li { 306 | list-style: square; 307 | padding: 0.5em 1.75em 0.5em 1.75em; 308 | font-size: 1.1428em; 309 | font-weight: bold; 310 | } 311 | 312 | #mainCol dd, #subCol dd { 313 | padding: 0.25em 0 1em; 314 | border-bottom: 1px solid #CCC; 315 | margin-bottom: 1em; 316 | margin-left: 0; 317 | /*padding-left: 28px;*/ 318 | padding-left: 0; 319 | } 320 | 321 | #mainCol dt, #subCol dt { 322 | font-size: 1.2857em; 323 | padding: 0.125em 0 0.25em 0; 324 | margin-bottom: 0; 325 | /*background: url(/images/book_icon.gif) no-repeat left top; 326 | padding: 0.125em 0 0.25em 28px;*/ 327 | } 328 | 329 | #mainCol dd.work-in-progress, #subCol dd.work-in-progress { 330 | background: #fff9d8 url(/images/tab_yellow.gif) no-repeat left top; 331 | border: none; 332 | padding: 1.25em 1em 1.25em 48px; 333 | margin-left: 0; 334 | margin-top: 0.25em; 335 | } 336 | 337 | #mainCol div.warning, #subCol dd.warning { 338 | background: #f9d9d8 url(/images/tab_red.gif) no-repeat left top; 339 | border: none; 340 | padding: 1.25em 1.25em 1.25em 48px; 341 | margin-left: 0; 342 | margin-top: 0.25em; 343 | } 344 | 345 | #subCol .chapters {color: #980905;} 346 | #subCol .chapters a {font-weight: bold;} 347 | #subCol .chapters ul a {font-weight: normal;} 348 | #subCol .chapters li {margin-bottom: 0.75em;} 349 | #subCol h3.chapter {margin-top: 0.25em;} 350 | #subCol h3.chapter img {vertical-align: text-bottom;} 351 | #subCol .chapters ul {margin-left: 0; margin-top: 0.5em;} 352 | #subCol .chapters ul li { 353 | list-style: square; 354 | margin-left: 2em; 355 | margin-right: 0; 356 | font-size: 1em; 357 | font-weight: normal; 358 | } 359 | 360 | div.code_container { 361 | background: #f8f8ff url(/images/graphics/tab-code.png) no-repeat left top; 362 | padding-left: 37px; 363 | } 364 | 365 | div.code_container code { 366 | padding: 10px; 367 | min-height: 27px; 368 | } 369 | 370 | div.code_container div.filename { 371 | background-color: #cbe1af !important; 372 | border-bottom: 1px solid #a0c98c; 373 | border-top: 1px solid #a0c98c; 374 | color: #666; 375 | font-family: monospace; 376 | margin-left: -10px; 377 | margin-right: -10px; 378 | padding-bottom: 4px; 379 | padding-left: 10px; 380 | padding-top: 5px; 381 | } 382 | 383 | div.code_container div.filename span { 384 | color: #666 !important; 385 | } 386 | 387 | .note { 388 | background: #fff9d8 url(/images/tab_note.gif) no-repeat left top; 389 | border: none; 390 | padding: 1em 1em 0.25em 48px; 391 | margin: 0.25em 0 1.5em 0; 392 | } 393 | 394 | .info { 395 | background: #d5e9f6 url(/images/tab_info.gif) no-repeat left top; 396 | border: none; 397 | padding: 1em 1em 0.25em 48px; 398 | margin: 0.25em 0 1.5em 0; 399 | } 400 | 401 | .note tt, .note ins, .info tt, .note ins {border:none; background: none; padding: 0;} 402 | 403 | #mainCol ul li { 404 | list-style: square; 405 | margin-left: 25px; 406 | margin-bottom: 5px; 407 | } 408 | 409 | #subCol .content { 410 | font-size: 0.7857em; 411 | line-height: 1.5em; 412 | } 413 | 414 | #subCol .content li { 415 | font-weight: normal; 416 | background: none; 417 | padding: 0 0 1em; 418 | font-size: 1.1667em; 419 | } 420 | 421 | /* Clearing 422 | --------------------------------------- */ 423 | 424 | .clearfix:after { 425 | content: "."; 426 | display: block; 427 | height: 0; 428 | clear: both; 429 | visibility: hidden; 430 | } 431 | 432 | .clearfix {display: inline-block;} 433 | * html .clearfix {height: 1%;} 434 | .clearfix {display: block;} 435 | .clear { clear:both; } 436 | 437 | /* Same bottom margin for special boxes than for regular paragraphs, this way 438 | intermediate whitespace looks uniform. */ 439 | div.code_container, div.important, div.caution, div.warning, div.note, div.info { 440 | margin-bottom: 1.5em; 441 | } 442 | 443 | /* Remove bottom margin of paragraphs in special boxes, otherwise they get a 444 | spurious blank area below with the box background. */ 445 | div.important p, div.caution p, div.warning p, div.note p, div.info p { 446 | margin-bottom: 1em; 447 | } 448 | 449 | /* Edge Badge 450 | --------------------------------------- */ 451 | 452 | #edge-badge { 453 | position: fixed; 454 | right: 0px; 455 | top: 0px; 456 | z-index: 100; 457 | border: none; 458 | } 459 | -------------------------------------------------------------------------------- /src/files/stylesheets/overrides.print.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sproutcore/guides/7aaca2fdd48724360782d3dc34c3b07355f1be8e/src/files/stylesheets/overrides.print.css -------------------------------------------------------------------------------- /src/files/stylesheets/overrides.style.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #3e434c; 3 | background: #fff; 4 | } 5 | 6 | strong { 7 | font-weight: bold !important; 8 | } 9 | 10 | em,dfn { 11 | font-style: italic !important; 12 | } 13 | 14 | #mainCol ol { 15 | list-style-type: decimal !important; 16 | margin-left: 40px !important; 17 | margin-bottom: 10px; 18 | } 19 | 20 | dd img { 21 | float: left; 22 | height: 36px; 23 | margin: 0.25em 1em 0 0; 24 | } 25 | 26 | #feature { 27 | background: #A7B5BF; 28 | background-image: url(../images/header/pixels.png); 29 | background-repeat: repeat; 30 | border-bottom: 1px #fff; 31 | padding: 1px; 32 | } 33 | 34 | #feature .wrapper { 35 | height: 70px; 36 | margin-top: 80px; 37 | margin-bottom: 30px; 38 | padding: 0; 39 | width: 960px; 40 | position: relative; 41 | z-index: 10; /* ensure the guides index is above #container content */ 42 | } 43 | 44 | #feature .feature_header { 45 | width: 680px; 46 | text-shadow: 0 0 1px #fff; 47 | text-shadow: 0 0 3px #fff; 48 | text-shadow: 0 0 5px #fff; 49 | } 50 | 51 | #feature img { 52 | float: left; 53 | margin: 0 10px 25px 0; 54 | } 55 | 56 | #feature h2 a { 57 | color: #000; 58 | font-size: 250%; 59 | font-weight: bold; 60 | margin-bottom: 0; 61 | } 62 | 63 | #feature p { 64 | font-size: 125%; 65 | margin-top: -0.5em; 66 | } 67 | 68 | #feature .feature_sidebar { 69 | position: absolute; 70 | right: 0; 71 | top: 0; 72 | width: 280px; 73 | } 74 | 75 | #feature #guidesMenu, 76 | #feature #guides .guidesMenu { 77 | font-size: 115%; 78 | text-transform: uppercase; 79 | font-weight: bold; 80 | color: #454b54; 81 | text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.32); 82 | } 83 | 84 | #feature #guidesMenu { 85 | display: block; 86 | width: 214px; 87 | height: 34px; 88 | margin: 1.2em auto; 89 | text-align: center; 90 | padding-top: 13px; 91 | background: url(../images/graphics/guide-button.png) no-repeat center -1px; 92 | } 93 | 94 | #feature #guidesMenu:hover { 95 | background-position: center center; 96 | } 97 | 98 | #feature #guidesMenu:active { 99 | background-position: center bottom; 100 | } 101 | 102 | #feature #guides .guidesMenu { 103 | float: right; 104 | margin: -10px 24px 10px 0; 105 | } 106 | 107 | #guides { 108 | top: 16px; 109 | right: 36px; 110 | width: 520px; 111 | background-color: #fff; 112 | color: #3e434c; 113 | } 114 | 115 | #guides .L { 116 | width: 240px; 117 | } 118 | #guides .R { 119 | width: 240px; 120 | margin-left: 40px; 121 | } 122 | 123 | #guides hr { 124 | background-color: #d2cece; 125 | } 126 | 127 | #guides dl { 128 | font-size: 140%; 129 | } 130 | 131 | #guides dl dt { 132 | color: #3e434c; 133 | letter-spacing: 0.05em; 134 | font-weight: bold; 135 | } 136 | 137 | #guides dl dd { 138 | text-indent: -10px; 139 | margin-left: 18px; 140 | } 141 | 142 | #container { 143 | background: white url(../images/graphics/containergradient.png) repeat-x scroll; 144 | } 145 | 146 | #container .wrapper { 147 | width: 960px; 148 | position: relative; 149 | margin-top: 3.5em; 150 | border: 1px solid #d2cece; 151 | border-radius: 6px; 152 | -webkit-border-radius: 6px; 153 | -moz-border-radius: 6px; 154 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.24); 155 | -webkit-box-shadow: 0 0 2px rgba(0, 0, 0, 0.24); 156 | -moz-box-shadow: 0 0 2px rgba(0, 0, 0, 0.24); 157 | /* background for sidebar */ 158 | background: url(../images/graphics/sidebar-gradient.png) repeat-x; 159 | } 160 | 161 | #mainCol { 162 | margin: 0; 163 | padding: 0 35px 2em 35px; 164 | width: 610px; 165 | border-right: 1px solid #d2cece; 166 | background: #fff; 167 | border-top-left-radius: 6px; 168 | border-bottom-left-radius: 6px; 169 | -webkit-border-top-left-radius: 6px; 170 | -webkit-border-bottom-left-radius: 6px; 171 | -moz-border-radius-topleft: 6px; 172 | -moz-border-radius-bottomleft: 6px; 173 | } 174 | 175 | /** Guide headers */ 176 | #mainCol .headerSection { 177 | position: relative; 178 | background: #f7f7f7 url(../images/graphics/guidegradient.png) repeat-x; 179 | border-top: 0; 180 | border-top-left-radius: 6px; 181 | -webkit-border-top-left-radius: 6px; 182 | -moz-border-radius-topleft: 6px; 183 | margin: 0 -35px; 184 | padding: 10px 35px; 185 | border-bottom: 1px solid #d2cece; 186 | } 187 | 188 | #mainCol .headerSection h2 { 189 | font-size: 225%; 190 | font-weight: 200; 191 | margin-bottom: 10px; 192 | } 193 | 194 | #mainCol .headerSection p { 195 | font-size: 125%; 196 | } 197 | 198 | #mainCol .headerSection ul li { 199 | font-size: 110%; 200 | font-weight: bold; 201 | padding-left: 30px; 202 | margin: 0.8em 0; 203 | background-image: url(../images/graphics/check.png); 204 | background-position: 7px 1px; 205 | list-style: none; 206 | background-repeat: no-repeat; 207 | } 208 | 209 | /** Guide body styles */ 210 | #mainCol h3, 211 | #mainCol h4, 212 | #mainCol h5 { 213 | font-size: 150%; 214 | font-weight: 500; 215 | margin: 1em 0; 216 | } 217 | 218 | #mainCol h4 { 219 | font-size: 125%; 220 | } 221 | 222 | #mainCol h5 { 223 | font-size: 110%; 224 | } 225 | 226 | #mainCol p { 227 | font-size: 110%; 228 | margin-bottom: 1em; 229 | } 230 | 231 | #mainCol ul { 232 | margin-bottom: 1em; 233 | } 234 | 235 | .note { 236 | background: #fff9d8 url(../images/graphics/tab-notes.png) no-repeat left top; 237 | margin-top: 10px; 238 | margin-bottom: 10px; 239 | } 240 | 241 | .note ins { 242 | background-color: #eee9be; 243 | border: 1px solid #eee9be; 244 | } 245 | 246 | 247 | .info { 248 | background: #e5cfec url(../images/graphics/tab-pin.png) no-repeat left top; 249 | margin-top: 10px; 250 | margin-bottom: 10px; 251 | } 252 | 253 | .info ins { 254 | background-color: #d4bae0; 255 | border: 1px solid #d4bae0; 256 | } 257 | 258 | #mainCol dd.work-in-progress, #subCol dd.work-in-progress { 259 | background: #fff9d8 url(../images/graphics/tab-notes.png) no-repeat left top; 260 | } 261 | 262 | #mainCol div.warning, #subCol dd.warning { 263 | background: #f9d9d8 url(../images/graphics/tab-warning.png) no-repeat left top; 264 | padding: 1em 1em 0.25em 48px; 265 | } 266 | 267 | 268 | /** Index and credits headers */ 269 | #mainCol h3.group:first-child { 270 | background: #f7f7f7 url(../images/graphics/guidegradient.png) repeat-x; 271 | border-top: 0; 272 | border-top-left-radius: 6px; 273 | -webkit-border-top-left-radius: 6px; 274 | -moz-border-radius-topleft: 6px; 275 | } 276 | 277 | #mainCol h3.group { 278 | font-size: 225%; 279 | font-weight: 200; 280 | margin: 0 -35px; 281 | padding: 10px 35px; 282 | border-bottom: 1px solid #d2cece; 283 | border-top: 1px solid #d2cece; 284 | } 285 | 286 | #mainCol h3.section { 287 | padding-top: 1em; 288 | padding-bottom: 0.5em; 289 | border-bottom: 1px solid #d2cece; 290 | font-size: 1.25em; 291 | font-weight: normal; 292 | text-transform: uppercase; 293 | } 294 | 295 | #mainCol dl dt { 296 | font-weight: normal; 297 | margin-top: 1em; 298 | clear: left; 299 | } 300 | 301 | #mainCol dl dd { 302 | font-size: 115%; 303 | padding-bottom: 1.5em; 304 | } 305 | 306 | #mainCol dl dd:last-child { 307 | margin-bottom: 0; 308 | border-bottom: 0; 309 | } 310 | 311 | #mainCol div { 312 | vertical-align: middle; 313 | } 314 | 315 | #mainCol div h3 { 316 | font-size: 150%; 317 | } 318 | 319 | #mainCol div.author { 320 | float: left; 321 | margin-top: 10px; 322 | width: 305px; 323 | } 324 | 325 | #mainCol div.author h3 { 326 | margin: 10px !important; 327 | } 328 | 329 | #mainCol div.author p { 330 | margin-left: 105px; 331 | } 332 | 333 | #mainCol img.credits-avatar { 334 | width: 80px; 335 | -webkit-border-radius: 15px; 336 | -moz-border-radius: 15px; 337 | border-radius: 15px; 338 | } 339 | 340 | #subCol { 341 | background: transparent; 342 | position: absolute; 343 | width: 240px; 344 | padding: 1em 20px; 345 | border-top-right-radius: 6px; 346 | -webkit-border-top-right-radius: 6px; 347 | -moz-border-radius-topright: 6px; 348 | } 349 | 350 | #subCol h3 { 351 | margin: 0.4em 0 1em 0; 352 | font-size: 125%; 353 | font-weight: bold; 354 | text-transform: uppercase; 355 | color: #718090; 356 | } 357 | 358 | #subCol h3.chapter { 359 | margin-top: -5px; 360 | } 361 | 362 | #subCol h3.chapter img { 363 | position: relative; 364 | top: 5px; 365 | margin-right: 10px; 366 | } 367 | 368 | #subCol p { 369 | clear: left; 370 | margin-bottom: 3em; 371 | font-size: 14px; 372 | line-height: 20px; 373 | } 374 | 375 | #subCol ul.links li { 376 | line-height: 2em; 377 | } 378 | 379 | #subCol p img { 380 | float: left; 381 | margin: 0.3em 1em 2em 0; 382 | } 383 | 384 | #subCol p.getInvolved a { 385 | font-weight: bold; 386 | } 387 | 388 | #mainCol a, #subCol a, #feature a, a, a:link, a:visited { 389 | color: #047ed7; 390 | } 391 | 392 | #mainCol a, #subCol a, #feature a { 393 | font-weight: normal; 394 | } 395 | 396 | .guide tt, .guide code, .guide ins { 397 | font-family: "Monaco", "Anonymous Pro", "Inconsolata", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace !important; 398 | font-size: 0.9em; 399 | } 400 | 401 | #mainCol tt { 402 | background-color: #deeac9; 403 | border: 1px solid #cad5b7; 404 | } 405 | 406 | #mainCol tt, #mainCol ins { 407 | padding: 1px 3px; 408 | -webkit-border-radius: 3px; 409 | -moz-border-radius: 3px; 410 | border-radius: 3px; 411 | } 412 | 413 | #subCol ol.chapters { 414 | list-style: decimal outside; 415 | } 416 | 417 | #subCol ol.chapters a { 418 | text-decoration: none; 419 | font-weight: normal; 420 | } 421 | 422 | #subCol ol.chapters > li { 423 | color: #000; 424 | font-size: 115%; 425 | margin: 1.5em; 426 | margin-right: 0; 427 | } 428 | 429 | #subCol ol.chapters li a p { 430 | /* FIXES: Firefox incorrectly renders block elements inside of list items */ 431 | display: inline; 432 | } 433 | 434 | .syntaxhighlighter { 435 | /* FIXES: Chome cuts off underscores on the last line of a code block */ 436 | padding-bottom: 1px; 437 | } 438 | -------------------------------------------------------------------------------- /src/files/stylesheets/print.css: -------------------------------------------------------------------------------- 1 | body, .wrapper, .note, .info, code, #topNav, .L, .R, #frame, #container, #header, #navigation, #footer, #feature, #mainCol, #subCol, #extraCol, .content {position: static; text-align: left; text-indent: 0; background: White; color: Black; border-color: Black; width: auto; height: auto; display: block; float: none; min-height: 0; margin: 0; padding: 0;} 2 | 3 | body { 4 | background: #FFF; 5 | font-size: 10pt !important; 6 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 7 | line-height: 1.5; 8 | color: #000; 9 | padding: 0 3%; 10 | } 11 | 12 | .hide, .nav { 13 | display: none !important; 14 | } 15 | 16 | a:link, a:visited { 17 | background: transparent; 18 | font-weight: bold; 19 | text-decoration: underline; 20 | } 21 | 22 | hr { 23 | background:#ccc; 24 | color:#ccc; 25 | width:100%; 26 | height:2px; 27 | margin:2em 0; 28 | padding:0; 29 | border:none; 30 | } 31 | 32 | h1,h2,h3,h4,h5,h6 { font-family: "Helvetica Neue", Arial, "Lucida Grande", sans-serif; } 33 | code { font:.9em "Courier New", Monaco, Courier, monospace; } 34 | 35 | img { float:left; margin:1.5em 1.5em 1.5em 0; } 36 | a img { border:none; } 37 | 38 | blockquote { 39 | margin:1.5em; 40 | padding:1em; 41 | font-style:italic; 42 | font-size:.9em; 43 | } 44 | 45 | .small { font-size: .9em; } 46 | .large { font-size: 1.1em; } 47 | -------------------------------------------------------------------------------- /src/files/stylesheets/reset.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, font, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td { 10 | margin: 0; 11 | padding: 0; 12 | border: 0; 13 | outline: 0; 14 | font-size: 100%; 15 | background: transparent; 16 | } 17 | 18 | body {line-height: 1; color: black; background: white;} 19 | a img {border:none;} 20 | ins {text-decoration: none;} 21 | del {text-decoration: line-through;} 22 | 23 | :focus { 24 | -moz-outline:0; 25 | outline:0; 26 | outline-offset:0; 27 | } 28 | 29 | /* tables still need 'cellspacing="0"' in the markup */ 30 | table {border-collapse: collapse; border-spacing: 0;} 31 | caption, th, td {text-align: left; font-weight: normal;} 32 | 33 | blockquote, q {quotes: none;} 34 | blockquote:before, blockquote:after, 35 | q:before, q:after { 36 | content: ''; 37 | content: none; 38 | } 39 | -------------------------------------------------------------------------------- /src/layouts/credits_layout.html.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= @document.title %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | <%- @getBlock("meta").toHTML() %> 24 | <%- @getBlock("styles").toHTML() %> 25 | 26 | 27 | 28 | 29 |
30 |
31 |

32 | SproutCore 33 |

34 | 44 |
45 |
46 | 47 |
48 |
49 |
50 | 51 |

Credits

52 |

We'd like to thank the following people for their tireless efforts.

53 |
54 |
55 | 56 | Guides Index 57 | 58 | 67 |
68 |
69 |
70 | 71 |
72 |
73 |
74 |
75 |

<%- @document.sc_title %>

76 |
77 | 78 | <% for index, section of @document.sc_credits: %> 79 |

<%= section.name %>

80 | 81 | 82 | <% for index, author of @document.sortBy(section.authors, ['lname', 'fname']): %> 83 |
84 | <%= author.fname %> <%= author.lname %> 85 |

<%= author.fname %> <%= author.lname %>

86 |

<%= author.github %>

87 |
88 | <% end %> 89 | 90 |
91 | <% end %> 92 |
93 | 94 |
95 |

Become a Contributor

96 | 101 |
102 |
103 |
104 | 105 |
106 | 107 | 153 | 154 | 155 | 156 | <%- @getBlock("scripts").toHTML() %> 157 | 158 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /src/layouts/default.html.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= @document.title %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | <%- @getBlock("meta").toHTML() %> 24 | <%- @getBlock("styles").toHTML() %> 25 | 26 | 27 | 28 | 29 |
30 |
31 |

32 | SproutCore 33 |

34 | 44 |
45 |
46 | 47 |
48 |
49 |
50 | 51 |

SproutCore Guides

52 |

These guides are designed to help you write and perfect your code.

53 |
54 |
55 | 56 | Guides Index 57 | 58 | 67 |
68 |
69 |
70 | 71 |
72 |
73 |
74 |
75 | <% if @document.sc_title: %> 76 |

<%- @document.sc_title %><%- if @document.sc_short_description then ' - ' + @document.sc_short_description else '' %>

77 | <% end %> 78 | 79 | <% if @document.sc_description: %> 80 |

<%- @document.sc_description %>

81 | <% end %> 82 | 83 | <% if @document.sc_list: %> 84 |
    85 | <% for item in @document.sc_list: %> 86 |
  • <%- item %>
  • 87 | <% end %> 88 |
89 | <% end %> 90 |
91 | 92 | <%- @content %> 93 |
94 | 95 |
96 |

Chapters

97 |
    98 | <% for index,chapter of @document.table_of_contents: %> 99 |
  1. 100 | '>

    <%- chapter['title'] %>

    101 |
      102 | <% for subheading in chapter['subheadings']: %> 103 |
    • '>

      <%- subheading %>

    • 104 | <% end %> 105 |
    106 |
  2. 107 | <% end %> 108 |
109 |
110 |
111 |
112 | 113 |
114 | 115 | 161 | 162 | 163 | 164 | <%- @getBlock("scripts").toHTML() %> 165 | 166 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /src/layouts/home.html.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= @document.title %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | <%- @getBlock("meta").toHTML() %> 24 | <%- @getBlock("styles").toHTML() %> 25 | 26 | 27 | 28 | 29 |
30 |
31 |

32 | SproutCore 33 |

34 | 44 |
45 |
46 | 47 |
48 |
49 |
50 | 51 |

SproutCore Guides

52 |

These guides are designed to help you write and perfect your code.

53 |
54 |
55 | 56 | Guides Index 57 | 58 | 67 |
68 |
69 |
70 | 71 |
72 |
73 | 74 | <%- @content %> 75 | 76 |
77 |
78 | 79 |
80 | 81 | 127 | 128 | 129 | 130 | <%- @getBlock("scripts").toHTML() %> 131 | 132 | 151 | 152 | 153 | 154 | --------------------------------------------------------------------------------
_\.(.*?)<\/td>/g, "$1